import axios from 'axios'; const client = axios.create({ baseURL: '/api/v1', timeout: 10000, headers: { 'Content-Type': 'application/json' }, }); // 请求缓存:短时间内相同请求复用结果 interface CacheEntry { data: unknown; timestamp: number; } const requestCache = new Map(); const CACHE_TTL = 5000; // 5 秒缓存 function getCacheKey(config: { url?: string; params?: unknown; method?: string }): string { return `${config.method || 'get'}:${config.url || ''}:${JSON.stringify(config.params || {})}`; } // Request interceptor: attach access token + cache client.interceptors.request.use((config) => { const token = localStorage.getItem('access_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } // GET 请求检查缓存 if (config.method === 'get' && config.url) { const key = getCacheKey(config); const entry = requestCache.get(key); if (entry && Date.now() - entry.timestamp < CACHE_TTL) { const source = axios.CancelToken.source(); config.cancelToken = source.token; // 通过适配器返回缓存数据 source.cancel(JSON.stringify({ __cached: true, data: entry.data })); } } return config; }); // 响应拦截器:缓存 GET 响应 + 自动刷新 token client.interceptors.response.use( (response) => { // 缓存 GET 响应 if (response.config.method === 'get' && response.config.url) { const key = getCacheKey(response.config); requestCache.set(key, { data: response.data, timestamp: Date.now() }); } return response; }, async (error) => { // 处理缓存命中 if (axios.isCancel(error)) { const cached = JSON.parse(error.message || '{}'); if (cached.__cached) { return { data: cached.data, status: 200, statusText: 'OK (cached)', headers: {}, config: {} }; } } const originalRequest = error.config; if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { return new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }); }).then((token) => { originalRequest.headers.Authorization = `Bearer ${token}`; return client(originalRequest); }); } originalRequest._retry = true; isRefreshing = true; try { const refreshToken = localStorage.getItem('refresh_token'); if (!refreshToken) throw new Error('No refresh token'); const { data } = await axios.post('/api/v1/auth/refresh', { refresh_token: refreshToken, }); const newAccessToken = data.data.access_token; const newRefreshToken = data.data.refresh_token; localStorage.setItem('access_token', newAccessToken); localStorage.setItem('refresh_token', newRefreshToken); processQueue(null, newAccessToken); originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; return client(originalRequest); } catch (refreshError) { processQueue(refreshError, null); localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); window.location.hash = '#/login'; return Promise.reject(refreshError); } finally { isRefreshing = false; } } return Promise.reject(error); } ); let isRefreshing = false; let failedQueue: Array<{ resolve: (token: string) => void; reject: (error: unknown) => void; }> = []; function processQueue(error: unknown, token: string | null) { failedQueue.forEach(({ resolve, reject }) => { if (token) resolve(token); else reject(error); }); failedQueue = []; } // 清除缓存(登录/登出时调用) export function clearApiCache() { requestCache.clear(); } export default client;