feat: add finance logs page, banner upload, and admin withdraw fix

## 财务流水
- 新增 FinanceLogs.vue(/finance-logs):额度流水 + 上下分流水双 Tab,支持时间/代理/玩家/操作人筛选与分页
- 管理员与代理共用页面,API 按角色自动切换(/admin/* 或 /agent/*)
- 侧栏「财务流水」替代原「额度流水」;代理侧栏同步新增入口
- /agent-credit-transactions 重定向至 /finance-logs?tab=credit,旧链接仍可用
- 后端:新增 GET /admin/wallet/transfer-transactions;增强额度/上下分列表筛选
- 代理端:新增 GET /agent/credit-transactions;GET /agent/wallet-transactions 支持分页与筛选
- 修复:管理员下分改为 adminWithdrawFromPlayer(),下分后重算上级代理 usedCredit

## 内容管理 Banner
- Contents.vue:各语言 Banner 支持本地上传、媒体库选择、手动填 URL(≤5MB)
- vite 开发代理 /uploads;生产 nginx 反代 /uploads/ 至 API

## 玩家端 Banner
- BannerCarousel:外链无协议时自动补 https://
- defaultBanner:API 加载中不闪默认图,仅空列表时展示默认 Banner

## 其他
- AgentManager:查看额度流水链接改为 /finance-logs
- i18n:finance.*、nav.finance_logs、content.upload.*(中/英/马来)

未纳入本次提交:.pnpm-store/、release/ 部署包、uploads/banners/ 下测试上传图片

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-10 10:02:53 +08:00
parent df20444be9
commit 6124313369
17 changed files with 1233 additions and 25 deletions

View File

@@ -1085,14 +1085,20 @@ export class AdminController {
@Query('pageSize') pageSize?: string,
@Query('agentId') agentId?: string,
@Query('keyword') keyword?: string,
@Query('operatorKeyword') operatorKeyword?: string,
@Query('transactionType') transactionType?: string,
@Query('dateFrom') dateFrom?: string,
@Query('dateTo') dateTo?: string,
) {
const result = await this.agents.listCreditTransactions({
page: page ? parseInt(page, 10) : 1,
pageSize: pageSize ? parseInt(pageSize, 10) : 20,
agentId: agentId ? BigInt(agentId) : undefined,
keyword,
operatorKeyword,
transactionType,
dateFrom: dateFrom ? new Date(dateFrom) : undefined,
dateTo: dateTo ? new Date(dateTo) : undefined,
});
return jsonResponse(result);
}
@@ -1187,7 +1193,7 @@ export class AdminController {
@Post('wallet/withdraw')
@RequirePermissions(P.walletWithdraw)
async withdraw(@CurrentUser('id') operatorId: bigint, @Body() dto: DepositDto & { userId: string }) {
const result = await this.wallet.withdraw(
const result = await this.agents.adminWithdrawFromPlayer(
BigInt(dto.userId),
dto.amount,
operatorId,
@@ -1204,6 +1210,35 @@ export class AdminController {
return jsonResponse(result);
}
@Get('wallet/transfer-transactions')
@RequirePermissions(P.walletDeposit, P.walletWithdraw, P.reports)
async listWalletTransferTransactions(
@Query('page') page?: string,
@Query('pageSize') pageSize?: string,
@Query('playerId') playerId?: string,
@Query('parentAgentId') parentAgentId?: string,
@Query('parentAgentKeyword') parentAgentKeyword?: string,
@Query('keyword') keyword?: string,
@Query('operatorKeyword') operatorKeyword?: string,
@Query('transactionType') transactionType?: string,
@Query('dateFrom') dateFrom?: string,
@Query('dateTo') dateTo?: string,
) {
const result = await this.wallet.listTransferTransactions({
page: page ? parseInt(page, 10) : 1,
pageSize: pageSize ? parseInt(pageSize, 10) : 20,
playerId: playerId ? BigInt(playerId) : undefined,
parentAgentId: parentAgentId ? BigInt(parentAgentId) : undefined,
parentAgentKeyword,
keyword,
operatorKeyword,
transactionType,
dateFrom: dateFrom ? new Date(dateFrom) : undefined,
dateTo: dateTo ? new Date(dateTo) : undefined,
});
return jsonResponse(result);
}
@Post('leagues')
@RequirePermissions(P.matches)
async createLeague(