Files
it/frontend/src/utils/retryManager.js
XCool f25b0307db
Some checks failed
CI/CD Pipeline / 测试 (18.x) (push) Has been cancelled
CI/CD Pipeline / 测试 (20.x) (push) Has been cancelled
CI/CD Pipeline / 安全检查 (push) Has been cancelled
CI/CD Pipeline / 部署 (push) Has been cancelled
CI/CD Pipeline / 通知 (push) Has been cancelled
初始化
2025-11-03 19:47:36 +08:00

238 lines
6.5 KiB
JavaScript

/**
* 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)
}
)
}