From b22e67ca6969c6faca0afb19a8745ee5c50c00b9 Mon Sep 17 00:00:00 2001 From: jw Date: Wed, 9 Sep 2026 13:44:19 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20#7304=20=E5=9B=A2=E6=9C=9F?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=BA=BF=E5=8F=AA=E8=AF=BB=E7=AB=AF=E7=82=B9?= =?UTF-8?q?=20GB-ADM-096=EF=BC=88=E6=96=B0=E5=A2=9E=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E9=9D=A2=E5=90=91=20mmg=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit group_batch_status_log 此前只写不可读:十余条链路持续写入,全仓零生产读回方, 运营在页面上一条也看不到——#7283 那条「同步失败靠时间线让运营看见并手动去产品 后台取消班期」的唯一可见兜底入口因此实际不可见。 新增 GET /v3/admin/order/group-batch/:groupBatchId/status-logs:按 changedAt 升序 全量返回,extra 由 JSON 字符串解析成结构化对象,事件类型与前后状态的中文名由后端 给出(前端不必自建映射表),权限同码 group-batch:view。 backend_status=deployed / gateway_status=verified:已部署 TEST 并经真实网关逐条 取证 AC-1~AC-6,含读回 #7283 的 BATCH_PRODUCT_CANCEL_FAILED 兜底留痕。 frontend_status=pending(mmg 待接团期详情的「操作记录/时间线」区块)。 Refs HL#7304 (PR #7359, 合并提交 0b85f6831) Co-Authored-By: Claude Opus 5 (1M context) --- ...¶间线只读端点status-logs-新增接口-管理后台.md | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 changelogs-v2/2026-09/09_7304_团期时间线只读端点status-logs-新增接口-管理后台.md diff --git a/changelogs-v2/2026-09/09_7304_团期时间线只读端点status-logs-新增接口-管理后台.md b/changelogs-v2/2026-09/09_7304_团期时间线只读端点status-logs-新增接口-管理后台.md new file mode 100644 index 00000000..456b2dd9 --- /dev/null +++ b/changelogs-v2/2026-09/09_7304_团期时间线只读端点status-logs-新增接口-管理后台.md @@ -0,0 +1,211 @@ +--- +schema: "hl-changelog/v2" +ticket: "7304" +title: "新增团期状态流水(时间线)只读端点 GB-ADM-096:GET /v3/admin/order/group-batch/:groupBatchId/status-logs" +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: "新增一个只读端点,把此前只写不可读的 group_batch_status_log 暴露给管理端。后端已部署 TEST 并经真实网关实测 AC-1~AC-6(含读回 #7283 的 BATCH_PRODUCT_CANCEL_FAILED 兜底留痕)。前端待接:团期详情加一个「操作记录/时间线」区块——STATUS 类画 fromStatusName→toStatusName 箭头,DATA 类只渲染 content;中文名一律用后端给的 eventTypeName/fromStatusName/toStatusName,不要自建映射表(枚举会继续新增);operatorType 可能是 SYSTEM/MQ,此时 operatorId 为 null、operatorName 是「系统」之类固定文案。权限与团期看板/芯片同码 group-batch:view。" +updated_at: "2026-09-09" +base: "dev-v3" +--- + +# 团期状态流水(时间线)只读端点 + +## 一、给前端的一句话 + +团期的「操作记录 / 时间线」现在有接口了。**一次 GET 拿到该团期从建团到当前的全部事件**,按时间升序,中文名后端已经拼好,前端直接渲染即可。这是个纯新增端点,不动任何既有接口。 + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|---|---|---|---|---| +| 1 | 团期状态流水(时间线) | GET | `/v3/admin/order/group-batch/:groupBatchId/status-logs` | 新增 | 一次返回该团期全部状态流水,按时间升序;此前该表只写不可读 | + +## 三、接口详情 + +### 1. 团期状态流水(时间线) `GET /v3/admin/order/group-batch/:groupBatchId/status-logs` + +**VO**: `GroupBatchStatusLogItemVO` + +#### 使用场景 + +团期详情页的「操作记录 / 时间线」区块。此前这张表只写不读——建团、成团、取消成团、流团、退单审批、转期、导出、合同保险十余条链路都往里写,但页面上一条也看不到。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|---|---|---|---|---|---| +| `groupBatchId` | path | Long | 是 | 雪花 ID,按字符串传 | 团期 ID | + +无 query、无 body。**不分页**——单团期的流水条数由其生命周期事件数决定(量级几十条),不是无界列表。 + +#### 出参 + +`Result>`,按 `changedAt` **升序**。 + +| 字段 | 类型 | 说明 | +|---|---|---| +| `logId` | String | 流水 ID(雪花) | +| `groupBatchId` | String | 团期 ID(雪花) | +| `changeType` | String | `STATUS`=状态流转 / `DATA`=状态内数据变更 | +| `eventType` | String | 事件类型值,如 `BATCH_GROUP` | +| `eventTypeName` | String | 事件类型中文名,如「成团」;枚举外的历史值为 `null` | +| `fromStatus` | String | 变更前团期状态;首次建团为 `null` | +| `fromStatusName` | String | 变更前状态中文名,如「招募中」 | +| `toStatus` | String | 变更后团期状态 | +| `toStatusName` | String | 变更后状态中文名,如「资源准备中」 | +| `content` | String | 展示文本,后端已拼好,前端直接渲染 | +| `reason` | String | 理由 / 备注,可为 `null` | +| `operatorType` | String | `USER` / `ADMIN` / `SYSTEM` / `MQ` | +| `operatorId` | String | 操作人 ID(雪花);`SYSTEM` / `MQ` 场景为 `null` | +| `operatorName` | String | 操作人姓名快照;系统场景为「系统」之类固定文案 | +| `extra` | Object | 附加快照,**已解析成对象**(不是转义 JSON 字符串);无附加数据时为 `null` | +| `changedAt` | String | `yyyy-MM-dd HH:mm:ss` | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/2097500233511362561/status-logs +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { + "logId": "2097500233519751171", + "groupBatchId": "2097500233511362561", + "changeType": "STATUS", + "eventType": "BATCH_CREATE", + "eventTypeName": "建团", + "fromStatus": null, + "fromStatusName": null, + "toStatus": "RECRUITING", + "toStatusName": "招募中", + "content": "建团:秋日川西小环线", + "reason": null, + "operatorType": "ADMIN", + "operatorId": "1001", + "operatorName": "admin", + "extra": null, + "changedAt": "2026-09-09 09:40:00" + }, + { + "logId": "2097501083860418561", + "groupBatchId": "2097500233511362561", + "changeType": "DATA", + "eventType": "BATCH_CONTRACT_ISSUE", + "eventTypeName": "手动开合同/保险", + "fromStatus": "RESOURCE_PREPARING", + "fromStatusName": "资源准备中", + "toStatus": "RESOURCE_PREPARING", + "toStatusName": "资源准备中", + "content": "手动开合同 · 张三(HL20260909094000073)", + "reason": null, + "operatorType": "ADMIN", + "operatorId": "1001", + "operatorName": "admin", + "extra": { + "orderId": "2097500234186702849", + "orderNo": "HL20260909094000073", + "kind": "合同", + "schemeName": "标准国内电子签约方案" + }, + "changedAt": "2026-09-09 09:43:23" + } + ] +} +``` + +#### 空数据 / 降级响应 + +团期不存在、或该团期尚无任何流水,一律返回空列表,**不报错**: + +```json +{ "code": 200, "message": "成功", "data": [] } +``` + +> 注意这一点与团期详情不同:`GET /v3/admin/order/group-batch/:groupBatchId` 对不存在的团期抛 `589500`,本端点回空数组。原因是这张表是 append-only 存证表,「查不到」在读语义上就等于「空时间线」。 + +单条记录的降级:`extra` 为空 / 空串 / 非法 JSON 时该条 `extra` 回 `null`,其余字段正常;事件类型或状态是枚举外的历史值时对应 `*Name` 回 `null`、code 原样返回。**一条脏数据不会让整条时间线查不出来。** + +#### 错误响应 + +当前角色没有 `group-batch:view` 权限(失败关闭,与团期看板 / 芯片明细同码): + +```json +{ "code": 589507, "message": "无操作权限(非团期管理员 / 非本定制师名下)", "data": null, "success": false } +``` + +缺少或无效的 token 由网关拦截,返回 `401`。本端点不产生其它业务错误码。 + +#### 业务边界 + +- 只读端点,不改任何数据。 +- 返回全量,不分页、不截断。 +- 中文名由后端给:`eventTypeName` / `fromStatusName` / `toStatusName`;枚举外的历史值为 `null`。 +- 团期不存在与无流水不区分,都回空数组。 +- 权限码 `group-batch:view`,与团期看板、芯片逐户明细一致。 + +## 四、契约约束与正确调用方式 + +1. **中文名一律用后端返回的字段渲染,不要在前端自建 code→中文 映射表。** 团期事件类型会持续新增(#7283 刚加过 `BATCH_PRODUCT_CANCEL_FAILED`),后端给名字前端就零改动;自建映射表会漏。 +2. **按 `changeType` 分叉渲染**:`STATUS` 画 `fromStatusName → toStatusName` 箭头;`DATA` 的 `fromStatus === toStatus`,只渲染 `content`。 +3. **`operatorType` 可能是 `SYSTEM` / `MQ`**(AFTER_COMMIT 或异步链路写入),此时 `operatorId` 为 `null`,`operatorName` 是「系统」之类的固定文案,照常渲染即可,别按「一定有操作人」写死。 +4. **`extra` 是对象或 `null`**,不是字符串,不要再 `JSON.parse`。不同事件的 `extra` 结构不同,按需取键、缺键要兜住。 +5. **雪花 ID 一律按字符串处理**(`logId` / `groupBatchId` / `operatorId` 都以字符串下发)。 +6. 名字字段可能为 `null`(库里存的是当前枚举外的历史值),此时前端兜底显示对应的 code。 + +## 六、边界行为 + +| 场景 | 行为 | +|---|---| +| 团期不存在 | `code=200`,`data=[]` | +| 团期存在但无流水 | `code=200`,`data=[]` | +| `extra` 为空 / 空串 / 非法 JSON | 该条 `extra` 回 `null`,其余字段正常,不影响整条时间线 | +| 事件类型 / 状态是枚举外的历史值 | 对应 `*Name` 回 `null`,`eventType` / `fromStatus` / `toStatus` 原样返回 | +| 无 `group-batch:view` 权限 | `589507` | + +## 七、不影响范围 + +- 不改任何既有接口的路径、入参、出参、错误码。 +- 不改团期状态流水的**写入**侧:十余条写入链路一行未动。 +- 无表变更、无 Flyway、无数据迁移。 +- 网关路由不新增(`/v3/admin/**` 已覆盖)。 +- 只滚 `hl-order-service-v3` 一个服务。 + +## 八、测试环境已验证 + +TEST(`api.test.1814.love`)真实网关逐条取证: + +| 验收项 | 实测 | +|---|---| +| 全量返回、按 `changedAt` 升序、条数与 `SELECT COUNT(*)` 一致 | ✅ | +| `STATUS` 与 `DATA` 两类各有实例,`DATA` 类 `fromStatus == toStatus` | ✅ | +| 能读回 #7283 的 `BATCH_PRODUCT_CANCEL_FAILED`,content 为「班期置不可售失败,请到产品后台手动取消该班期(班期 ID:…)」 | ✅ 本端点存在的直接理由 | +| `extra` 为结构化对象;空值 / 脏值回 `null` 不报错 | ✅ | +| 团期不存在 → `code=200, data=[]`(对照:团期详情同 id 抛 589500) | ✅ | +| `CUSTOMIZER` 角色 → `589507`,与 `chips/contract` 逐角色一致 | ✅ | + +## 十、相关文档 + +- 实施单编号:GB-ADM-096(新增) +- 前序 #7283:本端点是它「同步失败只记时间线让运营看见」那条兜底的读回出口,解开其 AC-9 的阻塞 + +## 关联 / 联系人 + +- 工单:https://git.1814.love:8443/wx/HL/issues/7304 +- PR:https://git.1814.love:8443/wx/HL/pulls/7359 +- 后端:jw;前端:mmg