/** * API请求重试机制 * 提供自动重试功能和UI反馈 */ import { ElMessage, ElNotification } from 'element-plus' // 重试配置 const retryConfig = { // 默认最大重试次数 defaultMaxRetries: 3, // 默认重试延迟(毫秒) defaultRetryDelay: 1000, // 指数退避因子 backoffFactor: 2, // 最大重试延迟(毫秒) maxRetryDelay: 10000, // 需要重试的HTTP状态码 retryableStatusCodes: [408, 429, 500, 502, 503, 504], // 需要重试的网络错误类型 retryableErrorTypes: ['NETWORK_ERROR', 'TIMEOUT', 'SERVER_ERROR'] } /** * 计算重试延迟时间 * @param {number} retryCount - 当前重试次数 * @param {number} baseDelay - 基础延迟时间 * @returns {number} 计算后的延迟时间 */ function calculateRetryDelay(retryCount, baseDelay = retryConfig.defaultRetryDelay) { const delay = baseDelay * Math.pow(retryConfig.backoffFactor, retryCount - 1) return Math.min(delay, retryConfig.maxRetryDelay) } /** * 判断错误是否可重试 * @param {Error} error - 错误对象 * @returns {boolean} 是否可重试 */ function isRetryableError(error) { // 检查是否是网络错误 if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT') { return true } // 检查HTTP状态码 if (error.response && error.response.status) { return retryConfig.retryableStatusCodes.includes(error.response.status) } // 检查错误类型 if (error.type && retryConfig.retryableErrorTypes.includes(error.type)) { return true } return false } /** * 显示重试通知 * @param {string} operation - 操作描述 * @param {number} retryCount - 当前重试次数 * @param {number} maxRetries - 最大重试次数 * @param {number} delay - 延迟时间 * @returns {Object} 通知对象 */ function showRetryNotification(operation, retryCount, maxRetries, delay) { const message = `${operation} 失败,正在第 ${retryCount}/${maxRetries} 次重试...` return ElNotification({ title: '请求重试', message, type: 'warning', duration: delay, showClose: false }) } /** * 显示重试失败通知 * @param {string} operation - 操作描述 * @param {Error} error - 错误对象 */ function showRetryFailedNotification(operation, error) { ElMessage.error({ message: `${operation} 失败,已达到最大重试次数: ${error.message || '未知错误'}`, duration: 5000 }) } /** * 带重试机制的请求函数 * @param {Function} requestFn - 请求函数 * @param {Object} options - 重试选项 * @param {string} options.operation - 操作描述 * @param {number} options.maxRetries - 最大重试次数 * @param {number} options.retryDelay - 基础重试延迟 * @param {Function} options.onRetry - 重试回调 * @param {Function} options.onFinalError - 最终错误回调 * @returns {Promise} 请求结果 */ export async function retryRequest(requestFn, options = {}) { const { operation = '请求', maxRetries = retryConfig.defaultMaxRetries, retryDelay = retryConfig.defaultRetryDelay, onRetry, onFinalError } = options let lastError = null let retryCount = 0 // 第一次尝试 try { return await requestFn() } catch (error) { lastError = error // 如果错误不可重试,直接抛出 if (!isRetryableError(error)) { throw error } } // 重试循环 for (retryCount = 1; retryCount <= maxRetries; retryCount++) { const delay = calculateRetryDelay(retryCount, retryDelay) // 显示重试通知 const notification = showRetryNotification(operation, retryCount, maxRetries, delay) // 调用重试回调 if (onRetry) { onRetry(retryCount, maxRetries, delay, lastError) } // 等待延迟 await new Promise(resolve => setTimeout(resolve, delay)) // 关闭通知 notification.close() try { // 执行重试请求 const result = await requestFn() // 重试成功,显示成功消息 ElMessage.success({ message: `${operation} 在第 ${retryCount} 次重试后成功`, duration: 3000 }) return result } catch (error) { lastError = error // 如果错误不可重试或已达到最大重试次数,跳出循环 if (!isRetryableError(error) || retryCount === maxRetries) { break } } } // 所有重试都失败 showRetryFailedNotification(operation, lastError) // 调用最终错误回调 if (onFinalError) { onFinalError(lastError, retryCount) } throw lastError } /** * 创建带重试机制的API请求函数 * @param {Function} apiRequest - API请求函数 * @param {Object} retryOptions - 重试选项 * @returns {Function} 带重试机制的请求函数 */ export function createRetryableRequest(apiRequest, retryOptions = {}) { return async function(...args) { return retryRequest(() => apiRequest(...args), retryOptions) } } /** * 为axios实例添加重试拦截器 * @param {Object} axiosInstance - axios实例 * @param {Object} options - 重试选项 */ export function addRetryInterceptor(axiosInstance, options = {}) { axiosInstance.interceptors.response.use( response => response, async error => { const config = error.config // 如果没有配置对象或已禁用重试,直接抛出错误 if (!config || config.disableRetry === true) { return Promise.reject(error) } // 获取重试配置 const maxRetries = config.maxRetries || options.maxRetries || retryConfig.defaultMaxRetries const retryDelay = config.retryDelay || options.retryDelay || retryConfig.defaultRetryDelay const operation = config.operation || options.operation || 'API请求' // 初始化重试计数 config.retryCount = config.retryCount || 0 // 如果已达到最大重试次数或错误不可重试,直接抛出错误 if (config.retryCount >= maxRetries || !isRetryableError(error)) { return Promise.reject(error) } // 增加重试计数 config.retryCount += 1 // 计算延迟时间 const delay = calculateRetryDelay(config.retryCount, retryDelay) // 显示重试通知 showRetryNotification(operation, config.retryCount, maxRetries, delay) // 等待延迟 await new Promise(resolve => setTimeout(resolve, delay)) // 重新发起请求 return axiosInstance(config) } ) }