// 全局错误处理管理器 - 微信小程序专用 // 统一处理应用中的各种错误,提供错误恢复和降级方案 const performanceMonitor = require('./performance-monitor.js'); /** * 全局错误处理管理器 * 功能: * 1. 全局错误捕获和处理 * 2. 错误分类和分析 * 3. 自动重试机制 * 4. 降级方案 * 5. 错误恢复策略 * 6. 用户友好的错误提示 */ class ErrorHandler { constructor() { this.isInitialized = false; // 错误处理配置 this.config = { // 错误处理开关 enabled: true, // 自动重试配置 retry: { enabled: true, maxAttempts: 3, baseDelay: 1000, // 基础延迟 (ms) maxDelay: 10000, // 最大延迟 (ms) backoffFactor: 2 // 退避因子 }, // 降级配置 fallback: { enabled: true, cacheTimeout: 300000, // 缓存超时 (5分钟) offlineMode: true // 离线模式 }, // 用户提示配置 userNotification: { enabled: true, showDetails: false, // 是否显示错误详情 autoHide: true, // 自动隐藏 hideDelay: 3000 // 隐藏延迟 (ms) } }; // 错误类型定义 this.errorTypes = { NETWORK_ERROR: 'network_error', API_ERROR: 'api_error', PARSE_ERROR: 'parse_error', STORAGE_ERROR: 'storage_error', PERMISSION_ERROR: 'permission_error', VALIDATION_ERROR: 'validation_error', UNKNOWN_ERROR: 'unknown_error' }; // 错误统计 this.errorStats = { totalErrors: 0, errorsByType: new Map(), errorsByPage: new Map(), recentErrors: [] }; // 重试队列 this.retryQueue = new Map(); // 降级缓存 this.fallbackCache = new Map(); this.init(); } // 初始化错误处理器 init() { if (this.isInitialized || !this.config.enabled) return; console.log('🚨 初始化全局错误处理器...'); try { // 设置全局错误监听 this.setupGlobalErrorHandlers(); // 设置网络错误监听 this.setupNetworkErrorHandlers(); // 设置Promise错误监听 this.setupPromiseErrorHandlers(); this.isInitialized = true; console.log('✅ 全局错误处理器初始化完成'); } catch (error) { console.error('❌ 全局错误处理器初始化失败:', error); } } // 🚨 ===== 错误捕获和处理 ===== // 处理错误 handleError(error, context = {}) { if (!this.config.enabled) return; try { // 错误分类 const errorType = this.classifyError(error); // 创建错误信息 const errorInfo = this.createErrorInfo(error, errorType, context); // 更新错误统计 this.updateErrorStats(errorInfo); // 记录错误到性能监控 performanceMonitor.recordError(error, context); // 处理特定类型的错误 this.handleSpecificError(errorInfo); // 显示用户提示 this.showUserNotification(errorInfo); console.error('🚨 错误处理:', errorInfo); return errorInfo; } catch (handlerError) { console.error('❌ 错误处理器本身出错:', handlerError); } } // 错误分类 classifyError(error) { if (!error) return this.errorTypes.UNKNOWN_ERROR; const message = error.message || error.toString(); const stack = error.stack || ''; // 网络错误 if (message.includes('network') || message.includes('timeout') || message.includes('连接') || error.code === 'NETWORK_ERROR') { return this.errorTypes.NETWORK_ERROR; } // API错误 if (message.includes('API') || message.includes('request') || message.includes('response') || error.statusCode) { return this.errorTypes.API_ERROR; } // 解析错误 if (message.includes('JSON') || message.includes('parse') || message.includes('Unexpected token')) { return this.errorTypes.PARSE_ERROR; } // 存储错误 if (message.includes('storage') || message.includes('setStorage') || message.includes('getStorage')) { return this.errorTypes.STORAGE_ERROR; } // 权限错误 if (message.includes('permission') || message.includes('unauthorized') || message.includes('权限') || error.code === 'PERMISSION_DENIED') { return this.errorTypes.PERMISSION_ERROR; } // 验证错误 if (message.includes('validation') || message.includes('invalid') || message.includes('验证')) { return this.errorTypes.VALIDATION_ERROR; } return this.errorTypes.UNKNOWN_ERROR; } // 创建错误信息 createErrorInfo(error, errorType, context) { return { id: this.generateErrorId(), timestamp: Date.now(), type: errorType, message: error.message || error.toString(), stack: error.stack || null, code: error.code || null, statusCode: error.statusCode || null, context: context, pagePath: this.getCurrentPagePath(), userAgent: this.getUserAgent(), canRetry: this.canRetry(errorType), canFallback: this.canFallback(errorType), severity: this.getErrorSeverity(errorType) }; } // 🔄 ===== 自动重试机制 ===== // 自动重试 async autoRetry(operation, context = {}) { if (!this.config.retry.enabled) { return await operation(); } const retryId = this.generateRetryId(); let lastError = null; for (let attempt = 1; attempt <= this.config.retry.maxAttempts; attempt++) { try { // 记录重试尝试 if (attempt > 1) { console.log(`🔄 重试第 ${attempt - 1} 次:`, context); } const result = await operation(); // 成功,清除重试记录 this.retryQueue.delete(retryId); return result; } catch (error) { lastError = error; // 检查是否可以重试 if (!this.canRetry(this.classifyError(error)) || attempt >= this.config.retry.maxAttempts) { break; } // 计算延迟时间 const delay = this.calculateRetryDelay(attempt); // 记录重试信息 this.retryQueue.set(retryId, { attempt: attempt, nextRetry: Date.now() + delay, context: context, error: error }); // 等待重试 await this.sleep(delay); } } // 所有重试都失败了 this.retryQueue.delete(retryId); throw lastError; } // 计算重试延迟 calculateRetryDelay(attempt) { const delay = this.config.retry.baseDelay * Math.pow(this.config.retry.backoffFactor, attempt - 1); return Math.min(delay, this.config.retry.maxDelay); } // 检查是否可以重试 canRetry(errorType) { const retryableErrors = [ this.errorTypes.NETWORK_ERROR, this.errorTypes.API_ERROR ]; return retryableErrors.includes(errorType); } // 🔄 ===== 降级方案 ===== // 降级处理 async fallbackHandler(operation, fallbackKey, fallbackData = null) { if (!this.config.fallback.enabled) { return await operation(); } try { const result = await operation(); // 成功时更新缓存 this.updateFallbackCache(fallbackKey, result); return result; } catch (error) { console.warn('🔄 操作失败,尝试降级方案:', error.message); // 尝试从缓存获取数据 const cachedData = this.getFallbackCache(fallbackKey); if (cachedData) { console.log('✅ 使用缓存数据作为降级方案'); return cachedData; } // 使用提供的降级数据 if (fallbackData !== null) { console.log('✅ 使用默认数据作为降级方案'); return fallbackData; } // 没有降级方案,重新抛出错误 throw error; } } // 更新降级缓存 updateFallbackCache(key, data) { this.fallbackCache.set(key, { data: data, timestamp: Date.now() }); } // 获取降级缓存 getFallbackCache(key) { const cached = this.fallbackCache.get(key); if (!cached) return null; // 检查缓存是否过期 if (Date.now() - cached.timestamp > this.config.fallback.cacheTimeout) { this.fallbackCache.delete(key); return null; } return cached.data; } // 检查是否可以降级 canFallback(errorType) { const fallbackableErrors = [ this.errorTypes.NETWORK_ERROR, this.errorTypes.API_ERROR ]; return fallbackableErrors.includes(errorType); } // 📱 ===== 用户提示 ===== // 显示用户提示 showUserNotification(errorInfo) { if (!this.config.userNotification.enabled) return; const userMessage = this.getUserFriendlyMessage(errorInfo); // 根据错误严重程度选择提示方式 switch (errorInfo.severity) { case 'low': // 低严重程度,不显示提示或显示简单提示 break; case 'medium': wx.showToast({ title: userMessage, icon: 'none', duration: this.config.userNotification.hideDelay }); break; case 'high': wx.showModal({ title: '操作失败', content: userMessage, showCancel: false, confirmText: '确定' }); break; case 'critical': wx.showModal({ title: '严重错误', content: userMessage + '\n\n建议重启应用或联系客服。', showCancel: true, cancelText: '稍后处理', confirmText: '重启应用', success: (res) => { if (res.confirm) { wx.reLaunch({ url: '/pages/splash/splash' }); } } }); break; } } // 获取用户友好的错误消息 getUserFriendlyMessage(errorInfo) { const messageMap = { [this.errorTypes.NETWORK_ERROR]: '网络连接异常,请检查网络设置', [this.errorTypes.API_ERROR]: '服务暂时不可用,请稍后重试', [this.errorTypes.PARSE_ERROR]: '数据格式错误,请稍后重试', [this.errorTypes.STORAGE_ERROR]: '存储空间不足或存储异常', [this.errorTypes.PERMISSION_ERROR]: '权限不足,请检查相关权限设置', [this.errorTypes.VALIDATION_ERROR]: '输入信息有误,请检查后重试', [this.errorTypes.UNKNOWN_ERROR]: '操作失败,请稍后重试' }; let message = messageMap[errorInfo.type] || messageMap[this.errorTypes.UNKNOWN_ERROR]; // 如果配置显示详情,添加错误详情 if (this.config.userNotification.showDetails && errorInfo.message) { message += `\n\n详情: ${errorInfo.message}`; } return message; } // 获取错误严重程度 getErrorSeverity(errorType) { const severityMap = { [this.errorTypes.NETWORK_ERROR]: 'medium', [this.errorTypes.API_ERROR]: 'medium', [this.errorTypes.PARSE_ERROR]: 'high', [this.errorTypes.STORAGE_ERROR]: 'high', [this.errorTypes.PERMISSION_ERROR]: 'medium', [this.errorTypes.VALIDATION_ERROR]: 'low', [this.errorTypes.UNKNOWN_ERROR]: 'high' }; return severityMap[errorType] || 'medium'; } // 🔧 ===== 特定错误处理 ===== // 处理特定类型的错误 handleSpecificError(errorInfo) { switch (errorInfo.type) { case this.errorTypes.NETWORK_ERROR: this.handleNetworkError(errorInfo); break; case this.errorTypes.API_ERROR: this.handleApiError(errorInfo); break; case this.errorTypes.STORAGE_ERROR: this.handleStorageError(errorInfo); break; case this.errorTypes.PERMISSION_ERROR: this.handlePermissionError(errorInfo); break; default: this.handleGenericError(errorInfo); } } // 处理网络错误 handleNetworkError(errorInfo) { console.log('🌐 处理网络错误:', errorInfo.message); // 检查网络状态 wx.getNetworkType({ success: (res) => { if (res.networkType === 'none') { // 无网络连接 this.handleOfflineMode(); } } }); } // 处理API错误 handleApiError(errorInfo) { console.log('🔌 处理API错误:', errorInfo.message); // 根据状态码进行特殊处理 if (errorInfo.statusCode === 401) { // 未授权,可能需要重新登录 this.handleUnauthorizedError(); } else if (errorInfo.statusCode >= 500) { // 服务器错误,可以尝试重试 console.log('🔄 服务器错误,建议重试'); } } // 处理存储错误 handleStorageError(errorInfo) { console.log('💾 处理存储错误:', errorInfo.message); // 尝试清理存储空间 this.cleanupStorage(); } // 处理权限错误 handlePermissionError(errorInfo) { console.log('🔐 处理权限错误:', errorInfo.message); // 可以引导用户去设置页面 } // 处理通用错误 handleGenericError(errorInfo) { console.log('❓ 处理通用错误:', errorInfo.message); } // 处理离线模式 handleOfflineMode() { if (!this.config.fallback.offlineMode) return; console.log('📱 进入离线模式'); // 可以设置全局离线状态 // app.globalData.isOffline = true; } // 处理未授权错误 handleUnauthorizedError() { console.log('🔐 处理未授权错误'); // 清除本地token wx.removeStorageSync('token'); // 跳转到登录页面 wx.reLaunch({ url: '/pages/login/login' }); } // 清理存储空间 cleanupStorage() { try { // 获取存储信息 const storageInfo = wx.getStorageInfoSync(); if (storageInfo.currentSize > storageInfo.limitSize * 0.8) { // 存储空间使用超过80%,进行清理 console.log('🧹 开始清理存储空间'); // 清理缓存数据 const keysToClean = ['cache_', 'temp_', 'old_']; storageInfo.keys.forEach(key => { if (keysToClean.some(prefix => key.startsWith(prefix))) { wx.removeStorageSync(key); } }); console.log('✅ 存储空间清理完成'); } } catch (error) { console.error('❌ 清理存储空间失败:', error); } } // 📊 ===== 错误统计 ===== // 更新错误统计 updateErrorStats(errorInfo) { this.errorStats.totalErrors++; // 按类型统计 const typeCount = this.errorStats.errorsByType.get(errorInfo.type) || 0; this.errorStats.errorsByType.set(errorInfo.type, typeCount + 1); // 按页面统计 const pageCount = this.errorStats.errorsByPage.get(errorInfo.pagePath) || 0; this.errorStats.errorsByPage.set(errorInfo.pagePath, pageCount + 1); // 记录最近错误 this.errorStats.recentErrors.push(errorInfo); // 保持最近50条错误记录 if (this.errorStats.recentErrors.length > 50) { this.errorStats.recentErrors.shift(); } } // 获取错误统计 getErrorStats() { return { totalErrors: this.errorStats.totalErrors, errorsByType: Object.fromEntries(this.errorStats.errorsByType), errorsByPage: Object.fromEntries(this.errorStats.errorsByPage), recentErrors: this.errorStats.recentErrors.slice(-10), // 最近10条 errorRate: this.calculateErrorRate() }; } // 计算错误率 calculateErrorRate() { const sessionDuration = Date.now() - (performanceMonitor.monitoringState?.startTime || Date.now()); const hours = sessionDuration / (1000 * 60 * 60); return hours > 0 ? this.errorStats.totalErrors / hours : 0; } // 🔧 ===== 工具方法 ===== // 设置全局错误监听 setupGlobalErrorHandlers() { // 微信小程序的错误监听 wx.onError((error) => { this.handleError(new Error(error), { source: 'global' }); }); // 监听未处理的Promise拒绝 wx.onUnhandledRejection((res) => { this.handleError(res.reason, { source: 'unhandled_promise' }); }); } // 设置网络错误监听 setupNetworkErrorHandlers() { // 监听网络状态变化 wx.onNetworkStatusChange((res) => { if (!res.isConnected) { this.handleError(new Error('Network disconnected'), { source: 'network_change', networkType: res.networkType }); } }); } // 设置Promise错误监听 setupPromiseErrorHandlers() { // 这个在微信小程序中通过wx.onUnhandledRejection已经处理 } // 生成错误ID generateErrorId() { return `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } // 生成重试ID generateRetryId() { return `retry_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } // 获取当前页面路径 getCurrentPagePath() { try { const pages = getCurrentPages(); const currentPage = pages[pages.length - 1]; return currentPage ? currentPage.route : 'unknown'; } catch (error) { return 'unknown'; } } // 获取用户代理 getUserAgent() { try { // 使用新的API替代已弃用的wx.getSystemInfoSync const deviceInfo = wx.getDeviceInfo(); const appBaseInfo = wx.getAppBaseInfo(); return `${deviceInfo.platform} ${deviceInfo.system} WeChat/${appBaseInfo.version}`; } catch (error) { // 兜底方案 try { const systemInfo = wx.getSystemInfoSync(); return `${systemInfo.platform} ${systemInfo.system} WeChat/${systemInfo.version}`; } catch (fallbackError) { return 'unknown'; } } } // 睡眠函数 sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // 销毁错误处理器 destroy() { this.errorStats = { totalErrors: 0, errorsByType: new Map(), errorsByPage: new Map(), recentErrors: [] }; this.retryQueue.clear(); this.fallbackCache.clear(); this.isInitialized = false; console.log('🚨 全局错误处理器已销毁'); } } // 创建全局实例 const errorHandler = new ErrorHandler(); module.exports = errorHandler;