feat(admin): 从已有玩家升级代理、修复 i18n 与过期 .js 冲突

- 新建一级代理改为选择已有玩家;新建用户可选一级代理

- 操作日志/注单等扁平 key 翻译;清理 src 内误生成 .js,Vite 优先解析 .ts

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-03 15:42:15 +08:00
parent cbfa18d1d3
commit 3b739982a1
27 changed files with 625 additions and 165 deletions

View File

@@ -358,6 +358,113 @@ export class AgentsService {
return this.getAgentAdminDetail(agentId);
}
/** 可升级为一级代理的玩家(尚无代理档案) */
async listPromotablePlayers(keyword?: string) {
const q = keyword?.trim();
return this.prisma.user.findMany({
where: {
userType: 'PLAYER',
deletedAt: null,
agentProfile: null,
...(q
? { username: { contains: q, mode: 'insensitive' } }
: {}),
},
select: {
id: true,
username: true,
status: true,
parentId: true,
preferences: { select: { phone: true, email: true } },
parent: { select: { username: true } },
},
orderBy: { id: 'desc' },
take: 50,
});
}
/** 将已有玩家账号升级为一级代理(不新建用户) */
async promotePlayerToTier1Agent(
userId: bigint,
data: {
creditLimit: number;
cashbackRate?: number;
phone?: string;
email?: string;
},
) {
const user = await this.prisma.user.findUnique({
where: { id: userId },
include: { agentProfile: true, preferences: true },
});
if (!user || user.deletedAt) {
throw new NotFoundException('用户不存在');
}
if (user.userType !== 'PLAYER') {
throw new BadRequestException('仅玩家账号可设为代理');
}
if (user.agentProfile) {
throw new BadRequestException('该用户已是代理');
}
const oldParentId = user.parentId;
const phone =
data.phone !== undefined
? data.phone.trim() || null
: user.preferences?.phone ?? null;
const email =
data.email !== undefined
? data.email.trim() || null
: user.preferences?.email ?? null;
await this.prisma.$transaction(async (tx) => {
await tx.user.update({
where: { id: userId },
data: {
userType: 'AGENT',
agentLevel: 1,
parentId: null,
},
});
if (user.preferences) {
await tx.userPreference.update({
where: { userId },
data: { phone, email },
});
} else {
await tx.userPreference.create({
data: {
userId,
locale: user.locale,
phone,
email,
},
});
}
await tx.agentProfile.create({
data: {
userId,
level: 1,
parentAgentId: null,
creditLimit: data.creditLimit,
cashbackRate: data.cashbackRate ?? 0,
},
});
await tx.agentClosure.create({
data: { ancestorId: userId, descendantId: userId, depth: 0 },
});
});
if (oldParentId) {
await this.recalculateUsedCredit(oldParentId);
}
return this.prisma.user.findUnique({ where: { id: userId } });
}
async createAgent(
operatorId: bigint,
data: {
@@ -455,8 +562,30 @@ export class AgentsService {
initialDeposit?: number;
depositRemark?: string;
depositRequestId?: string;
asTier1Agent?: boolean;
creditLimit?: number;
cashbackRate?: number;
},
) {
if (data.asTier1Agent) {
if (data.parentId != null) {
throw new BadRequestException('一级代理不可设置上级玩家');
}
if (data.initialDeposit && data.initialDeposit > 0) {
throw new BadRequestException('设为代理时请使用授信额度,勿填玩家初始余额');
}
return this.createAgent(operatorId, {
username: data.username,
password: data.password,
level: 1,
creditLimit: data.creditLimit ?? 0,
cashbackRate: data.cashbackRate ?? 0,
locale: data.locale,
phone: data.phone,
email: data.email,
});
}
let parentId: bigint | null = null;
if (data.parentId != null) {
const parent = await this.prisma.user.findUnique({ where: { id: data.parentId } });