添加AI排片功能

main
rogersun 2 weeks ago
parent 77aff56530
commit d91da23a3f
  1. 3
      .gitignore
  2. 0
      ai/__init__.py
  3. 3
      ai/admin.py
  4. 6
      ai/apps.py
  5. 51
      ai/migrations/0001_initial.py
  6. 23
      ai/migrations/0002_aishow_take_times_aishow_take_tokens.py
  7. 28
      ai/migrations/0003_prompttemplate.py
  8. 0
      ai/migrations/__init__.py
  9. 59
      ai/models.py
  10. 21
      ai/serializers.py
  11. 10
      ai/tasks.py
  12. 3
      ai/tests.py
  13. 25
      ai/urls.py
  14. 0
      ai/utils/__init__.py
  15. 37
      ai/utils/datetime_format.py
  16. 136
      ai/utils/show_database.py
  17. 54
      ai/utils/show_process.py
  18. 130
      ai/utils/show_prompt.py
  19. 182
      ai/utils/sql.py
  20. 75
      ai/views.py
  21. 2
      dingxin_toolbox_drf/settings.py
  22. 1
      dingxin_toolbox_drf/urls.py
  23. BIN
      reqirement_linux.txt
  24. 18
      update/migrations/0034_cinema_url.py

3
.gitignore vendored

@ -4,3 +4,6 @@
/logs/ /logs/
/logs/dingxin.log /logs/dingxin.log
*.log *.log
/celerybeat-schedule.bak
/celerybeat-schedule.dat
/celerybeat-schedule.dir

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ShowAiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'ai'

@ -0,0 +1,51 @@
# Generated by Django 4.2.7 on 2026-06-08 07:20
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='AiShow',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('cinema', models.CharField(max_length=50)),
('zz_code', models.CharField(max_length=50)),
('show_date', models.DateField()),
('is_ai_show', models.BooleanField(default=True)),
('show', models.TextField()),
('sales', models.CharField(max_length=50)),
('prompt', models.TextField()),
('result', models.TextField()),
('message', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'verbose_name': 'AI排片数据',
'verbose_name_plural': 'AI排片数据',
'db_table': 'ai_show_result',
},
),
migrations.CreateModel(
name='TestCinema',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=50)),
('zz_code', models.CharField(max_length=50)),
('db_config', models.TextField()),
('user_config', models.TextField()),
('is_active', models.BooleanField(default=True)),
],
options={
'verbose_name': '测试影院',
'verbose_name_plural': '测试影院',
'db_table': 'ai_show_cinema',
},
),
]

@ -0,0 +1,23 @@
# Generated by Django 4.2.7 on 2026-06-08 10:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ai', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='aishow',
name='take_times',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='aishow',
name='take_tokens',
field=models.CharField(default='', max_length=2000),
),
]

@ -0,0 +1,28 @@
# Generated by Django 4.2.7 on 2026-06-09 02:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ai', '0002_aishow_take_times_aishow_take_tokens'),
]
operations = [
migrations.CreateModel(
name='PromptTemplate',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('prompt_type', models.CharField(max_length=50)),
('prompt_key', models.CharField(max_length=50)),
('prompt_val', models.TextField()),
('del_flag', models.BooleanField(default=False)),
],
options={
'verbose_name': '提示词',
'verbose_name_plural': '提示词',
'db_table': 'ai_prompt_template',
},
),
]

@ -0,0 +1,59 @@
from django.db import models
# Create your models here.
class TestCinema(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
zz_code = models.CharField(max_length=50)
db_config = models.TextField()
user_config = models.TextField()
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '测试影院'
verbose_name_plural = '测试影院'
db_table = 'ai_show_cinema'
class PromptTemplate(models.Model):
id = models.AutoField(primary_key=True)
prompt_type = models.CharField(max_length=50)
prompt_key = models.CharField(max_length=50)
prompt_val = models.TextField()
del_flag = models.BooleanField(default=False)
def __str__(self):
return self.prompt_key
class Meta:
verbose_name = '提示词'
verbose_name_plural = '提示词'
db_table = 'ai_prompt_template'
class AiShow(models.Model):
id = models.AutoField(primary_key=True)
cinema = models.CharField(max_length=50)
zz_code = models.CharField(max_length=50)
show_date = models.DateField()
is_ai_show = models.BooleanField(default=True)
show = models.TextField()
sales = models.CharField(max_length=50)
prompt = models.TextField()
result = models.TextField()
message = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
take_times = models.IntegerField(default=0)
take_tokens = models.CharField(default='', max_length=2000)
def __str__(self):
return self.cinema
class Meta:
verbose_name = 'AI排片数据'
verbose_name_plural = 'AI排片数据'
db_table = 'ai_show_result'

@ -0,0 +1,21 @@
from rest_framework import serializers
from ai.models import *
class TestCinemaSerializer(serializers.ModelSerializer):
class Meta:
model = TestCinema
fields = '__all__'
class PromptTemplateSerializer(serializers.ModelSerializer):
class Meta:
model = PromptTemplate
fields = '__all__'
class AiShowSerializer(serializers.ModelSerializer):
class Meta:
model = AiShow
fields = '__all__'

@ -0,0 +1,10 @@
from celery import shared_task
from ai.utils.show_process import show_main_process
@shared_task
def ai_show_general():
show_main_process()
print("task success")
return True

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

@ -0,0 +1,25 @@
"""
URL configuration for dingxin_toolbox_drf project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from ai.views import *
urlpatterns = [
path('manual_general_show', manual_general_show),
path('get_cinema_show_result', get_cinema_show_result),
path('clear', clear_lock),
]

@ -0,0 +1,37 @@
import datetime
from chinese_calendar import (is_holiday, is_workday)
def get_data_datetime(show_date):
show_date_obj = datetime.datetime.strptime(show_date, '%Y-%m-%d')
target_start_datetime_obj = show_date_obj + datetime.timedelta(hours=6)
target_end_datetime_obj = show_date_obj + datetime.timedelta(hours=29, minutes=59, seconds=59)
data_start_datetime_obj = show_date_obj + datetime.timedelta(days=-7) + datetime.timedelta(hours=6)
data_end_datetime_obj = show_date_obj + datetime.timedelta(hours=5, minutes=59, seconds=59)
history_start = datetime.datetime.strftime(data_start_datetime_obj, '%Y-%m-%d %H:%M:%S')
history_end = datetime.datetime.strftime(data_end_datetime_obj, '%Y-%m-%d %H:%M:%S')
target_start = datetime.datetime.strftime(target_start_datetime_obj, '%Y-%m-%d %H:%M:%S')
target_end = datetime.datetime.strftime(target_end_datetime_obj, '%Y-%m-%d %H:%M:%S')
return history_start, history_end, target_start, target_end
def get_date_type(show_date, template_date):
show_date_obj = datetime.datetime.strptime(show_date, '%Y-%m-%d')
template_date_obj = datetime.datetime.strptime(template_date, '%Y-%m-%d')
result = dict()
# 处理参考数据日期
history_date_list = [(show_date_obj - datetime.timedelta(days=i)) for i in range(7, 0, -1)]
history_date = ''.join(
[f'{datetime.date.strftime(d, "%Y-%m-%d")}{"节假日" if is_holiday(d) else "工作日"}' for d in
history_date_list])
# 处理目标日期
show_date = f'{datetime.date.strftime(show_date_obj, "%Y-%m-%d")}{"节假日" if is_holiday(show_date_obj) else "工作日"}'
show_date_is_holiday = is_holiday(show_date_obj)
# 处理模板日期
template_date = f'{datetime.date.strftime(template_date_obj, "%Y-%m-%d")}{"节假日" if is_holiday(template_date_obj) else "工作日"}'
template_date_is_holiday = is_holiday(template_date_obj)
return history_date, show_date, show_date_is_holiday, template_date, template_date_is_holiday
if __name__ == '__main__':
get_date_type("2026-06-13", "2026-06-01")

@ -0,0 +1,136 @@
import pymysql
from pymysql.cursors import DictCursor
from ai.utils.sql import *
import datetime
import pandas as pd
class GetData:
def __init__(self, db_config):
self.conn = pymysql.connect(**db_config)
self.cur = self.conn.cursor(cursor=DictCursor)
def exit(self):
try:
self.cur.close()
self.conn.close()
except Exception as e:
print(e)
# 获取指定日期范围的排片数据
def get_show_data(self, start_datetime, end_datetime):
# print(self.cur.mogrify(GET_SHOW_DATA, (start_datetime, end_datetime)))
self.cur.execute(GET_SHOW_DATA, (start_datetime, end_datetime))
show_data = self.cur.fetchall()
show_data_mapping = {
'hall_name': '影厅别名',
'hall_id': '影厅id',
'movie_name': '影片别名',
'movie_id': '本地影片id',
'language': '语言',
'show_date': '放映日期',
'start_time': '开始时间',
'end_time': '结束时间',
'length': '片长',
'duration': '场间',
}
return self.format_to_csv(show_data, show_data_mapping)
# 获取指定范围的影片销售数据
def get_sell_data(self, start_datetime, end_datetime):
# print(self.cur.mogrify(GET_SELL_DATA, (start_datetime, end_datetime)))
self.cur.execute(GET_SELL_DATA, (start_datetime, end_datetime, start_datetime, end_datetime))
sell_data = self.cur.fetchall()
sell_data_mapping = {
'movie_name': '影片别名',
'movie_id': '本地影片id',
'show_date': '放映日期',
'start_time': '开始时间',
'hall_name': '影厅别名',
'hall_id': '影厅id',
'total_count': '场次观影人数',
'total_income': '场次收入',
'seat_num': '影厅座位数',
'average_price': '平均价',
'rate': '上座率(单位%',
}
return self.format_to_csv(sell_data, sell_data_mapping)
# 获取指定范围的影片销售数据
def get_total_income(self, start_datetime, end_datetime):
# print(self.cur.mogrify(GET_SELL_DATA, (start_datetime, end_datetime)))
self.cur.execute(GET_TOTAL_INCOME, (start_datetime, end_datetime, start_datetime, end_datetime))
return self.cur.fetchone()['total_income']
# 获取指定日期的可放映影片
def get_available_movie(self, date):
start_datetime = datetime.datetime.strptime(date, '%Y-%m-%d') + datetime.timedelta(hours=6)
end_datetime = datetime.datetime.strptime(date, '%Y-%m-%d') + datetime.timedelta(hours=29, minutes=59,
seconds=59)
self.cur.execute(GET_AVAILABLE_MOVIE, (start_datetime, end_datetime))
available_movie = self.cur.fetchall()
available_movie_list = [
f"\t{m['cinema_movie_alias']}》 - 影片id:{m['cinema_movie_id']},影片时长:{m['cinema_movie_time']},语言:{m['language']},制式:{m['media_type']}, 最早排片开始时间:{m['cinema_movie_start_datetime']}, 最晚排片截止时间:{m['cinema_movie_end_datetime']}"
for m in available_movie]
return '\n'.join(available_movie_list)
# 获取新上映的影片
def get_new_movie(self, date):
start_datetime = datetime.datetime.strptime(date, '%Y-%m-%d')
end_datetime = datetime.datetime.strptime(date, '%Y-%m-%d') + datetime.timedelta(hours=29, minutes=59,
seconds=59)
self.cur.execute(GET_NEW_MOVIE, (start_datetime, end_datetime))
new = self.cur.fetchall()
new_list = [f"{m['cinema_movie_alias']}" for m in new]
return new_list
# 获取影厅支持的制式
def get_hall_media_type(self):
self.cur.execute(GET_HALL_MEDIA_TYPE)
hall_data = self.cur.fetchall()
# print(hall_data)
hall_dict = dict()
# 处理成对应的字典格式 {"影厅别名": {"hall_id":影厅id,"media_type": 影片制式列表}}
for hall in hall_data:
if hall['cinema_hall_name'] not in hall_dict.keys():
hall_dict[hall['cinema_hall_name']] = {
'hall_id': hall['cinema_hall_id'],
'media_type': [hall['media_type'], ]
}
else:
hall_dict[hall['cinema_hall_name']]['media_type'].append(hall['media_type'])
# 拼装字符串
hall_str_list = []
for hall, val in hall_dict.items():
hall_str_list.append(f"\t{hall} - 影厅id:{val['hall_id']},支持制式:{''.join(val['media_type'])}")
return '\n'.join(hall_str_list)
# 获取模板日期
def get_template_date(self, start_datetime, end_datetime):
basic_val = 4
self.cur.execute(GET_TEMPLATE_DATE, (start_datetime, end_datetime, basic_val))
template_date = self.cur.fetchall()[0]['template_date']
return template_date
# 数据转csv格式字符串
@staticmethod
def format_to_csv(dict_data, mapping):
# print(dict_data)
new_dict = [{mapping[k]: v for k, v in d.items()} for d in dict_data]
return pd.DataFrame(new_dict).to_csv(index=False)
def get_media_type(self):
self.cur.execute(GET_HALL_MEDIA_TYPE)
if __name__ == '__main__':
from main import cinema_list
data = GetData(cinema_list[0]['db'])
# print(data.get_show_data('2026-05-28 06:00:00', '2026-06-04 05:59:59'))
# print(data.get_sell_data('2026-05-28 06:00:00', '2026-06-04 05:59:59'))
# print(data.get_available_movie('2026-06-05'))
print(data.get_new_movie('2026-06-05'))
# print(data.get_hall_media_type())
# print(data.get_template_date('2026-05-28 06:00:00', '2026-06-04 05:59:59'))

@ -0,0 +1,54 @@
from ai.models import *
from ai.utils.show_prompt import *
import datetime
from django_redis import get_redis_connection
# ai排片主流程
def show_main_process():
# 查看状态
redis_conn = get_redis_connection()
redis_key = f'ai_show{datetime.date.today().strftime("%Y%m%d")}'
if redis_conn.exists(redis_key):
return False
# 获取影院列表
redis_conn.set(redis_key, 1, ex=60*60*20)
test_cinema_list = TestCinema.objects.filter(is_active=True).all()
# 生成目标日期
show_date = datetime.date.strftime(datetime.date.today() + datetime.timedelta(days=3), '%Y-%m-%d')
for cinema in test_cinema_list:
print(cinema.name)
show_ai = ShowAI(cinema, show_date)
prompt = show_ai.general_prompt()
start = datetime.datetime.now()
result, message, tokens = show_ai.get_show_result_ai()
end = datetime.datetime.now()
print('prompt:', prompt)
print('result:', result)
print('message:', message)
print('tokens:', tokens)
# 获取排片数据
_show = next((s for s in result.split('------') if s.startswith('\n影厅别名,影厅id')), '')
_show = _show.strip()
print('_show:', _show)
# 获取销售额
_sales = next((s for s in result.split('\n') if s.startswith('预估销售数据')), '').replace('预估销售数据:', '')
_sales = _sales.replace('', '').replace('', '')
print('_sales:', _sales)
# 处理返回结果
try:
AiShow.objects.create(
cinema=cinema.name,
zz_code=cinema.zz_code,
show_date=show_date,
is_ai_show=True,
show=_show,
sales=_sales,
prompt=prompt,
result=result,
message=message,
take_times=int((end - start).seconds),
take_tokens=tokens
)
except Exception as e:
print(e)
return True

@ -0,0 +1,130 @@
from django.db.models import Q
from ai.models import PromptTemplate
from ai.utils.show_database import GetData
from ai.utils.datetime_format import (get_data_datetime, get_date_type)
import datetime
import random
import json
from openai import OpenAI
class ShowAI:
def __init__(self, cinema_info, show_date):
self.cinema_name = cinema_info.name
self.cinema_zz_code = cinema_info.zz_code
self.cinema_db_config = json.loads(cinema_info.db_config)
self.user_config = json.loads(cinema_info.user_config)
self.show_date = show_date
self.prompt = ""
self.client = OpenAI(
api_key='sk-b7993bf4c8844e79bf84f08f07ff86d9',
base_url='https://api.deepseek.com',
)
# 生成提示词
def general_prompt(self):
data = GetData(self.cinema_db_config)
# 准备各种日期时间
history_start, history_end, target_start, target_end = get_data_datetime(self.show_date)
temp = datetime.datetime.strftime(data.get_template_date(history_start, history_end), '%Y-%m-%d')
history_date, target_date, target_date_is_holiday, template_date, template_date_is_holiday = get_date_type(
self.show_date, temp)
# print(history_start, history_end, target_start, target_end, history_date, show_date, template_date)
# 开始处理提示词
prompt_list = []
# 处理排片数据
pre_data = PromptTemplate.objects.filter(
Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='PreData')).first().prompt_val # 获取提示词模板
pre_data = pre_data.replace('{history_show}', data.get_show_data(history_start, history_end)) # 处理排片数据
pre_data = pre_data.replace('{history_sales}', data.get_sell_data(history_start, history_end)) # 处理销售数据
pre_data = pre_data.replace('{target_already_show}',
data.get_show_data(target_start, target_end)) # 处理目标日期已排场次数据
pre_data = pre_data.replace('{reference_date}', history_date) # 处理参考日期
pre_data = pre_data.replace('{template_date}', template_date) # 处理模板日期
pre_data = pre_data.replace('{target_date}', target_date) # 处理目标日期
prompt_list.append(pre_data)
# 处理角色说明
role = PromptTemplate.objects.filter(
Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Role')).first().prompt_val # 获取提示词模板
role = role.replace('{template_date}', template_date)
prompt_list.append(role)
# 处理影片和影厅信息
movie_hall = PromptTemplate.objects.filter(
Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='MovieHall')).first().prompt_val # 获取提示词模板
movie_hall = movie_hall.replace('{available_movie}', data.get_available_movie(self.show_date)) # 处理可排影片
movie_hall = movie_hall.replace('{hall_info}', data.get_hall_media_type()) # 处理影厅信息
free_hall = self.user_config['holiday_date_free_hall'] if target_date_is_holiday else self.user_config[
'holiday_date_free_hall']
movie_hall = movie_hall.replace('{free_hall}',
'\t' if len(free_hall) == 0 else '\n'.join(
f'\t{h}' for h in free_hall)) # 处理不可排片影厅
prompt_list.append(movie_hall)
# 处理具体要求
rules = PromptTemplate.objects.filter(
Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Rules')).first().prompt_val # 获取提示词模板
rules = rules.replace('{template_date}', template_date) # 处理模板日期
# 获取新片
new_movie_str_list = []
new_movie = data.get_new_movie(self.show_date)
for new in new_movie:
new_movie_str_list.append(
f"\t增加{new}的场次,建议排{str(random.randint(2, 4))}场,可根据影片热度和口碑适当增加;")
rules = rules.replace('{new_movie}',
'\t无新片上映' if len(new_movie_str_list) == 0 else '\n'.join(new_movie_str_list)) # 处理新片数据
prompt_list.append(rules)
# 处理基本规则
basic = PromptTemplate.objects.filter(
Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Basic')).first().prompt_val # 获取提示词模板
start = self.user_config['holiday_date_start'] if target_date_is_holiday else self.user_config[
'work_date_start']
end = self.user_config['holiday_date_end'] if target_date_is_holiday else self.user_config['work_date_end']
basic = basic.replace('{start}', start)
basic = basic.replace('{end}', end)
prompt_list.append(basic)
# 处理增减场次规则
add_reduce = PromptTemplate.objects.filter(
Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='AddReduce')).first().prompt_val # 获取提示词模板
prompt_list.append(add_reduce)
# 处理黄金时段
prime_time = PromptTemplate.objects.filter(
Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='PrimeTime')).first().prompt_val # 获取提示词模板
prompt_list.append(prime_time)
# 处理输出目标
output = PromptTemplate.objects.filter(
Q(del_flag=False) & Q(prompt_type='show') & Q(prompt_key='Output')).first().prompt_val # 获取提示词模板
prompt_list.append(output)
# 打印提示词
self.prompt = '\n'.join(prompt_list)
print(self.prompt)
# 关闭数据库连接
data.exit()
return self.prompt
# 请求AI
def get_show_result_ai(self):
# 生成提示词
# self.general_prompt()
# 与DeepSeek开始对话
print("与DeepSeek开始对话")
response = self.client.chat.completions.create(
model='deepseek-v4-pro',
messages=[
{'role': 'system', 'content': 'You are a helpful assistant'},
{'role': 'user', 'content': self.prompt},
],
stream=False,
reasoning_effort="high",
extra_body={"thinking": {"type": "enabled"}}
)
# print(dict(response))
# print(response.choices[0].message.content)
return response.choices[0].message.content, response.model_dump_json(), response.usage.model_dump_json()

@ -0,0 +1,182 @@
# 排片数据
GET_SHOW_DATA = """
SELECT hi.cinema_hall_name as hall_name,
hi.cinema_hall_id as hall_id,
mi.cinema_movie_alias as movie_name,
mi.cinema_movie_id as movie_id,
mlt.cinema_movie_lang_type_desc as language,
DATE_FORMAT(ms.cinema_movie_show_start_time, '%%Y-%%m-%%d') as show_date,
DATE_FORMAT(ms.cinema_movie_show_start_time, '%%H:%%i') as start_time,
DATE_FORMAT(ms.cinema_movie_show_end_time, '%%H:%%i') as end_time,
CAST(TIME_TO_SEC(TIMEDIFF(ms.cinema_movie_show_end_time, ms.cinema_movie_show_start_time)) /
60 AS UNSIGNED) as length,
IFNULL(MINUTE(TIMEDIFF(ms.cinema_movie_show_start_time, (SELECT MAX(cinema_movie_show_end_time)
FROM cinema_movie_show t
WHERE t.cinema_hall_id = ms.cinema_hall_id
AND t.cinema_movie_show_start_date = ms.cinema_movie_show_start_date
AND t.cinema_del_flag = 1
AND ms.cinema_movie_show_start_time >= t.cinema_movie_show_end_time))),
0) as duration
FROM cinema_movie_show ms
LEFT JOIN cinema_hall_info hi ON ms.cinema_hall_id = hi.cinema_hall_id
LEFT JOIN cinema_movie_info mi ON ms.cinema_movie_id = mi.cinema_movie_id
LEFT JOIN cinema_movie_lang_type mlt ON mi.cinema_movie_lang_id = mlt.cinema_movie_lang_type_id
WHERE ms.cinema_del_flag = 1
AND ms.cinema_movie_show_start_time >= %s
AND ms.cinema_movie_show_start_time <= %s
ORDER BY show_date, hall_id, start_time;
"""
# 获取销售数据
GET_SELL_DATA = """
SELECT md.movie_name,
md.movie_id,
md.show_date,
md.start_time,
md.hall_name,
md.hall_id,
SUM(md.count) as total_count,
SUM(md.income) as total_income,
md.seat_num,
ROUND(IF(SUM(md.income) = 0, 0.0, SUM(md.income) / SUM(md.count)), 2) as average_price,
ROUND(SUM(md.count) / md.seat_num * 100, 2) as rate
FROM (
-- 售票
SELECT ms.cinema_movie_show_id as cinema_movie_show_id,
mi.cinema_movie_alias as movie_name,
mi.cinema_movie_id as movie_id,
DATE_FORMAT(ms.cinema_movie_show_start_time, '%%Y-%%m-%%d') as show_date,
CAST(TIME(ms.cinema_movie_show_start_time) AS CHAR) as start_time,
hi.cinema_hall_name as hall_name,
hi.cinema_hall_id as hall_id,
SUM(IF(sl.cinema_ticket_status <> 3, 1, 0)) as count,
SUM(IF(sl.cinema_ticket_status <> 3, sl.cinema_ticket_income, 0)) as income,
ms.cinema_hall_seat_num as seat_num
FROM cinema_movie_show ms
LEFT JOIN cinema_sell_log sl ON ms.cinema_movie_show_id = sl.cinema_movie_show_id
JOIN cinema_movie_info mi ON ms.cinema_movie_id = mi.cinema_movie_id
JOIN cinema_hall_info hi ON ms.cinema_hall_id = hi.cinema_hall_id
WHERE ms.cinema_movie_show_joinflg = 0
AND ms.cinema_movie_show_start_time >= %s
AND ms.cinema_movie_show_start_time <= %s
AND ms.cinema_del_flag = 1
AND NOT (ms.cinema_movie_show_sold_num = 0 AND ms.cinema_movie_show_status <> 1)
GROUP BY ms.cinema_movie_show_id, CAST(ms.cinema_movie_show_id AS CHAR)
UNION
-- 补登
SELECT ms.cinema_movie_show_id as cinema_movie_show_id,
mi.cinema_movie_alias as movie_name,
mi.cinema_movie_id as movie_id,
DATE_FORMAT(ms.cinema_movie_show_start_time, '%%Y-%%m-%%d') as show_date,
CAST(TIME(ms.cinema_movie_show_start_time) AS CHAR) as start_time,
hi.cinema_hall_name as hall_name,
hi.cinema_hall_id as hall_id,
SUM(sa.cinema_sell_add_num) as count,
SUM(sa.cinema_sell_add_total) as income,
ms.cinema_hall_seat_num as seat_num
FROM cinema_sell_add sa
LEFT JOIN cinema_movie_show ms ON sa.cinema_sell_add_showid = ms.cinema_movie_show_id
LEFT JOIN cinema_hall_info hall ON ms.cinema_hall_id = hall.cinema_hall_id
LEFT JOIN cinema_show_policy_map spm ON sa.cinema_sell_add_showid = spm.cinema_movie_show_id AND
sa.cinema_sell_add_policy = spm.cinema_show_policy_map_id
JOIN cinema_hall_info hi ON ms.cinema_hall_id = hi.cinema_hall_id
JOIN cinema_movie_info mi ON ms.cinema_movie_id = mi.cinema_movie_id
WHERE ms.cinema_movie_show_joinflg = 0
AND ms.cinema_movie_show_start_time >= %s
AND ms.cinema_movie_show_start_time <= %s
AND ms.cinema_del_flag = 1
AND NOT (ms.cinema_movie_show_sold_num = 0 AND ms.cinema_movie_show_status <> 1)
GROUP BY ms.cinema_movie_show_id, CAST(ms.cinema_movie_show_id AS CHAR)) as md
GROUP BY md.cinema_movie_show_id, CAST(md.cinema_movie_show_id AS CHAR)
ORDER BY md.show_date, md.hall_id, md.start_time;
"""
# 获取指定日期的可放映影片
GET_AVAILABLE_MOVIE = """
SELECT cmi.cinema_movie_alias,
cmi.cinema_movie_id,
cmi.cinema_movie_time,
cmlt.cinema_movie_lang_type_desc as language,
scvm.system_const_val_desc as media_type,
cmi.cinema_movie_start_datetime,
cmi.cinema_movie_end_datetime
FROM cinema_movie_info cmi
JOIN cinema_movie_lang_type cmlt ON cmi.cinema_movie_lang_id = cmlt.cinema_movie_lang_type_id
JOIN system_const_val_map scvm ON cmi.cinema_movie_media_type = scvm.system_const_val_value
WHERE scvm.system_const_sub_module = 'new_media_type'
AND cmi.cinema_movie_start_datetime <= %s
AND cmi.cinema_movie_end_datetime > %s;
"""
# 获取新上映的影片
GET_NEW_MOVIE = """SELECT cmi.cinema_movie_alias,
cmi.cinema_movie_id,
cmi.cinema_movie_time,
cmlt.cinema_movie_lang_type_desc as language,
scvm.system_const_val_desc as media_type,
cmi.cinema_movie_start_datetime,
cmi.cinema_movie_end_datetime
FROM cinema_movie_info cmi
JOIN cinema_movie_lang_type cmlt ON cmi.cinema_movie_lang_id = cmlt.cinema_movie_lang_type_id
JOIN system_const_val_map scvm ON cmi.cinema_movie_media_type = scvm.system_const_val_value
WHERE scvm.system_const_sub_module = 'new_media_type'
AND cmi.cinema_movie_start_datetime >= %s
AND cmi.cinema_movie_start_datetime <= %s;"""
# 获取影厅支持的制式
GET_HALL_MEDIA_TYPE = """
SELECT chi.cinema_hall_name, cms.cinema_hall_id, scvm.system_const_val_desc as media_type
FROM cinema_movie_show cms
JOIN cinema_movie_info cmi ON cms.cinema_movie_id = cmi.cinema_movie_id
JOIN cinema_hall_info chi ON cms.cinema_hall_id = chi.cinema_hall_id
JOIN system_const_val_map scvm ON cmi.cinema_movie_media_type = scvm.system_const_val_value
WHERE scvm.system_const_sub_module = 'new_media_type'
AND chi.cinema_delete_flag = 0
GROUP BY chi.cinema_hall_id, media_type;
"""
# 获取模板日期
GET_TEMPLATE_DATE = """
SELECT cms.cinema_movie_show_start_date as template_date, COUNT(cinema_movie_show_id) as show_count
FROM cinema_movie_show cms
WHERE cms.cinema_del_flag = 1
AND cms.cinema_movie_show_start_time >= %s
AND cms.cinema_movie_show_start_time <= %s
GROUP BY cms.cinema_movie_show_start_date
HAVING show_count > (SELECT COUNT(chi.cinema_hall_id) FROM cinema_hall_info chi WHERE chi.cinema_delete_flag = 0) * %s
ORDER BY template_date DESC
LIMIT 1;
"""
# 获取目标日期的销售总额
GET_TOTAL_INCOME = """
SELECT SUM(md.income) as total_income
FROM (
-- 售票
SELECT SUM(IF(sl.cinema_ticket_status <> 3, sl.cinema_ticket_income, 0)) as income
FROM cinema_movie_show ms
LEFT JOIN cinema_sell_log sl ON ms.cinema_movie_show_id = sl.cinema_movie_show_id
JOIN cinema_movie_info mi ON ms.cinema_movie_id = mi.cinema_movie_id
JOIN cinema_hall_info hi ON ms.cinema_hall_id = hi.cinema_hall_id
WHERE ms.cinema_movie_show_joinflg = 0
AND ms.cinema_movie_show_start_time >= %s
AND ms.cinema_movie_show_start_time <= %s
AND ms.cinema_del_flag = 1
AND NOT (ms.cinema_movie_show_sold_num = 0 AND ms.cinema_movie_show_status <> 1)
UNION
-- 补登
SELECT SUM(sa.cinema_sell_add_total) as income
FROM cinema_sell_add sa
LEFT JOIN cinema_movie_show ms ON sa.cinema_sell_add_showid = ms.cinema_movie_show_id
LEFT JOIN cinema_hall_info hall ON ms.cinema_hall_id = hall.cinema_hall_id
LEFT JOIN cinema_show_policy_map spm ON sa.cinema_sell_add_showid = spm.cinema_movie_show_id AND
sa.cinema_sell_add_policy = spm.cinema_show_policy_map_id
JOIN cinema_hall_info hi ON ms.cinema_hall_id = hi.cinema_hall_id
JOIN cinema_movie_info mi ON ms.cinema_movie_id = mi.cinema_movie_id
WHERE ms.cinema_movie_show_joinflg = 0
AND ms.cinema_movie_show_start_time >= %s
AND ms.cinema_movie_show_start_time <= %s
AND ms.cinema_del_flag = 1
AND NOT (ms.cinema_movie_show_sold_num = 0 AND ms.cinema_movie_show_status <> 1)) md
"""

@ -0,0 +1,75 @@
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from ai.models import *
import datetime
import json
from ai.utils.show_database import GetData
from ai.utils.show_process import show_main_process
from django_redis import get_redis_connection
# Create your views here.
# 手动触发ai排片
@csrf_exempt
def manual_general_show(request):
result = show_main_process()
result_dict = {
'status': 'success' if result else 'fail',
'message': '生成成功' if result else '生成失败',
}
return JsonResponse(result_dict, json_dumps_params={'ensure_ascii': False})
# 更新指定日期的排片
@csrf_exempt
def get_cinema_show_result(request):
zz_code = request.GET.dict().get('cinema_code')
show_date = request.GET.dict().get('show_date')
print(zz_code, show_date)
cinema = TestCinema.objects.filter(zz_code=zz_code).first()
start = datetime.datetime.strftime(datetime.datetime.strptime(show_date, '%Y-%m-%d') + datetime.timedelta(hours=6),
'%Y-%m-%d %H:%M:%S')
end = datetime.datetime.strftime(
datetime.datetime.strptime(show_date, '%Y-%m-%d') + datetime.timedelta(hours=29, minutes=59, seconds=59),
'%Y-%m-%d %H:%M:%S')
if cinema:
data = GetData(json.loads(cinema.db_config))
show = data.get_show_data(start, end)
income = data.get_total_income(start, end)
try:
AiShow.objects.create(
cinema=cinema.name,
zz_code=cinema.zz_code,
show_date=show_date,
is_ai_show=False,
show=show,
sales=income,
prompt='',
result='',
message='',
take_time=0,
take_tokens='0'
)
except Exception as e:
print(e)
result_dict = {
'status': 'success',
'message': '生成成功',
}
return JsonResponse(result_dict, json_dumps_params={'ensure_ascii': False})
# 清除redis锁
@csrf_exempt
def clear_lock(request):
redis_conn = get_redis_connection()
redis_key = f'ai_show{datetime.date.today().strftime("%Y%m%d")}'
if redis_conn.exists(redis_key):
redis_conn.delete(redis_key)
result_dict = {
'status': 'success',
'message': '完成',
}
return JsonResponse(result_dict, json_dumps_params={'ensure_ascii': False})

@ -146,6 +146,8 @@ INSTALLED_APPS = [
'dspt_api', 'dspt_api',
'product', 'product',
'config', 'config',
'group',
'ai',
'django_celery_beat', # 定时任务 'django_celery_beat', # 定时任务
'django_celery_results', # 定时任务 'django_celery_results', # 定时任务
] ]

@ -33,6 +33,7 @@ urlpatterns = [
path('docs/', include_docs_urls(title='接口文档')), path('docs/', include_docs_urls(title='接口文档')),
path('prd/', include('product.urls')), path('prd/', include('product.urls')),
path('config/', include('config.urls')), path('config/', include('config.urls')),
path('ai/', include('ai.urls')),
] ]
websocket_urlpatterns = [ websocket_urlpatterns = [

Binary file not shown.

@ -0,0 +1,18 @@
# Generated by Django 4.2.7 on 2026-05-15 03:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('update', '0033_alter_clientrelease_md5'),
]
operations = [
migrations.AddField(
model_name='cinema',
name='url',
field=models.CharField(default=models.CharField(help_text='影院ip', max_length=20, verbose_name='影院ip'), help_text='影院url', max_length=50, verbose_name='影院url'),
),
]
Loading…
Cancel
Save