初始化
Some checks failed
Some checks failed
This commit is contained in:
238
frontend/src/utils/retryManager.js
Normal file
238
frontend/src/utils/retryManager.js
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user