本文目录导读:

在Java项目中,设计统一的错误码结构至关重要,它能帮助开发者快速定位问题、方便前端处理异常、以及形成标准化的API文档。
下面提供几个从简单到复杂的统一错误码结构案例,包含枚举定义、接口规范、异常类和DTO输出。
基础枚举型(最常用,适合中小型项目)
特点:将错误码和消息定义在枚举中,通过接口约束。
定义错误码接口(可选,但推荐)
public interface IErrorCode {
/**
* 错误码
*/
Integer getCode();
/**
* 错误消息
*/
String getMessage();
}
枚举实现
public enum SystemErrorCode implements IErrorCode {
// 通用成功
SUCCESS(10000, "操作成功"),
// 通用系统错误 (5位数字: 2位子系统 + 3位具体错误)
SYSTEM_ERROR(10001, "系统内部异常"),
SYSTEM_BUSY(10002, "系统繁忙,请稍后再试"),
SERVICE_TIMEOUT(10003, "服务超时"),
// 参数校验错误
PARAM_ILLEGAL(20001, "参数不合法"),
PARAM_MISSING(20002, "缺少必要参数"),
PARAM_TYPE_ERROR(20003, "参数类型错误"),
PARAM_FORMAT_ERROR(20004, "参数格式错误"),
// 业务逻辑错误 (订单模块 300xx)
ORDER_NOT_FOUND(30001, "订单不存在"),
ORDER_STATUS_ERROR(30002, "订单状态异常"),
ORDER_DUPLICATE(30003, "订单重复提交"),
// 用户鉴权错误 (400xx)
TOKEN_EXPIRED(40001, "Token已过期"),
TOKEN_INVALID(40002, "Token无效"),
USER_NOT_LOGIN(40003, "用户未登录"),
PERMISSION_DENIED(40004, "权限不足"),
// 外部服务错误 (500xx)
THIRD_PARTY_ERROR(50001, "调用第三方服务异常"),
DATABASE_ERROR(50002, "数据库操作失败"),
REDIS_ERROR(50003, "Redis操作失败");
private final Integer code;
private final String message;
SystemErrorCode(Integer code, String message) {
this.code = code;
this.message = message;
}
@Override
public Integer getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
自定义业务异常类
public class BusinessException extends RuntimeException {
private IErrorCode errorCode;
private String detailMessage; // 用于填充具体的错误详情(如 userName 不存在)
public BusinessException(IErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
public BusinessException(IErrorCode errorCode, String detailMessage) {
super(errorCode.getMessage() + ":" + detailMessage);
this.errorCode = errorCode;
this.detailMessage = detailMessage;
}
public IErrorCode getErrorCode() {
return errorCode;
}
public Integer getCode() {
return errorCode.getCode();
}
public String getDetailMessage() {
return detailMessage;
}
}
统一响应体(DTO)
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL) // 空值不返回
public class ApiResult<T> {
private Integer code;
private String message;
private T data;
// 静态工厂方法
public static <T> ApiResult<T> success(T data) {
ApiResult<T> result = new ApiResult<>();
result.code = SystemErrorCode.SUCCESS.getCode();
result.message = SystemErrorCode.SUCCESS.getMessage();
result.data = data;
return result;
}
public static <T> ApiResult<T> error(IErrorCode errorCode) {
ApiResult<T> result = new ApiResult<>();
result.code = errorCode.getCode();
result.message = errorCode.getMessage();
return result;
}
public static <T> ApiResult<T> error(IErrorCode errorCode, String detail) {
ApiResult<T> result = new ApiResult<>();
result.code = errorCode.getCode();
result.message = errorCode.getMessage() + ":" + detail;
return result;
}
public static <T> ApiResult<T> error(int code, String message) {
ApiResult<T> result = new ApiResult<>();
result.code = code;
result.message = message;
return result;
}
}
可扩展/模块化分级(适合大型微服务项目)
思想:不同模块(用户、订单、商品)有独立的错误码段,且支持动态构建。
统一错误码前缀定义
public enum ErrorCodePrefix {
COMMON("COM", "通用"),
USER("USR", "用户模块"),
ORDER("ORD", "订单模块"),
PAYMENT("PAY", "支付模块"),
PRODUCT("PRO", "商品模块");
private final String prefix;
private final String desc;
ErrorCodePrefix(String prefix, String desc) {
this.prefix = prefix;
this.desc = desc;
}
public String getPrefix() {
return prefix;
}
}
模块化错误码枚举
以用户模块为例:
public enum UserErrorCode implements IErrorCode {
USER_REGISTER_FAIL(ErrorCodePrefix.USER, 001, "用户注册失败"),
USER_NOT_FOUND(ErrorCodePrefix.USER, 002, "用户未找到"),
USER_PASSWORD_ERROR(ErrorCodePrefix.USER, 003, "密码错误"),
USER_LOCKED(ErrorCodePrefix.USER, 004, "账户已锁定");
private final String prefix;
private final int seq; // 顺序号
private final String message;
// 最终代号如: USR_001
private final String code;
UserErrorCode(ErrorCodePrefix prefix, int seq, String message) {
this.prefix = prefix.getPrefix();
this.seq = seq;
this.message = message;
// 使用字符串作为错误码,方便阅读
this.code = prefix.getPrefix() + "_" + String.format("%03d", seq);
}
@Override
public String getCode() { // 这里改回 String 型
return code;
}
@Override
public String getMessage() {
return message;
}
}
注意:这里IErrorCode的getCode()返回类型要改为String,或者保留Integer的话,可以映射数字,但字符串前缀更适合大型文档归类。
国际化支持版
扩展:当需要返回多语言时,枚举只存code和i18n key。
public enum I18nErrorCode implements IErrorCode {
SUCCESS(10000, "common.success"),
SYSTEM_ERROR(10001, "common.system.error"),
PARAM_ILLEGAL(20001, "param.illegal");
private final Integer code;
private final String i18nKey;
@Override
public Integer getCode() {
return code;
}
@Override
public String getMessage() {
// 这里可以直接返回 i18nKey,或者由全局异常处理器根据 Locale 转换
return i18nKey;
}
}
在前端或全局异常处理器中,通过Locale从MessageSource获取真实消息。
全局异常捕获(Spring Boot示例)
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ApiResult<?> handleBusinessException(BusinessException e) {
return ApiResult.error(e.getErrorCode(), e.getDetailMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResult<?> handleValidationException(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getFieldErrors().stream()
.map(error -> error.getField() + ":" + error.getDefaultMessage())
.collect(Collectors.joining("; "));
return ApiResult.error(SystemErrorCode.PARAM_ILLEGAL.getCode(), msg);
}
@ExceptionHandler(Exception.class)
public ApiResult<?> handleUnknownException(Exception e) {
// 生产环境建议记录日志,返回通用错误
log.error("未知异常", e);
return ApiResult.error(SystemErrorCode.SYSTEM_ERROR);
}
}
总结建议
| 项目规模 | 推荐结构 | 原因 |
|---|---|---|
| 小项目 / 快速原型 | 案例一(枚举 + Integer code) | 简单直接,无需复杂管理 |
| 中大型项目 | 案例二(模块化字符串编码) | 可读性强,方便按模块归类 |
| 海外多语言项目 | 案例三(i18n key) | 方便统一国际化 |
编码规范建议:
- 成功码建议统一为 10000(避免与前端的 200 混淆)
- 错误码采用 5 位数字:
XXYYY(XX=模块,YYY=具体错误) - 或采用字符串:
MODULE_001 - 永远不要在代码里硬编码数字
return 40001;,始终引用枚举
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播