import { APP_CONFIG } from '@/config'; import { ensureLogin } from '@/utils/auth'; import { HttpMethod, httpRequest, toFormUrlEncoded } from '@/utils/http'; import { authStorage } from '@/utils/storage'; export interface RequestOptions { method?: HttpMethod; data?: unknown; header?: Record; skipAuth?: boolean; skipAutoLogin?: boolean; } export class RequestError extends Error { constructor( message: string, public readonly statusCode?: number, public readonly data?: unknown ) { super(message); this.name = 'RequestError'; } } const objectToQueryString = (data: object = {}) => { return Object.entries(data) .filter(([, value]) => value !== undefined && value !== null) .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`) .join('&'); }; const buildUrl = (endpoint: string) => { if (/^https?:\/\//.test(endpoint)) return endpoint; return `${APP_CONFIG.API_BASE_URL}/${endpoint.replace(/^\//, '')}`; }; const isAuthExpired = (statusCode: number, body: any) => { const businessCode = Number(body?.code); return statusCode === 401 || statusCode === 402 || businessCode === 401 || businessCode === 402; }; const requestOnce = async (url: string, options: RequestOptions, retried: boolean): Promise => { const headers: Record = { 'content-type': 'application/json', ...options.header }; if (!options.skipAuth) { const token = authStorage.getToken(); if (token) headers.Authorization = `Bearer ${token}`; } const response = await httpRequest({ url, method: options.method || 'GET', data: options.data, header: headers }); if (isAuthExpired(response.statusCode, response.data)) { if (!options.skipAutoLogin && !options.skipAuth && !retried) { authStorage.clearToken(); await ensureLogin(); return requestOnce(url, options, true); } throw new RequestError('登录状态已失效', response.statusCode, response.data); } if (response.statusCode < 200 || response.statusCode >= 300) { throw new RequestError(`HTTP 请求失败:${response.statusCode}`, response.statusCode, response.data); } if (Number((response.data as any)?.code) === 500) { throw new RequestError((response.data as any)?.msg || '服务器处理失败', response.statusCode, response.data); } return response.data; }; export const request = (endpoint: string, options: RequestOptions = {}) => { return requestOnce(buildUrl(endpoint), options, false); }; export const get = (endpoint: string, params?: object, options: RequestOptions = {}) => { const queryString = params ? `?${objectToQueryString(params)}` : ''; return request(`${endpoint}${queryString}`, { ...options, method: 'GET' }); }; export const post = (endpoint: string, data?: unknown, options: RequestOptions = {}) => { return request(endpoint, { ...options, method: 'POST', data }); }; export const postForm = (endpoint: string, data?: Record, options: RequestOptions = {}) => { return request(endpoint, { ...options, method: 'POST', data: toFormUrlEncoded(data), header: { ...options.header, 'content-type': 'application/x-www-form-urlencoded' } }); }; export const put = (endpoint: string, data?: unknown, options: RequestOptions = {}) => { return request(endpoint, { ...options, method: 'PUT', data }); }; export const del = (endpoint: string, data?: unknown, options: RequestOptions = {}) => { return request(endpoint, { ...options, method: 'DELETE', data }); };