--- schema: "hl-changelog/v2" ticket: "8481" title: "团期物资确认后再改清单,物资确认自动失效须重新确认;确认物资流水带清单快照,增删改写入时间线,进入待出发改用专用事件码" consumer: "admin" author: "jw(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "implemented" frontend_owner: "mmg" frontend_ref: "93a784cd0b1b69c00ff112675b44c083496a9593" target_release: "v2.1" verified_at: "2026-09-29" status_note: "PR #8487 已合入 dev-v3(22fce5985),TEST 的 hl-order-service-v3 运行 dev-v3 22fce5985(面板任务 45bfb475,两实例经运行字节探针确认)。经网关 api.test.1814.love 用 admin token 实测新增 / 改数量 / 删除后物资确认失效、改成原值不失效、失效后团期确认 589556、重新确认后放行、团期确认后三写口 589598、确认物资流水带清单快照、进入待出发记 BATCH_DEPARTURE_ADMIT,全部通过。前端需接:物资面板改数量 / 删除后要刷新团期详情(现在只有确认物资后才刷新),时间线识别三个新事件码(见第四节),故 frontend_status 记 pending。前端已交付(物资三能力成功补 emit changed+配置节点重检预检),详见 hl-admin v2.1 提交 93a784cd。" updated_at: "2026-09-28" base: "dev-v3" --- # 团期物资:确认后改清单自动失效 + 物资留痕补全(管理后台) > **服务**: hl-order-service-v3(端口 8086) > **PR**: #8487(合入 dev-v3 为 `22fce5985`) > **Issue**: #8481 > **日期**: 2026-09-28 > **影响范围**: 团期物资清单三个写接口、确认物资、团期状态流水(时间线) --- ## ⚠️ 关键变化 1. **物资确认后再改清单,确认自动失效**:团期在「配置」阶段、物资已确认时,新增一行、改数量(改成不同的数)、删除一行,任何一种都会把团期详情的 `materialConfirmed` 从 `true` 变回 `false`,要重新点「确认物资」。团期「确认」门因此只按最近一次确认过的清单放行。 2. **改数量改成原值不算变更**:返回 200,不失效、不写流水、数据不动。 3. **确认物资流水带清单快照**:`BATCH_MATERIAL_CONFIRM` 的 content 改为「确认物资清单(共 N 项)」,`extra` 带 `itemCount` 和逐行 `items[]`,能查到确认的是哪一版清单。 4. **时间线新增三个事件码**:`BATCH_SUPPLIES_CHANGE`「物资清单变更」、`BATCH_MATERIAL_CONFIRM_RESET`「物资变更·物资确认失效」、`BATCH_DEPARTURE_ADMIT`「进入待出发」。七项硬门全过进入待出发,原来记成「确认物资」,现在记「进入待出发」。 5. **请求、响应结构一字不改**,变的是副作用和时间线内容。前端要配合的两处见第四节「前端交接清单」。 --- ## 一、背景 团期缺口台账 g-072 / g-073,jw 2026-09-28 定案(方案 a):物资确认后,清单任一增删改,确认自动失效、须重新确认。 改前两个问题: | 问题 | 改前 | 后果 | |---|---|---| | 确认后改清单,确认不失效 | 三个写口都不碰 `material_confirmed` | 团期「确认」门按一份改过、没人再确认的清单放行 | | 留痕不完整 | 确认物资流水只有「确认物资清单」几个字;增删改零流水;进入待出发也记成「确认物资」 | 查不到确认的是哪一版清单、谁改过什么;时间线把准入显示成「确认物资」 | --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 新增团期物资行 | POST | `/v3/admin/order/group-batch/{groupBatchId}/supplies` | 修改行为 | 物资已确认时确认失效;写「物资清单变更」流水 | | 2 | 调整团期物资数量 | PUT | `/v3/admin/order/group-batch/supplies/{batchSuppliesId}/quantity` | 修改行为 | 同上;改成原值为无变更 | | 3 | 删除团期物资行 | DELETE | `/v3/admin/order/group-batch/supplies/{batchSuppliesId}` | 修改行为 | 同上 | | 4 | 确认团期物资 | POST | `/v3/admin/order/group-batch/{groupBatchId}/confirm-material` | 修改行为 | 流水带清单快照 | | 5 | 团期状态流水 | GET | `/v3/admin/order/group-batch/{groupBatchId}/status-logs` | 取值域扩展 | `eventType` 新增三个值 | --- ## 三、接口详情 ### 1. 新增团期物资行 `POST /v3/admin/order/group-batch/{groupBatchId}/supplies` **VO**: `AddSuppliesReqVO` → `Result` #### 使用场景 团期管理员在物资面板从备品库选一个备品(或手填临时物资)加进本团清单。只在「招募」「配置」两个阶段可调。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 雪花 ID | 团期主订单 ID | | suppliesResourceId | Body | Long | 否 | 备品库上架备品 | 传了从备品库取名称、分类、计费方式、默认单价 | | suppliesName | Body | String | 条件必填 | - | 不传 suppliesResourceId 时手填 | | quantity | Body | Integer | ✅ | ≥1 | 数量 | | unitPrice | Body | BigDecimal | 否 | - | 覆盖备品库默认单价 | | 其余字段 | Body | - | - | - | category / hasCost / billingType / sortOrder / remark,与现有契约一致,本次不变 | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | data | Long | 新建物资行 ID(batchSuppliesId),不变 | #### 请求示例 ```json { "suppliesResourceId": "2023438566624866411", "quantity": 2 } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": "2104513829419560962" } ``` #### 空数据 / 降级响应 无空数据形态;失败时 `data=null`,见错误响应。 #### 错误响应 ```json { "code": 589598, "message": "团期已确认,配置不可修改", "data": null } ``` ```json { "code": 589523, "message": "该备品已在本团期物资清单中,请直接调整数量", "data": null } ``` #### 业务边界 - 物资已确认时新增成功:`material_confirmed` 置回 false,时间线依次多两条:`BATCH_SUPPLIES_CHANGE`「新增物资:折叠桌椅套装 ×2」、`BATCH_MATERIAL_CONFIRM_RESET`「物资清单已变更,物资确认失效,需重新确认」。 - 物资未确认时新增成功:只多一条 `BATCH_SUPPLIES_CHANGE`。 - 阶段不对(团期已确认、已取消)返回 589598,零写入、不记流水。 - 备品库查询仍在事务外;备品不存在或已下架 589519、备品库不可用 589518,与改前相同。 ### 2. 调整团期物资数量 `PUT /v3/admin/order/group-batch/supplies/{batchSuppliesId}/quantity` **VO**: `AdjustSuppliesQuantityReqVO` → `Result` #### 使用场景 物资面板里改某一行的数量。只在「招募」「配置」两个阶段可调。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | batchSuppliesId | Path | Long | ✅ | 雪花 ID | 物资行 ID | | quantity | Body | Integer | ✅ | ≥1 | 新数量;**等于原值时视为无变更** | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | data | null | 不变 | #### 请求示例 ```json { "quantity": 6 } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": null } ``` #### 空数据 / 降级响应 无空数据形态。 #### 错误响应 ```json { "code": 589598, "message": "团期已确认,配置不可修改", "data": null } ``` ```json { "code": 589521, "message": "备品行不存在", "data": null } ``` #### 业务边界 - 数量真的变了:物资已确认时确认失效,时间线多两条(「修改物资数量:定制遮阳帽 4→6」+ 失效);未确认时只多一条变更。 - **改成原值**:返回 200,不写行、不失效、不记流水。 - 数量 ≤0 仍是参数校验拒绝,与改前相同。 - 两人同时删同一行时,后到的一笔在锁内读不到行,返回 589521(改前静默返回 200)。 ### 3. 删除团期物资行 `DELETE /v3/admin/order/group-batch/supplies/{batchSuppliesId}` **VO**: `Result`(无请求体) #### 使用场景 物资面板里删掉一行(逻辑删除)。只在「招募」「配置」两个阶段可调。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | batchSuppliesId | Path | Long | ✅ | 雪花 ID | 物资行 ID | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | data | null | 不变 | #### 请求示例 ```http DELETE /v3/admin/order/group-batch/supplies/2104513829419560962 ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": null } ``` #### 空数据 / 降级响应 无空数据形态。 #### 错误响应 ```json { "code": 589598, "message": "团期已确认,配置不可修改", "data": null } ``` #### 业务边界 - 物资已确认时删除:确认失效,时间线多「删除物资:折叠桌椅套装 ×2」+ 失效两条;未确认时只多一条变更。 - 行不存在或已删:589521。 ### 4. 确认团期物资 `POST /v3/admin/order/group-batch/{groupBatchId}/confirm-material` **VO**: `Result`(无请求体) #### 使用场景 团期管理员在「配置」阶段确认物资清单。物资已确认是团期「确认」的门条件之一。清单改过之后需要再点一次。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 雪花 ID | 团期主订单 ID | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | data | null | 不变 | #### 请求示例 ```http POST /v3/admin/order/group-batch/2104490621953794050/confirm-material ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": null } ``` #### 空数据 / 降级响应 清单为空也可以确认,流水记「确认物资清单(共 0 项)」,`items` 为空数组。 #### 错误响应 ```json { "code": 589501, "message": "团期状态不允许当前操作", "data": null } ``` #### 业务边界 - 只在「配置」(`RESOURCE_PREPARING`)可调,与改前相同;库里状态值认不出时也返回 589501(改前是 500)。 - 可以反复确认,每次都记一条带快照的流水。 - 与三个写口拿同一把团期行锁,快照一定是被确认的那一版清单。 ### 5. 团期状态流水 `GET /v3/admin/order/group-batch/{groupBatchId}/status-logs` **VO**: `GroupBatchStatusLogItemVO`(返回 `Result>`,无请求体) #### 使用场景 团期详情的时间线(GB-ADM-096)。本次结构不变,`eventType` 多了三个取值,确认物资那条的 `content` / `extra` 有内容了。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 雪花 ID | 团期主订单 ID | #### 出参 `Result>` | 字段 | 类型 | 说明 | |------|------|------| | eventType | String | 新增 `BATCH_SUPPLIES_CHANGE` / `BATCH_MATERIAL_CONFIRM_RESET` / `BATCH_DEPARTURE_ADMIT`,见六.5 | | eventTypeName | String | 对应「物资清单变更」/「物资变更·物资确认失效」/「进入待出发」 | | changeType | String | 前两个是 `DATA`,`BATCH_DEPARTURE_ADMIT` 是 `STATUS` | | content | String | 展示文本,前端直接渲染 | | extra | Object | 结构化快照,按事件类型不同,见六.5 | | 其余字段 | - | logId / fromStatus / toStatus / operatorName / changedAt 等,不变 | #### 请求示例 ```http GET /v3/admin/order/group-batch/2104490621953794050/status-logs ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": [ { "eventType": "BATCH_MATERIAL_CONFIRM", "eventTypeName": "确认物资", "changeType": "DATA", "content": "确认物资清单(共 4 项)", "operatorName": "admin", "extra": { "itemCount": 4, "items": [ { "batchSuppliesId": "2104513816161357826", "suppliesName": "定制遮阳帽", "category": "personal_gear", "quantity": 4, "unitPrice": 15.00, "billingType": "PER_PERSON", "hasCost": true } ] }, "changedAt": "2026-09-28 18:09:31" }, { "eventType": "BATCH_SUPPLIES_CHANGE", "eventTypeName": "物资清单变更", "changeType": "DATA", "content": "新增物资:折叠桌椅套装 ×2", "extra": { "action": "ADD", "batchSuppliesId": "2104513829419560962", "suppliesName": "折叠桌椅套装", "quantityBefore": 0, "quantityAfter": 2 }, "changedAt": "2026-09-28 18:09:32" }, { "eventType": "BATCH_MATERIAL_CONFIRM_RESET", "eventTypeName": "物资变更·物资确认失效", "changeType": "DATA", "content": "物资清单已变更,物资确认失效,需重新确认", "extra": { "triggerAction": "ADD", "batchSuppliesId": "2104513829419560962" }, "changedAt": "2026-09-28 18:09:32" }, { "eventType": "BATCH_DEPARTURE_ADMIT", "eventTypeName": "进入待出发", "changeType": "STATUS", "fromStatus": "MATERIAL_PREPARING", "toStatus": "PENDING_DEPARTURE", "content": "七项硬门全部通过,进入待出发", "extra": null, "changedAt": "2026-09-28 18:11:55" } ] } ``` #### 空数据 / 降级响应 无流水时 `data=[]`,与改前相同。 #### 错误响应 ```json { "code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "data": null } ``` #### 业务边界 - 历史流水不回补:本次上线前「进入待出发」写的是 `BATCH_MATERIAL_CONFIRM`(`changeType=STATUS`,`toStatus=PENDING_DEPARTURE`),这些旧行仍显示「确认物资」。 - 同一秒写入的「变更」「失效」两条按 `changedAt` 排序,秒级相同时靠库的自然顺序返回;实测为「变更」在前。 --- ## 四、契约约束与正确调用方式 > 本节只写**后端接受/拒绝的规则**和前端必须跟着做的事。 ### ✅ 正确 / ❌ 错误用法对照 | 场景 | 做法 | |------|------| | ✅ 改完清单后判断能不能点团期「确认」 | 重新拉团期详情的 `materialConfirmed`,或调 `confirm-check` 看 `MATERIAL_CONFIRMED` | | ❌ 改完清单后沿用页面上缓存的「物资已确认」 | 后端已经失效,点团期「确认」会返回 589556「团期尚不满足确认条件:物资未确认」 | | ✅ 时间线按 `eventTypeName` 渲染,未知码回落 `eventType` 原值 | 三个新码后端已带中文标签 | ### 前端交接清单 1. **物资面板改数量 / 删除之后刷新团期详情**:`hl-ui` `origin/v2.1` @ `501c4258` 的 `SuppliesPanel.vue` 现在只在确认物资后 `emit('changed')`,新增 / 改数量 / 删除后不刷新,页头和进度条「配物资·已确认」要手动刷新页面才会变。 2. **时间线认三个新事件码**:`BATCH_SUPPLIES_CHANGE` / `BATCH_MATERIAL_CONFIRM_RESET` / `BATCH_DEPARTURE_ADMIT`。若时间线有按事件码配图标或筛选,需要补上。 --- ## 五、数据库行为 | 情形 | order_batch_supplies | order_group_batch.material_confirmed | group_batch_status_log | |------|----------------------|--------------------------------------|------------------------| | 物资已确认时增 / 改(新值)/ 删 | 写入 | 1 → 0 | +`BATCH_SUPPLIES_CHANGE` +`BATCH_MATERIAL_CONFIRM_RESET` | | 物资未确认时增 / 改(新值)/ 删 | 写入 | 保持 0 | +`BATCH_SUPPLIES_CHANGE` | | 改数量为原值 | 不动 | 不动 | 不写 | | 阶段不对(589598) | 不动 | 不动 | 不写 | | 确认物资 | 不动 | → 1 | +`BATCH_MATERIAL_CONFIRM`(extra 带快照) | - 三个写口的「写物资行 + 失效标记」在同一个事务里:失效那一步失败时物资行一起回滚。流水写失败只告警、不回滚业务(与 #7023 口径一致)。 - 三个写口和确认物资都先锁团期行(`SELECT ... FOR UPDATE`),彼此串行。 - 无 DDL;新事件码是 `event_type` 列的新取值。 --- ## 六、边界行为 - 未登录 → 网关返回 `code=401`(HTTP 200)。 - 无团期管理权限 → 589507,与改前相同。 - 团期创建时从产品带入的物资(系统固化)不写流水、不动确认标记。 - 本单只在写口一侧加锁;团期「确认」本身的读法没改。 --- ## 六.5、枚举 / 数据字典 ### eventType 新增取值(`GroupBatchLogEventType`) | 值 | 中文 | changeType | extra | |---|---|---|---| | `BATCH_SUPPLIES_CHANGE` | 物资清单变更 | DATA | `action`(ADD / ADJUST_QUANTITY / DELETE)、`batchSuppliesId`(字符串)、`suppliesName`、`quantityBefore`、`quantityAfter`(新增时 before=0,删除时 after=0) | | `BATCH_MATERIAL_CONFIRM_RESET` | 物资变更·物资确认失效 | DATA | `triggerAction`、`batchSuppliesId`(字符串) | | `BATCH_DEPARTURE_ADMIT` | 进入待出发 | STATUS | null | ### BATCH_MATERIAL_CONFIRM 的 extra | 字段 | 类型 | 说明 | |---|---|---| | itemCount | Integer | 确认时清单行数 | | items[].batchSuppliesId | String | 物资行 ID(雪花,按字符串) | | items[].suppliesName | String | 名称 | | items[].category | String | 分类原值(字典码或历史中文,不翻译) | | items[].quantity | Integer | 数量 | | items[].unitPrice | BigDecimal | 单价,可空 | | items[].billingType | String | PER_PERSON / PER_QUANTITY,可空 | | items[].hasCost | Boolean | 是否计费 | --- ## 六.6、修改前后对比 ### 字段级对比 | 字段 | 改前 | 改后 | |------|------|------| | 五个接口的请求 / 响应字段 | - | 不变 | | 确认物资流水 content | 「确认物资清单」 | 「确认物资清单(共 N 项)」 | | 确认物资流水 extra | null | `itemCount` + `items[]` | | 确认物资流水 changeType | DATA | DATA(枚举定义由 STATUS 改正为 DATA,写入值不变) | ### 行为级对比 | 行为 | 改前 | 改后 | |------|------|------| | 物资已确认后增 / 改 / 删 | `materialConfirmed` 仍为 true | 置回 false,须重新确认 | | 改数量为原值 | 照写一次 | 不写、不失效、不记流水 | | 物资增删改的时间线 | 无 | `BATCH_SUPPLIES_CHANGE`(+ 失效时 `BATCH_MATERIAL_CONFIRM_RESET`) | | 七项硬门全过进入待出发 | 记 `BATCH_MATERIAL_CONFIRM`「确认物资」 | 记 `BATCH_DEPARTURE_ADMIT`「进入待出发」 | | 两人同时删同一行,后到的一笔 | 200 | 589521 | | 确认物资遇到认不出的状态值 | 500 | 589501 | ## 六.7、影响评估 - **是否破坏向后兼容**: 否(结构不变;时间线新增的是取值,旧前端按原值回落显示) - **前端是否必须同步上线**: 否,但不改的话,改完清单后页面上的「物资已确认」会一直显示到手动刷新(点团期「确认」时后端会拦住并提示) - **前端 workaround 清理点**: 无 --- ## 七、不影响范围 - **仅影响**: 上面五个接口的副作用与时间线内容 - **零影响**: - 五个接口的请求 / 响应结构 - 物资可写阶段(仍是「招募」「配置」两态,团期确认后 589598) - 团期「确认」门的判定口径(仍是四项 ready + 物资已确认 + 逐户预检) - 物资候选列表、物资列表两个读接口 - 团期创建时的物资固化 --- ## 八、测试环境已验证 部署:hl-order-service-v3 dev-v3 @ `22fce5985`,面板任务 `45bfb475`,两实例经运行字节探针确认加载的是本次代码(新文案命中、阴性对照未命中)。经网关 `api.test.1814.love` 用 admin token 实测,团期 `2104490621953794050`(两户全款、四项资源就绪),另用团期 `2104514146831904769` 补证团期详情: ``` POST confirm-material(清单 4 项) → 200,material_confirmed 0→1,流水「确认物资清单(共 4 项)」extra.itemCount=4 ✓ POST supplies 已确认后新增 折叠桌椅套装×2 → 200,material_confirmed 1→0,流水 BATCH_SUPPLIES_CHANGE + BATCH_MATERIAL_CONFIRM_RESET ✓ GET confirm-check → MATERIAL_CONFIRMED passed=false「物资未确认」,其余四项 true ✓ POST confirm(团期确认) → 589556「团期尚不满足确认条件:物资未确认」,团期行 / 物资行 / 流水零变化 ✓ POST confirm-material → 200,快照「共 5 项」,与上一份不同 ✓ PUT quantity 定制遮阳帽 4→6(已确认) → 200,1→0,流水「修改物资数量:定制遮阳帽 4→6」+ 失效 ✓ PUT quantity 6→6(已确认) → 200,标记仍 1,零流水,物资行不变 ✓ DELETE supplies 折叠桌椅套装(已确认) → 200,1→0,流水「删除物资:折叠桌椅套装 ×2」+ 失效 ✓ PUT quantity 晕车贴+口罩 4→5(未确认) → 200,只多 BATCH_SUPPLIES_CHANGE ✓ POST confirm-material → GET confirm-check → ready=true,五项全部通过 ✓ POST confirm(团期确认) → 200,RESOURCE_PREPARING→MATERIAL_PREPARING ✓ POST supplies / PUT quantity / DELETE(团期确认后) → 三个都是 589598「团期已确认,配置不可修改」,零变化 ✓ POST recheck-departure-gate → 七门全过,MATERIAL_PREPARING→PENDING_DEPARTURE,流水 BATCH_DEPARTURE_ADMIT「进入待出发」✓ GET 团期详情(另一团期)确认后 / 新增一行后 → materialConfirmed true→false,进度条「配物资·已确认」→「配物资·未确认」✓ POST supplies 不带 token → code=401,物资行 4→4 ✓ ``` 所有新流水 `operatorType=ADMIN`、`operatorName=admin`、`changedAt` 有值。 --- ## 九、相关历史 PR - #7023:物资阶段门与确认留痕(确认物资无条件记流水) - #8268:团期人工「确认」 - #8269:团期确认后锁定配置 - #8478:团期详情分叉进度条(「配物资」分支读 `materialConfirmed`) --- ## 十、相关文档 - 关联 Issue: [wx/HL#8481](https://git.1814.love/wx/HL/issues/8481) - 关联 PR: [wx/HL#8487](https://git.1814.love/wx/HL/pulls/8487) ## 关联 / 联系人 ### 链接 - **Issue**: [#8481](https://git.1814.love/wx/HL/issues/8481) - **PR**: [#8487](https://git.1814.love/wx/HL/pulls/8487) - **Merge commit**: [22fce5985](https://git.1814.love/wx/HL/commit/22fce5985) ### 联系人 - **后端负责人**: @jw