feat: multi-tier agent hierarchy, wallet ledger, and player UX polish
Add configurable agent max level and default sub-agent credit ratio, per-agent block direct player login on suspend, admin/agent wallet transaction views, and match detail my-bets section with refreshed player card styling. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -50,6 +50,7 @@ import {
|
||||
MinLength,
|
||||
IsIn,
|
||||
Min,
|
||||
Max,
|
||||
Equals,
|
||||
ValidateIf,
|
||||
} from 'class-validator';
|
||||
@@ -257,6 +258,19 @@ class AgentSuspendSettingsDto {
|
||||
suspendBlockPlayerLogin?: boolean;
|
||||
}
|
||||
|
||||
class AgentHierarchySettingsDto {
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
maxAgentLevel?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(1)
|
||||
@Max(100)
|
||||
defaultSubAgentCreditRatio?: number;
|
||||
}
|
||||
|
||||
class ResetDatabaseDto {
|
||||
@IsString()
|
||||
@Equals('RESET')
|
||||
@@ -340,6 +354,16 @@ class UpdateAgentAdminDto {
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
freezeDirectPlayers?: boolean;
|
||||
|
||||
/** 冻结时是否禁止直属玩家登录 */
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
blockDirectPlayerLogin?: boolean;
|
||||
|
||||
/** 解冻时是否级联解冻直属玩家 */
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
unfreezeDirectPlayers?: boolean;
|
||||
}
|
||||
|
||||
class DepositDto {
|
||||
@@ -945,6 +969,30 @@ export class AdminController {
|
||||
return jsonResponse(settings);
|
||||
}
|
||||
|
||||
@Get('agents/settings/hierarchy')
|
||||
@RequirePermissions(P.settings)
|
||||
async getAgentHierarchySettings() {
|
||||
const settings = await this.systemConfig.getAgentHierarchySettings();
|
||||
return jsonResponse(settings);
|
||||
}
|
||||
|
||||
@Put('agents/settings/hierarchy')
|
||||
@RequirePermissions(P.settings)
|
||||
async updateAgentHierarchySettings(
|
||||
@CurrentUser('id') operatorId: bigint,
|
||||
@Body() dto: AgentHierarchySettingsDto,
|
||||
) {
|
||||
const settings = await this.systemConfig.updateAgentHierarchySettings(dto);
|
||||
await this.audit.log({
|
||||
operatorId,
|
||||
operatorType: 'ADMIN',
|
||||
action: 'UPDATE_AGENT_HIERARCHY_SETTINGS',
|
||||
module: 'AGENTS',
|
||||
afterData: JSON.stringify(settings),
|
||||
});
|
||||
return jsonResponse(settings);
|
||||
}
|
||||
|
||||
@Get('settings/betting-limits')
|
||||
@RequirePermissions(P.settings)
|
||||
async getBettingLimits() {
|
||||
@@ -1122,6 +1170,13 @@ export class AdminController {
|
||||
);
|
||||
}
|
||||
|
||||
@Get('agents/level-counts')
|
||||
@RequirePermissions(P.agentsView)
|
||||
async getAgentLevelCounts() {
|
||||
const counts = await this.agents.countAgentsByLevel();
|
||||
return jsonResponse(counts);
|
||||
}
|
||||
|
||||
@Get('agents')
|
||||
@RequirePermissions(P.agentsView)
|
||||
async listAgents(
|
||||
@@ -1130,15 +1185,21 @@ export class AdminController {
|
||||
@Query('keyword') keyword?: string,
|
||||
@Query('status') status?: string,
|
||||
@Query('level') level?: string,
|
||||
@Query('minLevel') minLevel?: string,
|
||||
@Query('maxLevel') maxLevel?: string,
|
||||
@Query('parentAgentId') parentAgentId?: string,
|
||||
) {
|
||||
const parsedLevel = level === '2' ? 2 : level === '1' ? 1 : undefined;
|
||||
const parsedLevel = level != null && level !== '' ? parseInt(level, 10) : undefined;
|
||||
const parsedMinLevel = minLevel != null && minLevel !== '' ? parseInt(minLevel, 10) : undefined;
|
||||
const parsedMaxLevel = maxLevel != null && maxLevel !== '' ? parseInt(maxLevel, 10) : undefined;
|
||||
const result = await this.agents.listAgentsAdmin({
|
||||
page: page ? parseInt(page, 10) : 1,
|
||||
pageSize: pageSize ? parseInt(pageSize, 10) : 10,
|
||||
keyword,
|
||||
status,
|
||||
level: parsedLevel,
|
||||
level: Number.isFinite(parsedLevel) ? parsedLevel : undefined,
|
||||
minLevel: Number.isFinite(parsedMinLevel) ? parsedMinLevel : undefined,
|
||||
maxLevel: Number.isFinite(parsedMaxLevel) ? parsedMaxLevel : undefined,
|
||||
parentAgentId: parentAgentId ? BigInt(parentAgentId) : undefined,
|
||||
});
|
||||
return jsonResponse(result);
|
||||
@@ -1270,9 +1331,33 @@ export class AdminController {
|
||||
}
|
||||
|
||||
@Get('wallet/transactions')
|
||||
@RequirePermissions(P.walletDeposit, P.walletWithdraw)
|
||||
async walletTransactions(@Query('userId') userId: string, @Query('page') page?: string) {
|
||||
const result = await this.wallet.getTransactions(BigInt(userId), page ? parseInt(page) : 1);
|
||||
@RequirePermissions(P.walletDeposit, P.walletWithdraw, P.reports)
|
||||
async walletTransactions(
|
||||
@Query('page') page?: string,
|
||||
@Query('pageSize') pageSize?: string,
|
||||
@Query('playerId') playerId?: string,
|
||||
@Query('parentAgentId') parentAgentId?: string,
|
||||
@Query('parentAgentKeyword') parentAgentKeyword?: string,
|
||||
@Query('keyword') keyword?: string,
|
||||
@Query('operatorKeyword') operatorKeyword?: string,
|
||||
@Query('transactionType') transactionType?: string,
|
||||
@Query('typeCategory') typeCategory?: string,
|
||||
@Query('dateFrom') dateFrom?: string,
|
||||
@Query('dateTo') dateTo?: string,
|
||||
) {
|
||||
const result = await this.wallet.listWalletTransactionsAdmin({
|
||||
page: page ? parseInt(page, 10) : 1,
|
||||
pageSize: pageSize ? parseInt(pageSize, 10) : 20,
|
||||
playerId: playerId ? BigInt(playerId) : undefined,
|
||||
parentAgentId: parentAgentId ? BigInt(parentAgentId) : undefined,
|
||||
parentAgentKeyword,
|
||||
keyword,
|
||||
operatorKeyword,
|
||||
transactionType,
|
||||
typeCategory,
|
||||
dateFrom: dateFrom ? new Date(dateFrom) : undefined,
|
||||
dateTo: dateTo ? new Date(dateTo) : undefined,
|
||||
});
|
||||
return jsonResponse(result);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user