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:
@@ -359,17 +359,84 @@ export class AgentPortalController {
|
||||
return jsonResponse(summary);
|
||||
}
|
||||
|
||||
@Get('wallet-transactions')
|
||||
async walletTransactions(@CurrentUser('id') agentId: bigint, @Query('playerId') playerId?: string) {
|
||||
const players = playerId
|
||||
? [BigInt(playerId)]
|
||||
: (await this.agents.getDirectPlayers(agentId)).map((p) => p.id);
|
||||
|
||||
const transactions = await this.prisma.walletTransaction.findMany({
|
||||
where: { userId: { in: players } },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 50,
|
||||
@Get('credit-transactions')
|
||||
async listCreditTransactions(
|
||||
@CurrentUser('id') agentId: bigint,
|
||||
@Query('page') page?: string,
|
||||
@Query('pageSize') pageSize?: string,
|
||||
@Query('agentId') filterAgentId?: string,
|
||||
@Query('keyword') keyword?: string,
|
||||
@Query('operatorKeyword') operatorKeyword?: string,
|
||||
@Query('transactionType') transactionType?: string,
|
||||
@Query('dateFrom') dateFrom?: string,
|
||||
@Query('dateTo') dateTo?: string,
|
||||
) {
|
||||
const scopedAgentIds = await this.agents.getSubtreeAgentIds(agentId);
|
||||
const parsedAgentId = filterAgentId ? BigInt(filterAgentId) : undefined;
|
||||
if (parsedAgentId && !scopedAgentIds.some((id) => id === parsedAgentId)) {
|
||||
return jsonResponse({ items: [], total: 0, page: 1, pageSize: 20 });
|
||||
}
|
||||
const result = await this.agents.listCreditTransactions({
|
||||
page: page ? parseInt(page, 10) : 1,
|
||||
pageSize: pageSize ? parseInt(pageSize, 10) : 20,
|
||||
agentId: parsedAgentId,
|
||||
keyword,
|
||||
operatorKeyword,
|
||||
transactionType,
|
||||
scopedAgentIds,
|
||||
dateFrom: dateFrom ? new Date(dateFrom) : undefined,
|
||||
dateTo: dateTo ? new Date(dateTo) : undefined,
|
||||
});
|
||||
return jsonResponse(transactions);
|
||||
return jsonResponse(result);
|
||||
}
|
||||
|
||||
@Get('wallet-transactions')
|
||||
async walletTransactions(
|
||||
@CurrentUser('id') agentId: bigint,
|
||||
@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 scopedParentAgentIds = await this.agents.getSubtreeAgentIds(agentId);
|
||||
const parsedParentAgentId = parentAgentId ? BigInt(parentAgentId) : undefined;
|
||||
if (
|
||||
parsedParentAgentId &&
|
||||
!scopedParentAgentIds.some((id) => id === parsedParentAgentId)
|
||||
) {
|
||||
return jsonResponse({ items: [], total: 0, page: 1, pageSize: 20 });
|
||||
}
|
||||
if (playerId) {
|
||||
const player = await this.prisma.user.findFirst({
|
||||
where: { id: BigInt(playerId), userType: 'PLAYER', deletedAt: null },
|
||||
select: { id: true, parentId: true },
|
||||
});
|
||||
if (
|
||||
!player?.parentId ||
|
||||
!scopedParentAgentIds.some((id) => id === player.parentId)
|
||||
) {
|
||||
return jsonResponse({ items: [], total: 0, page: 1, pageSize: 20 });
|
||||
}
|
||||
}
|
||||
const result = await this.wallet.listTransferTransactions({
|
||||
page: page ? parseInt(page, 10) : 1,
|
||||
pageSize: pageSize ? parseInt(pageSize, 10) : 20,
|
||||
playerId: playerId ? BigInt(playerId) : undefined,
|
||||
parentAgentId: parsedParentAgentId,
|
||||
parentAgentKeyword,
|
||||
scopedParentAgentIds,
|
||||
keyword,
|
||||
operatorKeyword,
|
||||
transactionType,
|
||||
dateFrom: dateFrom ? new Date(dateFrom) : undefined,
|
||||
dateTo: dateTo ? new Date(dateTo) : undefined,
|
||||
});
|
||||
return jsonResponse(result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user