feat: 充值订单审计与重新申请,优化赛事展示和余额刷新
- 新增 deposit_order_audit_logs 表,记录提交/审批/拒绝/撤销/重提全链路 - 管理端充值单页增加审计历史;玩家端充值历史支持时间线与重新申请 - 已拒绝订单可原单号重提;撤销入账使用 PLAYER_DEPOSIT_REVERSAL 并加强幂等 - 结算后清除热门标记,允许归档已结算赛事,完善今日赛事时区窗口 - 足球页今日/早盘独立折叠;资料与余额在进入钱包/个人页及下注后自动刷新 - 补充投注玩法、结算返水规则文档;新增 smoke/settlement CLI 脚本 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -182,7 +182,9 @@ export class PlayerController {
|
||||
]);
|
||||
const timeZone = safeTimeZone(headerTimeZone);
|
||||
const now = new Date();
|
||||
const hotMatches = (allMatches as Array<{ isHot?: boolean }>).filter((m) => m.isHot);
|
||||
const hotMatches = (allMatches as Array<{ isHot?: boolean; status?: string }>).filter(
|
||||
(m) => m.isHot && m.status !== 'SETTLED',
|
||||
);
|
||||
const todayMatches = (allMatches as Array<{ startTime: string }>).filter((m) => {
|
||||
const kickoff = new Date(m.startTime);
|
||||
return !Number.isNaN(kickoff.getTime()) && isInLocalTodayMatchWindow(kickoff, now, timeZone);
|
||||
@@ -399,4 +401,52 @@ export class PlayerController {
|
||||
);
|
||||
return jsonResponse(result);
|
||||
}
|
||||
|
||||
@Get('deposit-orders/:id/audit-logs')
|
||||
async myDepositOrderAuditLogs(
|
||||
@CurrentUser('id') userId: bigint,
|
||||
@Param('id') id: string,
|
||||
) {
|
||||
const items = await this.deposit.getPlayerDepositOrderAuditLogs(BigInt(id), userId);
|
||||
return jsonResponse({ items });
|
||||
}
|
||||
|
||||
@Post('deposit-orders/:id/reapply')
|
||||
@UseInterceptors(FileInterceptor('screenshot', { limits: { fileSize: 5 * 1024 * 1024 } }))
|
||||
async reapplyDepositOrder(
|
||||
@CurrentUser('id') userId: bigint,
|
||||
@Param('id') id: string,
|
||||
@UploadedFile() file: { originalname: string; mimetype: string; buffer: Buffer; size: number } | undefined,
|
||||
@Body() body: { paymentMethodId?: string; amount?: string },
|
||||
) {
|
||||
if (!file) throw appBadRequest('SCREENSHOT_REQUIRED');
|
||||
if (!file.mimetype.startsWith('image/')) throw appBadRequest('FILE_MUST_BE_IMAGE');
|
||||
|
||||
const amount = body.amount != null && body.amount !== '' ? parseFloat(body.amount) : undefined;
|
||||
if (amount != null && (!amount || amount <= 0)) throw appBadRequest('INVALID_AMOUNT');
|
||||
|
||||
const ext = extname(file.originalname || '.jpg').toLowerCase() || '.jpg';
|
||||
const filename = `${Date.now()}-${randomUUID().slice(0, 8)}${ext}`;
|
||||
const root = getUploadRoot();
|
||||
const targetDir = join(root, 'deposits');
|
||||
await mkdir(targetDir, { recursive: true });
|
||||
await writeFile(join(targetDir, filename), file.buffer);
|
||||
const screenshotUrl = `/uploads/deposits/${filename}`;
|
||||
|
||||
const order = await this.deposit.reapplyDepositOrder(
|
||||
userId,
|
||||
BigInt(id),
|
||||
screenshotUrl,
|
||||
amount,
|
||||
body.paymentMethodId ? BigInt(body.paymentMethodId) : undefined,
|
||||
);
|
||||
|
||||
return jsonResponse({
|
||||
id: order!.id.toString(),
|
||||
orderNo: order!.orderNo,
|
||||
amount: order!.amount.toString(),
|
||||
status: order!.status,
|
||||
createdAt: order!.createdAt,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user