feat(admin,api,player): 代理层级管理、额度上下分与玩家钱包详情
新增代理管理器与二级代理体系,完善信用额度/上下分上下文与冻结策略;代理端玩家与子代理管理增强;玩家端新增钱包详情页与交易筛选优化。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Body,
|
||||
Param,
|
||||
Query,
|
||||
@@ -15,7 +16,7 @@ import { AgentsService } from '../../domains/agent/agents.service';
|
||||
import { WalletService } from '../../domains/ledger/wallet.service';
|
||||
import { BetsService } from '../../domains/betting/bets.service';
|
||||
import { PrismaService } from '../../shared/prisma/prisma.service';
|
||||
import { IsString, IsNumber, MinLength, IsOptional } from 'class-validator';
|
||||
import { IsString, IsNumber, MinLength, IsOptional, Min, IsBoolean } from 'class-validator';
|
||||
|
||||
class CreatePlayerDto {
|
||||
@IsString()
|
||||
@@ -24,12 +25,71 @@ class CreatePlayerDto {
|
||||
@IsString()
|
||||
@MinLength(8)
|
||||
password!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
email?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
locale?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
initialDeposit?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
class CreateSubAgentDto extends CreatePlayerDto {
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
creditLimit?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
cashbackRate?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
maxSingleDeposit?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
maxDailyDeposit?: number;
|
||||
}
|
||||
|
||||
class UpdatePlayerDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
username?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MinLength(8)
|
||||
password?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
email?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
}
|
||||
|
||||
class TransferDto {
|
||||
@@ -46,6 +106,33 @@ class CreditDto extends TransferDto {
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
class UpdateSubAgentDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
username?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MinLength(8)
|
||||
password?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
email?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
freezeDirectPlayers?: boolean;
|
||||
}
|
||||
|
||||
@ApiTags('Agent Portal')
|
||||
@Controller('agent')
|
||||
@UseGuards(JwtAuthGuard, AgentGuard)
|
||||
@@ -76,8 +163,57 @@ export class AgentPortalController {
|
||||
username: dto.username,
|
||||
password: dto.password,
|
||||
parentId: agentId,
|
||||
locale: dto.locale,
|
||||
phone: dto.phone,
|
||||
email: dto.email,
|
||||
});
|
||||
return jsonResponse(user);
|
||||
|
||||
if (dto.initialDeposit != null && dto.initialDeposit > 0) {
|
||||
await this.agents.depositToPlayer(
|
||||
agentId,
|
||||
user.id,
|
||||
dto.initialDeposit,
|
||||
`agent-create-${user.id}-${Date.now()}`,
|
||||
dto.remark ?? '开户初始余额',
|
||||
);
|
||||
}
|
||||
|
||||
const wallet = await this.prisma.wallet.findUnique({ where: { userId: user.id } });
|
||||
|
||||
return jsonResponse({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
status: user.status,
|
||||
createdAt: user.createdAt,
|
||||
availableBalance: wallet?.availableBalance ?? 0,
|
||||
});
|
||||
}
|
||||
|
||||
@Get('players/:id')
|
||||
async getPlayer(@CurrentUser('id') agentId: bigint, @Param('id') playerId: string) {
|
||||
const detail = await this.agents.getDirectPlayerDetail(agentId, BigInt(playerId));
|
||||
return jsonResponse(detail);
|
||||
}
|
||||
|
||||
@Get('players/:id/transfer-context')
|
||||
async getPlayerTransferContext(
|
||||
@CurrentUser('id') agentId: bigint,
|
||||
@Param('id') playerId: string,
|
||||
) {
|
||||
const ctx = await this.agents.getPlayerTransferContext(BigInt(playerId), {
|
||||
actingAgentId: agentId,
|
||||
});
|
||||
return jsonResponse(ctx);
|
||||
}
|
||||
|
||||
@Put('players/:id')
|
||||
async updatePlayer(
|
||||
@CurrentUser('id') agentId: bigint,
|
||||
@Param('id') playerId: string,
|
||||
@Body() dto: UpdatePlayerDto,
|
||||
) {
|
||||
const detail = await this.agents.updateDirectPlayer(agentId, BigInt(playerId), dto);
|
||||
return jsonResponse(detail);
|
||||
}
|
||||
|
||||
@Get('agents')
|
||||
@@ -85,7 +221,7 @@ export class AgentPortalController {
|
||||
if (level !== 1) {
|
||||
return jsonResponse([]);
|
||||
}
|
||||
const agents = await this.agents.getChildAgents(agentId);
|
||||
const agents = await this.agents.listChildAgentsSummary(agentId);
|
||||
return jsonResponse(agents);
|
||||
}
|
||||
|
||||
@@ -100,6 +236,9 @@ export class AgentPortalController {
|
||||
level: 2,
|
||||
parentAgentId: agentId,
|
||||
creditLimit: dto.creditLimit,
|
||||
cashbackRate: dto.cashbackRate,
|
||||
maxSingleDeposit: dto.maxSingleDeposit,
|
||||
maxDailyDeposit: dto.maxDailyDeposit,
|
||||
});
|
||||
return jsonResponse(user);
|
||||
}
|
||||
@@ -124,6 +263,47 @@ export class AgentPortalController {
|
||||
return jsonResponse(result);
|
||||
}
|
||||
|
||||
@Get('agents/:id')
|
||||
async getSubAgent(
|
||||
@CurrentUser('id') agentId: bigint,
|
||||
@CurrentUser('agentLevel') level: number,
|
||||
@Param('id') subAgentId: string,
|
||||
) {
|
||||
if (level !== 1) {
|
||||
return jsonResponse(null, 'Only level 1 agents can manage sub-agents');
|
||||
}
|
||||
const detail = await this.agents.getSubAgentForParent(agentId, BigInt(subAgentId));
|
||||
return jsonResponse(detail);
|
||||
}
|
||||
|
||||
@Put('agents/:id')
|
||||
async updateSubAgent(
|
||||
@CurrentUser('id') agentId: bigint,
|
||||
@CurrentUser('agentLevel') level: number,
|
||||
@Param('id') subAgentId: string,
|
||||
@Body() dto: UpdateSubAgentDto,
|
||||
) {
|
||||
if (level !== 1) {
|
||||
return jsonResponse(null, 'Only level 1 agents can manage sub-agents');
|
||||
}
|
||||
const detail = await this.agents.updateSubAgentForParent(agentId, BigInt(subAgentId), dto);
|
||||
return jsonResponse(detail);
|
||||
}
|
||||
|
||||
@Get('agents/:id/players')
|
||||
async listSubAgentPlayers(
|
||||
@CurrentUser('id') agentId: bigint,
|
||||
@CurrentUser('agentLevel') level: number,
|
||||
@Param('id') subAgentId: string,
|
||||
) {
|
||||
if (level !== 1) {
|
||||
return jsonResponse([]);
|
||||
}
|
||||
await this.agents.assertDirectChildAgent(agentId, BigInt(subAgentId));
|
||||
const players = await this.agents.getDirectPlayers(BigInt(subAgentId));
|
||||
return jsonResponse(players);
|
||||
}
|
||||
|
||||
@Post('agents/:id/credit')
|
||||
async allocateCredit(
|
||||
@CurrentUser('id') agentId: bigint,
|
||||
|
||||
Reference in New Issue
Block a user