import { RequestConfig } from './utils/umiExport'; import { getProxyUrl } from './utils/androidUtil'; import { message } from 'antd'; import EncryptUtil from './utils/EncryptUtil'; interface ResponseStructure { code: number; data: object; msg: string; error: boolean; success: boolean; } const { NODE_ENV } = process.env; /** * 需要加密的接口路径前缀,与 Vue2 基座保持一致 * - 仅对 POST 请求生效 * - 排除登录接口、openapi、devApi */ const ENCRYPT_URL_PREFIX = 'api/af-'; const EXCLUDE_URLS = ['/logic/openapi/', 'devApi', 'auth/login']; /** * 判断当前请求是否需要 v4-session-key CBC 加密 */ function shouldEncrypt(url: string, method: string): boolean { if (method.toLowerCase() !== 'post') return false; if (!url.includes(ENCRYPT_URL_PREFIX)) return false; return !EXCLUDE_URLS.some((ex) => url.includes(ex)); } const errorConfig: RequestConfig = { timeout: 60000, errorConfig: { errorThrower(res: ResponseStructure) { let settings: any = localStorage.getItem('defaultSettings'); if (settings) { settings = JSON.parse(settings); } console.error('请求异常 errorThrower', res); if (settings?.version === 'V4') { const { success, data, code, msg, error } = res as ResponseStructure; if (!success) { const errorCore: any = new Error(msg); errorCore.name = 'BizError'; errorCore.info = { code, msg, success, data, error }; throw errorCore; } } else { const { success, data, msg } = res as ResponseStructure; if (!success) { const errorCore: any = new Error(msg || data.toString()); errorCore.name = 'BizError'; errorCore.info = { code: 500, msg: msg || data, success: false, data: data || msg, error: true, }; throw errorCore; } } }, errorHandler(error: any, opts: any) { if (opts?.skipErrorHandler) throw error; if (error.name === 'BizError') { const errorInfo: ResponseStructure | undefined = error.info; if (errorInfo) { message.error(errorInfo.msg); } } else if (error.response) { message.error(`服务器内部错误:${error.response.status}`); } else if (error.request) { message.error('服务器无响应,请重试'); } else { message.error('请求错误,请重试'); } }, }, // ============ 请求拦截器 ============ requestInterceptors: [ // 1. Token 注入 + baseURL 处理 (url, options) => { console.log('>>>> 请求拦截器1 ', url, options); const token = localStorage.getItem('token'); if (token && !url.includes('auth/login')) { options.headers = { ...options.headers, Authorization: token }; } if (NODE_ENV !== 'development') { options.baseURL = getProxyUrl(); } return { url, options }; }, // 2. AES CBC 加密拦截器(对应 Vue2 基座的 v4SessionKey 加密逻辑) (url, options) => { console.log('>>>> 请求拦截器2 ', url, options); const v4SessionKey = localStorage.getItem('v4-session-key'); const method = (options.method as string) || 'GET'; if (v4SessionKey && shouldEncrypt(url, method)) { const body = options.data; // 跳过 FormData(文件上传等场景) if (body && !(body instanceof FormData)) { options.data = { encrypted: EncryptUtil.AESEncryptCBC(body, v4SessionKey), }; options.headers = { ...options.headers, 'X-Sec': '1', 'X-Rand': Math.random().toString(36).substring(2, 7), 'X-Ts': String(Date.now()), 'Content-Type': 'application/json;charset=UTF-8', }; } } return { url, options }; }, // 3. 兜底错误处理 [ (url, options) => ({ url, options }), (error: Error) => Promise.reject(error), ], ], // ============ 响应拦截器 ============ responseInterceptors: [ // 1. AES CBC 解密拦截器(响应头携带 x-encrypted: 1 时触发) (response) => { const headers = response.headers as Record; if (headers?.['x-encrypted'] === '1') { const v4SessionKey = localStorage.getItem('v4-session-key'); if (v4SessionKey && response.data) { // @ts-ignore const decrypted = EncryptUtil.decryptResponse(response.data, v4SessionKey); if (decrypted !== response.data) { response.data = decrypted; } } } return response; }, // 2. V4 业务数据解包(data.data 展开) (response) => { let settings: any = localStorage.getItem('defaultSettings'); if (settings) { settings = JSON.parse(settings); } console.info('响应拦截器 V4 解包', response); if (settings?.version === 'V4') { const { data = {} as any } = response; if (data?.data !== undefined) { response.data = data.data; } } return response; }, // 3. 兜底错误处理 [ (response) => response, (error: Error) => { console.info('响应拦截器 ERROR', error); return Promise.reject(error); }, ], ], }; export default errorConfig;