天天看點

讓錯誤碼規範起來吧

作者:京東雲開發者

一、前言

1、不規範的錯誤碼有什麼問題?

1)了解困難

描述:如果錯誤碼的命名或描述不清晰,可能導緻其他開發人員難以了解其含義。

舉例:例如,一個錯誤碼命名為“ERR1001”,沒有進一步的注釋或描述,可能導緻其他開發人員不知道這個錯誤碼代表的具體問題。

2)不一緻性

描述: 如果錯誤碼的命名、描述或分類不統一,可能導緻代碼的可讀性和可維護性降低。

舉例:例如,有的錯誤碼使用三位數,有的使用兩位數;有的錯誤碼描述具體的問題,而有的描述則較為模糊。

3)排查困難

描述:如果錯誤碼沒有清晰的命名和描述,可能使得調試過程變得困難。

舉例:當出現問題時,開發人員需要檢視大量的日志或代碼來定位問題所在。

4)備援和重複

描述:如果錯誤碼過多或過于複雜,可能導緻代碼中的錯誤處理邏輯變得備援和重複。

舉例:同一個錯誤可能在不同地方有不同的錯誤碼,導緻處理邏輯重複。

5)擴充性差

描述:如果錯誤碼已經定義但後來需要添加新的錯誤碼,可能需要修改多個地方的代碼,增加了維護成本。

2、規範的錯誤碼那麼好,為什麼不規範使用呢?

1)缺乏規範和标準:

在某些情況下,可能沒有明确的規範或标準來指導如何使用錯誤碼。這可能導緻開發人員根據自己的了解和習慣來定義錯誤碼,進而導緻不規範的情況。

2)缺乏意識和經驗:

某些開發人員可能沒有意識到錯誤碼規範化的重要性,或者缺乏足夠的經驗來正确地設計和使用錯誤碼。

3)曆史遺留問題:

在某些項目中,錯誤碼可能已經使用了很長時間,而且已經成為了代碼的一部分。在這種情況下,重新規範化錯誤碼可能會涉及到大量的代碼修改和測試,這可能會被視為成本較高。

4)個人習慣和偏好:

某些開發人員可能更傾向于按照自己的習慣和偏好來使用錯誤碼,而不是遵循團隊的規範。這可能會導緻代碼中的錯誤碼使用不一緻。

3、那怎麼規範錯誤碼呢

1)制定規範和标準:

團隊可以制定明确的規範和标準,指導如何使用錯誤碼,并将其納入代碼審查和開發流程中。

2)教育訓練和指導:

為新開發人員提供教育訓練和指導,使其了解如何正确地設計和使用錯誤碼。

3)重構和改進:

對于曆史遺留問題,可以通過逐漸重構和改進的方式來規範化錯誤碼的使用。

4)代碼審查和團隊協同:

通過代碼審查和團隊協同來確定錯誤碼的規範化和一緻性。

二、規範錯誤碼

1、錯誤碼-分片區

根據号段區分錯誤類型,這裡長度定義了5位,可以根據自己系統規模調整長度
錯誤碼 描述
00000 成功
10000 參數錯誤
20000 業務處理失敗(業務上給使用者吐出)
30000 RPC處理失敗 --->> 系統_失敗分類(請求0、傳回1)_業務_方法_調用方CODE碼(代碼補齊),極端情況下吐出 99999
40000 運作處理失敗:一般内部處理使用,極端情況下吐出 99999
99999 系統太火爆了,請稍後重試! -->極端情況下才吐出
@Getter
@AllArgsConstructor
enum ErrorCodeEnum implements CodeEnum {

    ERROR_CODE_SUCCESS("00000", "成功"),
    ERROR_CODE_PARAMS_ERROR("10000", "參數錯誤"),
    ERROR_CODE_BUSINESS_ERROR("20000", "業務處理失敗"),
    ERROR_CODE_PRC_ERROR("30000", "RPC處理失敗"),
    ERROR_CODE_RUNTIME_ERROR("40000", "運作時失敗"),
    ERROR_CODE_FAIL("99999", "系統太火爆了,請稍後重試!"),
    ;
    private final String code;
    private final String msg;

}

           

2、10000-參數異常

非常簡單,直接吐出即可
參數 說明
code 錯誤碼
msg 傳回錯誤資訊
@Getter
  @AllArgsConstructor
  enum ParamsErrorEnum implements CodeEnum {

      ERROR_CODE_10000("10000", "參數錯誤"),
      ERROR_CODE_10001("10001", "不支援的請求方式"),
      ERROR_CODE_10002("10002", "參數格式異常"),

      ;
      private final String code;

      private final String msg;


  }
           

3、20000-業務異常

參數 說明
code 錯誤碼
msg 底層-錯誤資訊
showMsg 吐出-錯誤資訊
@Getter
@AllArgsConstructor
enum BusinessErrorEnum implements CodeEnum {

    ERROR_CODE_20000("20000", "業務處理失敗", "系統太火爆了,請稍後重試!"),
    ERROR_CODE_20001("20001", "訂單建立失敗", "您有一個訂單正在建立,請稍後檢視"),
    ERROR_CODE_20002("20002", "付款失敗,存在建立中的訂單", "您的訂單付款失敗,請稍後檢視"),
    ERROR_CODE_20003("20003", "付款失敗,存在未支付的訂單", "您的訂單付款失敗,請稍後檢視"),

    ;
    private final String code;

    private final String msg;

    private final String showMsg;
}
           

4、30000-RPC異常

該異常一定要合理使用,這樣會讓微服務直接錯誤資訊更明确

說明:系統_失敗分類(請求0、傳回1)_業務_調用方法_調用方CODE碼(代碼補齊)

字段 說明
系統 調用系統:如使用者系統 User
失敗分類 請求失敗:0;傳回失敗:1
業務 業務(3開頭):使用者資訊業務:30001
調用方法 業務調用方法(指定一個數字,確定是該業務唯一的方法)
調用方Code碼 後續代碼中補齊,具體看異常抛出錯誤碼使用
參數 說明
code 錯誤碼:系統_失敗分類(請求0、傳回1)_業務_方法_調用方CODE碼(代碼補齊)
msg 底層-錯誤資訊
@Getter
@AllArgsConstructor
enum RpcErrorEnum implements CodeEnum {

    /**
     * 系統_失敗分類(請求0、傳回1)_業務_方法_調用方CODE碼(代碼補齊)
     */
    ERROR_CODE_USER_0_30001_0001("USER_0_30001_0001", "RPC異常-USER-使用者資訊-查詢單個使用者資訊-接口調用失敗"),
    ERROR_CODE_USER_1_30001_0001("USER_1_30001_0001", "RPC異常-USER-使用者資訊-查詢單個使用者資訊-接口傳回失敗"),
    ERROR_CODE_USER_1_30001_0002("USER_1_30001_0002", "RPC異常-USER-使用者資訊-分頁查詢使用者資訊-接口傳回失敗"),
    ;
    private final String code;
    private final String msg;
}
           

5、40000-運作異常

@Getter
@AllArgsConstructor
enum PlatformErrorEnum implements CodeEnum {

    ERROR_CODE_40000("40000", "運作時失敗"),
    ERROR_CODE_40001("40001", "路由消息處理失敗"),

    ;
    private final String code;

    private final String msg;

}

           

三、錯誤碼使用

1、10000-參數異常

構造器 說明
ParameterException(CodeEnum.ParamsErrorEnum paramErrorEnum) 建議使用:傳入一個固定的枚舉值
ParameterException(String message) 不推薦:傳入一個錯誤資訊
@Getter
public class ParameterException extends RuntimeException {


    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = -6114625076221233075L;
    /**
     * 傳回錯誤碼
     */
    private final String code;


    /**
     * BusinessException
     *
     * @param paramErrorEnum paramErrorEnum
     */
    public ParameterException(CodeEnum.ParamsErrorEnum paramErrorEnum) {
        super(paramErrorEnum.getMsg());
        this.code = paramErrorEnum.getCode();
    }

    /**
     * ParameterErrorException
     *
     * @param message message
     */
    public ParameterException(String message) {
        super(message);
        this.code = CodeEnum.ErrorCodeEnum.ERROR_CODE_PARAMS_ERROR.getCode();
    }

}
           

2、20000-業務異常

構造器 說明
BusinessException(CodeEnum.BusinessErrorEnum businessErrorEnum) 建議使用:傳入一個固定的枚舉值
BusinessException(String message) 不推薦:傳入一個錯誤資訊
@Getter
public class BusinessException extends RuntimeException {

    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = 799633539625676004L;

    /**
     * 傳回錯誤碼
     */
    private final String code;

    /**
     * 展示資訊
     */
    private final String showMsg;


    /**
     * BusinessException
     *
     * @param businessErrorEnum businessErrorEnum
     */
    public BusinessException(CodeEnum.BusinessErrorEnum businessErrorEnum) {
        super(businessErrorEnum.getMsg());
        this.code = businessErrorEnum.getCode();
        this.showMsg = businessErrorEnum.getShowMsg();

    }

    /**
     * BusinessException
     *
     * @param message message
     */
    public BusinessException(String message) {
        super(message);
        this.code = CodeEnum.ErrorCodeEnum.ERROR_CODE_BUSINESS_ERROR.getCode();
        this.showMsg = message;
    }

}
           

3、30000-RPC異常

構造器 說明
RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum) 場景:處理未知的RPC異常,如網絡逾時等
RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum, String code, String showMsg) 場景:處理已知的異常
RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum, String code, String msg, String showMsg) 場景:處理已知的異常
@Getter
public class RpcException extends RuntimeException {

    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = 799633539625676004L;

    /**
     * 傳回錯誤碼
     */
    private final String code;

    /**
     * 展示資訊
     */
    private final String showMsg;

    /**
     * RpcException-處理未知的異常
     *
     *
     * @param rpcErrorEnum rpcErrorEnum
     */
    public RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum) {
        super(rpcErrorEnum.getMsg());
        this.code = rpcErrorEnum.getCode();
        this.showMsg = CodeEnum.ErrorCodeEnum.ERROR_CODE_FAIL.getMsg();
    }

    /**
     * RpcException 處理已知的異常
     *
     * @param rpcErrorEnum rpcErrorEnum
     * @param code         code
     * @param showMsg      showMsg
     */
    public RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum, String code, String showMsg) {
        super(rpcErrorEnum.getMsg());
        this.code = rpcErrorEnum.getCode() + "_" + code;
        this.showMsg = showMsg;
    }

    /**
     * RpcException 處理已知的異常
     *
     * @param rpcErrorEnum rpcErrorEnum
     * @param code         code
     * @param showMsg      showMsg
     */
    public RpcException(CodeEnum.RpcErrorEnum rpcErrorEnum, String code, String msg, String showMsg) {
        super(msg);
        this.code = rpcErrorEnum.getCode() + "_" + code;
        this.showMsg = showMsg;
    }

}
           

4、40000-運作異常

@Getter
public class PlatformException extends RuntimeException {

    private static final long serialVersionUID = 5535821215702463243L;
    /**
     * 傳回錯誤碼
     */
    private final String code;

    /**
     * 展示資訊
     */
    private final String showMsg;

    /**
     * PlatformException
     *
     * @param platformErrorEnum platformErrorEnum
     */
    public PlatformException(CodeEnum.PlatformErrorEnum platformErrorEnum) {
        super(platformErrorEnum.getMsg());
        this.code = platformErrorEnum.getCode();
        this.showMsg = CodeEnum.ErrorCodeEnum.ERROR_CODE_FAIL.getMsg();

    }
}
           

四、異常吐出

1、10000-參數異常

/**
 * 不支援的請求方始
 */
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
@ResponseStatus(value = HttpStatus.METHOD_NOT_ALLOWED)
public BaseRes<?> methodNotSupportExceptionHandler(HttpRequestMethodNotSupportedException e) {
    log.error("不支援的請求方式", e);
    return BaseRes.buildFailure(CodeEnum.ParamsErrorEnum.ERROR_CODE_10001.getCode(), e.getMessage());
}

/**
 * 參數類型錯誤
 */
@ExceptionHandler(value = {BindException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> bindExceptionHandler(BindException e) {
    log.error("====參數類型錯誤===", e);
    return BaseRes.buildFailure(CodeEnum.ParamsErrorEnum.ERROR_CODE_10000);
}

/**
 * 參數格式問題
 */
@ExceptionHandler(value = {MethodArgumentTypeMismatchException.class, 
                           HttpMessageConversionException.class, UnexpectedTypeException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> httpMessageConversionExceptionHandler(Exception e) {
    log.error("====參數格式異常===", e);
    return BaseRes.buildFailure(CodeEnum.ParamsErrorEnum.ERROR_CODE_10002);
}

/**
 * 參數錯誤
 */
@ExceptionHandler(value = ParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> parameterErrorExceptionHandler(ParameterException e) {
    log.error("====參數異常:code:{},msg:{}", e.getCode(), e.getMessage(), e);
    return BaseRes.buildFailure(e.getCode(), e.getMessage());
}

           

2、20000-業務異常

/**
 * 業務異常,給前台傳回異常資料
 */
@ExceptionHandler(value = BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> businessExceptionHandler(BusinessException e) {
    log.error("====業務異常:code:{},msg:{},showMsg:{}", e.getCode(), e.getMessage(), e.getShowMsg(), e);
    return BaseRes.buildFailure(e.getCode(), e.getShowMsg());
}

           

3、30000-RPC異常

/**
 * RPC,給前台傳回異常資料
 */
@ExceptionHandler(value = RpcException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> rpcExceptionHandler(RpcException e) {
    log.error("====RPC異常:code:{},msg:{},showMsg:{}", e.getCode(), e.getMessage(), e.getShowMsg(), e);
    return BaseRes.buildFailure(e.getCode(), e.getShowMsg());
}
           

4、40000-運作異常

/**
 * 運作異常,給前台傳回異常資料
 */
@ExceptionHandler(value = PlatformException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public BaseRes<?> rpcExceptionHandler(PlatformException e) {
    log.error("====運作異常:code:{},msg:{},showMsg:{}", e.getCode(), e.getMessage(), e.getShowMsg(), e);
    return BaseRes.buildFailure(e.getCode(), e.getShowMsg());
}
           

五、Demo

1、10000-參數異常

@ApiOperation("parameterExceptionEnum")
@LogIndex
@GetMapping("parameterExceptionEnum")
@ResponseBody
public BaseRes<List<UserDemoVO>> parameterExceptionEnum() {
    throw new ParameterException(CodeEnum.ParamsErrorEnum.ERROR_CODE_10002);
}

{
    "success": false,
    "data": null,
    "msg": "參數格式異常",
    "code": "10002"
}
           
@ApiOperation("parameterExceptionMsg")
@LogIndex
@GetMapping("parameterExceptionMsg")
@ResponseBody
public BaseRes<List<UserDemoVO>> parameterExceptionMsg() {
    throw new ParameterException("使用者Id不能為空");
}


{
    "success": false,
    "data": null,
    "msg": "使用者Id不能為空",
    "code": "10000"
}
           

2、20000-業務異常

@ApiOperation("businessExceptionEnum")
@LogIndex
@GetMapping("businessExceptionEnum")
@ResponseBody
public BaseRes<List<UserDemoVO>> businessExceptionEnum() {
    throw new BusinessException(CodeEnum.BusinessErrorEnum.ERROR_CODE_20001);
}


{
  "success": false,
  "data": null,
  "msg": "您有一個訂單正在建立,請稍後檢視",
  "code": "20001"
}
           
@ApiOperation("businessExceptionMsg")
@LogIndex
@GetMapping("businessExceptionMsg")
@ResponseBody
public BaseRes<List<UserDemoVO>> businessExceptionMsg() {
    throw new BusinessException("使用者建立失敗");
}

{
    "success": false,
    "data": null,
    "msg": "使用者建立失敗",
    "code": "20000"
}
           

3、30000-RPC異常

@ApiOperation("rpcExceptionDefaultEnum")
    @LogIndex
    @GetMapping("rpcExceptionDefaultEnum")
    @ResponseBody
    public BaseRes<List<UserDemoVO>> rpcExceptionDefaultEnum() {
        throw new RpcException(CodeEnum.RpcErrorEnum.ERROR_CODE_USER_0_30001_0001);
    }

{
    "success": false,
    "data": null,
    "msg": "系統太火爆了,請稍後重試!",
    "code": "USER_0_30001_0001"
}
           
@ApiOperation("rpcExceptionEnumShowMsg")
@LogIndex
@GetMapping("rpcExceptionEnumShowMsg")
@ResponseBody
public BaseRes<List<UserDemoVO>> rpcExceptionEnumShowMsg() {
    throw new RpcException(CodeEnum.RpcErrorEnum.ERROR_CODE_USER_1_30001_0001, "1000", "使用者不存在");
}

{
    "success": false,
    "data": null,
    "msg": "使用者不存在",
    "code": "USER_1_30001_0001_1000"
}
           
@ApiOperation("rpcExceptionEnumMsg")
@LogIndex
@GetMapping("rpcExceptionEnumMsg")
@ResponseBody
public BaseRes<List<UserDemoVO>> rpcExceptionEnumMsg() {
    throw new RpcException(CodeEnum.RpcErrorEnum.ERROR_CODE_USER_1_30001_0001, 
                           "1000", "底層結構異常", "使用者不存在");
}



{
    "success": false,
    "data": null,
    "msg": "使用者不存在",
    "code": "USER_1_30001_0001_1000"
}
           

4、40000-運作異常

@ApiOperation("platformException")
@LogIndex
@GetMapping("platformException")
@ResponseBody
public BaseRes<List<UserDemoVO>> platformException() {
    throw new PlatformException(CodeEnum.PlatformErrorEnum.ERROR_CODE_40001);
}



{
    "success": false,
    "data": null,
    "msg": "系統太火爆了,請稍後重試!",
    "code": "40001"
}
           

作者:京東保險 張宇晉

來源:京東雲開發者社群 轉載請注明來源