import { Request, Response, NextFunction } from 'express' import { ZodError } from 'zod' import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library' import { AppError } from '../lib/AppError' import logger from '../lib/logger' export function errorHandler(err: unknown, _req: Request, res: Response, _next: NextFunction) { if (err instanceof AppError) { return res.status(err.statusCode).json({ success: false, error: { code: err.code, message: err.message }, }) } if (err instanceof ZodError) { return res.status(422).json({ success: false, error: { code: 'VALIDATION_ERROR', message: '输入校验失败', details: err.errors.map((e) => ({ path: e.path.join('.'), message: e.message })), }, }) } if (err instanceof PrismaClientKnownRequestError) { if (err.code === 'P2002') { return res.status(400).json({ success: false, error: { code: 'DUPLICATE', message: '数据已存在,请勿重复操作' }, }) } if (err.code === 'P2025') { return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' }, }) } } if (typeof err === 'object' && err !== null && 'code' in err && 'message' in err) { const e = err as { code: string; message: string } const statusMap: Record = { NOT_FOUND: 404, CONFLICT: 409, VALIDATION_ERROR: 422, PLAN_LIMIT: 403, AUTH_FAILED: 401, TOKEN_INVALID: 401, ACCOUNT_DISABLED: 403, DUPLICATE: 400, } return res.status(statusMap[e.code] || 400).json({ success: false, error: { code: e.code, message: e.message }, }) } logger.error({ err }, 'Unhandled error') return res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: '服务器内部错误' }, }) }