upload project

This commit is contained in:
unknown 2025-12-27 17:16:03 +08:00
commit 06961cae04
422 changed files with 110626 additions and 0 deletions

695
utils/error-handler.js Normal file
View file

@ -0,0 +1,695 @@
// 全局错误处理管理器 - 微信小程序专用
// 统一处理应用中的各种错误,提供错误恢复和降级方案
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;
try {
// 设置全局错误监听
this.setupGlobalErrorHandlers();
// 设置网络错误监听
this.setupNetworkErrorHandlers();
// 设置Promise错误监听
this.setupPromiseErrorHandlers();
this.isInitialized = true;
} 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) {
}
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) {
return cachedData;
}
// 使用提供的降级数据
if (fallbackData !== null) {
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 app = getApp();
const isLoggedIn = app?.globalData?.isLoggedIn || false;
if (!isLoggedIn) 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) {
// 检查网络状态
wx.getNetworkType({
success: (res) => {
if (res.networkType === 'none') {
// 无网络连接
this.handleOfflineMode();
}
}
});
}
// 处理API错误
handleApiError(errorInfo) {
// 根据状态码进行特殊处理
if (errorInfo.statusCode === 401) {
// 未授权,可能需要重新登录
this.handleUnauthorizedError();
} else if (errorInfo.statusCode >= 500) {
// 服务器错误,可以尝试重试
}
}
// 处理存储错误
handleStorageError(errorInfo) {
// 尝试清理存储空间
this.cleanupStorage();
}
// 处理权限错误
handlePermissionError(errorInfo) {
// 可以引导用户去设置页面
}
// 处理通用错误
handleGenericError(errorInfo) {
}
// 处理离线模式
handleOfflineMode() {
if (!this.config.fallback.offlineMode) return;
// 可以设置全局离线状态
// app.globalData.isOffline = true;
}
// 处理未授权错误
handleUnauthorizedError() {
// 清除本地token
wx.removeStorageSync('token');
// 跳转到登录页面
wx.reLaunch({
url: '/pages/login/login'
});
}
// 清理存储空间
cleanupStorage() {
try {
// 获取存储信息
const storageInfo = wx.getStorageInfoSync();
if (storageInfo.currentSize > storageInfo.limitSize * 0.8) {
// 存储空间使用超过80%,进行清理
// 清理缓存数据
const keysToClean = ['cache_', 'temp_', 'old_'];
storageInfo.keys.forEach(key => {
if (keysToClean.some(prefix => key.startsWith(prefix))) {
wx.removeStorageSync(key);
}
});
}
} 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();
if (!pages || pages.length === 0) {
console.warn('没有找到当前页面信息');
return 'unknown';
}
const currentPage = pages[pages.length - 1];
if (!currentPage || !currentPage.route) {
console.warn('当前页面或路由信息不存在');
return 'unknown';
}
return currentPage.route;
} catch (error) {
console.error('获取当前页面路径失败:', 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;
}
}
// 创建全局实例
const errorHandler = new ErrorHandler();
module.exports = errorHandler;