通用错误码模块设计

1. 错误码枚举类

package com.example.order.common.enums;

import lombok.Getter;

@Getter
public enum ResultCode {
    SUCCESS(200, "操作成功"),
    
    // 客户端错误 4xx
    PARAM_ERROR(400, "参数错误"),
    UNAUTHORIZED(401, "未授权"),
    FORBIDDEN(403, "禁止访问"),
    NOT_FOUND(404, "资源不存在"),
    
    // 登录相关错误 1000-1099
    USER_NOT_FOUND(1000, "用户不存在"),
    PASSWORD_ERROR(1001, "密码错误"),
    ACCOUNT_LOCKED(1002, "账号已被锁定"),
    LOGIN_EXPIRED(1003, "登录已过期"),
    INVALID_TOKEN(1004, "无效的token"),
    
    // 用户相关错误 1100-1199
    USERNAME_EXISTED(1100, "用户名已存在"),
    EMAIL_EXISTED(1101, "邮箱已被使用"),
    PHONE_EXISTED(1102, "手机号已被使用"),
    
    // 权限相关错误 1200-1299
    NO_PERMISSION(1200, "没有操作权限"),
    ROLE_NOT_FOUND(1201, "角色不存在"),
    
    // 业务操作错误 2000-2999
    OPERATION_FAILED(2000, "操作失败"),
    DATA_NOT_FOUND(2001, "数据不存在"),
    DATA_EXISTED(2002, "数据已存在"),
    
    // 文件相关错误 3000-3099
    FILE_UPLOAD_ERROR(3000, "文件上传失败"),
    FILE_TYPE_ERROR(3001, "文件类型错误"),
    FILE_SIZE_ERROR(3002, "文件大小超限"),
    
    // 系统错误 5xx
    SYSTEM_ERROR(500, "系统内部错误"),
    SERVICE_UNAVAILABLE(503, "服务不可用"),
    GATEWAY_ERROR(504, "网关错误");

    private final int code;
    private final String message;

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

2. 统一响应对象

package com.example.order.common.result;

import com.example.order.common.enums.ResultCode;
import lombok.Data;

@Data
public class Result<T> {
    private int code;
    private String message;
    private T data;
    private long timestamp;

    private Result() {
        this.timestamp = System.currentTimeMillis();
    }

    public static <T> Result<T> success() {
        return success(null);
    }

    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(ResultCode.SUCCESS.getCode());
        result.setMessage(ResultCode.SUCCESS.getMessage());
        result.setData(data);
        return result;
    }

    public static <T> Result<T> error(ResultCode resultCode) {
        Result<T> result = new Result<>();
        result.setCode(resultCode.getCode());
        result.setMessage(resultCode.getMessage());
        return result;
    }

    public static <T> Result<T> error(ResultCode resultCode, String message) {
        Result<T> result = new Result<>();
        result.setCode(resultCode.getCode());
        result.setMessage(message);
        return result;
    }
}

3. 自定义业务异常

package com.example.order.common.exception;

import com.example.order.common.enums.ResultCode;
import lombok.Getter;

@Getter
public class BusinessException extends RuntimeException {
    private final ResultCode resultCode;

    public BusinessException(ResultCode resultCode) {
        super(resultCode.getMessage());
        this.resultCode = resultCode;
    }

    public BusinessException(ResultCode resultCode, String message) {
        super(message);
        this.resultCode = resultCode;
    }
}

4. 全局异常处理器

package com.example.order.common.handler;

import com.example.order.common.exception.BusinessException;
import com.example.order.common.result.Result;
import com.example.order.common.enums.ResultCode;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.validation.BindException;
import cn.dev33.satoken.exception.NotLoginException;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public Result<?> handleBusinessException(BusinessException e) {
        return Result.error(e.getResultCode(), e.getMessage());
    }

    @ExceptionHandler(NotLoginException.class)
    public Result<?> handleNotLoginException(NotLoginException e) {
        return Result.error(ResultCode.UNAUTHORIZED, "请先登录");
    }

    @ExceptionHandler(BindException.class)
    public Result<?> handleBindException(BindException e) {
        return Result.error(ResultCode.PARAM_ERROR, e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception e) {
        return Result.error(ResultCode.SYSTEM_ERROR, "系统异常,请联系管理员");
    }
}

5. 使用示例

在登录服务中使用

@Override
public String login(String username, String password) {
    // 查询用户
    UPerson user = this.findByLoginIdentifier(username);
    
    if (user == null) {
        throw new BusinessException(ResultCode.USER_NOT_FOUND);
    }

    // 验证密码
    if (!BCrypt.checkpw(password, user.getEncodedPassword())) {
        throw new BusinessException(ResultCode.PASSWORD_ERROR);
    }

    // 检查账号状态
    if (user.getStatus() != null && user.getStatus() == 0) {
        throw new BusinessException(ResultCode.ACCOUNT_LOCKED);
    }

    // 登录成功,使用Sa-Token记录登录状态
    StpUtil.login(user.getId());
    
    return StpUtil.getTokenValue();
}

Controller中使用

@PostMapping("/login")
public Result<String> login(@RequestBody LoginRequest request) {
    String token = personService.login(request.getUsername(), request.getPassword());
    return Result.success(token);
}

响应示例

成功响应

{
    "code": 200,
    "message": "操作成功",
    "data": "your-token-here",
    "timestamp": 1646534400000
}

错误响应

{
    "code": 1001,
    "message": "密码错误",
    "data": null,
    "timestamp": 1646534400000
}

设计优点

  1. 错误码统一管理,便于维护
  2. 支持自定义错误信息
  3. 统一的响应格式
  4. 分类清晰的错误码区间
  5. 全局异常处理
  6. 支持国际化(可以扩展)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值