/** * 视频编辑API错误处理器 */ import { debugLog, errorHandlingConfig } from './config'; export interface ApiError { code: number; message: string; details?: any; timestamp: string; endpoint?: string; } export interface RetryOptions { maxRetries?: number; retryDelay?: number; shouldRetry?: (error: ApiError) => boolean; onRetry?: (attempt: number, error: ApiError) => void; } /** * 创建API错误对象 */ export function createApiError( code: number, message: string, details?: any, endpoint?: string ): ApiError { return { code, message, details, timestamp: new Date().toISOString(), endpoint }; } /** * 判断错误是否可重试 */ export function isRetryableError(error: ApiError): boolean { // 网络错误、超时、5xx服务器错误可重试 return ( error.code === 0 || // 网络错误 error.code === 408 || // 请求超时 error.code === 429 || // 请求过多 (error.code >= 500 && error.code < 600) // 服务器错误 ); } /** * 计算重试延迟(指数退避) */ export function calculateRetryDelay(attempt: number, baseDelay: number = 1000): number { return Math.min(baseDelay * Math.pow(2, attempt - 1), 10000); // 最大10秒 } /** * 带重试的API调用包装器 */ export async function withRetry( apiCall: () => Promise, options: RetryOptions = {} ): Promise { const { maxRetries = errorHandlingConfig.maxRetries, retryDelay = errorHandlingConfig.retryDelay, shouldRetry = isRetryableError, onRetry } = options; let lastError: ApiError; for (let attempt = 1; attempt <= maxRetries + 1; attempt++) { try { const result = await apiCall(); // 如果之前有重试,记录成功 if (attempt > 1) { debugLog(`API调用在第${attempt}次尝试后成功`); } return result; } catch (error: any) { // 转换为标准错误格式 lastError = error instanceof Error ? createApiError(0, error.message, error) : error; debugLog(`API调用失败 (尝试 ${attempt}/${maxRetries + 1})`, lastError); // 如果是最后一次尝试,或错误不可重试,直接抛出 if (attempt > maxRetries || !shouldRetry(lastError)) { throw lastError; } // 计算延迟时间 const delay = calculateRetryDelay(attempt, retryDelay); // 调用重试回调 onRetry?.(attempt, lastError); debugLog(`${delay}ms后进行第${attempt + 1}次重试`); // 等待后重试 await new Promise(resolve => setTimeout(resolve, delay)); } } throw lastError!; } /** * API错误分类 */ export enum ErrorCategory { NETWORK = 'network', VALIDATION = 'validation', AUTHENTICATION = 'authentication', AUTHORIZATION = 'authorization', NOT_FOUND = 'not_found', SERVER = 'server', UNKNOWN = 'unknown' } /** * 根据错误码分类错误 */ export function categorizeError(error: ApiError): ErrorCategory { const { code } = error; if (code === 0) return ErrorCategory.NETWORK; if (code === 400) return ErrorCategory.VALIDATION; if (code === 401) return ErrorCategory.AUTHENTICATION; if (code === 403) return ErrorCategory.AUTHORIZATION; if (code === 404) return ErrorCategory.NOT_FOUND; if (code >= 500) return ErrorCategory.SERVER; return ErrorCategory.UNKNOWN; } /** * 获取用户友好的错误消息 */ export function getUserFriendlyMessage(error: ApiError): string { const category = categorizeError(error); switch (category) { case ErrorCategory.NETWORK: return '网络连接失败,请检查网络连接后重试'; case ErrorCategory.VALIDATION: return '请求参数有误,请检查输入内容'; case ErrorCategory.AUTHENTICATION: return '登录已过期,请重新登录'; case ErrorCategory.AUTHORIZATION: return '权限不足,无法执行此操作'; case ErrorCategory.NOT_FOUND: return '请求的资源不存在'; case ErrorCategory.SERVER: return '服务器暂时不可用,请稍后重试'; default: return error.message || '发生未知错误'; } } /** * 错误通知处理 */ export function handleErrorNotification(error: ApiError): void { const category = categorizeError(error); const message = getUserFriendlyMessage(error); // 根据配置决定是否显示通知 const { notifications } = errorHandlingConfig; let shouldNotify = false; switch (category) { case ErrorCategory.NETWORK: shouldNotify = notifications.showNetworkErrors; break; case ErrorCategory.VALIDATION: shouldNotify = notifications.showValidationErrors; break; case ErrorCategory.SERVER: shouldNotify = notifications.showServerErrors; break; default: shouldNotify = true; } if (shouldNotify) { // 这里可以集成实际的通知系统 console.error('API错误通知:', message, error); // 如果有全局通知系统,在这里调用 if (typeof window !== 'undefined' && (window as any).msg) { (window as any).msg.error(message); } } } /** * 错误恢复策略 */ export interface RecoveryStrategy { name: string; canRecover: (error: ApiError) => boolean; recover: (error: ApiError) => Promise; } /** * Mock API回退策略 */ export const mockFallbackStrategy: RecoveryStrategy = { name: 'mock-fallback', canRecover: (error: ApiError) => { return errorHandlingConfig.fallbackToMock && (error.code === 404 || error.code >= 500); }, recover: async (error: ApiError) => { debugLog('API失败,回退到Mock模式', error); // 这里可以动态切换到Mock API return null; } }; /** * 缓存回退策略 */ export const cacheStrategy: RecoveryStrategy = { name: 'cache-fallback', canRecover: (error: ApiError) => { return error.code === 0 || error.code >= 500; }, recover: async (error: ApiError) => { debugLog('尝试从缓存恢复数据', error); // 这里可以从本地缓存获取数据 return null; } }; /** * 默认恢复策略列表 */ export const defaultRecoveryStrategies: RecoveryStrategy[] = [ cacheStrategy, mockFallbackStrategy ]; /** * 尝试错误恢复 */ export async function attemptRecovery( error: ApiError, strategies: RecoveryStrategy[] = defaultRecoveryStrategies ): Promise { for (const strategy of strategies) { if (strategy.canRecover(error)) { try { debugLog(`尝试恢复策略: ${strategy.name}`); const result = await strategy.recover(error); if (result !== null) { debugLog(`恢复策略 ${strategy.name} 成功`); return result; } } catch (recoveryError) { debugLog(`恢复策略 ${strategy.name} 失败`, recoveryError); } } } debugLog('所有恢复策略都失败了'); return null; } /** * 完整的错误处理流程 */ export async function handleApiError(error: any, endpoint?: string): Promise { // 标准化错误格式 const apiError: ApiError = error instanceof Error ? createApiError(0, error.message, error, endpoint) : { ...error, endpoint }; // 记录错误 debugLog('API错误', apiError); // 显示通知 handleErrorNotification(apiError); // 尝试恢复 const recoveryResult = await attemptRecovery(apiError); if (recoveryResult !== null) { return recoveryResult; } // 抛出错误 throw apiError; }