chore: Beat仅保留冻结解冻+可控自动结算,并加固关开关立即停结

自动结算结单前再校验俱乐部开关与 SettlementTime>=开启时间;关开关仍清 eligible。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-27 03:17:01 +08:00
parent 7002da6093
commit 193a82930e
2 changed files with 33 additions and 97 deletions

View File

@@ -1,92 +1,26 @@
"""
阿龙电竞 - Celery定时任务主配置
阿龙电竞 - Celery 定时任务主配置
业务约定2026-07Beat 只跑两条
1) 资金冻结到期解冻
2) 可控订单自动结算
其余榜单/清零类任务保留代码但不进 beat_schedule不会定时执行。
"""
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()
# 自动发现任务(不含 orders.tasks订单超时自动结算已永久关闭
app.autodiscover_tasks(['users.tasks', 'users.ranking_tasks', 'config.tasks', 'jituan.tasks'])
# 配置周期性任务Celery Beat Schedule
# 仅自动发现仍可能被调用的任务模块;旧超时结算 orders.tasks 永不加载
app.autodiscover_tasks(['jituan.tasks', 'users.tasks', 'users.ranking_tasks', 'config.tasks'])
# ========== Beat只保留这两个 ==========
app.conf.beat_schedule = {
# 月榜数据转移每月最后一天23:50执行
'yuebang_zhuanyi': {
'task': 'users.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': 'users.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': 'users.ranking_tasks.qingli_jiulishuju',
'schedule': crontab(hour=1, minute=0, day_of_month=1), # 每月1号凌晨1点
'options': {'queue': 'periodic_tasks', 'priority': 4},
},
# 补偿检查任务(订单自动结算已禁用)
# 'check_order_expire_task': {
# 'task': 'orders.tasks.check_order_expire_task',
# 'schedule': crontab(minute='*/5000'),
# 'options': {'queue': 'order_tasks', 'priority': 3},
# },
# 1. 每日凌晨0点执行 - 清零任务
'daily_reset_task': {
'task': 'users.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': 'users.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': 'config.tasks.daily_sz_reset_task',
'schedule': crontab(hour=0, minute=5), # 每天0点5分
'options': {
'queue': 'periodic_tasks',
'priority': 4
},
'args': (),
'kwargs': {}
},
# 5. 资金冻结到期解冻(默认无冻结单时几乎空跑)
# 资金冻结到期解冻(俱乐部未开冻结 / 无到期单 → 空跑
'scan_fund_freeze_due': {
'task': 'jituan.tasks.scan_fund_freeze_due',
'schedule': crontab(minute='*/5'),
@@ -95,8 +29,7 @@ app.conf.beat_schedule = {
'priority': 4,
},
},
# 6. 可控订单自动结算(仅 AutoSettleEligible俱乐部开关关则空跑
# 可控自动结算(俱乐部后台开关关 → 空跑;只处理 AutoSettleEligible 新单)
'scan_order_auto_settle_due': {
'task': 'jituan.tasks.scan_order_auto_settle_due',
'schedule': crontab(minute='*/5'),
@@ -107,35 +40,22 @@ app.conf.beat_schedule = {
},
}
# 时区设置
app.conf.timezone = 'Asia/Shanghai'
app.conf.enable_utc = True
# 队列路由配置orders.tasks 已移除:禁止 order_tasks 队列再调度超时结算)
app.conf.task_routes = {
# 'orders.tasks.*': {'queue': 'order_tasks'},
'jituan.tasks.*': {'queue': 'periodic_tasks'},
'users.tasks.*': {'queue': 'periodic_tasks'},
'config.tasks.*': {'queue': 'periodic_tasks'},
'jituan.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.task_time_limit = 300
app.conf.task_soft_time_limit = 240
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小时
app.conf.result_expires = 3600

View File

@@ -189,10 +189,19 @@ def mark_order_completed_unified(order, *, source='auto_settle', operator=''):
if locked.Status != 8:
return False, f'状态不是结算中({locked.Status})'
if source == 'auto_settle':
# 三重闸:资格位 + 到期时间 + 俱乐部开关仍开(关开关后立刻停,不靠等下一轮扫)
if not locked.AutoSettleEligible:
return False, '非自动结算资格单'
if locked.AutoExpireAt and locked.AutoExpireAt > timezone.now():
return False, '未到自动结算时间'
club_id = (getattr(locked, 'ClubID', None) or '').strip()
cfg = get_or_create_deal_config(club_id) if club_id else None
if not cfg or not cfg.auto_settle_enabled:
return False, '俱乐部已关闭自动结算'
# 只认「本次打开之后」进结算中的单;开启前的旧 8 状态单不会带 eligible
if cfg.auto_settle_enabled_at and getattr(locked, 'SettlementTime', None):
if locked.SettlementTime < cfg.auto_settle_enabled_at:
return False, '订单进入结算中早于本次开启,不自动结算'
dashou_id = locked.PlayerID
dashou_fencheng = locked.PlayerCommission
@@ -281,7 +290,7 @@ def mark_order_completed_unified(order, *, source='auto_settle', operator=''):
def scan_due_auto_settle(limit: int = 100) -> dict:
"""只处理 eligible 到期单;无关自动结算的俱乐部空跑。"""
"""只处理 eligible 到期单;无关自动结算的俱乐部空跑;关开关后立刻无单可扫"""
from jituan.models import ClubOrderDealConfig
from orders.models import Order
@@ -305,6 +314,13 @@ def scan_due_auto_settle(limit: int = 100) -> dict:
settled = 0
failed = 0
for order in qs:
# 每单再读一次开关:避免扫到一半被关掉仍继续结
cid = (getattr(order, 'ClubID', None) or '').strip()
cfg = ClubOrderDealConfig.query.filter(club_id=cid, auto_settle_enabled=True).first()
if not cfg:
failed += 1
logger.info('自动结算跳过 %s: 俱乐部已关开关', order.OrderID)
continue
ok, msg = mark_order_completed_unified(order, source='auto_settle', operator='auto')
if ok:
settled += 1