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

155 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 通用基础设施开发文档 - Part 1响应封装 + 异常处理)
---
## 一、统一响应封装 Result.java
```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
```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
```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
```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