将文件夹命名从拼音改为英文,整理了一些代码,将导入语句迁移到了头部

This commit is contained in:
2026-06-14 16:39:09 +08:00
parent 1fd0301e0e
commit 21bb129c5d
178 changed files with 29249 additions and 35862 deletions

View File

@@ -1,12 +1,6 @@
""" """
阿龙电竞 - Celery定时任务主配置 阿龙电竞 - Celery定时任务主配置
生产环境就绪版本,支持精确延时任务和周期性任务
""" """
import os import os
from celery import Celery from celery import Celery
from celery.schedules import crontab from celery.schedules import crontab
@@ -26,14 +20,9 @@ app.config_from_object('django.conf:settings', namespace='CELERY')
#app.autodiscover_tasks() #app.autodiscover_tasks()
app.autodiscover_tasks(['yonghu.tasks', 'yonghu.ranking_tasks', 'dingdan.tasks', 'peizhi.tasks']) app.autodiscover_tasks(['yonghu.tasks', 'yonghu.ranking_tasks', 'dingdan.tasks', 'peizhi.tasks'])
# 配置周期性任务Celery Beat Schedule # 配置周期性任务Celery Beat Schedule
app.conf.beat_schedule = { app.conf.beat_schedule = {
# 月榜数据转移每月最后一天23:50执行 # 月榜数据转移每月最后一天23:50执行
'yuebang_zhuanyi': { 'yuebang_zhuanyi': {
'task': 'yonghu.ranking_tasks.zhuanyi_yuebang', 'task': 'yonghu.ranking_tasks.zhuanyi_yuebang',
@@ -41,8 +30,6 @@ app.conf.beat_schedule = {
'options': {'queue': 'periodic_tasks', 'priority': 7}, 'options': {'queue': 'periodic_tasks', 'priority': 7},
}, },
# 日榜数据转移每天23:55执行在清零前 # 日榜数据转移每天23:55执行在清零前
'ribang_zhuanyi': { 'ribang_zhuanyi': {
'task': 'yonghu.ranking_tasks.zhuanyi_ribang', 'task': 'yonghu.ranking_tasks.zhuanyi_ribang',
@@ -51,8 +38,6 @@ app.conf.beat_schedule = {
'options': {'queue': 'periodic_tasks', 'priority': 7}, # 优先级高于清零任务 'options': {'queue': 'periodic_tasks', 'priority': 7}, # 优先级高于清零任务
}, },
# 清理旧历史数据每月1号凌晨1点执行 # 清理旧历史数据每月1号凌晨1点执行
'qingli_jiulishuju': { 'qingli_jiulishuju': {
'task': 'yonghu.ranking_tasks.qingli_jiulishuju', 'task': 'yonghu.ranking_tasks.qingli_jiulishuju',
@@ -60,21 +45,13 @@ app.conf.beat_schedule = {
'options': {'queue': 'periodic_tasks', 'priority': 4}, 'options': {'queue': 'periodic_tasks', 'priority': 4},
}, },
# 补偿检查任务每5分钟运行
# 🔥 补偿检查任务每5分钟运行
'check_order_expire_task': { 'check_order_expire_task': {
'task': 'dingdan.tasks.check_order_expire_task', 'task': 'dingdan.tasks.check_order_expire_task',
'schedule': crontab(minute='*/5000'), 'schedule': crontab(minute='*/5000'),
'options': {'queue': 'order_tasks', 'priority': 3}, 'options': {'queue': 'order_tasks', 'priority': 3},
}, },
# 1. 每日凌晨0点执行 - 清零任务 # 1. 每日凌晨0点执行 - 清零任务
'daily_reset_task': { 'daily_reset_task': {
'task': 'yonghu.tasks.daily_reset_task', 'task': 'yonghu.tasks.daily_reset_task',
@@ -99,10 +76,6 @@ app.conf.beat_schedule = {
'kwargs': {} 'kwargs': {}
}, },
# 4. 每天凌晨0点5分清理收支记录 # 4. 每天凌晨0点5分清理收支记录
'daily_sz_reset_task': { 'daily_sz_reset_task': {
'task': 'peizhi.tasks.daily_sz_reset_task', 'task': 'peizhi.tasks.daily_sz_reset_task',
@@ -197,4 +170,4 @@ app.conf.task_acks_late = True
app.conf.worker_disable_rate_limits = True app.conf.worker_disable_rate_limits = True
# 结果过期时间 # 结果过期时间
app.conf.result_expires = 3600 # 任务结果保留1小时 app.conf.result_expires = 3600 # 任务结果保留1小时

View File

@@ -1,10 +1,10 @@
# celery_app.py # celery_app.py
import os import os
from celery import Celery from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'a_long_dianjing.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'a_long_dianjing.settings')
app = Celery('a_long_dianjing') app = Celery('a_long_dianjing')
app.config_from_object('django.conf:settings', namespace='CELERY') app.config_from_object('django.conf:settings', namespace='CELERY')
# 手动导入任务模块,避免与已有 tasks.py 冲突 # 手动导入任务模块,避免与已有 tasks.py 冲突
app.autodiscover_tasks(['dingdan.tongzhi_tasks']) app.autodiscover_tasks(['dingdan.notice_tasks'])

View File

@@ -1,329 +1,329 @@
""" """
Django settings for a_long_dianjing project. Django settings for a_long_dianjing project.
""" """
from pathlib import Path from pathlib import Path
from datetime import timedelta from datetime import timedelta
import os import os
# 从密钥配置文件导入敏感信息 # 从密钥配置文件导入敏感信息
try: try:
import app_secrets import app_secrets
except ImportError: except ImportError:
raise ImportError("缺少 appapp_secrets.py 密钥配置文件,请参照项目说明创建") raise ImportError("缺少 appapp_secrets.py 密钥配置文件,请参照项目说明创建")
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = app_secrets.SECRET_KEY SECRET_KEY = app_secrets.SECRET_KEY
DEBUG = False DEBUG = False
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'www.abas.asia'] ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'www.abas.asia']
# ==================== 应用配置 ==================== # ==================== 应用配置 ====================
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'rest_framework', 'rest_framework',
'rest_framework_simplejwt', 'rest_framework_simplejwt',
'corsheaders', 'corsheaders',
'django_celery_results', 'django_celery_results',
'celery', 'celery',
'yonghu', 'yonghu',
'dingdan', 'dingdan',
'shangpin', 'shangpin',
'peizhi', 'peizhi',
'houtai', 'houtai',
'utils', 'utils',
'shangdian', 'shangdian',
'dengji', 'dengji',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', 'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
ROOT_URLCONF = 'a_long_dianjing.urls' ROOT_URLCONF = 'a_long_dianjing.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'], 'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
], ],
}, },
}, },
] ]
WSGI_APPLICATION = 'a_long_dianjing.wsgi.application' WSGI_APPLICATION = 'a_long_dianjing.wsgi.application'
# ==================== 数据库 ==================== # ==================== 数据库 ====================
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': app_secrets.DATABASE_NAME, 'NAME': app_secrets.DATABASE_NAME,
'USER': app_secrets.DATABASE_USER, 'USER': app_secrets.DATABASE_USER,
'HOST': app_secrets.DATABASE_HOST, 'HOST': app_secrets.DATABASE_HOST,
'PASSWORD': app_secrets.DATABASE_PASSWORD, 'PASSWORD': app_secrets.DATABASE_PASSWORD,
'PORT': app_secrets.DATABASE_PORT, 'PORT': app_secrets.DATABASE_PORT,
'OPTIONS': { 'OPTIONS': {
'charset': 'utf8mb4', 'charset': 'utf8mb4',
'init_command': "SET time_zone='+08:00'", 'init_command': "SET time_zone='+08:00'",
} }
} }
} }
# ==================== 密码验证 ==================== # ==================== 密码验证 ====================
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}, {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'}, {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}, {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
] ]
# ==================== 国际化 ==================== # ==================== 国际化 ====================
LANGUAGE_CODE = 'zh-Hans' LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai' TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True USE_I18N = True
USE_TZ = False USE_TZ = False
STATIC_URL = 'static/' STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# ==================== JWT 配置 ==================== # ==================== JWT 配置 ====================
SIMPLE_JWT = { SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=36500), 'ACCESS_TOKEN_LIFETIME': timedelta(days=36500),
'REFRESH_TOKEN_LIFETIME': timedelta(days=36500), 'REFRESH_TOKEN_LIFETIME': timedelta(days=36500),
'ROTATE_REFRESH_TOKENS': False, 'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': False, 'BLACKLIST_AFTER_ROTATION': False,
'UPDATE_LAST_LOGIN': False, 'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'HS256', 'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY, 'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None, 'VERIFYING_KEY': None,
'AUDIENCE': None, 'AUDIENCE': None,
'ISSUER': None, 'ISSUER': None,
'JWK_URL': None, 'JWK_URL': None,
'LEEWAY': 0, 'LEEWAY': 0,
'AUTH_HEADER_TYPES': ('Bearer',), 'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'yonghuid', 'USER_ID_FIELD': 'yonghuid',
'USER_ID_CLAIM': 'yonghuid', 'USER_ID_CLAIM': 'yonghuid',
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule', 'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type', 'TOKEN_TYPE_CLAIM': 'token_type',
'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser', 'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',
'JTI_CLAIM': 'jti', 'JTI_CLAIM': 'jti',
} }
AUTHENTICATION_BACKENDS = [ AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend', 'django.contrib.auth.backends.ModelBackend',
] ]
AUTH_USER_MODEL = 'yonghu.UserMain' AUTH_USER_MODEL = 'yonghu.UserMain'
# ==================== DRF 配置 ==================== # ==================== DRF 配置 ====================
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ( 'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication', 'rest_framework_simplejwt.authentication.JWTAuthentication',
), ),
'DEFAULT_PERMISSION_CLASSES': ( 'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', 'rest_framework.permissions.IsAuthenticated',
), ),
'DEFAULT_THROTTLE_CLASSES': [ 'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle', 'rest_framework.throttling.UserRateThrottle',
], ],
'DEFAULT_THROTTLE_RATES': { 'DEFAULT_THROTTLE_RATES': {
'anon': '100/minute', 'anon': '100/minute',
'user': '1000/minute', 'user': '1000/minute',
}, },
'EXCEPTION_HANDLER': 'utils.exception_handler.custom_exception_handler', 'EXCEPTION_HANDLER': 'utils.exception_handler.custom_exception_handler',
'DEFAULT_RENDERER_CLASSES': [ 'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.JSONRenderer',
], ],
} }
# ==================== H5 域名 ==================== # ==================== H5 域名 ====================
H5_DOMAIN = 'https://www.abas.asia:666' H5_DOMAIN = 'https://www.abas.asia:666'
# ==================== CORS 配置 ==================== # ==================== CORS 配置 ====================
CORS_ALLOWED_ORIGINS = [ CORS_ALLOWED_ORIGINS = [
"http://localhost:8080", "http://localhost:8080",
"http://127.0.0.1:8080", "http://127.0.0.1:8080",
"https://www.abas.asia", "https://www.abas.asia",
"https://www.abas.asia:666", "https://www.abas.asia:666",
"https://www.abas.asia:1234", "https://www.abas.asia:1234",
"https://www.abas.asia:963", "https://www.abas.asia:963",
"http://localhost:5174", "http://localhost:5174",
"http://localhost:5173", "http://localhost:5173",
"http://localhost:3000", "http://localhost:3000",
"http://localhost:3001", "http://localhost:3001",
] ]
CORS_ALLOW_CREDENTIALS = True CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_METHODS = ("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") CORS_ALLOW_METHODS = ("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
CORS_ALLOW_HEADERS = [ CORS_ALLOW_HEADERS = [
"accept", "accept-encoding", "authorization", "content-type", "accept", "accept-encoding", "authorization", "content-type",
"dnt", "origin", "user-agent", "x-csrftoken", "x-requested-with", "dnt", "origin", "user-agent", "x-csrftoken", "x-requested-with",
] ]
# ==================== 腾讯云 COS ==================== # ==================== 腾讯云 COS ====================
COS_SECRET_ID = app_secrets.COS_SECRET_ID COS_SECRET_ID = app_secrets.COS_SECRET_ID
COS_SECRET_KEY = app_secrets.COS_SECRET_KEY COS_SECRET_KEY = app_secrets.COS_SECRET_KEY
COS_REGION = app_secrets.COS_REGION COS_REGION = app_secrets.COS_REGION
COS_BUCKET = app_secrets.COS_BUCKET COS_BUCKET = app_secrets.COS_BUCKET
COS_SCHEME = 'https' COS_SCHEME = 'https'
COS_DOMAIN = f'https://{COS_BUCKET}.cos.{COS_REGION}.myqcloud.com' COS_DOMAIN = f'https://{COS_BUCKET}.cos.{COS_REGION}.myqcloud.com'
COS_APPID = app_secrets.COS_APPID COS_APPID = app_secrets.COS_APPID
COS_STS_DURATION = 7200 COS_STS_DURATION = 7200
# ==================== 微信小程序 & 服务号 ==================== # ==================== 微信小程序 & 服务号 ====================
WEIXIN_APPID = app_secrets.WEIXIN_APPID WEIXIN_APPID = app_secrets.WEIXIN_APPID
WEIXIN_SECRET = app_secrets.WEIXIN_SECRET WEIXIN_SECRET = app_secrets.WEIXIN_SECRET
WEIXIN_MCHID = app_secrets.WEIXIN_MCHID WEIXIN_MCHID = app_secrets.WEIXIN_MCHID
WEIXIN_SHANGHUMIYAO = app_secrets.WEIXIN_SHANGHUMIYAO WEIXIN_SHANGHUMIYAO = app_secrets.WEIXIN_SHANGHUMIYAO
WEIXIN_OFFICIAL_APPID = app_secrets.WEIXIN_OFFICIAL_APPID WEIXIN_OFFICIAL_APPID = app_secrets.WEIXIN_OFFICIAL_APPID
WEIXIN_OFFICIAL_SECRET = app_secrets.WEIXIN_OFFICIAL_SECRET WEIXIN_OFFICIAL_SECRET = app_secrets.WEIXIN_OFFICIAL_SECRET
WEIXIN_OFFICIAL_TOKEN = app_secrets.WEIXIN_OFFICIAL_TOKEN WEIXIN_OFFICIAL_TOKEN = app_secrets.WEIXIN_OFFICIAL_TOKEN
WEIXIN_OFFICIAL_ENCODING_AES_KEY = app_secrets.WEIXIN_OFFICIAL_ENCODING_AES_KEY WEIXIN_OFFICIAL_ENCODING_AES_KEY = app_secrets.WEIXIN_OFFICIAL_ENCODING_AES_KEY
WEIXIN_TEMPLATE_ID = app_secrets.WEIXIN_TEMPLATE_ID WEIXIN_TEMPLATE_ID = app_secrets.WEIXIN_TEMPLATE_ID
WEIXIN_TEMPLATE_MAX_PER_MINUTE = 950 WEIXIN_TEMPLATE_MAX_PER_MINUTE = 950
WEIXIN_TEMPLATE_BATCH_SIZE = 100 WEIXIN_TEMPLATE_BATCH_SIZE = 100
WEIXIN_CERT_PATH = app_secrets.WEIXIN_CERT_PATH WEIXIN_CERT_PATH = app_secrets.WEIXIN_CERT_PATH
WEIXIN_KEY_PATH = app_secrets.WEIXIN_KEY_PATH WEIXIN_KEY_PATH = app_secrets.WEIXIN_KEY_PATH
# ==================== 微信支付回调地址 ==================== # ==================== 微信支付回调地址 ====================
WEIXIN_NOTIFY_URL = 'https://www.abas.asia/hqhd/dingdan/pay-notify/' WEIXIN_NOTIFY_URL = 'https://www.abas.asia/hqhd/dingdan/pay-notify/'
WEIXIN_PAY_DESCRIPTION = "网络服务" WEIXIN_PAY_DESCRIPTION = "网络服务"
YAJIN_PAY_DESCRIPTION = "打手押金充值" YAJIN_PAY_DESCRIPTION = "打手押金充值"
YAJIN_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/yajinhuitiao" YAJIN_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/yajinhuitiao"
JIFEN_PAY_DESCRIPTION = "打手积分补充" JIFEN_PAY_DESCRIPTION = "打手积分补充"
JIFEN_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/jifenhuitiao" JIFEN_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/jifenhuitiao"
HUIYUAN_PAY_DESCRIPTION = "打手会员充值" HUIYUAN_PAY_DESCRIPTION = "打手会员充值"
HUIYUAN_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/huiyuanhuitiao" HUIYUAN_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/huiyuanhuitiao"
KAOHE_PAY_DESCRIPTION = '考核金牌-缴纳考核费' KAOHE_PAY_DESCRIPTION = '考核金牌-缴纳考核费'
KAOHE_PAY_NOTIFY_URL = 'https://www.abas.asia/hqhd/yonghu/kh_huitiao' KAOHE_PAY_NOTIFY_URL = 'https://www.abas.asia/hqhd/yonghu/kh_huitiao'
KAOHE_PAY_DEFAULT_FEE = 5.00 KAOHE_PAY_DEFAULT_FEE = 5.00
SHANGJIA_CZ_PREFIX = "CzSJ" SHANGJIA_CZ_PREFIX = "CzSJ"
SHANGJIA_CZ_PAY_DESCRIPTION = "阿龙电竞-商家余额充值" SHANGJIA_CZ_PAY_DESCRIPTION = "阿龙电竞-商家余额充值"
SHANGJIA_CZ_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/sjhuitiao" SHANGJIA_CZ_PAY_NOTIFY_URL = "https://www.abas.asia/hqhd/shangpin/sjhuitiao"
SHANGJIA_CZ_MIN_AMOUNT = 1 SHANGJIA_CZ_MIN_AMOUNT = 1
SHANGJIA_CZ_MAX_AMOUNT = 10000 SHANGJIA_CZ_MAX_AMOUNT = 10000
FAKUAN_PAY_DESCRIPTION = '罚款缴纳' FAKUAN_PAY_DESCRIPTION = '罚款缴纳'
FAKUAN_PAY_NOTIFY_URL = 'https://www.abas.asia/hqhd/yonghu/fk_huitiao' FAKUAN_PAY_NOTIFY_URL = 'https://www.abas.asia/hqhd/yonghu/fk_huitiao'
# ==================== 订单ID前缀 ==================== # ==================== 订单ID前缀 ====================
ORDER_PREFIX = { ORDER_PREFIX = {
'yajin': 'CzYJ', 'yajin': 'CzYJ',
'jifen': 'CzJF', 'jifen': 'CzJF',
'huiyuan': 'CzHY', 'huiyuan': 'CzHY',
} }
# ==================== 腾讯云 IM ==================== # ==================== 腾讯云 IM ====================
TENCENT_IM_CONFIG = { TENCENT_IM_CONFIG = {
'SDK_APP_ID': 0, 'SDK_APP_ID': 0,
'SECRET_KEY': '', 'SECRET_KEY': '',
} }
# ==================== 阿里云短信 ==================== # ==================== 阿里云短信 ====================
ALIYUN_SMS_CONFIG = { ALIYUN_SMS_CONFIG = {
'ACCESS_KEY_ID': '', 'ACCESS_KEY_ID': '',
'ACCESS_KEY_SECRET': '', 'ACCESS_KEY_SECRET': '',
'SIGN_NAME': '', 'SIGN_NAME': '',
'TEMPLATE_CODE': '', 'TEMPLATE_CODE': '',
'REGION_ID': 'cn-hangzhou', 'REGION_ID': 'cn-hangzhou',
} }
# ==================== GoEasy IM ==================== # ==================== GoEasy IM ====================
GOEASY_APPKEY = app_secrets.GOEASY_APPKEY GOEASY_APPKEY = app_secrets.GOEASY_APPKEY
GOEASY_SECRET = app_secrets.GOEASY_SECRET GOEASY_SECRET = app_secrets.GOEASY_SECRET
GOEASY_REST_URL = 'https://rest-hz.goeasy.io/v2/im/message' GOEASY_REST_URL = 'https://rest-hz.goeasy.io/v2/im/message'
# ==================== Celery 配置 ==================== # ==================== Celery 配置 ====================
REDIS_HOST = app_secrets.REDIS_HOST REDIS_HOST = app_secrets.REDIS_HOST
REDIS_PORT = app_secrets.REDIS_PORT REDIS_PORT = app_secrets.REDIS_PORT
REDIS_PASSWORD = app_secrets.REDIS_PASSWORD REDIS_PASSWORD = app_secrets.REDIS_PASSWORD
REDIS_DB_CELERY = app_secrets.REDIS_DB_CELERY REDIS_DB_CELERY = app_secrets.REDIS_DB_CELERY
REDIS_DB_RESULT = app_secrets.REDIS_DB_RESULT REDIS_DB_RESULT = app_secrets.REDIS_DB_RESULT
CACHES = { CACHES = {
'default': { 'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache', 'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': f'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/2', 'LOCATION': f'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/2',
'TIMEOUT': 300, 'TIMEOUT': 300,
'OPTIONS': {} 'OPTIONS': {}
} }
} }
if REDIS_PASSWORD: if REDIS_PASSWORD:
CELERY_BROKER_URL = f'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB_CELERY}' 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}' CELERY_RESULT_BACKEND = f'redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB_RESULT}'
else: else:
CELERY_BROKER_URL = f'redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB_CELERY}' 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_RESULT_BACKEND = f'redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB_RESULT}'
CELERY_ACCEPT_CONTENT = ['json'] CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json' CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Asia/Shanghai' CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = True CELERY_ENABLE_UTC = True
CELERY_IMPORTS = ('dingdan.tongzhi_tasks',) CELERY_IMPORTS = ('dingdan.notice_tasks',)
CELERY_TASK_TIME_LIMIT = 300 CELERY_TASK_TIME_LIMIT = 300
CELERY_TASK_SOFT_TIME_LIMIT = 240 CELERY_TASK_SOFT_TIME_LIMIT = 240
CELERY_WORKER_CONCURRENCY = 4 CELERY_WORKER_CONCURRENCY = 4
CELERY_WORKER_PREFETCH_MULTIPLIER = 1 CELERY_WORKER_PREFETCH_MULTIPLIER = 1
CELERY_TASK_ACKS_LATE = True CELERY_TASK_ACKS_LATE = True
CELERY_WORKER_DISABLE_RATE_LIMITS = True CELERY_WORKER_DISABLE_RATE_LIMITS = True
CELERY_TASK_DEFAULT_QUEUE = 'default' CELERY_TASK_DEFAULT_QUEUE = 'default'
CELERY_TASK_QUEUES = { CELERY_TASK_QUEUES = {
'default': {'exchange': 'default', 'routing_key': 'default'}, 'default': {'exchange': 'default', 'routing_key': 'default'},
'order_tasks': {'exchange': 'order_tasks', 'routing_key': 'order_tasks'}, 'order_tasks': {'exchange': 'order_tasks', 'routing_key': 'order_tasks'},
'periodic_tasks': {'exchange': 'periodic_tasks', 'routing_key': 'periodic_tasks'}, 'periodic_tasks': {'exchange': 'periodic_tasks', 'routing_key': 'periodic_tasks'},
} }
# ==================== 定时任务时间配置 ==================== # ==================== 定时任务时间配置 ====================
ORDER_EXPIRE_SECONDS = 48 * 60 * 60 # 48小时 = 172800秒 ORDER_EXPIRE_SECONDS = 48 * 60 * 60 # 48小时 = 172800秒
DAILY_TASK_HOUR = 0 DAILY_TASK_HOUR = 0
DAILY_TASK_MINUTE = 30 DAILY_TASK_MINUTE = 30
MONTHLY_TASK_DAY = 1 MONTHLY_TASK_DAY = 1
MONTHLY_TASK_HOUR = 0 MONTHLY_TASK_HOUR = 0
MONTHLY_TASK_MINUTE = 0 MONTHLY_TASK_MINUTE = 0
ORDER_CHECK_INTERVAL = 5 ORDER_CHECK_INTERVAL = 5
# ==================== 跨平台订单互通 ==================== # ==================== 跨平台订单互通 ====================
CROSS_PLATFORM_TOKEN = app_secrets.CROSS_PLATFORM_TOKEN CROSS_PLATFORM_TOKEN = app_secrets.CROSS_PLATFORM_TOKEN
CROSS_PLATFORM_SYNC_TOKEN = app_secrets.CROSS_PLATFORM_SYNC_TOKEN CROSS_PLATFORM_SYNC_TOKEN = app_secrets.CROSS_PLATFORM_SYNC_TOKEN
PARTNER_BASE_URL = app_secrets.PARTNER_BASE_URL PARTNER_BASE_URL = app_secrets.PARTNER_BASE_URL
PARTNER_SYNC_ORDER_URL = '/dingdan/partner_sync_order' PARTNER_SYNC_ORDER_URL = '/dingdan/partner_sync_order'
PARTNER_CHECK_CLAIM_URL = '/dingdan/partner_check_claim' PARTNER_CHECK_CLAIM_URL = '/dingdan/partner_check_claim'
PARTNER_NOTIFY_CLAIM_URL = '/dingdan/partner_notify_claim' PARTNER_NOTIFY_CLAIM_URL = '/dingdan/partner_notify_claim'
PARTNER_REFUND_NOTIFY_URL = '/dingdan/partner_refund_notify' PARTNER_REFUND_NOTIFY_URL = '/dingdan/partner_refund_notify'
AGENT_SHANGJIA_ID = app_secrets.AGENT_SHANGJIA_ID AGENT_SHANGJIA_ID = app_secrets.AGENT_SHANGJIA_ID
CROSS_PLATFORM_MEMBER_ID = app_secrets.CROSS_PLATFORM_MEMBER_ID CROSS_PLATFORM_MEMBER_ID = app_secrets.CROSS_PLATFORM_MEMBER_ID
CROSS_PLATFORM_GAME_TYPE_ID = 1 CROSS_PLATFORM_GAME_TYPE_ID = 1
CROSS_PLATFORM_WHITELIST = app_secrets.CROSS_PLATFORM_WHITELIST CROSS_PLATFORM_WHITELIST = app_secrets.CROSS_PLATFORM_WHITELIST
SPECIAL_GUANSHI_IDS = app_secrets.SPECIAL_GUANSHI_IDS SPECIAL_GUANSHI_IDS = app_secrets.SPECIAL_GUANSHI_IDS
# ==================== 微信支付 V3 ==================== # ==================== 微信支付 V3 ====================
WECHAT_PAY_V3_CONFIG = { WECHAT_PAY_V3_CONFIG = {
'MCHID': app_secrets.WECHAT_PAY_V3_MCHID, 'MCHID': app_secrets.WECHAT_PAY_V3_MCHID,
'APPID': app_secrets.WECHAT_PAY_V3_APPID, 'APPID': app_secrets.WECHAT_PAY_V3_APPID,
'API_V3_KEY': app_secrets.WECHAT_PAY_V3_API_V3_KEY, 'API_V3_KEY': app_secrets.WECHAT_PAY_V3_API_V3_KEY,
'PRIVATE_KEY_PATH': app_secrets.WECHAT_PAY_V3_PRIVATE_KEY_PATH, 'PRIVATE_KEY_PATH': app_secrets.WECHAT_PAY_V3_PRIVATE_KEY_PATH,
'CERT_SERIAL_NO': app_secrets.WECHAT_PAY_V3_CERT_SERIAL_NO, 'CERT_SERIAL_NO': app_secrets.WECHAT_PAY_V3_CERT_SERIAL_NO,
'PLATFORM_CERT_DIR': app_secrets.WECHAT_PAY_V3_PLATFORM_CERT_DIR, 'PLATFORM_CERT_DIR': app_secrets.WECHAT_PAY_V3_PLATFORM_CERT_DIR,
'NOTIFY_URL': 'https://www.abas.asia/hqhd/yonghu/tixiancallback', 'NOTIFY_URL': 'https://www.abas.asia/hqhd/yonghu/tixiancallback',
'TRANSFER_SCENE_ID': '1005', 'TRANSFER_SCENE_ID': '1005',
} }

View File

@@ -2,4 +2,4 @@ from django.apps import AppConfig
class HoutaiConfig(AppConfig): class HoutaiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'houtai' name = 'houtai'

View File

@@ -2,7 +2,7 @@
#小程序弹窗序列化器 #小程序弹窗序列化器
from rest_framework import serializers from rest_framework import serializers
from peizhi.models import PopupPage, PopupConfig, PopupImage from config.models import PopupPage, PopupConfig, PopupImage
class PopupImageSerializer(serializers.ModelSerializer): class PopupImageSerializer(serializers.ModelSerializer):
"""图片序列化器(输出用)""" """图片序列化器(输出用)"""

View File

@@ -11,6 +11,8 @@ from django.db.models import F
from datetime import date from datetime import date
from decimal import Decimal from decimal import Decimal
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -217,10 +219,6 @@ def update_guanshi_daily_by_action(yonghuid, action, amount=Decimal('0.00')):
根据操作行为更新管事每日统计邀请打手充值会员订单/押金分红 根据操作行为更新管事每日统计邀请打手充值会员订单/押金分红
action: 1 = 邀请打手, 2 = 充值会员, 3 = 商家订单分红, 4 = 押金分红 action: 1 = 邀请打手, 2 = 充值会员, 3 = 商家订单分红, 4 = 押金分红
""" """
from django.db import transaction
from django.db.models import F
from datetime import date
today = date.today() today = date.today()
with transaction.atomic(): with transaction.atomic():

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,7 @@
from django.db import models from django.db import models
class Lunbo(models.Model): class Lunbo(models.Model):
tupian_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='轮播图片URL') tupian_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='轮播图片URL')
leixing = models.IntegerField(null=True, blank=True, verbose_name='图片类型:1轮播2个人中心背景') leixing = models.IntegerField(null=True, blank=True, verbose_name='图片类型:1轮播2个人中心背景')
@@ -66,9 +68,6 @@ class Szjilu(models.Model):
# models/shangjia_moban.py
from django.db import models
class ShangjiaMoban(models.Model): class ShangjiaMoban(models.Model):
"""商家订单模板表 - 存储商家创建的订单模板""" """商家订单模板表 - 存储商家创建的订单模板"""
@@ -119,8 +118,6 @@ class ShangjiaMoban(models.Model):
return f"模板{self.id}-{self.yonghu_id}" return f"模板{self.id}-{self.yonghu_id}"
# models/shangjia_lianjie.py
from django.db import models
class ShangjiaLianjie(models.Model): class ShangjiaLianjie(models.Model):
@@ -274,10 +271,6 @@ class WithdrawConfig(models.Model):
# apps/club/models.py
from django.db import models
class Club(models.Model): class Club(models.Model):
"""合作俱乐部/平台信息表""" """合作俱乐部/平台信息表"""
# 自己的俱乐部ID6位字符 # 自己的俱乐部ID6位字符
@@ -384,8 +377,6 @@ class ClubConfig(models.Model):
return f"{self.club_nickname or self.club_id} ({'我方' if self.is_self else '对方'})" return f"{self.club_nickname or self.club_id} ({'我方' if self.is_self else '对方'})"
from django.db import models
class AccountPermission(models.Model): class AccountPermission(models.Model):
"""账号权限表:记录每个账号拥有的权限及开关状态""" """账号权限表:记录每个账号拥有的权限及开关状态"""
account_id = models.CharField(max_length=11, db_index=True, verbose_name='账号ID') account_id = models.CharField(max_length=11, db_index=True, verbose_name='账号ID')
@@ -521,10 +512,6 @@ class DailyPayoutStat(models.Model):
from django.db import models
class PopupPage(models.Model): class PopupPage(models.Model):
page_key = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='页面标识') page_key = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='页面标识')
name = models.CharField(max_length=100, verbose_name='页面名称') name = models.CharField(max_length=100, verbose_name='页面名称')

View File

@@ -3,7 +3,6 @@
处理收支记录表的每日清零 处理收支记录表的每日清零
固定使用ID=1的记录 固定使用ID=1的记录
""" """
from celery import shared_task from celery import shared_task
from django.db import transaction from django.db import transaction
from django.utils import timezone from django.utils import timezone
@@ -12,7 +11,7 @@ from decimal import Decimal
import logging import logging
# 导入模型 # 导入模型
from peizhi.models import Szjilu from config.models import Szjilu
from utils.celery_utils import log_task_execution, rollback_on_failure from utils.celery_utils import log_task_execution, rollback_on_failure
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -37,4 +37,4 @@ urlpatterns = [
path('yhbdsjh', CheckPhoneAuthView.as_view(), name='判断是否需要手机号绑定'), path('yhbdsjh', CheckPhoneAuthView.as_view(), name='判断是否需要手机号绑定'),
path('hqsjlslj', ShangjiaLianjieListView.as_view(), name='商家获取生成链接'), path('hqsjlslj', ShangjiaLianjieListView.as_view(), name='商家获取生成链接'),
] ]

File diff suppressed because it is too large Load Diff

View File

@@ -1,614 +0,0 @@
"""
阿龙电竞 - 信号处理器修复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)}")
# 不抛出异常,避免影响订单保存的主流程
# 信号处理失败不影响订单保存,有周期性检查作为备用方案'''

View File

@@ -1,28 +1,28 @@
# /opt/1panel/apps/openresty/nginx/www/sites/43.142.166.152.443/django/gunicorn.conf.py # /opt/1panel/apps/openresty/nginx/www/sites/43.142.166.152.443/django/gunicorn.conf.py
# 🔥 绑定的IP和端口根据你的Nginx配置后端在8001端口 # 绑定的IP和端口根据你的Nginx配置后端在8001端口
bind = "127.0.0.1:8001" bind = "127.0.0.1:8001"
# 🔥 工作进程数根据CPU核心数调整建议CPU核心数*2+1 # 工作进程数根据CPU核心数调整建议CPU核心数*2+1
# 查看CPU核心数cat /proc/cpuinfo | grep "processor" | wc -l # 查看CPU核心数cat /proc/cpuinfo | grep "processor" | wc -l
workers = 4 workers = 4
# 🔥 工作模式使用gevent支持异步更高效 # 工作模式使用gevent支持异步更高效
#worker_class = "gevent" #worker_class = "gevent"
worker_class = "sync" # 改成这个 worker_class = "sync" # 改成这个
# 🔥 每个worker的最大请求数防止内存泄漏 # 每个worker的最大请求数防止内存泄漏
max_requests = 1000 max_requests = 1000
max_requests_jitter = 50 # 随机抖动避免所有worker同时重启 max_requests_jitter = 50 # 随机抖动避免所有worker同时重启
# 🔥 超时时间(秒) # 超时时间(秒)
timeout = 30 timeout = 30
keepalive = 2 keepalive = 2
# 🔥 预加载应用(减少内存占用,加快请求处理) # 预加载应用(减少内存占用,加快请求处理)
preload_app = True preload_app = True
# 🔥 日志配置 # 日志配置
# 访问日志输出到标准输出便于OnePanel查看 # 访问日志输出到标准输出便于OnePanel查看
accesslog = "-" accesslog = "-"
# 错误日志:输出到标准错误 # 错误日志:输出到标准错误
@@ -30,23 +30,23 @@ errorlog = "-"
# 日志级别 # 日志级别
loglevel = "info" loglevel = "info"
# 🔥 进程名称(便于识别) # 进程名称(便于识别)
proc_name = "a_long_dianjing_gunicorn" proc_name = "a_long_dianjing_gunicorn"
# 🔥 工作进程的用户/组安全考虑用非root用户运行 # 工作进程的用户/组安全考虑用非root用户运行
# user = "www-data" # user = "www-data"
# group = "www-data" # group = "www-data"
# 🔥 工作进程临时目录(避免权限问题) # 工作进程临时目录(避免权限问题)
worker_tmp_dir = "/dev/shm" worker_tmp_dir = "/dev/shm"
# 🔥 限制请求数据大小(防止大文件攻击) # 限制请求数据大小(防止大文件攻击)
limit_request_line = 4094 limit_request_line = 4094
limit_request_fields = 100 limit_request_fields = 100
limit_request_field_size = 8190 limit_request_field_size = 8190
# 🔥 优雅退出时间 # 优雅退出时间
graceful_timeout = 30 graceful_timeout = 30
# 🔥 进程守护如果用systemd管理这里设为False # 进程守护如果用systemd管理这里设为False
daemon = False daemon = False

View File

@@ -7,4 +7,4 @@ class DingdanConfig(AppConfig):
def ready(self): def ready(self):
# 导入信号处理,确保信号被注册 # 导入信号处理,确保信号被注册
import dingdan.signals import orders.signals

View File

@@ -1,5 +1,6 @@
from django.db import models from django.db import models
class Dingdan(models.Model): class Dingdan(models.Model):
"""订单主表""" """订单主表"""
# 核心编号:唯一且不可为空[citation:3][citation:9] # 核心编号:唯一且不可为空[citation:3][citation:9]

View File

@@ -1,71 +1,71 @@
# dingdan/tongzhi_tasks.py # dingdan/notice_tasks.py
import logging import logging
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
from a_long_dianjing.celery_app import app from a_long_dianjing.celery_app import app
from yonghu.models import OfficialAccountUser from users.models import OfficialAccountUser
from utils.weixin_broadcast import WeixinBroadcastSender from utils.weixin_broadcast import WeixinBroadcastSender
logger = logging.getLogger('weixin_broadcast') logger = logging.getLogger('weixin_broadcast')
@app.task(bind=True, max_retries=3, default_retry_delay=60) @app.task(bind=True, max_retries=3, default_retry_delay=60)
def dingdan_guangbo(self, order_info): def dingdan_guangbo(self, order_info):
""" """
异步并发广播订单模板消息给所有关注服务号的用户 异步并发广播订单模板消息给所有关注服务号的用户
order_info 字典字段示例 order_info 字典字段示例
{ {
'dingdan_id': 'PT123456', 'dingdan_id': 'PT123456',
'game_type': '王者荣耀', 'game_type': '王者荣耀',
'amount': '88.00', 'amount': '88.00',
'order_desc': '星耀上王者' 'order_desc': '星耀上王者'
} }
""" """
sender = WeixinBroadcastSender() sender = WeixinBroadcastSender()
template_data = sender.build_order_message_data(order_info) template_data = sender.build_order_message_data(order_info)
order_id = order_info.get('dingdan_id') order_id = order_info.get('dingdan_id')
# 查询所有已关注用户 # 查询所有已关注用户
try: try:
openids = list( openids = list(
OfficialAccountUser.objects OfficialAccountUser.objects
.filter(is_subscribed=True) .filter(is_subscribed=True)
.values_list('official_openid', flat=True) .values_list('official_openid', flat=True)
) )
except Exception as e: except Exception as e:
logger.error(f'查询订阅用户失败: {e}') logger.error(f'查询订阅用户失败: {e}')
raise self.retry(exc=e) raise self.retry(exc=e)
if not openids: if not openids:
return {'success': True, 'msg': '无订阅用户'} return {'success': True, 'msg': '无订阅用户'}
total = len(openids) total = len(openids)
logger.info(f'📊 Celery 广播订单 {order_id} 开始,目标人数: {total}') logger.info(f'📊 Celery 广播订单 {order_id} 开始,目标人数: {total}')
success = 0 success = 0
fail = 0 fail = 0
# 20 个线程并发发送,微信接口耗时 200-500ms1000 人约 15 秒完成 # 20 个线程并发发送,微信接口耗时 200-500ms1000 人约 15 秒完成
with ThreadPoolExecutor(max_workers=20) as executor: with ThreadPoolExecutor(max_workers=20) as executor:
future_map = { future_map = {
executor.submit(sender.send_to_user, openid, template_data, order_id): openid executor.submit(sender.send_to_user, openid, template_data, order_id): openid
for openid in openids for openid in openids
} }
for future in as_completed(future_map): for future in as_completed(future_map):
openid = future_map[future] openid = future_map[future]
try: try:
res = future.result(timeout=30) res = future.result(timeout=30)
if res.get('success'): if res.get('success'):
success += 1 success += 1
else: else:
fail += 1 fail += 1
except Exception as e: except Exception as e:
fail += 1 fail += 1
logger.error(f'发送异常 {openid[:10]}...: {e}') logger.error(f'发送异常 {openid[:10]}...: {e}')
logger.info(f'🏁 广播完成:成功 {success},失败 {fail},总计 {total}') logger.info(f'🏁 广播完成:成功 {success},失败 {fail},总计 {total}')
return { return {
'success': True, 'success': True,
'total': total, 'total': total,
'success_count': success, 'success_count': success,
'fail_count': fail 'fail_count': fail
} }

102
orders/signals.py Normal file
View File

@@ -0,0 +1,102 @@
"""
阿龙电竞 - 订单信号处理器(生产稳定版)
经过验证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 orders.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 orders.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}")

View File

@@ -14,8 +14,8 @@ from decimal import Decimal
import logging import logging
# 导入模型 - 确保路径正确 # 导入模型 - 确保路径正确
from dingdan.models import Dingdan, DingdanShangjia from orders.models import Dingdan
from yonghu.models import UserMain, UserDashou, UserShangjia from users.models import UserMain, UserDashou, UserShangjia
from utils.celery_utils import safe_decimal_operation, log_task_execution, rollback_on_failure from utils.celery_utils import safe_decimal_operation, log_task_execution, rollback_on_failure
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -25,9 +25,6 @@ logger = logging.getLogger(__name__)
@shared_task(bind=True, max_retries=3, default_retry_delay=60) @shared_task(bind=True, max_retries=3, default_retry_delay=60)
def process_expired_order(self, dingdan_id): def process_expired_order(self, dingdan_id):
""" """
@@ -43,13 +40,6 @@ def process_expired_order(self, dingdan_id):
order = Dingdan.objects.select_for_update().get( order = Dingdan.objects.select_for_update().get(
dingdan_id=dingdan_id dingdan_id=dingdan_id
) )
# 其余代码不变...
# ... 保持原有代码
# 再次检查订单状态是否为8 # 再次检查订单状态是否为8
if order.zhuangtai != 8: if order.zhuangtai != 8:
@@ -124,7 +114,7 @@ def process_expired_order(self, dingdan_id):
if fadan_pingtai == 1: if fadan_pingtai == 1:
try: try:
from dingdan.utils import update_platform_profit # 导入函数 from orders.utils import update_platform_profit # 导入函数
update_platform_profit(dingdan_id) update_platform_profit(dingdan_id)
except Exception as e: except Exception as e:
# 平台收益更新失败不影响主流程,但记录日志 # 平台收益更新失败不影响主流程,但记录日志

View File

@@ -65,8 +65,6 @@ urlpatterns = [
path('partner_sync_order', csrf_exempt(PartnerSyncOrderView.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_check_claim', csrf_exempt(PartnerCheckClaimView.as_view()), name='对方抢单询问接口'),
@@ -86,6 +84,4 @@ urlpatterns = [
] ]

View File

@@ -1,21 +1,25 @@
import threading import threading
import requests import requests
import time
from django.conf import settings
from peizhi.models import Club
from shangpin.models import ShangpinLeixing
from dingdan.models import Dingdan # 根据实际路径导入
import logging import logging
from datetime import date from datetime import date
from decimal import Decimal
from django.conf import settings
from django.db import transaction from django.db import transaction
from django.db.models import F from django.db.models import F
from peizhi.models import DailyDispatchStat # 假设模型在 peizhi 应用中
from config.models import Club, DailyDispatchStat, DailyIncomeStat, DailyPayoutStat
from orders.models import Dingdan, DingdanPingtai, DingdanShangjia, Lilubiao
from products.models import ShangpinLeixing, Gsfenhong
from users.models import UserDashou, UserGuanshi
from backend.utils import update_guanshi_daily_by_action
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# 可选:线程池限制(避免大量线程同时创建,建议使用 Celery这里简单用线程 # 可选:线程池限制(避免大量线程同时创建,建议使用 Celery这里简单用线程
THREAD_POOL_SIZE = 10 THREAD_POOL_SIZE = 10
_semaphore = threading.Semaphore(THREAD_POOL_SIZE) _semaphore = threading.Semaphore(THREAD_POOL_SIZE)
@@ -149,7 +153,6 @@ def _send_to_single_partner(dingdan, club):
logger.error(f"同步订单异常(尝试 {attempt}/3: {e}") logger.error(f"同步订单异常(尝试 {attempt}/3: {e}")
if attempt < 3: if attempt < 3:
import time
time.sleep(2 ** attempt) # 指数退避 time.sleep(2 ** attempt) # 指数退避
else: else:
logger.error(f"同步订单 {dingdan.dingdan_id} 到俱乐部 {club.club_id} 最终失败") logger.error(f"同步订单 {dingdan.dingdan_id} 到俱乐部 {club.club_id} 最终失败")
@@ -158,7 +161,6 @@ def _send_to_single_partner(dingdan, club):
# 获取下单方ID根据订单类型 # 获取下单方ID根据订单类型
def get_order_user_id(order): def get_order_user_id(order):
from dingdan.models import DingdanPingtai, DingdanShangjia
if order.fadan_pingtai == 1: if order.fadan_pingtai == 1:
try: try:
ext = DingdanPingtai.objects.get(dingdan=order) ext = DingdanPingtai.objects.get(dingdan=order)
@@ -176,18 +178,6 @@ def get_order_user_id(order):
# 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, def update_daily_dispatch_stat(direction, partner_club_id,
dispatch_amount=None, dispatch_count=1, dispatch_amount=None, dispatch_count=1,
@@ -255,9 +245,6 @@ def update_daily_dispatch_stat(direction, partner_club_id,
from peizhi.models import DailyIncomeStat
logger = logging.getLogger(__name__)
def update_daily_income(amount): def update_daily_income(amount):
@@ -295,9 +282,6 @@ def update_daily_income(amount):
from peizhi.models import DailyPayoutStat
logger = logging.getLogger(__name__)
def update_daily_payout(amount): def update_daily_payout(amount):
""" """
@@ -335,10 +319,6 @@ def update_daily_payout(amount):
from decimal import Decimal
from dingdan.models import Lilubiao
def calc_shangjia_order_fencheng(jine): def calc_shangjia_order_fencheng(jine):
""" """
@@ -374,8 +354,6 @@ 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: if getattr(order, 'fadan_pingtai', None) != 2:
return return
@@ -433,7 +411,6 @@ def settle_shangjia_order_guanshi_fenhong(order, dashou_id):
return return
try: try:
from houtai.utils import update_guanshi_daily_by_action
update_guanshi_daily_by_action( update_guanshi_daily_by_action(
yonghuid=guanshi_id, yonghuid=guanshi_id,
action=3, action=3,

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More