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,79 @@
import {
reconcileStaffSessionFromToken,
useAuthStore,
type StaffUser,
type StaffUserType,
} from '../stores/auth';
let hydratePromise: Promise<boolean> | null = null;
function isStaffUserType(value: unknown): value is StaffUserType {
return value === 'ADMIN' || value === 'AGENT';
}
export function resetStaffSessionHydration() {
hydratePromise = null;
}
function hasCompleteStaffUser(u: StaffUser | null | undefined): u is StaffUser {
return !!(u?.id && u.username && u.userType);
}
/** Sync manage_user from JWT + /manage/auth/me (fixes stale localStorage userType). */
export async function hydrateStaffSession(): Promise<boolean> {
const auth = useAuthStore();
if (!auth.token.value) return false;
if (hydratePromise) return hydratePromise;
hydratePromise = (async () => {
reconcileStaffSessionFromToken();
if (!hasCompleteStaffUser(auth.user.value)) {
auth.clearStaffSession();
return false;
}
try {
const { default: api } = await import('../api');
const { data } = await api.get('/manage/auth/me');
const raw = data.data as Partial<StaffUser>;
if (!raw?.id || !raw.username || !isStaffUserType(raw.userType)) {
return true;
}
auth.setSession(auth.token.value, {
id: raw.id,
username: raw.username,
userType: raw.userType,
locale: raw.locale,
role: raw.role,
agentLevel: typeof raw.agentLevel === 'number' ? raw.agentLevel : null,
});
return true;
} catch (e: unknown) {
const status = (e as { response?: { status?: number } })?.response?.status;
if (status === 401) {
auth.clearStaffSession();
return false;
}
return hasCompleteStaffUser(auth.user.value);
} finally {
hydratePromise = null;
}
})();
return hydratePromise;
}
/** Run before any authenticated route — JWT reconcile + optional /me refresh. */
export async function ensureStaffSession(): Promise<boolean> {
reconcileStaffSessionFromToken();
const auth = useAuthStore();
if (!auth.token.value) return false;
if (!hasCompleteStaffUser(auth.user.value)) {
reconcileStaffSessionFromToken();
}
if (!hasCompleteStaffUser(auth.user.value)) {
return false;
}
return hydrateStaffSession();
}