import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders } from 'axios'; import { BASE_URL } from './constants' import { errorHandle } from './errorHandle'; import { showInsufficientPointsNotification } from '../utils/notifications'; /** * 统一的错误处理函数 * @param error - 错误对象 * @param defaultMessage - 默认错误信息 */ const handleRequestError = (error: any, defaultMessage: string = '请求失败') => { if (error.response) { const { status, data } = error.response; const errorMessage = data?.message || data?.detail?.message || defaultMessage; errorHandle(status, errorMessage); } else if (error.request) { errorHandle(0); } else { errorHandle(0, error.message || defaultMessage); } }; // 创建 axios 实例 const request: AxiosInstance = axios.create({ baseURL: BASE_URL, // 设置基础URL timeout: 300000, // 请求超时时间 headers: { 'Content-Type': 'application/json', }, }); // 请求拦截器 request.interceptors.request.use( (config: InternalAxiosRequestConfig) => { // 从 localStorage 获取 token const token = localStorage?.getItem('token'); if (token && config.headers) { (config.headers as AxiosHeaders).set('Authorization', `Bearer ${token}`); } return config; }, (error) => { return Promise.reject(error); } ); // 响应拦截器 request.interceptors.response.use( (response: AxiosResponse) => { // 检查业务状态码 if (response.data?.code !== 0 && response.data?.code !== 202) { console.log('response', response) // 处理业务层面的错误 const businessCode = response.data?.code; const errorMessage = response.data?.message; // 特殊处理 401 和 4001 业务状态码 if (businessCode === 401) { errorHandle(401, errorMessage); return Promise.reject(new Error(errorMessage)); } if (businessCode === 4001) { errorHandle(4001, errorMessage); return Promise.reject(new Error(errorMessage)); } // 其他业务错误 errorHandle(0, errorMessage); return Promise.reject(new Error(errorMessage)); } return response.data; }, (error) => { // 处理 402 错误 if (error.response?.status === 402 && error.response?.data?.detail) { // 使用动态导入并确保在下一个事件循环中执行 setTimeout(() => { import('../utils/notifications').then(({ showInsufficientPointsNotification }) => { showInsufficientPointsNotification(error.response.data.detail); }); }, 0); return Promise.reject(new Error('Insufficient points')); } // 其他错误走通用处理 handleRequestError(error); return Promise.reject(error); } ); // 封装 GET 请求 export const get = (url: string, config?: AxiosRequestConfig): Promise => { return request.get(url, config); }; // 封装 POST 请求 export const post = (url: string, data?: any, config?: AxiosRequestConfig): Promise => { return request.post(url, data, config); }; // 封装 PUT 请求 export const put = (url: string, data?: any, config?: AxiosRequestConfig): Promise => { return request.put(url, data, config); }; // 封装 DELETE 请求 export const del = (url: string, config?: AxiosRequestConfig): Promise => { return request.delete(url, config); }; // utils/streamJsonPost.ts export async function streamJsonPost( url: string, body: any, onJson: (json: T) => void ) { try { const token = localStorage?.getItem('token') || ''; const response = await fetch(`${BASE_URL}${url}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(body), }); // 处理 HTTP 错误状态 if (!response.ok) { const error = { response: { status: response.status, data: { message: await response.text().then(text => { try { const data = JSON.parse(text); return data.message || `HTTP error! status: ${response.status}`; } catch { return `HTTP error! status: ${response.status}`; } })} } }; handleRequestError(error); throw error; } if (!response.body) { throw new Error('Stream not supported'); } const reader = response.body.getReader(); const decoder = new TextDecoder('utf-8'); let buffer = ''; try { while (true) { const { done, value } = await reader.read(); if (done) { // Process any remaining data in the buffer if (buffer.trim()) { try { const parsed = JSON.parse(buffer.trim()); onJson(parsed); } catch (err) { console.warn('Final JSON parse error:', err, buffer); } } break; } // Decode the current chunk and add to buffer buffer += decoder.decode(value, { stream: true }); // Process complete JSON objects let boundary = buffer.indexOf('\n'); while (boundary !== -1) { const chunk = buffer.slice(0, boundary).trim(); buffer = buffer.slice(boundary + 1); if (chunk) { try { const parsed = JSON.parse(chunk); onJson(parsed); } catch (err) { // Only log if it's not an empty line if (chunk !== '') { console.warn('JSON parse error:', err, chunk); } } } boundary = buffer.indexOf('\n'); } } } catch (error) { console.error('Stream processing error:', error); throw error; } finally { // Ensure reader is released reader.releaseLock(); } } catch (error) { console.error('Stream request error:', error); // Handle specific error types if (error instanceof TypeError) { console.error('Network error - check your connection'); } if (error instanceof SyntaxError) { console.error('Invalid JSON in the stream'); } throw error; } } // 封装流式数据请求 export const stream = async ({ url, method = 'POST', data, onMessage, onError, onComplete, }: { url: string; method?: 'GET' | 'POST'; data?: any; onMessage: (data: T) => void; onError?: (error: any) => void; onComplete?: () => void; }) => { try { const config: AxiosRequestConfig = { url, method, data, responseType: 'stream', headers: { 'Accept': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', }, onDownloadProgress: (progressEvent: any) => { const responseText = progressEvent.event?.target?.responseText; if (!responseText) return; // 处理接收到的数据 const lines: string[] = responseText.split('\n'); lines.forEach((line: string) => { if (line.startsWith('data: ')) { try { const data = JSON.parse(line.slice(6)); onMessage(data); } catch (e) { console.warn('Failed to parse stream data:', e); } } }); }, }; const response = await request(config); onComplete?.(); return response; } catch (error: any) { handleRequestError(error, 'Stream request failed'); onError?.(error); throw error; } }; // 封装文件下载流请求 export const downloadStream = async ( url: string, filename: string, config?: AxiosRequestConfig ) => { try { const response = await request({ url, method: 'GET', responseType: 'blob', ...config, }); // 创建下载链接 const blob = new Blob([response.data], { type: response.headers['content-type'] }); const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = downloadUrl; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(downloadUrl); return response; } catch (error: any) { console.error('文件下载失败:', error); handleRequestError(error, 'File download failed'); throw error; } }; // 导出 request 实例 export default request;