import axios, { InternalAxiosRequestConfig, AxiosRequestConfig, AxiosResponse } from 'axios' import { useUserStore } from '@/store/modules/user' import { ApiStatus } from './status' import { HttpError, handleError, showError } from './error' import { $t } from '@/locales' // 常量定义 const REQUEST_TIMEOUT = 15000 // 请求超时时间(毫秒) const LOGOUT_DELAY = 1000 // 退出登录延迟时间(毫秒) const MAX_RETRIES = 2 // 最大重试次数 const RETRY_DELAY = 1000 // 重试延迟时间(毫秒) // 扩展 AxiosRequestConfig 类型 interface ExtendedAxiosRequestConfig extends AxiosRequestConfig { showErrorMessage?: boolean } const axiosInstance = axios.create({ timeout: REQUEST_TIMEOUT, // 请求超时时间(毫秒) baseURL: import.meta.env.VITE_API_URL, // API地址 withCredentials: true, // 异步请求携带cookie transformRequest: [(data) => JSON.stringify(data)], // 请求数据转换为 JSON 字符串 validateStatus: (status) => status >= 200 && status < 300, // 只接受 2xx 的状态码 headers: { get: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8' }, post: { 'Content-Type': 'application/json;charset=utf-8' } }, transformResponse: [ (data, headers) => { const contentType = headers['content-type'] if (contentType && contentType.includes('application/json')) { try { return JSON.parse(data) } catch { return data } } return data } ] }) // 请求拦截器 axiosInstance.interceptors.request.use( (request: InternalAxiosRequestConfig) => { const { accessToken } = useUserStore() const { language } = useUserStore() const { loginType } = useUserStore() const { companyabbr } = useUserStore() request.headers.set('Fdesk-Language', language) request.headers.set('Fdesk-Corp-Abbr', companyabbr) request.headers.set('Fdesk-System-Code', 'fdesk') request.headers.set('Fdesk-Login-Type', loginType) // 设置 token 和 请求头 if (accessToken) { request.headers.set('Fdesk-Auth-Token','Bearer '+accessToken) request.headers.set('Content-Type', 'application/json') } return request }, (error) => { showError(new HttpError($t('httpMsg.requestConfigError'), ApiStatus.error)) return Promise.reject(error) } ) // 响应拦截器 axiosInstance.interceptors.response.use( (response: AxiosResponse) => { const { code, msg } = response.data switch (code) { case ApiStatus.success: return response case ApiStatus.unauthorized: logOut() throw new HttpError(msg || $t('httpMsg.unauthorized'), ApiStatus.unauthorized) default: throw new HttpError(msg || $t('httpMsg.requestFailed'), code) } }, (error) => { const { code, msg } = error.response.data switch (code) { case ApiStatus.unauthorized: logOut() throw new HttpError(msg || $t('httpMsg.unauthorized'), ApiStatus.unauthorized) default: return Promise.reject(handleError(error)) } // return Promise.reject(handleError(error)) } ) // 请求重试函数 async function retryRequest( config: ExtendedAxiosRequestConfig, retries: number = MAX_RETRIES ): Promise { try { return await request(config) } catch (error) { if (retries > 0 && error instanceof HttpError && shouldRetry(error.code)) { await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY)) return retryRequest(config, retries - 1) } throw error } } // 判断是否需要重试 function shouldRetry(statusCode: number): boolean { return [ ApiStatus.requestTimeout, ApiStatus.internalServerError, ApiStatus.badGateway, ApiStatus.serviceUnavailable, ApiStatus.gatewayTimeout ].includes(statusCode) } // 请求函数 async function request(config: ExtendedAxiosRequestConfig): Promise { // 对 POST | PUT 请求特殊处理 if (config.method?.toUpperCase() === 'POST' || config.method?.toUpperCase() === 'PUT') { if (config.params && !config.data) { config.data = config.params config.params = undefined } } try { const res = await axiosInstance.request>(config) if (config.method?.toUpperCase() === 'POST' || config.method?.toUpperCase() === 'PUT') { return res.data as T }else{ return res.data.data as T } } catch (error) { if (error instanceof HttpError) { // 根据配置决定是否显示错误消息 const showErrorMessage = config.showErrorMessage !== false showError(error, showErrorMessage) } return Promise.reject(error) } } // API 方法集合 const api = { get(config: ExtendedAxiosRequestConfig): Promise { return retryRequest({ ...config, method: 'GET' }) }, post(config: ExtendedAxiosRequestConfig): Promise { return retryRequest({ ...config, method: 'POST' }) }, put(config: ExtendedAxiosRequestConfig): Promise { return retryRequest({ ...config, method: 'PUT' }) }, del(config: ExtendedAxiosRequestConfig): Promise { return retryRequest({ ...config, method: 'DELETE' }) }, request(config: ExtendedAxiosRequestConfig): Promise { return retryRequest({ ...config }) } } // 退出登录函数 const logOut = (): void => { setTimeout(() => { useUserStore().logOut() }, LOGOUT_DELAY) } export default api