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

@@ -1,28 +1,101 @@
import axios from 'axios';
import router from './router';
import { clearStaffSession } from './stores/auth';
import { clearStaffSession, reconcileStaffSessionFromToken, useAuthStore } from './stores/auth';
import { ensureStaffSession, resetStaffSessionHydration } from './utils/session-hydrate';
const api = axios.create({ baseURL: '/api' });
let handling401 = false;
let handling403Portal = false;
const PORTAL_MISMATCH_MESSAGES = new Set(['Admin access only', 'Agent access only']);
function requestPath(config: { url?: string; baseURL?: string } | undefined): string {
if (!config?.url) return '';
const base = config.baseURL ?? '';
return `${base}${config.url}`;
}
api.interceptors.request.use((config) => {
const t = localStorage.getItem('manage_token');
if (t) config.headers.Authorization = `Bearer ${t}`;
reconcileStaffSessionFromToken();
const auth = useAuthStore();
const path = requestPath(config);
if (path.includes('/admin/') && auth.isAgent.value) {
return Promise.reject(
Object.assign(new Error('Agent portal blocked admin API'), {
isPortalMismatch: true,
blockedPath: path,
}),
);
}
if (path.includes('/agent/') && auth.isAdmin.value) {
return Promise.reject(
Object.assign(new Error('Admin portal blocked agent API'), {
isPortalMismatch: true,
blockedPath: path,
}),
);
}
return config;
});
api.interceptors.response.use(
(res) => res,
async (err) => {
if (err.isPortalMismatch) {
await ensureStaffSession();
if (router.currentRoute.value.path !== '/login') {
await router.replace('/');
}
return Promise.reject(err);
}
if (err.response?.status === 401 && !handling401) {
handling401 = true;
clearStaffSession();
resetStaffSessionHydration();
if (router.currentRoute.value.path !== '/login') {
await router.replace('/login');
}
handling401 = false;
}
if (
err.response?.status === 403 &&
!handling403Portal &&
PORTAL_MISMATCH_MESSAGES.has(err.response?.data?.message)
) {
handling403Portal = true;
await ensureStaffSession();
handling403Portal = false;
const auth = useAuthStore();
const path = requestPath(err.config);
const isAdminApi = path.includes('/admin/');
const isAgentApi = path.includes('/agent/');
if ((isAdminApi && auth.isAgent.value) || (isAgentApi && auth.isAdmin.value)) {
if (router.currentRoute.value.path !== '/login') {
await router.replace('/');
}
return Promise.reject(err);
}
if (err.config) {
return api.request(err.config);
}
clearStaffSession();
if (router.currentRoute.value.path !== '/login') {
await router.replace('/login');
}
}
return Promise.reject(err);
},
);