feat: 充值订单审计与重新申请,优化赛事展示和余额刷新

- 新增 deposit_order_audit_logs 表,记录提交/审批/拒绝/撤销/重提全链路
- 管理端充值单页增加审计历史;玩家端充值历史支持时间线与重新申请
- 已拒绝订单可原单号重提;撤销入账使用 PLAYER_DEPOSIT_REVERSAL 并加强幂等
- 结算后清除热门标记,允许归档已结算赛事,完善今日赛事时区窗口
- 足球页今日/早盘独立折叠;资料与余额在进入钱包/个人页及下注后自动刷新
- 补充投注玩法、结算返水规则文档;新增 smoke/settlement CLI 脚本

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-15 14:52:05 +08:00
parent 73a94e6be3
commit afb5c5437e
55 changed files with 3050 additions and 160 deletions

View File

@@ -38,6 +38,7 @@ export class FundsPostingService {
remark?: string;
referenceId?: string;
transactionType?: string;
referenceType?: string;
businessKey?: string;
tx?: TxClient;
}) {
@@ -50,6 +51,7 @@ export class FundsPostingService {
command.transactionType ?? 'MANUAL_WITHDRAW',
command.tx,
command.businessKey,
command.referenceType,
);
}

View File

@@ -108,6 +108,7 @@ export class WalletService {
transactionType = 'MANUAL_WITHDRAW',
tx?: TxClient,
businessKey?: string,
referenceType = 'WITHDRAW',
) {
const amt = new Decimal(amount);
if (amt.lte(0)) throw appBadRequest('AMOUNT_MUST_BE_POSITIVE');
@@ -143,7 +144,7 @@ export class WalletService {
balanceAfter,
frozenBefore: w.frozen_balance,
frozenAfter: w.frozen_balance,
referenceType: 'WITHDRAW',
referenceType,
referenceId,
businessKey,
operatorId,
@@ -372,6 +373,7 @@ export class WalletService {
'ADMIN_WITHDRAW',
'AGENT_WITHDRAW',
'PLAYER_DEPOSIT',
'PLAYER_DEPOSIT_REVERSAL',
].includes(type)
) {
return type;
@@ -395,6 +397,7 @@ export class WalletService {
if (WalletService.SYSTEM_REMARKS.has(r)) return false;
if (r.startsWith('Cashback batch ')) return false;
if (r.startsWith('Deposit order ')) return false;
if (r.startsWith('Revoke approved deposit ')) return false;
return true;
}
@@ -418,6 +421,9 @@ export class WalletService {
const parts = [depositMethodName?.trim(), tx.referenceId?.trim()].filter(Boolean);
return parts.length ? parts.join(' · ') : null;
}
if (type === 'PLAYER_DEPOSIT_REVERSAL' && tx.referenceId) {
return tx.referenceId;
}
if (this.isCustomRemark(tx.remark)) return tx.remark!.trim();
return null;
}