From f3e200bc4543684cf2d0c11d40b6caf01490c67a Mon Sep 17 00:00:00 2001 From: jw Date: Mon, 28 Sep 2026 19:44:45 +0800 Subject: [PATCH] =?UTF-8?q?docs(order-v3):=20=E5=9B=A2=E6=9C=9F=E4=BA=BA?= =?UTF-8?q?=E5=91=98=E4=B8=8E=E6=8A=A5=E8=B4=A6=E4=BA=BA=E5=8F=98=E6=9B=B4?= =?UTF-8?q?=E5=86=99=E8=BF=9B=E5=9B=A2=E6=9C=9F=E6=97=B6=E9=97=B4=E7=BA=BF?= =?UTF-8?q?=E4=BA=A4=E6=8E=A5=E4=BB=B6=EF=BC=88#8482=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 五条人员写路径补留痕(新增 5 个事件码)+ GB-ADM-096 同秒按 logId 排序; PR #8490(e24be7af8)、#8492(75e182710),TEST 网关验收通过,前端零改动。 Refs wx/HL#8482 Co-Authored-By: Claude Opus 5.5 --- ...¸Ž报账人变更写进团期时间线-修改接口-管理后台.md | 916 ++++++++++++++++++ 1 file changed, 916 insertions(+) create mode 100644 changelogs-v2/2026-09/28_8482_团期人员与报账人变更写进团期时间线-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/28_8482_团期人员与报账人变更写进团期时间线-修改接口-管理后台.md b/changelogs-v2/2026-09/28_8482_团期人员与报账人变更写进团期时间线-修改接口-管理后台.md new file mode 100644 index 00000000..c8d36ced --- /dev/null +++ b/changelogs-v2/2026-09/28_8482_团期人员与报账人变更写进团期时间线-修改接口-管理后台.md @@ -0,0 +1,916 @@ +--- +schema: "hl-changelog/v2" +ticket: "8482" +title: "团期人员与报账人变更写进团期时间线:配置保存、设报账人、单人删除、单人更换、取消成团回收五条写路径补留痕(新增五个事件码),时间线同一秒内按 logId 排序" +consumer: "admin" +author: "jw(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "not_required" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "PR #8490 已合入 dev-v3(e24be7af8,五条写路径补留痕),PR #8492 已合入 dev-v3(75e182710,GB-ADM-096 同秒按 logId 排序);TEST 的 hl-order-service-v3 运行 dev-v3 75e182710。经网关 api.test.1814.love 用 admin token 实测:连设主报账人 A→B→C 三行可按时间还原更替、原样保存 / 只改排序 / 只改备注 / 设成已有等级零新增、单人删除与更换主(次)报账人各一行、保存移出主报账人一行、取消成团回收单独一行且排在取消成团之后、589508 / 589598 被拒零新增、CHECK 约束注入使留痕写失败时业务一起回滚、新增行无手机号,全部通过;排序修复部署后同一团期连读 5 次,同秒行全部按 logId 升序。frontend_status=not_required 的依据:#7987 时 mmg 已实证团期时间线组件 StatusLogsPanel 渲染 eventTypeName || eventType,中文名由后端直供,前端没有自建事件码映射表;2026-09-28 复核 hl-ui origin/v2.1@ce469f78 仍是这一写法,content 直接渲染,列表按接口返回顺序展示、不在前端重排,五个新事件码零改动即可正常显示。这一结论的前提是后端继续直供 eventTypeName:若后端对这些事件码返回 eventTypeName=null,页面会退化成显示原始事件码,那时本条须改回 pending。" +updated_at: "2026-09-28" +base: "dev-v3" +--- + +# 团期人员与报账人变更写进团期时间线(管理后台) + +> **服务**: hl-order-service-v3(端口 8086) +> **PR**: #8490(合入 dev-v3 为 `e24be7af8`)、#8492(合入 dev-v3 为 `75e182710`) +> **Issue**: #8482 +> **日期**: 2026-09-28 +> **影响范围**: 团期人员四个写接口与取消成团的副作用、团期状态流水(时间线 GB-ADM-096) + +--- + +## ⚠️ 关键变化 + +1. **五条人员写路径成功后各写一行时间线**:配置人员保存、设报账人、单人删除、单人更换、取消成团回收人员。新增 5 个 `eventType`:`BATCH_STAFF_CONFIG_SAVE`「调整团期人员」、`BATCH_REPORTER_CHANGE`「报账人变更」、`BATCH_STAFF_REMOVE`「移除团期人员」、`BATCH_STAFF_REPLACE`「更换团期人员」、`BATCH_STAFF_RECYCLED`「取消成团·回收人员」,`changeType` 都是 `DATA`。 +2. **一次操作只写一行**:设新主报账人时原主报账人自动降级、换人时报账人等级转给新人、被移出的人带走报账人身份,这些报账人变化都并进同一行的 `content` 和 `extra`,不另起一行。 +3. **没有实际变化不写**:原样保存、只改排序或备注、只改服务日期或日薪、把人设成他已有的报账人等级,时间线都不新增。 +4. **被拒的请求不写;留痕写失败时业务一起回滚**:接口返回 500,名册与子订单副本都不变。 +5. **extra 里的 ID 一律是字符串**;主 / 次报账人前后字段(`primaryReporterBefore` / `primaryReporterAfter` / `secondaryReporterBefore` / `secondaryReporterAfter`)只在对应归属真的变了时出现。 +6. **GB-ADM-096 同一秒内的多行按 `logId` 升序返回**。改前同秒行的先后由数据库决定,实测建团和成团会排反;改后「取消成团」与随后的回收行稳定是「取消成团」在前。 +7. **六个接口的请求、响应结构一字未改**。前端无需改动,理由见第四节「前端交接清单」。 + +--- + +## 一、背景 + +团期缺口台账 g-062(2026-09-26 四方对账,P2,后端缺口):配置、删除、更换导游和摄影,改主 / 次报账人,以及取消成团时回收人员,这五条写路径都不写团期操作记录,运营在时间线上看不到「谁在什么时候把谁换成了谁」。 + +主报账人不只是一个标签:代收、垫付、报账的归属实时按当前主报账人计算,不存历史快照;团期确认逐户校验主报账人(581062),预支的领款人也限定为主报账人(589557)。主报账人一变,这些都跟着变,但改前时间线上找不到原因。 + +jw 2026-09-28 裁决:内容没变的操作不写;一次操作只写一行,报账人变化并入这一行;取消成团回收单独写一行。 + +| 问题 | 改前 | 改后 | +|---|---|---| +| 人员 / 报账人变更无留痕 | 五条写路径都不写时间线 | 各写一行,带前后差异 | +| 取消成团回收了谁 | 时间线只有「取消成团」,回收人数只在服务端日志 | 单独一行,列出回收的人和报账人身份 | +| 同秒行顺序 | 只按 `changedAt`(精确到秒)排,同秒先后不稳定 | 同秒按 `logId` 升序 | + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|------|------|------|------|----------|------| +| 1 | 保存团期人员配置 | PUT | `/v3/admin/group-batch/{productBatchId}/staff` | 修改行为 | 名单或报账人归属有变化时写 `BATCH_STAFF_CONFIG_SAVE` | +| 2 | 设置报账人等级 | PUT | `/v3/admin/group-batch/{productBatchId}/staff/{staffId}/reporter-rank` | 修改行为 | 主 / 次报账人归属有变化时写 `BATCH_REPORTER_CHANGE` | +| 3 | 团期单人删除 | DELETE | `/v3/admin/group-batch/{productBatchId}/staff/{staffId}` | 修改行为 | 删除成功写 `BATCH_STAFF_REMOVE` | +| 4 | 团期单人更换 | PUT | `/v3/admin/group-batch/{productBatchId}/staff/{staffId}/replace` | 修改行为 | 更换成功写 `BATCH_STAFF_REPLACE` | +| 5 | 取消成团 | POST | `/v3/admin/order/group-batch/{groupBatchId}/cancel-group` | 修改行为 | 回收到人时,在「取消成团」那行之后再写一行 `BATCH_STAFF_RECYCLED` | +| 6 | 团期状态流水(GB-ADM-096) | GET | `/v3/admin/order/group-batch/{groupBatchId}/status-logs` | 取值域扩展 + 排序 | `eventType` 新增五个值;同秒按 `logId` 升序 | + +--- + +## 三、接口详情 + +### 1. 保存团期人员配置 `PUT /v3/admin/group-batch/{productBatchId}/staff` + +**VO**: `BatchStaffConfigReqVO` → `Result` + +#### 使用场景 + +「配置人员」弹窗保存(导游位、摄影位各一个弹窗)。请求与响应不变;保存成功后,名单或报账人归属有变化时,团期时间线多一行「调整团期人员」。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| productBatchId | Path | Long | ✅ | 雪花 ID | 产品侧排期 ID,不是 groupBatchId | +| scopeRoles | Body | String[] | 否 | 传了不能是空数组;导游位须同时传 GUIDE 与 LEADER | 本次覆盖的角色范围,不传 = 整期全量覆盖;也决定时间线里的位名 | +| staffList | Body | Array | ✅ | 可以是 `[]`(清空该范围) | 覆盖范围内的最终名单 | +| staffList[].staffId | Body | Long | ✅ | - | 员工 ID | +| staffList[].staffRole | Body | String | ✅ | LEADER / GUIDE / PHOTOGRAPHER 等 | 落库角色 | +| 其余字段 | Body | - | - | - | sortOrder / remark / serviceStartDate / serviceEndDate,与现有契约一致,本次不变;这些字段的变化不写时间线 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| productBatchId | String | 回显,不变 | +| groupBatchId | String | 团期主键,不变 | +| staffList | Array | 保存后的名册,不变 | +| affectedOrderCount | Integer | 扇出影响的子订单数,不变 | + +#### 请求示例 + +TEST 实测「加一人减一人」(导游位原有李雪梅、白云飞、刘大山,本次移出李雪梅、加入萨仁高娃): + +```json +{ + "staffList": [ + { "staffId": 1007, "staffRole": "LEADER", "sortOrder": 1 }, + { "staffId": 1005, "staffRole": "LEADER", "sortOrder": 2 }, + { "staffId": 1008, "staffRole": "LEADER", "sortOrder": 3 } + ], + "scopeRoles": ["GUIDE", "LEADER"] +} +``` + +#### 响应示例 + +响应结构不变(下为 TEST 原文节选,名册每人只列部分字段,略去手机号): + +```json +{ + "code": 200, + "message": "成功", + "data": { + "productBatchId": "2104527134162853890", + "groupBatchId": "2104527135173685249", + "staffList": [ + { "id": "2104527304799711233", "staffId": 1003, "staffRole": "PHOTOGRAPHER", "staffName": "王强", "sortOrder": 1, "reporterRank": "NONE", "reporterRankName": "非报账人" }, + { "id": "2104527526883930114", "staffId": 1007, "staffRole": "LEADER", "staffName": "白云飞", "sortOrder": 1, "reporterRank": "PRIMARY", "reporterRankName": "主报账人" }, + { "id": "2104527526888124417", "staffId": 1005, "staffRole": "LEADER", "staffName": "刘大山", "sortOrder": 2, "reporterRank": "SECONDARY", "reporterRankName": "次报账人" }, + { "id": "2104527526892318722", "staffId": 1008, "staffRole": "LEADER", "staffName": "萨仁高娃", "sortOrder": 3, "reporterRank": "NONE", "reporterRankName": "非报账人" } + ], + "affectedOrderCount": 1 + }, + "traceId": null, + "success": true +} +``` + +同时 GB-ADM-096 新增的一行(原文): + +```json +{ + "logId": "2104527526900707330", + "groupBatchId": "2104527135173685249", + "changeType": "DATA", + "eventType": "BATCH_STAFF_CONFIG_SAVE", + "eventTypeName": "调整团期人员", + "fromStatus": "RESOURCE_PREPARING", + "toStatus": "RESOURCE_PREPARING", + "content": "导游位:+萨仁高娃;−李雪梅", + "operatorType": "ADMIN", + "operatorId": "1001", + "operatorName": "admin", + "extra": { + "scopeRoles": ["GUIDE", "LEADER"], + "added": [{ "staffId": "1008", "staffName": "萨仁高娃", "staffRole": "LEADER" }], + "removed": [{ "staffId": "1002", "staffName": "李雪梅", "staffRole": "GUIDE", "reporterRank": "NONE" }], + "roleChanged": [] + }, + "changedAt": "2026-09-28 19:03:57" +} +``` + +#### 空数据 / 降级响应 + +无空数据形态。`staffList=[]` 表示清空该范围:有人被移出时同样写一行,位名后只有「−」一段。 + +#### 错误响应 + +```json +{ "code": 589598, "message": "团期已确认,配置不可修改", "data": null, "traceId": null, "success": false } +``` + +582115 / 582116 / 582119 / 589553 等其余错误码与改前相同。所有被拒请求零写入,也不写时间线。 + +#### 业务边界 + +- 写的条件:名单(staffId + 落库角色)有新增、移除或角色变化,或者主 / 次报账人归属变了。排序、备注、服务日期、日薪不参与比较。 +- 原样保存(前端形态:保留行只带 staffId / staffRole / sortOrder)、只改排序、只改备注:返回 200,时间线不新增(TEST 实测三次都是 8→8)。 +- content 格式:`{位名}:+新增的人;−移出的人;角色调整:某人(导游→领队)`,各段有内容才出现。位名:不传 `scopeRoles` 为「团期人员」,其余为「导游位」「摄影位」或「导游位、摄影位」。 +- 被移出的人带走报账人身份时,名字后写明去向:团期因此没有主报账人写「(原主报账人,团期现无主报账人)」,团期仍有别的主报账人写「(原主报账人,团期现主报账人为某某)」;次报账人同理。extra 同时带上报账人前后字段。 +- 报账人归属变了、却不是被移出的人带走的(少见,例如历史上同一人有重复行),content 末尾单列一段「主报账人:X → Y」。 +- content 超过 512 个字符时截断,末尾加「…」;完整名单以 extra 为准。 + +### 2. 设置报账人等级 `PUT /v3/admin/group-batch/{productBatchId}/staff/{staffId}/reporter-rank` + +**VO**: `SetReporterRankReqVO` → `Result` + +#### 使用场景 + +名册里给某人设主报账人、次报账人,或取消报账人。团期内主、次报账人各唯一:设新的主(次)报账人时,原来那位自动降为非报账人。一次操作改动两个人,时间线只写一行。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| productBatchId | Path | Long | ✅ | 雪花 ID | 产品侧排期 ID | +| staffId | Path | Long | ✅ | 须是本团已配人员 | 员工 ID | +| reporterRank | Body | String | ✅ | PRIMARY / SECONDARY / NONE | 目标等级 | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | null | 不变 | + +#### 请求示例 + +`PUT /v3/admin/group-batch/2104527134162853890/staff/1005/reporter-rank`(此前主报账人是李雪梅): + +```json +{ "reporterRank": "PRIMARY" } +``` + +#### 响应示例 + +```json +{ "code": 200, "message": "成功", "data": null, "traceId": null, "success": true } +``` + +同时 GB-ADM-096 新增的一行(原文节选): + +```json +{ + "logId": "2104527347090894850", + "eventType": "BATCH_REPORTER_CHANGE", + "eventTypeName": "报账人变更", + "changeType": "DATA", + "content": "主报账人:李雪梅 → 刘大山", + "operatorType": "ADMIN", + "operatorId": "1001", + "operatorName": "admin", + "extra": { + "staffId": "1005", + "staffName": "刘大山", + "staffRole": "LEADER", + "reporterRankBefore": "NONE", + "reporterRankAfter": "PRIMARY", + "primaryReporterBefore": { "staffId": "1002", "staffName": "李雪梅" }, + "primaryReporterAfter": { "staffId": "1005", "staffName": "刘大山" } + }, + "changedAt": "2026-09-28 19:03:14" +} +``` + +#### 空数据 / 降级响应 + +无空数据形态。把人设成他已有的等级:返回 200,时间线不新增(TEST 实测 7→7)。 + +#### 错误响应 + +```json +{ "code": 589508, "message": "报账人不是该团期已派员工", "data": null, "traceId": null, "success": false } +``` + +```json +{ "code": 589598, "message": "团期已确认,配置不可修改", "data": null, "traceId": null, "success": false } +``` + +#### 业务边界 + +- 主 / 次报账人归属都没变就不写,业务写入照常执行。 +- content:「主报账人:张三 → 李四」「次报账人:无 → 王五」「取消主报账人:张三」。把次报账人升为主报账人时两段并在一行:「主报账人:无 → 乌日娜;次报账人:乌日娜 → 无」。 +- 前任在同一事务内加锁读取,记下的前任不会是过期值。 +- 历史数据里一团有两个主报账人时,重设其中一个会把另一个降级;归属因此变了,会写一行。 +- 留痕写失败时整笔回滚,接口返回 500「系统繁忙,请稍后重试或联系客服」,团级行与子订单副本的等级都不变(TEST 用 CHECK 约束注入实测)。 + +### 3. 团期单人删除 `DELETE /v3/admin/group-batch/{productBatchId}/staff/{staffId}` + +**VO**: `Result`(无请求体) + +#### 使用场景 + +名册里单独删掉一个人,其他人不动;同步删掉本团活跃子订单上此人的团期副本。本单在删除成功后写一行「移除团期人员」。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| productBatchId | Path | Long | ✅ | 雪花 ID | 产品侧排期 ID | +| staffId | Path | Long | ✅ | 须在本团 | 要删除的员工 ID | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| productBatchId / groupBatchId | String | 回显,不变 | +| removedStaffId / removedStaffRole | Long / String | 被删的人,不变 | +| primaryReporterRemoved | Boolean | 删的是否主报账人,不变 | +| affectedOrderCount | Integer | 同步删除副本的子订单数,不变 | +| staffList | Array | 删除后的名册,不变 | + +#### 请求示例 + +```http +DELETE /v3/admin/group-batch/2104527134162853890/staff/1007 +``` + +#### 响应示例 + +响应结构不变(TEST 原文节选,略去名册): + +```json +{ + "code": 200, + "message": "成功", + "data": { + "productBatchId": "2104527134162853890", + "groupBatchId": "2104527135173685249", + "removedStaffId": 1007, + "removedStaffRole": "LEADER", + "primaryReporterRemoved": true, + "affectedOrderCount": 1 + }, + "traceId": null, + "success": true +} +``` + +同时 GB-ADM-096 新增的一行(原文节选): + +```json +{ + "logId": "2104527571683274754", + "eventType": "BATCH_STAFF_REMOVE", + "eventTypeName": "移除团期人员", + "changeType": "DATA", + "content": "移除领队 白云飞(原主报账人,团期现无主报账人)", + "operatorType": "ADMIN", + "operatorId": "1001", + "extra": { + "staffId": "1007", + "staffName": "白云飞", + "staffRole": "LEADER", + "reporterRankBefore": "PRIMARY", + "primaryReporterBefore": { "staffId": "1007", "staffName": "白云飞" }, + "primaryReporterAfter": null + }, + "changedAt": "2026-09-28 19:04:08" +} +``` + +#### 空数据 / 降级响应 + +无空数据形态。删的不是报账人时 content 只有「移除导游 张三」,extra 不带报账人前后字段。 + +#### 错误响应 + +```json +{ "code": 589508, "message": "报账人不是该团期已派员工", "data": null, "traceId": null, "success": false } +``` + +```json +{ "code": 589598, "message": "团期已确认,配置不可修改", "data": null, "traceId": null, "success": false } +``` + +#### 业务边界 + +- 每次删除成功写一行。加锁读到的名册里已经没有此人(并发下刚被别人删掉)时不写。 +- 删除不在本团的人返回 589508,message 是错误码共用的「报账人不是该团期已派员工」,与改前相同。 +- 响应里的 `primaryReporterRemoved` 与时间线 extra 的 `primaryReporterAfter: null` 是同一件事的两处体现。 + +### 4. 团期单人更换 `PUT /v3/admin/group-batch/{productBatchId}/staff/{staffId}/replace` + +**VO**: `BatchStaffReplaceReqVO` → `Result` + +#### 使用场景 + +名册里把一个人换成另一个人;配置位、排序、备注、报账人等级、服务日期都转给新人。本单在更换成功后写一行「更换团期人员」。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| productBatchId | Path | Long | ✅ | 雪花 ID | 产品侧排期 ID | +| staffId | Path | Long | ✅ | 须在本团 | 被换下的员工 ID | +| newStaffId | Body | Long | ✅ | 须上架、类型符合原配置位、不在本团 | 换上的员工 ID | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| oldStaffId / newStaffId | Long | 换下 / 换上的人,不变 | +| staffRole | String | 新人落库角色,不变 | +| reporterRank | String | 新人继承的等级,不变 | +| primaryReporterTransferred | Boolean | 主报账人是否随之转给新人,不变 | +| affectedOrderCount / staffList | - | 不变 | + +#### 请求示例 + +`PUT /v3/admin/group-batch/2104527134162853890/staff/1008/replace`(萨仁高娃是主报账人): + +```json +{ "newStaffId": 1009 } +``` + +#### 响应示例 + +响应结构不变(TEST 原文节选,略去名册): + +```json +{ + "code": 200, + "message": "成功", + "data": { + "productBatchId": "2104527134162853890", + "groupBatchId": "2104527135173685249", + "oldStaffId": 1008, + "newStaffId": 1009, + "staffRole": "LEADER", + "reporterRank": "PRIMARY", + "primaryReporterTransferred": true, + "affectedOrderCount": 1 + }, + "traceId": null, + "success": true +} +``` + +同时 GB-ADM-096 新增的一行(原文节选): + +```json +{ + "logId": "2104527581611192321", + "eventType": "BATCH_STAFF_REPLACE", + "eventTypeName": "更换团期人员", + "changeType": "DATA", + "content": "领队 萨仁高娃 → 陈志远(主报账人随之转给陈志远)", + "operatorType": "ADMIN", + "operatorId": "1001", + "extra": { + "oldStaff": { "staffId": "1008", "staffName": "萨仁高娃", "staffRole": "LEADER", "reporterRank": "PRIMARY" }, + "newStaff": { "staffId": "1009", "staffName": "陈志远", "staffRole": "LEADER", "reporterRank": "PRIMARY" }, + "primaryReporterBefore": { "staffId": "1008", "staffName": "萨仁高娃" }, + "primaryReporterAfter": { "staffId": "1009", "staffName": "陈志远" } + }, + "changedAt": "2026-09-28 19:04:10" +} +``` + +#### 空数据 / 降级响应 + +无空数据形态。换下的不是报账人时 content 只有「导游 张三 → 李四」,extra 不带报账人前后字段。 + +#### 错误响应 + +```json +{ "code": 589508, "message": "报账人不是该团期已派员工", "data": null, "traceId": null, "success": false } +``` + +```json +{ "code": 589598, "message": "团期已确认,配置不可修改", "data": null, "traceId": null, "success": false } +``` + +100001 / 589582 / 582103 / 582118 / 582114 / 582119 / 589553 与改前相同。 + +#### 业务边界 + +- 更换一定改了名单,每次成功都写一行。 +- 次报账人同理:「领队 刘大山 → 乌日娜(次报账人随之转给乌日娜)」,extra 带 `secondaryReporterBefore` / `secondaryReporterAfter`。 +- 落库角色变了时两边都带角色,如「领队 李四 → 导游 巴特尔」。 + +### 5. 取消成团 `POST /v3/admin/order/group-batch/{groupBatchId}/cancel-group` + +**VO**: `Result`(无请求体) + +#### 使用场景 + +团期从「资源准备中」退回「招募中」,同时回收整团的导游 / 摄影配置。本单在回收之后单独写一行「取消成团·回收人员」,原来那行「取消成团」不动。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | 雪花 ID | 团期主订单 ID(不是 productBatchId) | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | null | 不变 | + +#### 请求示例 + +```http +POST /v3/admin/order/group-batch/2104527135173685249/cancel-group +``` + +#### 响应示例 + +```json +{ "code": 200, "message": "成功", "data": null, "traceId": null, "success": true } +``` + +同时 GB-ADM-096 新增的两行(原文节选,同一秒,按 logId 排序): + +```json +[ + { + "logId": "2104528117127360513", + "changeType": "STATUS", + "eventType": "BATCH_CANCEL_GROUP", + "eventTypeName": "取消成团", + "fromStatus": "RESOURCE_PREPARING", + "toStatus": "RECRUITING", + "content": "取消成团", + "extra": null, + "changedAt": "2026-09-28 19:06:18" + }, + { + "logId": "2104528117160914946", + "changeType": "DATA", + "eventType": "BATCH_STAFF_RECYCLED", + "eventTypeName": "取消成团·回收人员", + "fromStatus": "RECRUITING", + "toStatus": "RECRUITING", + "content": "取消成团,回收团期人员 2 人:王强、乌日娜(主报账人)", + "operatorType": "ADMIN", + "operatorId": "1001", + "extra": { + "recycledCount": 2, + "staff": [ + { "staffId": "1003", "staffName": "王强", "staffRole": "PHOTOGRAPHER", "reporterRank": "NONE" }, + { "staffId": "1010", "staffName": "乌日娜", "staffRole": "LEADER", "reporterRank": "PRIMARY" } + ], + "primaryReporterBefore": { "staffId": "1010", "staffName": "乌日娜" }, + "primaryReporterAfter": null + }, + "changedAt": "2026-09-28 19:06:18" + } +] +``` + +#### 空数据 / 降级响应 + +团期没配人员时只写「取消成团」一行,不写回收行(TEST 实测团期 Z:2→3,只多 `BATCH_CANCEL_GROUP`)。 + +#### 错误响应 + +与改前相同(下为源码 `GroupBatchErrorCode` 定义的文案,本轮验收没有触发): + +```json +{ "code": 589502, "message": "取消成团被阻塞(有已确认子订单或已派单资源)", "data": null } +``` + +```json +{ "code": 589501, "message": "团期状态不允许当前操作", "data": null } +``` + +#### 业务边界 + +- 回收行的 `fromStatus` / `toStatus` 都是 `RECRUITING`(取消成团之后的状态)。它与「取消成团」通常在同一秒,靠 `logId` 稳定排在后面。 +- content:「取消成团,回收团期人员 N 人:张三(主报账人)、李四、王五(次报账人)」;回收的人里没有主报账人时末尾加「;其中无主报账人」。 +- 回收名单在同一事务内、软删之前加锁读取。回收或留痕任何一步失败,取消成团整体回滚。 + +### 6. 团期状态流水 `GET /v3/admin/order/group-batch/{groupBatchId}/status-logs` + +**VO**: `GroupBatchStatusLogItemVO`(返回 `Result>`,无请求体) + +#### 使用场景 + +团期详情的时间线(GB-ADM-096)。结构不变;`eventType` 多了五个取值;同一秒内的行改为按 `logId` 升序。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | 雪花 ID | 团期主订单 ID | + +#### 出参 `Result>` + +| 字段 | 类型 | 说明 | +|------|------|------| +| logId | String | 流水 ID;同秒排序的第二键 | +| eventType | String | 新增五个值,见六.5 | +| eventTypeName | String | 对应中文名,后端直供 | +| changeType | String | 五个新事件都是 `DATA` | +| fromStatus / toStatus | String | DATA 类两者相同,取写入时的团期状态 | +| content | String | 展示文本,前端直接渲染 | +| extra | Object | 结构化快照,按事件类型不同,见六.5 | +| operatorType / operatorId / operatorName | String | 五个新事件都是 `ADMIN` + 管理员 ID(字符串)+ 管理员名字 | +| 其余字段 | - | groupBatchId / fromStatusName / toStatusName / reason / changedAt 等,不变 | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/2104527135173685249/status-logs +``` + +#### 响应示例 + +TEST 原文节选(团期 X,排序修复后)。前两行同为 19:02:24,按 logId 升序: + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { + "logId": "2104527135194656770", + "changeType": "STATUS", + "eventType": "BATCH_CREATE", + "eventTypeName": "建团", + "content": "建团:2月18日海拉尔-呼伦湖2日团", + "changedAt": "2026-09-28 19:02:24" + }, + { + "logId": "2104527136431976449", + "changeType": "STATUS", + "eventType": "BATCH_GROUP", + "eventTypeName": "成团", + "content": "手动成团 · 已售 1/15 户 · 理由:客户已全款,手动成团(#8482 验收造数)", + "changedAt": "2026-09-28 19:02:24" + }, + { + "logId": "2104527342187737089", + "groupBatchId": "2104527135173685249", + "changeType": "DATA", + "eventType": "BATCH_REPORTER_CHANGE", + "eventTypeName": "报账人变更", + "fromStatus": "RESOURCE_PREPARING", + "fromStatusName": "资源准备中", + "toStatus": "RESOURCE_PREPARING", + "toStatusName": "资源准备中", + "content": "主报账人:无 → 李雪梅", + "reason": null, + "operatorType": "ADMIN", + "operatorId": "1001", + "operatorName": "admin", + "extra": { + "staffId": "1002", + "staffName": "李雪梅", + "staffRole": "GUIDE", + "reporterRankBefore": "NONE", + "reporterRankAfter": "PRIMARY", + "primaryReporterBefore": null, + "primaryReporterAfter": { "staffId": "1002", "staffName": "李雪梅" } + }, + "changedAt": "2026-09-28 19:03:13" + } + ], + "traceId": null, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无流水时 `data=[]`,与改前相同。 + +#### 错误响应 + +与改前相同(源码 `GroupBatchErrorCode` 文案): + +```json +{ "code": 589507, "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)", "data": null } +``` + +#### 业务边界 + +- 排序:`changedAt` 升序,同一秒内 `logId` 升序(#8492)。TEST 同一团期修复前连读 5 次,都是「成团」排在「建团」之前;修复后连读 5 次,两组同秒行(建团 / 成团、取消成团 / 回收)全部按 logId 升序。 +- 历史不回补:本单合入之前的人员变更查不回来。 +- 库里认不出的事件码,`eventTypeName` 为 null,与改前相同。 + +--- + +## 四、契约约束与正确调用方式 + +> 本节只写**后端接受/拒绝的规则**和前端必须跟着做的事。 + +### ✅ 正确 / ❌ 错误用法对照 + +| 场景 | 做法 | +|------|------| +| ✅ 时间线展示新事件 | 渲染 `eventTypeName`,取不到回落 `eventType`;正文直接渲染 `content` | +| ✅ 读 extra 里的报账人前后字段 | 缺键按 null 处理;要判断「这一行有没有改主报账人」,看 extra 里有没有 `primaryReporterAfter` 这个键 | +| ❌ 把「缺键」当成「团期没有主报账人」 | 缺键只表示这一行没改该项归属;键在且值为 null 才表示那一端没有报账人 | +| ❌ 把 extra 里的 staffId 转成数字 | extra 里的 ID 一律是字符串,按字符串比较 | +| ❌ 用 productBatchId 调时间线或取消成团 | 人员四个接口的路径参数是 `productBatchId`,时间线与取消成团是 `groupBatchId`,两者不能混用 | +| ❌ 前端按 changedAt 重排时间线 | changedAt 只到秒,重排会打乱同秒行的先后;按接口返回顺序展示 | + +### 前端交接清单 + +**结论:无需改动。** + +1. 团期时间线组件 `StatusLogsPanel` 渲染的是 `eventTypeName || eventType || '事件'`,中文名由后端直供,前端没有自建事件码映射表(#7987 时 mmg 实证;2026-09-28 复核 hl-ui `origin/v2.1@ce469f78` 未变)。五个新事件码不改代码就显示中文名。 +2. `content` 由后端拼好,面板直接渲染;新事件的说明文字都在 content 里。 +3. 面板对 extra 只挑 `orderNo` / `schemeName` / `kind` / `orderId` 几个键,缺键兜住。新事件的 extra 不含这些键,不会多渲染文字,也不会报错。 +4. 面板按接口返回顺序展示,没有在前端重排,同秒按 logId 排序的修复不用前端配合。 +5. 如果要在页面上用 extra(例如单独展示主报账人更替历史):报账人前后四个字段只在对应归属变了时出现,**缺键按 null 处理**;ID 都是字符串。 +6. 以上结论的前提是后端继续直供 `eventTypeName`。 + +--- + +## 五、数据库行为 + +| 情形 | order_batch_staff | group_batch_status_log | +|------|-------------------|------------------------| +| 保存配置:名单或报账人归属有变化 | 写入(与改前相同) | +1 `BATCH_STAFF_CONFIG_SAVE` | +| 保存配置:原样、只改排序 / 备注 / 服务日期 / 日薪 | 写入(与改前相同) | 不写 | +| 设报账人:归属变了 | 更新(与改前相同) | +1 `BATCH_REPORTER_CHANGE` | +| 设报账人:设成已有等级 | 照常执行 | 不写 | +| 单人删除 / 单人更换成功 | 写入(与改前相同) | +1 `BATCH_STAFF_REMOVE` / `BATCH_STAFF_REPLACE` | +| 取消成团,团期配了人员 | 整团软删(与改前相同) | 「取消成团」之后 +1 `BATCH_STAFF_RECYCLED` | +| 取消成团,团期没配人员 | 不动 | 只有「取消成团」一行 | +| 被拒(589508 / 589598 / 582115 等) | 不动 | 不写 | +| 留痕写失败 | 回滚 | 不写(接口 500) | + +- **零 DDL**,不需要 Flyway:只往 `group_batch_status_log` 追加行,新事件码是 `event_type` 列的新取值。 +- 写之前先对 `order_batch_staff` 做锁定读(`SELECT ... FOR UPDATE`)取前态:保存配置锁本次覆盖范围内的行,设报账人 / 单人删除 / 单人更换 / 取消成团回收锁整团行。时间线记下的前任不会是过期值。 +- 留痕与业务写入在同一事务:业务回滚,留痕跟着回滚;留痕写失败,业务也回滚。 +- 子订单上的团期人员副本照旧扇出,副本的变化不进订单时间线。 + +--- + +## 六、边界行为 + +- 未登录 → 网关返回 `code=401`(HTTP 200)。 +- 无团期管理权限 → 589507,与改前相同。 +- 操作人:五种新事件都由后台请求触发,`operatorType=ADMIN`,`operatorId` 是管理员 ID(字符串),`operatorName` 是管理员名字。TEST 新增的 17 行全部是 `ADMIN` / `"1001"` / `admin`。 +- 手机号:content 和 extra 都不带手机号,明文、库列值都不带。 +- 团期当前状态是库里认不出的历史值时,这一行的 `fromStatus` / `toStatus` 为 null,业务照常写入。 +- 订单自派人员(`source=ORDER`)的变更不在本单范围。 + +--- + +## 六.5、枚举 / 数据字典 + +### eventType 新增取值(`GroupBatchLogEventType`) + +| 值 | 中文名(eventTypeName) | changeType | 写入接口 | 典型 content(TEST 原文) | +|---|---|---|---|---| +| `BATCH_STAFF_CONFIG_SAVE` | 调整团期人员 | DATA | 保存团期人员配置 | 「导游位:+萨仁高娃;−李雪梅」「导游位:−陈志远(原主报账人,团期现无主报账人)」 | +| `BATCH_REPORTER_CHANGE` | 报账人变更 | DATA | 设置报账人等级 | 「主报账人:李雪梅 → 刘大山」「次报账人:无 → 刘大山」「主报账人:无 → 乌日娜;次报账人:乌日娜 → 无」 | +| `BATCH_STAFF_REMOVE` | 移除团期人员 | DATA | 团期单人删除 | 「移除领队 白云飞(原主报账人,团期现无主报账人)」 | +| `BATCH_STAFF_REPLACE` | 更换团期人员 | DATA | 团期单人更换 | 「领队 萨仁高娃 → 陈志远(主报账人随之转给陈志远)」「领队 刘大山 → 乌日娜(次报账人随之转给乌日娜)」 | +| `BATCH_STAFF_RECYCLED` | 取消成团·回收人员 | DATA | 取消成团 | 「取消成团,回收团期人员 2 人:王强、乌日娜(主报账人)」 | + +content 里的角色中文名与名册 `staffRoleName` 同源(导游 / 领队 / 摄影等);报账人等级中文名为「主报账人」「次报账人」「非报账人」。 + +### 公共:报账人前后字段(五种事件都可能带) + +| 字段 | 类型 | 说明 | +|---|---|---| +| primaryReporterBefore | Object 或 null | 主报账人归属变了才有这个键;值为 `{staffId, staffName}`(staffId 为字符串),那一端没有主报账人时为 null | +| primaryReporterAfter | Object 或 null | 同上,变更后 | +| secondaryReporterBefore | Object 或 null | 次报账人归属变了才有这个键;形态同上 | +| secondaryReporterAfter | Object 或 null | 同上,变更后 | + +### BATCH_STAFF_CONFIG_SAVE 的 extra + +| 字段 | 类型 | 说明 | +|---|---|---| +| scopeRoles | String[] 或 null | 本次覆盖范围;null 表示整期全量保存 | +| added | Array(恒在,可为空) | 新增的人:staffId(String)/ staffName / staffRole | +| removed | Array(恒在,可为空) | 移出的人:staffId(String)/ staffName / staffRole / reporterRank(移出前等级) | +| roleChanged | Array(恒在,可为空) | 落库角色变了的人:staffId(String)/ staffName / staffRoleBefore / staffRoleAfter(一人多角色时用「/」连接) | + +### BATCH_REPORTER_CHANGE 的 extra + +| 字段 | 类型 | 说明 | +|---|---|---| +| staffId | String | 被设等级的人 | +| staffName | String | 姓名快照 | +| staffRole | String | 落库角色 | +| reporterRankBefore | String | 此人原等级:PRIMARY / SECONDARY / NONE | +| reporterRankAfter | String | 此人新等级 | + +被自动降级的原主(次)报账人体现在报账人前后字段的 Before 里。 + +### BATCH_STAFF_REMOVE 的 extra + +| 字段 | 类型 | 说明 | +|---|---|---| +| staffId | String | 被删的人 | +| staffName | String | 姓名快照 | +| staffRole | String | 落库角色 | +| reporterRankBefore | String | 删除前等级 | + +### BATCH_STAFF_REPLACE 的 extra + +| 字段 | 类型 | 说明 | +|---|---|---| +| oldStaff | Object | 换下的人:staffId(String)/ staffName / staffRole / reporterRank(换下前的等级) | +| newStaff | Object | 换上的人:staffId(String)/ staffName / staffRole / reporterRank(继承到的等级) | + +### BATCH_STAFF_RECYCLED 的 extra + +| 字段 | 类型 | 说明 | +|---|---|---| +| recycledCount | Integer | 回收人数(按人计) | +| staff | Array | 每人一项:staffId(String)/ staffName / staffRole / reporterRank | + +回收到主(次)报账人时带报账人前后字段,After 恒为 null。 + +--- + +## 六.6、修改前后对比 + +### 字段级对比 + +| 字段 | 改前 | 改后 | +|------|------|------| +| 六个接口的请求 / 响应字段 | - | 不变 | +| GB-ADM-096 `eventType` 取值 | 没有人员类事件 | 新增五个 | +| GB-ADM-096 同秒行顺序 | 不稳定 | `logId` 升序 | + +### 行为级对比 + +| 行为 | 改前 | 改后 | +|------|------|------| +| 保存配置有增删、角色变化或报账人归属变化 | 时间线无记录 | +1 `BATCH_STAFF_CONFIG_SAVE` | +| 保存配置原样、只改排序或备注 | 时间线无记录 | 仍无记录 | +| 设报账人 | 时间线无记录 | +1 `BATCH_REPORTER_CHANGE`,被降级的原报账人写在同一行 | +| 单人删除 / 单人更换 | 时间线无记录 | +1 `BATCH_STAFF_REMOVE` / `BATCH_STAFF_REPLACE` | +| 取消成团回收人员 | 只有「取消成团」一行 | 「取消成团」之后 +1 回收行 | +| 同秒行顺序 | TEST 连读 5 次都是「成团」排在「建团」前 | 连读 5 次都按 logId 升序 | + +## 六.7、影响评估 + +- **是否破坏向后兼容**: 否(结构不变;新增的是 `eventType` 取值,前端按 `eventTypeName` 渲染) +- **前端是否必须同步上线**: 否(前端零改动) +- **前端 workaround 清理点**: 无 + +--- + +## 七、不影响范围 + +- **仅影响**: 五个写接口成功后的时间线副作用;GB-ADM-096 的取值域和同秒顺序 +- **零影响**: + - 六个接口的请求 / 响应结构和错误码 + - 保存配置时保留人员沿用原报账人等级和备注:这一行为随 #8468 发布(见 `28_8468` 交接件),本篇不重复 + - 名册、候选列表、扇出到子订单的逻辑 + - 订单级时间线 + - 订单自派人员(`source=ORDER`) + - 本单合入之前的历史变更(不回补) + +--- + +## 八、测试环境已验证 + +部署:主轮(19:02–19:09)跑在 hl-order-service-v3 dev-v3 @ `e24be7af8`(#8490,18:53:31 部署),两实例经运行字节探针确认加载的是本次代码(`BATCH_STAFF_CONFIG_SAVE`、「取消成团·回收人员」命中,进程 jar 与磁盘 jar 同 inode)。排序复验(19:28)跑在 dev-v3 @ `75e182710`(#8492)。 + +经网关 `api.test.1814.love` 用 admin token(operatorId 1001)实测。团期 X:groupBatchId `2104527135173685249`(productBatchId `2104527134162853890`,一户全款、手动成团);团期 Z:groupBatchId `2104528158369951745`(productBatchId `2104528157774356482`)。每步前后各读一次 GB-ADM-096 整段原文比对行数。 + +``` +AC-B1 19:03:13~16 X 依次设主报账人 李雪梅 → 刘大山 → 白云飞 + → GB-ADM-096 4→7,新增「主报账人:无 → 李雪梅」「主报账人:李雪梅 → 刘大山」「主报账人:刘大山 → 白云飞」, + operatorType=ADMIN、operatorId=1001;同一次读取里 BATCH_CREATE / BATCH_GROUP 为阳性对照 ✓ +AC-B3 19:03:16 X 对白云飞再设 PRIMARY(已有等级) → 200,7→7 ✓ +AC-A2 19:03:31 X 设次报账人 刘大山 → 7→8「次报账人:无 → 刘大山」✓ +AC-A1 19:03:36 X 按前端形态原样保存导游位(只带 staffId/staffRole/sortOrder) + AC-A2 → 200,8→8;团级行重新插入(白云飞 batch_staff_id 2104527299623956483 → 2104527440154095618)仍为 PRIMARY, + AC-B3 刘大山仍为 SECONDARY;子订单 2104527134926221314 的 GROUP_BATCH 副本同为 PRIMARY / SECONDARY,update_time 19:03:37 ✓ +AC-B3 19:03:54 X 只改排序 → 8→8;19:03:56 只改备注 → 8→8 ✓ +AC-B3 19:03:57 X 导游位加萨仁高娃、移出李雪梅 → 8→9「导游位:+萨仁高娃;−李雪梅」✓ +AC-B2 19:04:07 X 单人删除主报账人白云飞 → 9→10「移除领队 白云飞(原主报账人,团期现无主报账人)」, + 响应 primaryReporterRemoved=true,extra.primaryReporterAfter=null ✓ + 19:04:09 X 设主报账人 萨仁高娃 → 10→11「主报账人:无 → 萨仁高娃」 +AC-B2 19:04:10 X 单人更换主报账人 萨仁高娃 → 陈志远 → 11→12「领队 萨仁高娃 → 陈志远(主报账人随之转给陈志远)」✓ +AC-B2 19:04:11 X 单人更换次报账人 刘大山 → 乌日娜 → 12→13「领队 刘大山 → 乌日娜(次报账人随之转给乌日娜)」✓ +AC-A3 19:04:23 X 保存导游位时移出主报账人陈志远 → 13→14「导游位:−陈志远(原主报账人,团期现无主报账人)」, + extra.primaryReporterAfter=null,导游位只剩乌日娜(SECONDARY)✓ +AC-B5 19:04:24~26 X 对已不在本团的人 设报账人 / 删除 / 更换 → 三个都是 589508「报账人不是该团期已派员工」,14→14,名册不变 ✓ +AC-B6 19:05:28 X 给 group_batch_status_log 临时加 CHECK 约束(只拦本团新的 BATCH_REPORTER_CHANGE,约 1.4 秒)后设主报账人乌日娜 + → 500「系统繁忙,请稍后重试或联系客服」;服务端日志:order_batch_staff、order_staff_assignment 两条 UPDATE 已执行, + 随后 INSERT 时间线触发 3819;回滚后团级行与子订单副本的乌日娜仍为 SECONDARY,时间线 14→14; + 约束删除后 SHOW CREATE TABLE 与注入前逐字一致 ✓ + 19:06:04 X 约束删除后再设一次 → 14→15「主报账人:无 → 乌日娜;次报账人:乌日娜 → 无」✓ +AC-B4 19:06:18 X 取消成团(已配人员) → 15→17,BATCH_CANCEL_GROUP「取消成团」之后 + BATCH_STAFF_RECYCLED「取消成团,回收团期人员 2 人:王强、乌日娜(主报账人)」✓ +AC-B4 19:06:28 Z 取消成团(未配人员) → 2→3,只多 BATCH_CANCEL_GROUP,无回收行 ✓ +AC-B5 19:07:23~27 Z 团期确认后(MATERIAL_PREPARING)保存 / 设报账人 / 删除 / 更换 + → 四个都是 589598「团期已确认,配置不可修改」,14→14,名册不变 ✓ +AC-B7 19:08:21 X、Z 两团全部 31 行(新事件 17 行):11 个明文手机号逐个查、7 个库列值逐个查、掩码 **** 与 phone 键名,全部 0 命中; + 带数字边界的 11 位手机号正则 0 命中(19:44 复查);不带边界时命中的 3 行都是既有事件 extra 里雪花 ID 的片段,新事件 0 命中 ✓ +排序 19:21:19~23 排序修复部署前,X 连读 5 次:BATCH_GROUP 都排在 BATCH_CREATE 前(同为 19:02:24) + 19:28:36~40 dev-v3 75e182710,X 连读 5 次:两组同秒行(建团 / 成团、取消成团 / 回收)全部 logId 升序 ✓ +``` + +所有新增行 `operatorType=ADMIN`、`operatorId="1001"`、`operatorName=admin`、`changedAt` 有值,extra 里的 ID 全部是字符串。 + +单元层:五处留痕调用逐一去掉,对应用例各自转红(共 12 例);#8492 的排序键去掉后 `GroupBatchStatusLogMapperOrderTest` 转红。 + +--- + +## 九、相关历史 PR + +- #3676:引入主 / 次报账人等级 +- #7304:GB-ADM-096 团期状态流水只读端点 +- #7987:时间线新事件由后端直供 `eventTypeName`,前端零改动的先例 +- #8231:取消成团回收导游 / 摄影配置 +- #8354:团期单人删除、单人更换 +- #8468:保存配置时保留人员沿用原报账人等级和备注(PR #8475) +- #8481:团期物资留痕(同日,同一张时间线) + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#8482](https://git.1814.love/wx/HL/issues/8482) +- 关联 PR: [wx/HL#8490](https://git.1814.love/wx/HL/pulls/8490)、[wx/HL#8492](https://git.1814.love/wx/HL/pulls/8492) +- 同日交接件:`changelogs-v2/2026-09/28_8468_团期导领摄配置加服务日期与基础日薪并在选人列表显示占用-修改接口-管理后台.md`、`changelogs-v2/2026-09/28_8481_团期物资确认后改清单自动失效并补全物资留痕-修改接口-管理后台.md` + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#8482](https://git.1814.love/wx/HL/issues/8482) +- **PR**: [#8490](https://git.1814.love/wx/HL/pulls/8490)、[#8492](https://git.1814.love/wx/HL/pulls/8492) +- **Merge commit**: [e24be7af8](https://git.1814.love/wx/HL/commit/e24be7af8)、[75e182710](https://git.1814.love/wx/HL/commit/75e182710) + +### 联系人 + +- **后端负责人**: @jw