feat: 手动充值、邀请码注册与后台管理增强
新增玩家手动充值全流程(收款方式配置、充值下单/审核、钱包上分), 支持邀请码注册、邀请历史与专属返水率;完善后台代理/玩家管理与响应式操作栏, 并补充前台注册、充值页及多语言错误码。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,7 +4,9 @@ import { SUPPORTED_LOCALES, isValidAvatarKey, assertPlayerUsername } from '@theb
|
||||
import { PrismaService } from '../../shared/prisma/prisma.service';
|
||||
import { SystemConfigService } from '../../shared/config/system-config.service';
|
||||
import { AgentsService } from '../agent/agents.service';
|
||||
import { CashbackService } from '../operations/cashback/cashback.service';
|
||||
import { appBadRequest, appForbidden, appNotFound } from '../../shared/common/app-error';
|
||||
import { Decimal } from '@prisma/client/runtime/library';
|
||||
|
||||
export type PlayerListFilters = {
|
||||
keyword?: string;
|
||||
@@ -19,6 +21,7 @@ export class UsersService {
|
||||
private prisma: PrismaService,
|
||||
private agents: AgentsService,
|
||||
private systemConfig: SystemConfigService,
|
||||
private cashback: CashbackService,
|
||||
) {}
|
||||
|
||||
private buildAffiliationAgents(
|
||||
@@ -103,6 +106,7 @@ export class UsersService {
|
||||
parent?: { username: string; agentLevel: number | null } | null;
|
||||
} | null;
|
||||
auth?: { lastLoginAt: Date | null } | null;
|
||||
usedInvite?: { code: string } | null;
|
||||
},
|
||||
bet?: { count: number; totalStake: string; totalReturn: string },
|
||||
affiliationChain?: string[],
|
||||
@@ -116,6 +120,7 @@ export class UsersService {
|
||||
parentId: u.parentId?.toString() ?? null,
|
||||
parentUsername: u.parent?.username ?? null,
|
||||
affiliationAgents,
|
||||
inviteCode: u.usedInvite?.code ?? null,
|
||||
phone: u.preferences?.phone ?? null,
|
||||
email: u.preferences?.email ?? null,
|
||||
managedPassword: u.preferences?.managedPassword ?? null,
|
||||
@@ -281,6 +286,7 @@ export class UsersService {
|
||||
},
|
||||
},
|
||||
auth: { select: { lastLoginAt: true } },
|
||||
usedInvite: { select: { code: true } },
|
||||
},
|
||||
skip,
|
||||
take: pageSize,
|
||||
@@ -320,17 +326,23 @@ export class UsersService {
|
||||
},
|
||||
},
|
||||
auth: { select: { lastLoginAt: true, loginFailCount: true, lockedUntil: true } },
|
||||
usedInvite: { select: { code: true } },
|
||||
},
|
||||
});
|
||||
if (!user) throw appNotFound('PLAYER_NOT_FOUND');
|
||||
|
||||
const affiliationMap = await this.buildAffiliationChainMap([user.parentId]);
|
||||
const [betCount, betStake] = await Promise.all([
|
||||
const [betCount, betStake, customCashbackRate, defaultCashbackRate] = await Promise.all([
|
||||
this.prisma.bet.count({ where: { userId: playerId } }),
|
||||
this.prisma.bet.aggregate({
|
||||
where: { userId: playerId },
|
||||
_sum: { stake: true, actualReturn: true },
|
||||
}),
|
||||
this.cashback.getPlayerCustomCashbackRate(playerId),
|
||||
this.cashback.resolvePlayerDefaultCashbackRate({
|
||||
parentId: user.parentId,
|
||||
inviteSponsorId: user.inviteSponsorId,
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
@@ -345,6 +357,8 @@ export class UsersService {
|
||||
betCount,
|
||||
totalStake: betStake._sum.stake?.toString() ?? '0',
|
||||
totalReturn: betStake._sum.actualReturn?.toString() ?? '0',
|
||||
customCashbackRate: customCashbackRate?.toString() ?? null,
|
||||
defaultCashbackRate: defaultCashbackRate.toString(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -357,6 +371,7 @@ export class UsersService {
|
||||
email?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
cashbackRate?: number | null;
|
||||
},
|
||||
) {
|
||||
const user = await this.prisma.user.findFirst({
|
||||
@@ -440,6 +455,17 @@ export class UsersService {
|
||||
});
|
||||
}
|
||||
|
||||
if (data.cashbackRate !== undefined) {
|
||||
if (data.cashbackRate != null && (!Number.isFinite(data.cashbackRate) || data.cashbackRate < 0)) {
|
||||
throw appBadRequest('CASHBACK_RATE_NEGATIVE');
|
||||
}
|
||||
const rate =
|
||||
data.cashbackRate != null && data.cashbackRate > 0
|
||||
? new Decimal(data.cashbackRate)
|
||||
: null;
|
||||
await this.cashback.setPlayerCustomCashbackRate(playerId, rate);
|
||||
}
|
||||
|
||||
return this.getPlayerAdminDetail(playerId);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user