import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders } from 'axios'; import { message } from "antd"; import { BASE_URL } from './constants' import { errorHandle } from './errorHandle'; /** * 统一的错误处理函数 * @param error - 错误对象 * @param defaultMessage - 默认错误信息 */ const handleRequestError = (error: any, defaultMessage: string = '请求失败') => { if (error.response) { const { status, data } = error.response; const errorMessage = data?.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') || 'mock-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) { // 处理业务层面的错误 const businessCode = response.data?.code; const errorMessage = response.data?.message; // 特殊处理 401 和 403 业务状态码 if (businessCode === 401) { errorHandle(401, errorMessage); return Promise.reject(new Error(errorMessage)); } if (businessCode === 403) { errorHandle(403, errorMessage); return Promise.reject(new Error(errorMessage)); } // 其他业务错误 errorHandle(0, errorMessage); return Promise.reject(new Error(errorMessage)); } return response.data; }, (error) => { 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') || 'mock-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, '流式请求失败'); 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, '文件下载失败'); throw error; } }; // 导出 request 实例 export default request;