From 3395f69b42d2fe573a1217a1da19a1b2a0c025de Mon Sep 17 00:00:00 2001 From: pigeon Date: Sun, 7 Jun 2026 17:49:19 +0800 Subject: [PATCH] =?UTF-8?q?docs(standards):=20=E5=A2=9E=E5=8A=A0=20Result?= =?UTF-8?q?=20=E8=BF=94=E5=9B=9E=E8=A7=84=E8=8C=83=E7=AB=A0=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按用户反馈(i18n key 用 code 字符串,key 放 data 字段): - 字段语义:error/Http、code/i18n key、message/默认中文、data/业务数据 - 调用规范表:成功/校验失败/未授权/无权限/数据不存在/降级/通用失败 - 重点:数据不存在用 failNotFound(ResultCode.DATA_NOT_FOUND, key) - 禁止写法:Result.ok(null) 表示'未找到'(反直觉) - i18n 配合示例:前端用 code 字段路由 i18n,data 字段做模板替换 --- standards/coding-standards.md | 84 ++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/standards/coding-standards.md b/standards/coding-standards.md index 8e6cda4..eb92bcf 100644 --- a/standards/coding-standards.md +++ b/standards/coding-standards.md @@ -298,4 +298,86 @@ Closes #123 --- -> **最后提醒**:编码规范是为了团队协作,请务必遵守! \ No newline at end of file +> **最后提醒**:编码规范是为了团队协作,请务必遵守! + +--- + +## 📦 Result 返回规范 + +`com.rui.common.core.result.Result` 是统一的 API 响应封装。所有 controller 必须遵守: + +### 字段语义 + +| 字段 | 类型 | 用途 | +|---|---|---| +| `error` | int | HTTP 风格状态码(200/400/401/403/404/500/503 等) | +| `code` | String | **业务编码,前端 i18n key**(如 `DATA_NOT_FOUND`) | +| `message` | String | 默认中文提示,可由前端 i18n 覆盖 | +| `data` | T | 业务数据 | + +### 调用规范 + +| 场景 | 调用方式 | 备注 | +|---|---|---| +| 成功 | `Result.ok(data)` | data 可为 null,但**列表场景应返回 `emptyList`** | +| 业务校验失败 | `Result.fail(400, "msg")` 或 `Result.fail(ResultCode.X, "msg")` | 优先用枚举 | +| 未授权 | `Result.fail(401, "msg")` | 框架层 `GlobalExceptionHandler` 统一处理 | +| 无权限 | `Result.fail(403, "msg")` | 同上 | +| **数据不存在** | **`Result.failNotFound(ResultCode.DATA_NOT_FOUND, key)`** | **推荐写法**:key 放 data 字段便于前端模板替换 | +| 资源不存在(泛指) | `Result.fail(ResultCode.NOT_FOUND)` | 不带 key 的场景 | +| 服务降级 | `Result.fail(503, "服务降级: msg")` | Feign fallback 等场景 | +| 通用失败 | `Result.fail("msg")` | 兜底 | + +### ❌ 禁止写法 + +- **`Result.ok(null)` 表示"未找到"** —— 反直觉(HTTP 200 + null),且与 `fail(404)` 语义冲突 +- **message 中拼接 key** —— 如 `"字典不存在: " + dictCode`,应该用 `failNotFound(DATA_NOT_FOUND, dictCode)` 让前端用 i18n 模板 `"字典[${data}]不存在"` +- **数字 code 字符串比较** —— 应该用 `ResultCode` 枚举的 `getCode()` 字符串 + +### ✅ 正确示例 + +```java +// 查询接口 - 数据不存在 +@GetMapping("/dict/getByCode/{dictCode}") +public Result> getDictByCode(@PathVariable String dictCode) { + SysDictType dict = dictTypeService.findByCode(dictCode); + if (dict == null) { + return Result.failNotFound(ResultCode.DATA_NOT_FOUND, dictCode); + } + return Result.ok(buildDictResult(dict)); +} + +// 业务校验失败 +@PostMapping("/save") +public Result save(@RequestBody @Valid SysDictDTO dto) { + if (dictService.isCodeExists(dto.getCode())) { + return Result.fail(400, "字典编码已存在: " + dto.getCode()); + } + return Result.ok(); +} + +// 列表查询(即使是空也要返回空集合) +@GetMapping("/list") +public Result> list() { + return Result.ok(dictService.list()); // 不要 Result.ok(null) +} +``` + +### i18n 配合示例 + +前端拿到 `Result` 后: +```javascript +const i18nMap = { + 'DATA_NOT_FOUND': '数据[{0}]不存在', // 占位符 {0} 用 data 字段填充 + 'AUTH_UNAUTHORIZED': '请先登录', +}; + +if (result.code === 'DATA_NOT_FOUND') { + showError(i18nMap['DATA_NOT_FOUND'].replace('{0}', result.data)); +} +``` + +### 相关枚举 + +- `com.rui.common.core.result.ResultCode` —— 业务 code 枚举(404 用 `DATA_NOT_FOUND`,401 用 `UNAUTHORIZED` 等) +- 新增业务 code 时在 `ResultCode` 加枚举值,**不要直接 `Result.fail(int, String, String)` 硬编码字符串** \ No newline at end of file