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

@@ -0,0 +1,151 @@
import { FormValidationError } from '../../i18n/form-validation';
export interface AgentSubAgentRow {
userId: string;
username: string;
userStatus: string;
status: string;
level: number;
creditLimit: string;
usedCredit: string;
availableCredit: string;
directPlayerCount: number;
createdAt: string;
}
export interface AgentSubAgentDetail {
userId: string;
username: string;
userStatus: string;
status: string;
level: number;
creditLimit: string;
usedCredit: string;
availableCredit: string;
directPlayerCount: number;
phone: string | null;
email: string | null;
managedPassword: string | null;
loginFailCount: number;
lastLoginAt: string | null;
}
export interface AgentSubAgentEditForm {
userId: string;
username: string;
status: string;
phone: string;
email: string;
managedPassword: string | null;
newPassword: string;
creditLimit: string;
usedCredit: string;
availableCredit: string;
directPlayerCount: number;
loginFailCount: number;
lastLoginAt: string | null;
level: number;
}
export interface AgentSubAgentCreateForm {
username: string;
password: string;
confirmPassword: string;
creditLimit: number;
cashbackRate: number;
maxSingleDeposit: number;
maxDailyDeposit: number;
}
export function emptyAgentSubAgentCreateForm(): AgentSubAgentCreateForm {
return {
username: '',
password: 'Agent@123',
confirmPassword: 'Agent@123',
creditLimit: 10000,
cashbackRate: 0,
maxSingleDeposit: 0,
maxDailyDeposit: 0,
};
}
export function buildAgentSubAgentCreatePayload(form: AgentSubAgentCreateForm) {
if (!form.username.trim()) throw new FormValidationError('err.username_required');
if (form.password.length < 8) throw new FormValidationError('err.password_min');
if (form.password !== form.confirmPassword) throw new FormValidationError('err.password_mismatch');
if (form.creditLimit < 0) throw new FormValidationError('err.amount_negative');
return {
username: form.username.trim(),
password: form.password,
creditLimit: form.creditLimit,
cashbackRate: form.cashbackRate,
maxSingleDeposit: form.maxSingleDeposit > 0 ? form.maxSingleDeposit : undefined,
maxDailyDeposit: form.maxDailyDeposit > 0 ? form.maxDailyDeposit : undefined,
};
}
export function emptyAgentSubAgentEditForm(): AgentSubAgentEditForm {
return {
userId: '',
username: '',
status: 'ACTIVE',
phone: '',
email: '',
managedPassword: null,
newPassword: '',
creditLimit: '0',
usedCredit: '0',
availableCredit: '0',
directPlayerCount: 0,
loginFailCount: 0,
lastLoginAt: null,
level: 2,
};
}
export function editFormFromSubAgentDetail(d: AgentSubAgentDetail): AgentSubAgentEditForm {
return {
userId: d.userId,
username: d.username,
status: d.userStatus ?? d.status,
phone: d.phone ?? '',
email: d.email ?? '',
managedPassword: d.managedPassword ?? null,
newPassword: '',
creditLimit: d.creditLimit,
usedCredit: d.usedCredit,
availableCredit: d.availableCredit,
directPlayerCount: d.directPlayerCount,
loginFailCount: d.loginFailCount ?? 0,
lastLoginAt: d.lastLoginAt ?? null,
level: d.level,
};
}
export function buildAgentSubAgentUpdatePayload(form: AgentSubAgentEditForm) {
if (!form.username.trim()) throw new FormValidationError('err.username_required');
const payload: {
username: string;
phone?: string;
email?: string;
status: string;
password?: string;
} = {
username: form.username.trim(),
status: form.status,
phone: form.phone.trim() || undefined,
email: form.email.trim() || undefined,
};
const newPwd = form.newPassword.trim();
if (newPwd) {
if (newPwd.length < 8) throw new FormValidationError('err.password_min');
payload.password = newPwd;
}
return payload;
}
/** Prefer account lock status over profile status for list/actions. */
export function subAgentAccountStatus(row: Pick<AgentSubAgentRow, 'userStatus' | 'status'>) {
return row.userStatus ?? row.status;
}