feat: WC2026 赛事 seed、生产上线初始化脚本与目录归档

重构 seed 为 WC2026 72 场小组赛与 48 强优胜盘;新增 production 模式仅保留 admin 与赛事示例;提供 prod-init-db 全量重置脚本;管理端 i18n 分包与赛事归档能力。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-12 18:17:00 +08:00
parent 8f14e85ebd
commit e7e938f261
94 changed files with 12332 additions and 976 deletions

View File

@@ -806,6 +806,34 @@ export class AgentsService {
return this.getDirectPlayerDetail(agentId, playerId);
}
async deleteDirectPlayer(agentId: bigint, playerId: bigint) {
await this.requireDirectPlayer(agentId, playerId);
const betCount = await this.prisma.bet.count({
where: {
userId: playerId,
status: 'PENDING',
},
});
if (betCount > 0) {
throw appBadRequest('PLAYER_HAS_PENDING_BETS');
}
const wallet = await this.prisma.wallet.findUnique({ where: { userId: playerId } });
if (wallet) {
const available = new Decimal(wallet.availableBalance);
const frozen = new Decimal(wallet.frozenBalance);
if (available.gt(0) || frozen.gt(0)) {
throw appBadRequest('PLAYER_HAS_BALANCE');
}
}
return this.prisma.user.update({
where: { id: playerId },
data: { deletedAt: new Date(), status: 'SUSPENDED' },
});
}
async listAgentsAdmin(params?: {
page?: number;
pageSize?: number;
@@ -1696,9 +1724,18 @@ export class AgentsService {
return user;
}
async getPortalAgentDirectPlayers(rootAgentId: bigint, targetAgentId: bigint) {
async getPortalAgentDirectPlayers(
rootAgentId: bigint,
targetAgentId: bigint,
opts?: { page?: number; pageSize?: number },
) {
await this.assertDescendantAgent(rootAgentId, targetAgentId);
const players = await this.getDirectPlayers(targetAgentId);
const page = Math.max(1, opts?.page ?? 1);
const pageSize = Math.min(100, Math.max(1, opts?.pageSize ?? 20));
const { items: players, total } = await this.getDirectPlayers(targetAgentId, {
page,
pageSize,
});
const profile = await this.prisma.agentProfile.findUnique({
where: { userId: targetAgentId },
select: {
@@ -1714,7 +1751,7 @@ export class AgentsService {
players.map((p) => ({ id: BigInt(p.id), parentId: targetAgentId })),
parentCashbackMap,
);
return players.map((p) => ({
const mapped = players.map((p) => ({
...p,
parentAgentId: targetKey,
parentAgentUsername,
@@ -1722,18 +1759,30 @@ export class AgentsService {
inChain: true,
isDirect: targetKey === rootKey,
}));
return { items: mapped, total, page, pageSize };
}
async getDirectPlayers(agentId: bigint) {
const rows = await this.prisma.user.findMany({
where: { parentId: agentId, userType: 'PLAYER', deletedAt: null },
include: {
wallet: true,
usedInvite: { select: { code: true } },
},
orderBy: { createdAt: 'desc' },
});
return rows.map((u) => ({
async getDirectPlayers(
agentId: bigint,
opts?: { page?: number; pageSize?: number },
) {
const where = { parentId: agentId, userType: 'PLAYER' as const, deletedAt: null };
const page = Math.max(1, opts?.page ?? 1);
const pageSize = Math.min(100, Math.max(1, opts?.pageSize ?? 20));
const [total, rows] = await Promise.all([
this.prisma.user.count({ where }),
this.prisma.user.findMany({
where,
include: {
wallet: true,
usedInvite: { select: { code: true } },
},
orderBy: { createdAt: 'desc' },
skip: (page - 1) * pageSize,
take: pageSize,
}),
]);
const items = rows.map((u) => ({
id: u.id.toString(),
username: u.username,
status: u.status,
@@ -1746,6 +1795,7 @@ export class AgentsService {
}
: undefined,
}));
return { items, total, page, pageSize };
}
async getChildAgents(agentId: bigint) {