3. 异常与错误码设计:错误码编码规范、错误码分段规则、错误码与HTTP状态码映射

在企业级系统中,错误码是异常信息的“数字指纹”。一套严谨的错误码体系,能让运维人员、客户端、监控系统在毫秒级定位问题根因,避免“查日志猜错误”的低效模式。本章从编码规范、分段规则、HTTP 状态码映射三个维度,构建可落地的错误码骨架。

3.1 错误码编码规范:三段式结构

推荐采用 “系统标识 + 模块编号 + 错误序号” 的三段式结构,总长度固定为 8 位数字,便于机器解析和人工记忆。

段位 长度 说明 示例
系统标识 2 位 区分核心业务域,如 01=交易、02=用户、03=库存 01
模块编号 3 位 标识子系统或功能模块,如 001=订单、002=支付 001
错误序号 3 位 具体错误类型,从 001 开始顺序分配 015

完整示例:01001015 表示“交易系统-订单模块-库存不足”。

编码原则:
  • 唯一性:全局不重复,废弃错误码不再复用。
  • 可扩展:每个段位预留 20% 的编号空间,避免后期重构。
  • 可读性:错误序号按严重程度分组:001-099 为业务警告,100-199 为系统错误,200-299 为外部依赖错误。

3.2 错误码分段规则:按严重程度与来源划分

错误码不仅标识“发生了什么”,还要隐含“有多严重”和“谁导致的”。分段规则将错误码划分为三大区间:

错误码分段规则架构图 业务警告 错误序号 001-099 参数校验、状态冲突 系统错误 错误序号 100-199 数据库异常、配置缺失 外部依赖 错误序号 200-299 RPC超时、第三方拒绝 分段规则说明 • 业务警告:客户端可自行修复,如“手机号格式错误”。 • 系统错误:需运维或开发介入,如“数据库连接池耗尽”。 • 外部依赖:需通知第三方或重试,如“支付网关超时”。
实践建议:在错误码枚举类中,通过注释标注每个错误码的分段归属。例如:// 业务警告 - 订单模块 - 库存不足。这能让 IDE 在编码阶段就提示严重程度,减少误用。

3.3 错误码与HTTP状态码映射

错误码是业务层面的标识,HTTP 状态码是传输层面的协议。两者需要建立明确的映射关系,避免客户端收到 500 却不知道具体业务含义,或收到 200 但 body 里藏着错误。

HTTP 状态码 含义 适用错误码分段 典型场景
400 Bad Request 客户端请求格式错误 业务警告 (001-099) 参数校验失败、JSON 解析错误
401 Unauthorized 未认证或认证过期 业务警告 (001-099) Token 无效、签名错误
403 Forbidden 无权限访问 业务警告 (001-099) 角色不足、IP 白名单拒绝
404 Not Found 资源不存在 业务警告 (001-099) 订单号不存在、用户未找到
409 Conflict 资源状态冲突 业务警告 (001-099) 重复提交、乐观锁版本冲突
429 Too Many Requests 请求频率超限 业务警告 (001-099) API 限流、防刷
500 Internal Server Error 服务器内部错误 系统错误 (100-199) 数据库异常、空指针、配置错误
502 Bad Gateway 上游服务不可用 外部依赖 (200-299) RPC 超时、下游宕机
503 Service Unavailable 服务暂时过载 系统错误 (100-199) 连接池耗尽、CPU 满载
504 Gateway Timeout 上游响应超时 外部依赖 (200-299) 第三方接口超时、DNS 解析失败
常见反模式:所有错误都返回 200,仅在 body 中放错误码。这会导致网关层无法根据状态码做熔断、限流、重试,且客户端必须解析 body 才能判断成败。正确的做法是:HTTP 状态码反映传输层成败,错误码反映业务层细节。

3.4 工程落地:错误码枚举与响应体结构

在 Java 工程中,推荐使用枚举类集中管理错误码,避免散落在各处魔法数字。

public enum ErrorCode {
    // 业务警告 - 订单模块
    ORDER_STOCK_INSUFFICIENT("01001015", "库存不足"),
    ORDER_PRICE_CHANGED("01001016", "价格已变动"),

    // 系统错误 - 订单模块
    ORDER_DB_TIMEOUT("01001101", "订单数据库超时"),
    ORDER_CACHE_UNAVAILABLE("01001102", "订单缓存不可用"),

    // 外部依赖 - 支付模块
    PAY_GATEWAY_TIMEOUT("01002201", "支付网关超时"),
    PAY_THIRD_PARTY_REJECT("01002202", "第三方支付拒绝");

    private final String code;
    private final String message;

    ErrorCode(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getCode() { return code; }
    public String getMessage() { return message; }
}

统一响应体结构示例:

{
  "success": false,
  "code": "01001015",
  "message": "库存不足",
  "timestamp": 1712345678000,
  "path": "/api/order/create",
  "traceId": "a1b2c3d4e5f6"
}
关键设计点:
  • success 字段让客户端无需解析 code 即可判断成败。
  • traceId 关联日志链路,便于快速定位完整请求路径。
  • path 记录请求入口,方便监控系统按接口聚合错误分布。

3.5 错误码生命周期管理

错误码不是一次性定义就结束的,需要纳入变更管理流程:

  • 新增:必须经过架构评审,确认不与其他错误码语义重叠。
  • 废弃:标记为 @Deprecated,保留至少两个大版本后再移除。
  • 文档化:每个错误码在 Wiki 或 API 文档中记录触发条件、处理建议、示例日志。
  • 监控告警:系统错误和外部依赖段的错误码出现时,自动触发 P0/P1 告警。

通过以上规范,错误码从“一串数字”进化为“可编程的异常语义单元”,为后续的监控、告警、自动化运维打下坚实基础。