fix: 自动结算写入对齐 USE_TZ;lxs 打手注册同步开通管事
order_deal 不再 make_aware 入库以免 MySQL 报错;龙先生打手注册成功时创建管事且邀请人与打手相同。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,9 +5,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
from django.utils import timezone
|
||||
@@ -15,9 +16,25 @@ from django.utils import timezone
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _db_datetime(dt):
|
||||
"""写入 DateTimeField 用:与 USE_TZ 对齐,避免 MySQL 拒收 aware datetime。"""
|
||||
if dt is None:
|
||||
return None
|
||||
if settings.USE_TZ:
|
||||
if timezone.is_naive(dt):
|
||||
return timezone.make_aware(dt, timezone.get_current_timezone())
|
||||
return dt
|
||||
if timezone.is_aware(dt):
|
||||
return timezone.make_naive(dt, timezone.get_current_timezone())
|
||||
return dt
|
||||
|
||||
|
||||
def _default_stats_since():
|
||||
return _db_datetime(datetime(2020, 1, 1))
|
||||
|
||||
|
||||
def get_or_create_deal_config(club_id: str):
|
||||
from jituan.models import ClubOrderDealConfig
|
||||
from datetime import datetime
|
||||
cid = (club_id or '').strip()
|
||||
if not cid:
|
||||
return None
|
||||
@@ -27,7 +44,7 @@ def get_or_create_deal_config(club_id: str):
|
||||
row = ClubOrderDealConfig(
|
||||
club_id=cid,
|
||||
stats_enabled=True,
|
||||
stats_since=timezone.make_aware(datetime(2020, 1, 1)),
|
||||
stats_since=_default_stats_since(),
|
||||
)
|
||||
row.save()
|
||||
return row
|
||||
@@ -54,12 +71,8 @@ def order_auto_settle_payload(order) -> dict:
|
||||
expire_at = getattr(order, 'AutoExpireAt', None)
|
||||
if not expire_at:
|
||||
return {}
|
||||
now = timezone.now()
|
||||
# 与项目 USE_TZ 对齐,避免剩余秒数偏差
|
||||
if timezone.is_aware(expire_at) and timezone.is_naive(now):
|
||||
now = timezone.make_aware(now, timezone.get_current_timezone())
|
||||
elif timezone.is_naive(expire_at) and timezone.is_aware(now):
|
||||
expire_at = timezone.make_aware(expire_at, timezone.get_current_timezone())
|
||||
now = _db_datetime(timezone.now())
|
||||
expire_at = _db_datetime(expire_at)
|
||||
remain = int((expire_at - now).total_seconds())
|
||||
try:
|
||||
expire_ts = int(expire_at.timestamp())
|
||||
@@ -104,7 +117,7 @@ def save_deal_config(club_id: str, data: dict, updated_by: str = '') -> dict:
|
||||
|
||||
was_auto = bool(row.auto_settle_enabled)
|
||||
was_stats = bool(row.stats_enabled)
|
||||
now = timezone.now()
|
||||
now = _db_datetime(timezone.now())
|
||||
|
||||
if 'hours_platform' in data and data['hours_platform'] is not None:
|
||||
row.hours_platform = max(1, min(int(data['hours_platform']), 720))
|
||||
@@ -125,8 +138,7 @@ def save_deal_config(club_id: str, data: dict, updated_by: str = '') -> dict:
|
||||
row.stats_enabled = enabled
|
||||
if enabled and not was_stats:
|
||||
# 开启时默认从较早起点计,便于旧单刷新也能出成交率
|
||||
from datetime import datetime
|
||||
row.stats_since = timezone.make_aware(datetime(2020, 1, 1))
|
||||
row.stats_since = _default_stats_since()
|
||||
if not enabled:
|
||||
# 停止写入;保留 since 供只读理解历史起点;再次开启会刷新 since
|
||||
pass
|
||||
@@ -135,8 +147,11 @@ def save_deal_config(club_id: str, data: dict, updated_by: str = '') -> dict:
|
||||
|
||||
# 若刚打开统计且 since 空
|
||||
if row.stats_enabled and not row.stats_since:
|
||||
from datetime import datetime
|
||||
row.stats_since = timezone.make_aware(datetime(2020, 1, 1))
|
||||
row.stats_since = _default_stats_since()
|
||||
|
||||
# 历史脏数据:aware datetime 在 USE_TZ=False 下无法落库,保存前统一清洗
|
||||
row.auto_settle_enabled_at = _db_datetime(row.auto_settle_enabled_at)
|
||||
row.stats_since = _db_datetime(row.stats_since)
|
||||
|
||||
row.updated_by = (updated_by or '')[:32]
|
||||
row.save()
|
||||
@@ -166,18 +181,19 @@ def apply_enter_status_8(instance):
|
||||
订单进入结算中(8):写 SettlementTime;若俱乐部自动结算已开且本次开启后进入则打 eligible。
|
||||
由 signal 调用。instance 为即将 save 的 Order。
|
||||
"""
|
||||
now = timezone.now()
|
||||
now = _db_datetime(timezone.now())
|
||||
instance.SettlementTime = now
|
||||
instance.PendingDispatch = False
|
||||
instance.AutoTaskID = ''
|
||||
|
||||
club_id = (getattr(instance, 'ClubID', None) or '').strip()
|
||||
cfg = get_or_create_deal_config(club_id) if club_id else None
|
||||
enabled_at = _db_datetime(getattr(cfg, 'auto_settle_enabled_at', None)) if cfg else None
|
||||
if (
|
||||
cfg
|
||||
and cfg.auto_settle_enabled
|
||||
and cfg.auto_settle_enabled_at
|
||||
and now >= cfg.auto_settle_enabled_at
|
||||
and enabled_at
|
||||
and now >= enabled_at
|
||||
):
|
||||
hours = int(cfg.hours_merchant if instance.Platform == 2 else cfg.hours_platform) or 48
|
||||
instance.AutoSettleEligible = True
|
||||
@@ -215,16 +231,19 @@ def mark_order_completed_unified(order, *, source='auto_settle', operator=''):
|
||||
# 三重闸:资格位 + 到期时间 + 俱乐部开关仍开(关开关后立刻停,不靠等下一轮扫)
|
||||
if not locked.AutoSettleEligible:
|
||||
return False, '非自动结算资格单'
|
||||
if locked.AutoExpireAt and locked.AutoExpireAt > timezone.now():
|
||||
now = _db_datetime(timezone.now())
|
||||
expire_at = _db_datetime(locked.AutoExpireAt)
|
||||
if expire_at and expire_at > 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, '订单进入结算中早于本次开启,不自动结算'
|
||||
enabled_at = _db_datetime(cfg.auto_settle_enabled_at)
|
||||
settle_at = _db_datetime(getattr(locked, 'SettlementTime', None))
|
||||
if enabled_at and settle_at and settle_at < enabled_at:
|
||||
return False, '订单进入结算中早于本次开启,不自动结算'
|
||||
|
||||
dashou_id = locked.PlayerID
|
||||
dashou_fencheng = locked.PlayerCommission
|
||||
@@ -323,7 +342,7 @@ def scan_due_auto_settle(limit: int = 100) -> dict:
|
||||
if not clubs:
|
||||
return {'scanned': 0, 'settled': 0, 'failed': 0, 'clubs': 0}
|
||||
|
||||
now = timezone.now()
|
||||
now = _db_datetime(timezone.now())
|
||||
qs = (
|
||||
Order.objects.filter(
|
||||
ClubID__in=list(clubs),
|
||||
|
||||
Reference in New Issue
Block a user