docs(standards): 补充 Result<T> 统一响应类文档,修正 API 设计规范响应格式
- 新增 Result统一响应类.md 完整文档 - 修正 API设计规范.md 中响应字段与实际代码不一致的问题 - 错误码规范表按实际 ResultCode 枚举对齐
This commit is contained in:
+20
-14
@@ -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());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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 响应
|
||||
Reference in New Issue
Block a user