From 2b45ee9d442388a7843a4b5ddc7632cb4e307b0b Mon Sep 17 00:00:00 2001 From: jw Date: Wed, 9 Sep 2026 16:25:30 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20=E5=9B=A2=E6=9C=9F=E4=BA=BA?= =?UTF-8?q?=E5=91=98=E9=85=8D=E7=BD=AE=20staffList=20=E5=BF=85=E5=A1=AB=20?= =?UTF-8?q?+=20=E5=A4=87=E5=93=81=E5=80=99=E9=80=89=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E5=AD=97=E8=BD=AC=E4=B9=89=20+=20=E5=8A=A0=E5=A4=87=E5=93=81?= =?UTF-8?q?=E8=A1=8C=E6=94=B9=E5=AE=9A=E7=82=B9=E6=9F=A5=EF=BC=88#7377?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 收口 2026-09-03 团期人员/备品合并后审计的余项(F-03/F-05/F-06/F-07),随 PR #7028 合入 dev-v3。 两处对外行为变更需前端注意: - PUT /v3/admin/group-batch/:productBatchId/staff 的 staffList 改为必填, 字段缺失或名字写错一律 400;改前会被当成「传空=清空」,静音软删整团配置还返 200。 「显式传 [] 即清空」这条语义不变。 - 备品候选列表的 keyword 转义 LIKE 通配符,搜 % 或 _ 由「返回全库」变为「零结果」。 第三处对调用方透明:加备品行改走资源域新增的定点查,解除「备品库超 500 条后 第 501 条起加不进」的隐性上限,契约与错误码不变。 后端已部署 TEST 并经真实网关逐条实测;order-v3 全量 9303 例、resource 全量 2707 例 0 失败。 Co-Authored-By: Claude Opus 5 (1M context) --- ...˜配置必填与备品库口径收口-修改接口-管理后台.md | 337 ++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 changelogs-v2/2026-09/09_7377_团期人员配置必填与备品库口径收口-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/09_7377_团期人员配置必填与备品库口径收口-修改接口-管理后台.md b/changelogs-v2/2026-09/09_7377_团期人员配置必填与备品库口径收口-修改接口-管理后台.md new file mode 100644 index 00000000..9ff3f2e6 --- /dev/null +++ b/changelogs-v2/2026-09/09_7377_团期人员配置必填与备品库口径收口-修改接口-管理后台.md @@ -0,0 +1,337 @@ +--- +schema: "hl-changelog/v2" +ticket: "7377" +title: "团期人员配置 staffList 改必填 + 备品候选关键字转义 + 加备品行改定点查" +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: "2026-09-09" +status_note: "收口 2026-09-03 团期人员/备品合并后审计的余项。**前端必看两条**:① 保存团期人员配置时 staffList 现在是必填,字段缺失或名字写错一律 400——改前会被当成「传空=清空」,静音软删整团人员配置还返 200;要清空请显式传 []。② 备品候选列表的关键字现在会转义 LIKE 通配符,搜 % 或 _ 从「返回全库」变成「零结果」,正常关键字不受影响。后端已部署 TEST 并经真实网关逐条实测。" +updated_at: "2026-09-09" +base: "dev-v3" +--- + +# 团期人员配置必填与备品库口径收口 + +## 一、给前端的一句话 + +两处**行为变更**要注意:保存团期人员配置**必须带 `staffList` 字段**(要清空就传 `[]`,别省略);备品候选列表搜 `%` / `_` 不再返回全库。另有一处纯修复不影响调用方——加备品行不再受「备品库超 500 条」的隐性上限。 + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|---|---|---|---|---| +| 1 | 保存团期人员配置 | PUT | `/v3/admin/group-batch/:productBatchId/staff` | 修改接口 | `staffList` 由可省略改为**必填**,缺失返 400;显式传 `[]` 仍是清空 | +| 2 | 团期物资候选列表 | GET | `/v3/admin/order/group-batch/:groupBatchId/supplies/candidates` | 修改接口 | `keyword` 转义 LIKE 通配符,`%` / `_` 不再当通配符 | +| 3 | 团期物资新增行 | POST | `/v3/admin/order/group-batch/:groupBatchId/supplies` | 修改接口 | 取备品库快照改走定点查,解除「备品库超 500 条后加不进」的隐性上限 | + +## 三、接口详情 + +### 1. 保存团期人员配置 `PUT /v3/admin/group-batch/:productBatchId/staff` + +**VO**: `BatchStaffConfigReqVO / BatchStaffConfigRespVO` + +#### 使用场景 + +团期详情的「导游 / 摄影」配置,全量覆盖式保存。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|---|---|---|---|---|---| +| `productBatchId` | Path | String | 是 | 正整数 | 产品侧班期 ID | +| `staffList` | Body | Array | **是(本次变更)** | 可为空数组 | 全量覆盖列表;**显式传 `[]` 即清空**,字段缺失一律 400 | +| `staffList[].staffId` | Body | String | 是 | 正整数 | 资源域人员 ID | +| `staffList[].staffRole` | Body | String | 是 | `LEADER` / `GUIDE` / `DRIVER` / `PHOTOGRAPHER` / `OTHER`,区分大小写 | 越界值返 400;与该人在资源域的实际类型不符返 582114 | +| `staffList[].sortOrder` | Body | Integer | 否 | 默认 0 | 展示排序 | +| `staffList[].remark` | Body | String | 否 | 最长 500 | 备注 | + +#### 出参 + +| 字段 | 类型 | 说明 | +|---|---|---| +| `code` / `message` / `success` | Integer / String / Boolean | 业务结果 | +| `data.staffList` | Array | 保存后的配置快照,含 `staffId` / `staffName` / `staffPhone` / `staffRole` 等 | +| `data.affectedOrderCount` | Integer | 已触发异步扇出的活跃订单数,仅作提示 | + +#### 请求示例 + +```json +{ + "staffList": [ + { + "staffId": "1002", + "staffRole": "GUIDE", + "sortOrder": 0 + } + ] +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "staffList": [ + { + "staffId": "1002", + "staffName": "李雪梅", + "staffPhone": "138****1002", + "staffRole": "GUIDE", + "sortOrder": 0 + } + ], + "affectedOrderCount": 2 + } +} +``` + +#### 空数据 / 降级响应 + +显式传 `[]` 是合法请求:清空该团期全部人员配置并返回空 `staffList`,`code` 仍为 200。 + +#### 错误响应 + +```json +{ + "code": 400, + "message": "staff 配置列表不能缺失;确要清空请显式传空数组 []", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- **字段缺失或字段名写错一律 400,且现有配置零变动、不触发扇出**——这正是本次要堵的洞。 +- 团期未创建返 589553、未成团返 589552;人员角色与资源域类型不符返 582114,整批拒绝不落库。 +- 「显式传 `[]` 即清空」这条既有语义**没有改变**。 + +### 2. 团期物资候选列表 `GET /v3/admin/order/group-batch/:groupBatchId/supplies/candidates` + +**VO**: `SuppliesCandidateRespVO` + +#### 使用场景 + +团期物资清单的「从备品库选择」弹窗,支持按分类与关键字筛选。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|---|---|---|---|---|---| +| `groupBatchId` | Path | String | 是 | 正整数 | 团期主订单 ID | +| `categoryCode` | Query | String | 否 | 字典 `supplies_category` | 分类筛选 | +| `keyword` | Query | String | 否 | 无长度限制 | 匹配名称或副标题;**本次起 LIKE 通配符被转义** | + +#### 出参 + +| 字段 | 类型 | 说明 | +|---|---|---| +| `code` / `message` / `success` | Integer / String / Boolean | 业务结果 | +| `data[].suppliesResourceId` | String | 备品资源 ID | +| `data[].suppliesName` / `subtitle` / `category` | String | 备品名称、副标题、分类 | +| `data[].billingType` / `hasCost` / `basePrice` | String / Boolean / String | 计费方式、是否收费、库价 | +| `data[].added` / `batchSuppliesId` | Boolean / String | 是否已加入本团期清单及对应行 ID | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/2097500233511362561/supplies/candidates?keyword=%E5%B8%BD +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": [ + { + "suppliesResourceId": "2023438566624866305", + "suppliesName": "定制遮阳帽", + "subtitle": "呼籁旅行专属防晒鸭舌帽", + "category": "personal_gear", + "billingType": "PER_PERSON", + "hasCost": true, + "basePrice": "15.00", + "added": false, + "batchSuppliesId": null + } + ] +} +``` + +#### 空数据 / 降级响应 + +无命中返回 `data: []`,`code` 仍为 200。 + +#### 错误响应 + +```json +{ + "code": 589500, + "message": "团期不存在", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- `keyword` 里的 `%` / `_` / `\` / `!` 一律按**字面字符**匹配,不再当通配符。 +- 普通关键字的搜索结果**不受影响**(实测「帽」仍命中「定制遮阳帽」)。 +- 只返上架备品,排序与分页口径未变。 + +### 3. 团期物资新增行 `POST /v3/admin/order/group-batch/:groupBatchId/supplies` + +**VO**: `AddSuppliesReqVO` + +#### 使用场景 + +团期物资清单新增一行,可从备品库选择(传 `suppliesResourceId`)或纯手填(传 `suppliesName`)。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|---|---|---|---|---|---| +| `groupBatchId` | Path | String | 是 | 正整数 | 团期主订单 ID | +| `suppliesResourceId` | Body | String | 与 `suppliesName` 二选一 | 正整数 | 备品库资源 ID;传了则名称、分类、计费方式以库为准覆盖入参 | +| `suppliesName` | Body | String | 与 `suppliesResourceId` 二选一 | 最长 100 | 纯手填备品名 | +| `quantity` | Body | Integer | 是 | 大于 0 | 数量 | +| `unitPrice` | Body | String | 否 | 金额字符串 | 单价,**入参优先**;不传取库价 | + +#### 出参 + +| 字段 | 类型 | 说明 | +|---|---|---| +| `code` / `message` / `success` | Integer / String / Boolean | 业务结果 | +| `data` | String | 新建行主键 `batchSuppliesId` | + +#### 请求示例 + +```json +{ + "suppliesResourceId": "2023438566624866305", + "quantity": 1 +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": "2097600714757783553" +} +``` + +#### 空数据 / 降级响应 + +无空成功结果;任一校验失败返回 `data: null`、`success: false`。 + +#### 错误响应 + +```json +{ + "code": 589523, + "message": "该备品已在本团期物资清单中,请直接调整数量", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- 备品库不可用(资源域故障)与备品本身不可用(不存在 / 已下架)**分开报**:前者 589522,后者 589519。 +- 同一 `suppliesResourceId` 重复加入返 589523;纯手填行不参与判重。 +- 团期不在「物料准备中」返 589520,团期不存在返 589500。 + +## 四、契约约束与正确调用方式 + +- 保存人员配置**永远显式带上 `staffList`**:要清空传 `[]`,不要省略字段。省略在改前会静音清空整团配置,现在会 400。 +- 备品搜索框不需要前端做任何转义,直接把用户输入原样传 `keyword` 即可。 +- 加备品行时 `unitPrice` 不传即取库价;传了以入参为准(同团期可议价不同)。 + +## 五、数据库行为 + +- 人员配置:全量覆盖式保存——软删旧行 + 插入新行,随后异步扇出到该班期全部活跃订单的 `order_staff_assignment`。校验失败时**一行都不写**。 +- 物资新增:向 `order_batch_supplies` 插一行;重复 `supplies_resource_id` 在插入前被拒。 +- 本次**无表结构变更、无 Flyway 脚本**。 + +## 六、边界行为 + +- 人员配置越界角色值区分大小写,`guide` 不等于 `GUIDE`。 +- 备品候选列表只返上架备品;已下架的既有行不受影响,仍留在清单里。 +- 加备品行的库快照覆盖入参的字段是名称、分类、计费方式;单价例外,入参优先。 + +## 六.6、修改前后对比 + +| 场景 | 改前 | 改后 | +|---|---|---| +| 保存人员配置时不带 `staffList`(或字段名写错) | **静音软删整团人员配置 + 触发全团扇出,返 200** | 返 400,现有配置零变动 | +| 保存人员配置时显式传 `[]` | 清空 | 清空(**不变**) | +| 备品候选搜 `%` 或 `_` | 返回全库 | 返回零结果 | +| 备品候选搜普通关键字 | 正常命中 | 正常命中(**不变**) | +| 备品库超过 500 条时加第 501 条之后的备品 | 一律报「所选备品不存在或已下架」,实际是被 500 条上限截断 | 正常加入 | +| 资源域故障时加备品行 | 报「不存在或已下架」,与备品真下架无法区分 | 报 589522「备品库查询失败」,与 589519 分开 | + +## 六.7、影响评估 + +- **需要前端配合的只有一条**:保存人员配置必须带 `staffList`。经排查管理后台现有调用是带该字段的,理论上不受影响;但字段名一旦写错,改前是静默清空、改后是 400,属"报错优于静默毁数据"。 +- 备品搜索语义变化只影响把 `%` / `_` 当通配符用的用法,业务上没有这种用法。 +- 加备品行的改动对调用方**完全透明**,契约与错误码不变,只是不再有隐性上限。 + +## 七、不影响范围 + +- 团期看板、详情、子订单、合同保险、财务、流团与退单等其余端点未改动。 +- 备品清单的查询、改数量、删除、确认物资四个端点契约不变。 +- 无表结构变更、无网关路由变更、无新增错误码(589519 / 589522 / 589523 / 582114 均为既有)。 + +## 八、测试环境已验证 + +真实网关(`api.test.1814.love`)逐条实测,`dev-v3` 分支已部署 `hl-resource-service` 与 `hl-order-service-v3`: + +| 用例 | 结果 | +|---|---| +| 保存人员配置缺 `staffList`(模拟字段名写成 `items`) | 400「staff 配置列表不能缺失;确要清空请显式传空数组 []」 | +| 保存人员配置显式传 `[]` | 200,配置清空为 0 条 | +| GUIDE 类型的人配成 PHOTOGRAPHER | 582114,配置数仍为 0(零写入) | +| GUIDE 类型的人配成 GUIDE | 200,回显 `staffId` / `staffName` / `staffPhone` / `staffRole` 全字段 | +| 配完后看导游位候选 | `assigned=true`、`assignedRole=GUIDE` | +| 配完后看摄影位候选 | 该人不在候选内(勾选态按配置位收敛) | +| 备品候选 `keyword=%` / `keyword=_` | 均返 0 条(改前返回全库 15 条) | +| 备品候选 `keyword=帽` | 返 1 条「定制遮阳帽」 | +| 从备品库加行 | 200,库快照生效(`billingType=PER_PERSON`、`hasCost=true`、`unitPrice=15.00`) | +| 同一备品再加一次 | 589523,清单无重复行 | +| 三个写端点传不存在的 ID | 589500 / 589521 / 589553,一律零写入 | + +单元测试:`hl-order-service-v3` 全量 9303 例、`hl-resource-service` 全量 2707 例,均 0 失败 0 错误。 + +## 九、相关历史 PR + +- PR #7028(本次合入,含 2026-09-03 审计的 F-01 / F-02 / F-04 三项中危修复) +- 前序 #6950(团期人员配置候选列表)、#6986(团期物资备品库选择) + +## 十、相关文档 + +- 审计记录:`dev-records/records/2026-09-03-local-group-tour-6950-6986-post-merge-audit.md` +- 实施单:`docs/group/实施单/07-人员-导游摄影司机.html`、`docs/group/实施单/10-物资清单.html` + +## 关联 / 联系人 + +- 工单:#7377(收口审计余项)、#7028(PR) +- 后端:jw +- 前端:mmg —— 只需确认保存人员配置时始终带 `staffList` 字段