129 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			129 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const { logger } = require('../config/logger');
 | |
| const { ERROR_CODES, HTTP_STATUS } = require('../config/constants');
 | |
| 
 | |
| // 全局错误处理中间件
 | |
| const errorHandler = (err, req, res, next) => {
 | |
|   let error = { ...err };
 | |
|   error.message = err.message;
 | |
| 
 | |
|   // 记录错误日志
 | |
|   logger.error('Error occurred:', {
 | |
|     message: err.message,
 | |
|     stack: err.stack,
 | |
|     url: req.originalUrl,
 | |
|     method: req.method,
 | |
|     ip: req.ip,
 | |
|     userAgent: req.get('User-Agent'),
 | |
|     userId: req.user?.id
 | |
|   });
 | |
| 
 | |
|   // MySQL错误处理
 | |
|   if (err.code) {
 | |
|     switch (err.code) {
 | |
|       case 'ER_DUP_ENTRY':
 | |
|         error.message = '数据已存在';
 | |
|         error.statusCode = HTTP_STATUS.CONFLICT;
 | |
|         error.errorCode = ERROR_CODES.DUPLICATE_ENTRY;
 | |
|         break;
 | |
|       case 'ER_NO_REFERENCED_ROW_2':
 | |
|         error.message = '关联数据不存在';
 | |
|         error.statusCode = HTTP_STATUS.BAD_REQUEST;
 | |
|         error.errorCode = ERROR_CODES.VALIDATION_ERROR;
 | |
|         break;
 | |
|       case 'ER_ROW_IS_REFERENCED_2':
 | |
|         error.message = '数据正在被使用,无法删除';
 | |
|         error.statusCode = HTTP_STATUS.CONFLICT;
 | |
|         error.errorCode = ERROR_CODES.VALIDATION_ERROR;
 | |
|         break;
 | |
|       case 'ECONNREFUSED':
 | |
|         error.message = '数据库连接失败';
 | |
|         error.statusCode = HTTP_STATUS.INTERNAL_SERVER_ERROR;
 | |
|         error.errorCode = ERROR_CODES.DATABASE_ERROR;
 | |
|         break;
 | |
|       default:
 | |
|         error.message = '数据库操作失败';
 | |
|         error.statusCode = HTTP_STATUS.INTERNAL_SERVER_ERROR;
 | |
|         error.errorCode = ERROR_CODES.DATABASE_ERROR;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // JWT错误处理
 | |
|   if (err.name === 'JsonWebTokenError') {
 | |
|     error.message = '无效的访问令牌';
 | |
|     error.statusCode = HTTP_STATUS.UNAUTHORIZED;
 | |
|     error.errorCode = ERROR_CODES.AUTHENTICATION_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (err.name === 'TokenExpiredError') {
 | |
|     error.message = '访问令牌已过期';
 | |
|     error.statusCode = HTTP_STATUS.UNAUTHORIZED;
 | |
|     error.errorCode = ERROR_CODES.AUTHENTICATION_ERROR;
 | |
|   }
 | |
| 
 | |
|   // 参数验证错误
 | |
|   if (err.name === 'ValidationError' || err.isJoi) {
 | |
|     const message = err.details ? err.details.map(detail => detail.message).join(', ') : err.message;
 | |
|     error.message = `参数验证失败: ${message}`;
 | |
|     error.statusCode = HTTP_STATUS.BAD_REQUEST;
 | |
|     error.errorCode = ERROR_CODES.VALIDATION_ERROR;
 | |
|   }
 | |
| 
 | |
|   // 业务逻辑错误处理
 | |
|   if (err.message === '余额不足') {
 | |
|     error.message = '用户积分余额不足,无法完成转账操作。请先为用户充值积分或选择其他用户。';
 | |
|     error.statusCode = HTTP_STATUS.BAD_REQUEST;
 | |
|     error.errorCode = ERROR_CODES.VALIDATION_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (err.message === '用户不存在') {
 | |
|     error.message = '指定的用户不存在,请检查用户信息后重试。';
 | |
|     error.statusCode = HTTP_STATUS.BAD_REQUEST;
 | |
|     error.errorCode = ERROR_CODES.VALIDATION_ERROR;
 | |
|   }
 | |
| 
 | |
|   // 自定义错误
 | |
|   if (err.statusCode) {
 | |
|     error.statusCode = err.statusCode;
 | |
|     error.errorCode = err.errorCode || ERROR_CODES.INTERNAL_ERROR;
 | |
|   }
 | |
| 
 | |
|   // 默认错误
 | |
|   const statusCode = error.statusCode || HTTP_STATUS.INTERNAL_SERVER_ERROR;
 | |
|   const errorCode = error.errorCode || ERROR_CODES.INTERNAL_ERROR;
 | |
|   const message = error.message || '服务器内部错误';
 | |
| 
 | |
|   res.status(statusCode).json({
 | |
|     success: false,
 | |
|     error: {
 | |
|       code: errorCode,
 | |
|       message: message
 | |
|     },
 | |
|     ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
 | |
|   });
 | |
| };
 | |
| 
 | |
| // 404错误处理
 | |
| const notFound = (req, res, next) => {
 | |
|   const error = new Error(`路径 ${req.originalUrl} 未找到`);
 | |
|   error.statusCode = HTTP_STATUS.NOT_FOUND;
 | |
|   error.errorCode = ERROR_CODES.NOT_FOUND;
 | |
|   next(error);
 | |
| };
 | |
| 
 | |
| // 自定义错误类
 | |
| class AppError extends Error {
 | |
|   constructor(message, statusCode, errorCode) {
 | |
|     super(message);
 | |
|     this.statusCode = statusCode;
 | |
|     this.errorCode = errorCode;
 | |
|     this.isOperational = true;
 | |
| 
 | |
|     Error.captureStackTrace(this, this.constructor);
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = {
 | |
|   errorHandler,
 | |
|   notFound,
 | |
|   AppError
 | |
| }; |