docs(standards): 补充 Result<T> 统一响应类文档,修正 API 设计规范响应格式

- 新增 Result统一响应类.md 完整文档
- 修正 API设计规范.md 中响应字段与实际代码不一致的问题
- 错误码规范表按实际 ResultCode 枚举对齐
This commit is contained in:
2026-06-08 16:01:03 +08:00
parent d4a3bd5847
commit 1324a52049
2 changed files with 352 additions and 14 deletions
+20 -14
View File
@@ -116,10 +116,12 @@ GET /v1/user/users?username=admin&status=1&createdAt_start=2024-01-01&createdAt_
### 4.1 统一响应格式
> 详细规范见 [Result 统一响应类](Result统一响应类.md)
```json
{
"code": 200,
"msg": "操作成功",
"error": 0,
"message": "操作成功",
"data": {}
}
```
@@ -128,8 +130,8 @@ GET /v1/user/users?username=admin&status=1&createdAt_start=2024-01-01&createdAt_
```json
{
"code": 200,
"msg": "操作成功",
"error": 0,
"message": "操作成功",
"data": {
"records": [],
"total": 100,
@@ -159,19 +161,23 @@ GET /v1/user/users?username=admin&status=1&createdAt_start=2024-01-01&createdAt_
| 区间 | 模块 | 示例 |
|------|------|------|
| 1000-1999 | 通用 | 1001: 参数校验失败, 1002: 资源不存在 |
| 2000-2999 | 用户模块 | 2001: 用户名已存在, 2002: 密码错误 |
| 3000-3999 | 系统模块 | 3001: 字典不存在, 3002: 配置错误 |
| 4000-4999 | 认证模块 | 4001: Token 过期, 4002: 无权访问 |
| 5000-5999 | 文件模块 | 5001: 上传失败, 5002: 文件过大 |
| 6000-6999 | 消息模块 | 6001: 发送失败, 6002: 模板不存在 |
| 0 | 通用成功 | `0: 操作成功` |
| 1 | 通用失败 | `1: 操作失败` |
| 400-499 | HTTP 标准 | `401: 未授权, 404: 资源不存在` |
| 4000-4099 | 认证模块 | `4001: Token 过期, 4002: Token 无效` |
| 4100-4199 | 用户信息 | `4101: 用户不存在, 4102: 用户名已存在` |
| 4200-4299 | 用户等级 | `4201: 等级编码已存在` |
| 5000-5999 | 文件模块(预留) | `5001: 上传失败, 5002: 文件大小超限` |
| 6000-6999 | 消息模块(预留) | `6001: 发送失败, 6002: 模板不存在` |
> 完整枚举值见 [Result 统一响应类 → ResultCode 枚举](Result统一响应类.md#四resultcode-枚举)
**错误响应示例**
```json
{
"code": 2001,
"msg": "用户名已存在",
"data": null
"error": 4102,
"message": "用户名已存在",
"code": "USER_INFO_USERNAME_EXISTS"
}
```
@@ -321,7 +327,7 @@ public class UserRemoteService {
if (result.isSuccess()) {
return result.getData();
}
throw new BizException(result.getCode(), result.getMsg());
throw new BizException(result.getError(), result.getMessage());
}
}
```
+332
View File
@@ -0,0 +1,332 @@
# Result<T> 统一响应类规范
> RUI 框架所有 API 接口的统一返回包装
---
## 一、类信息
| 属性 | 值 |
|------|-----|
| 包路径 | `com.rui.common.core.result.Result` |
| 所在模块 | `rui-common-core` |
| 泛型参数 | `<T>` — data 字段的具体类型 |
| 序列化 | 实现 `Serializable` |
| JSON 策略 | `@JsonInclude(NON_NULL)` — 值为 `null` 的字段自动忽略 |
---
## 二、响应结构
### 2.1 字段定义
| 字段 | 类型 | 说明 | 成功时 | 失败时 |
|------|------|------|--------|--------|
| `error` | `int` | 状态码(0 = 成功) | `0` | 非 0 |
| `message` | `String` | 提示信息 | `"操作成功"` | 具体错误描述 |
| `code` | `String` | 业务错误码 | `null`(不输出) | 如 `"AUTH_UNAUTHORIZED"` |
| `data` | `T` | 业务数据 | 实际数据 | 通常为 `null`(不输出) |
### 2.2 成功响应示例
**无数据返回:**
```json
{
"error": 0,
"message": "操作成功"
}
```
**带数据返回:**
```json
{
"error": 0,
"message": "操作成功",
"data": {
"id": 1001,
"username": "admin"
}
}
```
**分页数据:**
```json
{
"error": 0,
"message": "操作成功",
"data": {
"records": [],
"total": 100,
"size": 10,
"current": 1,
"pages": 10
}
}
```
### 2.3 失败响应示例
**通用失败:**
```json
{
"error": 1,
"message": "操作失败"
}
```
**带业务错误码:**
```json
{
"error": 401,
"message": "未授权",
"code": "AUTH_UNAUTHORIZED"
}
```
**带数据(未找到场景):**
```json
{
"error": 404,
"message": "数据不存在",
"code": "DATA_NOT_FOUND",
"data": "dictCode_001"
}
```
> `data` 字段在 `failNotFound` 场景下用于传递资源 key,便于前端做国际化模板替换。
---
## 三、静态工厂方法
### 3.1 成功系列
| 方法签名 | 说明 | 使用场景 |
|----------|------|----------|
| `Result.ok()` | 无数据成功 | 删除、更新等不需要返回数据的操作 |
| `Result.ok(T data)` | 带数据成功 | 查询详情、列表、新增返回实体 |
### 3.2 失败系列
| 方法签名 | 说明 | 使用场景 |
|----------|------|----------|
| `Result.fail()` | 通用失败 | 兜底异常、未知错误 |
| `Result.fail(String message)` | 自定义提示 | 需要特定提示信息的业务异常 |
| `Result.fail(ResultCode resultCode)` | 枚举驱动 | 标准业务错误,推荐使用 |
| `Result.fail(int error, String message)` | 自定义错误码+提示 | 非标准错误场景 |
| `Result.fail(int error, String message, String code)` | 完全自定义 | 需要同时指定三个字段 |
| `Result.fail(ResultCode resultCode, T data)` | 枚举+数据 | 失败时需携带部分数据 |
| `Result.failNotFound(ResultCode resultCode, String key)` | 404 未找到 | 数据不存在,key 放入 data |
### 3.3 判断方法
| 方法 | 说明 |
|------|------|
| `result.isSuccess()` | 判断是否成功(`error == 0` |
| `result.toJsonString()` | 序列化为 JSON 字符串 |
---
## 四、ResultCode 枚举
### 4.1 枚举结构
| 属性 | 类型 | 说明 |
|------|------|------|
| `error` | `int` | HTTP 风格状态码,0 为成功 |
| `message` | `String` | 默认提示文本 |
| `code` | `String` | 业务错误码(大写蛇形,模块前缀) |
### 4.2 枚举值一览
| 枚举值 | error | message | code | 说明 |
|--------|-------|---------|------|------|
| `SUCCESS` | 0 | 操作成功 | `null` | 成功 |
| `FAILURE` | 1 | 操作失败 | `null` | 通用失败 |
| `UNAUTHORIZED` | 401 | 未授权 | `AUTH_UNAUTHORIZED` | 未登录 |
| `FORBIDDEN` | 403 | 无权限 | `AUTH_FORBIDDEN` | 无权限 |
| `NOT_FOUND` | 404 | 资源不存在 | `COMMON_NOT_FOUND` | 资源未找到 |
| `DATA_NOT_FOUND` | 404 | 数据不存在 | `DATA_NOT_FOUND` | 数据未找到 |
| `VALIDATE_FAILED` | 400 | 参数校验失败 | `COMMON_VALIDATE_FAILED` | 参数错误 |
| `TOKEN_EXPIRED` | 4001 | Token 已过期 | `AUTH_TOKEN_EXPIRED` | Token 过期 |
| `TOKEN_INVALID` | 4002 | Token 无效 | `AUTH_TOKEN_INVALID` | Token 无效 |
| `TENANT_NOT_FOUND` | 4003 | 租户不存在 | `AUTH_TENANT_NOT_FOUND` | 租户不存在 |
| `TENANT_DISABLED` | 4004 | 租户已禁用 | `AUTH_TENANT_DISABLED` | 租户禁用 |
| `USER_NOT_FOUND` | 4101 | 用户不存在 | `USER_INFO_NOT_FOUND` | 用户不存在 |
| `USERNAME_EXISTS` | 4102 | 用户名已存在 | `USER_INFO_USERNAME_EXISTS` | 用户名重复 |
| `LEVEL_CODE_EXISTS` | 4201 | 等级编码已存在 | `USER_LEVEL_CODE_EXISTS` | 等级编码重复 |
### 4.3 错误码规划规则
| 区间 | 模块 | code 前缀 |
|------|------|-----------|
| 0 | 通用成功 | — |
| 1 | 通用失败 | — |
| 400-499 | HTTP 标准错误 | `COMMON_*` / `AUTH_*` |
| 4000-4099 | 认证错误 | `AUTH_*` |
| 4100-4199 | 用户信息错误 | `USER_INFO_*` |
| 4200-4299 | 用户等级错误 | `USER_LEVEL_*` |
| 5000-5999 | 文件模块(预留) | `FILE_*` |
| 6000-6999 | 消息模块(预留) | `MSG_*` |
**新增规则**:新模块取 100 的整数倍区间,code 格式为 `{模块}_{业务}_{具体}`
---
## 五、Controller 使用示例
### 5.1 标准 CRUD
```java
@RestController
@RequestMapping("/v1/system/roles")
@RequiredArgsConstructor
public class SysRoleController {
private final SysRoleService roleService;
@GetMapping
public Result<IPage<SysRoleVO>> list(SysRoleQuery query) {
return Result.ok(roleService.page(query));
}
@GetMapping("/{id}")
public Result<SysRoleVO> getById(@PathVariable Long id) {
SysRoleVO role = roleService.getById(id);
if (role == null) {
return Result.failNotFound(ResultCode.DATA_NOT_FOUND, String.valueOf(id));
}
return Result.ok(role);
}
@PostMapping
public Result<SysRoleVO> create(@RequestBody @Valid SysRoleDTO dto) {
return Result.ok(roleService.create(dto));
}
@PutMapping("/{id}")
public Result<SysRoleVO> update(@PathVariable Long id, @RequestBody @Valid SysRoleDTO dto) {
return Result.ok(roleService.update(id, dto));
}
@DeleteMapping("/{id}")
public Result<Void> delete(@PathVariable Long id) {
roleService.delete(id);
return Result.ok();
}
}
```
### 5.2 异常处理中返回
```java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BizException.class)
public Result<Void> handleBizException(BizException e) {
return Result.fail(e.getCode(), e.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValidation(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
return Result.fail(ResultCode.VALIDATE_FAILED.getError(), message, ResultCode.VALIDATE_FAILED.getCode());
}
}
```
### 5.3 Feign 远程调用中处理 Result
```java
@Service
@RequiredArgsConstructor
public class UserRemoteService {
private final RemoteUserService remoteUserService;
public UserVO getUserById(Long userId) {
Result<UserVO> result = remoteUserService.getById(userId);
if (result.isSuccess()) {
return result.getData();
}
throw new BizException(result.getError(), result.getMessage());
}
}
```
---
## 六、前端对接指南
### 6.1 判断成功
```typescript
// response.data 为 Result<T> 结构
const isSuccess = response.data.error === 0;
```
### 6.2 错误处理
```typescript
if (result.error !== 0) {
// 优先用 code 做国际化
if (result.code) {
showI18nMessage(result.code, { key: result.data });
} else {
// 降级显示 message
showMessage(result.message);
}
}
```
### 6.3 TypeScript 类型定义
```typescript
interface Result<T = any> {
error: number;
message: string;
code?: string; // 失败时存在
data?: T; // 成功时或 failNotFound 时存在
}
```
---
## 七、扩展指南
### 7.1 新增 ResultCode
`ResultCode` 枚举中添加新值,遵循以下规则:
1. **error 取值**:按模块区间分配(见 4.3 节)
2. **code 命名**`{模块}_{业务}_{具体}`,全大写蛇形
3. **向后兼容**:禁止修改已有枚举值的 error 或 code
```java
// 示例:文件模块新增
FILE_UPLOAD_FAILED(5001, "文件上传失败", "FILE_UPLOAD_FAILED"),
FILE_SIZE_EXCEEDED(5002, "文件大小超限", "FILE_SIZE_EXCEEDED"),
```
### 7.2 禁止事项
- ❌ 不要在 Controller 中直接 `new Result<>()`,必须使用静态工厂方法
- ❌ 不要修改 `Result` 类的字段名(`error`/`message`/`code`/`data`),影响序列化兼容
- ❌ 不要用 `error` 字段传递 HTTP 状态码(它只是业务状态码,HTTP 状态码由框架控制)
- ❌ 不要在 `code` 字段中使用小写或特殊字符
---
> **文档版本**: v1.0
> **创建日期**: 2026-06-08
> **源码位置**: `rui-common/rui-common-core/src/main/java/com/rui/common/core/result/`
> **适用范围**: RUI 框架所有模块的 API 响应