/** * v2 API 统一错误码系统 * * 设计原则: * - code: 0 表示成功(与 HTTP 状态码解耦) * - 非零 code 表示失败,按模块分段 * - 兼容 v1(code: 200 = 成功),通过 v2 响应头区分版本 * * 错误码分段: * 0 — 成功 * 1xxx — 通用错误(参数/鉴权/权限/限流) * 2xxx — 用户模块 * 3xxx — 商品模块 * 4xxx — 订单模块 * 5xxx — 购物车模块 * 6xxx — 库存模块 * 7xxx — 退款模块 * 8xxx — 积分模块 * 9xxx — 其他模块 */ // ============ 通用错误码 1xxx ============ const SUCCESS = 0 const ERR_BAD_REQUEST = 1000 // 请求参数错误 const ERR_UNAUTHORIZED = 1001 // 未登录/Token 无效 const ERR_FORBIDDEN = 1002 // 无权限 const ERR_NOT_FOUND = 1003 // 资源不存在 const ERR_CONFLICT = 1004 // 资源冲突 const ERR_RATE_LIMIT = 1005 // 请求过于频繁 const ERR_INTERNAL = 1006 // 服务器内部错误 const ERR_VALIDATION = 1007 // 数据校验失败 const ERR_DEPRECATED = 1008 // 接口已废弃 // ============ 用户模块 2xxx ============ const ERR_USER_NOT_FOUND = 2001 const ERR_USER_PASSWORD = 2002 // 密码错误 const ERR_USER_DISABLED = 2003 // 账号已禁用 const ERR_USER_EXISTS = 2004 // 用户已存在 const ERR_USER_PHONE_INVALID = 2005 // ============ 商品模块 3xxx ============ const ERR_GOODS_NOT_FOUND = 3001 const ERR_GOODS_OFF_SHELF = 3002 // 商品已下架 const ERR_GOODS_STOCK_LOW = 3003 // 库存不足 const ERR_GOODS_NAME_DUPLICATE = 3004 // ============ 订单模块 4xxx ============ const ERR_ORDER_NOT_FOUND = 4001 const ERR_ORDER_STATUS = 4002 // 订单状态不允许此操作 const ERR_ORDER_EMPTY = 4003 // 订单为空 const ERR_ORDER_CANNOT_CANCEL = 4004 // ============ 购物车模块 5xxx ============ const ERR_CART_ITEM_NOT_FOUND = 5001 const ERR_CART_QUANTITY_INVALID = 5002 const ERR_CART_GOODS_OFF_SHELF = 5003 // ============ 库存模块 6xxx ============ const ERR_STOCK_NEGATIVE = 6001 // 库存不能为负 const ERR_STOCK_LOG_NOT_FOUND = 6002 // ============ 退款模块 7xxx ============ const ERR_REFUND_NOT_FOUND = 7001 const ERR_REFUND_AMOUNT_INVALID = 7002 const ERR_REFUND_DUPLICATE = 7003 // 已有待处理退款 const ERR_REFUND_STATUS = 7004 // 退款状态不允许此操作 // ============ 积分模块 8xxx ============ const ERR_POINTS_INSUFFICIENT = 8001 const ERR_POINTS_GOODS_NOT_FOUND = 8002 const ERR_POINTS_GOODS_OFF_SHELF = 8003 const ERR_POINTS_GOODS_NO_STOCK = 8004 const ERR_POINTS_DELTA_EXCEED = 8005 // 积分变动超限 // ============ 错误码映射表 ============ const ERROR_MESSAGES = { [SUCCESS]: 'success', [ERR_BAD_REQUEST]: '请求参数错误', [ERR_UNAUTHORIZED]: '未登录或登录已过期', [ERR_FORBIDDEN]: '无权限访问', [ERR_NOT_FOUND]: '资源不存在', [ERR_CONFLICT]: '资源冲突', [ERR_RATE_LIMIT]: '请求过于频繁,请稍后再试', [ERR_INTERNAL]: '服务器内部错误', [ERR_VALIDATION]: '数据校验失败', [ERR_DEPRECATED]: '接口已废弃', [ERR_USER_NOT_FOUND]: '用户不存在', [ERR_USER_PASSWORD]: '密码错误', [ERR_USER_DISABLED]: '账号已禁用', [ERR_USER_EXISTS]: '用户已存在', [ERR_USER_PHONE_INVALID]: '手机号格式错误', [ERR_GOODS_NOT_FOUND]: '商品不存在', [ERR_GOODS_OFF_SHELF]: '商品已下架', [ERR_GOODS_STOCK_LOW]: '库存不足', [ERR_GOODS_NAME_DUPLICATE]: '商品名称已存在', [ERR_ORDER_NOT_FOUND]: '订单不存在', [ERR_ORDER_STATUS]: '订单状态不允许此操作', [ERR_ORDER_EMPTY]: '订单为空', [ERR_ORDER_CANNOT_CANCEL]: '订单无法取消', [ERR_CART_ITEM_NOT_FOUND]: '购物车商品不存在', [ERR_CART_QUANTITY_INVALID]: '数量无效', [ERR_CART_GOODS_OFF_SHELF]: '商品已下架', [ERR_STOCK_NEGATIVE]: '库存不能为负数', [ERR_STOCK_LOG_NOT_FOUND]: '库存记录不存在', [ERR_REFUND_NOT_FOUND]: '退款申请不存在', [ERR_REFUND_AMOUNT_INVALID]: '退款金额无效', [ERR_REFUND_DUPLICATE]: '已有待处理的退款申请', [ERR_REFUND_STATUS]: '退款状态不允许此操作', [ERR_POINTS_INSUFFICIENT]: '积分不足', [ERR_POINTS_GOODS_NOT_FOUND]: '积分商品不存在', [ERR_POINTS_GOODS_OFF_SHELF]: '积分商品已下架', [ERR_POINTS_GOODS_NO_STOCK]: '积分商品库存不足', [ERR_POINTS_DELTA_EXCEED]: '积分变动超出允许范围', } // ============ v1 错误码 → v2 映射 ============ const V1_TO_V2_MAP = { 200: SUCCESS, 400: ERR_BAD_REQUEST, 401: ERR_UNAUTHORIZED, 403: ERR_FORBIDDEN, 404: ERR_NOT_FOUND, 500: ERR_INTERNAL, } // ============ v2 错误码 → HTTP 状态码映射 ============ const CODE_TO_HTTP_STATUS = { [SUCCESS]: 200, [ERR_BAD_REQUEST]: 400, [ERR_UNAUTHORIZED]: 401, [ERR_FORBIDDEN]: 403, [ERR_NOT_FOUND]: 404, [ERR_CONFLICT]: 409, [ERR_RATE_LIMIT]: 429, [ERR_INTERNAL]: 500, [ERR_VALIDATION]: 422, [ERR_DEPRECATED]: 410, } // ============ 工具函数 ============ /** * 生成 v2 标准响应 * @param {number} code - 错误码(0 = 成功) * @param {*} data - 响应数据 * @param {string} [message] - 自定义消息(默认从映射表取) * @returns {{ code: number, data: *, message: string }} */ function respond(code, data, message) { return { code, data: code === SUCCESS ? data : null, message: message || ERROR_MESSAGES[code] || '未知错误', } } /** * 成功响应快捷方法 */ function success(data, message) { return respond(SUCCESS, data, message) } /** * 错误响应快捷方法 */ function error(code, message) { return respond(code, null, message) } /** * 将 v1 风格响应转换为 v2 风格 * v1: { code: 200, data, message } * v2: { code: 0, data, message } */ function fromV1(v1Body) { if (!v1Body || typeof v1Body.code !== 'number') return v1Body const v2Code = V1_TO_V2_MAP[v1Body.code] ?? v1Body.code return { code: v2Code, data: v2Code === SUCCESS ? v1Body.data : null, message: v1Body.message || ERROR_MESSAGES[v2Code] || '', } } /** * 获取 v2 错误码对应的 HTTP 状态码 */ function toHttpStatus(code) { return CODE_TO_HTTP_STATUS[code] || 400 } module.exports = { // 错误码常量 SUCCESS, ERR_BAD_REQUEST, ERR_UNAUTHORIZED, ERR_FORBIDDEN, ERR_NOT_FOUND, ERR_CONFLICT, ERR_RATE_LIMIT, ERR_INTERNAL, ERR_VALIDATION, ERR_DEPRECATED, ERR_USER_NOT_FOUND, ERR_USER_PASSWORD, ERR_USER_DISABLED, ERR_USER_EXISTS, ERR_USER_PHONE_INVALID, ERR_GOODS_NOT_FOUND, ERR_GOODS_OFF_SHELF, ERR_GOODS_STOCK_LOW, ERR_GOODS_NAME_DUPLICATE, ERR_ORDER_NOT_FOUND, ERR_ORDER_STATUS, ERR_ORDER_EMPTY, ERR_ORDER_CANNOT_CANCEL, ERR_CART_ITEM_NOT_FOUND, ERR_CART_QUANTITY_INVALID, ERR_CART_GOODS_OFF_SHELF, ERR_STOCK_NEGATIVE, ERR_STOCK_LOG_NOT_FOUND, ERR_REFUND_NOT_FOUND, ERR_REFUND_AMOUNT_INVALID, ERR_REFUND_DUPLICATE, ERR_REFUND_STATUS, ERR_POINTS_INSUFFICIENT, ERR_POINTS_GOODS_NOT_FOUND, ERR_POINTS_GOODS_OFF_SHELF, ERR_POINTS_GOODS_NO_STOCK, ERR_POINTS_DELTA_EXCEED, // 映射表 ERROR_MESSAGES, V1_TO_V2_MAP, CODE_TO_HTTP_STATUS, // 工具函数 respond, success, error, fromV1, toHttpStatus, }