fix(weibo): 隔离换绑角色的微豆核账

This commit is contained in:
yml
2026-11-13 17:20:44 +08:00
parent d2550d4ab7
commit b89ee4fd18
3 changed files with 267 additions and 99 deletions
+145 -99
View File
@@ -82,6 +82,14 @@ BALANCE_GROUP_STATUSES = (
STATUS_WEIDOU_ERROR,
)
AUTO_DONE_SOURCE_STATUSES = (
STATUS_TO_RECHARGE,
STATUS_RECHARGED,
STATUS_SCRIPTED,
STATUS_WEIDOU_ERROR,
STATUS_FAILED,
)
VERDICT_SUCCESS = "success"
VERDICT_FAILED = "failed"
VERDICT_UNKNOWN = "unknown"
@@ -935,7 +943,8 @@ def check_balance_groups(
if len(rows) > CHECK_BATCH_MAX:
raise CollabError(f"一次最多核账 {CHECK_BATCH_MAX} 条", code="batch_too_large")
threshold = max(0, min(int(threshold), 1_000_000))
grouped: dict[int, list[WeiboExternalOrder]] = {}
selected_keys: list[tuple[int, str]] = []
grouped: dict[tuple[int, str], list[WeiboExternalOrder]] = {}
failures: list[dict[str, Any]] = []
skipped: list[dict[str, Any]] = []
for order in rows:
@@ -958,28 +967,49 @@ def check_balance_groups(
}
)
continue
grouped.setdefault(int(order.account_id), []).append(order)
key = (
int(order.account_id),
_text(order.game_role_name, max_length=128),
)
if key not in grouped:
selected_keys.append(key)
grouped.setdefault(key, []).append(order)
selected_account_ids = list(dict.fromkeys(key[0] for key in selected_keys))
keys_by_account: dict[int, list[tuple[int, str]]] = {}
for key in selected_keys:
keys_by_account.setdefault(key[0], []).append(key)
selected_account_ids = set(grouped)
grouped = {}
for account_id in selected_account_ids:
grouped[account_id] = (
if selected_account_ids:
for row in (
db.query(WeiboExternalOrder)
.filter(
WeiboExternalOrder.account_id == account_id,
WeiboExternalOrder.account_id.in_(selected_account_ids),
WeiboExternalOrder.status.in_(BALANCE_GROUP_STATUSES),
WeiboExternalOrder.archived_at.is_(None),
)
.order_by(WeiboExternalOrder.id.asc())
.all()
)
):
if not row.account_id:
continue
key = (
int(row.account_id),
_text(row.game_role_name, max_length=128),
)
grouped.setdefault(key, []).append(row)
groups: list[dict[str, Any]] = []
updated = 0
for account_id, group_orders in grouped.items():
for account_id in selected_account_ids:
account_keys = keys_by_account.get(account_id, [])
account_orders = [
order for key in account_keys for order in grouped.get(key, [])
]
account = db.query(WeiboAccount).filter(WeiboAccount.id == account_id).first()
if account is None:
for order in group_orders:
for order in account_orders:
failures.append(
{
"id": order.id,
@@ -989,7 +1019,7 @@ def check_balance_groups(
)
continue
if not account.is_enabled:
for order in group_orders:
for order in account_orders:
failures.append(
{
"id": order.id,
@@ -1001,114 +1031,130 @@ def check_balance_groups(
try:
data = WeiboClient(account.cookie or "").list_mall(activity)
except (WeiboError, ValueError) as exc:
for order in group_orders:
for order in account_orders:
failures.append(
{"id": order.id, "order_no": order.order_no, "message": str(exc)}
)
continue
balance = _to_int(data.get("weidou_balance"))
required_total = sum(int(order.required_weidou or 0) for order in group_orders)
surplus = balance - required_total if balance is not None else None
if surplus is None:
reason = "error"
message = "未拿到微豆余额"
target = None
elif surplus < 0:
reason = "shortage"
message = (
f"微豆不足:余额 {balance},订单合计 {required_total},还差 {-surplus}"
)
target = STATUS_WEIDOU_ERROR
elif surplus > threshold:
reason = "excess"
message = (
f"微豆超出阈值:余额 {balance},订单合计 {required_total},"
f"多出 {surplus}(阈值 {threshold})"
)
target = STATUS_WEIDOU_ERROR
else:
reason = "ok"
message = (
f"核账通过:余额 {balance},订单合计 {required_total},结余 {surplus}"
)
target = STATUS_RECHARGED
if balance is not None:
account.weidou_balance = balance
now = _now()
for order in group_orders:
order.weidou_balance = balance
order.weidou_required_total = required_total
order.weidou_surplus = surplus
order.weidou_threshold = threshold
order.weidou_checked_at = now
order.weidou_check_reason = reason
order.weidou_check_message = message[:512]
order.weidou_check_detail = {
"account_id": account.id,
"uid": account.uid,
"balance": balance,
"required_total": required_total,
"surplus": surplus,
"threshold": threshold,
"order_ids": [item.id for item in group_orders],
}
if target is not None:
set_status(
for account_key in account_keys:
game_role_name = account_key[1]
group_orders = grouped.get(account_key, [])
if not group_orders:
continue
required_total = sum(
int(order.required_weidou or 0) for order in group_orders
)
surplus = balance - required_total if balance is not None else None
if surplus is None:
reason = "error"
message = "未拿到微豆余额"
target = None
elif surplus < 0:
reason = "shortage"
message = (
f"微豆不足:角色「{game_role_name or '未绑定'}」,余额 {balance},"
f"订单合计 {required_total},还差 {-surplus}"
)
target = STATUS_WEIDOU_ERROR
elif surplus > threshold:
reason = "excess"
message = (
f"微豆超出阈值:角色「{game_role_name or '未绑定'}」,余额 {balance},"
f"订单合计 {required_total},多出 {surplus}(阈值 {threshold})"
)
target = STATUS_WEIDOU_ERROR
else:
reason = "ok"
message = (
f"核账通过:角色「{game_role_name or '未绑定'}」,余额 {balance},"
f"订单合计 {required_total},结余 {surplus}"
)
target = STATUS_RECHARGED
now = _now()
for order in group_orders:
order.weidou_balance = balance
order.weidou_required_total = required_total
order.weidou_surplus = surplus
order.weidou_threshold = threshold
order.weidou_checked_at = now
order.weidou_check_reason = reason
order.weidou_check_message = message[:512]
order.weidou_check_detail = {
"account_id": account.id,
"uid": account.uid,
"role": game_role_name,
"balance": balance,
"required_total": required_total,
"surplus": surplus,
"threshold": threshold,
"order_ids": [item.id for item in group_orders],
}
if target is not None:
set_status(
db,
current,
order,
target,
reason=message,
allow_check=True,
commit=False,
)
order.recharged_at = now
updated += 1
_metric(
db,
current,
order,
target,
reason=message,
allow_check=True,
commit=False,
balance=balance,
required_total=required_total,
surplus=surplus,
records_total=0,
records_matched=0,
verdict=VERDICT_UNKNOWN if target is None else "",
message=message,
source="balance_check",
raw={
"reason": reason,
"role": game_role_name,
"order_ids": [item.id for item in group_orders],
},
)
order.recharged_at = now
updated += 1
_metric(
db,
current,
order,
balance=balance,
required_total=required_total,
surplus=surplus,
records_total=0,
records_matched=0,
verdict=VERDICT_UNKNOWN if target is None else "",
message=message,
source="balance_check",
raw={"reason": reason, "order_ids": [item.id for item in group_orders]},
)
_audit(
db,
current,
order,
"weibo:collab:balance_check",
_audit(
db,
current,
order,
"weibo:collab:balance_check",
{
"account_id": account.id,
"role": game_role_name,
"balance": balance,
"required_total": required_total,
"surplus": surplus,
"threshold": threshold,
"reason": reason,
},
)
groups.append(
{
"account_id": account.id,
"uid": account.uid,
"game_role_name": game_role_name,
"status": target or "",
"status_label": ORDER_STATUS_LABELS.get(target or "", ""),
"balance": balance,
"required_total": required_total,
"surplus": surplus,
"threshold": threshold,
"reason": reason,
},
"message": message,
"order_ids": [item.id for item in group_orders],
}
)
groups.append(
{
"account_id": account.id,
"uid": account.uid,
"status": target or "",
"status_label": ORDER_STATUS_LABELS.get(target or "", ""),
"balance": balance,
"required_total": required_total,
"surplus": surplus,
"threshold": threshold,
"reason": reason,
"message": message,
"order_ids": [item.id for item in group_orders],
}
)
db.commit()
return {
"total": len(rows),
@@ -1231,7 +1277,7 @@ def check_exchange_records(
order.exchange_record_snapshot = _json_safe(record)
order.exchange_verdict = VERDICT_SUCCESS
order.exchange_message = "兑换记录唯一命中"
if order.status in (STATUS_RECHARGED, STATUS_SCRIPTED):
if order.status in AUTO_DONE_SOURCE_STATUSES:
set_status(
db,
current,