# Result 统一响应类规范 > RUI 框架所有 API 接口的统一返回包装 --- ## 一、类信息 | 属性 | 值 | |------|-----| | 包路径 | `com.rui.common.core.result.Result` | | 所在模块 | `rui-common-core` | | 泛型参数 | `` — 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> list(SysRoleQuery query) { return Result.ok(roleService.page(query)); } @GetMapping("/{id}") public Result 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 create(@RequestBody @Valid SysRoleDTO dto) { return Result.ok(roleService.create(dto)); } @PutMapping("/{id}") public Result update(@PathVariable Long id, @RequestBody @Valid SysRoleDTO dto) { return Result.ok(roleService.update(id, dto)); } @DeleteMapping("/{id}") public Result delete(@PathVariable Long id) { roleService.delete(id); return Result.ok(); } } ``` ### 5.2 异常处理中返回 ```java @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BizException.class) public Result handleBizException(BizException e) { return Result.fail(e.getCode(), e.getMessage()); } @ExceptionHandler(MethodArgumentNotValidException.class) public Result 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 result = remoteUserService.getById(userId); if (result.isSuccess()) { return result.getData(); } throw new BizException(result.getError(), result.getMessage()); } } ``` --- ## 六、前端对接指南 ### 6.1 判断成功 ```typescript // response.data 为 Result 结构 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 { 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 响应