最新版生产环境后端
This commit is contained in:
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
db.sqlite3
|
||||
/venv/
|
||||
/env/
|
||||
.env
|
||||
staticfiles/
|
||||
media/
|
||||
*.log
|
||||
.DS_Store
|
||||
migrations/00*.py
|
||||
2
_init_.py
Normal file
2
_init_.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# 3. 在项目的 `__init__.py` 文件(与manage.py同级)中,添加两行代码
|
||||
# 文件路径:a_long_dianjing/__init__.py
|
||||
8
a_long_dianjing/__init__.py
Normal file
8
a_long_dianjing/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""
|
||||
阿龙电竞 - Django项目初始化
|
||||
确保Celery在Django启动时自动加载
|
||||
"""
|
||||
|
||||
from .celery import app as celery_app
|
||||
|
||||
__all__ = ('celery_app',)
|
||||
16
a_long_dianjing/asgi.py
Normal file
16
a_long_dianjing/asgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for a_long_dianjing project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'a_long_dianjing.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
200
a_long_dianjing/celery.py
Normal file
200
a_long_dianjing/celery.py
Normal file
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
阿龙电竞 - Celery定时任务主配置
|
||||
生产环境就绪版本,支持精确延时任务和周期性任务
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import os
|
||||
from celery import Celery
|
||||
from celery.schedules import crontab
|
||||
# 在 Django Shell 中执行
|
||||
|
||||
|
||||
# 设置Django默认设置模块
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'a_long_dianjing.settings')
|
||||
|
||||
# 创建Celery应用实例
|
||||
app = Celery('a_long_dianjing')
|
||||
|
||||
# 从Django settings中加载Celery配置(CELERY_前缀)
|
||||
app.config_from_object('django.conf:settings', namespace='CELERY')
|
||||
|
||||
# 自动发现所有已注册app中的tasks.py文件
|
||||
#app.autodiscover_tasks()
|
||||
app.autodiscover_tasks(['yonghu.tasks', 'yonghu.ranking_tasks', 'dingdan.tasks', 'peizhi.tasks'])
|
||||
|
||||
|
||||
|
||||
# 配置周期性任务(Celery Beat Schedule)
|
||||
app.conf.beat_schedule = {
|
||||
|
||||
|
||||
|
||||
|
||||
# 月榜数据转移(每月最后一天23:50执行)
|
||||
'yuebang_zhuanyi': {
|
||||
'task': 'yonghu.ranking_tasks.zhuanyi_yuebang',
|
||||
'schedule': crontab(hour=23, minute=50, day_of_month='28-31'), # ✅ 修改这里
|
||||
'options': {'queue': 'periodic_tasks', 'priority': 7},
|
||||
},
|
||||
|
||||
|
||||
|
||||
# 日榜数据转移(每天23:55执行,在清零前)
|
||||
'ribang_zhuanyi': {
|
||||
'task': 'yonghu.ranking_tasks.zhuanyi_ribang',
|
||||
#'schedule': crontab(minute='*/1'), # 每分钟执行一次,测试用
|
||||
'schedule': crontab(hour=23, minute=55), # 每天23:55
|
||||
'options': {'queue': 'periodic_tasks', 'priority': 7}, # 优先级高于清零任务
|
||||
},
|
||||
|
||||
|
||||
|
||||
# 清理旧历史数据(每月1号凌晨1点执行)
|
||||
'qingli_jiulishuju': {
|
||||
'task': 'yonghu.ranking_tasks.qingli_jiulishuju',
|
||||
'schedule': crontab(hour=1, minute=0, day_of_month=1), # 每月1号凌晨1点
|
||||
'options': {'queue': 'periodic_tasks', 'priority': 4},
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 🔥 补偿检查任务(每5分钟运行)
|
||||
'check_order_expire_task': {
|
||||
'task': 'dingdan.tasks.check_order_expire_task',
|
||||
'schedule': crontab(minute='*/5000'),
|
||||
'options': {'queue': 'order_tasks', 'priority': 3},
|
||||
},
|
||||
|
||||
|
||||
# 1. 每日凌晨0点执行 - 清零任务
|
||||
'daily_reset_task': {
|
||||
'task': 'yonghu.tasks.daily_reset_task',
|
||||
'schedule': crontab(hour=0, minute=0), # 每天0点
|
||||
'options': {
|
||||
'queue': 'periodic_tasks',
|
||||
'priority': 5
|
||||
},
|
||||
'args': (),
|
||||
'kwargs': {}
|
||||
},
|
||||
|
||||
# 2. 每月1日凌晨0点执行 - 月度清零任务
|
||||
'monthly_reset_task': {
|
||||
'task': 'yonghu.tasks.monthly_reset_task',
|
||||
'schedule': crontab(hour=0, minute=0, day_of_month=1), # 每月1日0点
|
||||
'options': {
|
||||
'queue': 'periodic_tasks',
|
||||
'priority': 5
|
||||
},
|
||||
'args': (),
|
||||
'kwargs': {}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 4. 每天凌晨0点5分清理收支记录
|
||||
'daily_sz_reset_task': {
|
||||
'task': 'peizhi.tasks.daily_sz_reset_task',
|
||||
'schedule': crontab(hour=0, minute=5), # 每天0点5分
|
||||
'options': {
|
||||
'queue': 'periodic_tasks',
|
||||
'priority': 4
|
||||
},
|
||||
'args': (),
|
||||
'kwargs': {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# 配置周期性任务(Celery Beat Schedule)
|
||||
'''app.conf.beat_schedule = {
|
||||
# 1. 每日凌晨0点执行 - 清零任务
|
||||
'daily_reset_task': {
|
||||
'task': 'yonghu.tasks.daily_reset_task',
|
||||
'schedule': crontab(hour=0, minute=0), # 每天0点
|
||||
'options': {
|
||||
'queue': 'periodic_tasks',
|
||||
'priority': 5
|
||||
},
|
||||
'args': (),
|
||||
'kwargs': {}
|
||||
},
|
||||
|
||||
# 2. 每月1日凌晨0点执行 - 月度清零任务
|
||||
'monthly_reset_task': {
|
||||
'task': 'yonghu.tasks.monthly_reset_task',
|
||||
'schedule': crontab(hour=0, minute=0, day_of_month=1), # 每月1日0点
|
||||
'options': {
|
||||
'queue': 'periodic_tasks',
|
||||
'priority': 5
|
||||
},
|
||||
'args': (),
|
||||
'kwargs': {}
|
||||
},
|
||||
|
||||
# 3. 每5分钟检查一次待处理订单 - 作为补偿机制
|
||||
'check_order_expire_task': {
|
||||
'task': 'dingdan.tasks.check_order_expire_task',
|
||||
'schedule': crontab(minute='*/5'), # 每5分钟
|
||||
'options': {
|
||||
'queue': 'order_tasks',
|
||||
'priority': 3
|
||||
},
|
||||
'args': (),
|
||||
'kwargs': {}
|
||||
},
|
||||
|
||||
# 4. 每天凌晨0点5分清理收支记录
|
||||
'daily_sz_reset_task': {
|
||||
'task': 'peizhi.tasks.daily_sz_reset_task',
|
||||
'schedule': crontab(hour=0, minute=5), # 每天0点5分
|
||||
'options': {
|
||||
'queue': 'periodic_tasks',
|
||||
'priority': 4
|
||||
},
|
||||
'args': (),
|
||||
'kwargs': {}
|
||||
}
|
||||
}'''
|
||||
|
||||
# 时区设置
|
||||
app.conf.timezone = 'Asia/Shanghai'
|
||||
app.conf.enable_utc = True
|
||||
|
||||
# 队列路由配置
|
||||
app.conf.task_routes = {
|
||||
'dingdan.tasks.*': {'queue': 'order_tasks'},
|
||||
'yonghu.tasks.*': {'queue': 'periodic_tasks'},
|
||||
'peizhi.tasks.*': {'queue': 'periodic_tasks'},
|
||||
}
|
||||
|
||||
# 任务序列化
|
||||
app.conf.accept_content = ['json']
|
||||
app.conf.task_serializer = 'json'
|
||||
app.conf.result_serializer = 'json'
|
||||
|
||||
# 任务超时设置
|
||||
app.conf.task_time_limit = 300 # 任务最大执行时间300秒
|
||||
app.conf.task_soft_time_limit = 240 # 软超时240秒
|
||||
|
||||
# Worker并发设置
|
||||
app.conf.worker_concurrency = 4
|
||||
app.conf.worker_prefetch_multiplier = 1
|
||||
|
||||
# 任务确认设置
|
||||
app.conf.task_acks_late = True
|
||||
app.conf.worker_disable_rate_limits = True
|
||||
|
||||
# 结果过期时间
|
||||
app.conf.result_expires = 3600 # 任务结果保留1小时
|
||||
10
a_long_dianjing/celery_app.py
Normal file
10
a_long_dianjing/celery_app.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# celery_app.py
|
||||
import os
|
||||
from celery import Celery
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'a_long_dianjing.settings')
|
||||
|
||||
app = Celery('a_long_dianjing')
|
||||
app.config_from_object('django.conf:settings', namespace='CELERY')
|
||||
# 手动导入任务模块,避免与已有 tasks.py 冲突
|
||||
app.autodiscover_tasks(['dingdan.tongzhi_tasks'])
|
||||
541
a_long_dianjing/settings.py
Normal file
541
a_long_dianjing/settings.py
Normal file
@@ -0,0 +1,541 @@
|
||||
"""
|
||||
Django settings for a_long_dianjing project.
|
||||
开发环境配置 - 参照旧项目结构精简
|
||||
"""
|
||||
from pathlib import Path
|
||||
from datetime import timedelta
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
SECRET_KEY = 'django-insecure-9+xu)m%#!#qblgby5sq!sjyg5nj9lir78+&*f$28$459#yktej' # 开发环境可暂用,生产必须改
|
||||
DEBUG = False # 开发环境为 True
|
||||
ALLOWED_HOSTS = ['localhost', '127.0.0.1','www.abas.asia'] # 根据开发前端地址可添加
|
||||
|
||||
# Application definition
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
# 第三方应用 (根据 requirements.txt 已安装的)
|
||||
'rest_framework',
|
||||
'rest_framework_simplejwt',
|
||||
'corsheaders',
|
||||
'django_celery_results',
|
||||
# 你的自定义应用 (按已创建的拼音命名)
|
||||
'celery',
|
||||
'yonghu',
|
||||
'dingdan',
|
||||
'shangpin',
|
||||
'peizhi',
|
||||
'houtai',
|
||||
# 'games', # 【待确认】你提到 games 还没创建,暂不添加
|
||||
'utils',
|
||||
'shangdian',
|
||||
'dengji',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'corsheaders.middleware.CorsMiddleware', # 跨域中间件放最前
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware', # 默认开启,如API无需可注释
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
# 'guanapi.middleware.OpLogMiddleware', # 【待确认】这是旧项目的自定义中间件,新项目需要吗?
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'a_long_dianjing.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates'],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'a_long_dianjing.wsgi.application'
|
||||
|
||||
# Database
|
||||
# 根据你提供的信息配置 (本地开发,端口3307)
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'xaio_cheng_xu', # 你创建的数据库名
|
||||
'USER': 'root', # 你的本地MySQL用户名
|
||||
#'PASSWORD': 'Dujieduze6363....', # 你的本地MySQL密码
|
||||
#'PASSWORD': 'ahgs673g2h63hgawwdqw',
|
||||
#'HOST': '172.19.0.2',
|
||||
'HOST': '172.17.0.15',
|
||||
'PASSWORD':'sajksh.sdfGH3YUge.wjkd+',
|
||||
#'HOST': 'sh-cdb-hmyxpv1m.sql.tencentcdb.com',
|
||||
'PORT': '3306', # 注意端口是3307
|
||||
#'PORT': '20950',
|
||||
'OPTIONS': {
|
||||
'charset': 'utf8mb4',
|
||||
'init_command': "SET time_zone='+08:00'", # 保持与旧项目一致
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Password validation
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
# Internationalization
|
||||
LANGUAGE_CODE = 'zh-Hans' # 使用中文
|
||||
TIME_ZONE = 'Asia/Shanghai'
|
||||
USE_I18N = True
|
||||
USE_TZ = False # 保持与旧项目一致,不使用UTC时区
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
STATIC_URL = 'static/'
|
||||
# 开发环境静态文件目录,可参照旧项目添加 MEDIA 配置
|
||||
# MEDIA_URL = 'media/'
|
||||
# MEDIA_ROOT = BASE_DIR / 'media'
|
||||
|
||||
# Default primary key field type
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
# ==================== 以下为第三方库及自定义配置 ====================
|
||||
# settings.py
|
||||
# 修改SIMPLE_JWT配置
|
||||
SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(days=36500),
|
||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=36500),
|
||||
'ROTATE_REFRESH_TOKENS': False,
|
||||
'BLACKLIST_AFTER_ROTATION': False,
|
||||
'UPDATE_LAST_LOGIN': False,
|
||||
'ALGORITHM': 'HS256',
|
||||
'SIGNING_KEY': SECRET_KEY,
|
||||
'VERIFYING_KEY': None,
|
||||
'AUDIENCE': None,
|
||||
'ISSUER': None,
|
||||
'JWK_URL': None,
|
||||
'LEEWAY': 0,
|
||||
'AUTH_HEADER_TYPES': ('Bearer',),
|
||||
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
|
||||
# 修改这里:使用yonghuid而不是id
|
||||
'USER_ID_FIELD': 'yonghuid',
|
||||
'USER_ID_CLAIM': 'yonghuid',
|
||||
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
|
||||
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
|
||||
'TOKEN_TYPE_CLAIM': 'token_type',
|
||||
'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',
|
||||
'JTI_CLAIM': 'jti',
|
||||
}
|
||||
|
||||
# 添加自定义认证后端
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
]
|
||||
|
||||
# 必须指定用户模型(即使它不继承AbstractBaseUser)
|
||||
AUTH_USER_MODEL = 'yonghu.UserMain'
|
||||
|
||||
|
||||
# DRF配置(也非常简单)
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
||||
),
|
||||
'DEFAULT_PERMISSION_CLASSES': (
|
||||
'rest_framework.permissions.IsAuthenticated',
|
||||
),
|
||||
'DEFAULT_THROTTLE_CLASSES': [
|
||||
'rest_framework.throttling.AnonRateThrottle', # 匿名用户限制
|
||||
'rest_framework.throttling.UserRateThrottle', # 登录用户限制
|
||||
],
|
||||
'DEFAULT_THROTTLE_RATES': {
|
||||
'anon': '100/minute', # 匿名用户每分钟100次
|
||||
'user': '1000/minute', # 登录用户每分钟1000次
|
||||
},
|
||||
# 🔥 关键:使用自定义异常处理器
|
||||
'EXCEPTION_HANDLER': 'utils.exception_handler.custom_exception_handler',
|
||||
|
||||
# 🔥 生产环境只返回JSON,不返回HTML(更安全)
|
||||
'DEFAULT_RENDERER_CLASSES': [
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# settings.py
|
||||
# H5静态页面域名配置
|
||||
H5_DOMAIN = 'https://www.abas.asia:666' # 生产环境
|
||||
# H5_DOMAIN = 'https://test-h5.yourdomain.com' # 测试环境
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# CORS settings
|
||||
CORS_ALLOWED_ORIGINS = [
|
||||
"http://localhost:8080", # 假设前端开发服务器地址
|
||||
"http://127.0.0.1:8080",
|
||||
"https://www.abas.asia",
|
||||
#"http://192.168.240.1:5173/",
|
||||
"https://www.abas.asia:666",
|
||||
"https://www.abas.asia:1234",
|
||||
"https://www.abas.asia:963",
|
||||
"http://localhost:5174",
|
||||
"http://localhost:5173",
|
||||
"http://localhost:3000",
|
||||
"http://localhost:3001",
|
||||
|
||||
|
||||
#"www.abas.asia",
|
||||
]
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
CORS_ALLOW_METHODS = (
|
||||
"GET",
|
||||
"POST",
|
||||
"PUT",
|
||||
"PATCH",
|
||||
"DELETE",
|
||||
"OPTIONS",
|
||||
)
|
||||
CORS_ALLOW_HEADERS = [
|
||||
"accept",
|
||||
"accept-encoding",
|
||||
"authorization",
|
||||
"content-type",
|
||||
"dnt",
|
||||
"origin",
|
||||
"user-agent",
|
||||
"x-csrftoken",
|
||||
"x-requested-with",
|
||||
]
|
||||
|
||||
# 【待确认】自定义用户模型,你需要在 yonghu/models.py 中创建 class Yonghu
|
||||
#AUTH_USER_MODEL = 'yonghu.Yonghu'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# OSS配置
|
||||
COS_SECRET_ID ='AKIDU6WikZBcz96xesxpVM3XoslS3TjogLDx'
|
||||
COS_SECRET_KEY ='qcJzVqLdkeKyXwKckjV5SCmncFfrqdu2'
|
||||
COS_REGION = 'ap-shanghai'
|
||||
COS_BUCKET = 'xingque999qygwuyq-1404472910'
|
||||
COS_SCHEME = 'https' # 使用HTTPS
|
||||
COS_DOMAIN = f'https://{COS_BUCKET}.cos.{COS_REGION}.myqcloud.com'
|
||||
# settings.py - 在COS配置部分添加
|
||||
COS_APPID = '1404472910' # 你的腾讯云APPID(julebu-1361527063中的数字部分)
|
||||
COS_STS_DURATION = 7200 # STS临时凭证有效期(秒)
|
||||
|
||||
# ==================== 业务相关配置 (值先空着,结构可保留) ====================
|
||||
|
||||
# 微信小程序配置 (开发环境可先留空或使用测试号)
|
||||
WEIXIN_APPID = 'wx0e4be86faac4a8d1' # 你的小程序APPID
|
||||
WEIXIN_SECRET = '3c3673cf052571dea9e3bea88e198434' # 你的小程序SECRET
|
||||
WEIXIN_MCHID = '1746545364' # 商户号,支付功能需要
|
||||
WEIXIN_SHANGHUMIYAO = 'Dujieduze53631383771411017633490' # 商户密钥,支付功能需要
|
||||
# 🆕【新增】服务号配置
|
||||
WEIXIN_OFFICIAL_APPID = 'wxd400adf1911c534f' # 服务号APPID
|
||||
WEIXIN_OFFICIAL_SECRET = '19c946cf02ac6ef13f40333fc8029b92' # 服务号SECRET
|
||||
WEIXIN_OFFICIAL_TOKEN = '78324hjwewehrui3yferhf34' # 服务号Token
|
||||
WEIXIN_OFFICIAL_ENCODING_AES_KEY = 'NimrG1Fmq2ydplHJrXeg34NqbCJerltAGVx9xJ5WBx2' # 可选
|
||||
WEIXIN_TEMPLATE_ID = 'jZ73G_0KbmFK864WVUr0QFNnqdH-ILBp_hUPMiwx4YY' # 模板ID
|
||||
|
||||
# 🆕【新增】模板消息配置
|
||||
WEIXIN_TEMPLATE_MAX_PER_MINUTE = 950 # 每分钟最大发送量(留50缓冲)
|
||||
WEIXIN_TEMPLATE_BATCH_SIZE = 100 # 每批发送数量
|
||||
|
||||
WEIXIN_CERT_PATH = '/opt/1panel/apps/openresty/nginx/www/sites/43.142.166.152.1/tuikuanzhengshu/apiclient_cert.pem' # 证书文件路径
|
||||
WEIXIN_KEY_PATH = '/opt/1panel/apps/openresty/nginx/www/sites/43.142.166.152.1/tuikuanzhengshu/apiclient_key.pem' # 密钥文件路径
|
||||
|
||||
WEIXIN_NOTIFY_URL = 'https://www.abas.asia/hqhd/dingdan/pay-notify/' # 支付回调地址,如:http://你的域名/api/pay/callback/
|
||||
# settings.py 或相应配置文件中
|
||||
WEIXIN_PAY_DESCRIPTION = "网络服务" # 支付描述
|
||||
# 押金充值相关配置
|
||||
YAJIN_PAY_DESCRIPTION = "打手押金充值" # 押金支付描述
|
||||
YAJIN_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/yajinhuitiao" # 押金支付回调地址
|
||||
|
||||
# 积分充值相关配置
|
||||
JIFEN_PAY_DESCRIPTION = "打手积分补充" # 积分支付描述
|
||||
JIFEN_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/jifenhuitiao" # 积分支付回调地址
|
||||
|
||||
# 会员充值相关配置
|
||||
HUIYUAN_PAY_DESCRIPTION = "打手会员充值" # 会员支付描述
|
||||
HUIYUAN_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/huiyuanhuitiao" # 会员支付回调地址
|
||||
|
||||
|
||||
# 考核支付配置
|
||||
KAOHE_PAY_DESCRIPTION = '考核金牌-缴纳考核费'
|
||||
KAOHE_PAY_NOTIFY_URL = 'https://www.abas.asia/hqhd/yonghu/kh_huitiao' # 替换为实际回调地址
|
||||
KAOHE_PAY_DEFAULT_FEE = 5.00 # 未配置费用时的默认金额
|
||||
|
||||
|
||||
# settings.py
|
||||
SHANGJIA_CZ_PREFIX = "CzSJ" # 商家充值订单前缀
|
||||
SHANGJIA_CZ_PAY_DESCRIPTION = "阿龙电竞-商家余额充值"
|
||||
SHANGJIA_CZ_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/sjhuitiao"
|
||||
SHANGJIA_CZ_MIN_AMOUNT = 1
|
||||
SHANGJIA_CZ_MAX_AMOUNT = 10000
|
||||
|
||||
|
||||
|
||||
# 罚款缴纳微信支付配置
|
||||
FAKUAN_PAY_DESCRIPTION = '罚款缴纳' # 支付描述
|
||||
FAKUAN_PAY_NOTIFY_URL = 'https://www.abas.asia/hqhd/yonghu/fk_huitiao' # 微信支付回调地址(根据实际域名调整)
|
||||
|
||||
|
||||
|
||||
|
||||
# 订单ID前缀配置
|
||||
ORDER_PREFIX = {
|
||||
'yajin': 'CzYJ', # 押金订单前缀
|
||||
'jifen': 'CzJF', # 积分订单前缀
|
||||
'huiyuan': 'CzHY', # 会员订单前缀
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 腾讯云即时通讯 (IM) - 开发环境可暂不配置
|
||||
TENCENT_IM_CONFIG = {
|
||||
'SDK_APP_ID': 0, # 整数
|
||||
'SECRET_KEY': '',
|
||||
}
|
||||
|
||||
# 阿里云短信 - 开发环境可暂不配置
|
||||
ALIYUN_SMS_CONFIG = {
|
||||
'ACCESS_KEY_ID': '',
|
||||
'ACCESS_KEY_SECRET': '',
|
||||
'SIGN_NAME': '',
|
||||
'TEMPLATE_CODE': '',
|
||||
'REGION_ID': 'cn-hangzhou',
|
||||
}
|
||||
|
||||
# HashID (用于加密ID,旧项目有,新项目需要吗?)
|
||||
# HASHID_FIELD_SALT = 'a_long_dianjing_2025_salt' # 【待确认】请设置你的盐值
|
||||
|
||||
|
||||
# settings.py 尾部添加
|
||||
# settings.py
|
||||
# settings.py
|
||||
# settings.py 中添加配置
|
||||
GOEASY_APPKEY = 'BC-32e2991c69bc47e1b538e73aaed37fc1' # 你的AppKey
|
||||
GOEASY_SECRET = 'A469ABAA3cc34D64' # 从GoEasy控制台获取
|
||||
GOEASY_REST_URL = 'https://rest-hz.goeasy.io/v2/im/message' # 正式环境
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
阿龙电竞 - Django设置文件中的Celery配置部分
|
||||
请在您的settings.py中添加以下配置
|
||||
"""
|
||||
|
||||
# ==================== Celery配置 ====================
|
||||
# Redis作为消息代理和结果后端
|
||||
# 生产环境请修改这里的Redis连接信息
|
||||
REDIS_HOST = '172.19.0.2' # Redis服务器IP
|
||||
REDIS_PORT = 6379
|
||||
REDIS_PASSWORD = 'Dujieduze5.3' # 如果有密码,替换为你的密码
|
||||
REDIS_DB_CELERY = 0
|
||||
REDIS_DB_RESULT = 1
|
||||
|
||||
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
|
||||
'LOCATION': f'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/2',
|
||||
'TIMEOUT': 300,
|
||||
'OPTIONS': {
|
||||
# 不需要 CLIENT_CLASS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 构建Redis连接字符串
|
||||
if REDIS_PASSWORD:
|
||||
CELERY_BROKER_URL = f'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB_CELERY}'
|
||||
CELERY_RESULT_BACKEND = f'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB_RESULT}'
|
||||
else:
|
||||
CELERY_BROKER_URL = f'redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB_CELERY}'
|
||||
CELERY_RESULT_BACKEND = f'redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB_RESULT}'
|
||||
|
||||
# 任务接受的内容类型
|
||||
CELERY_ACCEPT_CONTENT = ['json']
|
||||
CELERY_TASK_SERIALIZER = 'json'
|
||||
CELERY_RESULT_SERIALIZER = 'json'
|
||||
|
||||
# 时区设置
|
||||
CELERY_TIMEZONE = 'Asia/Shanghai'
|
||||
CELERY_ENABLE_UTC = True
|
||||
|
||||
|
||||
|
||||
# settings.py 末尾添加(在其他 Celery 配置之后)
|
||||
CELERY_IMPORTS = ('dingdan.tongzhi_tasks',)
|
||||
|
||||
|
||||
|
||||
# 任务超时设置
|
||||
CELERY_TASK_TIME_LIMIT = 300 # 任务最大执行时间300秒
|
||||
CELERY_TASK_SOFT_TIME_LIMIT = 240 # 软超时240秒
|
||||
|
||||
# Worker并发设置
|
||||
CELERY_WORKER_CONCURRENCY = 4 # 根据服务器CPU核心数调整
|
||||
CELERY_WORKER_PREFETCH_MULTIPLIER = 1
|
||||
|
||||
# 任务确认设置
|
||||
CELERY_TASK_ACKS_LATE = True
|
||||
CELERY_WORKER_DISABLE_RATE_LIMITS = True
|
||||
|
||||
# 队列配置
|
||||
CELERY_TASK_DEFAULT_QUEUE = 'default'
|
||||
CELERY_TASK_QUEUES = {
|
||||
'default': {
|
||||
'exchange': 'default',
|
||||
'routing_key': 'default',
|
||||
},
|
||||
'order_tasks': {
|
||||
'exchange': 'order_tasks',
|
||||
'routing_key': 'order_tasks',
|
||||
},
|
||||
'periodic_tasks': {
|
||||
'exchange': 'periodic_tasks',
|
||||
'routing_key': 'periodic_tasks',
|
||||
},
|
||||
}
|
||||
|
||||
# ==================== 定时任务时间配置 ====================
|
||||
# 所有时间配置都在这里,方便修改
|
||||
|
||||
# 订单超时时间(秒) - 48小时
|
||||
ORDER_EXPIRE_SECONDS = 48000 * 60 * 60 # 172800秒
|
||||
|
||||
# 每日任务执行时间(24小时制)
|
||||
DAILY_TASK_HOUR = 0 # 0点
|
||||
DAILY_TASK_MINUTE = 30 # 0分
|
||||
|
||||
# 每月任务执行时间
|
||||
MONTHLY_TASK_DAY = 1 # 每月1日
|
||||
MONTHLY_TASK_HOUR = 0 # 0点
|
||||
MONTHLY_TASK_MINUTE = 0 # 0分
|
||||
|
||||
# 订单检查频率(分钟)
|
||||
ORDER_CHECK_INTERVAL = 5 # 每5分钟检查一次
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ==================== 跨平台订单互通配置 ====================
|
||||
# 接口认证令牌(双方约定,用于简单验证)
|
||||
CROSS_PLATFORM_TOKEN = 'jdjdjshwjqk'
|
||||
|
||||
# 对方平台接口基础URL(需根据实际部署修改)
|
||||
PARTNER_BASE_URL = 'https://ios808.top/hqhd'
|
||||
|
||||
# 对方平台各接口路径(可根据实际情况调整,但双方需保持一致)
|
||||
PARTNER_SYNC_ORDER_URL = '/dingdan/partner_sync_order' # 接收派单
|
||||
PARTNER_CHECK_CLAIM_URL = '/dingdan/partner_check_claim' # 抢单询问
|
||||
PARTNER_NOTIFY_CLAIM_URL = '/dingdan/partner_notify_claim' # 抢单成功通知
|
||||
PARTNER_REFUND_NOTIFY_URL = '/dingdan/partner_refund_notify' # 退款通知
|
||||
|
||||
# 我方代表对方发单的商家ID(需在数据库中存在,且具有商家扩展表)
|
||||
AGENT_SHANGJIA_ID = '9106027'
|
||||
|
||||
# 用于验证的会员ID(根据业务约定)
|
||||
CROSS_PLATFORM_MEMBER_ID = 'V00QQI'
|
||||
|
||||
# 默认游戏类型ID(当对方未传递时使用)
|
||||
CROSS_PLATFORM_GAME_TYPE_ID = 1
|
||||
|
||||
# 允许跨平台的商家ID列表(白名单)
|
||||
CROSS_PLATFORM_WHITELIST = ['5438898']
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 特殊管事ID列表(yonghuid),这些管事的首次分红金额从 UserGuanshi.fenhong_yici 字段获取
|
||||
SPECIAL_GUANSHI_IDS = [
|
||||
'2295516',
|
||||
'9106027',
|
||||
'2244062',
|
||||
# ... 按需添加
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
WECHAT_PAY_V3_CONFIG = {
|
||||
'MCHID': '1746545364', # 商户号
|
||||
'APPID': 'wx0e4be86faac4a8d1', # 小程序APPID
|
||||
'API_V3_KEY': 'dujie4637rufjd98ijuyrgcvbvbn4789', # APIv3密钥(32字节)
|
||||
'PRIVATE_KEY_PATH': '/opt/1panel/apps/openresty/nginx/www/sites/43.142.166.152.443/huizhifuzhengshu/apiclient_key.pem', # 商户私钥文件路径
|
||||
'CERT_SERIAL_NO': '13D0CECB42C48DE78FF35A8A72EF4341FE769FE9', # 商户证书序列号
|
||||
'PLATFORM_CERT_DIR': '/opt/1panel/apps/openresty/nginx/www/sites/43.142.166.152.443/huizhifuzhengshu/pub_key.pem', # 存放微信平台证书公钥的目录(文件名:序列号.pem)
|
||||
'NOTIFY_URL': 'https://www.abas.asia/hqhd/yonghu/tixiancallback', # 回调地址
|
||||
'TRANSFER_SCENE_ID': '1005', # 替换为你的实际场景ID
|
||||
}
|
||||
|
||||
|
||||
CROSS_PLATFORM_SYNC_TOKEN = 'xK9mP2vL8qW5rT7y'
|
||||
58
a_long_dianjing/urls.py
Normal file
58
a_long_dianjing/urls.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
URL configuration for a_long_dianjing project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/6.0/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
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
]'''
|
||||
|
||||
# a_long_dianjing/urls.py
|
||||
"""
|
||||
项目根路由配置 - 按照生产环境格式,连接所有APP路由
|
||||
注意:本项目使用MinIO模拟OSS,文件上传不经过Django MEDIA_URL,所以不需要static(settings.MEDIA_URL)配置
|
||||
"""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
|
||||
urlpatterns = [
|
||||
# Django管理后台
|
||||
path('admin/', admin.site.urls),
|
||||
|
||||
# 用户相关路由
|
||||
path('yonghu/', include('yonghu.urls')),
|
||||
|
||||
# 订单相关路由
|
||||
path('dingdan/', include('dingdan.urls')),
|
||||
|
||||
# 商品/陪玩服务相关路由
|
||||
path('shangpin/', include('shangpin.urls')),
|
||||
|
||||
|
||||
# 配置相关路由(系统配置)
|
||||
path('peizhi/', include('peizhi.urls')),
|
||||
path('houtai/', include('houtai.urls')),
|
||||
path('shangdian/', include('shangdian.urls')),
|
||||
path('dengji/', include('dengji.urls')),
|
||||
]
|
||||
|
||||
# 重要说明:
|
||||
# 1. 本项目使用MinIO(模拟OSS)作为对象存储,文件上传通过MinIO的S3接口直接处理
|
||||
# 2. 文件存储不经过Django的MEDIA_URL,所以不需要配置 static(settings.MEDIA_URL, ...)
|
||||
# 3. 未来迁移到腾讯云COS/阿里云OSS时,也通过django-storages直接处理,不经过Django静态文件服务
|
||||
# 4. 所有文件访问将通过MinIO/COS的CDN域名直接访问,不需要Django路由转发
|
||||
16
a_long_dianjing/wsgi.py
Normal file
16
a_long_dianjing/wsgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for a_long_dianjing project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'a_long_dianjing.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
BIN
celerybeat-schedule
Normal file
BIN
celerybeat-schedule
Normal file
Binary file not shown.
0
dengji/__init__.py
Normal file
0
dengji/__init__.py
Normal file
3
dengji/admin.py
Normal file
3
dengji/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
dengji/apps.py
Normal file
6
dengji/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DengjiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'dengji'
|
||||
111
dengji/migrations/0001_initial.py
Normal file
111
dengji/migrations/0001_initial.py
Normal file
@@ -0,0 +1,111 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-12 02:17
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('dingdan', '0026_fadanfenhonglilv_alter_fadan_shenqingren_shenfen_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Bankuai',
|
||||
fields=[
|
||||
('bankuai_id', models.AutoField(primary_key=True, serialize=False, verbose_name='板块ID')),
|
||||
('mingcheng', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='板块名称')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '板块',
|
||||
'verbose_name_plural': '板块',
|
||||
'db_table': 'bankuai',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Chenghao',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('mingcheng', models.CharField(max_length=50, unique=True, verbose_name='称号名称')),
|
||||
('leixing', models.CharField(choices=[('dashou', '打手'), ('boss', '老板'), ('shangjia', '商家'), ('guanshi', '管事'), ('zuzhang', '组长')], max_length=10, verbose_name='称号类型')),
|
||||
('tubiao_url', models.CharField(blank=True, max_length=200, null=True, verbose_name='称号图标相对URL')),
|
||||
('texiao_miaoshu', models.TextField(blank=True, null=True, verbose_name='特效描述(JSON)')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('bankuai', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dengji.bankuai', verbose_name='所属板块(空=全板块通用)')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '称号',
|
||||
'verbose_name_plural': '称号',
|
||||
'db_table': 'chenghao',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='YonghuChenghao',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('huode_time', models.DateTimeField(auto_now_add=True, verbose_name='获得时间')),
|
||||
('chenghao', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.chenghao', verbose_name='称号')),
|
||||
('yonghu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chenghao_list', to=settings.AUTH_USER_MODEL, verbose_name='用户')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户称号关联',
|
||||
'verbose_name_plural': '用户称号关联',
|
||||
'db_table': 'yonghu_chenghao',
|
||||
'indexes': [models.Index(fields=['yonghu', 'chenghao'], name='yonghu_chen_yonghu__cac0a1_idx')],
|
||||
'unique_together': {('yonghu', 'chenghao')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='JingyanJilu',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('yonghu_id', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')),
|
||||
('yonghu_leixing', models.CharField(choices=[('dashou', '打手'), ('boss', '老板'), ('shangjia', '商家'), ('guanshi', '管事'), ('zuzhang', '组长')], max_length=10, verbose_name='用户类型')),
|
||||
('sudu_jingyan', models.DecimalField(decimal_places=2, default=0, max_digits=8, verbose_name='上线速度经验')),
|
||||
('shili_jingyan', models.DecimalField(decimal_places=2, default=0, max_digits=8, verbose_name='打手实力经验')),
|
||||
('taidu_jingyan', models.DecimalField(decimal_places=2, default=0, max_digits=8, verbose_name='服务态度经验')),
|
||||
('zonghe_jingyan', models.DecimalField(decimal_places=2, default=0, max_digits=8, verbose_name='综合经验')),
|
||||
('dingdan_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='关联订单ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('bankuai', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dengji.bankuai', verbose_name='所属板块')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '经验变动记录',
|
||||
'verbose_name_plural': '经验变动记录',
|
||||
'db_table': 'jingyan_jilu',
|
||||
'indexes': [models.Index(fields=['yonghu_id', 'bankuai', 'create_time'], name='jingyan_jil_yonghu__786992_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DashouBiaoxian',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dashou_id', models.CharField(db_index=True, max_length=32, verbose_name='打手ID')),
|
||||
('shangxian_sudu', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='上线速度评价(1-5)')),
|
||||
('fuwu_taidu', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='服务态度评价(1-5)')),
|
||||
('fanche_cishu', models.PositiveIntegerField(blank=True, null=True, verbose_name='翻车次数')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('jiedan_time', models.DateTimeField(blank=True, null=True, verbose_name='接单时间')),
|
||||
('tijiao_time', models.DateTimeField(blank=True, null=True, verbose_name='提交时间')),
|
||||
('bankuai', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='dengji.bankuai', verbose_name='所属板块')),
|
||||
('dingdan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='biaoxian', to='dingdan.dingdan', verbose_name='关联订单')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '打手订单表现',
|
||||
'verbose_name_plural': '打手订单表现',
|
||||
'db_table': 'dashou_biaoxian',
|
||||
'indexes': [models.Index(fields=['dashou_id', 'bankuai', 'create_time'], name='dashou_biao_dashou__36e1c3_idx')],
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='chenghao',
|
||||
index=models.Index(fields=['leixing', 'bankuai'], name='chenghao_leixing_8f4371_idx'),
|
||||
),
|
||||
]
|
||||
20
dengji/migrations/0002_alter_dashoubiaoxian_dingdan.py
Normal file
20
dengji/migrations/0002_alter_dashoubiaoxian_dingdan.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-12 02:25
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0026_fadanfenhonglilv_alter_fadan_shenqingren_shenfen_and_more'),
|
||||
('dengji', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='dashoubiaoxian',
|
||||
name='dingdan',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='biaoxian', to='dingdan.dingdan', to_field='dingdan_id', verbose_name='关联订单'),
|
||||
),
|
||||
]
|
||||
19
dengji/migrations/0003_alter_dashoubiaoxian_bankuai.py
Normal file
19
dengji/migrations/0003_alter_dashoubiaoxian_bankuai.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-12 02:32
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dengji', '0002_alter_dashoubiaoxian_dingdan'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='dashoubiaoxian',
|
||||
name='bankuai',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='dengji.bankuai', verbose_name='所属板块'),
|
||||
),
|
||||
]
|
||||
26
dengji/migrations/0004_dingdanbiaoqian.py
Normal file
26
dengji/migrations/0004_dingdanbiaoqian.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# dengji/migrations/0004_dingdanbiaoqian.py
|
||||
from django.db import migrations
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0026_fadanfenhonglilv_alter_fadan_shenqingren_shenfen_and_more'), # 保持你之前的依赖
|
||||
('dengji', '0003_alter_dashoubiaoxian_bankuai'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunSQL(
|
||||
sql="""
|
||||
CREATE TABLE IF NOT EXISTS dingdan_biaoqian (
|
||||
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
create_time DATETIME(6) NOT NULL,
|
||||
chenghao_id BIGINT NOT NULL,
|
||||
dingdan_id VARCHAR(32) NOT NULL,
|
||||
CONSTRAINT fk_dingdanbiaoqian_chenghao FOREIGN KEY (chenghao_id) REFERENCES chenghao (id),
|
||||
CONSTRAINT fk_dingdanbiaoqian_dingdan FOREIGN KEY (dingdan_id) REFERENCES dingdan (dingdan_id),
|
||||
UNIQUE KEY uniq_dingdan_chenghao (dingdan_id, chenghao_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
""",
|
||||
reverse_sql="DROP TABLE IF EXISTS dingdan_biaoqian;"
|
||||
),
|
||||
]
|
||||
36
dengji/migrations/0005_fix_state.py
Normal file
36
dengji/migrations/0005_fix_state.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# dengji/migrations/0005_fix_state.py
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0026_fadanfenhonglilv_alter_fadan_shenqingren_shenfen_and_more'), # 保持你之前的依赖
|
||||
('dengji', '0004_dingdanbiaoqian'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.SeparateDatabaseAndState(
|
||||
database_operations=[], # 表已存在,不做任何数据库操作
|
||||
state_operations=[
|
||||
migrations.CreateModel(
|
||||
name='DingdanBiaoqian',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('chenghao', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.chenghao', verbose_name='标签')),
|
||||
('dingdan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dingdan.dingdan', to_field='dingdan_id', verbose_name='订单')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '订单需求标签',
|
||||
'verbose_name_plural': '订单需求标签',
|
||||
'db_table': 'dingdan_biaoqian',
|
||||
'unique_together': {('dingdan', 'chenghao')},
|
||||
'indexes': [
|
||||
models.Index(fields=['dingdan'], name='dingdan_bia_dingdan_4d046b_idx'),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
35
dengji/migrations/0006_shenhejilu.py
Normal file
35
dengji/migrations/0006_shenhejilu.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-14 18:42
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dengji', '0005_fix_state'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ShenheJilu',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('shenqingren_id', models.CharField(max_length=7, verbose_name='申请人用户ID')),
|
||||
('shenheguan_id', models.CharField(max_length=7, null=True, verbose_name='审核官用户ID')),
|
||||
('zhuangtai', models.IntegerField(choices=[(0, '审核中'), (1, '通过'), (2, '未通过')], default=0, verbose_name='审核状态')),
|
||||
('cishu', models.IntegerField(default=1, verbose_name='第几次审核')),
|
||||
('jiaofei_jine', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='缴纳费用')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('bankuai', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dengji.bankuai', verbose_name='审核板块')),
|
||||
('chenghao', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='dengji.chenghao', verbose_name='审核标签')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '审核记录',
|
||||
'verbose_name_plural': '审核记录',
|
||||
'db_table': 'shenhe_jilu',
|
||||
'indexes': [models.Index(fields=['shenqingren_id', 'zhuangtai'], name='shenhe_jilu_shenqin_502e9b_idx'), models.Index(fields=['shenheguan_id'], name='shenhe_jilu_shenheg_5c9959_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
29
dengji/migrations/0007_kaoheguanbankuai.py
Normal file
29
dengji/migrations/0007_kaoheguanbankuai.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-15 00:31
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dengji', '0006_shenhejilu'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='KaoheguanBankuai',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('kaoheguan_id', models.CharField(db_index=True, max_length=7, verbose_name='考核官用户ID')),
|
||||
('bankuai', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.bankuai', verbose_name='所属板块')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '考核官板块关联',
|
||||
'verbose_name_plural': '考核官板块关联',
|
||||
'db_table': 'kaoheguan_bankuai',
|
||||
'indexes': [models.Index(fields=['kaoheguan_id'], name='kaoheguan_b_kaohegu_00f7c5_idx')],
|
||||
'unique_together': {('kaoheguan_id', 'bankuai')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,48 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-15 04:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dengji', '0007_kaoheguanbankuai'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='chenghao',
|
||||
name='kaohe_guize',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='考核规则'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='shenhejilu',
|
||||
name='dashou_beizhu',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='打手备注'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='shenhejilu',
|
||||
name='dashou_youxi_id',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='打手游戏ID'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='shenhejilu',
|
||||
name='guize_neirong',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='规则内容'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='shenhejilu',
|
||||
name='jilu_id',
|
||||
field=models.CharField(db_index=True, max_length=32, null=True, verbose_name='记录ID'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='shenhejilu',
|
||||
name='shenheguan_youxi_id',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='审核官游戏ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='shenhejilu',
|
||||
name='zhuangtai',
|
||||
field=models.IntegerField(choices=[(0, '审核中'), (1, '通过'), (2, '未通过'), (3, '待审核')], default=0, verbose_name='审核状态'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,58 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-15 13:20
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dengji', '0008_chenghao_kaohe_guize_shenhejilu_dashou_beizhu_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='chenghao',
|
||||
name='kaioi_jinpai',
|
||||
field=models.BooleanField(default=False, verbose_name='是否同时开启金牌'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='shenhejilu',
|
||||
name='jilu_id',
|
||||
field=models.CharField(db_index=True, default='Sh111', max_length=32, unique=True, verbose_name='记录ID'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='KaoheJujueJilu',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('cishu', models.IntegerField(verbose_name='对应次数')),
|
||||
('shenheguan_id', models.CharField(max_length=7, verbose_name='考核官用户ID')),
|
||||
('yuanyin', models.TextField(verbose_name='拒绝原因')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('jilu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.shenhejilu', to_field='jilu_id', verbose_name='审核记录')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '拒绝原因记录',
|
||||
'verbose_name_plural': '拒绝原因记录',
|
||||
'db_table': 'kaohe_jujue_jilu',
|
||||
'indexes': [models.Index(fields=['jilu_id', 'cishu'], name='kaohe_jujue_jilu_id_1f2b29_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='KaoheCishuFeiyong',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('cishu', models.IntegerField(verbose_name='第几次考核')),
|
||||
('feiyong', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='需缴纳金额')),
|
||||
('chenghao', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.chenghao', verbose_name='标签')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '考核次数费用',
|
||||
'verbose_name_plural': '考核次数费用',
|
||||
'db_table': 'kaohe_cishu_feiyong',
|
||||
'indexes': [models.Index(fields=['chenghao', 'cishu'], name='kaohe_cishu_chengha_edc272_idx')],
|
||||
'unique_together': {('chenghao', 'cishu')},
|
||||
},
|
||||
),
|
||||
]
|
||||
29
dengji/migrations/0010_kaohejilufeiyong.py
Normal file
29
dengji/migrations/0010_kaohejilufeiyong.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-15 16:10
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dengji', '0009_chenghao_kaioi_jinpai_alter_shenhejilu_jilu_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='KaoheJiluFeiyong',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('cishu', models.IntegerField(verbose_name='第几次考核')),
|
||||
('jine', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='缴纳金额')),
|
||||
('jilu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dengji.shenhejilu', to_field='jilu_id', verbose_name='考核记录')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '考核记录费用',
|
||||
'verbose_name_plural': '考核记录费用',
|
||||
'db_table': 'kaohe_jilu_feiyong',
|
||||
'unique_together': {('jilu', 'cishu')},
|
||||
},
|
||||
),
|
||||
]
|
||||
35
dengji/migrations/0011_kaohepaytemp.py
Normal file
35
dengji/migrations/0011_kaohepaytemp.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-19 13:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dengji', '0010_kaohejilufeiyong'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='KaohePayTemp',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dingdan_id', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='订单ID')),
|
||||
('yonghuid', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')),
|
||||
('tag_id', models.IntegerField(verbose_name='标签ID')),
|
||||
('game_nick', models.CharField(max_length=100, verbose_name='游戏昵称')),
|
||||
('remark', models.TextField(blank=True, verbose_name='备注')),
|
||||
('reapply', models.BooleanField(default=False, verbose_name='是否再次申请')),
|
||||
('jilu_id', models.CharField(blank=True, max_length=32, null=True, verbose_name='原记录ID(再次申请时)')),
|
||||
('shenheguan_id', models.CharField(blank=True, max_length=7, verbose_name='指定考核官ID')),
|
||||
('cishu', models.IntegerField(verbose_name='本次考核次数(预计算)')),
|
||||
('fee', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='本次费用(分账单位)')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '考核支付临时表',
|
||||
'verbose_name_plural': '考核支付临时表',
|
||||
'db_table': 'kaohe_pay_temp',
|
||||
},
|
||||
),
|
||||
]
|
||||
0
dengji/migrations/__init__.py
Normal file
0
dengji/migrations/__init__.py
Normal file
224
dengji/models.py
Normal file
224
dengji/models.py
Normal file
@@ -0,0 +1,224 @@
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from dingdan.models import Dingdan
|
||||
|
||||
# ==================== 板块表 ====================
|
||||
class Bankuai(models.Model):
|
||||
bankuai_id = models.AutoField(primary_key=True, verbose_name='板块ID')
|
||||
mingcheng = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='板块名称')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'bankuai'
|
||||
verbose_name = '板块'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.mingcheng
|
||||
|
||||
|
||||
# ==================== 打手订单表现记录 ====================
|
||||
class DashouBiaoxian(models.Model):
|
||||
dingdan = models.ForeignKey('dingdan.Dingdan', to_field='dingdan_id', on_delete=models.CASCADE, related_name='biaoxian', verbose_name='关联订单', db_index=True)
|
||||
|
||||
dashou_id = models.CharField(max_length=32, db_index=True, verbose_name='打手ID')
|
||||
bankuai = models.ForeignKey(Bankuai, on_delete=models.PROTECT, to_field='bankuai_id', verbose_name='所属板块', db_index=True,null=True)
|
||||
shangxian_sudu = models.PositiveSmallIntegerField(null=True, blank=True, verbose_name='上线速度评价(1-5)')
|
||||
fuwu_taidu = models.PositiveSmallIntegerField(null=True, blank=True, verbose_name='服务态度评价(1-5)')
|
||||
fanche_cishu = models.PositiveIntegerField(null=True, blank=True, verbose_name='翻车次数')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
jiedan_time = models.DateTimeField(null=True, blank=True, verbose_name='接单时间')
|
||||
tijiao_time = models.DateTimeField(null=True, blank=True, verbose_name='提交时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'dashou_biaoxian'
|
||||
verbose_name = '打手订单表现'
|
||||
verbose_name_plural = verbose_name
|
||||
indexes = [models.Index(fields=['dashou_id', 'bankuai', 'create_time'])]
|
||||
|
||||
|
||||
# ==================== 经验变动记录 ====================
|
||||
class JingyanJilu(models.Model):
|
||||
yonghu_id = models.CharField(max_length=7, db_index=True, verbose_name='用户ID')
|
||||
yonghu_leixing = models.CharField(max_length=10, choices=[('dashou','打手'),('boss','老板'),('shangjia','商家'),('guanshi','管事'),('zuzhang','组长')], verbose_name='用户类型')
|
||||
bankuai = models.ForeignKey(Bankuai, on_delete=models.PROTECT, to_field='bankuai_id', verbose_name='所属板块', db_index=True)
|
||||
sudu_jingyan = models.DecimalField(max_digits=8, decimal_places=2, default=0, verbose_name='上线速度经验')
|
||||
shili_jingyan = models.DecimalField(max_digits=8, decimal_places=2, default=0, verbose_name='打手实力经验')
|
||||
taidu_jingyan = models.DecimalField(max_digits=8, decimal_places=2, default=0, verbose_name='服务态度经验')
|
||||
zonghe_jingyan = models.DecimalField(max_digits=8, decimal_places=2, default=0, verbose_name='综合经验')
|
||||
dingdan_id = models.CharField(max_length=32, null=True, blank=True, db_index=True, verbose_name='关联订单ID')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
|
||||
class Meta:
|
||||
db_table = 'jingyan_jilu'
|
||||
verbose_name = '经验变动记录'
|
||||
verbose_name_plural = verbose_name
|
||||
indexes = [models.Index(fields=['yonghu_id', 'bankuai', 'create_time'])]
|
||||
|
||||
|
||||
# ==================== 称号主表 ====================
|
||||
class Chenghao(models.Model):
|
||||
mingcheng = models.CharField(max_length=50, unique=True, verbose_name='称号名称')
|
||||
leixing = models.CharField(max_length=10, choices=[('dashou','打手'),('boss','老板'),('shangjia','商家'),('guanshi','管事'),('zuzhang','组长')], verbose_name='称号类型')
|
||||
bankuai = models.ForeignKey(Bankuai, on_delete=models.PROTECT, to_field='bankuai_id', null=True, blank=True, verbose_name='所属板块(空=全板块通用)')
|
||||
tubiao_url = models.CharField(max_length=200, null=True, blank=True, verbose_name='称号图标相对URL')
|
||||
texiao_miaoshu = models.TextField(null=True, blank=True, verbose_name='特效描述(JSON)')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
kaohe_guize = models.TextField(null=True, blank=True, verbose_name='考核规则') # 新增
|
||||
kaioi_jinpai = models.BooleanField(default=False, verbose_name='是否同时开启金牌')
|
||||
|
||||
class Meta:
|
||||
db_table = 'chenghao'
|
||||
verbose_name = '称号'
|
||||
verbose_name_plural = verbose_name
|
||||
indexes = [models.Index(fields=['leixing', 'bankuai'])]
|
||||
|
||||
|
||||
# ==================== 用户 - 称号关联 ====================
|
||||
class YonghuChenghao(models.Model):
|
||||
yonghu = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chenghao_list', verbose_name='用户', db_index=True)
|
||||
chenghao = models.ForeignKey(Chenghao, on_delete=models.CASCADE, verbose_name='称号')
|
||||
huode_time = models.DateTimeField(auto_now_add=True, verbose_name='获得时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'yonghu_chenghao'
|
||||
verbose_name = '用户称号关联'
|
||||
verbose_name_plural = verbose_name
|
||||
unique_together = ('yonghu', 'chenghao')
|
||||
# 修复:只索引本表字段
|
||||
indexes = [models.Index(fields=['yonghu', 'chenghao'])]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class DingdanBiaoqian(models.Model):
|
||||
"""订单需求标签关联表"""
|
||||
dingdan = models.ForeignKey(
|
||||
Dingdan,
|
||||
to_field='dingdan_id', # 关联到字符串字段 dingdan_id
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name='订单',
|
||||
db_index=True
|
||||
)
|
||||
chenghao = models.ForeignKey('dengji.Chenghao', on_delete=models.CASCADE, verbose_name='标签')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'dingdan_biaoqian'
|
||||
verbose_name = '订单需求标签'
|
||||
verbose_name_plural = verbose_name
|
||||
unique_together = ('dingdan', 'chenghao')
|
||||
indexes = [models.Index(fields=['dingdan'])]
|
||||
|
||||
|
||||
|
||||
|
||||
class ShenheJilu(models.Model):
|
||||
"""审核记录表"""
|
||||
shenqingren_id = models.CharField(max_length=7, verbose_name='申请人用户ID')
|
||||
shenheguan_id = models.CharField(max_length=7, null = True,verbose_name='审核官用户ID')
|
||||
zhuangtai = models.IntegerField(choices=[(0, '审核中'), (1, '通过'), (2, '未通过'), (3, '待审核')], default=0, verbose_name='审核状态')
|
||||
bankuai = models.ForeignKey(Bankuai, on_delete=models.PROTECT, to_field='bankuai_id', null=True, blank=True, verbose_name='审核板块')
|
||||
chenghao = models.ForeignKey(Chenghao, on_delete=models.PROTECT, null=True, blank=True, verbose_name='审核标签')
|
||||
cishu = models.IntegerField(default=1, verbose_name='第几次审核')
|
||||
jiaofei_jine = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='缴纳费用')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
jilu_id = models.CharField(max_length=32, unique=True, db_index=True, verbose_name='记录ID') # 新增
|
||||
guize_neirong = models.TextField(null=True, blank=True, verbose_name='规则内容')
|
||||
shenheguan_youxi_id = models.CharField(max_length=50, null=True, blank=True, verbose_name='审核官游戏ID')
|
||||
dashou_youxi_id = models.CharField(max_length=50, null=True, blank=True, verbose_name='打手游戏ID')
|
||||
dashou_beizhu = models.TextField(null=True, blank=True, verbose_name='打手备注')
|
||||
|
||||
class Meta:
|
||||
db_table = 'shenhe_jilu'
|
||||
verbose_name = '审核记录'
|
||||
verbose_name_plural = verbose_name
|
||||
indexes = [
|
||||
models.Index(fields=['shenqingren_id', 'zhuangtai']),
|
||||
models.Index(fields=['shenheguan_id']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
class KaohePayTemp(models.Model):
|
||||
"""考核支付临时上下文(仅用于支付成功后回调取参,不删除数据)"""
|
||||
dingdan_id = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='订单ID')
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='用户ID')
|
||||
tag_id = models.IntegerField(verbose_name='标签ID')
|
||||
game_nick = models.CharField(max_length=100, verbose_name='游戏昵称')
|
||||
remark = models.TextField(blank=True, verbose_name='备注')
|
||||
reapply = models.BooleanField(default=False, verbose_name='是否再次申请')
|
||||
jilu_id = models.CharField(max_length=32, null=True, blank=True, verbose_name='原记录ID(再次申请时)')
|
||||
shenheguan_id = models.CharField(max_length=7, blank=True, verbose_name='指定考核官ID')
|
||||
cishu = models.IntegerField(verbose_name='本次考核次数(预计算)')
|
||||
fee = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='本次费用(分账单位)')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'kaohe_pay_temp'
|
||||
verbose_name = '考核支付临时表'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class KaoheCishuFeiyong(models.Model):
|
||||
"""各标签各考核次数所需缴纳金额"""
|
||||
chenghao = models.ForeignKey(Chenghao, on_delete=models.CASCADE, verbose_name='标签')
|
||||
cishu = models.IntegerField(verbose_name='第几次考核')
|
||||
feiyong = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='需缴纳金额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'kaohe_cishu_feiyong'
|
||||
verbose_name = '考核次数费用'
|
||||
verbose_name_plural = verbose_name
|
||||
unique_together = ('chenghao', 'cishu')
|
||||
indexes = [models.Index(fields=['chenghao', 'cishu'])]
|
||||
|
||||
|
||||
class KaoheJujueJilu(models.Model):
|
||||
"""考核拒绝原因记录"""
|
||||
jilu = models.ForeignKey(ShenheJilu, on_delete=models.CASCADE, to_field='jilu_id', verbose_name='审核记录')
|
||||
cishu = models.IntegerField(verbose_name='对应次数')
|
||||
shenheguan_id = models.CharField(max_length=7, verbose_name='考核官用户ID')
|
||||
yuanyin = models.TextField(verbose_name='拒绝原因')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'kaohe_jujue_jilu'
|
||||
verbose_name = '拒绝原因记录'
|
||||
verbose_name_plural = verbose_name
|
||||
indexes = [models.Index(fields=['jilu_id', 'cishu'])]
|
||||
|
||||
|
||||
class KaoheJiluFeiyong(models.Model):
|
||||
"""每次考核记录实际缴纳的费用明细"""
|
||||
jilu = models.ForeignKey(ShenheJilu, on_delete=models.CASCADE, to_field='jilu_id', verbose_name='考核记录')
|
||||
cishu = models.IntegerField(verbose_name='第几次考核')
|
||||
jine = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='缴纳金额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'kaohe_jilu_feiyong'
|
||||
verbose_name = '考核记录费用'
|
||||
verbose_name_plural = verbose_name
|
||||
unique_together = ('jilu', 'cishu')
|
||||
|
||||
|
||||
|
||||
class KaoheguanBankuai(models.Model):
|
||||
"""考核官与板块多对多关联表"""
|
||||
kaoheguan_id = models.CharField(max_length=7, db_index=True, verbose_name='考核官用户ID')
|
||||
bankuai = models.ForeignKey(Bankuai, on_delete=models.CASCADE, to_field='bankuai_id', verbose_name='所属板块')
|
||||
|
||||
class Meta:
|
||||
db_table = 'kaoheguan_bankuai'
|
||||
verbose_name = '考核官板块关联'
|
||||
verbose_name_plural = verbose_name
|
||||
unique_together = ('kaoheguan_id', 'bankuai')
|
||||
indexes = [models.Index(fields=['kaoheguan_id'])]
|
||||
87
dengji/services.py
Normal file
87
dengji/services.py
Normal file
@@ -0,0 +1,87 @@
|
||||
# dengji/services.py
|
||||
import logging
|
||||
from django.db import transaction, IntegrityError
|
||||
from django.utils import timezone
|
||||
from .models import DashouBiaoxian
|
||||
|
||||
logger = logging.getLogger('dengji')
|
||||
|
||||
def record_dashou_biaoxian(dingdan_id, dashou_id, xingwei, **kwargs):
|
||||
"""
|
||||
统一记录打手订单表现
|
||||
:param dingdan_id: 订单ID
|
||||
:param dashou_id: 打手ID
|
||||
:param xingwei: 行为类型 1-抢单 2-提交订单 3-结算
|
||||
:param kwargs: 可选字段(与模型字段同名):
|
||||
jiedan_time, tijiao_time,
|
||||
bankuai, shangxian_sudu, fuwu_taidu, fanche_cishu
|
||||
注意:不管是哪种行为,dingdan_id 和 dashou_id 都是必传的,
|
||||
如果没传时间且业务需要时间,则自动取当前时间。
|
||||
"""
|
||||
try:
|
||||
# ----- 抢单:创建记录 -----
|
||||
if xingwei == 1:
|
||||
jiedan_time = kwargs.get('jiedan_time') or timezone.now()
|
||||
bankuai = kwargs.get('bankuai') # 可为 None
|
||||
|
||||
# 避免重复创建(同一订单只会有一条表现记录)
|
||||
obj, created = DashouBiaoxian.objects.get_or_create(
|
||||
dingdan_id=dingdan_id,
|
||||
dashou_id=dashou_id,
|
||||
defaults={
|
||||
'bankuai': bankuai,
|
||||
'jiedan_time': jiedan_time,
|
||||
}
|
||||
)
|
||||
if not created:
|
||||
logger.info(f"表现记录已存在,跳过创建: dingdan={dingdan_id}, dashou={dashou_id}")
|
||||
else:
|
||||
logger.info(f"创建抢单表现记录: dingdan={dingdan_id}, dashou={dashou_id}")
|
||||
|
||||
# ----- 提交订单:更新提交时间 -----
|
||||
elif xingwei == 2:
|
||||
tijiao_time = kwargs.get('tijiao_time') or timezone.now()
|
||||
# 只更新已存在的记录,找不到就直接跳过
|
||||
updated = DashouBiaoxian.objects.filter(
|
||||
dingdan_id=dingdan_id,
|
||||
dashou_id=dashou_id
|
||||
).update(tijiao_time=tijiao_time)
|
||||
if updated:
|
||||
logger.info(f"更新提交时间: dingdan={dingdan_id}, dashou={dashou_id}")
|
||||
else:
|
||||
logger.info(f"无对应记录,跳过提交时间更新: dingdan={dingdan_id}, dashou={dashou_id}")
|
||||
|
||||
# ----- 结算:更新评价信息 -----
|
||||
elif xingwei == 3:
|
||||
# 构建要更新的字段,只更新 kwargs 中传入的非 None 字段
|
||||
update_fields = {}
|
||||
for field in ['shangxian_sudu', 'fuwu_taidu', 'fanche_cishu']:
|
||||
val = kwargs.get(field)
|
||||
if val is not None:
|
||||
update_fields[field] = val
|
||||
# 如果还传了板块也可以更新
|
||||
bankuai = kwargs.get('bankuai')
|
||||
if bankuai is not None:
|
||||
update_fields['bankuai'] = bankuai
|
||||
|
||||
if update_fields:
|
||||
updated = DashouBiaoxian.objects.filter(
|
||||
dingdan_id=dingdan_id,
|
||||
dashou_id=dashou_id
|
||||
).update(**update_fields)
|
||||
if updated:
|
||||
logger.info(f"更新评价信息: dingdan={dingdan_id}, dashou={dashou_id}, fields={update_fields}")
|
||||
else:
|
||||
logger.info(f"无对应记录,跳过评价更新: dingdan={dingdan_id}, dashou={dashou_id}")
|
||||
else:
|
||||
logger.info(f"结算时未传入任何评价字段,跳过更新")
|
||||
|
||||
else:
|
||||
logger.warning(f"未知的行为类型: {xingwei}")
|
||||
|
||||
except IntegrityError as e:
|
||||
# 唯一约束冲突(get_or_create 理论上已处理,但并发时仍可能撞上)
|
||||
logger.warning(f"表现记录写入冲突,可能并发创建: {e}")
|
||||
except Exception as e:
|
||||
# 无论发生什么错误,都不影响主流程
|
||||
logger.error(f"记录打手表现异常: dingdan={dingdan_id}, dashou={dashou_id}, xingwei={xingwei}, error={e}", exc_info=True)
|
||||
3
dengji/tests.py
Normal file
3
dengji/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
22
dengji/urls.py
Normal file
22
dengji/urls.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from django.urls import path
|
||||
from .views import DashouBiaoqianView , BankuaiBiaoqianView, KaoheguanRegisterView, \
|
||||
KaoheguanRefreshView , ShenheDatingModuleView, ShenheDatingDataView, ShenheQiangdanView, ShenheCaozuoView,ShenheXiangqingView, KaoheJinpaiTagsView, KaoheJiluView, KaoheFreeApplyView,\
|
||||
KaoheJiluListView
|
||||
|
||||
urlpatterns = [
|
||||
path('dsbqhq', DashouBiaoqianView.as_view(), name='打手标签获取'),
|
||||
# 其他已有路由保持不变
|
||||
path('bkqhbq', BankuaiBiaoqianView.as_view(), name='抢单页面获取标签'),
|
||||
path('khgzc', KaoheguanRegisterView.as_view(), name='kaoheguan_register'),
|
||||
path('khgsx', KaoheguanRefreshView.as_view(), name='kaoheguan_refresh'),
|
||||
path('shdthqmk', ShenheDatingModuleView.as_view(), name='审核官获取板块'),
|
||||
path('hqshsj', ShenheDatingDataView.as_view(), name='审核官获取待审核列表'),
|
||||
path('shqd', ShenheQiangdanView.as_view(), name='审核官接待审核'),
|
||||
path('hqkhxq', ShenheXiangqingView.as_view(), name='获取审核内容'),
|
||||
path('khgxgkh', ShenheCaozuoView.as_view(), name='修改审核状态'),
|
||||
|
||||
path('dskhhq', KaoheJinpaiTagsView.as_view(), name='考核金牌页面标签'),
|
||||
path('dskhjl', KaoheJiluView.as_view(), name='打手考核记录'),
|
||||
path('dssqkh', KaoheFreeApplyView.as_view(), name='免费考核申请接口'),
|
||||
path('khghqkhjl', KaoheJiluListView.as_view(), name='考核官获取考核记录'),
|
||||
]
|
||||
414
dengji/utils.py
Normal file
414
dengji/utils.py
Normal file
@@ -0,0 +1,414 @@
|
||||
# dengji/utils.py
|
||||
from .models import Chenghao, KaoheCishuFeiyong, ShenheJilu, KaoheJiluFeiyong, YonghuChenghao, KaoheguanBankuai
|
||||
from yonghu.models import UserMain, UserShenheguan, UserDashou
|
||||
from django.db import models
|
||||
from decimal import Decimal
|
||||
from django.db.models import Q, Max
|
||||
import time
|
||||
import random
|
||||
import logging
|
||||
from django.db.models import F, Max
|
||||
logger = logging.getLogger('dengji')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# dingdan/utils.py
|
||||
from .models import DingdanBiaoqian
|
||||
from yonghu.models import UserMain
|
||||
from dengji.models import YonghuChenghao
|
||||
|
||||
def check_dashou_biaoqian_required(order_id, dashou_yonghuid):
|
||||
"""
|
||||
检查打手是否满足订单的标签需求。
|
||||
- 如果订单没有需求标签,直接放行。
|
||||
- 如果有,则要求打手至少拥有其中一个标签。
|
||||
返回 (allowed: bool, message: str)
|
||||
"""
|
||||
try:
|
||||
# 1. 获取订单的需求标签ID集合
|
||||
required_ids = set(
|
||||
DingdanBiaoqian.objects.filter(dingdan_id=order_id)
|
||||
.values_list('chenghao_id', flat=True)
|
||||
)
|
||||
if not required_ids:
|
||||
return True, ''
|
||||
|
||||
# 2. 找到打手对应的用户记录
|
||||
try:
|
||||
user = UserMain.objects.get(yonghuid=dashou_yonghuid)
|
||||
except UserMain.DoesNotExist:
|
||||
return False, '用户信息异常'
|
||||
|
||||
# 3. 获取打手已拥有的标签ID集合(只取打手类型)
|
||||
owned_ids = set(
|
||||
YonghuChenghao.objects.filter(
|
||||
yonghu=user,
|
||||
chenghao__leixing='dashou'
|
||||
).values_list('chenghao_id', flat=True)
|
||||
)
|
||||
|
||||
if not owned_ids:
|
||||
return False, '您未拥有任何打手标签,无法抢此订单'
|
||||
|
||||
# 4. 交集判断
|
||||
if required_ids.intersection(owned_ids):
|
||||
return True, ''
|
||||
else:
|
||||
return False, '您的标签不符合此订单要求,无法抢单'
|
||||
|
||||
except Exception as e:
|
||||
# 标签校验失败不影响主流程,记录日志并保守放行(也可保守拒绝,按需求选择)
|
||||
import logging
|
||||
logger = logging.getLogger('dingdan')
|
||||
logger.error(f"标签校验异常: {e}", exc_info=True)
|
||||
# 安全起见,如果校验出错,选择拒绝,避免越权
|
||||
return False, '订单标签校验异常,请稍后重试'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_tag_fee(chenghao, cishu):
|
||||
"""
|
||||
获取标签在指定考核次数下的费用。
|
||||
- 若有对应次数的配置,直接返回。
|
||||
- 若没有该次数,取该标签下已有最大次数的费用(作为后续次数的价格)。
|
||||
- 若该标签完全没有费用记录,返回 0.00(免费)。
|
||||
"""
|
||||
fee_obj = KaoheCishuFeiyong.objects.filter(chenghao=chenghao, cishu=cishu).first()
|
||||
if fee_obj:
|
||||
return fee_obj.feiyong
|
||||
|
||||
max_obj = KaoheCishuFeiyong.objects.filter(chenghao=chenghao).order_by('-cishu').first()
|
||||
if max_obj:
|
||||
return max_obj.feiyong
|
||||
|
||||
return Decimal('0.00')
|
||||
|
||||
|
||||
def generate_unique_jilu_id():
|
||||
"""
|
||||
生成唯一的审核记录 ID,格式:SH + 时间戳(10位) + 随机数(4位)
|
||||
循环检查数据库避免重复,最多尝试 10 次。
|
||||
"""
|
||||
for _ in range(10):
|
||||
timestamp = str(int(time.time() * 1000))[-10:]
|
||||
random_part = str(random.randint(1000, 9999))
|
||||
jilu_id = f'SH{timestamp}{random_part}'
|
||||
if not ShenheJilu.objects.filter(jilu_id=jilu_id).exists():
|
||||
return jilu_id
|
||||
raise Exception('无法生成唯一记录ID,请稍后重试')
|
||||
|
||||
|
||||
def validate_shenheguan(shenheguan_id, bankuai, fee, applicant_user):
|
||||
"""
|
||||
校验指定考核官是否合法
|
||||
- 考核官必须存在且未被禁用
|
||||
- 必须属于对应板块
|
||||
- 必须处于空闲状态
|
||||
- 若费用>0,则要求已认证
|
||||
返回 (valid, error_msg)
|
||||
"""
|
||||
if not shenheguan_id:
|
||||
return True, ''
|
||||
try:
|
||||
kg = UserShenheguan.objects.select_related('user').get(
|
||||
user__yonghuid=shenheguan_id,
|
||||
zhuangtai=1
|
||||
)
|
||||
except UserShenheguan.DoesNotExist:
|
||||
return False, '考核官不存在或已被禁用'
|
||||
|
||||
# 板块权限
|
||||
if not KaoheguanBankuai.objects.filter(kaoheguan_id=shenheguan_id, bankuai=bankuai).exists():
|
||||
return False, '该考核官不属于此板块'
|
||||
|
||||
# 空闲状态
|
||||
if kg.shenhe_zhuangtai != 0:
|
||||
return False, '该考核官当前忙碌,无法指定'
|
||||
|
||||
# 费用>0需要认证
|
||||
if float(fee) > 0 and not kg.is_renzheng:
|
||||
return False, '费用大于0时只能指定已认证的考核官'
|
||||
|
||||
return True, ''
|
||||
|
||||
|
||||
def create_shenhe_jilu(user, tag_id, game_nick, remark, reapply, jilu_id=None, shenheguan_id=''):
|
||||
"""
|
||||
创建或更新审核记录。
|
||||
- 首次申请:创建新记录,生成唯一 jilu_id。
|
||||
- 再次申请:基于原记录(必须状态为未通过)更新次数、累加总费用、更新备注和游戏昵称,
|
||||
保留审核官信息,状态重置为待审核。不创建新记录。
|
||||
- 每次申请均创建一条费用明细记录(KaoheJiluFeiyong),记录本次缴费。
|
||||
- 若原记录有考核官,则将其状态改为审核中。
|
||||
- 新增 shenheguan_id 参数:指定考核官ID,仅在原记录无考核官时可传入。
|
||||
返回 (success, message, record)
|
||||
"""
|
||||
try:
|
||||
# 1. 校验标签存在
|
||||
try:
|
||||
chenghao = Chenghao.objects.get(id=tag_id)
|
||||
except Chenghao.DoesNotExist:
|
||||
return False, '标签不存在', None
|
||||
|
||||
# 2. 判断用户是否已拥有该标签
|
||||
if YonghuChenghao.objects.filter(yonghu=user, chenghao=chenghao).exists():
|
||||
return False, '您已拥有该标签,无需重复考核', None
|
||||
|
||||
# 3. 检查是否有进行中的审核记录(待审核、审核中)
|
||||
has_pending = ShenheJilu.objects.filter(
|
||||
shenqingren_id=user.yonghuid,
|
||||
chenghao=chenghao,
|
||||
zhuangtai__in=[0, 3]
|
||||
).exists()
|
||||
if has_pending:
|
||||
return False, '您有未完成的考核申请,请等待结果', None
|
||||
|
||||
last_record = None
|
||||
current_fee = Decimal('0.00')
|
||||
|
||||
# 4. 处理再次申请
|
||||
if reapply and jilu_id:
|
||||
# 查询原记录(未通过状态)
|
||||
last_record = ShenheJilu.objects.filter(
|
||||
jilu_id=jilu_id,
|
||||
shenqingren_id=user.yonghuid,
|
||||
zhuangtai=2
|
||||
).first()
|
||||
if not last_record:
|
||||
return False, '原记录不存在或状态不是未通过', None
|
||||
|
||||
new_cishu = last_record.cishu + 1
|
||||
current_fee = get_tag_fee(chenghao, new_cishu)
|
||||
|
||||
# 确定最终状态:有考核官 = 审核中(0),否则待审核(3)
|
||||
final_kg_id = last_record.shenheguan_id # 保留原来的
|
||||
if shenheguan_id and not last_record.shenheguan_id:
|
||||
final_kg_id = shenheguan_id # 新指定
|
||||
|
||||
final_zhuangtai = 0 if final_kg_id else 3 # 有考核官就是审核中
|
||||
if final_zhuangtai == 3:
|
||||
final_kg_id = None
|
||||
|
||||
# 原子化更新
|
||||
ShenheJilu.objects.filter(pk=last_record.pk).update(
|
||||
cishu=new_cishu,
|
||||
jiaofei_jine=F('jiaofei_jine') + current_fee,
|
||||
dashou_youxi_id=game_nick,
|
||||
dashou_beizhu=remark,
|
||||
guize_neirong=chenghao.kaohe_guize or '',
|
||||
zhuangtai=final_zhuangtai,
|
||||
shenheguan_id=final_kg_id,
|
||||
)
|
||||
last_record.refresh_from_db()
|
||||
record = last_record
|
||||
|
||||
# 如果原考核官被替换或清除,释放旧考核官
|
||||
if last_record.shenheguan_id != final_kg_id:
|
||||
if last_record.shenheguan_id:
|
||||
UserShenheguan.objects.filter(
|
||||
user__yonghuid=last_record.shenheguan_id
|
||||
).update(shenhe_zhuangtai=0)
|
||||
else:
|
||||
# 首次申请
|
||||
max_cishu = ShenheJilu.objects.filter(
|
||||
shenqingren_id=user.yonghuid,
|
||||
chenghao=chenghao,
|
||||
zhuangtai__in=[1, 2]
|
||||
).aggregate(max=Max('cishu'))['max'] or 0
|
||||
new_cishu = max_cishu + 1
|
||||
current_fee = get_tag_fee(chenghao, new_cishu)
|
||||
|
||||
bankuai = chenghao.bankuai
|
||||
rule_text = chenghao.kaohe_guize or ''
|
||||
new_jilu_id = generate_unique_jilu_id()
|
||||
|
||||
initial_zhuangtai = 0 if shenheguan_id else 3 # 指定→审核中,否则待审核
|
||||
record = ShenheJilu.objects.create(
|
||||
jilu_id=new_jilu_id,
|
||||
shenqingren_id=user.yonghuid,
|
||||
zhuangtai=initial_zhuangtai,
|
||||
bankuai=bankuai,
|
||||
chenghao=chenghao,
|
||||
cishu=new_cishu,
|
||||
jiaofei_jine=current_fee,
|
||||
dashou_youxi_id=game_nick,
|
||||
dashou_beizhu=remark,
|
||||
guize_neirong=rule_text,
|
||||
shenheguan_id=shenheguan_id if shenheguan_id else None,
|
||||
)
|
||||
|
||||
# 5. 创建费用明细(防重复)
|
||||
KaoheJiluFeiyong.objects.get_or_create(
|
||||
jilu=record,
|
||||
cishu=record.cishu,
|
||||
defaults={'jine': current_fee}
|
||||
)
|
||||
|
||||
# 6. 更新考核官统计(仅当状态为审核中且有考核官)
|
||||
if record.zhuangtai == 0 and record.shenheguan_id:
|
||||
UserShenheguan.objects.filter(
|
||||
user__yonghuid=record.shenheguan_id
|
||||
).update(
|
||||
shenhe_zongshu=F('shenhe_zongshu') + 1,
|
||||
shenhe_zhuangtai=1
|
||||
)
|
||||
|
||||
return True, '申请成功', record
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"创建审核记录异常: {e}", exc_info=True)
|
||||
return False, '系统异常', None
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def create_shenhe_jilu_from_temp(user, temp_data):
|
||||
"""
|
||||
根据临时表数据创建审核记录(专供支付回调使用)
|
||||
参数 temp_data 是 KaohePayTemp 对象
|
||||
返回 (success, message, record)
|
||||
"""
|
||||
from .models import ShenheJilu, KaoheJiluFeiyong, Chenghao, YonghuChenghao
|
||||
from django.db.models import F
|
||||
import logging
|
||||
logger = logging.getLogger('dengji')
|
||||
|
||||
try:
|
||||
logger.info(f"开始从临时表创建审核记录: 用户={user.yonghuid}, 订单={temp_data.dingdan_id}")
|
||||
|
||||
chenghao = Chenghao.objects.get(id=temp_data.tag_id)
|
||||
logger.info(f"获取标签成功: {chenghao.mingcheng}")
|
||||
|
||||
# 检查是否已拥有标签
|
||||
if YonghuChenghao.objects.filter(yonghu=user, chenghao=chenghao).exists():
|
||||
logger.warning(f"用户 {user.yonghuid} 已拥有标签 {chenghao.mingcheng}")
|
||||
return False, '您已拥有该标签,无需重复考核', None
|
||||
|
||||
# 检查是否有进行中的审核
|
||||
if ShenheJilu.objects.filter(
|
||||
shenqingren_id=user.yonghuid,
|
||||
chenghao=chenghao,
|
||||
zhuangtai__in=[0, 3]
|
||||
).exists():
|
||||
logger.warning(f"用户 {user.yonghuid} 有未完成的考核申请")
|
||||
return False, '您有未完成的考核申请,请等待结果', None
|
||||
|
||||
# 使用临时表中预计算好的次数和费用
|
||||
new_cishu = temp_data.cishu
|
||||
current_fee = temp_data.fee
|
||||
reapply = temp_data.reapply
|
||||
jilu_id = temp_data.jilu_id
|
||||
shenheguan_id = temp_data.shenheguan_id
|
||||
|
||||
logger.info(f"临时表数据: cishu={new_cishu}, fee={current_fee}, reapply={reapply}, jilu_id={jilu_id}, shenheguan_id={shenheguan_id}")
|
||||
|
||||
# 再次申请校验
|
||||
if reapply:
|
||||
if not jilu_id:
|
||||
logger.error("再次申请但未提供原记录ID")
|
||||
return False, '再次申请必须提供原记录ID', None
|
||||
last = ShenheJilu.objects.filter(
|
||||
jilu_id=jilu_id,
|
||||
shenqingren_id=user.yonghuid,
|
||||
zhuangtai=2
|
||||
).first()
|
||||
if not last:
|
||||
logger.error(f"原记录不存在或状态不是未通过: jilu_id={jilu_id}")
|
||||
return False, '原记录不存在或状态不是未通过', None
|
||||
if new_cishu != last.cishu + 1:
|
||||
logger.error(f"考核次数不一致: 临时表={new_cishu}, 原记录次数={last.cishu}")
|
||||
return False, '考核次数不一致', None
|
||||
logger.info(f"再次申请校验通过,原记录ID: {jilu_id}")
|
||||
|
||||
# 创建或更新审核记录
|
||||
if reapply and jilu_id:
|
||||
# 更新原记录
|
||||
final_kg_id = last.shenheguan_id
|
||||
if shenheguan_id and not last.shenheguan_id:
|
||||
final_kg_id = shenheguan_id
|
||||
final_zhuangtai = 0 if final_kg_id else 3
|
||||
if final_zhuangtai == 3:
|
||||
final_kg_id = None
|
||||
|
||||
ShenheJilu.objects.filter(pk=last.pk).update(
|
||||
cishu=new_cishu,
|
||||
jiaofei_jine=F('jiaofei_jine') + current_fee,
|
||||
dashou_youxi_id=temp_data.game_nick,
|
||||
dashou_beizhu=temp_data.remark,
|
||||
guize_neirong=chenghao.kaohe_guize or '',
|
||||
zhuangtai=final_zhuangtai,
|
||||
shenheguan_id=final_kg_id,
|
||||
)
|
||||
last.refresh_from_db()
|
||||
record = last
|
||||
|
||||
# 释放旧考核官
|
||||
if last.shenheguan_id != final_kg_id and last.shenheguan_id:
|
||||
UserShenheguan.objects.filter(user__yonghuid=last.shenheguan_id).update(shenhe_zhuangtai=0)
|
||||
logger.info(f"释放旧考核官: {last.shenheguan_id}")
|
||||
|
||||
logger.info(f"更新审核记录成功: jilu_id={record.jilu_id}, 新次数={new_cishu}")
|
||||
else:
|
||||
# 首次申请
|
||||
bankuai = chenghao.bankuai
|
||||
rule_text = chenghao.kaohe_guize or ''
|
||||
new_jilu_id = generate_unique_jilu_id()
|
||||
initial_zhuangtai = 0 if shenheguan_id else 3
|
||||
|
||||
record = ShenheJilu.objects.create(
|
||||
jilu_id=new_jilu_id,
|
||||
shenqingren_id=user.yonghuid,
|
||||
zhuangtai=initial_zhuangtai,
|
||||
bankuai=bankuai,
|
||||
chenghao=chenghao,
|
||||
cishu=new_cishu,
|
||||
jiaofei_jine=current_fee,
|
||||
dashou_youxi_id=temp_data.game_nick,
|
||||
dashou_beizhu=temp_data.remark,
|
||||
guize_neirong=rule_text,
|
||||
shenheguan_id=shenheguan_id if shenheguan_id else None,
|
||||
)
|
||||
logger.info(f"创建审核记录成功: jilu_id={new_jilu_id}, 状态={initial_zhuangtai}")
|
||||
|
||||
# 记录费用明细
|
||||
fee_detail, created = KaoheJiluFeiyong.objects.get_or_create(
|
||||
jilu=record,
|
||||
cishu=record.cishu,
|
||||
defaults={'jine': current_fee}
|
||||
)
|
||||
if created:
|
||||
logger.info(f"创建费用明细成功: 记录ID={record.jilu_id}, 金额={current_fee}")
|
||||
else:
|
||||
logger.warning(f"费用明细已存在: 记录ID={record.jilu_id}, 次数={record.cishu}")
|
||||
|
||||
# 更新考核官状态
|
||||
if record.zhuangtai == 0 and record.shenheguan_id:
|
||||
UserShenheguan.objects.filter(user__yonghuid=record.shenheguan_id).update(
|
||||
shenhe_zongshu=F('shenhe_zongshu') + 1,
|
||||
shenhe_zhuangtai=1
|
||||
)
|
||||
logger.info(f"更新考核官状态: {record.shenheguan_id}, 审核总数+1, 状态=忙碌")
|
||||
|
||||
logger.info(f"从临时表创建审核记录完成: jilu_id={record.jilu_id}")
|
||||
return True, '申请成功', record
|
||||
|
||||
except Chenghao.DoesNotExist:
|
||||
logger.error(f"标签不存在: tag_id={temp_data.tag_id}")
|
||||
return False, '标签不存在', None
|
||||
except Exception as e:
|
||||
logger.error(f"从临时表创建审核记录异常: {str(e)}", exc_info=True)
|
||||
return False, '系统异常', None
|
||||
975
dengji/views.py
Normal file
975
dengji/views.py
Normal file
@@ -0,0 +1,975 @@
|
||||
# dengji/views.py
|
||||
import logging
|
||||
import json
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
|
||||
from shangpin.models import ShangpinLeixing
|
||||
from .models import YonghuChenghao, Chenghao, KaoheCishuFeiyong, KaoheJujueJilu
|
||||
from .utils import get_tag_fee, create_shenhe_jilu
|
||||
|
||||
logger = logging.getLogger('dengji')
|
||||
|
||||
|
||||
class DashouBiaoqianView(APIView):
|
||||
"""获取当前打手所有称号标签"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
yonghu = request.user
|
||||
# 查询用户的所有称号关联,一次连表取出
|
||||
relations = YonghuChenghao.objects.filter(
|
||||
yonghu=yonghu
|
||||
).select_related('chenghao')
|
||||
|
||||
chenghao_list = []
|
||||
for rel in relations:
|
||||
ch = rel.chenghao
|
||||
# 解析特效描述 JSON(前端需要对象)
|
||||
texiao_json = {}
|
||||
if ch.texiao_miaoshu:
|
||||
try:
|
||||
texiao_json = json.loads(ch.texiao_miaoshu)
|
||||
except json.JSONDecodeError:
|
||||
texiao_json = {}
|
||||
chenghao_list.append({
|
||||
'mingcheng': ch.mingcheng,
|
||||
'tubiao_url': ch.tubiao_url or '',
|
||||
'texiao_json': texiao_json,
|
||||
})
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '成功',
|
||||
'data': {'chenghao_list': chenghao_list}
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"获取打手标签失败: {e}", exc_info=True)
|
||||
return Response({'code': 1, 'msg': '服务器错误', 'data': None})
|
||||
|
||||
|
||||
|
||||
from shangpin.models import ShangpinLeixing
|
||||
from .models import YonghuChenghao, Chenghao
|
||||
class BankuaiBiaoqianView(APIView):
|
||||
"""获取商品类型所属板块的所有标签"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
leixing_id = request.data.get('leixing_id')
|
||||
if not leixing_id:
|
||||
return Response({'code': 1, 'msg': '缺少leixing_id'})
|
||||
|
||||
try:
|
||||
leixing = ShangpinLeixing.objects.select_related('bankuai').get(id=leixing_id)
|
||||
except ShangpinLeixing.DoesNotExist:
|
||||
return Response({'code': 2, 'msg': '商品类型不存在'})
|
||||
|
||||
bankuai = leixing.bankuai
|
||||
if not bankuai:
|
||||
return Response({'code': 0, 'data': {'biaoqian_list': []}})
|
||||
|
||||
# 获取该板块下所有类型的标签(leixing可以为dashou/shangjia等,这里先返回所有)
|
||||
chenghao_list = Chenghao.objects.filter(bankuai=bankuai)
|
||||
data = []
|
||||
for ch in chenghao_list:
|
||||
texiao_json = {}
|
||||
if ch.texiao_miaoshu:
|
||||
try:
|
||||
texiao_json = json.loads(ch.texiao_miaoshu)
|
||||
except:
|
||||
pass
|
||||
data.append({
|
||||
'id': ch.id,
|
||||
'mingcheng': ch.mingcheng,
|
||||
'texiao_json': texiao_json
|
||||
})
|
||||
return Response({'code': 0, 'data': {'biaoqian_list': data}})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# dengji/views.py
|
||||
import logging
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from .models import KaoheguanBankuai, Bankuai
|
||||
from django.db import transaction
|
||||
from yonghu.models import UserShenheguan, UserMain, UserDashou
|
||||
|
||||
logger = logging.getLogger('dengji')
|
||||
|
||||
# 硬编码邀请码
|
||||
KAOHEGUAN_INVITE_CODE = 'KHG2026VIP'
|
||||
|
||||
class KaoheguanRegisterView(APIView):
|
||||
"""
|
||||
考核官注册接口
|
||||
POST /dengji/khgzc
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
yonghu = request.user
|
||||
invite_code = request.data.get('inviteCode', '').strip()
|
||||
|
||||
if not invite_code:
|
||||
return Response({'code': 400, 'msg': '邀请码不能为空'})
|
||||
if invite_code != KAOHEGUAN_INVITE_CODE:
|
||||
return Response({'code': 400, 'msg': '邀请码错误'})
|
||||
|
||||
kg = UserShenheguan.objects.filter(user=yonghu).first()
|
||||
if kg:
|
||||
if kg.zhuangtai == 1:
|
||||
return Response({'code': 200, 'msg': '您已是考核官', 'data': self._build_data(kg)})
|
||||
else:
|
||||
return Response({'code': 400, 'msg': '账号已被封禁'})
|
||||
|
||||
with transaction.atomic():
|
||||
kg = UserShenheguan.objects.create(
|
||||
user=yonghu,
|
||||
zhuangtai=1,
|
||||
shenhe_zhuangtai=0,
|
||||
shenhe_zongshu=0,
|
||||
tongguo_zongshu=0,
|
||||
yue=0.00,
|
||||
zonge=0.00
|
||||
)
|
||||
return Response({'code': 200, 'msg': '注册成功', 'data': self._build_data(kg)})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"考核官注册失败: {e}", exc_info=True)
|
||||
return Response({'code': 500, 'msg': '服务器内部错误'})
|
||||
|
||||
def _build_data(self, kg):
|
||||
bankuai_qs = KaoheguanBankuai.objects.filter(kaoheguan_id=kg.user.yonghuid).select_related('bankuai')
|
||||
bankuai_list = [{'bankuai_id': b.bankuai.bankuai_id, 'mingcheng': b.bankuai.mingcheng} for b in bankuai_qs]
|
||||
return {
|
||||
'zhuangtai': kg.zhuangtai,
|
||||
'shenhe_zongshu': kg.shenhe_zongshu,
|
||||
'tongguo_zongshu': kg.tongguo_zongshu,
|
||||
'yue': str(kg.yue),
|
||||
'zonge': str(kg.zonge),
|
||||
'shenhe_zhuangtai': kg.shenhe_zhuangtai,
|
||||
'bankuai_list': bankuai_list,
|
||||
'kaoheguanstatus': 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class KaoheguanRefreshView(APIView):
|
||||
"""
|
||||
考核官数据刷新接口
|
||||
POST /dengji/khgsx
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
yonghu = request.user
|
||||
kg = UserShenheguan.objects.filter(user=yonghu).first()
|
||||
if not kg:
|
||||
return Response({'code': 400, 'msg': '您还未注册考核官'})
|
||||
|
||||
if kg.zhuangtai != 1:
|
||||
return Response({'code': 200, 'data': {'zhuangtai': kg.zhuangtai, 'kaoheguanstatus': 0}})
|
||||
|
||||
# 查询所属板块
|
||||
bankuai_qs = KaoheguanBankuai.objects.filter(kaoheguan_id=kg.user.yonghuid).select_related('bankuai')
|
||||
bankuai_list = [{'bankuai_id': b.bankuai.bankuai_id, 'mingcheng': b.bankuai.mingcheng} for b in bankuai_qs]
|
||||
|
||||
# 统计待审核 / 审核中的记录数量
|
||||
pending_count = ShenheJilu.objects.filter(
|
||||
shenheguan_id=yonghu.yonghuid,
|
||||
zhuangtai=0 # 审核中
|
||||
).count()
|
||||
|
||||
data = {
|
||||
'zhuangtai': kg.zhuangtai,
|
||||
'shenhe_zongshu': kg.shenhe_zongshu,
|
||||
'tongguo_zongshu': kg.tongguo_zongshu,
|
||||
'yue': str(kg.yue),
|
||||
'zonge': str(kg.zonge),
|
||||
'shenhe_zhuangtai': kg.shenhe_zhuangtai,
|
||||
'bankuai_list': bankuai_list,
|
||||
'kaoheguanstatus': 1,
|
||||
'is_renzheng': kg.is_renzheng, # 🆕 是否认证
|
||||
'pending_shenhe_count': pending_count # 🆕 审核中数量
|
||||
}
|
||||
return Response({'code': 200, 'data': data})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"考核官刷新失败: {e}", exc_info=True)
|
||||
return Response({'code': 500, 'msg': '服务器内部错误'})
|
||||
|
||||
|
||||
|
||||
import json
|
||||
from django.db import transaction, IntegrityError
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
|
||||
from .models import Bankuai, ShenheJilu, KaoheguanBankuai, Chenghao
|
||||
|
||||
class ShenheDatingModuleView(APIView):
|
||||
"""审核大厅获取模块列表(板块 + 待审核数量 + 是否关联)"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
yonghu = request.user
|
||||
related_ids = set(KaoheguanBankuai.objects.filter(kaoheguan_id=yonghu.yonghuid)
|
||||
.values_list('bankuai_id', flat=True))
|
||||
|
||||
bankuai_list = []
|
||||
for bk in Bankuai.objects.all():
|
||||
count = ShenheJilu.objects.filter(bankuai=bk, zhuangtai=3, shenheguan_id__isnull=True).count()
|
||||
bankuai_list.append({
|
||||
'bankuai_id': bk.bankuai_id,
|
||||
'mingcheng': bk.mingcheng,
|
||||
'count': count,
|
||||
'is_related': bk.bankuai_id in related_ids
|
||||
})
|
||||
|
||||
return Response({'code': 0, 'data': {'modules': bankuai_list}})
|
||||
except Exception as e:
|
||||
return Response({'code': 500, 'msg': str(e)}, status=500)
|
||||
|
||||
class ShenheDatingDataView(APIView):
|
||||
"""获取审核大厅待审核记录列表"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
data = request.data
|
||||
bankuai_id = data.get('bankuai_id')
|
||||
page = int(data.get('page', 1))
|
||||
page_size = int(data.get('page_size', 5))
|
||||
|
||||
query = ShenheJilu.objects.filter(zhuangtai=3, shenheguan_id__isnull=True)
|
||||
if bankuai_id:
|
||||
query = query.filter(bankuai_id=bankuai_id)
|
||||
|
||||
total = query.count()
|
||||
offset = (page - 1) * page_size
|
||||
jilu_list = query.select_related('bankuai', 'chenghao').order_by('-create_time')[
|
||||
offset:offset + page_size]
|
||||
|
||||
records = []
|
||||
for jl in jilu_list:
|
||||
user = UserMain.objects.filter(yonghuid=jl.shenqingren_id).first()
|
||||
avatar = user.avatar if user else ''
|
||||
texiao_json = {}
|
||||
if jl.chenghao and jl.chenghao.texiao_miaoshu:
|
||||
try:
|
||||
texiao_json = json.loads(jl.chenghao.texiao_miaoshu)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 新增字段:本次考核费用
|
||||
kaohe_feiyong = float(jl.jiaofei_jine) if jl.jiaofei_jine is not None else 0.0
|
||||
|
||||
records.append({
|
||||
'jilu_id': jl.jilu_id,
|
||||
'shenqingren_id': jl.shenqingren_id,
|
||||
'shenqingren_avatar': avatar,
|
||||
'dashou_youxi_id': jl.dashou_youxi_id or '',
|
||||
'dashou_beizhu': jl.dashou_beizhu or '',
|
||||
'bankuai_id': jl.bankuai.bankuai_id if jl.bankuai else None,
|
||||
'bankuai_mingcheng': jl.bankuai.mingcheng if jl.bankuai else '',
|
||||
'chenghao_id': jl.chenghao_id,
|
||||
'chenghao_mingcheng': jl.chenghao.mingcheng if jl.chenghao else '',
|
||||
'chenghao_texiao_json': texiao_json,
|
||||
'guize_neirong': jl.guize_neirong or '',
|
||||
'kaohe_feiyong': kaohe_feiyong, # 新增
|
||||
'create_time': jl.create_time.strftime('%Y-%m-%d %H:%M:%S') if jl.create_time else '',
|
||||
})
|
||||
|
||||
has_more = (page * page_size) < total
|
||||
return Response({'code': 0, 'data': {'list': records, 'has_more': has_more, 'total': total}})
|
||||
except Exception as e:
|
||||
return Response({'code': 500, 'msg': str(e)}, status=500)
|
||||
|
||||
class ShenheQiangdanView(APIView):
|
||||
"""
|
||||
审核官抢单(接审核任务)
|
||||
校验:身份、账号封禁、已有进行中审核、记录状态、不能审核自己、板块权限
|
||||
成功后更新考核官扩展表:审核总数+1,审核状态设为“审核中”
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
yonghu = request.user
|
||||
jilu_id = request.data.get('jilu_id')
|
||||
if not jilu_id:
|
||||
return Response({'code': 400, 'msg': '记录ID不能为空'})
|
||||
|
||||
# 1. 检查考核官身份及账号是否被禁用
|
||||
kg = UserShenheguan.objects.filter(user=yonghu, zhuangtai=1).first()
|
||||
if not kg:
|
||||
return Response({'code': 403, 'msg': '您不是考核官或账号已被禁用'})
|
||||
|
||||
# 2. 检查是否有正在审核中的记录(zhuangtai=0),防止同时审核多个
|
||||
ongoing = ShenheJilu.objects.filter(
|
||||
shenheguan_id=yonghu.yonghuid, zhuangtai=0
|
||||
).exists()
|
||||
if ongoing:
|
||||
return Response({'code': 400, 'msg': '您有正在审核中的任务,请完成后再接新任务'})
|
||||
|
||||
with transaction.atomic():
|
||||
# 3. 行锁抢单:仅当状态为待审核(3)且无审核官时才能抢
|
||||
jilu = ShenheJilu.objects.select_for_update().filter(
|
||||
jilu_id=jilu_id,
|
||||
zhuangtai=3,
|
||||
shenheguan_id__isnull=True
|
||||
).first()
|
||||
if not jilu:
|
||||
return Response({'code': 400, 'msg': '该审核任务已被接走、状态变更或不存在'})
|
||||
|
||||
# 4. 不能审核自己
|
||||
if jilu.shenqingren_id == yonghu.yonghuid:
|
||||
return Response({'code': 400, 'msg': '不能审核自己提交的申请'})
|
||||
|
||||
# 5. 板块权限:考核官必须属于该板块
|
||||
is_related = KaoheguanBankuai.objects.filter(
|
||||
kaoheguan_id=yonghu.yonghuid,
|
||||
bankuai=jilu.bankuai
|
||||
).exists()
|
||||
if not is_related:
|
||||
return Response({'code': 400, 'msg': '您不属于该板块,无法审核'})
|
||||
|
||||
# 新增:6. 费用>0时,要求考核官已认证
|
||||
if jilu.jiaofei_jine and float(jilu.jiaofei_jine) > 0:
|
||||
if not kg.is_renzheng:
|
||||
return Response({'code': 400, 'msg': '本考核需要认证考核官接单,您尚未认证'})
|
||||
|
||||
# 7. 更新审核记录:分配给当前考核官,状态变为审核中
|
||||
jilu.zhuangtai = 0 # 审核中
|
||||
jilu.shenheguan_id = yonghu.yonghuid
|
||||
jilu.save()
|
||||
|
||||
# 8. 更新考核官扩展表:审核总数 +1,审核状态置为审核中(1)
|
||||
UserShenheguan.objects.filter(user=yonghu).update(
|
||||
shenhe_zongshu=F('shenhe_zongshu') + 1,
|
||||
shenhe_zhuangtai=1
|
||||
)
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '抢单成功,请前往游戏内审核,审核完成后请到考核打分页面进行评定。',
|
||||
'data': {'bankuai_id': jilu.bankuai.bankuai_id}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"审核官抢单异常: {str(e)}", exc_info=True)
|
||||
return Response({'code': 500, 'msg': '服务器内部错误'}, status=500)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import json
|
||||
import logging
|
||||
from decimal import Decimal
|
||||
|
||||
from django.db import transaction, models
|
||||
from django.db.models import F, Q, OuterRef, Subquery, Count, Max
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
|
||||
class ShenheXiangqingView(APIView):
|
||||
"""获取当前考核官审核中的记录详情(新增待收考核费字段)"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
yonghu = request.user
|
||||
kg = UserShenheguan.objects.filter(user=yonghu, zhuangtai=1).first()
|
||||
if not kg:
|
||||
return Response({'code': 403, 'msg': '您不是考核官或账号异常'})
|
||||
|
||||
page = int(request.data.get('page', 1))
|
||||
page_size = int(request.data.get('page_size', 10))
|
||||
|
||||
# 查询当前考核官名下状态为“审核中”的记录
|
||||
base_query = ShenheJilu.objects.filter(
|
||||
shenheguan_id=yonghu.yonghuid,
|
||||
zhuangtai=0
|
||||
).select_related('bankuai', 'chenghao')
|
||||
total = base_query.count()
|
||||
offset = (page - 1) * page_size
|
||||
jilu_list = base_query[offset:offset + page_size]
|
||||
|
||||
records = []
|
||||
for jl in jilu_list:
|
||||
user = UserMain.objects.filter(yonghuid=jl.shenqingren_id).first()
|
||||
avatar = user.avatar if user else ''
|
||||
texiao_json = {}
|
||||
if jl.chenghao and jl.chenghao.texiao_miaoshu:
|
||||
try:
|
||||
texiao_json = json.loads(jl.chenghao.texiao_miaoshu)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 是否开启金牌
|
||||
is_jinpai = jl.chenghao.kaioi_jinpai if jl.chenghao else False
|
||||
|
||||
# 本次待收考核费(基于记录中的当前次数,从费用明细表查询)
|
||||
daishou_fei = 0.00
|
||||
fee_detail = KaoheJiluFeiyong.objects.filter(
|
||||
jilu=jl,
|
||||
cishu=jl.cishu
|
||||
).first()
|
||||
if fee_detail:
|
||||
daishou_fei = float(fee_detail.jine)
|
||||
else:
|
||||
# 兜底:若明细表无记录,尝试用通用费用表计算
|
||||
daishou_fei = 0.00
|
||||
# 上次失败原因
|
||||
last_fail_reason = ''
|
||||
last_fail = KaoheJujueJilu.objects.filter(
|
||||
jilu=jl,
|
||||
cishu=jl.cishu - 1
|
||||
).order_by('-create_time').first()
|
||||
if last_fail:
|
||||
last_fail_reason = last_fail.yuanyin
|
||||
|
||||
records.append({
|
||||
'jilu_id': jl.jilu_id,
|
||||
'shenqingren_id': jl.shenqingren_id,
|
||||
'shenqingren_avatar': avatar,
|
||||
'bankuai_mingcheng': jl.bankuai.mingcheng if jl.bankuai else '',
|
||||
'chenghao_mingcheng': jl.chenghao.mingcheng if jl.chenghao else '',
|
||||
'chenghao_texiao_json': texiao_json,
|
||||
'guize_neirong': jl.guize_neirong or '',
|
||||
'dashou_beizhu': jl.dashou_beizhu or '',
|
||||
'dashou_youxi_id': jl.dashou_youxi_id or '',
|
||||
'shenheguan_youxi_id': jl.shenheguan_youxi_id or '',
|
||||
'cishu': jl.cishu,
|
||||
'daishou_fei': daishou_fei, # 新增:待收考核费
|
||||
'last_fail_reason': last_fail_reason,
|
||||
'is_jinpai': is_jinpai,
|
||||
'create_time': jl.create_time.strftime('%Y-%m-%d %H:%M:%S') if jl.create_time else '',
|
||||
})
|
||||
|
||||
has_more = (page * page_size) < total
|
||||
return Response({'code': 0, 'data': {'list': records, 'has_more': has_more, 'total': total}})
|
||||
except Exception as e:
|
||||
return Response({'code': 500, 'msg': str(e)}, status=500)
|
||||
|
||||
class ShenheCaozuoView(APIView):
|
||||
"""
|
||||
考核官执行操作(同意/拒绝/转移/修改昵称),并更新考核官收益
|
||||
- 只有“同意”或“拒绝”才增加余额和总额,金额严格从 KaoheJiluFeiyong 表获取
|
||||
- 查不到费用则加 0 元,绝不影响流程
|
||||
- 转移、修改昵称等操作不涉及收益
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
yonghu = request.user
|
||||
# 1. 验证考核官身份及账号状态
|
||||
kg = UserShenheguan.objects.filter(user=yonghu, zhuangtai=1).first()
|
||||
if not kg:
|
||||
return Response({'code': 403, 'msg': '账号异常,无法操作'})
|
||||
|
||||
jilu_id = request.data.get('jilu_id')
|
||||
caozuo = request.data.get('caozuo') # agree / reject / transfer / modify_nick
|
||||
if not jilu_id or not caozuo:
|
||||
return Response({'code': 400, 'msg': '缺少必要参数'})
|
||||
|
||||
with transaction.atomic():
|
||||
# 2. 锁定审核记录,确保属于当前考核官且状态为审核中(0)
|
||||
jilu = ShenheJilu.objects.select_for_update().filter(
|
||||
jilu_id=jilu_id,
|
||||
shenheguan_id=yonghu.yonghuid,
|
||||
zhuangtai=0
|
||||
).first()
|
||||
if not jilu:
|
||||
return Response({'code': 400, 'msg': '该记录不存在或不属于您'})
|
||||
|
||||
# 3. 本次操作产生的收入(仅通过/拒绝时有意义)
|
||||
daishou_fei = 0.00
|
||||
|
||||
# 4. 根据操作类型执行对应业务逻辑
|
||||
if caozuo == 'agree':
|
||||
# --- 通过考核 ---
|
||||
# 查询本次考核应得的费用(基于记录ID和当前次数)
|
||||
fee_detail = KaoheJiluFeiyong.objects.filter(
|
||||
jilu=jilu,
|
||||
cishu=jilu.cishu
|
||||
).first()
|
||||
if fee_detail:
|
||||
daishou_fei = float(fee_detail.jine)
|
||||
# 找不到就保持 0,绝不使用任何默认值
|
||||
|
||||
jilu.zhuangtai = 1
|
||||
jilu.save()
|
||||
|
||||
# 授予标签
|
||||
if jilu.chenghao:
|
||||
yh = UserMain.objects.get(yonghuid=jilu.shenqingren_id)
|
||||
if not YonghuChenghao.objects.filter(yonghu=yh, chenghao=jilu.chenghao).exists():
|
||||
YonghuChenghao.objects.create(yonghu=yh, chenghao=jilu.chenghao)
|
||||
# 如果标签开启了金牌,同步更新打手称号
|
||||
if jilu.chenghao.kaioi_jinpai:
|
||||
dashou_prof = UserDashou.objects.filter(user=yh).first()
|
||||
if dashou_prof:
|
||||
dashou_prof.chenghao = '金牌选手'
|
||||
dashou_prof.save()
|
||||
|
||||
elif caozuo == 'reject':
|
||||
# --- 拒绝考核 ---
|
||||
yuanyin = request.data.get('yuanyin', '')
|
||||
if not yuanyin.strip():
|
||||
return Response({'code': 400, 'msg': '请填写拒绝原因'})
|
||||
|
||||
# 查询本次考核应得的费用(拒绝时同样获得收入)
|
||||
fee_detail = KaoheJiluFeiyong.objects.filter(
|
||||
jilu=jilu,
|
||||
cishu=jilu.cishu
|
||||
).first()
|
||||
if fee_detail:
|
||||
daishou_fei = float(fee_detail.jine)
|
||||
# 找不到就保持 0
|
||||
|
||||
jilu.zhuangtai = 2
|
||||
jilu.save()
|
||||
# 记录拒绝原因
|
||||
KaoheJujueJilu.objects.create(
|
||||
jilu=jilu,
|
||||
cishu=jilu.cishu,
|
||||
shenheguan_id=yonghu.yonghuid,
|
||||
yuanyin=yuanyin
|
||||
)
|
||||
|
||||
# 新增:不合格时,如果考核官未认证,则清空考核官ID和游戏昵称
|
||||
if not kg.is_renzheng:
|
||||
jilu.shenheguan_id = None
|
||||
jilu.shenheguan_youxi_id = ''
|
||||
jilu.save(update_fields=['shenheguan_id', 'shenheguan_youxi_id'])
|
||||
|
||||
elif caozuo == 'transfer':
|
||||
# 转移考核官:清空考核官信息,状态回退为待审核
|
||||
jilu.shenheguan_id = None
|
||||
jilu.shenheguan_youxi_id = None
|
||||
jilu.zhuangtai = 3
|
||||
jilu.save()
|
||||
# 转移不更新收益,直接返回
|
||||
|
||||
|
||||
elif caozuo == 'modify_nick':
|
||||
# 修改考核官游戏昵称
|
||||
youxi_id = request.data.get('youxi_id', '').strip()
|
||||
if not youxi_id:
|
||||
return Response({'code': 400, 'msg': '请输入游戏昵称'})
|
||||
jilu.shenheguan_youxi_id = youxi_id
|
||||
jilu.save()
|
||||
return Response({'code': 0, 'msg': '修改成功'})
|
||||
|
||||
else:
|
||||
return Response({'code': 400, 'msg': '无效的操作类型'})
|
||||
|
||||
# 5. 更新考核官收益(仅“通过”或“拒绝”时执行)
|
||||
if caozuo in ('transfer'):
|
||||
update_fields = {
|
||||
|
||||
'shenhe_zhuangtai': 0, # 设为空闲
|
||||
}
|
||||
UserShenheguan.objects.filter(user=yonghu).update(**update_fields)
|
||||
if caozuo in ('agree', 'reject'):
|
||||
update_fields = {
|
||||
'yue': F('yue') + Decimal(str(daishou_fei)),
|
||||
'zonge': F('zonge') + Decimal(str(daishou_fei)),
|
||||
'shenhe_zhuangtai': 0, # 设为空闲
|
||||
}
|
||||
# 只有同意时才增加“通过总数”
|
||||
if caozuo == 'agree':
|
||||
update_fields['tongguo_zongshu'] = F('tongguo_zongshu') + 1
|
||||
|
||||
# 审核总数不再增加
|
||||
UserShenheguan.objects.filter(user=yonghu).update(**update_fields)
|
||||
|
||||
return Response({'code': 0, 'msg': '操作成功'})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"考核操作异常: {str(e)}", exc_info=True)
|
||||
return Response({'code': 500, 'msg': '服务器内部错误'}, status=500)
|
||||
|
||||
|
||||
|
||||
|
||||
# dengji/views.py
|
||||
class KaoheJinpaiTagsView(APIView):
|
||||
"""获取考核金牌页面的标签和板块信息"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user = request.user
|
||||
# 获取用户已拥有的标签名称集合
|
||||
owned_names = set(
|
||||
YonghuChenghao.objects.filter(yonghu=user, chenghao__leixing='dashou')
|
||||
.values_list('chenghao__mingcheng', flat=True)
|
||||
)
|
||||
|
||||
# 板块列表
|
||||
plate_list = []
|
||||
for bk in Bankuai.objects.all():
|
||||
tags = Chenghao.objects.filter(bankuai=bk, leixing='dashou')
|
||||
tag_list = []
|
||||
for tag in tags:
|
||||
# 费用列表
|
||||
fee_objs = KaoheCishuFeiyong.objects.filter(chenghao=tag).order_by('cishu')
|
||||
fee_list = [{'cishu': f.cishu, 'feiyong': float(f.feiyong)} for f in fee_objs]
|
||||
|
||||
# 若没有记录,补充默认费用
|
||||
if not fee_list:
|
||||
fee_list = [{'cishu': 1, 'feiyong': 5.00}]
|
||||
|
||||
tag_list.append({
|
||||
'id': tag.id,
|
||||
'name': tag.mingcheng,
|
||||
'texiao_json': json.loads(tag.texiao_miaoshu) if tag.texiao_miaoshu else {},
|
||||
'jinpai': tag.kaioi_jinpai,
|
||||
'rule': tag.kaohe_guize or '',
|
||||
'fee_list': fee_list,
|
||||
'owned': tag.mingcheng in owned_names,
|
||||
})
|
||||
|
||||
if tag_list:
|
||||
plate_list.append({
|
||||
'bankuai_id': bk.bankuai_id,
|
||||
'mingcheng': bk.mingcheng,
|
||||
'tags': tag_list
|
||||
})
|
||||
|
||||
# 新增:返回所有在职考核官信息(用于指定考核官)
|
||||
kaoheguan_list = []
|
||||
for kg in UserShenheguan.objects.filter(zhuangtai=1).select_related('user'):
|
||||
uid = kg.user.yonghuid
|
||||
avatar = kg.user.avatar or ''
|
||||
# 获取考核官的打手昵称(从打手扩展表)
|
||||
dashou_nick = ''
|
||||
dashou_prof = UserDashou.objects.filter(user=kg.user).first()
|
||||
if dashou_prof:
|
||||
dashou_nick = dashou_prof.nicheng or ''
|
||||
kaoheguan_list.append({
|
||||
'yonghuid': uid,
|
||||
'avatar': avatar,
|
||||
'dashou_nick': dashou_nick,
|
||||
'is_renzheng': kg.is_renzheng,
|
||||
'shenhe_zhuangtai': kg.shenhe_zhuangtai,
|
||||
})
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': {
|
||||
'plate_list': plate_list,
|
||||
'my_tags': list(owned_names),
|
||||
'kaoheguan_list': kaoheguan_list, # 新增
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
return Response({'code': 500, 'msg': str(e)}, status=500)
|
||||
|
||||
# dengji/utils.py
|
||||
from .models import Chenghao, KaoheCishuFeiyong, ShenheJilu, KaoheJiluFeiyong, YonghuChenghao
|
||||
from yonghu.models import UserMain
|
||||
from django.db import models
|
||||
class KaoheJiluView(APIView):
|
||||
"""获取打手本人的考核记录(支持筛选状态)"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user = request.user
|
||||
status = request.data.get('status', 'pending') # pending / passed
|
||||
page = int(request.data.get('page', 1))
|
||||
page_size = int(request.data.get('page_size', 5))
|
||||
|
||||
if status == 'pending':
|
||||
# 进行中/未通过:审核中(0)、待审核(3)、未通过(2)
|
||||
query = ShenheJilu.objects.filter(
|
||||
shenqingren_id=user.yonghuid,
|
||||
zhuangtai__in=[0, 2, 3]
|
||||
)
|
||||
else:
|
||||
query = ShenheJilu.objects.filter(
|
||||
shenqingren_id=user.yonghuid,
|
||||
zhuangtai=1 # 已通过
|
||||
)
|
||||
|
||||
total = query.count()
|
||||
records = query.order_by('-create_time')[(page-1)*page_size : page*page_size]
|
||||
|
||||
data = []
|
||||
for jl in records:
|
||||
# 汇总缴纳总额
|
||||
total_fee = KaoheJiluFeiyong.objects.filter(jilu=jl).aggregate(
|
||||
total=models.Sum('jine')
|
||||
)['total'] or 0.00
|
||||
|
||||
# 上次拒绝原因(最近一条)
|
||||
last_fail = KaoheJujueJilu.objects.filter(jilu=jl).order_by('-create_time').first()
|
||||
fail_reason = last_fail.yuanyin if last_fail else ''
|
||||
|
||||
# 考核官头像
|
||||
kg_user = UserMain.objects.filter(yonghuid=jl.shenheguan_id).first()
|
||||
kg_avatar = kg_user.avatar if kg_user else ''
|
||||
|
||||
data.append({
|
||||
'jilu_id': jl.jilu_id,
|
||||
'chenghao_name': jl.chenghao.mingcheng if jl.chenghao else '',
|
||||
'bankuai_name': jl.bankuai.mingcheng if jl.bankuai else '',
|
||||
'zhuangtai': jl.zhuangtai,
|
||||
'statusText': dict(ShenheJilu._meta.get_field('zhuangtai').choices).get(jl.zhuangtai, '未知'),
|
||||
'statusColor': {0: '#FFA500', 1: '#27ae60', 2: '#e74c3c', 3: '#3498db'}.get(jl.zhuangtai, '#888'),
|
||||
'shenheguan_id': jl.shenheguan_id,
|
||||
'shenheguan_avatar': kg_avatar,
|
||||
'dashou_youxi_id': jl.dashou_youxi_id,
|
||||
'dashou_beizhu': jl.dashou_beizhu,
|
||||
'cishu': jl.cishu,
|
||||
'total_fee': float(total_fee),
|
||||
'last_fail_reason': fail_reason,
|
||||
'create_time': jl.create_time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
# 为再次申请预留标签对象数据(前端可能用到)
|
||||
'tag_obj': {
|
||||
'id': jl.chenghao_id,
|
||||
'name': jl.chenghao.mingcheng if jl.chenghao else '',
|
||||
# 其他字段按需,但申请时会重新拉取
|
||||
} if jl.chenghao else None,
|
||||
})
|
||||
|
||||
has_more = (page * page_size) < total
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': {'list': data, 'has_more': has_more, 'total': total}
|
||||
})
|
||||
except Exception as e:
|
||||
return Response({'code': 500, 'msg': str(e)}, status=500)
|
||||
|
||||
|
||||
|
||||
from .utils import validate_shenheguan
|
||||
class KaoheFreeApplyView(APIView):
|
||||
"""免费申请考核(首次/再次,费用为0时调用)"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user = request.user
|
||||
tag_id = request.data.get('tag_id')
|
||||
game_nick = request.data.get('game_nick', '').strip()
|
||||
remark = request.data.get('remark', '').strip()
|
||||
reapply = request.data.get('reapply', False)
|
||||
jilu_id = request.data.get('jilu_id') if reapply else None
|
||||
shenheguan_id = request.data.get('shenheguan_id', '').strip() # 新增
|
||||
|
||||
if not tag_id or not game_nick:
|
||||
return Response({'code': 400, 'msg': '标签ID和游戏昵称必填'})
|
||||
|
||||
# 校验费用是否为0
|
||||
try:
|
||||
chenghao = Chenghao.objects.get(id=tag_id)
|
||||
except Chenghao.DoesNotExist:
|
||||
return Response({'code': 400, 'msg': '标签不存在'})
|
||||
|
||||
# 计算本次次数
|
||||
if reapply and jilu_id:
|
||||
last_record = ShenheJilu.objects.filter(
|
||||
jilu_id=jilu_id, shenqingren_id=user.yonghuid, zhuangtai=2
|
||||
).first()
|
||||
if not last_record:
|
||||
return Response({'code': 400, 'msg': '原记录不存在或状态不是未通过'})
|
||||
new_cishu = last_record.cishu + 1
|
||||
else:
|
||||
max_cishu = ShenheJilu.objects.filter(
|
||||
shenqingren_id=user.yonghuid, chenghao=chenghao, zhuangtai__in=[1,2]
|
||||
).aggregate(max=models.Max('cishu'))['max'] or 0
|
||||
new_cishu = max_cishu + 1
|
||||
|
||||
fee = get_tag_fee(chenghao, new_cishu)
|
||||
if fee > 0:
|
||||
return Response({'code': 400, 'msg': '您有未通过的记录请查看并重考'})
|
||||
|
||||
# 新增:如果指定了考核官,进行合法性校验
|
||||
if shenheguan_id:
|
||||
valid, err = validate_shenheguan(shenheguan_id, chenghao.bankuai, fee, user)
|
||||
if not valid:
|
||||
return Response({'code': 400, 'msg': err})
|
||||
|
||||
# 调用公共创建逻辑
|
||||
success, msg, record = create_shenhe_jilu(
|
||||
user, tag_id, game_nick, remark, reapply, jilu_id,
|
||||
shenheguan_id=shenheguan_id # 新增参数
|
||||
)
|
||||
if success:
|
||||
return Response({'code': 0, 'msg': msg, 'data': {'jilu_id': record.jilu_id}})
|
||||
else:
|
||||
return Response({'code': 400, 'msg': msg})
|
||||
except Exception as e:
|
||||
return Response({'code': 500, 'msg': str(e)}, status=500)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class KaoheJiluListView(APIView):
|
||||
"""
|
||||
获取当前考核官的历史审核记录
|
||||
路径:POST /dengji/khghqkhjl
|
||||
权限:JWT Token (考核官)
|
||||
参数:
|
||||
bankuai_id : int (可选,0或空表示全部)
|
||||
status : int (可选,0-全部 1-审核中 2-已通过 3-未通过)
|
||||
page : int (默认1)
|
||||
page_size : int (默认5)
|
||||
返回:
|
||||
list, has_more, total
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
yonghu = request.user
|
||||
# 1. 验证考核官身份及账号状态
|
||||
kg = UserShenheguan.objects.filter(user=yonghu, zhuangtai=1).first()
|
||||
if not kg:
|
||||
return Response({'code': 403, 'msg': '您不是考核官或账号异常'})
|
||||
|
||||
# 2. 获取参数
|
||||
bankuai_id = request.data.get('bankuai_id')
|
||||
status = int(request.data.get('status', 0)) # 0全部 1审核中 2已通过 3未通过
|
||||
page = int(request.data.get('page', 1))
|
||||
page_size = int(request.data.get('page_size', 5))
|
||||
|
||||
# 3. 构建查询条件:只查当前考核官审核的记录
|
||||
base_query = ShenheJilu.objects.filter(shenheguan_id=yonghu.yonghuid)
|
||||
|
||||
# 板块筛选
|
||||
if bankuai_id and int(bankuai_id) > 0:
|
||||
base_query = base_query.filter(bankuai_id=int(bankuai_id))
|
||||
|
||||
# 状态筛选(前端传的状态映射到后端zhuangtai字段)
|
||||
status_map = {
|
||||
0: None, # 全部不筛选
|
||||
1: 0, # 审核中
|
||||
2: 1, # 已通过
|
||||
3: 2, # 未通过
|
||||
}
|
||||
if status_map.get(status) is not None:
|
||||
base_query = base_query.filter(zhuangtai=status_map[status])
|
||||
|
||||
# 4. 计数与分页
|
||||
total = base_query.count()
|
||||
offset = (page - 1) * page_size
|
||||
jilu_list = base_query.order_by('-create_time')[offset:offset + page_size]
|
||||
|
||||
# 5. 预取关联数据,减少查询次数
|
||||
jilu_list = jilu_list.select_related('bankuai', 'chenghao')
|
||||
# 为每条记录预取费用明细和拒绝记录(通过 related_name 或手动过滤)
|
||||
# 由于 KaoheJiluFeiyong 和 KaoheJujueJilu 通过外键指向 ShenheJilu (to_field='jilu_id'),
|
||||
# 可以使用 prefetch_related
|
||||
jilu_ids = [j.jilu_id for j in jilu_list]
|
||||
fee_dict = {}
|
||||
reject_dict = {}
|
||||
if jilu_ids:
|
||||
# 批量获取所有费用明细
|
||||
fee_qs = KaoheJiluFeiyong.objects.filter(jilu_id__in=jilu_ids).order_by('cishu')
|
||||
for fee in fee_qs:
|
||||
if fee.jilu_id not in fee_dict:
|
||||
fee_dict[fee.jilu_id] = []
|
||||
fee_dict[fee.jilu_id].append({'cishu': fee.cishu, 'jine': float(fee.jine)})
|
||||
|
||||
# 批量获取所有拒绝记录
|
||||
reject_qs = KaoheJujueJilu.objects.filter(jilu_id__in=jilu_ids).order_by('cishu')
|
||||
for rej in reject_qs:
|
||||
if rej.jilu_id not in reject_dict:
|
||||
reject_dict[rej.jilu_id] = []
|
||||
reject_dict[rej.jilu_id].append({
|
||||
'cishu': rej.cishu,
|
||||
'yuanyin': rej.yuanyin,
|
||||
'create_time': rej.create_time.strftime('%Y-%m-%d %H:%M:%S') if rej.create_time else ''
|
||||
})
|
||||
|
||||
# 6. 批量获取申请人信息(昵称和头像)
|
||||
applicant_ids = {j.shenqingren_id for j in jilu_list}
|
||||
user_map = {}
|
||||
if applicant_ids:
|
||||
users = UserMain.objects.filter(yonghuid__in=applicant_ids).select_related('dashou_profile')
|
||||
for u in users:
|
||||
avatar = u.avatar or ''
|
||||
nicheng = ''
|
||||
try:
|
||||
# 打手扩展表可能有 nicheng
|
||||
nicheng = u.dashou_profile.nicheng if u.dashou_profile else ''
|
||||
except UserDashou.DoesNotExist:
|
||||
pass
|
||||
user_map[u.yonghuid] = {
|
||||
'avatar': avatar,
|
||||
'nicheng': nicheng or f'用户{u.yonghuid}'
|
||||
}
|
||||
|
||||
# 7. 组装数据
|
||||
records = []
|
||||
for jl in jilu_list:
|
||||
user_info = user_map.get(jl.shenqingren_id, {'avatar': '', 'nicheng': '未知'})
|
||||
# 标签特效JSON
|
||||
texiao_json = {}
|
||||
if jl.chenghao and jl.chenghao.texiao_miaoshu:
|
||||
try:
|
||||
texiao_json = json.loads(jl.chenghao.texiao_miaoshu)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 汇总总缴纳费用
|
||||
fee_items = fee_dict.get(jl.jilu_id, [])
|
||||
total_fee = sum(item['jine'] for item in fee_items)
|
||||
|
||||
records.append({
|
||||
'jilu_id': jl.jilu_id,
|
||||
'shenqingren_id': jl.shenqingren_id,
|
||||
'shenqingren_avatar': user_info['avatar'],
|
||||
'shenqingren_nicheng': user_info['nicheng'],
|
||||
'bankuai_mingcheng': jl.bankuai.mingcheng if jl.bankuai else '',
|
||||
'chenghao_mingcheng': jl.chenghao.mingcheng if jl.chenghao else '',
|
||||
'chenghao_texiao_json': texiao_json,
|
||||
'zhuangtai': jl.zhuangtai, # 0审核中 1通过 2未通过 3待审核
|
||||
'cishu': jl.cishu,
|
||||
'total_fee': total_fee,
|
||||
'dashou_youxi_id': jl.dashou_youxi_id or '',
|
||||
'dashou_beizhu': jl.dashou_beizhu or '',
|
||||
'shenheguan_youxi_id': jl.shenheguan_youxi_id or '',
|
||||
'guize_neirong': jl.guize_neirong or '',
|
||||
'create_time': jl.create_time.strftime('%Y-%m-%d %H:%M:%S') if jl.create_time else '',
|
||||
'update_time': jl.update_time.strftime('%Y-%m-%d %H:%M:%S') if jl.update_time else '',
|
||||
'fee_list': fee_items,
|
||||
'reject_list': reject_dict.get(jl.jilu_id, []),
|
||||
})
|
||||
|
||||
has_more = (page * page_size) < total
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': {
|
||||
'list': records,
|
||||
'has_more': has_more,
|
||||
'total': total
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取考核记录异常: {str(e)}", exc_info=True)
|
||||
return Response({'code': 500, 'msg': '服务器内部错误'}, status=500)
|
||||
0
dingdan/__init__.py
Normal file
0
dingdan/__init__.py
Normal file
3
dingdan/admin.py
Normal file
3
dingdan/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
10
dingdan/apps.py
Normal file
10
dingdan/apps.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DingdanConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'dingdan'
|
||||
|
||||
def ready(self):
|
||||
# 导入信号处理,确保信号被注册
|
||||
import dingdan.signals
|
||||
38
dingdan/cross_platform_tasks.py
Normal file
38
dingdan/cross_platform_tasks.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# dingdan/cross_platform_tasks.py
|
||||
import logging
|
||||
import requests
|
||||
from django.conf import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def send_partner_sync_order(partner_order_id, jine, dashou_fencheng, jieshao, beizhu, create_time):
|
||||
"""
|
||||
异步发送派单通知给对方平台(使用简单线程,非Celery)
|
||||
注意:此函数应在线程中调用,避免阻塞主请求。
|
||||
生产环境建议使用Celery,此处提供线程兼容版本。
|
||||
"""
|
||||
url = f"{settings.PARTNER_BASE_URL}/dingdan/partner_sync_order"
|
||||
payload = {
|
||||
'token': settings.CROSS_PLATFORM_TOKEN,
|
||||
'partner_order_id': partner_order_id,
|
||||
'jine': jine,
|
||||
'dashou_fencheng': dashou_fencheng,
|
||||
'jieshao': jieshao,
|
||||
'beizhu': beizhu,
|
||||
'create_time': create_time,
|
||||
}
|
||||
try:
|
||||
response = requests.post(url, json=payload, timeout=5)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get('code') == 0:
|
||||
logger.info(f"跨平台派单同步成功: {partner_order_id}")
|
||||
# 可在此处保存对方返回的 local_order_id 到本地订单(如果需要)
|
||||
# local_order_id = data['data'].get('local_order_id')
|
||||
# Dingdan.objects.filter(dingdan_id=partner_order_id).update(partner_order_id=local_order_id)
|
||||
else:
|
||||
logger.error(f"跨平台派单同步业务失败: {data}")
|
||||
else:
|
||||
logger.error(f"跨平台派单同步HTTP失败: {response.status_code}")
|
||||
except Exception as e:
|
||||
logger.error(f"跨平台派单同步异常: {str(e)}", exc_info=True)
|
||||
205
dingdan/migrations/0001_initial.py
Normal file
205
dingdan/migrations/0001_initial.py
Normal file
@@ -0,0 +1,205 @@
|
||||
# Generated by Django 5.2.10 on 2026-01-14 19:59
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Lilubiao',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('lilu', models.DecimalField(decimal_places=4, max_digits=5, null=True, verbose_name='平台分成利率,1是平台,2是商家')),
|
||||
('fadanpingtai', models.CharField(max_length=10, null=True, verbose_name='订单所属平台,(商家或平台)')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'lilubiao',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Ptliushui',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('zonge', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='收取总金额')),
|
||||
('zhichuzonge', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='支出总金额')),
|
||||
('zongshouyi', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='总收益')),
|
||||
('rizonge', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='日收取')),
|
||||
('rizhichu', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='日支出')),
|
||||
('rishouyi', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='日收益')),
|
||||
('zongshoucishu', models.IntegerField(default=0, verbose_name='总收取次数')),
|
||||
('rishoucishu', models.IntegerField(default=0, verbose_name='日收取次数')),
|
||||
('zongzhichucishu', models.IntegerField(default=0, verbose_name='总支出次数')),
|
||||
('rizhichucishu', models.IntegerField(default=0, verbose_name='日支出次数')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'ptliushui',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Chufajilu',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dashouid', models.CharField(db_index=True, max_length=7, null=True, verbose_name='被罚打手ID')),
|
||||
('qingqiuid', models.CharField(db_index=True, max_length=7, null=True, verbose_name='请求处罚ID')),
|
||||
('chuliid', models.CharField(db_index=True, max_length=7, null=True, verbose_name='处理处罚ID')),
|
||||
('cfliyou', models.TextField(blank=True, null=True, verbose_name='处罚理由')),
|
||||
('sqzhuangtai', models.IntegerField(blank=True, default=0, null=True, verbose_name='处罚申请状态:0待处理,1成功,2驳回')),
|
||||
('bhliyou', models.TextField(blank=True, null=True, verbose_name='驳回处罚理由')),
|
||||
('jifen', models.IntegerField(default=0, verbose_name='扣除打手积分')),
|
||||
('dingdan_id', models.CharField(db_index=True, max_length=32, null=True, verbose_name='订单ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'chufajilu',
|
||||
'indexes': [models.Index(fields=['dashouid'], name='chufajilu_dashoui_07aac0_idx'), models.Index(fields=['qingqiuid'], name='chufajilu_qingqiu_ac160e_idx'), models.Index(fields=['chuliid'], name='chufajilu_chuliid_cd63ac_idx'), models.Index(fields=['dingdan_id'], name='chufajilu_dingdan_b8f0c2_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Dashoutupian',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dingdan_id', models.CharField(db_index=True, max_length=32, null=True, verbose_name='订单ID')),
|
||||
('dashou', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='接单打手ID')),
|
||||
('tupian', models.CharField(blank=True, max_length=500, null=True, verbose_name='打手提交图片URL')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'dashoutupian',
|
||||
'indexes': [models.Index(fields=['dashou'], name='dashoutupia_dashou_7de8bd_idx'), models.Index(fields=['dingdan_id'], name='dashoutupia_dingdan_97c685_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Dingdan',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dingdan_id', models.CharField(db_index=True, max_length=32, unique=True, verbose_name='订单ID')),
|
||||
('zhuangtai', models.IntegerField(blank=True, db_index=True, default=1, null=True, verbose_name='订单状态')),
|
||||
('fadan_pingtai', models.IntegerField(blank=True, null=True, verbose_name='发单平台')),
|
||||
('jine', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='订单金额')),
|
||||
('dashou_fencheng', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='打手分成')),
|
||||
('jiedan_dashou_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='接单打手ID')),
|
||||
('dashou_liuyan', models.TextField(blank=True, null=True, verbose_name='打手留言')),
|
||||
('zhiding_id', models.CharField(blank=True, max_length=32, null=True, verbose_name='指定ID')),
|
||||
('shangpin_id', models.IntegerField(blank=True, null=True, verbose_name='商品ID')),
|
||||
('leixing_id', models.PositiveIntegerField(blank=True, null=True, verbose_name='商品类型ID')),
|
||||
('tupian', models.CharField(blank=True, max_length=500, null=True, verbose_name='商品图片URL')),
|
||||
('yaoqiuleixing', models.PositiveIntegerField(blank=True, null=True, verbose_name='抢单要求类型')),
|
||||
('huiyuan_id', models.CharField(blank=True, max_length=6, null=True, verbose_name='会员ID')),
|
||||
('yongjin', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='佣金要求')),
|
||||
('jieshao', models.TextField(blank=True, null=True, verbose_name='订单介绍')),
|
||||
('beizhu', models.TextField(blank=True, null=True, verbose_name='订单备注')),
|
||||
('nicheng', models.CharField(blank=True, max_length=50, null=True, verbose_name='游戏昵称')),
|
||||
('user1_id', models.CharField(db_index=True, max_length=30, null=True, verbose_name='用户1标识')),
|
||||
('user2_id', models.CharField(db_index=True, max_length=30, null=True, verbose_name='用户2打手标识')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '订单',
|
||||
'verbose_name_plural': '订单',
|
||||
'db_table': 'dingdan',
|
||||
'indexes': [models.Index(fields=['zhuangtai'], name='dingdan_zhuangt_2febe8_idx'), models.Index(fields=['dingdan_id'], name='dingdan_dingdan_83dcb6_idx'), models.Index(fields=['jiedan_dashou_id'], name='dingdan_jiedan__921bfa_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Liaotian',
|
||||
fields=[
|
||||
('id', models.BigAutoField(primary_key=True, serialize=False, verbose_name='主键ID')),
|
||||
('user1_id', models.CharField(db_index=True, max_length=30, verbose_name='用户1标识')),
|
||||
('user2_id', models.CharField(db_index=True, max_length=30, verbose_name='用户2标识')),
|
||||
('dingdan_id', models.CharField(max_length=32, unique=True, verbose_name='订单ID')),
|
||||
('zhuangtai', models.IntegerField(choices=[(1, '开启中'), (0, '已关闭')], db_index=True, default=1, verbose_name='聊天状态')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'liaotian',
|
||||
'indexes': [models.Index(fields=['user1_id'], name='liaotian_user1_i_6be6cd_idx'), models.Index(fields=['user2_id'], name='liaotian_user2_i_9f61a4_idx'), models.Index(fields=['zhuangtai'], name='liaotian_zhuangt_5e8be5_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Pingfen',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dashou', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='接单打手ID')),
|
||||
('zongfen', models.PositiveIntegerField(default=0, verbose_name='总分')),
|
||||
('cishu', models.PositiveIntegerField(default=0, verbose_name='评分次数')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '评分表',
|
||||
'db_table': 'pingfenbiao',
|
||||
'indexes': [models.Index(fields=['dashou'], name='pingfenbiao_dashou_f62f10_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Tuikuanjilu',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dingdan_id', models.CharField(max_length=32, unique=True, verbose_name='订单ID')),
|
||||
('dashouid', models.CharField(max_length=7, null=True, verbose_name='接单打手ID')),
|
||||
('qingqiuid', models.CharField(max_length=7, null=True, verbose_name='请求用户ID')),
|
||||
('chuliid', models.CharField(db_index=True, max_length=7, null=True, verbose_name='处理人ID')),
|
||||
('liyou', models.TextField(blank=True, null=True, verbose_name='理由')),
|
||||
('sqzhuangtai', models.IntegerField(blank=True, db_index=True, default=0, null=True, verbose_name='申请状态:0待处理,1成功,2驳回')),
|
||||
('bhliyou', models.TextField(blank=True, null=True, verbose_name='驳回理由')),
|
||||
('jine', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='订单金额')),
|
||||
('dashou_fencheng', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='打手分成')),
|
||||
('jieshao', models.TextField(blank=True, null=True, verbose_name='订单介绍')),
|
||||
('beizhu', models.TextField(blank=True, null=True, verbose_name='订单备注')),
|
||||
('nicheng', models.CharField(blank=True, max_length=50, null=True, verbose_name='游戏昵称')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'tuikuanjilu',
|
||||
'indexes': [models.Index(fields=['chuliid'], name='tuikuanjilu_chuliid_0b0f16_idx'), models.Index(fields=['sqzhuangtai'], name='tuikuanjilu_sqzhuan_d89dc9_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DingdanPingtai',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('laoban_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='老板ID')),
|
||||
('laoban_pingjia', models.TextField(blank=True, null=True, verbose_name='老板评价')),
|
||||
('dingdan', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='pingtai_kuozhan', to='dingdan.dingdan', verbose_name='订单')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '平台订单扩展',
|
||||
'verbose_name_plural': '平台订单扩展',
|
||||
'db_table': 'dingdan_pingtai',
|
||||
'indexes': [models.Index(fields=['laoban_id'], name='dingdan_pin_laoban__d55e6f_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DingdanShangjia',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('shangjia_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='商家ID')),
|
||||
('shangjia_pingjia', models.TextField(blank=True, null=True, verbose_name='商家评价')),
|
||||
('cfliyou', models.TextField(blank=True, null=True, verbose_name='商家处罚理由')),
|
||||
('sqzhuangtai', models.IntegerField(blank=True, default=0, null=True, verbose_name='处罚申请状态:0待处理,1成功,2驳回')),
|
||||
('bhliyou', models.TextField(blank=True, null=True, verbose_name='驳回商家处罚理由')),
|
||||
('sjnicheng', models.CharField(max_length=50, null=True, verbose_name='商家昵称')),
|
||||
('dingdan', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='shangjia_kuozhan', to='dingdan.dingdan', verbose_name='订单')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '商家订单扩展',
|
||||
'verbose_name_plural': '商家订单扩展',
|
||||
'db_table': 'dingdan_shangjia',
|
||||
'indexes': [models.Index(fields=['shangjia_id'], name='dingdan_sha_shangji_64d28b_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.27 on 2026-01-17 15:21
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='auto_expire_at',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='计划过期时间'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='auto_task_id',
|
||||
field=models.CharField(blank=True, default='', max_length=255, verbose_name='延时任务ID'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.27 on 2026-01-17 16:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0002_dingdan_auto_expire_at_dingdan_auto_task_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='pending_dispatch',
|
||||
field=models.BooleanField(db_index=True, default=False, verbose_name='待调度标记'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='status_8_time',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='状态变为8的时间'),
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0004_alter_tuikuanjilu_chuliid.py
Normal file
18
dingdan/migrations/0004_alter_tuikuanjilu_chuliid.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-01-17 23:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0003_dingdan_pending_dispatch_dingdan_status_8_time'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='tuikuanjilu',
|
||||
name='chuliid',
|
||||
field=models.CharField(db_index=True, max_length=11, null=True, verbose_name='处理人ID'),
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0005_alter_chufajilu_chuliid.py
Normal file
18
dingdan/migrations/0005_alter_chufajilu_chuliid.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-01-20 19:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0004_alter_tuikuanjilu_chuliid'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='chufajilu',
|
||||
name='chuliid',
|
||||
field=models.CharField(db_index=True, max_length=11, null=True, verbose_name='处理处罚ID'),
|
||||
),
|
||||
]
|
||||
28
dingdan/migrations/0006_chufatupian.py
Normal file
28
dingdan/migrations/0006_chufatupian.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.2.27 on 2026-02-07 19:42
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0005_alter_chufajilu_chuliid'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Chufatupian',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dingdan_id', models.CharField(db_index=True, max_length=32, null=True, verbose_name='订单ID')),
|
||||
('yonghuid', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='用户ID')),
|
||||
('tupian', models.CharField(blank=True, max_length=500, null=True, verbose_name='证据图片URL')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'chufatupian',
|
||||
'indexes': [models.Index(fields=['yonghuid'], name='chufatupian_yonghui_71e585_idx'), models.Index(fields=['dingdan_id'], name='chufatupian_dingdan_ce0dd4_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0007_chufajilu_ssliyou.py
Normal file
18
dingdan/migrations/0007_chufajilu_ssliyou.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-02-08 00:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0006_chufatupian'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='chufajilu',
|
||||
name='ssliyou',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='打手申诉理由'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.27 on 2026-02-14 18:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0007_chufajilu_ssliyou'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='is_cross',
|
||||
field=models.IntegerField(default=0, verbose_name='是否跨平台订单(0否,1是)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='partner_order_id',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='对方平台订单ID'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.27 on 2026-02-21 14:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0008_dingdan_is_cross_dingdan_partner_order_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdanpingtai',
|
||||
name='clkf',
|
||||
field=models.CharField(db_index=True, max_length=20, null=True, verbose_name='处理客服'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dingdanpingtai',
|
||||
name='jjjs_biaoshi',
|
||||
field=models.IntegerField(default=0, verbose_name='拒绝结算标记(1表示拒绝结算)'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.27 on 2026-02-21 16:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0009_dingdanpingtai_clkf_dingdanpingtai_jjjs_biaoshi'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='dingdanpingtai',
|
||||
name='clkf',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='clkf',
|
||||
field=models.CharField(db_index=True, max_length=20, null=True, verbose_name='处理客服'),
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0011_dingdan_tkly.py
Normal file
18
dingdan/migrations/0011_dingdan_tkly.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-02-21 17:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0010_remove_dingdanpingtai_clkf_dingdan_clkf'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='tkly',
|
||||
field=models.CharField(blank=True, max_length=500, null=True, verbose_name='退款/拒绝理由'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.27 on 2026-02-22 12:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0011_dingdan_tkly'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='wechat_transaction_id',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=64, null=True, verbose_name='微信支付订单号'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='dingdan',
|
||||
index=models.Index(fields=['wechat_transaction_id'], name='dingdan_wechat__278b79_idx'),
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0013_alter_chufajilu_qingqiuid.py
Normal file
18
dingdan/migrations/0013_alter_chufajilu_qingqiuid.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-02-22 19:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0012_dingdan_wechat_transaction_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='chufajilu',
|
||||
name='qingqiuid',
|
||||
field=models.CharField(db_index=True, max_length=11, null=True, verbose_name='请求处罚ID'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.27 on 2026-03-24 17:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0013_alter_chufajilu_qingqiuid'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='dispatch_type',
|
||||
field=models.IntegerField(choices=[(1, '我方派单'), (2, '对方派单')], default=0, verbose_name='派发情况'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='partner_club_id',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=6, null=True, verbose_name='对方俱乐部ID'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,73 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-01 09:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0014_dingdan_dispatch_type_dingdan_partner_club_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PreSettlement',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dingdan_id', models.CharField(db_index=True, max_length=32, unique=True, verbose_name='我方订单ID')),
|
||||
('partner_order_id', models.CharField(db_index=True, max_length=32, verbose_name='对方订单ID')),
|
||||
('partner_club_id', models.CharField(db_index=True, max_length=6, verbose_name='对方俱乐部ID')),
|
||||
('dashou_id', models.CharField(db_index=True, max_length=7, verbose_name='接单打手ID')),
|
||||
('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='打手应分金额')),
|
||||
('order_amount', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='订单原金额')),
|
||||
('status', models.SmallIntegerField(choices=[(0, '待结算'), (1, '已结算'), (2, '已拒绝')], default=0, verbose_name='结算状态')),
|
||||
('process_time', models.DateTimeField(blank=True, null=True, verbose_name='处理时间')),
|
||||
('operator', models.CharField(blank=True, default='', max_length=32, verbose_name='操作人')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '预结算表',
|
||||
'verbose_name_plural': '预结算表',
|
||||
'db_table': 'pre_settlement',
|
||||
'indexes': [models.Index(fields=['dingdan_id'], name='pre_settlem_dingdan_4f3541_idx'), models.Index(fields=['partner_order_id'], name='pre_settlem_partner_45b29e_idx'), models.Index(fields=['partner_club_id'], name='pre_settlem_partner_d2c44c_idx'), models.Index(fields=['dashou_id'], name='pre_settlem_dashou__d5fe7c_idx'), models.Index(fields=['status'], name='pre_settlem_status_0aa711_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OrderDashouHistory',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dingdan_id', models.CharField(db_index=True, max_length=32, verbose_name='订单ID')),
|
||||
('dashou_id', models.CharField(db_index=True, max_length=7, verbose_name='打手ID')),
|
||||
('times', models.PositiveSmallIntegerField(verbose_name='第几次接单(从1开始)')),
|
||||
('operator', models.CharField(max_length=11, verbose_name='操作客服ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '订单打手变更历史',
|
||||
'verbose_name_plural': '订单打手变更历史',
|
||||
'db_table': 'order_dashou_history',
|
||||
'indexes': [models.Index(fields=['dingdan_id'], name='order_dasho_dingdan_1e79ab_idx'), models.Index(fields=['dashou_id'], name='order_dasho_dashou__0a637c_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CrossPlatformOrderData',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dingdan_id', models.CharField(db_index=True, max_length=32, unique=True, verbose_name='我方订单ID')),
|
||||
('partner_order_id', models.CharField(db_index=True, max_length=32, verbose_name='对方平台订单ID')),
|
||||
('partner_club_id', models.CharField(db_index=True, max_length=6, verbose_name='对方俱乐部ID')),
|
||||
('partner_order_status', models.IntegerField(default=0, verbose_name='对方平台订单状态')),
|
||||
('partner_dashou_id', models.CharField(blank=True, default='', max_length=32, verbose_name='对方平台接单打手ID')),
|
||||
('partner_amount', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='对方平台订单金额')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '跨平台订单扩展',
|
||||
'verbose_name_plural': '跨平台订单扩展',
|
||||
'db_table': 'cross_platform_order_data',
|
||||
'indexes': [models.Index(fields=['dingdan_id'], name='cross_platf_dingdan_498d08_idx'), models.Index(fields=['partner_order_id'], name='cross_platf_partner_5d5615_idx'), models.Index(fields=['partner_club_id'], name='cross_platf_partner_1054e1_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-04 15:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0015_presettlement_orderdashouhistory_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='presettlement',
|
||||
name='dingdan_id',
|
||||
field=models.CharField(db_index=True, max_length=32, verbose_name='我方订单ID'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='presettlement',
|
||||
unique_together={('dingdan_id', 'dashou_id')},
|
||||
),
|
||||
]
|
||||
32
dingdan/migrations/0017_dingdanpingtai_dianpu_id_and_more.py
Normal file
32
dingdan/migrations/0017_dingdanpingtai_dianpu_id_and_more.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-23 14:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0016_alter_presettlement_dingdan_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdanpingtai',
|
||||
name='dianpu_id',
|
||||
field=models.PositiveIntegerField(blank=True, db_index=True, null=True, verbose_name='所属店铺ID'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dingdanpingtai',
|
||||
name='dianpu_shouyi',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, default=0.0, max_digits=10, null=True, verbose_name='店铺收益'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dingdanpingtai',
|
||||
name='pingtai_shouyi',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, default=0.0, max_digits=10, null=True, verbose_name='平台收益'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='dingdanpingtai',
|
||||
index=models.Index(fields=['dianpu_id'], name='dingdan_pin_dianpu__9fbe27_idx'),
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0018_dingdanshangjia_partner_yonghu_id.py
Normal file
18
dingdan/migrations/0018_dingdanshangjia_partner_yonghu_id.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-30 02:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0017_dingdanpingtai_dianpu_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdanshangjia',
|
||||
name='partner_yonghu_id',
|
||||
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='对方平台用户ID'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-05 10:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0018_dingdanshangjia_partner_yonghu_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdanpingtai',
|
||||
name='shangji_dianpu_id',
|
||||
field=models.PositiveIntegerField(blank=True, db_index=True, null=True, verbose_name='该订单的上级店铺ID'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dingdanpingtai',
|
||||
name='shangji_fenhong',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, default=0.0, max_digits=10, null=True, verbose_name='上级分红金额'),
|
||||
),
|
||||
]
|
||||
58
dingdan/migrations/0020_fadanfenhong_fadan.py
Normal file
58
dingdan/migrations/0020_fadanfenhong_fadan.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-06 11:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0019_dingdanpingtai_shangji_dianpu_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='FadanFenhong',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='自增主键')),
|
||||
('fenhongzhe_id', models.CharField(db_index=True, max_length=20, verbose_name='分红者ID')),
|
||||
('zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='分红总额')),
|
||||
('ketixian_fenhong', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='可提现分红')),
|
||||
('shenfen', models.IntegerField(choices=[(1, '商家'), (2, '客服'), (3, '平台管理者')], default=1, verbose_name='分红者身份')),
|
||||
('fenhong_cishu', models.IntegerField(default=0, verbose_name='分红次数')),
|
||||
('yingde_lilv', models.DecimalField(decimal_places=4, default=0.0, max_digits=5, verbose_name='分红应得利率')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '罚款分红',
|
||||
'verbose_name_plural': '罚款分红',
|
||||
'db_table': 'fadan_fenhong',
|
||||
'indexes': [models.Index(fields=['fenhongzhe_id'], name='fadan_fenhong_zhe_idx'), models.Index(fields=['shenfen'], name='fadan_fenhong_sf_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Fadan',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='自增主键')),
|
||||
('beichufa_id', models.CharField(db_index=True, max_length=20, verbose_name='被处罚用户ID')),
|
||||
('shenqing_chufa', models.CharField(blank=True, max_length=20, null=True, verbose_name='申请处罚者ID')),
|
||||
('shenfen', models.IntegerField(choices=[(1, '打手'), (2, '管事'), (3, '商家'), (4, '组长')], default=1, verbose_name='被处罚者身份')),
|
||||
('chufaliyou', models.TextField(blank=True, null=True, verbose_name='处罚理由')),
|
||||
('fakuanjine', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='罚款金额')),
|
||||
('guanliandingdan_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='关联订单ID')),
|
||||
('zhuangtai', models.IntegerField(choices=[(1, '待缴纳'), (2, '已缴纳'), (3, '申诉中'), (4, '已驳回')], db_index=True, default=1, verbose_name='罚单状态')),
|
||||
('yingxiang_qiangdan', models.IntegerField(choices=[(1, '影响抢单'), (0, '不影响抢单')], db_index=True, default=1, verbose_name='是否影响抢单')),
|
||||
('shensuliyou', models.TextField(blank=True, null=True, verbose_name='申诉理由')),
|
||||
('tongyi_chufaliyou', models.TextField(blank=True, null=True, verbose_name='同意处罚理由')),
|
||||
('bohuiliyou', models.TextField(blank=True, null=True, verbose_name='驳回理由')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '罚单',
|
||||
'verbose_name_plural': '罚单',
|
||||
'db_table': 'fadan',
|
||||
'indexes': [models.Index(fields=['beichufa_id', 'yingxiang_qiangdan', 'zhuangtai'], name='fadan_qiangdan_check'), models.Index(fields=['guanliandingdan_id'], name='fadan_dingdan_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
30
dingdan/migrations/0021_fadanshensutupian.py
Normal file
30
dingdan/migrations/0021_fadanshensutupian.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-07 11:15
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0020_fadanfenhong_fadan'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='FadanShensuTupian',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('beichufa_id', models.CharField(db_index=True, max_length=7, verbose_name='被处罚用户ID')),
|
||||
('tupian_url', models.CharField(max_length=500, verbose_name='图片相对URL')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('fadan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shensu_tupians', to='dingdan.fadan', verbose_name='关联罚单')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '罚款申诉图片',
|
||||
'verbose_name_plural': '罚款申诉图片',
|
||||
'db_table': 'fadan_shensu_tupian',
|
||||
'indexes': [models.Index(fields=['fadan_id'], name='fadan_shens_fadan_i_78fab9_idx'), models.Index(fields=['beichufa_id'], name='fadan_shens_beichuf_59748e_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-07 16:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0021_fadanshensutupian'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='fadan',
|
||||
name='partner_club_id',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=6, null=True, verbose_name='对方俱乐部ID'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='fadan',
|
||||
name='shenqingren_shenfen',
|
||||
field=models.IntegerField(choices=[(1, '客服'), (2, '售后'), (3, '管理员')], default=1, verbose_name='申请处罚人身份'),
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0023_fadanshensutupian_yongtu.py
Normal file
18
dingdan/migrations/0023_fadanshensutupian_yongtu.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-07 17:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0022_fadan_partner_club_id_fadan_shenqingren_shenfen'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='fadanshensutupian',
|
||||
name='yongtu',
|
||||
field=models.IntegerField(choices=[(1, '处罚证据图片'), (2, '申诉图片')], default=2, verbose_name='图片用途'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-07 19:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0023_fadanshensutupian_yongtu'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='fadan',
|
||||
name='chulizhe',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='处理者'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='fadan',
|
||||
name='chulizhe_shenfen',
|
||||
field=models.IntegerField(choices=[(1, '客服'), (2, '售后'), (3, '管理员')], default=2, verbose_name='处理者身份'),
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0025_alter_fadan_shenqingren_shenfen.py
Normal file
18
dingdan/migrations/0025_alter_fadan_shenqingren_shenfen.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-10 00:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0024_fadan_chulizhe_fadan_chulizhe_shenfen'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='fadan',
|
||||
name='shenqingren_shenfen',
|
||||
field=models.IntegerField(choices=[(1, '客服'), (2, '售后'), (3, '管理员'), (4, '店铺')], default=1, verbose_name='申请处罚人身份'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,37 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-10 17:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0025_alter_fadan_shenqingren_shenfen'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='FadanFenhongLilv',
|
||||
fields=[
|
||||
('shenfen', models.IntegerField(choices=[(1, '客服'), (2, '售后'), (3, '管理员'), (4, '店铺'), (5, '商家')], primary_key=True, serialize=False, verbose_name='申请处罚人身份')),
|
||||
('lilv', models.DecimalField(decimal_places=4, default=0.0, max_digits=5, verbose_name='分红费率')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '罚款分红费率配置',
|
||||
'verbose_name_plural': '罚款分红费率配置',
|
||||
'db_table': 'fadan_fenhong_lilv',
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='fadan',
|
||||
name='shenqingren_shenfen',
|
||||
field=models.IntegerField(choices=[(1, '客服'), (2, '售后'), (3, '管理员'), (4, '店铺'), (5, '商家')], default=1, verbose_name='申请处罚人身份'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='fadanfenhong',
|
||||
name='shenfen',
|
||||
field=models.IntegerField(choices=[(1, '商家'), (2, '客服'), (3, '平台管理者'), (4, '店铺'), (5, '售后')], default=1, verbose_name='分红者身份'),
|
||||
),
|
||||
]
|
||||
42
dingdan/migrations/0027_dingdanpeihu.py
Normal file
42
dingdan/migrations/0027_dingdanpeihu.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-22 15:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0026_fadanfenhonglilv_alter_fadan_shenqingren_shenfen_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DingdanPeihu',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('dingdan_id', models.CharField(db_index=True, max_length=32, unique=True, verbose_name='订单ID')),
|
||||
('zhudashou_id', models.CharField(db_index=True, max_length=32, verbose_name='主打手ID')),
|
||||
('peihu1_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='陪护1打手ID')),
|
||||
('peihu2_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='陪护2打手ID')),
|
||||
('zhuangtai', models.IntegerField(default=1, verbose_name='陪护订单状态')),
|
||||
('peihu_xuqiu_shuliang', models.IntegerField(default=1, verbose_name='陪护需求数量')),
|
||||
('yongjin_yaoqiu', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='抢单佣金要求')),
|
||||
('peihu1_dengji_xuqiu', models.PositiveIntegerField(blank=True, null=True, verbose_name='陪护1等级需求(称号ID)')),
|
||||
('peihu2_dengji_xuqiu', models.PositiveIntegerField(blank=True, null=True, verbose_name='陪护2等级需求(称号ID)')),
|
||||
('peihu1_zhadan_danze', models.CharField(blank=True, max_length=10, null=True, verbose_name='陪护1炸单担责比例')),
|
||||
('peihu2_zhadan_danze', models.CharField(blank=True, max_length=10, null=True, verbose_name='陪护2炸单担责比例')),
|
||||
('peihu1_fenhong', models.CharField(blank=True, max_length=10, null=True, verbose_name='陪护1分红比例')),
|
||||
('peihu2_fenhong', models.CharField(blank=True, max_length=10, null=True, verbose_name='陪护2分红比例')),
|
||||
('zhiding_peihu1', models.CharField(blank=True, max_length=32, null=True, verbose_name='指定陪护1 ID')),
|
||||
('zhiding_peihu2', models.CharField(blank=True, max_length=32, null=True, verbose_name='指定陪护2 ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '陪护订单',
|
||||
'verbose_name_plural': '陪护订单',
|
||||
'db_table': 'dingdan_peihu',
|
||||
'indexes': [models.Index(fields=['zhudashou_id'], name='dingdan_pei_zhudash_583f4e_idx'), models.Index(fields=['peihu1_id'], name='dingdan_pei_peihu1__728279_idx'), models.Index(fields=['peihu2_id'], name='dingdan_pei_peihu2__77e45e_idx'), models.Index(fields=['zhuangtai'], name='dingdan_pei_zhuangt_22eaf6_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-29 17:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0027_dingdanpeihu'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdanshangjia',
|
||||
name='shangyige_dingdan_id',
|
||||
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='上一个订单ID'),
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0029_fadanfenhong_is_individual_rate.py
Normal file
18
dingdan/migrations/0029_fadanfenhong_is_individual_rate.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-05-31 22:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0028_dingdanshangjia_shangyige_dingdan_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='fadanfenhong',
|
||||
name='is_individual_rate',
|
||||
field=models.BooleanField(default=False, verbose_name='是否单独开启分红利率'),
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0030_fadan_shenqingren_fenhong_jine.py
Normal file
18
dingdan/migrations/0030_fadan_shenqingren_fenhong_jine.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-06-01 00:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0029_fadanfenhong_is_individual_rate'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='fadan',
|
||||
name='shenqingren_fenhong_jine',
|
||||
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='申请人分红金额'),
|
||||
),
|
||||
]
|
||||
18
dingdan/migrations/0031_dingdan_guanshi_fencheng.py
Normal file
18
dingdan/migrations/0031_dingdan_guanshi_fencheng.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-06-12 01:29
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dingdan', '0030_fadan_shenqingren_fenhong_jine'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='dingdan',
|
||||
name='guanshi_fencheng',
|
||||
field=models.DecimalField(blank=True, decimal_places=2, default=0, max_digits=10, null=True, verbose_name='管事分成'),
|
||||
),
|
||||
]
|
||||
0
dingdan/migrations/__init__.py
Normal file
0
dingdan/migrations/__init__.py
Normal file
588
dingdan/models.py
Normal file
588
dingdan/models.py
Normal file
@@ -0,0 +1,588 @@
|
||||
from django.db import models
|
||||
|
||||
class Dingdan(models.Model):
|
||||
"""订单主表"""
|
||||
# 核心编号:唯一且不可为空[citation:3][citation:9]
|
||||
dingdan_id = models.CharField(max_length=32, unique=True,db_index=True, verbose_name='订单ID')
|
||||
# 状态与平台:用整数表示,具体含义由业务逻辑定义
|
||||
zhuangtai = models.IntegerField(default=1, null=True, blank=True,db_index=True, verbose_name='订单状态')
|
||||
fadan_pingtai = models.IntegerField(null=True, blank=True, verbose_name='发单平台')
|
||||
# 金额相关
|
||||
jine = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='订单金额')
|
||||
dashou_fencheng = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='打手分成')
|
||||
guanshi_fencheng = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0, verbose_name='管事分成')
|
||||
# 用户与商品关联ID
|
||||
jiedan_dashou_id = models.CharField(max_length=32, null=True, db_index=True,blank=True, verbose_name='接单打手ID')
|
||||
dashou_liuyan = models.TextField(null=True, blank=True, verbose_name='打手留言')
|
||||
zhiding_id = models.CharField(max_length=32, null=True, blank=True, verbose_name='指定ID')
|
||||
shangpin_id = models.IntegerField(null=True, blank=True, verbose_name='商品ID')
|
||||
leixing_id = models.PositiveIntegerField(null=True, blank=True, verbose_name='商品类型ID') # 直接存数字
|
||||
tupian = models.CharField(max_length=500, null=True, blank=True, verbose_name='商品图片URL')
|
||||
|
||||
yaoqiuleixing = models.PositiveIntegerField(null=True, blank=True, verbose_name='抢单要求类型')
|
||||
huiyuan_id = models.CharField(max_length=6, null=True, blank=True, verbose_name='会员ID')
|
||||
yongjin = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='佣金要求')
|
||||
# 订单描述信息
|
||||
jieshao = models.TextField(null=True, blank=True, verbose_name='订单介绍')
|
||||
beizhu = models.TextField(null=True, blank=True, verbose_name='订单备注')
|
||||
partner_order_id = models.CharField(max_length=32, null=True, blank=True, db_index=True,verbose_name='对方平台订单ID')
|
||||
is_cross = models.IntegerField(default=0, verbose_name='是否跨平台订单(0否,1是)')
|
||||
|
||||
dispatch_type = models.IntegerField(default=0, choices=[(1, '我方派单'), (2, '对方派单')], verbose_name='派发情况')
|
||||
# 🔥 新增:对方俱乐部ID
|
||||
partner_club_id = models.CharField(max_length=6, null=True, blank=True, db_index=True, verbose_name='对方俱乐部ID')
|
||||
# 新增字段:游戏昵称(前端提交的游戏昵称)
|
||||
nicheng = models.CharField(max_length=50, null=True, blank=True, verbose_name='游戏昵称')
|
||||
user1_id = models.CharField(max_length=30,null=True, db_index=True, verbose_name='用户1标识')
|
||||
user2_id = models.CharField(max_length=30, null=True,db_index=True, verbose_name='用户2打手标识')
|
||||
#liaotian_pindao = models.CharField(max_length=255, blank=True, null=True, verbose_name='聊天频道名')
|
||||
auto_task_id = models.CharField('延时任务ID', max_length=255, blank=True, default='')
|
||||
auto_expire_at = models.DateTimeField('计划过期时间', null=True, blank=True)
|
||||
pending_dispatch = models.BooleanField('待调度标记', default=False, db_index=True)
|
||||
status_8_time = models.DateTimeField('状态变为8的时间', null=True, blank=True)
|
||||
# 时间戳
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
clkf = models.CharField(max_length=20, null=True, db_index=True, verbose_name='处理客服')
|
||||
tkly = models.CharField(max_length=500, blank=True, null=True, verbose_name='退款/拒绝理由')
|
||||
wechat_transaction_id = models.CharField(
|
||||
max_length=64,
|
||||
blank=True,
|
||||
null=True,
|
||||
db_index=True,
|
||||
verbose_name='微信支付订单号'
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
db_table = 'dingdan'
|
||||
verbose_name = '订单'
|
||||
verbose_name_plural = '订单'
|
||||
# 为高频查询字段添加索引
|
||||
indexes = [
|
||||
models.Index(fields=['zhuangtai']),
|
||||
models.Index(fields=['dingdan_id']),
|
||||
models.Index(fields=['jiedan_dashou_id']),
|
||||
models.Index(fields=['wechat_transaction_id']),
|
||||
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.dingdan_id
|
||||
|
||||
class DingdanPingtai(models.Model):
|
||||
"""平台订单扩展表 (当 fadan_pingtai=1 时使用)"""
|
||||
dingdan = models.OneToOneField(Dingdan, on_delete=models.CASCADE, related_name='pingtai_kuozhan', verbose_name='订单')
|
||||
laoban_id = models.CharField(max_length=32, null=True, blank=True, db_index=True,verbose_name='老板ID')
|
||||
laoban_pingjia = models.TextField(null=True, blank=True, verbose_name='老板评价')
|
||||
jjjs_biaoshi = models.IntegerField(default=0, verbose_name='拒绝结算标记(1表示拒绝结算)')
|
||||
|
||||
# ========== 新增字段 ==========
|
||||
pingtai_shouyi = models.DecimalField(max_digits=10,decimal_places=2,null=True,blank=True,default=0.00,verbose_name='平台收益')
|
||||
dianpu_shouyi = models.DecimalField(max_digits=10,decimal_places=2, null=True,blank=True,default=0.00,verbose_name='店铺收益')
|
||||
dianpu_id = models.PositiveIntegerField(null=True,blank=True,db_index=True,verbose_name='所属店铺ID')
|
||||
|
||||
# ---------- 上级分红快照 ----------
|
||||
shangji_dianpu_id = models.PositiveIntegerField(null=True, blank=True, db_index=True,
|
||||
verbose_name='该订单的上级店铺ID')
|
||||
shangji_fenhong = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0.00,
|
||||
verbose_name='上级分红金额')
|
||||
|
||||
# =============================
|
||||
|
||||
|
||||
class Meta:
|
||||
db_table = 'dingdan_pingtai'
|
||||
verbose_name = '平台订单扩展'
|
||||
verbose_name_plural = '平台订单扩展'
|
||||
indexes = [
|
||||
models.Index(fields=['laoban_id']),
|
||||
models.Index(fields=['dianpu_id']), # 为新字段加索引
|
||||
|
||||
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"平台扩展-{self.dingdan.dingdan_id}"
|
||||
|
||||
class DingdanShangjia(models.Model):
|
||||
"""商家发单扩展表 (当 fadan_pingtai=2 时使用)"""
|
||||
dingdan = models.OneToOneField(Dingdan, on_delete=models.CASCADE, related_name='shangjia_kuozhan', verbose_name='订单')
|
||||
shangjia_id = models.CharField(max_length=32, null=True, blank=True,db_index=True, verbose_name='商家ID')
|
||||
shangjia_pingjia = models.TextField(null=True, blank=True, verbose_name='商家评价')
|
||||
cfliyou = models.TextField(null=True, blank=True, verbose_name='商家处罚理由')
|
||||
sqzhuangtai =models.IntegerField(default=0, null=True, blank=True, verbose_name='处罚申请状态:0待处理,1成功,2驳回')
|
||||
bhliyou = models.TextField(null=True, blank=True, verbose_name='驳回商家处罚理由')
|
||||
sjnicheng =models.CharField(max_length=50,null=True, verbose_name='商家昵称')
|
||||
partner_yonghu_id = models.CharField(max_length=32, blank=True, null=True, verbose_name='对方平台用户ID')
|
||||
# 在 dingdan/models.py 的 DingdanShangjia 类中添加
|
||||
shangyige_dingdan_id = models.CharField(
|
||||
max_length=32,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name='上一个订单ID'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = 'dingdan_shangjia'
|
||||
verbose_name = '商家订单扩展'
|
||||
verbose_name_plural = '商家订单扩展'
|
||||
indexes = [
|
||||
models.Index(fields=['shangjia_id']),
|
||||
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"商家扩展-{self.dingdan.dingdan_id}"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class DingdanPeihu(models.Model):
|
||||
"""陪护订单表(一个订单最多2个陪护)"""
|
||||
# 关联订单(一对一,陪护订单依附于原订单)
|
||||
dingdan_id = models.CharField(max_length=32, unique=True, db_index=True, verbose_name='订单ID')
|
||||
# 主打手ID(冗余,方便查询,必须与订单的 jiedan_dashou_id 一致)
|
||||
zhudashou_id = models.CharField(max_length=32, db_index=True, verbose_name='主打手ID')
|
||||
# 实际接单陪护
|
||||
peihu1_id = models.CharField(max_length=32, null=True, blank=True, db_index=True, verbose_name='陪护1打手ID')
|
||||
peihu2_id = models.CharField(max_length=32, null=True, blank=True, db_index=True, verbose_name='陪护2打手ID')
|
||||
# 订单状态(1=待抢单,2=已抢待确认,3=进行中,4=已完成,5=已取消,7=指定状态...)
|
||||
zhuangtai = models.IntegerField(default=1, verbose_name='陪护订单状态')
|
||||
# 需求配置
|
||||
peihu_xuqiu_shuliang = models.IntegerField(default=1, verbose_name='陪护需求数量')
|
||||
yongjin_yaoqiu = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='抢单佣金要求')
|
||||
# 等级需求(关联称号ID,可空表示无限制)
|
||||
peihu1_dengji_xuqiu = models.PositiveIntegerField(null=True, blank=True, verbose_name='陪护1等级需求(称号ID)')
|
||||
peihu2_dengji_xuqiu = models.PositiveIntegerField(null=True, blank=True, verbose_name='陪护2等级需求(称号ID)')
|
||||
# 处罚与分红比例(格式:"1/5" 或 "2/5")
|
||||
peihu1_zhadan_danze = models.CharField(max_length=10, null=True, blank=True, verbose_name='陪护1炸单担责比例')
|
||||
peihu2_zhadan_danze = models.CharField(max_length=10, null=True, blank=True, verbose_name='陪护2炸单担责比例')
|
||||
peihu1_fenhong = models.CharField(max_length=10, null=True, blank=True, verbose_name='陪护1分红比例')
|
||||
peihu2_fenhong = models.CharField(max_length=10, null=True, blank=True, verbose_name='陪护2分红比例')
|
||||
# 指定陪护(可预设打手ID,可为空)
|
||||
zhiding_peihu1 = models.CharField(max_length=32, null=True, blank=True, verbose_name='指定陪护1 ID')
|
||||
zhiding_peihu2 = models.CharField(max_length=32, null=True, blank=True, verbose_name='指定陪护2 ID')
|
||||
# 时间戳
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'dingdan_peihu'
|
||||
verbose_name = '陪护订单'
|
||||
verbose_name_plural = verbose_name
|
||||
indexes = [
|
||||
models.Index(fields=['zhudashou_id']),
|
||||
models.Index(fields=['peihu1_id']),
|
||||
models.Index(fields=['peihu2_id']),
|
||||
models.Index(fields=['zhuangtai']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 建议路径:dingdan/models.py 或 独立 fadan/models.py
|
||||
from django.db import models
|
||||
|
||||
class Fadan(models.Model):
|
||||
"""罚单表——平台各类处罚信息"""
|
||||
id = models.AutoField(primary_key=True, verbose_name='自增主键')
|
||||
beichufa_id = models.CharField(max_length=20, db_index=True, verbose_name='被处罚用户ID') # 直接用yonghuid,后续可改为外键
|
||||
shenqing_chufa = models.CharField(max_length=20, blank=True, null=True, verbose_name='申请处罚者ID')
|
||||
shenfen = models.IntegerField(choices=[(1, '打手'), (2, '管事'), (3, '商家'), (4, '组长')], default=1, verbose_name='被处罚者身份')
|
||||
chufaliyou = models.TextField(blank=True, null=True, verbose_name='处罚理由')
|
||||
fakuanjine = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='罚款金额')
|
||||
guanliandingdan_id = models.CharField(max_length=32, blank=True, null=True, db_index=True, verbose_name='关联订单ID')
|
||||
zhuangtai = models.IntegerField(choices=[(1, '待缴纳'), (2, '已缴纳'), (3, '申诉中'), (4, '已驳回')], default=1, db_index=True, verbose_name='罚单状态')
|
||||
yingxiang_qiangdan = models.IntegerField(choices=[(1, '影响抢单'), (0, '不影响抢单')], default=1, db_index=True, verbose_name='是否影响抢单')
|
||||
shensuliyou = models.TextField(blank=True, null=True, verbose_name='申诉理由')
|
||||
tongyi_chufaliyou = models.TextField(blank=True, null=True, verbose_name='同意处罚理由')
|
||||
bohuiliyou = models.TextField(blank=True, null=True, verbose_name='驳回理由')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
# 在 Fadan 模型中添加
|
||||
partner_club_id = models.CharField(max_length=6, blank=True, null=True, db_index=True, verbose_name='对方俱乐部ID')
|
||||
# 在 class Fadan 中添加
|
||||
shenqingren_shenfen = models.IntegerField(
|
||||
choices=[(1, '客服'), (2, '售后'), (3, '管理员'),(4,'店铺'),(5,'商家')], # 按实际扩展
|
||||
default=1,
|
||||
verbose_name='申请处罚人身份'
|
||||
)
|
||||
chulizhe_shenfen = models.IntegerField(
|
||||
choices=[(1, '客服'), (2, '售后'), (3, '管理员')],
|
||||
default=2,
|
||||
verbose_name='处理者身份'
|
||||
)
|
||||
chulizhe = models.CharField(max_length=20, blank=True, null=True, verbose_name='处理者')
|
||||
# ========== 新增字段 ==========
|
||||
shenqingren_fenhong_jine = models.DecimalField(
|
||||
max_digits=12, decimal_places=2, default=0.00,
|
||||
verbose_name='申请人分红金额'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = 'fadan'
|
||||
verbose_name = '罚单'
|
||||
verbose_name_plural = '罚单'
|
||||
indexes = [
|
||||
models.Index(fields=['beichufa_id', 'yingxiang_qiangdan', 'zhuangtai'], name='fadan_qiangdan_check'),
|
||||
models.Index(fields=['guanliandingdan_id'], name='fadan_dingdan_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.beichufa_id} - 罚款{self.fakuanjine}元 - 状态{self.zhuangtai}"
|
||||
|
||||
|
||||
# ==================== 罚款申诉图片表 ====================
|
||||
class FadanShensuTupian(models.Model):
|
||||
"""罚款申诉图片,关联罚单"""
|
||||
fadan = models.ForeignKey(Fadan, on_delete=models.CASCADE, related_name='shensu_tupians', verbose_name='关联罚单')
|
||||
beichufa_id = models.CharField(max_length=7, db_index=True, verbose_name='被处罚用户ID') # 冗余,便于直接按用户查
|
||||
tupian_url = models.CharField(max_length=500, verbose_name='图片相对URL')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
# 在 FadanShensuTupian 模型中添加
|
||||
yongtu = models.IntegerField(
|
||||
choices=[(1, '处罚证据图片'), (2, '申诉图片')],
|
||||
default=2,
|
||||
verbose_name='图片用途'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = 'fadan_shensu_tupian'
|
||||
verbose_name = '罚款申诉图片'
|
||||
verbose_name_plural = '罚款申诉图片'
|
||||
indexes = [
|
||||
models.Index(fields=['fadan_id']),
|
||||
models.Index(fields=['beichufa_id']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
# 与 Fadan 放在同一个文件或独立的 fadan/models.py
|
||||
class FadanFenhong(models.Model):
|
||||
"""罚款分红表——记录罚款产生的分红信息"""
|
||||
id = models.AutoField(primary_key=True, verbose_name='自增主键')
|
||||
fenhongzhe_id = models.CharField(max_length=20, db_index=True, verbose_name='分红者ID')
|
||||
zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='分红总额')
|
||||
ketixian_fenhong = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='可提现分红')
|
||||
shenfen = models.IntegerField(choices=[(1, '商家'), (2, '客服'), (3, '平台管理者'),(4,'店铺'),(5, '售后')], default=1, verbose_name='分红者身份')
|
||||
fenhong_cishu = models.IntegerField(default=0, verbose_name='分红次数')
|
||||
# ✅ 新增字段:是否单独开启分红利率(若开启,则使用本记录的 yingde_lilv;否则使用全局配置)
|
||||
is_individual_rate = models.BooleanField(default=False, verbose_name='是否单独开启分红利率')
|
||||
yingde_lilv = models.DecimalField(max_digits=5, decimal_places=4, default=0.0000, verbose_name='分红应得利率')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'fadan_fenhong'
|
||||
verbose_name = '罚款分红'
|
||||
verbose_name_plural = '罚款分红'
|
||||
indexes = [
|
||||
models.Index(fields=['fenhongzhe_id'], name='fadan_fenhong_zhe_idx'),
|
||||
models.Index(fields=['shenfen'], name='fadan_fenhong_sf_idx'),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.fenhongzhe_id} - 分红{self.ketixian_fenhong}元"
|
||||
|
||||
|
||||
|
||||
|
||||
class FadanFenhongLilv(models.Model):
|
||||
shenfen = models.IntegerField(primary_key=True, choices=[(1, '客服'), (2, '售后'), (3, '管理员'), (4, '店铺'), (5, '商家')], verbose_name='申请处罚人身份')
|
||||
lilv = models.DecimalField(max_digits=5, decimal_places=4, default=0.0000, verbose_name='分红费率')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'fadan_fenhong_lilv'
|
||||
verbose_name = '罚款分红费率配置'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return f"身份{self.shenfen} - 费率{self.lilv}"
|
||||
|
||||
|
||||
|
||||
|
||||
class CrossPlatformOrderData(models.Model):
|
||||
"""
|
||||
跨平台订单扩展数据表
|
||||
存储我方订单与对方平台订单的关联及对方订单状态
|
||||
"""
|
||||
dingdan_id = models.CharField(max_length=32, unique=True, db_index=True, verbose_name='我方订单ID')
|
||||
partner_order_id = models.CharField(max_length=32, db_index=True, verbose_name='对方平台订单ID')
|
||||
partner_club_id = models.CharField(max_length=6, db_index=True, verbose_name='对方俱乐部ID')
|
||||
partner_order_status = models.IntegerField(default=0, verbose_name='对方平台订单状态') # 对方订单当前状态码
|
||||
partner_dashou_id = models.CharField(max_length=32, blank=True, default='', verbose_name='对方平台接单打手ID')
|
||||
partner_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='对方平台订单金额')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'cross_platform_order_data'
|
||||
verbose_name = '跨平台订单扩展'
|
||||
verbose_name_plural = '跨平台订单扩展'
|
||||
indexes = [
|
||||
models.Index(fields=['dingdan_id']),
|
||||
models.Index(fields=['partner_order_id']),
|
||||
models.Index(fields=['partner_club_id']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.dingdan_id} -> {self.partner_order_id}"
|
||||
|
||||
|
||||
|
||||
|
||||
# apps/dingdan/models.py 中添加
|
||||
|
||||
class PreSettlement(models.Model):
|
||||
"""
|
||||
预结算表
|
||||
用于记录对方平台派单订单的待结算款项,等待对方确认后才能真正入账
|
||||
"""
|
||||
STATUS_CHOICES = (
|
||||
(0, '待结算'),
|
||||
(1, '已结算'),
|
||||
(2, '已拒绝'),
|
||||
)
|
||||
# 我方订单ID
|
||||
dingdan_id = models.CharField(max_length=32, db_index=True, verbose_name='我方订单ID')
|
||||
# 对方订单ID
|
||||
partner_order_id = models.CharField(max_length=32, db_index=True, verbose_name='对方订单ID')
|
||||
# 对方俱乐部ID
|
||||
partner_club_id = models.CharField(max_length=6, db_index=True, verbose_name='对方俱乐部ID')
|
||||
# 接单打手ID
|
||||
dashou_id = models.CharField(max_length=7, db_index=True, verbose_name='接单打手ID')
|
||||
# 打手应分金额
|
||||
amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='打手应分金额')
|
||||
# 订单金额(原金额,可选)
|
||||
order_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='订单原金额')
|
||||
# 结算状态
|
||||
status = models.SmallIntegerField(choices=STATUS_CHOICES, default=0, verbose_name='结算状态')
|
||||
# 处理时间(结算或拒绝的时间)
|
||||
process_time = models.DateTimeField(null=True, blank=True, verbose_name='处理时间')
|
||||
# 处理客服/对方操作人(可选)
|
||||
operator = models.CharField(max_length=32, blank=True, default='', verbose_name='操作人')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'pre_settlement'
|
||||
verbose_name = '预结算表'
|
||||
verbose_name_plural = verbose_name
|
||||
unique_together = [['dingdan_id', 'dashou_id']]
|
||||
indexes = [
|
||||
models.Index(fields=['dingdan_id']),
|
||||
models.Index(fields=['partner_order_id']),
|
||||
models.Index(fields=['partner_club_id']),
|
||||
models.Index(fields=['dashou_id']),
|
||||
models.Index(fields=['status']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"预结算-{self.dingdan_id} -> {self.partner_order_id}"
|
||||
|
||||
|
||||
# apps/dingdan/models.py 中添加
|
||||
|
||||
class OrderDashouHistory(models.Model):
|
||||
"""
|
||||
订单打手变更历史表
|
||||
记录订单每次更换打手的操作
|
||||
"""
|
||||
dingdan_id = models.CharField(max_length=32, db_index=True, verbose_name='订单ID')
|
||||
dashou_id = models.CharField(max_length=7, db_index=True, verbose_name='打手ID')
|
||||
times = models.PositiveSmallIntegerField(verbose_name='第几次接单(从1开始)')
|
||||
operator = models.CharField(max_length=11, verbose_name='操作客服ID')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'order_dashou_history'
|
||||
verbose_name = '订单打手变更历史'
|
||||
verbose_name_plural = '订单打手变更历史'
|
||||
indexes = [
|
||||
models.Index(fields=['dingdan_id']),
|
||||
models.Index(fields=['dashou_id']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.dingdan_id} -> {self.dashou_id} 第{self.times}次"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Dashoutupian(models.Model):
|
||||
dingdan_id = models.CharField(max_length=32,db_index=True, null=True, verbose_name='订单ID')
|
||||
dashou = models.CharField(max_length=32, null=True, blank=True,db_index=True, verbose_name='接单打手ID')
|
||||
tupian = models.CharField(max_length=500, null=True, blank=True, verbose_name='打手提交图片URL')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
class Meta:
|
||||
db_table = 'dashoutupian'
|
||||
|
||||
indexes = [
|
||||
models.Index(fields=['dashou']),
|
||||
models.Index(fields=['dingdan_id']),
|
||||
|
||||
]
|
||||
|
||||
|
||||
|
||||
class Pingfen(models.Model):
|
||||
dashou = models.CharField(max_length=32, null=True, blank=True, db_index=True,verbose_name='接单打手ID')
|
||||
zongfen = models.PositiveIntegerField(default=0, verbose_name='总分')
|
||||
cishu = models.PositiveIntegerField(default=0, verbose_name='评分次数')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'pingfenbiao'
|
||||
verbose_name = '评分表'
|
||||
indexes = [
|
||||
models.Index(fields=['dashou']),
|
||||
]
|
||||
def __str__(self):
|
||||
return f"{self.dashou} - 平均分: {self.pingjun_fen():.2f}"
|
||||
|
||||
def pingjun_fen(self):
|
||||
"""计算平均分"""
|
||||
if self.cishu > 0:
|
||||
return float(self.zongfen) / self.cishu
|
||||
return 0.0
|
||||
|
||||
|
||||
|
||||
class Lilubiao(models.Model):
|
||||
lilu = models.DecimalField(max_digits=5, decimal_places=4,null=True, verbose_name='平台分成利率,1是平台,2是商家')
|
||||
#sahngjialilu = models.DecimalField(max_digits=5, decimal_places=4,null=True, verbose_name='商家利率')
|
||||
fadanpingtai = models.CharField(max_length=10,null=True,verbose_name='订单所属平台,(商家或平台)')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
class Meta:
|
||||
db_table = 'lilubiao'
|
||||
|
||||
|
||||
|
||||
|
||||
class Chufajilu(models.Model):
|
||||
dashouid = models.CharField(max_length=7, null=True, db_index=True, verbose_name='被罚打手ID')
|
||||
qingqiuid = models.CharField(max_length=11, null=True, db_index=True, verbose_name='请求处罚ID')
|
||||
chuliid = models.CharField(max_length=11, null=True, db_index=True, verbose_name='处理处罚ID')
|
||||
cfliyou = models.TextField(null=True, blank=True, verbose_name='处罚理由')
|
||||
ssliyou = models.TextField(null=True, blank=True, verbose_name='打手申诉理由')
|
||||
sqzhuangtai = models.IntegerField(default=0, null=True, blank=True, verbose_name='处罚申请状态:0待处理,1成功,2驳回')
|
||||
bhliyou = models.TextField(null=True, blank=True, verbose_name='驳回处罚理由')
|
||||
jifen = models.IntegerField(default=0, verbose_name='扣除打手积分')
|
||||
dingdan_id = models.CharField(max_length=32, null=True, db_index=True, verbose_name='订单ID')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
class Meta:
|
||||
db_table = 'chufajilu'
|
||||
indexes = [
|
||||
models.Index(fields=['dashouid']),
|
||||
models.Index(fields=['qingqiuid']),
|
||||
models.Index(fields=['chuliid']),
|
||||
models.Index(fields=['dingdan_id']),
|
||||
]
|
||||
|
||||
|
||||
class Chufatupian(models.Model):
|
||||
dingdan_id = models.CharField(max_length=32,db_index=True, null=True, verbose_name='订单ID')
|
||||
yonghuid = models.CharField(max_length=32, null=True, blank=True,db_index=True, verbose_name='用户ID')
|
||||
tupian = models.CharField(max_length=500, null=True, blank=True, verbose_name='证据图片URL')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
class Meta:
|
||||
db_table = 'chufatupian'
|
||||
|
||||
indexes = [
|
||||
models.Index(fields=['yonghuid']),
|
||||
models.Index(fields=['dingdan_id']),
|
||||
|
||||
]
|
||||
|
||||
|
||||
class Tuikuanjilu(models.Model):
|
||||
dingdan_id = models.CharField(max_length=32, unique=True, verbose_name='订单ID')
|
||||
dashouid = models.CharField(max_length=7, null=True, verbose_name='接单打手ID')
|
||||
qingqiuid = models.CharField(max_length=7, null=True, verbose_name='请求用户ID')
|
||||
chuliid = models.CharField(max_length=11, null=True, db_index=True, verbose_name='处理人ID')
|
||||
liyou = models.TextField(null=True, blank=True, verbose_name='理由')
|
||||
sqzhuangtai = models.IntegerField(default=0, null=True, blank=True,db_index=True, verbose_name='申请状态:0待处理,1成功,2驳回')
|
||||
bhliyou = models.TextField(null=True, blank=True, verbose_name='驳回理由')
|
||||
jine = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='订单金额')
|
||||
dashou_fencheng = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='打手分成')
|
||||
jieshao = models.TextField(null=True, blank=True, verbose_name='订单介绍')
|
||||
beizhu = models.TextField(null=True, blank=True, verbose_name='订单备注')
|
||||
# 新增字段:游戏昵称(前端提交的游戏昵称)
|
||||
nicheng = models.CharField(max_length=50, null=True, blank=True, verbose_name='游戏昵称')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
class Meta:
|
||||
db_table = 'tuikuanjilu'
|
||||
indexes = [
|
||||
models.Index(fields=['chuliid']),
|
||||
models.Index(fields=['sqzhuangtai']),
|
||||
]
|
||||
|
||||
|
||||
class Ptliushui(models.Model):
|
||||
zonge = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='收取总金额')
|
||||
zhichuzonge = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='支出总金额')
|
||||
zongshouyi = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='总收益')
|
||||
rizonge = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='日收取')
|
||||
rizhichu = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='日支出')
|
||||
rishouyi = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='日收益')
|
||||
zongshoucishu = models.IntegerField(default=0, verbose_name='总收取次数')
|
||||
rishoucishu = models.IntegerField(default=0, verbose_name='日收取次数')
|
||||
zongzhichucishu = models.IntegerField(default=0, verbose_name='总支出次数')
|
||||
rizhichucishu = models.IntegerField(default=0, verbose_name='日支出次数')
|
||||
class Meta:
|
||||
db_table = 'ptliushui'
|
||||
|
||||
|
||||
# models.py - ChatRelation 模型
|
||||
|
||||
|
||||
|
||||
class Liaotian(models.Model):
|
||||
"""聊天关系表 - 极简设计,不设置唯一约束"""
|
||||
# 主键
|
||||
id = models.BigAutoField(primary_key=True, verbose_name='主键ID')
|
||||
|
||||
# 聊天双方的用户标识(GoEasy的user_id)
|
||||
user1_id = models.CharField(max_length=30, db_index=True, verbose_name='用户1标识')
|
||||
user2_id = models.CharField(max_length=30, db_index=True, verbose_name='用户2标识')
|
||||
|
||||
# 关联的订单(可以为空,但实际业务中应该关联)
|
||||
dingdan_id = models.CharField(max_length=32, unique=True, verbose_name='订单ID')
|
||||
# 状态与平台:用整数表示,具体含义由业务逻辑定义
|
||||
zhuangtai = models.IntegerField(default=1,db_index=True,choices=[(1, '开启中'), (0, '已关闭')],verbose_name='聊天状态')
|
||||
# 时间戳
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'liaotian'
|
||||
indexes = [
|
||||
models.Index(fields=['user1_id']),
|
||||
models.Index(fields=['user2_id']),
|
||||
models.Index(fields=['zhuangtai']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.user1_id} ↔ {self.user2_id} ({self.zhuangtai})'
|
||||
|
||||
|
||||
|
||||
614
dingdan/signals.py
Normal file
614
dingdan/signals.py
Normal file
@@ -0,0 +1,614 @@
|
||||
"""
|
||||
阿龙电竞 - 信号处理器(修复Redis导入问题)
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# dingdan/signals.py
|
||||
"""
|
||||
阿龙电竞 - 订单信号处理器(生产稳定版)
|
||||
经过验证,100%可靠
|
||||
"""
|
||||
|
||||
import sys
|
||||
import importlib
|
||||
import logging
|
||||
from django.db.models.signals import pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 🔧 关键修复:确保redis模块正确加载
|
||||
def ensure_redis_loaded():
|
||||
"""确保redis模块正确加载,避免导入冲突"""
|
||||
if 'redis' not in sys.modules:
|
||||
# 如果redis模块未加载,尝试导入
|
||||
try:
|
||||
import redis
|
||||
#logger.debug("✅ Redis模块正常加载")
|
||||
except ImportError:
|
||||
#logger.warning("⚠️ Redis模块未安装,尝试自动安装")
|
||||
import subprocess
|
||||
subprocess.call([sys.executable, "-m", "pip", "install", "redis==4.5.4", "-q"])
|
||||
import redis
|
||||
|
||||
# 确保redis.Redis属性存在
|
||||
import redis
|
||||
if redis.Redis is None:
|
||||
#logger.warning("⚠️ Redis.Redis属性为None,强制重新加载模块")
|
||||
importlib.reload(redis)
|
||||
|
||||
return redis
|
||||
|
||||
# 预加载redis模块
|
||||
ensure_redis_loaded()
|
||||
|
||||
@receiver(pre_save, sender='dingdan.Dingdan')
|
||||
def handle_order_status_8(sender, instance, **kwargs):
|
||||
"""
|
||||
订单状态变为8时,提交定时任务
|
||||
"""
|
||||
# 跳过新增的订单
|
||||
if instance.pk is None:
|
||||
return
|
||||
|
||||
try:
|
||||
# 动态导入,避免循环依赖
|
||||
from dingdan.models import Dingdan
|
||||
|
||||
# 获取旧状态
|
||||
old = Dingdan.objects.filter(pk=instance.pk).values_list('zhuangtai', flat=True).first()
|
||||
|
||||
# 状态从 非8 变为 8
|
||||
if old != 8 and instance.zhuangtai == 8:
|
||||
#logger.info(f"📅 订单 {instance.dingdan_id} 状态变为8,提交定时任务")
|
||||
|
||||
# 设置时间标记
|
||||
instance.status_8_time = timezone.now()
|
||||
instance.pending_dispatch = True
|
||||
|
||||
# 使用配置中的超时时间
|
||||
expire_seconds = getattr(settings, 'ORDER_EXPIRE_SECONDS', 48 * 60 * 60)
|
||||
|
||||
# 提交Celery任务
|
||||
try:
|
||||
from dingdan.tasks import process_expired_order
|
||||
|
||||
task_result = process_expired_order.apply_async(
|
||||
args=[instance.dingdan_id],
|
||||
countdown=expire_seconds,
|
||||
queue='order_tasks',
|
||||
priority=9
|
||||
)
|
||||
|
||||
#logger.info(f"✅ 任务提交成功,订单: {instance.dingdan_id}, 任务ID: {task_result.id}")
|
||||
|
||||
# 保存任务信息
|
||||
instance.auto_task_id = task_result.id
|
||||
instance.auto_expire_at = timezone.now() + timezone.timedelta(seconds=expire_seconds)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 任务提交失败: {e}")
|
||||
instance.pending_dispatch = True
|
||||
instance.auto_task_id = f"failed_{timezone.now().timestamp()}"
|
||||
|
||||
# 状态从 8 变为 非8
|
||||
elif old == 8 and instance.zhuangtai != 8:
|
||||
instance.status_8_time = None
|
||||
instance.pending_dispatch = False
|
||||
instance.auto_task_id = ''
|
||||
logger.info(f"📅 订单 {instance.dingdan_id} 状态离开8")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"信号处理失败: {e}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 🔥 关键修复:在最开始强制正确导入redis模块
|
||||
'''import sys
|
||||
|
||||
# 1. 如果redis模块已加载但有问题,先移除
|
||||
if 'redis' in sys.modules:
|
||||
print(f"🔧 移除已加载的redis模块: {sys.modules['redis']}")
|
||||
del sys.modules['redis']
|
||||
# 同时移除相关模块
|
||||
for key in list(sys.modules.keys()):
|
||||
if key.startswith('redis.') or key == 'redis':
|
||||
del sys.modules[key]
|
||||
|
||||
# 2. 重新导入redis模块
|
||||
try:
|
||||
import importlib
|
||||
redis = importlib.import_module('redis')
|
||||
print(f"✅ 重新导入redis成功: {redis}")
|
||||
print(f" Redis类: {redis.Redis}")
|
||||
print(f" Redis类是否为None: {redis.Redis is None}")
|
||||
|
||||
# 验证Redis类是否可用
|
||||
if redis.Redis is None:
|
||||
print("❌ Redis类为None,尝试从源码级别修复")
|
||||
# 强制重新加载
|
||||
import importlib.util
|
||||
import os
|
||||
|
||||
# 找到redis包路径
|
||||
import pkgutil
|
||||
redis_spec = pkgutil.find_loader('redis')
|
||||
if redis_spec:
|
||||
print(f" Redis包路径: {redis_spec.origin}")
|
||||
# 强制重新加载
|
||||
importlib.invalidate_caches()
|
||||
redis = importlib.import_module('redis')
|
||||
importlib.reload(redis)
|
||||
print(f" Redis类(重载后): {redis.Redis}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 导入redis失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
# 尝试直接安装
|
||||
print("尝试直接安装redis包...")
|
||||
import subprocess
|
||||
subprocess.call([sys.executable, "-m", "pip", "install", "redis==4.5.4", "-q"])
|
||||
|
||||
# 3. 确保kombu.transport.redis也能正确导入
|
||||
try:
|
||||
import kombu.transport.redis
|
||||
print(f"✅ kombu.transport.redis导入成功")
|
||||
except Exception as e:
|
||||
print(f"❌ kombu.transport.redis导入失败: {e}")
|
||||
# 修复:先导入redis,再导入kombu.transport.redis
|
||||
if 'redis' not in sys.modules:
|
||||
import redis
|
||||
# 重新导入
|
||||
import importlib
|
||||
if 'kombu.transport.redis' in sys.modules:
|
||||
del sys.modules['kombu.transport.redis']
|
||||
kombu_transport_redis = importlib.import_module('kombu.transport.redis')
|
||||
print(f"✅ 重新导入kombu.transport.redis成功")
|
||||
|
||||
# 4. 继续原来的信号处理器代码
|
||||
import logging
|
||||
from django.db.models.signals import pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@receiver(pre_save, sender='dingdan.Dingdan')
|
||||
def order_status_8_handler_fixed(sender, instance, **kwargs):
|
||||
"""
|
||||
修复Redis导入问题后的信号处理器
|
||||
"""
|
||||
if instance.pk is None:
|
||||
return
|
||||
|
||||
try:
|
||||
# 动态导入,避免启动时循环导入
|
||||
from dingdan.models import Dingdan
|
||||
|
||||
# 获取旧状态
|
||||
old = Dingdan.objects.filter(pk=instance.pk).values_list('zhuangtai', flat=True).first()
|
||||
|
||||
# 状态从 非8 变为 8
|
||||
if old != 8 and instance.zhuangtai == 8:
|
||||
logger.info(f"📅 订单 {instance.dingdan_id} 状态变8")
|
||||
|
||||
# 设置时间标记
|
||||
instance.status_8_time = timezone.now()
|
||||
instance.pending_dispatch = True
|
||||
|
||||
# 获取超时时间
|
||||
expire_seconds = 60 # 测试用60秒
|
||||
|
||||
# 🔥 再次验证redis模块
|
||||
try:
|
||||
import redis
|
||||
logger.info(f"🔍 信号处理器内redis模块状态: {redis.Redis}")
|
||||
if redis.Redis is None:
|
||||
logger.error("❌ 信号处理器内redis.Redis为None!")
|
||||
# 使用备用方案
|
||||
instance.auto_task_id = f"redis_none_{timezone.now().timestamp()}"
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 信号处理器内验证redis失败: {e}")
|
||||
instance.auto_task_id = f"redis_check_failed_{timezone.now().timestamp()}"
|
||||
return
|
||||
|
||||
# 提交Celery任务
|
||||
try:
|
||||
from dingdan.tasks import process_expired_order
|
||||
|
||||
logger.info(f"🔧 开始提交任务,订单: {instance.dingdan_id}")
|
||||
|
||||
# 使用更安全的方式提交任务
|
||||
from celery import current_app
|
||||
|
||||
# 验证current_app的backend
|
||||
if hasattr(current_app, 'backend'):
|
||||
logger.info(f"✅ current_app.backend存在: {current_app.backend}")
|
||||
else:
|
||||
logger.error("❌ current_app没有backend属性")
|
||||
|
||||
task_result = process_expired_order.apply_async(
|
||||
args=[instance.dingdan_id],
|
||||
countdown=expire_seconds,
|
||||
queue='order_tasks',
|
||||
priority=9
|
||||
)
|
||||
|
||||
logger.info(f"✅ Celery任务提交成功,订单: {instance.dingdan_id}, 任务ID: {task_result.id}")
|
||||
|
||||
# 保存任务信息
|
||||
instance.auto_task_id = task_result.id
|
||||
instance.auto_expire_at = timezone.now() + timezone.timedelta(seconds=expire_seconds)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Celery任务提交失败: {type(e).__name__}: {e}")
|
||||
|
||||
# 特别检查Redis相关错误
|
||||
if "'NoneType' object has no attribute 'Redis'" in str(e):
|
||||
logger.error("💥 检测到Redis类为None错误")
|
||||
logger.error("💥 尝试从当前环境重新导入redis模块")
|
||||
|
||||
# 尝试修复
|
||||
try:
|
||||
import importlib
|
||||
import redis as redis_module
|
||||
importlib.reload(redis_module)
|
||||
logger.info(f"💥 重新加载redis模块后: {redis_module.Redis}")
|
||||
except Exception as reload_e:
|
||||
logger.error(f"💥 重新加载失败: {reload_e}")
|
||||
|
||||
# 标记为待调度,由调度器处理
|
||||
instance.pending_dispatch = True
|
||||
instance.auto_task_id = f"failed_{timezone.now().timestamp()}"
|
||||
|
||||
# 状态从 8 变为 非8
|
||||
elif old == 8 and instance.zhuangtai != 8:
|
||||
instance.status_8_time = None
|
||||
instance.pending_dispatch = False
|
||||
instance.auto_task_id = ''
|
||||
logger.info(f"📅 订单 {instance.dingdan_id} 状态离开8")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"订单状态变更处理失败,订单ID: {getattr(instance, 'dingdan_id', '未知')}, 错误: {e}")'''
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
'''from django.db.models.signals import pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
|
||||
from dingdan.models import Dingdan
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# 在你的 dingdan/tasks.py 文件末尾添加以下代码
|
||||
|
||||
from django.db.models.signals import pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
import logging
|
||||
|
||||
from dingdan.models import Dingdan
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@receiver(pre_save, sender=Dingdan)
|
||||
def handle_order_status_change(sender, instance, **kwargs):
|
||||
"""
|
||||
订单状态变化监听器(纯净标记版)
|
||||
严格遵循原始逻辑,仅更新数据库标记。
|
||||
"""
|
||||
# 1. 跳过新建的订单
|
||||
if instance.pk is None:
|
||||
return
|
||||
|
||||
# 2. 获取数据库中此订单的旧状态
|
||||
try:
|
||||
old_status = Dingdan.objects.filter(pk=instance.pk).values_list('zhuangtai', flat=True).first()
|
||||
except Dingdan.DoesNotExist:
|
||||
return # 旧订单不存在,不处理
|
||||
except Exception as e:
|
||||
logger.error(f"获取订单 {instance.pk} 旧状态失败: {e},跳过信号处理")
|
||||
return # 出错时静默退出,绝不阻塞订单保存
|
||||
|
||||
old_status = old_status or 0 # 防None
|
||||
|
||||
# 3. 【核心逻辑】状态从 非8 变为 8
|
||||
if old_status != 8 and instance.zhuangtai == 8:
|
||||
instance.pending_dispatch = True # 标记为待处理
|
||||
instance.status_8_time = timezone.now() # 记录变8时间
|
||||
# 清空可能存在的旧任务信息(安全起见)
|
||||
instance.auto_task_id = ''
|
||||
instance.auto_expire_at = None
|
||||
logger.info(f"📝 订单 {instance.dingdan_id} 状态变8,已标记为待调度。")
|
||||
|
||||
# 4. 【核心逻辑】状态从 8 变为 非8
|
||||
elif old_status == 8 and instance.zhuangtai != 8:
|
||||
# 清除所有调度标记和任务信息
|
||||
instance.pending_dispatch = False
|
||||
instance.status_8_time = None
|
||||
instance.auto_task_id = ''
|
||||
instance.auto_expire_at = None
|
||||
logger.info(f"📝 订单 {instance.dingdan_id} 状态离开8,已清除所有调度标记。")'''
|
||||
|
||||
|
||||
from django.db.models.signals import pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from celery.result import AsyncResult
|
||||
import logging
|
||||
|
||||
from dingdan.models import Dingdan
|
||||
from dingdan.tasks import process_expired_order # 确保导入你的任务函数
|
||||
|
||||
'''logger = logging.getLogger(__name__)
|
||||
@receiver(pre_save, sender=Dingdan)
|
||||
def handle_order_status_change(sender, instance, **kwargs):
|
||||
"""
|
||||
监听订单状态变化(稳定生产版)- 修复配置问题
|
||||
"""
|
||||
# 1. 跳过新建的订单(无旧状态可比)
|
||||
if instance.pk is None:
|
||||
return
|
||||
|
||||
try:
|
||||
old = sender.objects.get(pk=instance.pk)
|
||||
except sender.DoesNotExist:
|
||||
return
|
||||
# dingdan/signals.py - 修改状态变8的逻辑部分
|
||||
|
||||
|
||||
# 2. 状态变成 8:提交任务
|
||||
if old.zhuangtai != 8 and instance.zhuangtai == 8:
|
||||
from django.conf import settings
|
||||
from celery import current_app
|
||||
|
||||
# 🔥【核心修复】确保信号上下文中的Celery应用有正确配置
|
||||
# 直接从你的Django settings中获取Redis连接字符串,并设置给current_app
|
||||
if hasattr(settings, 'CELERY_BROKER_URL'):
|
||||
current_app.conf.update(broker_url=settings.CELERY_BROKER_URL)
|
||||
else:
|
||||
# 如果settings里没有,就按你的配置拼接(和celery.py里一致)
|
||||
redis_password = getattr(settings, 'REDIS_PASSWORD', 'Dujieduze5.3')
|
||||
redis_host = getattr(settings, 'REDIS_HOST', '172.19.0.3')
|
||||
redis_port = getattr(settings, 'REDIS_PORT', 6379)
|
||||
redis_db_celery = getattr(settings, 'REDIS_DB_CELERY', 0)
|
||||
broker_url = f'redis://:{redis_password}@{redis_host}:{redis_port}/{redis_db_celery}'
|
||||
current_app.conf.update(broker_url=broker_url)
|
||||
|
||||
countdown_seconds = getattr(settings, 'ORDER_EXPIRE_SECONDS', 172800) # 默认48小时
|
||||
|
||||
try:
|
||||
# 现在再提交任务
|
||||
job = process_expired_order.apply_async(
|
||||
args=[instance.dingdan_id],
|
||||
countdown=countdown_seconds,
|
||||
queue='order_tasks',
|
||||
priority=9
|
||||
)
|
||||
# ✅ 核心:将任务ID和计划时间记录到当前实例
|
||||
instance.auto_task_id = job.id
|
||||
instance.auto_expire_at = timezone.now() + timedelta(seconds=countdown_seconds)
|
||||
logger.info(f"✅ 订单 {instance.dingdan_id} 状态变8,已提交延时任务,ID: {job.id}")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 提交订单 {instance.dingdan_id} 的延时任务失败: {str(e)}")
|
||||
return # 处理完毕,直接返回
|
||||
|
||||
# 3. 状态从 8 被改成别的 -> 尝试撤销任务 (这部分代码不变)
|
||||
if old.zhuangtai == 8 and instance.zhuangtai != 8:
|
||||
if old.auto_task_id:
|
||||
try:
|
||||
AsyncResult(old.auto_task_id).revoke()
|
||||
logger.info(f"订单 {instance.dingdan_id} 状态离开8,已撤销原任务: {old.auto_task_id}")
|
||||
except Exception as e:
|
||||
logger.warning(f"撤销订单 {instance.dingdan_id} 的任务失败: {str(e)}")
|
||||
instance.auto_task_id = ''
|
||||
instance.auto_expire_at = None'''
|
||||
|
||||
|
||||
|
||||
'''def try_start_celery_task_async(dingdan_id, countdown):
|
||||
"""
|
||||
异步尝试启动Celery任务(在独立线程中运行)
|
||||
如果Redis不可用,立即失败,不影响主流程
|
||||
"""
|
||||
def _async_task():
|
||||
try:
|
||||
# 设置超时时间(500毫秒),避免长时间等待
|
||||
import socket
|
||||
socket.setdefaulttimeout(0.5)
|
||||
|
||||
from dingdan.tasks import process_expired_order
|
||||
from celery.exceptions import TimeoutError
|
||||
|
||||
# 尝试启动任务,设置极短的超时
|
||||
task = process_expired_order.apply_async(
|
||||
args=[dingdan_id],
|
||||
countdown=countdown,
|
||||
queue='order_tasks',
|
||||
priority=9,
|
||||
expires=countdown + 3600 # 任务1小时后过期,避免堆积
|
||||
)
|
||||
|
||||
# 不等待任务结果,立即返回
|
||||
logger.info(f"订单 {dingdan_id} 异步任务已提交(任务ID: {task.id})")
|
||||
|
||||
except Exception as e:
|
||||
# 记录警告,但不要影响主流程
|
||||
logger.warning(f"订单 {dingdan_id} 异步任务提交失败: {str(e)}")
|
||||
# 这个失败不影响订单保存,系统有补偿机制
|
||||
|
||||
# 启动异步线程(后台运行,不阻塞主流程)
|
||||
thread = threading.Thread(target=_async_task)
|
||||
thread.daemon = True # 设置为守护线程,主线程退出时会自动结束
|
||||
thread.start()
|
||||
|
||||
@receiver(pre_save, sender=Dingdan)
|
||||
def handle_order_status_change_lightweight(sender, instance, **kwargs):
|
||||
print(f"🚨【信号触发】订单ID: {getattr(instance, 'dingdan_id', 'N/A')}, 新状态: {instance.zhuangtai}")
|
||||
"""
|
||||
轻量级订单状态变化监听器
|
||||
设计原则:
|
||||
1. 绝对不阻塞订单保存
|
||||
2. 不依赖外部服务可用性
|
||||
3. 只记录,不执行
|
||||
"""
|
||||
try:
|
||||
# 1. 快速检查:如果是新建订单,直接返回
|
||||
if instance.pk is None:
|
||||
return
|
||||
|
||||
# 2. 快速获取旧状态(使用数据库当前值,不加载完整对象)
|
||||
try:
|
||||
old_status = Dingdan.objects.filter(pk=instance.pk).values_list('zhuangtai', flat=True).first()
|
||||
except Exception:
|
||||
old_status = None
|
||||
|
||||
# 3. 状态变化判断
|
||||
if old_status is None or old_status != instance.zhuangtai:
|
||||
# 状态发生变化,记录到订单扩展字段或日志(但不要阻塞)
|
||||
try:
|
||||
# 这里只是示例,实际可以根据需要记录状态变化历史
|
||||
pass
|
||||
except:
|
||||
pass # 即使记录失败也不影响主流程
|
||||
|
||||
# 4. 特殊状态处理:状态变为8
|
||||
if old_status != 8 and instance.zhuangtai == 8:
|
||||
# 4.1 立即记录日志(不等待)
|
||||
logger.info(f"订单 {instance.dingdan_id} 状态变为8")
|
||||
|
||||
# 4.2 异步尝试启动定时任务(不阻塞主流程)
|
||||
# 使用线程池或直接新线程,确保不阻塞
|
||||
try:
|
||||
# 启动异步线程尝试提交Celery任务
|
||||
if hasattr(settings, 'CELERY_BROKER_URL') and settings.CELERY_BROKER_URL:
|
||||
# 在独立线程中尝试,完全不阻塞
|
||||
threading.Thread(
|
||||
target=try_start_celery_task_async,
|
||||
args=(instance.dingdan_id, settings.ORDER_EXPIRE_SECONDS),
|
||||
daemon=True
|
||||
).start()
|
||||
except Exception as e:
|
||||
# 即使线程启动失败也不影响主流程
|
||||
logger.warning(f"启动异步任务线程失败: {str(e)}")
|
||||
|
||||
# 5. 状态从8变为其他
|
||||
elif old_status == 8 and instance.zhuangtai != 8:
|
||||
# 状态不再是8,记录日志即可
|
||||
# 定时任务执行时会检查状态,如果不是8会自动跳过
|
||||
logger.info(f"订单 {instance.dingdan_id} 状态从8变为{instance.zhuangtai}")
|
||||
|
||||
except Exception as e:
|
||||
# 捕获所有异常,确保不影响订单保存
|
||||
# 只记录错误,不抛出异常
|
||||
logger.error(f"信号处理异常(已捕获,不影响订单): {str(e)}")'''
|
||||
|
||||
|
||||
'''from django.db.models.signals import pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings
|
||||
import logging
|
||||
|
||||
from dingdan.models import Dingdan
|
||||
from dingdan.tasks import process_expired_order
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@receiver(pre_save, sender=Dingdan)
|
||||
def handle_order_status_change(sender, instance, **kwargs):
|
||||
"""
|
||||
监听订单状态变化
|
||||
当状态变为8时:启动48小时延时任务
|
||||
当状态从8变为其他时:任务执行时会自动跳过,无需取消
|
||||
"""
|
||||
try:
|
||||
# 如果是新建订单,没有旧记录,直接返回
|
||||
if instance.pk is None:
|
||||
return
|
||||
|
||||
# 获取数据库中旧的订单记录
|
||||
old_instance = Dingdan.objects.get(pk=instance.pk)
|
||||
|
||||
# ============ 状态从非8变为8 ============
|
||||
if old_instance.zhuangtai != 8 and instance.zhuangtai == 8:
|
||||
# 启动48小时后的延时任务(countdown单位:秒)
|
||||
task = process_expired_order.apply_async(
|
||||
args=[instance.dingdan_id],
|
||||
countdown=settings.ORDER_EXPIRE_SECONDS, # 172800秒 = 48小时
|
||||
queue='order_tasks',
|
||||
priority=9 # 高优先级
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"订单 {instance.dingdan_id} 状态变为8,已启动48小时延时任务,"
|
||||
f"任务ID: {task.id}"
|
||||
)
|
||||
|
||||
# 记录任务启动成功(可选,可以存储到订单表或日志表)
|
||||
|
||||
# ============ 状态从8变为其他 ============
|
||||
# 我们不需要取消任务,因为任务执行时会检查状态是否为8
|
||||
# 如果不是状态8,任务会自动跳过处理
|
||||
|
||||
except Dingdan.DoesNotExist:
|
||||
# 新订单,无需处理
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"处理订单状态变化信号失败,订单ID: {instance.dingdan_id if hasattr(instance, 'dingdan_id') else '未知'}, 错误: {str(e)}")
|
||||
# 不抛出异常,避免影响订单保存的主流程
|
||||
# 信号处理失败不影响订单保存,有周期性检查作为备用方案'''
|
||||
216
dingdan/tasks.py
Normal file
216
dingdan/tasks.py
Normal file
@@ -0,0 +1,216 @@
|
||||
"""
|
||||
阿龙电竞 - 订单定时任务
|
||||
处理订单超时自动确认等逻辑
|
||||
完整版本,包含所有必要的异常处理
|
||||
"""
|
||||
|
||||
from celery import shared_task
|
||||
from django.db import transaction
|
||||
from django.db.models import F, Q
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
|
||||
# 导入模型 - 确保路径正确
|
||||
from dingdan.models import Dingdan, DingdanShangjia
|
||||
from yonghu.models import UserMain, UserDashou, UserShangjia
|
||||
from utils.celery_utils import safe_decimal_operation, log_task_execution, rollback_on_failure
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@shared_task(bind=True, max_retries=3, default_retry_delay=60)
|
||||
def process_expired_order(self, dingdan_id):
|
||||
"""
|
||||
处理单个超时订单(订单状态=8,超过48小时未处理)
|
||||
"""
|
||||
|
||||
|
||||
try:
|
||||
# 使用select_for_update锁定记录,防止并发修改
|
||||
with transaction.atomic():
|
||||
|
||||
# 获取订单,同时锁定记录
|
||||
order = Dingdan.objects.select_for_update().get(
|
||||
dingdan_id=dingdan_id
|
||||
)
|
||||
|
||||
|
||||
|
||||
# 其余代码不变...
|
||||
# ... 保持原有代码
|
||||
|
||||
|
||||
|
||||
# 再次检查订单状态是否为8
|
||||
if order.zhuangtai != 8:
|
||||
logger.info(f"订单{dingdan_id}状态已不是8(当前:{order.zhuangtai}),跳过处理")
|
||||
return f"订单{dingdan_id}状态已变更,跳过处理"
|
||||
|
||||
# 获取发单平台类型
|
||||
fadan_pingtai = order.fadan_pingtai
|
||||
|
||||
# 获取打手ID和分成金额
|
||||
dashou_id = order.jiedan_dashou_id
|
||||
dashou_fencheng = safe_decimal_operation(order.dashou_fencheng)
|
||||
|
||||
# 1. 更新订单状态为3(已完成)
|
||||
order.zhuangtai = 3
|
||||
order.save(update_fields=['zhuangtai'])
|
||||
|
||||
# 2. 更新打手信息(如果存在)
|
||||
updated_dashou = False
|
||||
if dashou_id:
|
||||
try:
|
||||
# 查询打手用户主表
|
||||
dashou_user = UserMain.objects.filter(yonghuid=dashou_id).first()
|
||||
if dashou_user:
|
||||
# 获取打手扩展表
|
||||
dashou_profile = getattr(dashou_user, 'dashou_profile', None)
|
||||
if dashou_profile:
|
||||
# 使用F表达式原子更新
|
||||
UserDashou.objects.filter(pk=dashou_profile.pk).update(
|
||||
chengjiaozongliang=F('chengjiaozongliang') + 1,
|
||||
yue=F('yue') + dashou_fencheng,
|
||||
zonge=F('zonge') + dashou_fencheng,
|
||||
jinrishouyi=F('jinrishouyi') + dashou_fencheng,
|
||||
jinyueshouyi=F('jinyueshouyi') + dashou_fencheng
|
||||
)
|
||||
updated_dashou = True
|
||||
logger.info(f"更新打手{dashou_id}信息成功")
|
||||
except Exception as e:
|
||||
logger.error(f"更新打手信息失败,打手ID: {dashou_id}, 错误: {str(e)}")
|
||||
# 继续执行,不中断流程
|
||||
|
||||
# 3. 如果是商家发单,更新商家信息
|
||||
updated_shangjia = False
|
||||
if fadan_pingtai == 2: # 商家发单
|
||||
try:
|
||||
# 获取商家扩展信息
|
||||
shangjia_kuozhan = getattr(order, 'shangjia_kuozhan', None)
|
||||
if shangjia_kuozhan:
|
||||
shangjia_id = shangjia_kuozhan.shangjia_id
|
||||
if shangjia_id:
|
||||
# 查询商家用户
|
||||
shangjia_user = UserMain.objects.filter(yonghuid=shangjia_id).first()
|
||||
if shangjia_user:
|
||||
shangjia_profile = getattr(shangjia_user, 'shop_profile', None)
|
||||
if shangjia_profile:
|
||||
# 更新商家成交订单数量
|
||||
UserShangjia.objects.filter(pk=shangjia_profile.pk).update(
|
||||
chengjiao=F('chengjiao') + 1
|
||||
)
|
||||
updated_shangjia = True
|
||||
logger.info(f"更新商家{shangjia_id}信息成功")
|
||||
except Exception as e:
|
||||
logger.error(f"更新商家信息失败,订单ID: {dingdan_id}, 错误: {str(e)}")
|
||||
# 继续执行,不中断流程
|
||||
|
||||
# 记录任务执行结果
|
||||
result_msg = f"订单{dingdan_id}自动结算完成"
|
||||
if updated_dashou:
|
||||
result_msg += ",打手信息已更新"
|
||||
if updated_shangjia:
|
||||
result_msg += ",商家信息已更新"
|
||||
|
||||
if fadan_pingtai == 1:
|
||||
try:
|
||||
from dingdan.utils import update_platform_profit # 导入函数
|
||||
update_platform_profit(dingdan_id)
|
||||
except Exception as e:
|
||||
# 平台收益更新失败不影响主流程,但记录日志
|
||||
logger.error(f"强制结单后更新平台收益失败: {str(e)}")
|
||||
|
||||
log_task_execution(f"自动结算订单 {dingdan_id}", True, result_msg)
|
||||
return result_msg
|
||||
|
||||
except Dingdan.DoesNotExist:
|
||||
error_msg = f"订单{dingdan_id}不存在"
|
||||
logger.warning(error_msg)
|
||||
return error_msg
|
||||
except Exception as e:
|
||||
error_msg = f"处理订单{dingdan_id}时发生错误: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
|
||||
# 重试机制
|
||||
if self.request.retries < self.max_retries:
|
||||
logger.info(f"订单{dingdan_id}处理失败,准备第{self.request.retries + 1}次重试")
|
||||
raise self.retry(exc=e)
|
||||
|
||||
log_task_execution(f"自动结算订单 {dingdan_id}", False, error_msg)
|
||||
return error_msg
|
||||
|
||||
@shared_task
|
||||
@rollback_on_failure("批量检查超时订单")
|
||||
def check_order_expire_task():
|
||||
"""
|
||||
批量检查超时订单(补偿机制)
|
||||
每5分钟执行一次,作为信号处理的备用方案
|
||||
查找状态为8且创建时间超过72小时的订单(宽松时间,防止误处理)
|
||||
"""
|
||||
try:
|
||||
# 计算更宽松的时间点:当前时间减去72小时(48+24小时)
|
||||
# 这样避免误处理刚变为状态8的订单
|
||||
check_time = timezone.now() - timedelta(seconds=settings.ORDER_EXPIRE_SECONDS + 86400) # 48+24=72小时
|
||||
|
||||
# 查询状态为8且创建时间早于检查时间的订单
|
||||
expired_orders = Dingdan.objects.filter(
|
||||
zhuangtai=8,
|
||||
create_time__lt=check_time
|
||||
).values_list('dingdan_id', flat=True)
|
||||
|
||||
expired_count = expired_orders.count()
|
||||
|
||||
if expired_count == 0:
|
||||
logger.info("补偿检查:无超时订单")
|
||||
return {"success": True, "message": "无超时订单", "count": 0}
|
||||
|
||||
# 分批处理超时订单(避免一次处理太多)
|
||||
batch_size = 10
|
||||
processed_count = 0
|
||||
|
||||
for i in range(0, expired_count, batch_size):
|
||||
batch = expired_orders[i:i+batch_size]
|
||||
|
||||
# 异步处理每个超时订单
|
||||
for dingdan_id in batch:
|
||||
process_expired_order.apply_async(
|
||||
args=[dingdan_id],
|
||||
queue='order_tasks',
|
||||
priority=8 # 补偿任务的优先级低一些
|
||||
)
|
||||
processed_count += 1
|
||||
|
||||
result_msg = f"补偿检查:发现{expired_count}个可能超时的订单,已提交处理{processed_count}个"
|
||||
log_task_execution("批量检查超时订单", True, result_msg)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": result_msg,
|
||||
"total_count": expired_count,
|
||||
"processed_count": processed_count
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"批量检查超时订单失败: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
log_task_execution("批量检查超时订单", False, error_msg)
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"message": error_msg,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
3
dingdan/tests.py
Normal file
3
dingdan/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
71
dingdan/tongzhi_tasks.py
Normal file
71
dingdan/tongzhi_tasks.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# dingdan/tongzhi_tasks.py
|
||||
import logging
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from a_long_dianjing.celery_app import app
|
||||
from yonghu.models import OfficialAccountUser
|
||||
from utils.weixin_broadcast import WeixinBroadcastSender
|
||||
|
||||
logger = logging.getLogger('weixin_broadcast')
|
||||
|
||||
|
||||
@app.task(bind=True, max_retries=3, default_retry_delay=60)
|
||||
def dingdan_guangbo(self, order_info):
|
||||
"""
|
||||
异步并发广播订单模板消息给所有关注服务号的用户
|
||||
order_info 字典字段示例:
|
||||
{
|
||||
'dingdan_id': 'PT123456',
|
||||
'game_type': '王者荣耀',
|
||||
'amount': '88.00',
|
||||
'order_desc': '星耀上王者'
|
||||
}
|
||||
"""
|
||||
sender = WeixinBroadcastSender()
|
||||
template_data = sender.build_order_message_data(order_info)
|
||||
order_id = order_info.get('dingdan_id')
|
||||
|
||||
# 查询所有已关注用户
|
||||
try:
|
||||
openids = list(
|
||||
OfficialAccountUser.objects
|
||||
.filter(is_subscribed=True)
|
||||
.values_list('official_openid', flat=True)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f'查询订阅用户失败: {e}')
|
||||
raise self.retry(exc=e)
|
||||
|
||||
if not openids:
|
||||
return {'success': True, 'msg': '无订阅用户'}
|
||||
|
||||
total = len(openids)
|
||||
logger.info(f'📊 Celery 广播订单 {order_id} 开始,目标人数: {total}')
|
||||
|
||||
success = 0
|
||||
fail = 0
|
||||
|
||||
# 20 个线程并发发送,微信接口耗时 200-500ms,1000 人约 15 秒完成
|
||||
with ThreadPoolExecutor(max_workers=20) as executor:
|
||||
future_map = {
|
||||
executor.submit(sender.send_to_user, openid, template_data, order_id): openid
|
||||
for openid in openids
|
||||
}
|
||||
for future in as_completed(future_map):
|
||||
openid = future_map[future]
|
||||
try:
|
||||
res = future.result(timeout=30)
|
||||
if res.get('success'):
|
||||
success += 1
|
||||
else:
|
||||
fail += 1
|
||||
except Exception as e:
|
||||
fail += 1
|
||||
logger.error(f'发送异常 {openid[:10]}...: {e}')
|
||||
|
||||
logger.info(f'🏁 广播完成:成功 {success},失败 {fail},总计 {total}')
|
||||
return {
|
||||
'success': True,
|
||||
'total': total,
|
||||
'success_count': success,
|
||||
'fail_count': fail
|
||||
}
|
||||
91
dingdan/urls.py
Normal file
91
dingdan/urls.py
Normal file
@@ -0,0 +1,91 @@
|
||||
from django.urls import path
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from .views import CreateOrderView, WechatPayNotifyView, \
|
||||
PaymentFailView, PaymentVerifyView, DingdanHuoquView, DingdanXiangqingView, JiedanView, \
|
||||
ShangpinLeixingView, ShangjiaPaifaView, ShangjiaDingdanHuoquView, ShangjiaJiesuanView, \
|
||||
ShangjiaDingdanXiangqingView, ShangjiaChufaShenqingView, ShangjiaTuikuanShenqingView, \
|
||||
ShangjiaChexiaoView, DashouDingdanHuoquView, ShangpinLeixingHuoquView, QiangdanView, \
|
||||
DashouDingdanHuoquView1, DashouCOSZhengshuView, DashouTijiaoView, DashouDingdanXiangqingView, AdGetDingdanXiangQing, \
|
||||
AdTongYiTuiKuanPingTai, AdTongYiTuiKuanShangJia, AdQiangZhiJieDan, AdJuJueTuiKuan,\
|
||||
AdGengHuanDaShou,ShangjiaCOSZhengshuView, DashouHuoquLeixingView,PartnerNotifyClaimView, PartnerSyncOrderView, PartnerCheckClaimView, PartnerRefundNotifyView,AdJuJueJieSuan, AdZhuanYiDaTing, AdQuXiaoZhiDing,DashouXiugaiView,DaiLiQunLiaoXiaoXiView,\
|
||||
ShangjiaLianjieLiuYanZhuanFaView, DingdanXiangqingView2, JiedanView2, ZxsjghdsView, \
|
||||
ShangjiaFakuanApplyView, ShangjiaFakuaiXiugaiView
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('xiadan', CreateOrderView.as_view(), name='下单订单生成接口'),
|
||||
#path('pay-notify/', WechatPayNotifyView.as_view(), name='老板下单微信支付回调'),
|
||||
path('pay-notify/', csrf_exempt(WechatPayNotifyView.as_view()), name='老板下单微信支付回调'),
|
||||
path('shibai', PaymentFailView.as_view(), name='老板支付失败接口'),
|
||||
path('fucha', PaymentVerifyView.as_view(), name='老板支付成功复查接口'),
|
||||
path('dingdanhuoqu', DingdanHuoquView.as_view(), name='老板订单获取'),
|
||||
path('dingdanxiangqing', DingdanXiangqingView.as_view(), name='老板订单详情获取'),
|
||||
path('jiedan', JiedanView.as_view(), name='老板结单'),
|
||||
path('sjspleixing', ShangpinLeixingView.as_view(), name='派单获取商品类型'),
|
||||
path('sjpaifa', ShangjiaPaifaView.as_view(), name='商家发单'),
|
||||
path('sjdingdanhq', ShangjiaDingdanHuoquView.as_view(), name='商家订单获取'),
|
||||
path('sjddxq', ShangjiaDingdanXiangqingView.as_view(), name='商家订单详情获取'),
|
||||
path('sjjiesuan', ShangjiaJiesuanView.as_view(), name='商家结单'),
|
||||
path('sjchexiao', ShangjiaChexiaoView.as_view(), name='商家撤销订单'),
|
||||
path('sjtkshenqing', ShangjiaTuikuanShenqingView.as_view(), name='商家退款申请'),
|
||||
path('sjchufa', ShangjiaChufaShenqingView.as_view(), name='商家处罚打手申请'),
|
||||
path('ddlxhq', ShangpinLeixingHuoquView.as_view(), name='打手抢单页商品类型获取'),
|
||||
path('dshqdingdan', DashouDingdanHuoquView1.as_view(), name='打手订单列表页面获取'),
|
||||
|
||||
# 获取订单列表
|
||||
path('ddhq', DashouDingdanHuoquView.as_view(), name='打手抢单页获取订单'),
|
||||
path('qiangdan', QiangdanView.as_view(), name='打手抢单'),
|
||||
path('dsscpz', DashouCOSZhengshuView.as_view(), name='打手上传图片OSS参数获取接口'),
|
||||
# 打手提交订单完成接口
|
||||
path('dstijiao', DashouTijiaoView.as_view(), name='打手订单提交'),
|
||||
# 打手订单详情获取接口
|
||||
path('dsddxq', DashouDingdanXiangqingView.as_view(), name='打手获取订单详情'),
|
||||
path('adddxq', AdGetDingdanXiangQing.as_view(), name='管理员获取订单详情页面具体信息'),
|
||||
path('adddpttk', AdTongYiTuiKuanPingTai.as_view(), name='同意平台订单退款'),
|
||||
|
||||
# 商家订单退款接口(之前写的)
|
||||
path('adddsjtk', AdTongYiTuiKuanShangJia.as_view(), name='同意商家退款'),
|
||||
|
||||
# 强制结单接口
|
||||
#path('adddqzjd', AdQiangZhiJieDan.as_view(), name='强制结单'),
|
||||
|
||||
# 拒绝退款接口
|
||||
path('adddjj', AdJuJueTuiKuan.as_view(), name='拒绝退款'),
|
||||
|
||||
# 更换打手接口
|
||||
path('adddgh', AdGengHuanDaShou.as_view(), name='管理更换打手'),
|
||||
path('sjscpz', ShangjiaCOSZhengshuView.as_view(), name='商家上传证据图片获取密钥'),
|
||||
path('dsqdhqddlx', DashouHuoquLeixingView.as_view(), name='打手抢单页面获取订单类型'),
|
||||
|
||||
|
||||
|
||||
path('adddjjjs', AdJuJueJieSuan.as_view(), name='管理员拒绝给打手钱'),
|
||||
#path('adddzydt', AdZhuanYiDaTing.as_view(), name='订单转移大厅'),
|
||||
path('adddqxzd', AdQuXiaoZhiDing.as_view(), name='取消指定'),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
path('partner_sync_order', csrf_exempt(PartnerSyncOrderView.as_view()), name='东道主派单接口'),
|
||||
path('partner_check_claim', csrf_exempt(PartnerCheckClaimView.as_view()), name='对方抢单询问接口'),
|
||||
|
||||
|
||||
path('partner_notify_claim', csrf_exempt(PartnerNotifyClaimView.as_view()), name='东道主订单被抢通知接口'),
|
||||
#path('partner_refund_notify', csrf_exempt(PartnerRefundNotifyView.as_view()), name='接受东道主退款通知'),
|
||||
path('dsxiugaidd', DashouXiugaiView.as_view(), name='打手修改订单详情'),
|
||||
|
||||
path('kptxxfs', DaiLiQunLiaoXiaoXiView.as_view(), name='前端聊天转发'), # 群聊消息代理发送
|
||||
path('sjljlbxxzf', ShangjiaLianjieLiuYanZhuanFaView.as_view(), name='商家链接老板聊天转发'),
|
||||
|
||||
path('dingdanxiangqing2', DingdanXiangqingView2.as_view(), name='老板获取订单详情新街口'),
|
||||
path('jiedan2', JiedanView2.as_view(), name='老板结单新街口'),
|
||||
path('zxsjghds', ZxsjghdsView.as_view(), name='商家更换打手'), # 商家更换打手
|
||||
path('sjfksq', ShangjiaFakuanApplyView.as_view(), name='商家罚款'),
|
||||
path('sjfkxgycx', ShangjiaFakuaiXiugaiView.as_view(), name='商家罚款和退款修改'),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
]
|
||||
443
dingdan/utils.py
Normal file
443
dingdan/utils.py
Normal file
@@ -0,0 +1,443 @@
|
||||
|
||||
import threading
|
||||
import requests
|
||||
|
||||
from django.conf import settings
|
||||
from peizhi.models import Club
|
||||
from shangpin.models import ShangpinLeixing
|
||||
from dingdan.models import Dingdan # 根据实际路径导入
|
||||
|
||||
import logging
|
||||
from datetime import date
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
from peizhi.models import DailyDispatchStat # 假设模型在 peizhi 应用中
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 可选:线程池限制(避免大量线程同时创建,建议使用 Celery,这里简单用线程)
|
||||
THREAD_POOL_SIZE = 10
|
||||
_semaphore = threading.Semaphore(THREAD_POOL_SIZE)
|
||||
|
||||
def sync_order_to_partners(dingdan_id):
|
||||
"""
|
||||
异步向所有开启互通的合作平台同步订单。
|
||||
使用信号量限制并发线程数。
|
||||
"""
|
||||
def _task():
|
||||
_semaphore.acquire()
|
||||
try:
|
||||
_sync_order_task(dingdan_id)
|
||||
finally:
|
||||
_semaphore.release()
|
||||
|
||||
thread = threading.Thread(target=_task)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
def _sync_order_task(dingdan_id):
|
||||
"""实际同步任务(在线程中执行)"""
|
||||
try:
|
||||
# 获取订单对象(只取必要字段,提升性能)
|
||||
dingdan = Dingdan.objects.only(
|
||||
'dingdan_id', 'zhuangtai', 'jine', 'dashou_fencheng', 'jieshao',
|
||||
'beizhu', 'nicheng', 'leixing_id', 'yaoqiuleixing', 'huiyuan_id',
|
||||
'yongjin', 'zhiding_id', 'create_time'
|
||||
).get(dingdan_id=dingdan_id)
|
||||
|
||||
# 基本条件判断
|
||||
if dingdan.zhuangtai != 1:
|
||||
logger.info(f"订单 {dingdan_id} 状态 {dingdan.zhuangtai} 不是1,不同步")
|
||||
return
|
||||
if dingdan.zhiding_id:
|
||||
logger.info(f"订单 {dingdan_id} 是指定单,不同步")
|
||||
return
|
||||
if dingdan.yaoqiuleixing != 1:
|
||||
logger.info(f"订单 {dingdan_id} 抢单要求类型不是会员抢单,不同步")
|
||||
return
|
||||
if not dingdan.leixing_id:
|
||||
logger.info(f"订单 {dingdan_id} 无商品类型,不同步")
|
||||
return
|
||||
try:
|
||||
leixing = ShangpinLeixing.objects.get(id=dingdan.leixing_id)
|
||||
if not leixing.is_cross_enabled:
|
||||
logger.info(f"订单 {dingdan_id} 的商品类型未开启互通,不同步")
|
||||
return
|
||||
except ShangpinLeixing.DoesNotExist:
|
||||
logger.info(f"订单 {dingdan_id} 的商品类型不存在,不同步")
|
||||
return
|
||||
|
||||
# 查询所有开启互通的俱乐部
|
||||
clubs = Club.objects.filter(is_interop_enabled=True).select_related()
|
||||
if not clubs.exists():
|
||||
return
|
||||
|
||||
# 为每个俱乐部发送订单
|
||||
for club in clubs:
|
||||
_send_to_single_partner(dingdan, club)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"同步订单任务异常: {e}", exc_info=True)
|
||||
|
||||
def _send_to_single_partner(dingdan, club):
|
||||
"""向单个俱乐部发送订单(带重试,幂等处理)"""
|
||||
# 计算实际金额:订单打手分成 × 对方费率
|
||||
actual_amount = dingdan.dashou_fencheng * club.dashou_rate
|
||||
|
||||
# 构建请求数据
|
||||
payload = {
|
||||
'token': settings.CROSS_PLATFORM_TOKEN,
|
||||
'our_club_id': club.club_id, # 我方在对方系统中的标识(存储于对方表的 partner_club_id)
|
||||
'partner_club_id': club.partner_club_id, # 对方自己的俱乐部ID
|
||||
'partner_order_id': dingdan.dingdan_id, # 我方订单ID作为对方的外部订单号
|
||||
'jine': str(actual_amount), # 对方平台应展示的订单金额
|
||||
'dashou_fencheng': str(actual_amount), # 对方平台打手分成(与金额一致)
|
||||
'jieshao': dingdan.jieshao,
|
||||
'beizhu': dingdan.beizhu,
|
||||
'create_time': dingdan.create_time.isoformat(),
|
||||
'nicheng': dingdan.nicheng,
|
||||
'leixing_id': dingdan.leixing_id, # 我方商品类型ID,对方需要映射
|
||||
'user_id': get_order_user_id(dingdan), # 对方平台下单用户ID(不带前缀)
|
||||
}
|
||||
|
||||
# 重试机制(最多3次,指数退避)
|
||||
for attempt in range(1, 4):
|
||||
try:
|
||||
response = requests.post(
|
||||
club.partner_domain + '/dingdan/partner_sync_order',
|
||||
json=payload,
|
||||
timeout=5
|
||||
)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
# 对方返回 code=0 表示成功(包括新建或已存在)
|
||||
if data.get('code') == 0:
|
||||
# 判断本次响应是新创建还是已存在
|
||||
status = data.get('data', {}).get('status', '')
|
||||
if status == 'existed':
|
||||
# 订单已经存在于对方系统,不重复统计
|
||||
logger.info(f"订单 {dingdan.dingdan_id} 在俱乐部 {club.club_id} 已存在,跳过统计")
|
||||
else:
|
||||
# 新创建成功,更新统计
|
||||
try:
|
||||
update_daily_dispatch_stat(
|
||||
direction=1, # 我方派单
|
||||
partner_club_id=club.partner_club_id,
|
||||
dispatch_amount=actual_amount,
|
||||
|
||||
)
|
||||
logger.info(f"成功同步订单 {dingdan.dingdan_id} 到俱乐部 {club.club_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"更新派单统计失败: {e}", exc_info=True)
|
||||
|
||||
# 标记订单为跨平台(仅当第一次成功时标记,避免重复标记)
|
||||
if not dingdan.is_cross:
|
||||
Dingdan.objects.filter(dingdan_id=dingdan.dingdan_id).update(
|
||||
is_cross=1,
|
||||
dispatch_type=1 # 我方派单
|
||||
)
|
||||
logger.info(f"订单 {dingdan.dingdan_id} 已标记为跨平台订单")
|
||||
break
|
||||
else:
|
||||
# 对方返回业务错误(如映射不存在等),不重试
|
||||
logger.error(f"同步订单失败(业务): {data}")
|
||||
break
|
||||
else:
|
||||
logger.error(f"同步订单HTTP失败: {response.status_code}")
|
||||
except Exception as e:
|
||||
logger.error(f"同步订单异常(尝试 {attempt}/3): {e}")
|
||||
|
||||
if attempt < 3:
|
||||
import time
|
||||
time.sleep(2 ** attempt) # 指数退避
|
||||
else:
|
||||
logger.error(f"同步订单 {dingdan.dingdan_id} 到俱乐部 {club.club_id} 最终失败")
|
||||
|
||||
|
||||
|
||||
# 获取下单方ID(根据订单类型)
|
||||
def get_order_user_id(order):
|
||||
from dingdan.models import DingdanPingtai, DingdanShangjia
|
||||
if order.fadan_pingtai == 1:
|
||||
try:
|
||||
ext = DingdanPingtai.objects.get(dingdan=order)
|
||||
return ext.laoban_id # 老板ID
|
||||
except DingdanPingtai.DoesNotExist:
|
||||
return None
|
||||
elif order.fadan_pingtai == 2:
|
||||
try:
|
||||
ext = DingdanShangjia.objects.get(dingdan=order)
|
||||
return ext.shangjia_id # 商家ID
|
||||
except DingdanShangjia.DoesNotExist:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
# dingdan/utils/dispatch_stat.py
|
||||
import logging
|
||||
|
||||
import logging
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
from peizhi.models import DailyDispatchStat # 请根据实际模型位置修改导入路径
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def update_daily_dispatch_stat(direction, partner_club_id,
|
||||
dispatch_amount=None, dispatch_count=1,
|
||||
claimed_amount=None, claimed_count=1,
|
||||
success_amount=None, success_count=1):
|
||||
"""
|
||||
原子更新每日派单统计(支持派单、抢单、成交三种类型)。
|
||||
只更新传递了非None参数的字段,未传递的字段保持不变。
|
||||
|
||||
参数:
|
||||
direction: 1=我方派单,2=对方派单
|
||||
partner_club_id: 对方俱乐部ID
|
||||
dispatch_amount: 派单金额(Decimal),如果提供则累加到 dispatch_amount 和 dispatch_count
|
||||
dispatch_count: 派单数量,默认为1,与 dispatch_amount 同时提供时生效
|
||||
claimed_amount: 抢单金额(Decimal),如果提供则累加到 claimed_amount 和 claimed_count
|
||||
claimed_count: 抢单数量,默认为1,与 claimed_amount 同时提供时生效
|
||||
success_amount: 成交金额(Decimal),如果提供则累加到 success_amount 和 success_count
|
||||
success_count: 成交数量,默认为1,与 success_amount 同时提供时生效
|
||||
"""
|
||||
today = date.today()
|
||||
year, month, day = today.year, today.month, today.day
|
||||
|
||||
with transaction.atomic():
|
||||
# 使用 select_for_update 锁定行,避免并发累加冲突
|
||||
stat, created = DailyDispatchStat.objects.select_for_update().get_or_create(
|
||||
date=today,
|
||||
year=year,
|
||||
month=month,
|
||||
day=day,
|
||||
direction=direction,
|
||||
partner_club_id=partner_club_id,
|
||||
defaults={
|
||||
'dispatch_count': dispatch_count if dispatch_amount is not None else 0,
|
||||
'dispatch_amount': dispatch_amount or Decimal('0.00'),
|
||||
'claimed_count': claimed_count if claimed_amount is not None else 0,
|
||||
'claimed_amount': claimed_amount or Decimal('0.00'),
|
||||
'success_count': success_count if success_amount is not None else 0,
|
||||
'success_amount': success_amount or Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
if not created:
|
||||
update_fields = []
|
||||
if dispatch_amount is not None:
|
||||
stat.dispatch_count = F('dispatch_count') + dispatch_count
|
||||
stat.dispatch_amount = F('dispatch_amount') + dispatch_amount
|
||||
update_fields.extend(['dispatch_count', 'dispatch_amount'])
|
||||
if claimed_amount is not None:
|
||||
stat.claimed_count = F('claimed_count') + claimed_count
|
||||
stat.claimed_amount = F('claimed_amount') + claimed_amount
|
||||
update_fields.extend(['claimed_count', 'claimed_amount'])
|
||||
if success_amount is not None:
|
||||
stat.success_count = F('success_count') + success_count
|
||||
stat.success_amount = F('success_amount') + success_amount
|
||||
update_fields.extend(['success_count', 'success_amount'])
|
||||
|
||||
if update_fields:
|
||||
stat.save(update_fields=update_fields)
|
||||
|
||||
logger.info(f"更新派单统计: 日期={today}, 方向={direction}, 俱乐部={partner_club_id}, "
|
||||
f"派单+{dispatch_count if dispatch_amount else 0}单/{dispatch_amount or 0}, "
|
||||
f"抢单+{claimed_count if claimed_amount else 0}单/{claimed_amount or 0}, "
|
||||
f"成交+{success_count if success_amount else 0}单/{success_amount or 0}")
|
||||
|
||||
|
||||
|
||||
|
||||
from peizhi.models import DailyIncomeStat
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def update_daily_income(amount):
|
||||
"""
|
||||
原子更新当日收入统计(金额累加,笔数加1)。
|
||||
用于微信支付成功回调。
|
||||
|
||||
参数:
|
||||
amount: Decimal 本次收入金额
|
||||
"""
|
||||
today = date.today()
|
||||
year, month, day = today.year, today.month, today.day
|
||||
|
||||
with transaction.atomic():
|
||||
stat, created = DailyIncomeStat.objects.select_for_update().get_or_create(
|
||||
date=today,
|
||||
defaults={
|
||||
'year': year,
|
||||
'month': month,
|
||||
'day': day,
|
||||
'total_amount': amount,
|
||||
'total_count': 1
|
||||
}
|
||||
)
|
||||
if not created:
|
||||
# 原子累加
|
||||
stat.total_amount = F('total_amount') + amount
|
||||
stat.total_count = F('total_count') + 1
|
||||
stat.save(update_fields=['total_amount', 'total_count'])
|
||||
|
||||
logger.info(
|
||||
f"每日收入更新: {today}, +{amount}元, 总金额={stat.total_amount if created else stat.total_amount + amount}, 总笔数={stat.total_count if created else stat.total_count + 1}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
from peizhi.models import DailyPayoutStat
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def update_daily_payout(amount):
|
||||
"""
|
||||
原子更新当日出款统计(金额累加,笔数加1)。
|
||||
用于提现成功、结算打款等场景。
|
||||
|
||||
参数:
|
||||
amount: Decimal 本次出款金额
|
||||
"""
|
||||
today = date.today()
|
||||
year, month, day = today.year, today.month, today.day
|
||||
|
||||
with transaction.atomic():
|
||||
stat, created = DailyPayoutStat.objects.select_for_update().get_or_create(
|
||||
date=today,
|
||||
defaults={
|
||||
'year': year,
|
||||
'month': month,
|
||||
'day': day,
|
||||
'total_amount': amount,
|
||||
'total_count': 1
|
||||
}
|
||||
)
|
||||
if not created:
|
||||
# 原子累加
|
||||
stat.total_amount = F('total_amount') + amount
|
||||
stat.total_count = F('total_count') + 1
|
||||
stat.save(update_fields=['total_amount', 'total_count'])
|
||||
|
||||
logger.info(f"每日出款更新: {today}, +{amount}元, 总金额={stat.total_amount if created else stat.total_amount + amount}, 总笔数={stat.total_count if created else stat.total_count + 1}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from dingdan.models import Lilubiao
|
||||
|
||||
|
||||
def calc_shangjia_order_fencheng(jine):
|
||||
"""
|
||||
商家发单时计算打手/管事分成。
|
||||
打手优先:打手+管事 > 订单金额时,管事分成为 0。
|
||||
"""
|
||||
jine = Decimal(str(jine))
|
||||
try:
|
||||
lilu_obj = Lilubiao.objects.get(fadanpingtai='3')
|
||||
rate_dashou = Decimal(str(lilu_obj.lilu)) if lilu_obj.lilu and lilu_obj.lilu > 0 else Decimal('1')
|
||||
except Lilubiao.DoesNotExist:
|
||||
rate_dashou = Decimal('1')
|
||||
|
||||
lilu_guanshi_obj = Lilubiao.objects.filter(fadanpingtai='13').first()
|
||||
rate_guanshi = (
|
||||
Decimal(str(lilu_guanshi_obj.lilu))
|
||||
if lilu_guanshi_obj and lilu_guanshi_obj.lilu is not None
|
||||
else Decimal('0')
|
||||
)
|
||||
|
||||
dashou_fencheng = (jine * rate_dashou).quantize(Decimal('0.01'))
|
||||
guanshi_raw = (jine * rate_guanshi).quantize(Decimal('0.01'))
|
||||
|
||||
if dashou_fencheng + guanshi_raw > jine:
|
||||
guanshi_fencheng = Decimal('0.00')
|
||||
else:
|
||||
guanshi_fencheng = guanshi_raw
|
||||
|
||||
return dashou_fencheng, guanshi_fencheng
|
||||
|
||||
|
||||
def settle_shangjia_order_guanshi_fenhong(order, dashou_id):
|
||||
"""
|
||||
商家订单结单时结算管事分红(幂等,失败不抛异常阻断主流程)。
|
||||
"""
|
||||
from shangpin.models import Gsfenhong
|
||||
from yonghu.models import UserDashou, UserGuanshi
|
||||
|
||||
if getattr(order, 'fadan_pingtai', None) != 2:
|
||||
return
|
||||
|
||||
guanshi_fencheng = order.guanshi_fencheng or Decimal('0')
|
||||
if guanshi_fencheng <= 0:
|
||||
return
|
||||
|
||||
if Gsfenhong.objects.filter(dingdan_id=order.dingdan_id).exists():
|
||||
logger.info(f"商家订单管事分红已处理: {order.dingdan_id}")
|
||||
return
|
||||
|
||||
if not dashou_id:
|
||||
return
|
||||
|
||||
try:
|
||||
dashou = UserDashou.objects.select_related('user').get(user__yonghuid=dashou_id)
|
||||
except UserDashou.DoesNotExist:
|
||||
logger.info(f"商家订单管事分红跳过: 打手{dashou_id}不存在")
|
||||
return
|
||||
|
||||
guanshi_id = dashou.yaoqingren
|
||||
if not guanshi_id:
|
||||
logger.info(f"商家订单管事分红跳过: 打手{dashou_id}无邀请管事")
|
||||
return
|
||||
|
||||
try:
|
||||
with transaction.atomic():
|
||||
guanshi = UserGuanshi.objects.select_for_update().get(user__yonghuid=guanshi_id)
|
||||
UserGuanshi.objects.filter(id=guanshi.id).update(
|
||||
yue=F('yue') + guanshi_fencheng,
|
||||
chongzhifenrun=F('chongzhifenrun') + guanshi_fencheng,
|
||||
)
|
||||
|
||||
user_main = dashou.user
|
||||
Gsfenhong.objects.create(
|
||||
dingdan_id=order.dingdan_id,
|
||||
guanshi=guanshi_id,
|
||||
dashouid=dashou_id,
|
||||
shuoming='商家订单分红',
|
||||
fenhong=guanshi_fencheng,
|
||||
avatar=user_main.avatar if user_main else None,
|
||||
nicheng=dashou.nicheng or '未知打手',
|
||||
fenhong_leixing=3,
|
||||
)
|
||||
logger.info(
|
||||
f"商家订单管事分红成功: 订单{order.dingdan_id}, 管事{guanshi_id}, "
|
||||
f"打手{dashou_id}, 金额{guanshi_fencheng}元"
|
||||
)
|
||||
except UserGuanshi.DoesNotExist:
|
||||
logger.warning(f"商家订单管事分红跳过: 管事{guanshi_id}不存在")
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error(f"商家订单管事分红失败: {e}", exc_info=True)
|
||||
return
|
||||
|
||||
try:
|
||||
from houtai.utils import update_guanshi_daily_by_action
|
||||
update_guanshi_daily_by_action(
|
||||
yonghuid=guanshi_id,
|
||||
action=3,
|
||||
amount=guanshi_fencheng,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"商家订单管事日统计更新失败: {e}")
|
||||
11494
dingdan/views.py
Normal file
11494
dingdan/views.py
Normal file
File diff suppressed because it is too large
Load Diff
52
gunicorn.conf.py
Normal file
52
gunicorn.conf.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# /opt/1panel/apps/openresty/nginx/www/sites/43.142.166.152.443/django/gunicorn.conf.py
|
||||
|
||||
# 🔥 绑定的IP和端口(根据你的Nginx配置,后端在8001端口)
|
||||
bind = "127.0.0.1:8001"
|
||||
|
||||
# 🔥 工作进程数(根据CPU核心数调整,建议:CPU核心数*2+1)
|
||||
# 查看CPU核心数:cat /proc/cpuinfo | grep "processor" | wc -l
|
||||
workers = 4
|
||||
|
||||
# 🔥 工作模式(使用gevent支持异步,更高效)
|
||||
#worker_class = "gevent"
|
||||
worker_class = "sync" # 改成这个
|
||||
|
||||
# 🔥 每个worker的最大请求数(防止内存泄漏)
|
||||
max_requests = 1000
|
||||
max_requests_jitter = 50 # 随机抖动,避免所有worker同时重启
|
||||
|
||||
# 🔥 超时时间(秒)
|
||||
timeout = 30
|
||||
keepalive = 2
|
||||
|
||||
# 🔥 预加载应用(减少内存占用,加快请求处理)
|
||||
preload_app = True
|
||||
|
||||
# 🔥 日志配置
|
||||
# 访问日志:输出到标准输出,便于OnePanel查看
|
||||
accesslog = "-"
|
||||
# 错误日志:输出到标准错误
|
||||
errorlog = "-"
|
||||
# 日志级别
|
||||
loglevel = "info"
|
||||
|
||||
# 🔥 进程名称(便于识别)
|
||||
proc_name = "a_long_dianjing_gunicorn"
|
||||
|
||||
# 🔥 工作进程的用户/组(安全考虑,用非root用户运行)
|
||||
# user = "www-data"
|
||||
# group = "www-data"
|
||||
|
||||
# 🔥 工作进程临时目录(避免权限问题)
|
||||
worker_tmp_dir = "/dev/shm"
|
||||
|
||||
# 🔥 限制请求数据大小(防止大文件攻击)
|
||||
limit_request_line = 4094
|
||||
limit_request_fields = 100
|
||||
limit_request_field_size = 8190
|
||||
|
||||
# 🔥 优雅退出时间
|
||||
graceful_timeout = 30
|
||||
|
||||
# 🔥 进程守护(如果用systemd管理,这里设为False)
|
||||
daemon = False
|
||||
0
houtai/_init_.py
Normal file
0
houtai/_init_.py
Normal file
5
houtai/apps.py
Normal file
5
houtai/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class HoutaiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'houtai'
|
||||
296
houtai/migrations/0001_initial.py
Normal file
296
houtai/migrations/0001_initial.py
Normal file
@@ -0,0 +1,296 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-06 23:31
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="AbnormalUserLog",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"account_id",
|
||||
models.CharField(
|
||||
db_index=True, max_length=20, verbose_name="账号ID(phone)"
|
||||
),
|
||||
),
|
||||
(
|
||||
"ip_address",
|
||||
models.GenericIPAddressField(
|
||||
blank=True, null=True, verbose_name="IP地址"
|
||||
),
|
||||
),
|
||||
(
|
||||
"real_ip",
|
||||
models.GenericIPAddressField(
|
||||
blank=True, null=True, verbose_name="真实IP(穿透代理后)"
|
||||
),
|
||||
),
|
||||
(
|
||||
"x_forwarded_for",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
default="",
|
||||
max_length=255,
|
||||
verbose_name="X-Forwarded-For",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_agent",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
default="",
|
||||
max_length=500,
|
||||
verbose_name="User-Agent",
|
||||
),
|
||||
),
|
||||
(
|
||||
"request_method",
|
||||
models.CharField(
|
||||
blank=True, default="", max_length=10, verbose_name="请求方法"
|
||||
),
|
||||
),
|
||||
(
|
||||
"request_path",
|
||||
models.CharField(
|
||||
blank=True, default="", max_length=500, verbose_name="请求路径"
|
||||
),
|
||||
),
|
||||
(
|
||||
"request_params",
|
||||
models.TextField(
|
||||
blank=True, default="", verbose_name="请求参数(GET/POST)"
|
||||
),
|
||||
),
|
||||
(
|
||||
"request_body",
|
||||
models.TextField(
|
||||
blank=True, default="", verbose_name="请求体(原始)"
|
||||
),
|
||||
),
|
||||
(
|
||||
"request_headers",
|
||||
models.TextField(
|
||||
blank=True, default="", verbose_name="请求头(JSON格式)"
|
||||
),
|
||||
),
|
||||
(
|
||||
"attempt_type",
|
||||
models.CharField(
|
||||
blank=True, default="", max_length=50, verbose_name="攻击类型"
|
||||
),
|
||||
),
|
||||
(
|
||||
"detail",
|
||||
models.TextField(blank=True, default="", verbose_name="详细描述"),
|
||||
),
|
||||
(
|
||||
"create_time",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "异常用户日志",
|
||||
"verbose_name_plural": "异常用户日志",
|
||||
"db_table": "abnormal_user_log",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Permission",
|
||||
fields=[
|
||||
(
|
||||
"perm_id",
|
||||
models.AutoField(
|
||||
primary_key=True, serialize=False, verbose_name="权限ID"
|
||||
),
|
||||
),
|
||||
(
|
||||
"perm_code",
|
||||
models.CharField(
|
||||
db_index=True,
|
||||
max_length=100,
|
||||
unique=True,
|
||||
verbose_name="权限编码",
|
||||
),
|
||||
),
|
||||
(
|
||||
"perm_name",
|
||||
models.CharField(max_length=100, verbose_name="权限名称"),
|
||||
),
|
||||
(
|
||||
"description",
|
||||
models.CharField(
|
||||
blank=True, default="", max_length=200, verbose_name="权限描述"
|
||||
),
|
||||
),
|
||||
(
|
||||
"create_time",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
|
||||
),
|
||||
(
|
||||
"update_time",
|
||||
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "权限",
|
||||
"verbose_name_plural": "权限",
|
||||
"db_table": "permission",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Role",
|
||||
fields=[
|
||||
(
|
||||
"role_id",
|
||||
models.AutoField(
|
||||
primary_key=True, serialize=False, verbose_name="角色ID"
|
||||
),
|
||||
),
|
||||
(
|
||||
"role_code",
|
||||
models.CharField(
|
||||
db_index=True,
|
||||
max_length=50,
|
||||
unique=True,
|
||||
verbose_name="角色编码",
|
||||
),
|
||||
),
|
||||
("role_name", models.CharField(max_length=50, verbose_name="角色名称")),
|
||||
(
|
||||
"description",
|
||||
models.CharField(
|
||||
blank=True, default="", max_length=200, verbose_name="角色描述"
|
||||
),
|
||||
),
|
||||
(
|
||||
"create_time",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
|
||||
),
|
||||
(
|
||||
"update_time",
|
||||
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "角色",
|
||||
"verbose_name_plural": "角色",
|
||||
"db_table": "role",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="UserRole",
|
||||
fields=[
|
||||
("id", models.AutoField(primary_key=True, serialize=False)),
|
||||
(
|
||||
"account_id",
|
||||
models.CharField(
|
||||
db_index=True, max_length=11, verbose_name="账号ID"
|
||||
),
|
||||
),
|
||||
(
|
||||
"create_time",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
|
||||
),
|
||||
(
|
||||
"update_time",
|
||||
models.DateTimeField(auto_now=True, verbose_name="更新时间"),
|
||||
),
|
||||
(
|
||||
"role",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="houtai.role",
|
||||
verbose_name="角色",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "用户角色关联",
|
||||
"verbose_name_plural": "用户角色关联",
|
||||
"db_table": "user_role",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="RolePermission",
|
||||
fields=[
|
||||
("id", models.AutoField(primary_key=True, serialize=False)),
|
||||
(
|
||||
"create_time",
|
||||
models.DateTimeField(auto_now_add=True, verbose_name="创建时间"),
|
||||
),
|
||||
(
|
||||
"permission",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="houtai.permission",
|
||||
verbose_name="权限",
|
||||
),
|
||||
),
|
||||
(
|
||||
"role",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="houtai.role",
|
||||
verbose_name="角色",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "角色权限关联",
|
||||
"verbose_name_plural": "角色权限关联",
|
||||
"db_table": "role_permission",
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="role",
|
||||
index=models.Index(fields=["role_code"], name="role_role_co_b9f497_idx"),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="permission",
|
||||
index=models.Index(
|
||||
fields=["perm_code"], name="permission_perm_co_56cbe3_idx"
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="abnormaluserlog",
|
||||
index=models.Index(
|
||||
fields=["account_id"], name="abnormal_us_account_425899_idx"
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="abnormaluserlog",
|
||||
index=models.Index(
|
||||
fields=["ip_address"], name="abnormal_us_ip_addr_3d612b_idx"
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="abnormaluserlog",
|
||||
index=models.Index(
|
||||
fields=["create_time"], name="abnormal_us_create__ee16da_idx"
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="userrole",
|
||||
unique_together={("account_id", "role")},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="rolepermission",
|
||||
unique_together={("role", "permission")},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,339 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-09 14:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("houtai", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ZuzhangRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"yonghuid",
|
||||
models.CharField(
|
||||
db_index=True, max_length=7, verbose_name="组长用户ID"
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"yaoqing_guanshi_shu",
|
||||
models.IntegerField(default=0, verbose_name="邀请管事总数"),
|
||||
),
|
||||
(
|
||||
"shouru_zonge",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="收入总额(分佣)",
|
||||
),
|
||||
),
|
||||
(
|
||||
"fenyong_jine",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="分佣金额",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "组长每日统计",
|
||||
"db_table": "zuzhang_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "-yaoqing_guanshi_shu"],
|
||||
name="zuzhang_ri__riqi_87169e_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-shouru_zonge"],
|
||||
name="zuzhang_ri__riqi_1d7e51_idx",
|
||||
),
|
||||
],
|
||||
"unique_together": {("yonghuid", "riqi")},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ShangjiaRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"yonghuid",
|
||||
models.CharField(
|
||||
db_index=True, max_length=7, verbose_name="商家用户ID"
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"paifa_dingdan_shu",
|
||||
models.IntegerField(default=0, verbose_name="派发订单总数"),
|
||||
),
|
||||
(
|
||||
"paifa_jine",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="派发订单总金额",
|
||||
),
|
||||
),
|
||||
(
|
||||
"jiesuan_dingdan_shu",
|
||||
models.IntegerField(default=0, verbose_name="结算订单总数"),
|
||||
),
|
||||
(
|
||||
"jiesuan_jine",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="结算总金额",
|
||||
),
|
||||
),
|
||||
(
|
||||
"tuikuan_dingdan_shu",
|
||||
models.IntegerField(default=0, verbose_name="退款订单总数"),
|
||||
),
|
||||
(
|
||||
"tuikuan_jine",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="退款总金额",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "商家每日统计",
|
||||
"db_table": "shangjia_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "-paifa_jine"],
|
||||
name="shangjia_ri_riqi_d00f7f_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-jiesuan_jine"],
|
||||
name="shangjia_ri_riqi_25772a_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-paifa_dingdan_shu"],
|
||||
name="shangjia_ri_riqi_e05c54_idx",
|
||||
),
|
||||
],
|
||||
"unique_together": {("yonghuid", "riqi")},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="GuanshiXufeiRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"yonghuid",
|
||||
models.CharField(
|
||||
db_index=True, max_length=7, verbose_name="管事用户ID"
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"xufei_zongshu",
|
||||
models.IntegerField(default=0, verbose_name="今日续费总数"),
|
||||
),
|
||||
(
|
||||
"xufei_shouyi",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="续费收益金额",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "管事续费每日统计",
|
||||
"db_table": "guanshi_xufei_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "-xufei_zongshu"],
|
||||
name="guanshi_xuf_riqi_7c128f_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-xufei_shouyi"],
|
||||
name="guanshi_xuf_riqi_8f09b5_idx",
|
||||
),
|
||||
],
|
||||
"unique_together": {("yonghuid", "riqi")},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="GuanshiRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"yonghuid",
|
||||
models.CharField(
|
||||
db_index=True, max_length=7, verbose_name="管事用户ID"
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"yaoqing_dashou_shu",
|
||||
models.IntegerField(default=0, verbose_name="邀请打手总数"),
|
||||
),
|
||||
(
|
||||
"chongzhi_dashou_shu",
|
||||
models.IntegerField(default=0, verbose_name="充值打手数量"),
|
||||
),
|
||||
(
|
||||
"shouru_zonge",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="收入总额",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "管事每日统计",
|
||||
"db_table": "guanshi_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "-yaoqing_dashou_shu"],
|
||||
name="guanshi_ri__riqi_075e1a_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-chongzhi_dashou_shu"],
|
||||
name="guanshi_ri__riqi_78a79d_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-shouru_zonge"],
|
||||
name="guanshi_ri__riqi_334f52_idx",
|
||||
),
|
||||
],
|
||||
"unique_together": {("yonghuid", "riqi")},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="DashouRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"yonghuid",
|
||||
models.CharField(
|
||||
db_index=True, max_length=7, verbose_name="打手用户ID"
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"jiedan_zongliang",
|
||||
models.IntegerField(default=0, verbose_name="接单总量"),
|
||||
),
|
||||
(
|
||||
"chengjiao_zongliang",
|
||||
models.IntegerField(default=0, verbose_name="成交单总量"),
|
||||
),
|
||||
(
|
||||
"jiedan_zonge",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="接单总金额",
|
||||
),
|
||||
),
|
||||
(
|
||||
"chengjiao_zonge",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="成交总金额",
|
||||
),
|
||||
),
|
||||
(
|
||||
"tuikuan_liang",
|
||||
models.IntegerField(default=0, verbose_name="退款订单量"),
|
||||
),
|
||||
(
|
||||
"tuikuan_jine",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="退款总金额",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "打手每日统计",
|
||||
"db_table": "dashou_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "-jiedan_zongliang"],
|
||||
name="dashou_ri_t_riqi_95a785_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-chengjiao_zongliang"],
|
||||
name="dashou_ri_t_riqi_ad9c1a_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-jiedan_zonge"],
|
||||
name="dashou_ri_t_riqi_5da703_idx",
|
||||
),
|
||||
models.Index(
|
||||
fields=["riqi", "-chengjiao_zonge"],
|
||||
name="dashou_ri_t_riqi_0a3156_idx",
|
||||
),
|
||||
],
|
||||
"unique_together": {("yonghuid", "riqi")},
|
||||
},
|
||||
),
|
||||
]
|
||||
54
houtai/migrations/0003_tixianritongji.py
Normal file
54
houtai/migrations/0003_tixianritongji.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-10 18:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("houtai", "0002_zuzhangritongji_shangjiaritongji_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="TixianRiTongji",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("riqi", models.DateField(db_index=True, verbose_name="日期")),
|
||||
(
|
||||
"leixing",
|
||||
models.IntegerField(
|
||||
choices=[(1, "打手"), (2, "管事"), (3, "组长")],
|
||||
verbose_name="用户类型",
|
||||
),
|
||||
),
|
||||
(
|
||||
"total_amount",
|
||||
models.DecimalField(
|
||||
decimal_places=2,
|
||||
default=0.0,
|
||||
max_digits=12,
|
||||
verbose_name="当天提现总额(元)",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "每日提现统计",
|
||||
"db_table": "tixian_ri_tongji",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["riqi", "leixing"], name="tixian_ri_t_riqi_c18bb2_idx"
|
||||
)
|
||||
],
|
||||
"unique_together": {("riqi", "leixing")},
|
||||
},
|
||||
),
|
||||
]
|
||||
0
houtai/migrations/__init__.py
Normal file
0
houtai/migrations/__init__.py
Normal file
0
houtai/migrations/_init_.py
Normal file
0
houtai/migrations/_init_.py
Normal file
269
houtai/models.py
Normal file
269
houtai/models.py
Normal file
@@ -0,0 +1,269 @@
|
||||
from django.db import models
|
||||
|
||||
# ==================== 1. 角色表 ====================
|
||||
class Role(models.Model):
|
||||
"""角色表"""
|
||||
role_id = models.AutoField(primary_key=True, verbose_name='角色ID')
|
||||
role_code = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='角色编码')
|
||||
role_name = models.CharField(max_length=50, verbose_name='角色名称')
|
||||
description = models.CharField(max_length=200, blank=True, default='', verbose_name='角色描述')
|
||||
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'role'
|
||||
verbose_name = '角色'
|
||||
verbose_name_plural = '角色'
|
||||
indexes = [
|
||||
models.Index(fields=['role_code']),
|
||||
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.role_name
|
||||
|
||||
|
||||
# ==================== 2. 权限表 ====================
|
||||
class Permission(models.Model):
|
||||
"""权限表"""
|
||||
perm_id = models.AutoField(primary_key=True, verbose_name='权限ID')
|
||||
perm_code = models.CharField(max_length=100, unique=True, db_index=True, verbose_name='权限编码')
|
||||
perm_name = models.CharField(max_length=100, verbose_name='权限名称')
|
||||
description = models.CharField(max_length=200, blank=True, default='', verbose_name='权限描述')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'permission'
|
||||
verbose_name = '权限'
|
||||
verbose_name_plural = '权限'
|
||||
indexes = [
|
||||
models.Index(fields=['perm_code']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.perm_name} ({self.perm_code})"
|
||||
|
||||
|
||||
# ==================== 3. 角色-权限关联表 ====================
|
||||
class RolePermission(models.Model):
|
||||
"""角色权限关联表"""
|
||||
id = models.AutoField(primary_key=True)
|
||||
role = models.ForeignKey(Role, on_delete=models.CASCADE, db_index=True, verbose_name='角色')
|
||||
permission = models.ForeignKey(Permission, on_delete=models.CASCADE, db_index=True, verbose_name='权限')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'role_permission'
|
||||
verbose_name = '角色权限关联'
|
||||
verbose_name_plural = '角色权限关联'
|
||||
unique_together = [['role', 'permission']] # 防止重复授权
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.role.role_name} -> {self.permission.perm_name}"
|
||||
|
||||
|
||||
# ==================== 4. 用户-角色关联表 ====================
|
||||
class UserRole(models.Model):
|
||||
"""用户角色关联表"""
|
||||
id = models.AutoField(primary_key=True)
|
||||
account_id = models.CharField(max_length=11, db_index=True, verbose_name='账号ID') # 对应 UserMain 的 phone 字段
|
||||
role = models.ForeignKey(Role, on_delete=models.CASCADE, db_index=True, verbose_name='角色')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'user_role'
|
||||
verbose_name = '用户角色关联'
|
||||
verbose_name_plural = '用户角色关联'
|
||||
unique_together = [['account_id', 'role']] # 同一用户同一角色只能有一条
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.account_id} -> {self.role.role_name}"
|
||||
|
||||
|
||||
class AbnormalUserLog(models.Model):
|
||||
"""记录恶意尝试越权操作的用户信息"""
|
||||
# 账号信息
|
||||
account_id = models.CharField(max_length=20, db_index=True, verbose_name='账号ID(phone)')
|
||||
|
||||
# 网络信息
|
||||
ip_address = models.GenericIPAddressField(null=True, blank=True, verbose_name='IP地址')
|
||||
real_ip = models.GenericIPAddressField(null=True, blank=True, verbose_name='真实IP(穿透代理后)')
|
||||
x_forwarded_for = models.CharField(max_length=255, blank=True, default='', verbose_name='X-Forwarded-For')
|
||||
user_agent = models.CharField(max_length=500, blank=True, default='', verbose_name='User-Agent')
|
||||
|
||||
# 地理位置信息(需借助第三方API,这里只存储请求时的IP,后续可离线解析)
|
||||
# 但我们可以存储更多请求细节
|
||||
request_method = models.CharField(max_length=10, blank=True, default='', verbose_name='请求方法')
|
||||
request_path = models.CharField(max_length=500, blank=True, default='', verbose_name='请求路径')
|
||||
request_params = models.TextField(blank=True, default='', verbose_name='请求参数(GET/POST)')
|
||||
request_body = models.TextField(blank=True, default='', verbose_name='请求体(原始)')
|
||||
request_headers = models.TextField(blank=True, default='', verbose_name='请求头(JSON格式)')
|
||||
|
||||
# 攻击特征
|
||||
attempt_type = models.CharField(max_length=50, blank=True, default='', verbose_name='攻击类型')
|
||||
detail = models.TextField(blank=True, default='', verbose_name='详细描述')
|
||||
|
||||
# 时间戳
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'abnormal_user_log'
|
||||
verbose_name = '异常用户日志'
|
||||
verbose_name_plural = verbose_name
|
||||
indexes = [
|
||||
models.Index(fields=['account_id']),
|
||||
models.Index(fields=['ip_address']),
|
||||
models.Index(fields=['create_time']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.account_id} @ {self.ip_address} - {self.create_time}"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ShangjiaRiTongji(models.Model):
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='商家用户ID')
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期') # 年月日
|
||||
|
||||
# 派发统计
|
||||
paifa_dingdan_shu = models.IntegerField(default=0, verbose_name='派发订单总数')
|
||||
paifa_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='派发订单总金额')
|
||||
|
||||
# 结算统计(成交)
|
||||
jiesuan_dingdan_shu = models.IntegerField(default=0, verbose_name='结算订单总数')
|
||||
jiesuan_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='结算总金额')
|
||||
|
||||
# 退款统计(可选)
|
||||
tuikuan_dingdan_shu = models.IntegerField(default=0, verbose_name='退款订单总数')
|
||||
tuikuan_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='退款总金额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'shangjia_ri_tongji'
|
||||
verbose_name = '商家每日统计'
|
||||
unique_together = [['yonghuid', 'riqi']] # 每人每天一条记录
|
||||
indexes = [
|
||||
models.Index(fields=['riqi', '-paifa_jine']),
|
||||
models.Index(fields=['riqi', '-jiesuan_jine']),
|
||||
models.Index(fields=['riqi', '-paifa_dingdan_shu']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class DashouRiTongji(models.Model):
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='打手用户ID')
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期') # 年月日
|
||||
|
||||
# 接单统计
|
||||
jiedan_zongliang = models.IntegerField(default=0, verbose_name='接单总量')
|
||||
chengjiao_zongliang = models.IntegerField(default=0, verbose_name='成交单总量')
|
||||
|
||||
# 金额统计
|
||||
jiedan_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='接单总金额')
|
||||
chengjiao_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='成交总金额')
|
||||
|
||||
# 退款统计
|
||||
tuikuan_liang = models.IntegerField(default=0, verbose_name='退款订单量')
|
||||
tuikuan_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='退款总金额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'dashou_ri_tongji'
|
||||
verbose_name = '打手每日统计'
|
||||
unique_together = [['yonghuid', 'riqi']]
|
||||
indexes = [
|
||||
models.Index(fields=['riqi', '-jiedan_zongliang']),
|
||||
models.Index(fields=['riqi', '-chengjiao_zongliang']),
|
||||
models.Index(fields=['riqi', '-jiedan_zonge']),
|
||||
models.Index(fields=['riqi', '-chengjiao_zonge']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
class ZuzhangRiTongji(models.Model):
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='组长用户ID')
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期') # 年月日
|
||||
|
||||
yaoqing_guanshi_shu = models.IntegerField(default=0, verbose_name='邀请管事总数')
|
||||
shouru_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='收入总额(分佣)')
|
||||
|
||||
# 可选明细
|
||||
fenyong_jine = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='分佣金额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'zuzhang_ri_tongji'
|
||||
verbose_name = '组长每日统计'
|
||||
unique_together = [['yonghuid', 'riqi']]
|
||||
indexes = [
|
||||
models.Index(fields=['riqi', '-yaoqing_guanshi_shu']),
|
||||
models.Index(fields=['riqi', '-shouru_zonge']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
class GuanshiRiTongji(models.Model):
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='管事用户ID')
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期') # 年月日
|
||||
|
||||
yaoqing_dashou_shu = models.IntegerField(default=0, verbose_name='邀请打手总数')
|
||||
chongzhi_dashou_shu = models.IntegerField(default=0, verbose_name='充值打手数量')
|
||||
shouru_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='收入总额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'guanshi_ri_tongji'
|
||||
verbose_name = '管事每日统计'
|
||||
unique_together = [['yonghuid', 'riqi']]
|
||||
indexes = [
|
||||
models.Index(fields=['riqi', '-yaoqing_dashou_shu']),
|
||||
models.Index(fields=['riqi', '-chongzhi_dashou_shu']),
|
||||
models.Index(fields=['riqi', '-shouru_zonge']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
class GuanshiXufeiRiTongji(models.Model):
|
||||
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='管事用户ID')
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期') # 年月日
|
||||
|
||||
xufei_zongshu = models.IntegerField(default=0, verbose_name='今日续费总数') # 续费订单数(或续费打手数)
|
||||
xufei_shouyi = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='续费收益金额')
|
||||
|
||||
class Meta:
|
||||
db_table = 'guanshi_xufei_ri_tongji'
|
||||
verbose_name = '管事续费每日统计'
|
||||
unique_together = [['yonghuid', 'riqi']]
|
||||
indexes = [
|
||||
models.Index(fields=['riqi', '-xufei_zongshu']),
|
||||
models.Index(fields=['riqi', '-xufei_shouyi']),
|
||||
]
|
||||
|
||||
|
||||
|
||||
# models.py 中添加
|
||||
|
||||
class TixianRiTongji(models.Model):
|
||||
"""
|
||||
每日提现统计表(按身份统计当天提现总额)
|
||||
"""
|
||||
riqi = models.DateField(db_index=True, verbose_name='日期')
|
||||
leixing = models.IntegerField(
|
||||
choices=[(1, '打手'), (2, '管事'), (3, '组长')],
|
||||
verbose_name='用户类型'
|
||||
)
|
||||
total_amount = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='当天提现总额(元)')
|
||||
|
||||
class Meta:
|
||||
db_table = 'tixian_ri_tongji'
|
||||
verbose_name = '每日提现统计'
|
||||
unique_together = [['riqi', 'leixing']] # 每天每个身份一条记录
|
||||
indexes = [models.Index(fields=['riqi', 'leixing'])]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.riqi} - {self.get_leixing_display()} 提现总额: {self.total_amount}"
|
||||
39
houtai/serializers.py
Normal file
39
houtai/serializers.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# serializers.py
|
||||
|
||||
#小程序弹窗序列化器
|
||||
from rest_framework import serializers
|
||||
from peizhi.models import PopupPage, PopupConfig, PopupImage
|
||||
|
||||
class PopupImageSerializer(serializers.ModelSerializer):
|
||||
"""图片序列化器(输出用)"""
|
||||
class Meta:
|
||||
model = PopupImage
|
||||
fields = ['id', 'image_url', 'text', 'sort_order']
|
||||
|
||||
|
||||
class PopupConfigSerializer(serializers.ModelSerializer):
|
||||
"""弹窗配置序列化器(输出用)"""
|
||||
images = PopupImageSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PopupConfig
|
||||
fields = [
|
||||
'id', 'popup_id', 'title', 'description',
|
||||
'strategy_type', 'max_count', 'reset_interval',
|
||||
'duration', 'force_even_muted', 'sort_order',
|
||||
'is_active', 'start_time', 'end_time',
|
||||
'created_at', 'updated_at', 'images'
|
||||
]
|
||||
|
||||
|
||||
class PopupPageSerializer(serializers.ModelSerializer):
|
||||
"""页面序列化器(输出用)"""
|
||||
popups = PopupConfigSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PopupPage
|
||||
fields = [
|
||||
'id', 'page_key', 'name', 'description',
|
||||
'is_active', 'ignore_user_mute',
|
||||
'created_at', 'updated_at', 'popups'
|
||||
]
|
||||
137
houtai/urls.py
Normal file
137
houtai/urls.py
Normal file
@@ -0,0 +1,137 @@
|
||||
from django.urls import path
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from .view import GetClubConfigView, GetCrossOrderListView, PartnerGetOrderDataView,PartnerRefundNotifyView,\
|
||||
CrossPlatformRefundView, GetCrossOrderDetailView, CrossPlatformForceCompleteView, CrossPlatformTransferHallView,\
|
||||
PartnerTransferHallNotifyView, GetSettingsView, UpdateSettingsView, GetMappingsView, UpdateMappingsView,\
|
||||
PartnerUpdateSelfConfigView, GetPartnerClubsView, GetStatsView, PunishDashouView, PartnerPunishNotifyView,\
|
||||
GetClubConfigForPreSettlementView, PreSettlementActionView, GetPreSettlementDataView, GetRolePermissionView,\
|
||||
ModifyRolePermissionView, AddRoleView, GetAdminRolesView, GetAdminUserListView, AddAdminUserView,\
|
||||
ModifyAdminUserView, KefuGetDashouListView, KefuGetDashouDetailView, KefuUpdateDashouView, KefuGetShangjiaListView,\
|
||||
KefuGetShangjiaDetailView, KefuUpdateShangjiaView, GetProductBaseDataView, GetProductListView, SaveProductOrderView,\
|
||||
AddProductView, DeleteProductView, GetProductDetailView, UpdateProductView, UpdateMemberView, GetMemberListView,\
|
||||
AddMemberView, GetGuanliListView, GetGuanliDetailView, UpdateGuanliView, GetWithdrawSettingsView,\
|
||||
UpdateWithdrawSettingsView, GetZuzhangListView, GetZuzhangDetailView, UpdateZuzhangView, GetProductTypeZoneView,\
|
||||
ModifyProductTypeZoneView, GetRateView, ModifyRateView, PopupNoticeListAPIView, PopupNoticeModifyAPIView,ShopListView, ShopModifyView, ShopPublicTypeAndAuditView, ShopListForProductView, ShopProductModifyView,\
|
||||
ShopProductTypeMappingView, ProductListView, ProductModifyView, KefuChatPermissionsView,\
|
||||
PartnerFineNotifyView,FineApplyView, FaKuanTongJiView, FaKuanLieBiaoView, FaKuanChuLiView, FaKuanChuangJianView,\
|
||||
HqbkxxView,BkxgView,CaiwuView,CwhybkhqView, HybkjtsjView, SzxxView, CwddhqlxView, CwhqjtddsjView,\
|
||||
CwqtczhqView, KhpzhqView, ChzsgcView,KhgglView, ShgxgsjView, ZxkfghdsView, KptxwztjbView
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
path('kpthq', GetClubConfigView.as_view(), name='跨平台订单页面获取俱乐部数据'),
|
||||
path('hqddlb', GetCrossOrderListView.as_view(), name='获取跨平台订单'),
|
||||
path('kpthqtp',csrf_exempt(PartnerGetOrderDataView.as_view()), name='跨平台接受对方服务器获取订单数据'),
|
||||
# 对方平台退款通知接口
|
||||
path('dfddtk', csrf_exempt(PartnerRefundNotifyView.as_view()), name='跨平台接受对方服务器更新订单状态'),
|
||||
path('kptk', CrossPlatformRefundView.as_view(), name='跨平台订单退款'),
|
||||
path('hqkptddxq', GetCrossOrderDetailView.as_view(), name='获取跨平台订单数据详情'),
|
||||
path('kpqzjd', CrossPlatformForceCompleteView.as_view(), name='跨平台订单结算'),
|
||||
path('kpzydt', CrossPlatformTransferHallView.as_view(), name='跨平台转移大厅'),
|
||||
path('kpttzzydt', csrf_exempt(PartnerTransferHallNotifyView.as_view()), name='跨平台接受对方转移大厅通知'),
|
||||
path('hqkptsz', GetSettingsView.as_view(), name='获取俱乐部数据信息'),
|
||||
path('xgjlbsj', UpdateSettingsView.as_view(), name='更新俱乐部数据信息'),
|
||||
path('hqqtjlbsj', GetMappingsView.as_view(), name='获取俱乐部订单类型映射'),
|
||||
path('xgdfddys', UpdateMappingsView.as_view(), name='工薪俱乐部订单类型映射'),
|
||||
path('xgzjsjcs', csrf_exempt(PartnerUpdateSelfConfigView.as_view()), name='partner_update_self_config'),
|
||||
path('kpthqpt', GetPartnerClubsView.as_view(), name='前端获取俱乐部数据展示派发数据'),
|
||||
path('hqpfsj', GetStatsView.as_view(), name='前端派发统计表获取'),
|
||||
path('kpcf', PunishDashouView.as_view(), name='跨平台处罚打手'),
|
||||
path('kptcfds', PartnerPunishNotifyView.as_view(), name='接受跨平台对方平台处罚申请'),
|
||||
path('hqkttjsyjs', GetClubConfigForPreSettlementView.as_view(), name='预处理获取俱乐部数据'),
|
||||
path('hqyjssj', GetPreSettlementDataView.as_view(), name='获取预处理数据'),
|
||||
path('yjsjjhdk', PreSettlementActionView.as_view(), name='操作预处理'),
|
||||
|
||||
path('gljshq', GetRolePermissionView.as_view(), name='获取就角色信息'),
|
||||
path('sctjjsnr', ModifyRolePermissionView.as_view(), name='修改角色数据'),
|
||||
path('tjjs', AddRoleView.as_view(), name='添加角色'),
|
||||
|
||||
path('hqyhjsgl', GetAdminRolesView.as_view(), name='管理用户页面获取用户角色'),
|
||||
path('hqjsyhsj', GetAdminUserListView.as_view(), name='管理用户页面获取用户数据'),
|
||||
path('xghtyhsj', ModifyAdminUserView.as_view(), name='管理用户页面修改用户数据'),
|
||||
path('tjyhjs', AddAdminUserView.as_view(), name='管理用户页面添加用户'),
|
||||
path('kefuhqdslb', KefuGetDashouListView.as_view(), name='客服获取打手数据'),
|
||||
path('kefuhqdsxq', KefuGetDashouDetailView.as_view(), name='客服获取打手具体信息'),
|
||||
path('kefuxgds', KefuUpdateDashouView.as_view(), name='客服修改打手数据'),
|
||||
path('hqsjgllb', KefuGetShangjiaListView.as_view(), name='后台获取商家列表'),
|
||||
path('hthqsjxq', KefuGetShangjiaDetailView.as_view(), name='后台获取商家详情'),
|
||||
path('xgsjxx', KefuUpdateShangjiaView.as_view(), name='后台修改商家数据'),
|
||||
|
||||
path('htsphqjcsx', GetProductBaseDataView.as_view(), name='后台获取商品类型专区'),
|
||||
path('hthqsplb', GetProductListView.as_view(), name='后台获取商品数据'),
|
||||
path('htsppaixu', SaveProductOrderView.as_view(), name='后台修改商品类型排序'),
|
||||
path('htfbsp', AddProductView.as_view(), name='后台添加商品'),
|
||||
path('htscsp', DeleteProductView.as_view(), name='后台删除商品'),
|
||||
path('htsphqxq', GetProductDetailView.as_view(), name='后台获取商品详情'),
|
||||
path('bcspxg', UpdateProductView.as_view(), name='后台更新商品数据'),
|
||||
|
||||
path('hthqhylb', GetMemberListView.as_view(), name='后台获取会员列表'),
|
||||
path('htxghyxx', UpdateMemberView.as_view(), name='后台更新会员信息'),
|
||||
path('httjhy', AddMemberView.as_view(), name='后台添加会员'),
|
||||
path('hthqgslb', GetGuanliListView.as_view(), name='后台获取管事列表'),
|
||||
|
||||
path('hthqgsxq', GetGuanliDetailView.as_view(), name='后台获取管事详情信息'),
|
||||
path('htxggsxx', UpdateGuanliView.as_view(), name='后台更新管事数据'),
|
||||
path('hthqtxpz', GetWithdrawSettingsView.as_view(), name='后台获取提现配置'),
|
||||
path('htxgtxsz', UpdateWithdrawSettingsView.as_view(), name='后台修改提现配置'),
|
||||
path('hthqzzlb', GetZuzhangListView.as_view(), name='后台获取组长列表'),
|
||||
path('hthqzzxq', GetZuzhangDetailView.as_view(), name='后台获取组长详情页面'),
|
||||
path('htxgzzxx', UpdateZuzhangView.as_view(), name='后台修改组长信息'),
|
||||
|
||||
|
||||
path('hthqsplxzq', GetProductTypeZoneView.as_view(), name='后台获取商品类型专区'),
|
||||
path('htxgsplx', ModifyProductTypeZoneView.as_view(), name='后台修改添加商品类型专区'),
|
||||
path('hthqfhpz', GetRateView.as_view(), name='后台获取费率'),
|
||||
path('htxgfl', ModifyRateView.as_view(), name='后台修改费率'),
|
||||
path('hthqtcxx', PopupNoticeListAPIView.as_view(), name='后台获取弹窗配置'),
|
||||
path('htxgtcxx', PopupNoticeModifyAPIView.as_view(), name='后台更新创建弹窗配置'),
|
||||
|
||||
path('hqdplbsj', ShopListView.as_view(), name='获取店铺列表数据'),
|
||||
path('xgdpxx', ShopModifyView.as_view(), name='修改或添加店铺数据'),
|
||||
|
||||
|
||||
path('htdphqsplx', ShopPublicTypeAndAuditView.as_view(), name='后台获取店铺公共商品类型,默认审核配置'),
|
||||
path('htdphqdpys', ShopListForProductView.as_view(), name='后台获取商品店铺'),
|
||||
path('htdphqyssplx', ShopProductTypeMappingView.as_view(), name='后台获取商品店铺类型'),
|
||||
path('htxgdpsplx', ShopProductModifyView.as_view(), name='后台修改店铺商品类型和默认审核模式'),
|
||||
|
||||
path('hqhtdpsplx', ProductListView.as_view(), name='后台获取店铺商品数据'),
|
||||
path('htxgdpspsj', ProductModifyView.as_view(), name='后台修改店铺商品数据'),
|
||||
|
||||
path('kfhqltqx', KefuChatPermissionsView.as_view(), name='后台获取客服聊天配置'),
|
||||
|
||||
path('kffkdssq', FineApplyView.as_view(), name='跨平台处罚罚款申请接口'),
|
||||
path('kffkdssq_notify', PartnerFineNotifyView.as_view(), name='跨屏要对方平台处罚通知接口'),
|
||||
|
||||
|
||||
path('htglyhqcfsltj', FaKuanTongJiView.as_view(),name='后台罚单统计图片'), # 罚款统计
|
||||
path('hthqfklb', FaKuanLieBiaoView.as_view(),name='后台罚单列表'), # 罚款列表
|
||||
path('glyclfk', FaKuanChuLiView.as_view(),name='后台罚单操作'), # 处理罚款
|
||||
path('htfksc', FaKuanChuangJianView.as_view(),name='后台生成罚单'), # 创建罚款
|
||||
|
||||
path('hqbkxx', HqbkxxView.as_view(), name='获取板块数据'), # 获取板块数据
|
||||
path('bkxg', BkxgView.as_view(), name='板块增删改查'), # 板块增删改
|
||||
path('caiwu', CaiwuView.as_view(), name='财务页面基础数据'),
|
||||
|
||||
path('cwhybkhq', CwhybkhqView.as_view(), name='财务会员模块'),
|
||||
path('hybkjtsj', HybkjtsjView.as_view(), name='财务会员统计'),
|
||||
path('szxx', SzxxView.as_view(), name='详细收支记录'),
|
||||
|
||||
path('cwddhqlx', CwddhqlxView.as_view(), name='后台财务获取订单类型'),
|
||||
path('cwhqjtddsj', CwhqjtddsjView.as_view(), name='后台财务获取订单数据'),
|
||||
path('cwqtczhq', CwqtczhqView.as_view(), name='财务获取其他充值统计'),
|
||||
|
||||
path('khpzhq', KhpzhqView.as_view(), name='后台获取称号配置'),
|
||||
path('chzsgc', ChzsgcView.as_view(), name='后台对称号增删改查'),
|
||||
|
||||
path('khggl', KhgglView.as_view(), name='获取考核管信息'),
|
||||
path('shgxgsj', ShgxgsjView.as_view(), name='修改考核管数据'),
|
||||
|
||||
path('zxkfghds', ZxkfghdsView.as_view(), name='链接换打手或重新下单'),
|
||||
|
||||
path('kptxwztjb', csrf_exempt(KptxwztjbView.as_view()), name='跨平台脚本询问订单状态'),
|
||||
|
||||
|
||||
|
||||
]
|
||||
424
houtai/utils.py
Normal file
424
houtai/utils.py
Normal file
@@ -0,0 +1,424 @@
|
||||
# houtai/utils.py
|
||||
import logging
|
||||
import json
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from rest_framework.response import Response
|
||||
from .models import AbnormalUserLog, UserRole, RolePermission, Permission, DashouRiTongji, ShangjiaRiTongji, \
|
||||
GuanshiXufeiRiTongji, GuanshiRiTongji, ZuzhangRiTongji, TixianRiTongji
|
||||
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_real_ip(request):
|
||||
"""获取真实IP(考虑代理)"""
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
ip = x_forwarded_for.split(',')[0].strip()
|
||||
return ip, x_forwarded_for
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip, ''
|
||||
|
||||
|
||||
def _record_abnormal(account_id, request, attempt_type, detail=''):
|
||||
"""记录异常操作(仅用于越权等攻击行为)"""
|
||||
try:
|
||||
real_ip, xff = _get_real_ip(request)
|
||||
headers = {k: v for k, v in request.META.items() if k.startswith('HTTP_')}
|
||||
headers_json = json.dumps(headers, ensure_ascii=False)[:2000]
|
||||
|
||||
request_params = json.dumps(request.query_params.dict(), ensure_ascii=False) if request.query_params else ''
|
||||
request_body = request.body.decode('utf-8', errors='ignore')[:5000]
|
||||
|
||||
AbnormalUserLog.objects.create(
|
||||
account_id=account_id,
|
||||
ip_address=real_ip,
|
||||
real_ip=real_ip,
|
||||
x_forwarded_for=xff,
|
||||
user_agent=request.META.get('HTTP_USER_AGENT', ''),
|
||||
request_method=request.method,
|
||||
request_path=request.path,
|
||||
request_params=request_params,
|
||||
request_body=request_body,
|
||||
request_headers=headers_json,
|
||||
attempt_type=attempt_type,
|
||||
detail=detail,
|
||||
)
|
||||
logger.warning(f"记录异常用户: {account_id} IP:{real_ip} 类型:{attempt_type}")
|
||||
except Exception as e:
|
||||
logger.error(f"记录异常日志失败: {e}")
|
||||
|
||||
|
||||
def verify_kefu_permission(request, username_from_frontend=None):
|
||||
"""
|
||||
验证客服身份,并返回该用户的所有权限列表(基于新RBAC表)
|
||||
只有前端传递的username与当前登录用户不匹配时,才记录异常并警告前端。
|
||||
|
||||
参数:
|
||||
request: DRF request 对象
|
||||
username_from_frontend: 前端传递的账号(用于防越权)
|
||||
|
||||
返回:
|
||||
(kefu_obj, permissions_list) # 验证通过
|
||||
(None, Response) # 验证失败,直接返回Response
|
||||
"""
|
||||
current_user = request.user
|
||||
|
||||
# ---------- 1. 必须是客服 ----------
|
||||
if current_user.user_type != 'kefu':
|
||||
return None, Response({'code': 403, 'msg': '身份错误,非客服账号'}, status=403)
|
||||
|
||||
# ---------- 2. 客服扩展表存在且状态正常 ----------
|
||||
try:
|
||||
kefu = current_user.kefu_profile
|
||||
if kefu.zhuangtai != 1:
|
||||
return None, Response({'code': 403, 'msg': '客服账号已被禁用'}, status=403)
|
||||
except ObjectDoesNotExist:
|
||||
return None, Response({'code': 403, 'msg': '客服账号不存在'}, status=403)
|
||||
|
||||
# ---------- 3. 越权检测(只记录前端篡改username的情况) ----------
|
||||
if username_from_frontend and current_user.phone != username_from_frontend:
|
||||
detail = f'前端username: {username_from_frontend},实际登录账号: {current_user.phone}'
|
||||
_record_abnormal(current_user.phone, request, '越权尝试篡改username', detail=detail)
|
||||
return None, Response({
|
||||
'code': 403,
|
||||
'msg': '身份错误,无权操作其他账号,相关操作已记录'
|
||||
}, status=403)
|
||||
|
||||
# ---------- 4. 获取用户的所有权限(通过角色) ----------
|
||||
# 步骤1:获取用户的所有角色ID
|
||||
role_ids = UserRole.objects.filter(account_id=current_user.phone).values_list('role_id', flat=True)
|
||||
# 步骤2:通过角色ID获取权限编码
|
||||
permissions = list(Permission.objects.filter(
|
||||
rolepermission__role_id__in=role_ids
|
||||
).values_list('perm_code', flat=True).distinct())
|
||||
|
||||
if not permissions:
|
||||
logger.warning(f"用户 {current_user.phone} 没有任何权限,请检查角色分配")
|
||||
|
||||
return kefu, permissions
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def update_dashou_daily_by_action(yonghuid, amount, action):
|
||||
"""
|
||||
根据行为类型更新打手每日统计(接单、成交、退款)
|
||||
|
||||
参数:
|
||||
yonghuid: 打手用户ID
|
||||
amount: 本单涉及的金额(Decimal)
|
||||
action: 1 = 接单, 2 = 成交(结算), 3 = 退款
|
||||
"""
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount <= 0:
|
||||
return # 金额非正不统计
|
||||
|
||||
today = date.today()
|
||||
|
||||
with transaction.atomic():
|
||||
stat, created = DashouRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'jiedan_zongliang': 0,
|
||||
'jiedan_zonge': Decimal('0.00'),
|
||||
'chengjiao_zongliang': 0,
|
||||
'chengjiao_zonge': Decimal('0.00'),
|
||||
'tuikuan_liang': 0,
|
||||
'tuikuan_jine': Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
update_fields = {}
|
||||
if action == 1: # 接单
|
||||
update_fields['jiedan_zongliang'] = F('jiedan_zongliang') + 1
|
||||
update_fields['jiedan_zonge'] = F('jiedan_zonge') + amount
|
||||
elif action == 2: # 成交
|
||||
update_fields['chengjiao_zongliang'] = F('chengjiao_zongliang') + 1
|
||||
update_fields['chengjiao_zonge'] = F('chengjiao_zonge') + amount
|
||||
elif action == 3: # 退款
|
||||
update_fields['tuikuan_liang'] = F('tuikuan_liang') + 1
|
||||
update_fields['tuikuan_jine'] = F('tuikuan_jine') + amount
|
||||
else:
|
||||
return # 无效行为
|
||||
|
||||
if update_fields:
|
||||
DashouRiTongji.objects.filter(id=stat.id).update(**update_fields)
|
||||
|
||||
|
||||
def update_shangjia_daily(yonghuid, amount, action):
|
||||
"""
|
||||
根据操作行为更新商家每日统计(派发、结算、退款)
|
||||
|
||||
参数:
|
||||
yonghuid: 商家用户ID
|
||||
amount: 当前订单涉及的金额(Decimal)
|
||||
action: 操作类型,1 = 派发,2 = 结算(成交),3 = 退款
|
||||
|
||||
说明:
|
||||
- 金额必须大于0,否则不执行任何操作
|
||||
- 使用 select_for_update + F() 表达式保证原子性和并发安全
|
||||
- 每天每个商家只会有一条统计记录,不存在则自动创建
|
||||
"""
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount <= 0:
|
||||
return # 金额非正,不统计
|
||||
|
||||
today = date.today()
|
||||
|
||||
with transaction.atomic():
|
||||
# 获取或创建当天统计记录,同时加锁防止并发
|
||||
stat, created = ShangjiaRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'paifa_dingdan_shu': 0,
|
||||
'paifa_jine': Decimal('0.00'),
|
||||
'jiesuan_dingdan_shu': 0,
|
||||
'jiesuan_jine': Decimal('0.00'),
|
||||
'tuikuan_dingdan_shu': 0,
|
||||
'tuikuan_jine': Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
# 构建本次更新的字段,使用 F 表达式进行原子累加
|
||||
update_fields = {}
|
||||
if action == 1: # 派发
|
||||
update_fields['paifa_dingdan_shu'] = F('paifa_dingdan_shu') + 1
|
||||
update_fields['paifa_jine'] = F('paifa_jine') + amount
|
||||
elif action == 2: # 结算(成交)
|
||||
update_fields['jiesuan_dingdan_shu'] = F('jiesuan_dingdan_shu') + 1
|
||||
update_fields['jiesuan_jine'] = F('jiesuan_jine') + amount
|
||||
elif action == 3: # 退款
|
||||
update_fields['tuikuan_dingdan_shu'] = F('tuikuan_dingdan_shu') + 1
|
||||
update_fields['tuikuan_jine'] = F('tuikuan_jine') + amount
|
||||
else:
|
||||
# 无效的行为类型,直接返回
|
||||
return
|
||||
|
||||
# 执行原子更新
|
||||
ShangjiaRiTongji.objects.filter(id=stat.id).update(**update_fields)
|
||||
|
||||
|
||||
|
||||
def update_guanshi_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
|
||||
"""
|
||||
根据操作行为更新管事每日统计(邀请打手、充值会员、订单/押金分红)
|
||||
action: 1 = 邀请打手, 2 = 充值会员, 3 = 商家订单分红, 4 = 押金分红
|
||||
"""
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
from datetime import date
|
||||
|
||||
today = date.today()
|
||||
|
||||
with transaction.atomic():
|
||||
stat, created = GuanshiRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'yaoqing_dashou_shu': 0,
|
||||
'chongzhi_dashou_shu': 0,
|
||||
'shouru_zonge': Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
if action == 1:
|
||||
# 邀请打手:无论新创建还是已存在,直接 +1
|
||||
if created:
|
||||
stat.yaoqing_dashou_shu = 1
|
||||
stat.save()
|
||||
else:
|
||||
GuanshiRiTongji.objects.filter(id=stat.id).update(
|
||||
yaoqing_dashou_shu=F('yaoqing_dashou_shu') + 1
|
||||
)
|
||||
|
||||
elif action == 2:
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount <= 0:
|
||||
return
|
||||
if created:
|
||||
stat.chongzhi_dashou_shu = 1
|
||||
stat.shouru_zonge = amount
|
||||
stat.save()
|
||||
else:
|
||||
GuanshiRiTongji.objects.filter(id=stat.id).update(
|
||||
chongzhi_dashou_shu=F('chongzhi_dashou_shu') + 1,
|
||||
shouru_zonge=F('shouru_zonge') + amount
|
||||
)
|
||||
|
||||
elif action in (3, 4):
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount <= 0:
|
||||
return
|
||||
if created:
|
||||
stat.shouru_zonge = amount
|
||||
stat.save()
|
||||
else:
|
||||
GuanshiRiTongji.objects.filter(id=stat.id).update(
|
||||
shouru_zonge=F('shouru_zonge') + amount
|
||||
)
|
||||
|
||||
'''def update_guanshi_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
|
||||
"""
|
||||
根据操作行为更新管事每日统计(邀请打手、充值会员)
|
||||
|
||||
参数:
|
||||
yonghuid: 管事用户ID
|
||||
action: 操作类型
|
||||
1 = 邀请打手(次数自动+1,无需传其他参数)
|
||||
2 = 充值会员(需要传 amount 参数)
|
||||
amount: 充值金额(Decimal),仅在 action=2 时有效
|
||||
|
||||
说明:
|
||||
- 每次调用只代表一次操作,不会批量处理。
|
||||
- 使用 select_for_update + F() 保证原子性和并发安全。
|
||||
"""
|
||||
today = date.today()
|
||||
|
||||
# 邀请打手:次数+1,金额不变
|
||||
if action == 1:
|
||||
count_field = 'yaoqing_dashou_shu'
|
||||
amount = Decimal('0.00')
|
||||
|
||||
# 充值会员:次数+1,金额累加
|
||||
elif action == 2:
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount <= 0:
|
||||
return
|
||||
count_field = 'chongzhi_dashou_shu'
|
||||
|
||||
else:
|
||||
return # 无效行为,直接返回
|
||||
|
||||
with transaction.atomic():
|
||||
stat, created = GuanshiRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'yaoqing_dashou_shu': 0,
|
||||
'chongzhi_dashou_shu': 0,
|
||||
'shouru_zonge': Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
if created:
|
||||
# 新记录直接赋初值
|
||||
if action == 1:
|
||||
stat.yaoqing_dashou_shu = 1
|
||||
else:
|
||||
# 已存在记录,使用 F 表达式原子累加次数
|
||||
update_fields = {count_field: F(count_field) + 1}
|
||||
|
||||
if action == 2:
|
||||
update_fields['shouru_zonge'] = F('shouru_zonge') + amount
|
||||
|
||||
GuanshiRiTongji.objects.filter(id=stat.id).update(**update_fields)'''
|
||||
|
||||
def update_guanshi_xufei_daily(yonghuid, xufei_jine=Decimal('0.00')):
|
||||
"""
|
||||
管事续费统计(单独表)
|
||||
用法: update_guanshi_xufei_daily('300001', Decimal('30'))
|
||||
"""
|
||||
today = date.today()
|
||||
with transaction.atomic():
|
||||
stat, created = GuanshiXufeiRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'xufei_zongshu': 1,
|
||||
'xufei_shouyi': xufei_jine,
|
||||
}
|
||||
)
|
||||
if not created:
|
||||
GuanshiXufeiRiTongji.objects.filter(id=stat.id).update(
|
||||
xufei_zongshu=F('xufei_zongshu') + 1,
|
||||
xufei_shouyi=F('xufei_shouyi') + xufei_jine
|
||||
)
|
||||
|
||||
|
||||
def update_zuzhang_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
|
||||
"""
|
||||
根据操作行为更新组长每日统计(邀请管事、分佣收入)
|
||||
|
||||
参数:
|
||||
yonghuid: 组长用户ID
|
||||
action: 操作类型
|
||||
1 = 邀请管事(次数+1,无金额)
|
||||
2 = 分佣收入(累加金额)
|
||||
amount: 分佣金额(Decimal),仅在 action=2 时有效
|
||||
"""
|
||||
today = date.today()
|
||||
|
||||
with transaction.atomic():
|
||||
stat, created = ZuzhangRiTongji.objects.select_for_update().get_or_create(
|
||||
yonghuid=yonghuid,
|
||||
riqi=today,
|
||||
defaults={
|
||||
'yonghuid': yonghuid,
|
||||
'riqi': today,
|
||||
'yaoqing_guanshi_shu': 0,
|
||||
'shouru_zonge': Decimal('0.00'),
|
||||
'fenyong_jine': Decimal('0.00'),
|
||||
}
|
||||
)
|
||||
|
||||
if created:
|
||||
# 新记录直接设置初始值
|
||||
if action == 1:
|
||||
stat.yaoqing_guanshi_shu = 1
|
||||
elif action == 2:
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
stat.shouru_zonge = amount
|
||||
stat.fenyong_jine = amount
|
||||
stat.save()
|
||||
else:
|
||||
# 已存在记录,使用 F 表达式原子累加
|
||||
if action == 1:
|
||||
ZuzhangRiTongji.objects.filter(id=stat.id).update(
|
||||
yaoqing_guanshi_shu=F('yaoqing_guanshi_shu') + 1
|
||||
)
|
||||
elif action == 2:
|
||||
amount = Decimal(str(amount)) if amount else Decimal('0.00')
|
||||
if amount > 0:
|
||||
ZuzhangRiTongji.objects.filter(id=stat.id).update(
|
||||
shouru_zonge=F('shouru_zonge') + amount,
|
||||
fenyong_jine=F('fenyong_jine') + amount
|
||||
)
|
||||
|
||||
|
||||
|
||||
def update_tixian_daily_stat(leixing, amount):
|
||||
"""
|
||||
更新每日提现统计(平台维度)
|
||||
leixing: 1=打手, 2=管事, 3=组长
|
||||
amount: 本次提现金额(申请金额,含手续费)
|
||||
"""
|
||||
today = date.today()
|
||||
with transaction.atomic():
|
||||
stat, created = TixianRiTongji.objects.select_for_update().get_or_create(
|
||||
riqi=today,
|
||||
leixing=leixing,
|
||||
defaults={'total_amount': amount}
|
||||
)
|
||||
if not created:
|
||||
TixianRiTongji.objects.filter(id=stat.id).update(
|
||||
total_amount=F('total_amount') + amount
|
||||
)
|
||||
12489
houtai/view.py
Normal file
12489
houtai/view.py
Normal file
File diff suppressed because it is too large
Load Diff
22
manage.py
Normal file
22
manage.py
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'a_long_dianjing.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
0
peizhi/__init__.py
Normal file
0
peizhi/__init__.py
Normal file
3
peizhi/admin.py
Normal file
3
peizhi/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
peizhi/apps.py
Normal file
5
peizhi/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PeizhiConfig(AppConfig):
|
||||
name = "peizhi"
|
||||
92
peizhi/migrations/0001_initial.py
Normal file
92
peizhi/migrations/0001_initial.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# Generated by Django 5.2.10 on 2026-01-14 19:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Gonggao',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('leixing', models.IntegerField(blank=True, null=True, verbose_name='公告类型')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更改时间')),
|
||||
('jieshao', models.TextField(blank=True, null=True, verbose_name='公告介绍')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '公告',
|
||||
'verbose_name_plural': '公告',
|
||||
'db_table': 'gonggao',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Lunbo',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('tupian_url', models.CharField(blank=True, max_length=500, null=True, verbose_name='轮播图片URL')),
|
||||
('leixing', models.IntegerField(blank=True, null=True, verbose_name='图片类型:1:轮播2:个人中心背景')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更改时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '轮播',
|
||||
'verbose_name_plural': '轮播',
|
||||
'db_table': 'lunbo',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Qunpeizhi',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('peizhiid', models.IntegerField(blank=True, null=True, verbose_name='群类型:1.打手 2.管事')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更改时间')),
|
||||
('neirong', models.CharField(blank=True, max_length=200, null=True, verbose_name='群介绍')),
|
||||
('qunid', models.CharField(blank=True, max_length=100, null=True, verbose_name='群id')),
|
||||
('jieshao', models.CharField(blank=True, max_length=200, null=True, verbose_name='介绍')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'qunpeizhi',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Szjilu',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='记录ID')),
|
||||
('zongsy', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='总收益')),
|
||||
('zongls', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='总流水')),
|
||||
('zongzc', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='总支出')),
|
||||
('jrzc', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日支出')),
|
||||
('jrls', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='今日流水')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更改时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '收支记录',
|
||||
'verbose_name_plural': '收支记录',
|
||||
'db_table': 'szjilu',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Tupianpeizhi',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('tupian', models.CharField(blank=True, max_length=500, null=True, verbose_name='配置图片URL')),
|
||||
('leixing', models.PositiveIntegerField(blank=True, null=True, verbose_name='提现类型1:打手规则图片2:默认头像图片')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更改时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '配置图片',
|
||||
'verbose_name_plural': '配置图片',
|
||||
'db_table': 'tupianpeizhi',
|
||||
},
|
||||
),
|
||||
]
|
||||
56
peizhi/migrations/0002_shangjiamoban_shangjialianjie.py
Normal file
56
peizhi/migrations/0002_shangjiamoban_shangjialianjie.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# Generated by Django 4.2.27 on 2026-01-27 12:18
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('peizhi', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ShangjiaMoban',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='模板ID')),
|
||||
('yonghu_id', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')),
|
||||
('leixing_id', models.PositiveIntegerField(db_index=True, verbose_name='商品类型ID')),
|
||||
('moban_jieshao', models.CharField(max_length=500, verbose_name='模板介绍')),
|
||||
('jiage', models.DecimalField(decimal_places=2, help_text='单位:元', max_digits=10, verbose_name='模板价格')),
|
||||
('fabu_shuliang', models.PositiveIntegerField(default=0, verbose_name='发布数量')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '商家订单模板',
|
||||
'verbose_name_plural': '商家订单模板',
|
||||
'db_table': 'shangjia_moban',
|
||||
'indexes': [models.Index(fields=['yonghu_id', 'leixing_id'], name='shangjia_mo_yonghu__bb287d_idx'), models.Index(fields=['yonghu_id', 'leixing_id', 'id'], name='shangjia_mo_yonghu__4240d0_idx')],
|
||||
'unique_together': {('yonghu_id', 'leixing_id', 'moban_jieshao')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ShangjiaLianjie',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='链接ID')),
|
||||
('yonghu_id', models.CharField(db_index=True, max_length=7, verbose_name='用户ID')),
|
||||
('leixing_id', models.PositiveIntegerField(db_index=True, verbose_name='商品类型ID')),
|
||||
('moban_id', models.PositiveIntegerField(db_index=True, verbose_name='模板ID')),
|
||||
('lianjie', models.CharField(max_length=500, unique=True, verbose_name='链接地址')),
|
||||
('dingdan_id', models.CharField(blank=True, db_index=True, max_length=32, null=True, verbose_name='订单ID')),
|
||||
('is_used', models.BooleanField(db_index=True, default=False, verbose_name='是否已使用')),
|
||||
('shiyongzhe_ip', models.CharField(blank=True, max_length=50, null=True, verbose_name='使用者IP')),
|
||||
('youxiao_shijian', models.DateTimeField(blank=True, null=True, verbose_name='有效截止时间')),
|
||||
('lianjie_token', models.CharField(blank=True, db_index=True, max_length=32, null=True, unique=True, verbose_name='链接Token')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '商家链接',
|
||||
'verbose_name_plural': '商家链接',
|
||||
'db_table': 'shangjia_lianjie',
|
||||
'indexes': [models.Index(fields=['yonghu_id', 'moban_id', 'leixing_id'], name='shangjia_li_yonghu__9f2b25_idx'), models.Index(fields=['lianjie_token'], name='shangjia_li_lianjie_99014c_idx'), models.Index(fields=['dingdan_id'], name='shangjia_li_dingdan_95b682_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
18
peizhi/migrations/0003_tupianpeizhi_fangwen_cishu.py
Normal file
18
peizhi/migrations/0003_tupianpeizhi_fangwen_cishu.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-03-16 17:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('peizhi', '0002_shangjiamoban_shangjialianjie'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='tupianpeizhi',
|
||||
name='fangwen_cishu',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='访问次数'),
|
||||
),
|
||||
]
|
||||
28
peizhi/migrations/0004_tixianquotadefault.py
Normal file
28
peizhi/migrations/0004_tixianquotadefault.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.2.27 on 2026-03-20 15:50
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('peizhi', '0003_tupianpeizhi_fangwen_cishu'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TixianQuotaDefault',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('leixing', models.IntegerField(choices=[(1, '打手'), (2, '管事')], unique=True, verbose_name='用户类型')),
|
||||
('default_quota', models.DecimalField(decimal_places=2, default=20.0, max_digits=10, verbose_name='默认每日提现限额(元)')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '默认提现限额',
|
||||
'verbose_name_plural': '默认提现限额',
|
||||
'db_table': 'tixian_quota_default',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,76 @@
|
||||
# Generated by Django 4.2.27 on 2026-03-24 17:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('peizhi', '0004_tixianquotadefault'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='OrderTypeMapping',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('partner_club_id', models.CharField(db_index=True, max_length=6, verbose_name='对方俱乐部ID')),
|
||||
('partner_type_id', models.PositiveIntegerField(verbose_name='对方订单类型ID')),
|
||||
('partner_type_name', models.CharField(max_length=100, verbose_name='对方订单类型名称')),
|
||||
('our_type_id', models.PositiveIntegerField(verbose_name='我方订单类型ID')),
|
||||
('our_type_name', models.CharField(max_length=100, verbose_name='我方订单类型名称')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '订单类型映射',
|
||||
'verbose_name_plural': '订单类型映射',
|
||||
'db_table': 'order_type_mapping',
|
||||
'unique_together': {('partner_club_id', 'partner_type_id')},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DailyDispatchStat',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField(db_index=True, verbose_name='统计日期')),
|
||||
('year', models.PositiveSmallIntegerField(verbose_name='年份')),
|
||||
('month', models.PositiveSmallIntegerField(verbose_name='月份')),
|
||||
('day', models.PositiveSmallIntegerField(verbose_name='日期')),
|
||||
('direction', models.PositiveSmallIntegerField(choices=[(1, '我方派单'), (2, '对方派单')], verbose_name='派单方向')),
|
||||
('partner_club_id', models.CharField(db_index=True, max_length=6, verbose_name='对方俱乐部ID')),
|
||||
('dispatch_count', models.PositiveIntegerField(default=0, verbose_name='派单数')),
|
||||
('dispatch_amount', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='派单总额')),
|
||||
('success_count', models.PositiveIntegerField(default=0, verbose_name='成交订单数')),
|
||||
('success_amount', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='成交总额')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '每日派单统计',
|
||||
'verbose_name_plural': '每日派单统计',
|
||||
'db_table': 'daily_dispatch_stat',
|
||||
'indexes': [models.Index(fields=['year', 'month', 'day', 'direction', 'partner_club_id'], name='daily_dispa_year_f98f91_idx'), models.Index(fields=['date'], name='daily_dispa_date_9ac7fe_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Club',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('club_id', models.CharField(db_index=True, max_length=6, verbose_name='本俱乐部ID')),
|
||||
('partner_club_id', models.CharField(max_length=6, unique=True, verbose_name='对方俱乐部ID')),
|
||||
('is_interop_enabled', models.BooleanField(default=False, verbose_name='是否开启平台互通')),
|
||||
('is_receive_enabled', models.BooleanField(default=False, verbose_name='是否同意接收对方订单')),
|
||||
('dashou_rate', models.DecimalField(decimal_places=4, default=1.0, max_digits=5, verbose_name='打手分红费率')),
|
||||
('partner_domain', models.URLField(blank=True, verbose_name='对方服务器域名')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '俱乐部',
|
||||
'verbose_name_plural': '俱乐部',
|
||||
'db_table': 'club',
|
||||
'indexes': [models.Index(fields=['club_id'], name='club_club_id_e75636_idx'), models.Index(fields=['partner_club_id'], name='club_partner_b76039_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
52
peizhi/migrations/0006_clubconfig_accountpermission.py
Normal file
52
peizhi/migrations/0006_clubconfig_accountpermission.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-01 09:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('peizhi', '0005_ordertypemapping_dailydispatchstat_club'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ClubConfig',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('club_id', models.CharField(db_index=True, max_length=6, verbose_name='俱乐部ID')),
|
||||
('club_nickname', models.CharField(blank=True, default='', max_length=50, verbose_name='俱乐部昵称')),
|
||||
('club_avatar', models.CharField(blank=True, default='', max_length=500, verbose_name='俱乐部头像地址')),
|
||||
('backend_domain', models.CharField(blank=True, default='', max_length=200, verbose_name='后台域名')),
|
||||
('chat_api_key', models.CharField(blank=True, default='', max_length=200, verbose_name='聊天API密钥')),
|
||||
('chat_api_id', models.CharField(blank=True, default='', max_length=200, verbose_name='聊天API ID')),
|
||||
('customer_service_api_url', models.CharField(blank=True, default='', max_length=200, verbose_name='客服API URL')),
|
||||
('customer_service_corp_id', models.CharField(blank=True, default='', max_length=100, verbose_name='客服企业ID')),
|
||||
('storage_bucket_domain', models.CharField(blank=True, default='', max_length=500, verbose_name='存储桶域名')),
|
||||
('is_self', models.SmallIntegerField(choices=[(0, '对方'), (1, '我方')], db_index=True, default=0, verbose_name='是否我方俱乐部')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '俱乐部配置',
|
||||
'verbose_name_plural': '俱乐部配置',
|
||||
'db_table': 'club_config',
|
||||
'unique_together': {('club_id',)},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AccountPermission',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('account_id', models.CharField(db_index=True, max_length=11, verbose_name='账号ID')),
|
||||
('permission_code', models.IntegerField(verbose_name='权限代码')),
|
||||
('is_enabled', models.BooleanField(default=False, verbose_name='是否开启')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '账号权限',
|
||||
'verbose_name_plural': '账号权限',
|
||||
'db_table': 'account_permission',
|
||||
'unique_together': {('account_id', 'permission_code')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-02 13:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('peizhi', '0006_clubconfig_accountpermission'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='club',
|
||||
name='receive_rate',
|
||||
field=models.DecimalField(decimal_places=4, default=1.0, max_digits=5, verbose_name='接收打手分红费率'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dailydispatchstat',
|
||||
name='claimed_amount',
|
||||
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='抢单总额'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dailydispatchstat',
|
||||
name='claimed_count',
|
||||
field=models.PositiveIntegerField(default=0, verbose_name='抢单数'),
|
||||
),
|
||||
]
|
||||
18
peizhi/migrations/0008_clubconfig_storage_bucket_name.py
Normal file
18
peizhi/migrations/0008_clubconfig_storage_bucket_name.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.27 on 2026-04-04 18:51
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('peizhi', '0007_club_receive_rate_dailydispatchstat_claimed_amount_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='clubconfig',
|
||||
name='storage_bucket_name',
|
||||
field=models.CharField(blank=True, default='', max_length=100, verbose_name='存储桶名称'),
|
||||
),
|
||||
]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user