feat: 订单列表/详情返回自动结算倒计时字段,并加小程序结算率接口
仅 eligible 结算中订单下发预计时间;支持 auto_settle_pending 筛选;新增 /dingdan/wo-de-deal-stat。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -287,3 +287,48 @@ def serialize_dashou_stat(row) -> dict:
|
||||
'deal_rate': round(c / n, 6) if n else 0,
|
||||
'fined_rate': round(f / n, 6) if n else 0,
|
||||
}
|
||||
|
||||
|
||||
def miniapp_merchant_deal_payload(row) -> dict | None:
|
||||
"""小程序商家订单列表顶:有样本才返回;文案字段供前端标「结算率」。"""
|
||||
if not row:
|
||||
return None
|
||||
n = int(row.order_count or 0)
|
||||
if n <= 0:
|
||||
return None
|
||||
full = serialize_merchant_stat(row)
|
||||
out = {
|
||||
'role': 'merchant',
|
||||
'order_count': full['order_count'],
|
||||
'deal_rate': full['deal_rate'],
|
||||
'fine_rate': full['fine_rate'],
|
||||
'deal_rate_text': f"{round(full['deal_rate'] * 100, 1)}%",
|
||||
'fine_rate_text': f"{round(full['fine_rate'] * 100, 1)}%",
|
||||
}
|
||||
sc = int(full.get('settle_duration_sample_count') or 0)
|
||||
if sc > 0 and full.get('avg_deal_hours'):
|
||||
h = float(full['avg_deal_hours'])
|
||||
out['avg_deal_hours'] = h
|
||||
if h < 1:
|
||||
out['avg_deal_hours_text'] = f'{max(1, int(round(h * 60)))}分钟'
|
||||
else:
|
||||
out['avg_deal_hours_text'] = f'{round(h, 1)}小时'
|
||||
return out
|
||||
|
||||
|
||||
def miniapp_dashou_deal_payload(row) -> dict | None:
|
||||
"""小程序打手订单列表顶:有样本才返回。"""
|
||||
if not row:
|
||||
return None
|
||||
n = int(row.order_count or 0)
|
||||
if n <= 0:
|
||||
return None
|
||||
full = serialize_dashou_stat(row)
|
||||
return {
|
||||
'role': 'dashou',
|
||||
'order_count': full['order_count'],
|
||||
'deal_rate': full['deal_rate'],
|
||||
'fined_rate': full['fined_rate'],
|
||||
'deal_rate_text': f"{round(full['deal_rate'] * 100, 1)}%",
|
||||
'fined_rate_text': f"{round(full['fined_rate'] * 100, 1)}%",
|
||||
}
|
||||
|
||||
@@ -27,6 +27,41 @@ def get_or_create_deal_config(club_id: str):
|
||||
return row
|
||||
|
||||
|
||||
def order_auto_settle_payload(order) -> dict:
|
||||
"""
|
||||
小程序列表/详情展示用。
|
||||
仅 Status=8 且 AutoSettleEligible 且有 AutoExpireAt 时返回字段;否则空 dict。
|
||||
前端有字段才展示,避免关开关/老单误报。
|
||||
"""
|
||||
from backend.utils import fmt_datetime
|
||||
|
||||
if not order:
|
||||
return {}
|
||||
try:
|
||||
status = int(getattr(order, 'Status', 0) or 0)
|
||||
except (TypeError, ValueError):
|
||||
status = 0
|
||||
if status != 8:
|
||||
return {}
|
||||
if not bool(getattr(order, 'AutoSettleEligible', False)):
|
||||
return {}
|
||||
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())
|
||||
remain = int((expire_at - now).total_seconds())
|
||||
return {
|
||||
'auto_settle_eligible': True,
|
||||
'auto_expire_at': fmt_datetime(expire_at),
|
||||
'auto_settle_remain_seconds': max(0, remain),
|
||||
}
|
||||
|
||||
|
||||
def config_to_dict(row) -> dict:
|
||||
if not row:
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user