From 6f4e1b05fe5ea8b1d73b0d5c3c48affa317e984f Mon Sep 17 00:00:00 2001 From: rogersun Date: Tue, 23 Jun 2026 17:47:15 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E6=AC=A1=E6=8F=90=E4=BA=A4Gr?= =?UTF-8?q?oup=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dingxin_toolbox_drf/settings.py | 2 +- dingxin_toolbox_drf/urls.py | 1 + group/__init__.py | 0 group/admin.py | 3 + group/apps.py | 6 + group/migrations/0001_initial.py | 65 +++++++ .../migrations/0002_alter_groupdata_table.py | 17 ++ group/migrations/0003_groupruntime.py | 27 +++ .../0004_rename_functions_groupfuncs.py | 17 ++ .../0005_alter_groupdata_update_time.py | 18 ++ .../0006_alter_groupruntime_runtime_value.py | 18 ++ group/migrations/__init__.py | 0 group/models.py | 70 +++++++ group/serializers.py | 22 +++ group/tasks.py | 8 + group/tests.py | 3 + group/urls.py | 24 +++ group/utils/get_group_info.py | 39 ++++ group/utils/main_process.py | 33 ++++ group/utils/update_group_data.py | 101 ++++++++++ group/views.py | 36 ++++ group/集团SQL.txt | 172 ++++++++++++++++++ group/集团功能整理.xlsx | Bin 0 -> 21657 bytes 23 files changed, 681 insertions(+), 1 deletion(-) create mode 100644 group/__init__.py create mode 100644 group/admin.py create mode 100644 group/apps.py create mode 100644 group/migrations/0001_initial.py create mode 100644 group/migrations/0002_alter_groupdata_table.py create mode 100644 group/migrations/0003_groupruntime.py create mode 100644 group/migrations/0004_rename_functions_groupfuncs.py create mode 100644 group/migrations/0005_alter_groupdata_update_time.py create mode 100644 group/migrations/0006_alter_groupruntime_runtime_value.py create mode 100644 group/migrations/__init__.py create mode 100644 group/models.py create mode 100644 group/serializers.py create mode 100644 group/tasks.py create mode 100644 group/tests.py create mode 100644 group/urls.py create mode 100644 group/utils/get_group_info.py create mode 100644 group/utils/main_process.py create mode 100644 group/utils/update_group_data.py create mode 100644 group/views.py create mode 100644 group/集团SQL.txt create mode 100644 group/集团功能整理.xlsx diff --git a/dingxin_toolbox_drf/settings.py b/dingxin_toolbox_drf/settings.py index 6e6efaa..659c951 100644 --- a/dingxin_toolbox_drf/settings.py +++ b/dingxin_toolbox_drf/settings.py @@ -146,7 +146,7 @@ INSTALLED_APPS = [ 'dspt_api', 'product', 'config', - # 'group', + 'group', 'ai', 'django_celery_beat', # 定时任务 'django_celery_results', # 定时任务 diff --git a/dingxin_toolbox_drf/urls.py b/dingxin_toolbox_drf/urls.py index 98194f0..bda0ba2 100644 --- a/dingxin_toolbox_drf/urls.py +++ b/dingxin_toolbox_drf/urls.py @@ -34,6 +34,7 @@ urlpatterns = [ path('prd/', include('product.urls')), path('config/', include('config.urls')), path('ai/', include('ai.urls')), + path('group/', include('group.urls')), ] websocket_urlpatterns = [ diff --git a/group/__init__.py b/group/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/group/admin.py b/group/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/group/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/group/apps.py b/group/apps.py new file mode 100644 index 0000000..d9c36a4 --- /dev/null +++ b/group/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class GroupConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'group' diff --git a/group/migrations/0001_initial.py b/group/migrations/0001_initial.py new file mode 100644 index 0000000..93015ae --- /dev/null +++ b/group/migrations/0001_initial.py @@ -0,0 +1,65 @@ +# Generated by Django 4.2.7 on 2026-05-09 09:23 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Functions', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)), + ('func_model', models.CharField(max_length=150, verbose_name='模块')), + ('func_name', models.CharField(max_length=150, verbose_name='功能名称')), + ('exec_sql', models.TextField(verbose_name='查询sql')), + ('is_delete', models.BooleanField(default=False, verbose_name='删除标记')), + ], + options={ + 'verbose_name': '集团功能表', + 'verbose_name_plural': '集团功能表', + 'db_table': 'group_func', + 'db_table_comment': '集团功能表', + }, + ), + migrations.CreateModel( + name='Group', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)), + ('group_code', models.CharField(max_length=20, unique=True, verbose_name='集团编码')), + ('group_name', models.CharField(max_length=150, unique=True, verbose_name='集团编码')), + ('group_address', models.CharField(max_length=150, unique=True, verbose_name='集团地址')), + ('cinema_count', models.IntegerField(verbose_name='影院数量')), + ('is_delete', models.BooleanField(default=False, verbose_name='删除标记')), + ], + options={ + 'verbose_name': '集团信息表', + 'verbose_name_plural': '集团信息表', + 'db_table': 'group_info', + 'db_table_comment': '集团信息表', + }, + ), + migrations.CreateModel( + name='GroupData', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)), + ('data_count', models.IntegerField(verbose_name='数据量')), + ('data_lasttime', models.DateTimeField(verbose_name='数据最后更新时间')), + ('update_time', models.DateTimeField(verbose_name='数据更新时间')), + ('func', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='group.functions')), + ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='group.group')), + ], + options={ + 'verbose_name': '集团功能数据统计表', + 'verbose_name_plural': '集团功能数据统计表', + 'db_table': 'group_data_statistics', + 'db_table_comment': '集团功能数据统计表', + }, + ), + ] diff --git a/group/migrations/0002_alter_groupdata_table.py b/group/migrations/0002_alter_groupdata_table.py new file mode 100644 index 0000000..af466dc --- /dev/null +++ b/group/migrations/0002_alter_groupdata_table.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.7 on 2026-05-09 09:27 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0001_initial'), + ] + + operations = [ + migrations.AlterModelTable( + name='groupdata', + table='group_data', + ), + ] diff --git a/group/migrations/0003_groupruntime.py b/group/migrations/0003_groupruntime.py new file mode 100644 index 0000000..cf5c85d --- /dev/null +++ b/group/migrations/0003_groupruntime.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.7 on 2026-05-09 09:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0002_alter_groupdata_table'), + ] + + operations = [ + migrations.CreateModel( + name='GroupRuntime', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('runtime_key', models.CharField(max_length=150, verbose_name='运行时key')), + ('runtime_value', models.CharField(max_length=300, verbose_name='运行时value')), + ], + options={ + 'verbose_name': '集团数据运行时表', + 'verbose_name_plural': '集团数据运行时表', + 'db_table': 'group_runtime', + 'db_table_comment': '集团数据运行时表', + }, + ), + ] diff --git a/group/migrations/0004_rename_functions_groupfuncs.py b/group/migrations/0004_rename_functions_groupfuncs.py new file mode 100644 index 0000000..538ae04 --- /dev/null +++ b/group/migrations/0004_rename_functions_groupfuncs.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.7 on 2026-06-23 02:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0003_groupruntime'), + ] + + operations = [ + migrations.RenameModel( + old_name='Functions', + new_name='GroupFuncs', + ), + ] diff --git a/group/migrations/0005_alter_groupdata_update_time.py b/group/migrations/0005_alter_groupdata_update_time.py new file mode 100644 index 0000000..b22f33e --- /dev/null +++ b/group/migrations/0005_alter_groupdata_update_time.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2026-06-23 06:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0004_rename_functions_groupfuncs'), + ] + + operations = [ + migrations.AlterField( + model_name='groupdata', + name='update_time', + field=models.DateTimeField(auto_now=True, verbose_name='数据更新时间'), + ), + ] diff --git a/group/migrations/0006_alter_groupruntime_runtime_value.py b/group/migrations/0006_alter_groupruntime_runtime_value.py new file mode 100644 index 0000000..2e4d29c --- /dev/null +++ b/group/migrations/0006_alter_groupruntime_runtime_value.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2026-06-23 08:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('group', '0005_alter_groupdata_update_time'), + ] + + operations = [ + migrations.AlterField( + model_name='groupruntime', + name='runtime_value', + field=models.TextField(verbose_name='运行时value'), + ), + ] diff --git a/group/migrations/__init__.py b/group/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/group/models.py b/group/models.py new file mode 100644 index 0000000..802dc3a --- /dev/null +++ b/group/models.py @@ -0,0 +1,70 @@ +from django.db import models + + +# Create your models here. + +class Group(models.Model): + id = models.AutoField(primary_key=True, auto_created=True) + group_code = models.CharField(verbose_name='集团编码', max_length=20, null=False, unique=True) + group_name = models.CharField(verbose_name='集团编码', max_length=150, null=False, unique=True) + group_address = models.CharField(verbose_name='集团地址', max_length=150, null=False, unique=True) + cinema_count = models.IntegerField(verbose_name='影院数量') + is_delete = models.BooleanField(verbose_name="删除标记", default=False) + + def __str__(self): + return self.group_code + + class Meta: + verbose_name = '集团信息表' + verbose_name_plural = '集团信息表' + db_table_comment = '集团信息表' + db_table = 'group_info' + + +class GroupFuncs(models.Model): + id = models.AutoField(primary_key=True, auto_created=True) + func_model = models.CharField(verbose_name='模块', max_length=150, null=False) + func_name = models.CharField(verbose_name='功能名称', max_length=150, null=False) + exec_sql = models.TextField(verbose_name='查询sql', null=False) + is_delete = models.BooleanField(verbose_name="删除标记", default=False) + + def __str__(self): + return self.func_name + + class Meta: + verbose_name = '集团功能表' + verbose_name_plural = '集团功能表' + db_table_comment = '集团功能表' + db_table = 'group_func' + + +class GroupData(models.Model): + id = models.AutoField(primary_key=True, auto_created=True) + group = models.ForeignKey("Group", on_delete=models.CASCADE, null=False) + func = models.ForeignKey("GroupFuncs", on_delete=models.CASCADE, null=False) + data_count = models.IntegerField(verbose_name='数据量', null=False) + data_lasttime = models.DateTimeField(verbose_name='数据最后更新时间', null=False) + update_time = models.DateTimeField(verbose_name="数据更新时间", auto_now=True, null=False) + + def __str__(self): + return self.id + + class Meta: + verbose_name = '集团功能数据统计表' + verbose_name_plural = '集团功能数据统计表' + db_table_comment = '集团功能数据统计表' + db_table = 'group_data' + + +class GroupRuntime(models.Model): + runtime_key = models.CharField(verbose_name='运行时key', max_length=150, null=False) + runtime_value = models.TextField(verbose_name='运行时value', null=False) + + def __str__(self): + return self.runtime_key + + class Meta: + verbose_name = '集团数据运行时表' + verbose_name_plural = '集团数据运行时表' + db_table_comment = '集团数据运行时表' + db_table = 'group_runtime' diff --git a/group/serializers.py b/group/serializers.py new file mode 100644 index 0000000..712e719 --- /dev/null +++ b/group/serializers.py @@ -0,0 +1,22 @@ +from rest_framework import serializers +from group.models import * + +class GroupInfoSerializer(serializers.ModelSerializer): + class Meta: + model = Group + fields = '__all__' + +class GroupFuncsSerializer(serializers.ModelSerializer): + class Meta: + model = GroupFuncs + fields = '__all__' + +class GroupDataStatisticsSerializer(serializers.ModelSerializer): + class Meta: + model = GroupData + fields = '__all__' + +class GroupRuntimeSerializer(serializers.ModelSerializer): + class Meta: + model = GroupRuntime + fields = '__all__' \ No newline at end of file diff --git a/group/tasks.py b/group/tasks.py new file mode 100644 index 0000000..0e4316d --- /dev/null +++ b/group/tasks.py @@ -0,0 +1,8 @@ +from celery import shared_task +from group.utils.main_process import main_update_process + +@shared_task(time_limit=10*60, soft_time_limit=10*60) +def group_data_task(): + main_update_process() + print("task success") + return True diff --git a/group/tests.py b/group/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/group/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/group/urls.py b/group/urls.py new file mode 100644 index 0000000..f68b7ac --- /dev/null +++ b/group/urls.py @@ -0,0 +1,24 @@ +""" +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 group.views import * + +urlpatterns = [ + path('get_group_data', get_group_data), + path('get_group_func', GroupFuncsViewSet.as_view({'get': 'list'}), name='get_group_func'), +] diff --git a/group/utils/get_group_info.py b/group/utils/get_group_info.py new file mode 100644 index 0000000..8ca6c13 --- /dev/null +++ b/group/utils/get_group_info.py @@ -0,0 +1,39 @@ +import pymysql +from pymysql.cursors import DictCursor +from group.models import * + +group_database_info = { + 'host': '10.10.1.52', + 'port': 3306, + 'user': 'dgp_read', + 'password': '1d8qB83F9s63kpS', +} + + +class GetGroupInfo: + def __init__(self): + self.db_conn = pymysql.connect(**group_database_info) + self.db_cursor = self.db_conn.cursor(cursor=DictCursor) + self.group_list = self.get_groups() + + # 如果集团数据不存在则返回False + def is_group_exist(self, group_code): + if Group.objects.filter(group_code=group_code).exists(): + return Group.objects.get(group_code=group_code) + else: + for group in self.group_list: + if group.get('group_code') == group_code: + return Group.objects.create(group_code=group_code, + group_name=group.get('group_name'), + group_address=f"https://plus.yinghezhong.com/{group_code}", + cinema_count=group.get('cinema_count')) + return False + + # 查询集团数据库获取集团信息 + def get_groups(self): + group_info_sql = """SELECT bcg.group_code, bcg.group_name, COUNT(bc.group_code) as cinema_count FROM dgp_bd_db.bd_cinema_group as bcg LEFT JOIN dgp_bd_db.bd_cinema as bc ON bcg.group_code = bc.group_code WHERE bcg.status = 1 AND bc.status = 1 AND length(bcg.group_code) = 8 AND bcg.group_code != '87879012' GROUP BY bcg.group_code ORDER BY cinema_count DESC;""" + self.db_cursor.execute(group_info_sql) + group_list = self.db_cursor.fetchall() + self.db_cursor.close() + self.db_conn.close() + return group_list diff --git a/group/utils/main_process.py b/group/utils/main_process.py new file mode 100644 index 0000000..13986a9 --- /dev/null +++ b/group/utils/main_process.py @@ -0,0 +1,33 @@ +from datetime import datetime +import json +from group.models import * +from group.utils.update_group_data import * + + +def main_update_process(): + # 遍历功能列表并连接DGP线上数据库获取统计数据 + func_list = GroupFuncs.objects.filter(is_delete=False).all() + ugd = UpdateGroupData() + failed_list = [] + for func in func_list: + result = ugd.update_group_data(func) + if result is not True: + failed_list.extend(result) + + # 记录最后运行时间以及最后的错误信息 + if runtime := GroupRuntime.objects.get(runtime_key='update_datetime'): + runtime.runtime_value = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S') + runtime.save() + else: + GroupRuntime.objects.create(runtime_key='update_datetime', + runtime_value=datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')) + if len(failed_list) > 0: + print("failed_list", failed_list) + if fail := GroupRuntime.objects.get(runtime_key='update_datetime'): + fail.runtime_value = json.dumps(failed_list) + runtime.save() + else: + GroupRuntime.objects.create(runtime_key='failed_data', runtime_value=json.dumps(failed_list)) + + # 生成影院功能对应数据字典并写入redis + set_group_data_to_redis() diff --git a/group/utils/update_group_data.py b/group/utils/update_group_data.py new file mode 100644 index 0000000..c9f8d25 --- /dev/null +++ b/group/utils/update_group_data.py @@ -0,0 +1,101 @@ +import pymysql +import json +from datetime import datetime +from pymysql.cursors import DictCursor +from group.models import * +from group.models import Group +from group.utils.get_group_info import * +from django_redis import get_redis_connection + +group_database_info = { + 'host': '10.10.1.52', + 'port': 3306, + 'user': 'dgp_read', + 'password': '1d8qB83F9s63kpS', +} + + +class UpdateGroupData: + def __init__(self): + self.db_conn = pymysql.connect(**group_database_info) + self.db_cursor = self.db_conn.cursor(cursor=DictCursor) + + # 根据func获取集团数据,如果集团不存在则插入集团数据 + def update_group_data(self, func): + group_info = GetGroupInfo() + self.db_cursor.execute(func.exec_sql) + data = self.db_cursor.fetchall() + failed_list = [] + for d in data: + code = d.get('group_code') + print(func.func_model, func.func_name, d.get('group_code'), d.get('data'), d.get('last_time')) + if g_obj := group_info.is_group_exist(code): + print('找到集团插入数据') + if GroupData.objects.filter(group=g_obj, func=func).first() is not None: + gd = GroupData.objects.get(group=g_obj, func=func) + gd.data_count = d.get('data') + gd.data_lasttime = d.get('last_time') + gd.save() + else: + GroupData.objects.create(group=g_obj, + func=func, + data_count=d.get('data'), + data_lasttime=d.get('last_time')) + + else: + print('没有找到集团') + failed_list.append({'func_name': func.func_name, 'group_code': d.get('group_code')}) + if len(failed_list) > 0: + return failed_list + return True + + +# 组装返回数据并写入json +def set_group_data_to_redis(): + # 准备数据 + groups = Group.objects.order_by('-cinema_count').all() + funcs = GroupFuncs.objects.all() + result = {'groups_data': [], + 'update_datetime': GroupRuntime.objects.filter(runtime_key='update_datetime').first().runtime_value, } + for group in groups: + print(group.group_code) + group_data = { + 'group_info': { + 'group_code': group.group_code, + 'group_name': group.group_name, + 'group_address': group.group_address, + 'cinema_count': group.cinema_count + }, + 'group_data': [] + } + for func in funcs: + print(func.func_name) + data = GroupData.objects.filter(group=group, func=func).first() + if data is not None: + data_count = data.data_count + data_lasttime = datetime.strftime(data.data_lasttime, '%Y-%m-%d %H:%M:%S') + else: + data_count = 0 + data_lasttime = '' + group_data['group_data'].append({ + 'func_id': func.id, + 'func_model': func.func_model, + 'func_name': func.func_name, + 'data_count': data_count, + 'data_lasttime': data_lasttime, + }) + print({ + 'func_model': func.func_model, + 'func_name': func.func_name, + 'data_count': data_count, + 'data_lasttime': data_lasttime, + }) + result['groups_data'].append(group_data) + # 写入Redis + print("写入Redis") + redis_conn = get_redis_connection() + redis_key = f"group_data_{datetime.strftime(datetime.now(), '%Y-%m-%d')}" + if redis_conn.exists(redis_key): + redis_conn.delete(redis_key) + redis_conn.set(redis_key, json.dumps(result), 3600 * 48) + return result diff --git a/group/views.py b/group/views.py new file mode 100644 index 0000000..07ef75c --- /dev/null +++ b/group/views.py @@ -0,0 +1,36 @@ +from django.http import JsonResponse +from django_redis import get_redis_connection +from django.shortcuts import render +from django.views.decorators.csrf import csrf_exempt +from rest_framework import viewsets + +from group.serializers import GroupFuncsSerializer +from group.utils.update_group_data import * +from group.utils.main_process import main_update_process + + +# Create your views here. +@csrf_exempt +def get_group_data(request): + redis_conn = get_redis_connection() + redis_key = f"group_data_{datetime.strftime(datetime.now(), '%Y-%m-%d')}" + if not redis_conn.exists(redis_key): + main_update_process() + if redis_conn.exists(redis_key): + print('获取Redis数据') + redis_value = json.loads(redis_conn.get(redis_key)) + result_dict = { + 'status': 'success', + 'data': redis_value, + } + else: + result_dict = { + 'status': 'fail', + 'data': [], + } + return JsonResponse(result_dict, json_dumps_params={'ensure_ascii': False}) + + +class GroupFuncsViewSet(viewsets.ModelViewSet): + queryset = GroupFuncs.objects.all() + serializer_class = GroupFuncsSerializer diff --git a/group/集团SQL.txt b/group/集团SQL.txt new file mode 100644 index 0000000..b72597d --- /dev/null +++ b/group/集团SQL.txt @@ -0,0 +1,172 @@ +# 票务管理 +# 影片库 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_film_db.f_cinema_film GROUP BY group_code; + +# 价格策略 +# 票价组 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(gmt_modified, gmt_create)) as last_time FROM dgp_ticket_db.ticket_group GROUP BY group_code; + +# 排片指导 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_film_db.f_instruction GROUP BY group_code; + +# 票房补登审核 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(auditor_time, add_time)) as last_time FROM dgp_film_db.f_box_office_add GROUP BY group_code; + +# 客群管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(gmt_modified, gmt_create)) as last_time FROM dgp_membership_db.customer_group WHERE customer_group_name NOT IN ('活跃用户', '促活用户', '潜在用户') GROUP BY group_code; + +# 会员卡活动 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(gmt_modified, gmt_create)) as last_time FROM dgp_membership_db.membership_activity GROUP BY group_code; + +# 会员卡 +# 订单管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(gmt_modified, gmt_create)) as last_time FROM dgp_membership_db.card_order GROUP BY group_code; + +# 会员卡 +# 等级管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(gmt_modified, gmt_create)) as last_time FROM dgp_membership_db.group_card_level WHERE is_deleted=0 GROUP BY group_code; + +# 卖品管理 +# 售卖分组 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_sell_group WHERE status=1 GROUP BY group_code; + +# 卖品管理 +# 品牌管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_brands WHERE status=1 GROUP BY group_code; + +# 库房管理 +# 仓库 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_warehouse WHERE status=1 GROUP BY group_code; + +# 库房管理 +# 货架 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_shelf WHERE status=1 GROUP BY group_code; + +# 品项管理 +# 原材料 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_goods_bill WHERE status=1 AND goods_attribute=1 AND show_flag = 1 GROUP BY group_code; + +# 品项管理 +# 单品 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_goods_bill WHERE status=1 AND goods_attribute=2 AND show_flag = 1 GROUP BY group_code; + +# 品项管理 +# 合成品 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_goods_bill WHERE status=1 AND goods_attribute=3 AND show_flag = 1 GROUP BY group_code; + +# 品项管理 +# 套餐 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_goods_bill WHERE status=1 AND goods_attribute=4 AND show_flag = 1 GROUP BY group_code; + +# 品项管理 +# 售卖键管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_goods_rule_bill WHERE status=1 AND show_flag=1 GROUP BY group_code; + +# 品项管理 +# 售卖规则 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_sell_rule_bill WHERE status=1 GROUP BY group_code; + +# 供应商管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_supplier_bill WHERE status=1 GROUP BY group_code; + +# 采购管理 +# 代购协议 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_purchase_agreement WHERE status=1 GROUP BY group_code; + +# 调货管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_retail_db.r_goods_transfer WHERE status=1 GROUP BY group_code; + +# 联名卡 +# 卡级别管理 +SELECT group_no as group_code, COUNT(*) as data, MAX(IFNULL(updated_time, created_time)) as last_time FROM dgp_card_db.card_level WHERE status=1 GROUP BY group_no; + +# 联名卡 +# 活动管理 +SELECT group_no as group_code, COUNT(*) as data, MAX(IFNULL(updated_time, created_time)) as last_time FROM dgp_card_db.card_level_activity WHERE status=1 GROUP BY group_no; + +# 预售券 +# 券类设置 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_coupon_db.coupon_strategy_bill WHERE audit_status IN (0,1,2,3) AND delete_flag = 0 GROUP BY group_code; + +# 预售券 +# 销售单管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_coupon_db.coupon_sell_order WHERE delete_flag=0 AND coupon_strategy_id IN (SELECT coupon_strategy_id FROM dgp_coupon_db.coupon_strategy_bill WHERE audit_status IN (0,1,2,3) AND delete_flag = 0) GROUP BY group_code; + +# 预售券 +# 券码订单 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_coupon_db.coupon_barcode_order WHERE delete_flag=0 GROUP BY group_code; + +# 预售券 +# 影城券审核 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_coupon_db.coupon_sell_order_cinema GROUP BY group_code; + +# 预售券 +# 平台券管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_coupon_db.coupon_platform_refund GROUP BY group_code; + +# 用户管理 +# 总部岗位 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.role_group_role WHERE delete_flag = 0 AND role_type=0 AND source_type=1 AND show_flag=1 AND levels_flag=2 GROUP BY group_code; + +# 用户管理 +# 影院岗位 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.role_group_role WHERE delete_flag = 0 AND role_type=0 AND source_type=1 AND show_flag=1 AND levels_flag=8 GROUP BY group_code; + +# 用户管理 +# 总部账号 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.role_user WHERE delete_flag=0 AND user_type=1 AND show_flag=1 AND status=1 GROUP BY group_code; + +# 用户管理 +# 影院账号 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.role_user WHERE delete_flag=0 AND user_type=3 AND show_flag=1 AND status=1 GROUP BY group_code; + +# 影院管理 +# 影院管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.bd_cinema WHERE status <> 3 GROUP BY group_code; + +# 影院管理 +# 跨集团影院管理 +SELECT interflow_group as group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.bd_cross_cinema WHERE delete_flag=0 GROUP BY interflow_group; + +# 影院管理 +# 影院指标管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.bd_kpi_target GROUP BY group_code; + +# 影院管理 +# 客户管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.bd_customer WHERE del_flag=0 GROUP BY group_code; + +# 影院管理 +# 时段设置 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.bd_cinema_time_rule WHERE del_flag=0 GROUP BY group_code; + +# 影院管理 +# 招待管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.bd_cinema_entertain WHERE delete_flag=0 GROUP BY group_code; + +# 影院管理 +# 补贴方管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.bd_cinema_subsidies WHERE status=1 GROUP BY group_code; + +# 影院管理 +# 银行管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.bd_bank GROUP BY group_code; + +# 影院管理 +# 第三方卡管理 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_bd_db.customer_card_info WHERE delete_flag = 1 GROUP BY group_code; + +# 审批流 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(update_time, create_time)) as last_time FROM dgp_workflow_db.wf_flow_definition WHERE status = 1 GROUP BY group_code; + +# 广告管理 +# 设备列表 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(gmt_modified, gmt_create)) as last_time FROM dgp_ad_db.equipment GROUP BY group_code; + +# 广告管理 +# 模板列表 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(gmt_modified, gmt_create)) as last_time FROM dgp_ad_db.template GROUP BY group_code; + +# 广告管理 +# 素材列表 +SELECT group_code, COUNT(*) as data, MAX(IFNULL(gmt_modified, gmt_create)) as last_time FROM dgp_ad_db.material GROUP BY group_code; diff --git a/group/集团功能整理.xlsx b/group/集团功能整理.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..27938a4e2c69679ccc98708bf4e4c981d03bea9e GIT binary patch literal 21657 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⪚8XoAZMm3vbnH+o~3%sLTKnlArZQ4K`l1Mvx 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 A%>V!Z literal 0 HcmV?d00001