feat: split admin dashboard, improve match ops, and player closed-match UX

Admin: add match/player overview sub-nav; refine settlement flow and league
match management UI; improve action button enabled/disabled styles; enhance
logo upload and outright odds sync.

API: expose matchPhase/bettingOpen for closed matches; league publish guards;
settlement preview with auto score save; outright team auto-sync.

Player: watermark for closed/settled states; keep match and bet details visible;
remove default login credentials.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-10 13:00:14 +08:00
parent 6124313369
commit 03f54ca689
43 changed files with 2787 additions and 519 deletions

View File

@@ -252,7 +252,7 @@ export class AgentsService {
playerId,
amount,
operatorId,
remark,
remark ?? '管理员上分',
requestId,
);
const player = await this.prisma.user.findUnique({
@@ -277,7 +277,7 @@ export class AgentsService {
playerId,
amount,
operatorId,
remark,
remark ?? '管理员下分',
requestId,
);
const player = await this.prisma.user.findUnique({
@@ -432,7 +432,7 @@ export class AgentsService {
await this.assertAgentDepositLimits(agentId, amt);
await this.wallet.deposit(playerId, amt, agentId, remark ?? 'Agent deposit', requestId);
await this.wallet.deposit(playerId, amt, agentId, remark ?? '代理上分', requestId);
await this.recalculateUsedCredit(agentId);
return { success: true };
@@ -443,10 +443,11 @@ export class AgentsService {
playerId: bigint,
amount: number,
requestId: string,
remark?: string,
) {
await this.requireDirectPlayer(agentId, playerId);
await this.wallet.withdraw(playerId, amount, agentId, 'Agent withdraw', requestId);
await this.wallet.withdraw(playerId, amount, agentId, remark ?? '代理下分', requestId);
await this.recalculateUsedCredit(agentId);
return { success: true };
@@ -1481,11 +1482,27 @@ export class AgentsService {
}
async getSubtreeAgentIds(agentId: bigint) {
const descendants = await this.prisma.agentClosure.findMany({
where: { ancestorId: agentId },
select: { descendantId: true },
});
return descendants.map((d) => d.descendantId);
const ids: bigint[] = [];
const queue: bigint[] = [agentId];
const seen = new Set<string>();
while (queue.length > 0) {
const current = queue.shift()!;
const key = current.toString();
if (seen.has(key)) continue;
seen.add(key);
ids.push(current);
const children = await this.prisma.agentProfile.findMany({
where: { parentAgentId: current },
select: { userId: true },
});
for (const child of children) {
queue.push(child.userId);
}
}
return ids;
}
async getReportSummary(agentId: bigint) {