feat(admin,api,player): 代理层级管理、额度上下分与玩家钱包详情

新增代理管理器与二级代理体系,完善信用额度/上下分上下文与冻结策略;代理端玩家与子代理管理增强;玩家端新增钱包详情页与交易筛选优化。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-08 15:34:12 +08:00
parent b2216abd0c
commit 414998ce36
54 changed files with 6641 additions and 481 deletions

View File

@@ -101,6 +101,15 @@ class CreatePlayerAdminDto {
@IsOptional()
asTier1Agent?: boolean;
/** 创建为二级代理(需要 parentAgentId */
@IsOptional()
asSubAgent?: boolean;
/** 二级代理的上级代理 ID */
@IsOptional()
@IsString()
parentAgentId?: string;
@IsOptional()
@IsNumber()
@Min(0)
@@ -110,6 +119,16 @@ class CreatePlayerAdminDto {
@IsNumber()
@Min(0)
cashbackRate?: number;
@IsOptional()
@IsNumber()
@Min(0)
maxSingleDeposit?: number;
@IsOptional()
@IsNumber()
@Min(0)
maxDailyDeposit?: number;
}
class UpdatePlayerAdminDto {
@@ -154,6 +173,16 @@ class PlayerAccountSettingsDto {
allowUsernameChange?: boolean;
}
class AgentSuspendSettingsDto {
@IsOptional()
@IsBoolean()
suspendFreezeDirectPlayers?: boolean;
@IsOptional()
@IsBoolean()
suspendBlockPlayerLogin?: boolean;
}
class ResetDatabaseDto {
@IsString()
@Equals('RESET')
@@ -181,6 +210,16 @@ class CreateAgentAdminDto {
@IsNumber()
@Min(0)
cashbackRate?: number;
@IsOptional()
@IsNumber()
@Min(0)
maxSingleDeposit?: number;
@IsOptional()
@IsNumber()
@Min(0)
maxDailyDeposit?: number;
}
class UpdateAgentAdminDto {
@@ -204,6 +243,29 @@ class UpdateAgentAdminDto {
@IsNumber()
@Min(0)
cashbackRate?: number;
@IsOptional()
@IsNumber()
@Min(0)
maxSingleDeposit?: number;
@IsOptional()
@IsNumber()
@Min(0)
maxDailyDeposit?: number;
@IsOptional()
@IsString()
username?: string;
@IsOptional()
@IsString()
password?: string;
/** 冻结时是否级联冻结直属玩家 */
@IsOptional()
@IsBoolean()
freezeDirectPlayers?: boolean;
}
class DepositDto {
@@ -717,6 +779,30 @@ export class AdminController {
return jsonResponse(settings);
}
@Get('agents/settings/suspend')
@RequirePermissions(P.settings)
async getAgentSuspendSettings() {
const settings = await this.systemConfig.getAgentSuspendSettings();
return jsonResponse(settings);
}
@Put('agents/settings/suspend')
@RequirePermissions(P.settings)
async updateAgentSuspendSettings(
@CurrentUser('id') operatorId: bigint,
@Body() dto: AgentSuspendSettingsDto,
) {
const settings = await this.systemConfig.updateAgentSuspendSettings(dto);
await this.audit.log({
operatorId,
operatorType: 'ADMIN',
action: 'UPDATE_AGENT_SUSPEND_SETTINGS',
module: 'AGENTS',
afterData: JSON.stringify(settings),
});
return jsonResponse(settings);
}
@Get('settings/betting-limits')
@RequirePermissions(P.settings)
async getBettingLimits() {
@@ -830,17 +916,21 @@ export class AdminController {
depositRemark: dto.remark,
depositRequestId: `create-player-${dto.username}-${Date.now()}`,
asTier1Agent: dto.asTier1Agent,
asSubAgent: dto.asSubAgent,
parentAgentId: dto.parentAgentId ? BigInt(dto.parentAgentId) : undefined,
creditLimit: dto.creditLimit,
cashbackRate: dto.cashbackRate,
maxSingleDeposit: dto.maxSingleDeposit,
maxDailyDeposit: dto.maxDailyDeposit,
});
await this.audit.log({
operatorId,
operatorType: 'ADMIN',
action: dto.asTier1Agent ? 'CREATE_AGENT' : 'CREATE_PLAYER',
module: dto.asTier1Agent ? 'AGENTS' : 'USERS',
action: dto.asTier1Agent || dto.asSubAgent ? 'CREATE_AGENT' : 'CREATE_PLAYER',
module: dto.asTier1Agent || dto.asSubAgent ? 'AGENTS' : 'USERS',
targetId: user.id.toString(),
});
if (dto.asTier1Agent) {
if (dto.asTier1Agent || dto.asSubAgent) {
const detail = await this.agents.getAgentAdminDetail(user.id);
return jsonResponse(detail);
}
@@ -884,11 +974,13 @@ export class AdminController {
@Query('page') page?: string,
@Query('pageSize') pageSize?: string,
@Query('keyword') keyword?: string,
@Query('parentAgentId') parentAgentId?: string,
) {
const result = await this.agents.listAgentsAdmin({
page: page ? parseInt(page, 10) : 1,
pageSize: pageSize ? parseInt(pageSize, 10) : 10,
keyword,
parentAgentId: parentAgentId ? BigInt(parentAgentId) : undefined,
});
return jsonResponse(result);
}
@@ -929,6 +1021,8 @@ export class AdminController {
phone: dto.phone,
email: dto.email,
cashbackRate: dto.cashbackRate,
maxSingleDeposit: dto.maxSingleDeposit,
maxDailyDeposit: dto.maxDailyDeposit,
});
await this.audit.log({
operatorId,
@@ -961,7 +1055,7 @@ export class AdminController {
@Post('wallet/deposit')
@RequirePermissions(P.walletDeposit)
async deposit(@CurrentUser('id') operatorId: bigint, @Body() dto: DepositDto & { userId: string }) {
const result = await this.wallet.deposit(
const result = await this.agents.adminDepositToPlayer(
BigInt(dto.userId),
dto.amount,
operatorId,
@@ -971,6 +1065,13 @@ export class AdminController {
return jsonResponse(result);
}
@Get('wallet/transfer-context/:userId')
@RequirePermissions(P.walletDeposit, P.walletWithdraw)
async walletTransferContext(@Param('userId') userId: string) {
const ctx = await this.agents.getPlayerTransferContext(BigInt(userId), { forAdmin: true });
return jsonResponse(ctx);
}
@Post('wallet/withdraw')
@RequirePermissions(P.walletWithdraw)
async withdraw(@CurrentUser('id') operatorId: bigint, @Body() dto: DepositDto & { userId: string }) {