Files
rui-docs/standards/Result统一响应类.md
T
vifo 1324a52049 docs(standards): 补充 Result<T> 统一响应类文档,修正 API 设计规范响应格式
- 新增 Result统一响应类.md 完整文档
- 修正 API设计规范.md 中响应字段与实际代码不一致的问题
- 错误码规范表按实际 ResultCode 枚举对齐
2026-06-08 16:01:33 +08:00

333 lines
9.0 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.
# 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 响应