收款商户限额/不可用时强制静默换号,审核单记录实际打款商户号。
对账无package一律放行;额度/IP/4xx跳过查单直接试下一户;新增 dakuan_mch_id 防止串户;平台限额文案加前缀区分。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -505,7 +505,7 @@ def _check_personal_quota_readonly(user_main, leixing, jine, shenhe_danhao=''):
|
||||
if projected_personal > personal_quota:
|
||||
return (
|
||||
False,
|
||||
f'今日提现已达个人限额,今日已提{personal_today}元,本次申请{jine}元,限额{personal_quota}元',
|
||||
f'【平台个人限额】今日提现已达上限,今日已提{personal_today}元,本次申请{jine}元,限额{personal_quota}元',
|
||||
'personal',
|
||||
)
|
||||
return True, '', ''
|
||||
@@ -530,7 +530,7 @@ def _check_platform_quota_readonly(leixing, shijidaozhang, shenhe_danhao=''):
|
||||
)
|
||||
return (
|
||||
False,
|
||||
f'今日该角色提现总额已达上限,今日已提{daily_used}元,本次到账{additional}元,限额{platform_limit}元',
|
||||
f'【平台总限额】今日该角色提现已达上限,今日已提{daily_used}元,本次到账{additional}元,限额{platform_limit}元',
|
||||
'platform',
|
||||
)
|
||||
|
||||
@@ -589,7 +589,7 @@ def reserve_collect_quota_limits(user_main, leixing, jine, shijidaozhang, shenhe
|
||||
if personal_today + jine > personal_quota:
|
||||
return (
|
||||
False,
|
||||
f'今日提现已达个人限额,今日已提{personal_today}元,本次申请{jine}元,限额{personal_quota}元',
|
||||
f'【平台个人限额】今日提现已达上限,今日已提{personal_today}元,本次申请{jine}元,限额{personal_quota}元',
|
||||
'personal',
|
||||
)
|
||||
|
||||
@@ -604,7 +604,7 @@ def reserve_collect_quota_limits(user_main, leixing, jine, shijidaozhang, shenhe
|
||||
if platform_stat.total_amount + shijidaozhang > platform_limit:
|
||||
return (
|
||||
False,
|
||||
f'今日该角色提现总额已达上限,今日已提{platform_stat.total_amount}元,'
|
||||
f'【平台总限额】今日该角色提现已达上限,今日已提{platform_stat.total_amount}元,'
|
||||
f'本次到账{shijidaozhang}元,限额{platform_limit}元',
|
||||
'platform',
|
||||
)
|
||||
@@ -1369,11 +1369,19 @@ def get_auto_record_for_collect(shenhe_danhao):
|
||||
)
|
||||
|
||||
|
||||
def _mark_auto_record_failed_local(rec, reason):
|
||||
if rec.zhuangtai == 0:
|
||||
rec.zhuangtai = 2
|
||||
rec.fail_reason = (reason or '打款失败')[:500]
|
||||
rec.save(update_fields=['zhuangtai', 'fail_reason', 'update_time'])
|
||||
|
||||
|
||||
def reconcile_shenhe_wechat_bills(shenhe_danhao):
|
||||
"""
|
||||
P0 防双笔:同一 shenhe_danhao 下所有打款记录逐条问微信
|
||||
任一 SUCCESS / 进行中 → 禁止新建;全部终态失败才 ALLOW_NEW
|
||||
返回 (action, payload)
|
||||
- SUCCESS → completed
|
||||
- 真正待确认(有 package) → wait_confirm(禁止换户)
|
||||
- 其余(无 package / 限额失败 / IP / 查单失败)→ 标失败,ALLOW_NEW 以便换商户
|
||||
"""
|
||||
records = list(
|
||||
TixianAutoRecord.objects.filter(shenhe_danhao=shenhe_danhao).order_by('-create_time')
|
||||
@@ -1386,66 +1394,68 @@ def reconcile_shenhe_wechat_bills(shenhe_danhao):
|
||||
if not records:
|
||||
return RECONCILE_ALLOW_NEW, {}
|
||||
|
||||
pending_hit = None
|
||||
pending_with_package = None
|
||||
for rec in records:
|
||||
if rec.zhuangtai == 1:
|
||||
ok, msg = ensure_shenhe_collect_completed(shenhe_danhao)
|
||||
return RECONCILE_COMPLETED, {'msg': msg, 'synced': ok}
|
||||
|
||||
wx_data = query_wechat_transfer_bill(rec.tixian_id)
|
||||
if wx_data is None:
|
||||
# IP 未放行时创建/查单都会失败。若从未拿到 package,说明没进待确认,
|
||||
# 标失败放行换商户;否则会永远卡在「处理中」并反复打第一个商户。
|
||||
has_pkg = bool(_parse_package_info(rec.wechat_package))
|
||||
if rec.zhuangtai == 0 and not has_pkg:
|
||||
rec.zhuangtai = 2
|
||||
rec.fail_reason = '查单不可用且未待确认(常见于IP白名单),已换其他商户重试'
|
||||
rec.save(update_fields=['zhuangtai', 'fail_reason', 'update_time'])
|
||||
logger.warning(
|
||||
'对账查单不可用已放行换号 shenhe=%s bill=%s mch=%s',
|
||||
shenhe_danhao, rec.tixian_id, rec.mch_id,
|
||||
)
|
||||
continue
|
||||
logger.error(
|
||||
'对账查单不可用 shenhe_danhao=%s bill=%s',
|
||||
shenhe_danhao, rec.tixian_id,
|
||||
)
|
||||
return RECONCILE_PENDING, {
|
||||
'msg': '有收款处理中,系统正在核对微信状态,请稍后再试',
|
||||
'tixian_id': rec.tixian_id,
|
||||
}
|
||||
local_pkg = _parse_package_info(rec.wechat_package)
|
||||
|
||||
action, payload = _sync_record_from_wx_query(rec, wx_data)
|
||||
if action == RECONCILE_COMPLETED:
|
||||
return action, payload
|
||||
if action == RECONCILE_WAIT_CONFIRM:
|
||||
return action, payload
|
||||
if action == RECONCILE_PENDING:
|
||||
# ACCEPTED/PROCESSING 且无 package:视为该出资户卡住,标失败以便换商户重发
|
||||
qstate = _wx_transfer_state(wx_data)
|
||||
has_pkg = bool(
|
||||
wx_data.get('package_info') or _parse_package_info(rec.wechat_package)
|
||||
)
|
||||
if (
|
||||
rec.zhuangtai == 0
|
||||
and qstate in ('ACCEPTED', 'PROCESSING')
|
||||
and not has_pkg
|
||||
):
|
||||
rec.zhuangtai = 2
|
||||
rec.fail_reason = (f'微信状态卡住:{qstate},已切换其他商户重试')[:500]
|
||||
rec.save(update_fields=['zhuangtai', 'fail_reason', 'update_time'])
|
||||
# 不 release 限额:后续同笔收款仍占用预占
|
||||
logger.warning(
|
||||
'对账放行卡住状态 shenhe=%s bill=%s mch=%s state=%s',
|
||||
shenhe_danhao, rec.tixian_id, rec.mch_id, qstate,
|
||||
)
|
||||
continue
|
||||
if pending_hit is None:
|
||||
pending_hit = (action, payload)
|
||||
# 本地已有收款包:必须先对齐数字,禁止为换号新建第二笔待确认
|
||||
if rec.zhuangtai == 0 and local_pkg:
|
||||
wx_data = query_wechat_transfer_bill(rec.tixian_id)
|
||||
if wx_data is None:
|
||||
return RECONCILE_PENDING, {
|
||||
'msg': '有收款处理中,系统正在核对微信状态,请稍后再试',
|
||||
'tixian_id': rec.tixian_id,
|
||||
}
|
||||
action, payload = _sync_record_from_wx_query(rec, wx_data)
|
||||
if action in (RECONCILE_COMPLETED, RECONCILE_WAIT_CONFIRM, RECONCILE_PENDING):
|
||||
return action, payload
|
||||
continue
|
||||
|
||||
if pending_hit:
|
||||
return pending_hit
|
||||
# 无 package:商户侧基本未进入待确认。限额/IP/卡单都不能挡住换号。
|
||||
if rec.zhuangtai == 0 and not local_pkg:
|
||||
wx_data = query_wechat_transfer_bill(rec.tixian_id)
|
||||
if wx_data is None or wx_data.get('_not_found'):
|
||||
_mark_auto_record_failed_local(
|
||||
rec, '未进入待确认(查无单或查单失败),已换商户重试',
|
||||
)
|
||||
continue
|
||||
action, payload = _sync_record_from_wx_query(rec, wx_data)
|
||||
if action == RECONCILE_COMPLETED:
|
||||
return action, payload
|
||||
if action == RECONCILE_WAIT_CONFIRM:
|
||||
return action, payload
|
||||
# PENDING / ALLOW_NEW:若仍无 package,标失败继续换号
|
||||
if action == RECONCILE_PENDING:
|
||||
pkg = (
|
||||
(payload or {}).get('package_info')
|
||||
if isinstance(payload, dict) else None
|
||||
) or wx_data.get('package_info')
|
||||
if pkg:
|
||||
if pending_with_package is None:
|
||||
pending_with_package = (action, payload)
|
||||
continue
|
||||
_mark_auto_record_failed_local(
|
||||
rec,
|
||||
f'微信不可用状态:{_wx_transfer_state(wx_data) or "未知"},已换商户重试',
|
||||
)
|
||||
continue
|
||||
|
||||
# 已失败记录:仍问一遍微信,防本地误标
|
||||
if rec.zhuangtai == 2:
|
||||
wx_data = query_wechat_transfer_bill(rec.tixian_id)
|
||||
if wx_data is None or wx_data.get('_not_found'):
|
||||
continue
|
||||
action, payload = _sync_record_from_wx_query(rec, wx_data)
|
||||
if action in (RECONCILE_COMPLETED, RECONCILE_WAIT_CONFIRM):
|
||||
return action, payload
|
||||
continue
|
||||
|
||||
if pending_with_package:
|
||||
return pending_with_package
|
||||
return RECONCILE_ALLOW_NEW, {}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user