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
|
|
}; |