diff --git a/a_long_dianjing/celery.py b/a_long_dianjing/celery.py index afc447e..3b05de9 100644 --- a/a_long_dianjing/celery.py +++ b/a_long_dianjing/celery.py @@ -1,92 +1,26 @@ """ -阿龙电竞 - Celery定时任务主配置 +阿龙电竞 - Celery 定时任务主配置 + +业务约定(2026-07):Beat 只跑两条 +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 diff --git a/jituan/services/order_deal.py b/jituan/services/order_deal.py index 1296df6..c4495c4 100644 --- a/jituan/services/order_deal.py +++ b/jituan/services/order_deal.py @@ -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