Files
number/后端架构设计/11-通用基础设施-part1-响应与异常.md
2026-03-17 12:09:43 +08:00

4.4 KiB
Raw Permalink Blame History

通用基础设施开发文档 - Part 1响应封装 + 异常处理)


一、统一响应封装 Result.java

package com.openclaw.common;

import lombok.Data;
import java.time.Instant;

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

    public static <T> Result<T> ok(T data) {
        Result<T> r = new Result<>();
        r.code = 200; r.message = "success";
        r.data = data;
        r.timestamp = Instant.now().toEpochMilli();
        return r;
    }

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

    public static <T> Result<T> fail(int code, String message) {
        Result<T> r = new Result<>();
        r.code = code; r.message = message;
        r.timestamp = Instant.now().toEpochMilli();
        return r;
    }
}

二、ErrorCode.java

package com.openclaw.constant;

public interface ErrorCode {
    // ---------- 通用 ----------
    int[] PARAM_ERROR             = {400, "请求参数错误"};
    int[] UNAUTHORIZED            = {401, "请先登录"};
    int[] FORBIDDEN               = {403, "无权限"};
    int[] NOT_FOUND               = {404, "资源不存在"};

    // ---------- 用户 1xxx ----------
    int[] USER_NOT_FOUND          = {1001, "用户不存在"};
    int[] WRONG_PASSWORD          = {1002, "密码错误"};
    int[] PHONE_REGISTERED        = {1003, "手机号已注册"};
    int[] USER_BANNED             = {1004, "账号已被封禁"};

    // ---------- Skill 2xxx ----------
    int[] SKILL_NOT_FOUND         = {2001, "Skill不存在"};
    int[] SKILL_ALREADY_OWNED     = {2002, "已拥有该Skill"};
    int[] SKILL_STATUS_ERROR      = {2003, "Skill状态不允许此操作"};

    // ---------- 积分 3xxx ----------
    int[] POINTS_NOT_ENOUGH       = {3001, "积分不足"};
    int[] POINTS_RULE_NOT_FOUND   = {3002, "积分规则不存在"};

    // ---------- 订单 4xxx ----------
    int[] ORDER_NOT_FOUND         = {4001, "订单不存在"};
    int[] ORDER_STATUS_ERROR      = {4002, "订单状态不允许此操作"};

    // ---------- 退款 5xxx ----------
    int[] REFUND_NOT_FOUND        = {5001, "退款单不存在"};
    int[] REFUND_STATUS_ERROR     = {5002, "退款状态不允许此操作"};

    // ---------- 邀请 6xxx ----------
    int[] INVITE_CODE_INVALID     = {6001, "邀请码无效"};
    int[] INVITE_SELF_NOT_ALLOWED = {6002, "不能邀请自己"};
    int[] INVITE_CODE_EXHAUSTED   = {6003, "邀请码已达使用上限"};
}

三、BusinessException.java

package com.openclaw.exception;

import lombok.Getter;

@Getter
public class BusinessException extends RuntimeException {
    private final int    code;
    private final String msg;

    public BusinessException(int code, String msg) {
        super(msg);
        this.code = code;
        this.msg  = msg;
    }

    /** 接受 int[] {code, message} 格式的 ErrorCode 常量 */
    public BusinessException(int[] errorCode) {
        this(errorCode[0], String.valueOf(errorCode[1]));
    }
}

四、GlobalExceptionHandler.java

package com.openclaw.exception;

import com.openclaw.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<?> handleBusiness(BusinessException e) {
        return Result.fail(e.getCode(), e.getMsg());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> handleValidation(MethodArgumentNotValidException e) {
        String msg = e.getBindingResult().getFieldErrors().stream()
            .findFirst()
            .map(FieldError::getDefaultMessage)
            .orElse("参数校验失败");
        return Result.fail(400, msg);
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> handleUnknown(Exception e) {
        log.error("未知异常", e);
        return Result.fail(500, "服务器内部错误");
    }
}

文档版本v1.0 | 创建日期2026-03-16