fix: 成交配置禁派单开关可保存,兼容旧 hours 字段与 minutes 列缺失

This commit is contained in:
XingQue committed 2026-08-10 14:02:34 +08:00
1 parent c10727c356
commit 5de74f0444
1 file changed
+71 -23
+71 -23
View File
@@ -70,6 +70,22 @@ def _pick_display_unit(minutes: int) -> tuple[str, int]:
return 'm', m
def _as_bool(value, default: bool = False) -> bool:
"""兼容 JSON bool / 0|1 / "true"|"false",避免开关保存后读回仍是关。"""
if value is None:
return default
if isinstance(value, bool):
return value
if isinstance(value, (int, float)):
return value != 0
s = str(value).strip().lower()
if s in ('1', 'true', 'yes', 'on', 'y'):
return True
if s in ('0', 'false', 'no', 'off', 'n', ''):
return False
return default
def _parse_timeout_to_minutes(
data: dict,
*,
@@ -88,7 +104,8 @@ def _parse_timeout_to_minutes(
"""
if not isinstance(data, dict):
return None
unit = (data.get(unit_key) or '').strip().lower()
raw_unit = data.get(unit_key)
unit = str(raw_unit or '').strip().lower()
if value_key in data and data.get(value_key) is not None and unit in ('h', 'm', 'hour', 'hours', 'min', 'minute', 'minutes'):
try:
val = int(data.get(value_key))
@@ -622,11 +639,11 @@ def save_deal_config(club_id: str, data: dict, updated_by: str = '') -> dict:
row.dispatch_block_minutes = m_block
row.dispatch_block_hours = max(1, (m_block + 59) // 60)
if 'dispatch_block_enabled' in data and data['dispatch_block_enabled'] is not None:
row.dispatch_block_enabled = bool(data['dispatch_block_enabled'])
if 'dispatch_block_enabled' in data and data.get('dispatch_block_enabled') is not None:
row.dispatch_block_enabled = _as_bool(data.get('dispatch_block_enabled'), False)
if 'auto_settle_enabled' in data and data['auto_settle_enabled'] is not None:
enabled = bool(data['auto_settle_enabled'])
if 'auto_settle_enabled' in data and data.get('auto_settle_enabled') is not None:
enabled = _as_bool(data.get('auto_settle_enabled'), False)
row.auto_settle_enabled = enabled
if enabled and not was_auto:
row.auto_settle_enabled_at = now
@@ -634,8 +651,8 @@ def save_deal_config(club_id: str, data: dict, updated_by: str = '') -> dict:
row.auto_settle_enabled_at = None
_clear_club_auto_settle_eligible(row.club_id)
if 'stats_enabled' in data and data['stats_enabled'] is not None:
enabled = bool(data['stats_enabled'])
if 'stats_enabled' in data and data.get('stats_enabled') is not None:
enabled = _as_bool(data.get('stats_enabled'), False)
row.stats_enabled = enabled
if enabled and not was_stats:
# 开启时默认从较早起点计,便于旧单刷新也能出成交率
@@ -643,23 +660,24 @@ def save_deal_config(club_id: str, data: dict, updated_by: str = '') -> dict:
if not enabled:
# 停止写入;保留 since 供只读理解历史起点;再次开启会刷新 since
pass
if enabled and was_stats and data.get('reset_stats_since'):
if enabled and was_stats and _as_bool(data.get('reset_stats_since'), False):
row.stats_since = now
if 'default_partial_settle_ratio' in data and data['default_partial_settle_ratio'] is not None:
from decimal import Decimal, InvalidOperation
raw = data['default_partial_settle_ratio']
# 兼容前端传百分比 10~100
try:
val = Decimal(str(raw).strip())
except (InvalidOperation, ValueError, TypeError) as e:
raise ValueError('默认比例结算比例无效') from e
if val > 1:
val = val / Decimal('100')
val = val.quantize(Decimal('0.0001'))
if val < Decimal('0.01') or val > Decimal('1'):
raise ValueError('默认比例结算比例须在 1%~100%')
row.default_partial_settle_ratio = val
# 未传或空值时跳过,避免前端误传 undefined/null 把库里的比例冲掉或直接 400
if 'default_partial_settle_ratio' in data:
raw = data.get('default_partial_settle_ratio')
if raw is not None and str(raw).strip() != '' and str(raw).strip().lower() != 'undefined':
from decimal import Decimal, InvalidOperation
try:
val = Decimal(str(raw).strip())
except (InvalidOperation, ValueError, TypeError) as e:
raise ValueError('默认比例结算比例无效') from e
if val > 1:
val = val / Decimal('100')
val = val.quantize(Decimal('0.0001'))
if val < Decimal('0.01') or val > Decimal('1'):
raise ValueError('默认比例结算比例须在 1%~100%')
row.default_partial_settle_ratio = val
# 若刚打开统计且 since 空
if row.stats_enabled and not row.stats_since:
@@ -670,7 +688,7 @@ def save_deal_config(club_id: str, data: dict, updated_by: str = '') -> dict:
row.stats_since = _db_datetime(row.stats_since)
row.updated_by = (updated_by or '')[:32]
row.save()
_persist_deal_config_row(row)
# 保存后触发一次历史回填,保证抢单池马上能刷到成交率/罚款率
try:
from jituan.models import DealStatEvent
@@ -682,6 +700,36 @@ def save_deal_config(club_id: str, data: dict, updated_by: str = '') -> dict:
return config_to_dict(row)
def _persist_deal_config_row(row):
"""
落库。若线上尚未跑 minutes 迁移,回落到只写 hours/开关,避免整次保存 500 导致前端像「保存了又被清空」。
"""
from jituan.models import ClubOrderDealConfig
try:
row.save()
return
except Exception as e:
err = str(e).lower()
if not any(k in err for k in ('minutes_platform', 'minutes_merchant', 'dispatch_block_minutes', 'unknown column')):
raise
logger.warning('deal config minutes 列不可用,回落 hours 写入 club=%s: %s', row.club_id, e)
ClubOrderDealConfig.objects.filter(pk=row.pk).update(
auto_settle_enabled=bool(row.auto_settle_enabled),
auto_settle_enabled_at=row.auto_settle_enabled_at,
hours_platform=int(row.hours_platform or 48),
hours_merchant=int(row.hours_merchant or 48),
dispatch_block_enabled=bool(getattr(row, 'dispatch_block_enabled', False)),
dispatch_block_hours=int(getattr(row, 'dispatch_block_hours', None) or 24),
stats_enabled=bool(row.stats_enabled),
stats_since=row.stats_since,
default_partial_settle_ratio=getattr(row, 'default_partial_settle_ratio', None),
updated_by=(row.updated_by or '')[:32],
)
row.refresh_from_db()
def _clear_club_auto_settle_eligible(club_id: str):
from orders.models import Order
Order.objects.filter(ClubID=club_id, AutoSettleEligible=True).update(