From 4c8f93f978eaa69d359d4d96bfa1e6e089615289 Mon Sep 17 00:00:00 2001 From: jw Date: Fri, 18 Sep 2026 11:02:39 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20#7916=20=E5=9B=A2=E6=9C=9F?= =?UTF-8?q?=E7=89=A9=E8=B5=84=E5=88=97=E8=A1=A8=E4=B8=8E=E5=80=99=E9=80=89?= =?UTF-8?q?=E8=A1=A5=E5=88=86=E7=B1=BB=E4=B8=AD=E6=96=87=E5=90=8D=20catego?= =?UTF-8?q?ryName=EF=BC=88=E4=BF=AE=E6=94=B9=E6=8E=A5=E5=8F=A3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...候选补分类中文名categoryName-修改接口-管理后台.md | 370 ++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 changelogs-v2/2026-09/18_7916_团期物资列表与候选补分类中文名categoryName-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/18_7916_团期物资列表与候选补分类中文名categoryName-修改接口-管理后台.md b/changelogs-v2/2026-09/18_7916_团期物资列表与候选补分类中文名categoryName-修改接口-管理后台.md new file mode 100644 index 00000000..d57d9de3 --- /dev/null +++ b/changelogs-v2/2026-09/18_7916_团期物资列表与候选补分类中文名categoryName-修改接口-管理后台.md @@ -0,0 +1,370 @@ +--- +schema: "hl-changelog/v2" +ticket: "7916" +title: "团期物资列表与候选列表出参新增分类中文名 categoryName(字典外取值按原值显示)" +consumer: "admin" +author: "jw(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "mmg" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "团期物资列表 GET /v3/admin/order/group-batch/{groupBatchId}/supplies 与候选列表 .../supplies/candidates 出参各纯新增一个 categoryName;既有 category 原值一字不改,入参与错误码零变化。翻译三段回落:命中字典 supplies_category 的码译中文;值本身已是中文原样返回;字典外取值(TEST 存量的 EQUIPMENT / PROTECTION,共 140 行)按原值显示,不编造也不置空。字典 Feign 不可达时整批回落原值且接口仍 200(TEST 实测注入过)。后端已合并 dev-v3 并部署 TEST。" +updated_at: "2026-09-18" +base: "dev-v3" +--- + +# 团期物资: 列表与候选列表出参新增分类中文名 `categoryName` + +> **服务**: hl-order-service-v3 (端口 8086) +> **PR**: #7920 +> **Issue**: #7916 +> **日期**: 2026-09-18 +> **影响范围**: 管理后台团期详情「物资清单」与「从备品库选择」候选弹窗的分类列 + +--- + +## ⚠️ 关键变化 + +- 本次两个接口的出参**各纯新增一个 `categoryName`**,既有 `category` 原值**一字不改**。 +- 前端以前可能以为:拿到 `category` 自己查字典 `supplies_category` 就能译出中文。**这个做法译不出线上多数行**——TEST 实测 `order_batch_supplies.category` 存量 442 行里有 140 行是字典里根本没有的 `EQUIPMENT` / `PROTECTION`,另有 301 行**存的本来就是中文**。 +- 实际现在是:后端按三段回落给出 `categoryName`,**字典外的取值原样回传**(显示出来仍是 `EQUIPMENT`),不是后端漏译,是该列的存量脏值,归一另列工单。 + +--- + +## 一、背景 + +`order_batch_supplies.category` 是**下单时固化的快照**,写入来源有三个且口径各异(产品备品中文与大写码混杂、备品库基本是字典码、管理员手工新增无校验),因此同一列并存三套词表。 + +| 维度 | TEST 实测(2026-09-18,`hl_order_service_v3`) | +|------|--------------------------------------------| +| 中文标签(如 `个人装备`) | 301 行 | +| 字典码(如 `safety_protection`) | 1 行 | +| 字典外大写码 `EQUIPMENT` | 70 行 | +| 字典外大写码 `PROTECTION` | 70 行 | +| 字典 `supplies_category` 取值 | 8 项,**全是小写码**,无 `EQUIPMENT` / `PROTECTION` | + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 查询团期备品列表 | GET | `/v3/admin/order/group-batch/{groupBatchId}/supplies` | 出参新增字段 | 每行新增 `categoryName`,`category` 不变 | +| 2 | 团期物资候选列表 | GET | `/v3/admin/order/group-batch/{groupBatchId}/supplies/candidates` | 出参新增字段 | 每项新增 `categoryName`,与列表同一套翻译规则 | + +--- + +## 三、接口详情 + +### 1. 查询团期备品列表 `GET /v3/admin/order/group-batch/{groupBatchId}/supplies` + +**VO**: `GroupBatchSuppliesRespVO` + +#### 使用场景 + +管理后台团期详情页「物资清单」区域加载已固化/已手工追加的备品行;分类列改为显示 `categoryName`,排序、筛选、提交仍用 `category` 原值。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | 雪花 ID | 团期主订单 ID | + +#### 出参 `Result>` + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | String | 备品行 ID(19 位雪花,字符串输出) | +| groupBatchId | String | 团期 ID | +| suppliesName | String | 备品名称 | +| category | String | **分类原值(本次不变)**:可能是字典码、中文标签或字典外取值,可为 null | +| categoryName | String | **本次新增**:分类展示名;`category` 为 null/空白时为 null | +| hasCost | Boolean | 是否计费 | +| billingType | String | 计费方式 PER_PERSON / PER_QUANTITY | +| unitPrice | String | 单价 | +| quantity | Integer | 数量 | +| sortOrder | Integer | 排序号 | +| remark | String | 备注 | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/2100509904627191810/supplies +Authorization: Bearer {admin-jwt} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { "id": "2100509905000000001", "suppliesName": "团队识别手环1", "category": "个人装备", "categoryName": "个人装备", "quantity": 1 }, + { "id": "2100509905000000002", "suppliesName": "户外急救箱", "category": "safety_protection", "categoryName": "安全防护", "quantity": 1 } + ], + "success": true +} +``` + +#### 空数据 / 降级响应 + +```json +{ "code": 200, "message": "成功", "data": [], "success": true } +``` + +字典服务不可达时**不降级整个接口**,只是 `categoryName` 全批等于 `category`: + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { "id": "2100509905000000002", "suppliesName": "户外急救箱", "category": "safety_protection", "categoryName": "safety_protection" } + ], + "success": true +} +``` + +#### 错误响应 + +```json +{ + "code": 589500, + "message": "团期不存在", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- 鉴权:管理后台 JWT + 团期查看权限(`GroupBatchPermissionGuard` VIEW),未登录 401。 +- `category` 为 null 或纯空白 → `categoryName` 为 **null**,不是空串、不是字符串 "null"。 +- `category` 命中字典 → 中文标签;已是中文标签 → 原样返回;字典外取值 → **原样返回**,后端不编造分类。 +- 只读接口,天然幂等;整页只加载一次字典,不按行调用。 + +--- + +### 2. 团期物资候选列表 `GET /v3/admin/order/group-batch/{groupBatchId}/supplies/candidates` + +**VO**: `SuppliesCandidateRespVO` + +#### 使用场景 + +管理后台团期物资「从备品库选择」弹窗,列出资源域备品库中上架未软删的备品,并标出哪些已加入本团清单;分类列同样改用 `categoryName`。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | 雪花 ID | 团期主订单 ID | +| categoryCode | Query | String | ❌ | 字典 `supplies_category` 的值 | 按分类筛选,**筛选仍用码,不用中文名** | +| keyword | Query | String | ❌ | - | 匹配名称或副标题 | + +#### 出参 `Result>` + +| 字段 | 类型 | 说明 | +|------|------|------| +| suppliesId | String | 备品库 ID | +| suppliesName | String | 备品名称 | +| subtitle | String | 副标题 | +| category | String | 分类码原值(本次不变),来自备品库 `category_code` | +| categoryName | String | **本次新增**:分类展示名,规则与列表接口完全一致 | +| billingType | String | 计费方式(已归一为 PER_PERSON / PER_QUANTITY) | +| hasCost | Boolean | 是否计费 | +| unitPrice | String | 单价 | +| selected | Boolean | 是否已加入本团清单 | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/2099919772194836482/supplies/candidates?categoryCode=camping_equipment +Authorization: Bearer {admin-jwt} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { "suppliesId": "2074063318837792770", "suppliesName": "折叠桌椅套装", "category": "camping_equipment", "categoryName": "露营设备" }, + { "suppliesId": "2074063318837792771", "suppliesName": "对讲机", "category": "electronics", "categoryName": "电子设备" } + ], + "success": true +} +``` + +#### 空数据 / 降级响应 + +资源域备品库不可达或无候选时返回空数组,不 500: + +```json +{ "code": 200, "message": "成功", "data": [], "success": true } +``` + +#### 错误响应 + +```json +{ + "code": 589500, + "message": "团期不存在", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- 鉴权同列表接口(VIEW 权限)。 +- `categoryCode` 筛选**只认字典码**,传中文名筛不出结果。 +- 备品库里存在字典外的 `category_code`(TEST 现存 1 条 `TENT`,已下架)→ `categoryName` 回传 `TENT`。 +- 只读接口,整页只加载一次字典。 + +--- + +## 四、契约约束与正确调用方式(接口类必写) + +> 本节只写后端返回值的契约,不写 UI 渲染建议。 + +| 场景 | `category` | `categoryName` | +|------|-----------|----------------| +| ✅ 字典码 | `safety_protection` | `安全防护` | +| ✅ 存量中文 | `个人装备` | `个人装备` | +| ✅ 字典外取值 | `EQUIPMENT` | `EQUIPMENT`(原值,非空、非 null) | +| ✅ 空分类 | `null` | `null` | +| ✅ 字典不可达 | `safety_protection` | `safety_protection`(整批回落) | + +### 调用方必须注意 + +- **写入与筛选一律用 `category` / `categoryCode` 原值**,`categoryName` 只用于展示,不要拿它回传给任何写接口。 +- **不要假设 `categoryName` 一定是中文**:字典外取值原样回传,这是刻意的(编造分类比显示英文码更有害)。 +- **不要用 `categoryName` 做分组键**:同一分类可能因存量三套词表而出现 `个人装备` 与 `personal_gear` 两个不同的 `category`,却都展示成「个人装备」。分组仍应按 `category`。 + +--- + +## 五、数据库行为 + +**零变更**:本次无 Flyway 迁移,不新增/修改任何表与列。`categoryName` 是读取时按数据字典翻译出来的**派生展示字段,不落库**;`order_batch_supplies.category` 只读不写,存量脏值不回填。 + +--- + +## 六、边界行为 + +- 未登录 → 401(网关拦截) +- 无团期查看权限 → 权限守卫拒绝 +- 团期不存在 → 589500 +- 字典服务降级 → `categoryName` 全批等于 `category`,接口仍 200,不阻断页面 +- 老数据兼容 → `category` 为 null 的历史行 → `categoryName` 为 null,不异常 + +--- + +## 六.5、枚举 / 数据字典 + +### categoryName 的取值来源(数据字典 `supplies_category`) + +**所属字段**: `GroupBatchSuppliesRespVO.categoryName` / `SuppliesCandidateRespVO.categoryName` | **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `personal_gear` | 个人装备 | 字典项 | +| `camping_equipment` | 露营设备 | 字典项 | +| `riding_gear` | 骑行装备 | 字典项 | +| `electronics` | 电子设备 | 字典项 | +| `safety_protection` | 安全防护 | 字典项 | +| `entertainment` | 娱乐器材 | 字典项 | +| `warmth_gear` | 保暖装备 | 字典项 | +| `vehicle_accessories` | 车载装备 | 字典项 | +| `EQUIPMENT` / `PROTECTION` / `TENT` | (无) | **不在字典内的存量取值,原样回传** | + +## 六.6、修改前后对比 + +### 字段级对比 + +| 字段 | 改前 | 改后 | +|------|------|------| +| `GroupBatchSuppliesRespVO.category` | 分类原值 | **不变** | +| `GroupBatchSuppliesRespVO.categoryName` | 不存在 | 新增,三段回落的展示名,可为 null | +| `SuppliesCandidateRespVO.category` | 分类码原值 | **不变** | +| `SuppliesCandidateRespVO.categoryName` | 不存在 | 新增,同一套规则 | + +### 行为级对比 + +| 行为 | 改前 | 改后 | +|------|------|------| +| 前端显示分类中文 | 需自查字典,且线上多数行查不到 | 直接用 `categoryName` | +| 字典服务不可达 | 与本字段无关 | 接口仍 200,`categoryName` 整批等于 `category` | +| 字典查询次数 | — | 每次请求整页一次,不按行调用 | +| 入参 / 错误码 | — | **零变化** | + +## 六.7、影响评估 + +- **是否破坏向后兼容**: 否(纯新增出参字段,既有字段与错误码零变化) +- **前端是否必须同步上线**: 否(不接入则与现状一致) +- **前端 workaround 清理点**: 前端若已有「按 `category` 自查字典译中文」的逻辑,可改用 `categoryName`;但**不要顺手把字典外取值改成显示空白**,那是存量脏值,另有工单归一。 + +--- + +## 七、不影响范围 + +- **仅影响**: 管理后台团期物资清单、团期物资候选弹窗两个只读接口的出参 +- **零影响**: + - 小程序端备品清单与其分组接口(本次未改) + - 物资新增 / 改数量 / 软删 / 确认物资等写接口(取值域未收紧,手工新增仍可自由填) + - 终止退款链路取备品行的内部调用(刻意不走翻译,避免在写事务内发跨服务字典调用) + - 数据库(零 Flyway、存量不回填) + +--- + +## 八、测试环境已验证 + +部署:`dev-v3@a18569162`(合并 PR #7920 后)部署至 TEST,`hl-order-service-v3` 双实例滚动完成。 + +``` +GET /v3/admin/order/group-batch/2100509904627191810/supplies + → 200;category=个人装备 → categoryName=个人装备 ✓(存量中文原样) + → 200;category=safety_protection → categoryName=安全防护 ✓(字典码译中文) + +GET /v3/admin/order/group-batch/2099463556951941122/supplies + → 200;category=EQUIPMENT → categoryName=EQUIPMENT ✓(字典外原值) + → 200;category=PROTECTION → categoryName=PROTECTION ✓ + +GET /v3/admin/order/group-batch/2099919772194836482/supplies/candidates + → 200;15 项全部带 categoryName,personal_gear→个人装备、camping_equipment→露营设备 ✓ + +category 为空的行(临时夹具,取证后已删) + → 200;category=null → categoryName=null ✓(非空串、非 "null") + +字典不可达注入(临时改 sys_dict_type.dict_type 使 supplies_category 查不到,重启后冷缓存) + → 列表 200;safety_protection → safety_protection ✓(整批回落原值) + → 候选 200;personal_gear → personal_gear、camping_equipment → camping_equipment ✓ + → 还原字典后翻译恢复 ✓ +``` + +验证团期: `2100509904627191810`(三段全覆盖)、`2099463556951941122`(字典外取值)、`2099919772194836482`(候选列表) + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#7916](https://git.1814.love:8443/wx/HL/issues/7916) +- 关联 PR: [wx/HL#7920](https://git.1814.love:8443/wx/HL/pulls/7920) +- 同类先例(子订单 `roomTypeName`): 见 `changelogs-v2/2026-09/14_7536_*` + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#7916](https://git.1814.love:8443/wx/HL/issues/7916) +- **PR**: [#7920](https://git.1814.love:8443/wx/HL/pulls/7920) +- **Merge commit**: [a18569162](https://git.1814.love:8443/wx/HL/commit/a18569162cb97e54b63f6b442e75292db4ad88d7) + +### 联系人 + +- **后端负责人**: @jw