changelog-filename-gate / validate (push) Failing after 1s
正文第 38 行说「前端必须新增这两个 event_type 的渲染分支」,而 mmg 在 frontmatter 里已用实证翻成 not_required(StatusLogsPanel 渲染 eventTypeName || eventType || '事件',后端直供中文名+原码兜底,前端无自建 映射表,零改动)。订正了状态位却没回头改正文,那句话会让下一个读的人以为 前端还有活。原句划掉保留接 grep,并注明这条结论依赖「后端继续直供 eventTypeName」——该字段哪天返 null,渲染缺口会重新成立。 frontend_owner/frontend_ref 清空:交付指南写死 not_required 条目任何人 (含前端)不得回写这 4 个认领字段,填了会被 E_FRONTEND 残留校验拦下 (2026-08-19 #6077 先例)。本文件能推上去是因为门禁只扫新增路径, 不是因为合规。mmg 的实证本身原样保留。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
499 行
25 KiB
Markdown
499 行
25 KiB
Markdown
---
|
||
schema: "hl-changelog/v2"
|
||
ticket: "7987"
|
||
title: "团期时间线新增开窗与重配两类事件,事件类型枚举扩展"
|
||
consumer: "admin"
|
||
author: "wx(GIT)"
|
||
change_type: "修改接口"
|
||
backend_status: "deployed"
|
||
gateway_status: "verified"
|
||
frontend_status: "not_required"
|
||
frontend_owner: ""
|
||
frontend_ref: ""
|
||
target_release: ""
|
||
verified_at: ""
|
||
status_note: "⚠️ 2026-09-21 清空 frontend_owner/frontend_ref:按 BACKEND_CHANGELOG_DELIVERY_GUIDE,not_required 条目**任何人(含前端)不得回写** frontend_owner / frontend_ref / target_release / verified_at——这 4 个是「前端需要动作」的认领标记,填了会被发布门禁 E_FRONTEND 残留校验拦下(2026-08-19 #6077 就是这么被拦的)。前端表达「已知悉」用评论/口头即可,不动 frontmatter。本文件能推上去是因为门禁**只扫新增路径**,不是因为合规。mmg 的实证本身保留在下文。 gateway_status=verified 的依据:2026-09-20 18:22-18:46 经网关 https://api.test.1814.love:9443 实测(账号 cw_test_7444)。GET /v3/admin/order/group-batch/{id}/status-logs 在两个批次上分别取到 BATCH_VEHICLE_REQUIREMENT_REOPENED(开窗,extra 含 windowMinutes/blockedStage/expiresAt/scopeGroupCodes/scopeDates 五项齐全)与 BATCH_VEHICLE_DISPATCH_RECONFIGURED(重配),eventType/eventTypeName/changeType=DATA/extra 各字段逐一对照一致。⚠️ 本次实测发现并已订正一处契约偏差:重配事件的 operatorId 原写字符串 SYSTEM,实测为 null(与库里其它 SYSTEM 类事件惯例一致),判为文档写错而非实现错,已在字段表/字段值/JSON 示例/注意事项共 5 处改完。前端若按原文档判空会得到相反预期,故这条订正与发布同一批。 前端实证翻 not_required(mmg 2026-09-20):前端无自建 eventType 映射表,StatusLogsPanel 渲染 `eventTypeName || eventType || '事件'`(后端直供中文名+原码兜底),operatorType=SYSTEM 时 operatorId=null/operatorName「系统」是该面板注释钉住的既有口径,两类新事件与 operatorId 订正均自动正常显示,零改动。 【上一轮原注】backend_status=deployed 的判据(2026-09-20 18:35 复核):hl-order-service-v3 测试服部署点 e179e09bd(2026-09-20 17:55:02 发布),`git merge-base --is-ancestor ab92377c7 e179e09bd` = true,故本篇端点的代码确已在测试服运行的字节里。⚠️ 这是一次**时点读数**:测试服由多会话共用,随时可能被滚到别的提交;origin/dev-v3 在本次复核时已前进到 f56692a51,落后的是部署点不是本篇。 gateway_status 保持 pending——本会话未对本篇端点做任何真实网关调用,正文示例值的来源已在各小节逐处标注(单元测试字面量 / @ApiModelProperty example 声明),不是抓包。待网关复验后再置 verified,**不得为了让门禁变绿改这个位**。"
|
||
updated_at: "2026-09-20"
|
||
base: "dev-v3"
|
||
---
|
||
|
||
# order-v3: 团期时间线新增开窗与重配两类事件
|
||
|
||
> **存放目录**: 二期(v3) → `changelogs-v2/{YYYY-MM}/`
|
||
>
|
||
> **服务**: hl-order-v3 (端口 8021)
|
||
> **PR**: #8039
|
||
> **Issue**: #7987
|
||
> **日期**: 2026-09-20
|
||
> **影响范围**: 管理后台 - 团期详情页 - 状态流水/时间线
|
||
|
||
---
|
||
|
||
## ⚠️ 关键变化
|
||
|
||
后端在团期时间线(`group_batch_status_log` 表)新增两种事件记录:
|
||
- **BATCH_VEHICLE_REQUIREMENT_REOPENED**(开窗事件)—— 管理员显式开启受控重配窗口时写入
|
||
- **BATCH_VEHICLE_DISPATCH_RECONFIGURED**(重配事件)—— fleet 回调确认重配生效时写入
|
||
|
||
~~**前端必须新增这两个 event_type 值的渲染分支**,否则这两类事件在管理后台团期时间线上将无法正常显示其标签名(见下文「③ 前端渲染缺口」)。~~
|
||
|
||
✅ **2026-09-20 mmg 实证推翻了上面这句,前端零改动**(原句划掉保留接 grep):`StatusLogsPanel` 渲染的是 `eventTypeName || eventType || '事件'`,**后端直供中文名 + 原码兜底**,前端**没有**自建 eventType 映射表 ⇒ 两类新事件与 `operatorId` 那处订正都会自动正常显示。
|
||
📌 下文「③ 前端渲染缺口」一节同样按这条实证作废——**它描述的缺口在当前前端实现里不存在**。⚠️ 但这个结论依赖「后端继续直供 `eventTypeName`」:哪天后端让该字段返回 null(正文「注意事项」里写的历史脏值场景就是一例),前端会退化成显示原始 code,那时这条渲染缺口会重新成立。
|
||
|
||
---
|
||
|
||
## 一、背景
|
||
|
||
#7987 实施"受控重配窗口"机制:管理员可在团期进入待出发后仍显式开一个时间限制的窗口,允许车务在此期间重新排车。此前这两件事的事实只落在需求行的三个字段(`reconfigure_reason` / `reconfigure_opened_by` / `reconfigure_opened_at`),但运营打开团期详情页时看不到这些列,无法追溯窗口何时开过。
|
||
|
||
本次变更补上团期时间线的留痕,让管理员在团期详情页就能看到「谁在何时以什么原因开了重配窗口」与「窗口内换车是否生效」。
|
||
|
||
---
|
||
|
||
## 二、变更接口清单
|
||
|
||
既存接口(#7304 落地)返回值扩展:
|
||
|
||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||
|---|------|------|------|----------|------|
|
||
| 1 | 团期状态流水查询 | GET | `/v3/admin/order/group-batch/{id}/status-logs` | 响应新增 event_type 值 | 新增两种事件类型 |
|
||
|
||
---
|
||
|
||
## 三、接口详情
|
||
|
||
### 1. 团期状态流水查询 `GET /v3/admin/order/group-batch/{id}/status-logs`
|
||
|
||
**VO**: `GroupBatchStatusLogItemVO` (既存响应 VO,新增 eventTypeName 映射值)
|
||
|
||
#### 使用场景
|
||
|
||
管理后台团期详情页的状态流水/时间线面板,用于向运营展示该团期的历史状态变化与重要操作记录。本次变更使该时间线新增两类事件的可见性。
|
||
|
||
#### 入参
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| id | Path | Long | ✅ | - | 团期主订单 ID |
|
||
|
||
#### 出参 `Result<List<GroupBatchStatusLogItemVO>>`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| logId | String | 流水记录 ID |
|
||
| groupBatchId | Long | 团期主订单 ID |
|
||
| changeType | String | 变更类型:STATUS(状态流转) / DATA(数据变更) |
|
||
| eventType | String | 事件类型代码(如 BATCH_VEHICLE_REQUIREMENT_REOPENED) |
|
||
| eventTypeName | String | 事件类型中文名(**新增两种**:团车受控重开·已开配车窗口 / 团车受控重配·车辆安排已变更) |
|
||
| fromStatus | String | 状态前值代码(如 PENDING_DEPARTURE) |
|
||
| fromStatusName | String | 状态前值中文名 |
|
||
| toStatus | String | 状态后值代码 |
|
||
| toStatusName | String | 状态后值中文名 |
|
||
| content | String | 事件文本内容(前端直接渲染) |
|
||
| reason | String | 事件原因/备注(可为 null) |
|
||
| operatorType | String | 操作人类型:ADMIN / SYSTEM / MQ |
|
||
| operatorId | String | 操作人 ID。⚠️ **2026-09-20 网关实测订正**:**`operatorType=SYSTEM` 时本字段是 `null`,不是字符串 `"SYSTEM"`**(与库里其它 SYSTEM 类事件如 `BATCH_VEHICLE_REQUIREMENT_DONE` 的惯例一致)。前端判空请按 `null` 处理 |
|
||
| operatorName | String | 操作人名称 |
|
||
| extra | Map<String, Object> | 结构化附加数据,字段随事件类型变化(见下文"新增事件详解") |
|
||
| changedAt | String | 事件发生时刻(ISO 8601) |
|
||
|
||
#### 新增事件类型详解
|
||
|
||
**BATCH_VEHICLE_REQUIREMENT_REOPENED 事件**
|
||
- `eventType`: `"BATCH_VEHICLE_REQUIREMENT_REOPENED"`
|
||
- `eventTypeName`: `"团车受控重开·已开配车窗口"`
|
||
- `changeType`: `"DATA"` (状态内数据变更,fromStatus = toStatus)
|
||
- `operatorType`: `"ADMIN"`
|
||
- `operatorId`: 开窗管理员 ID
|
||
- `operatorName`: 开窗管理员真名
|
||
- `content`: "开启受控重配窗口:允许车务在 X 分钟内重新安排本团车辆(授权 N 个乘车分组 / M 个行程日,2026-09-20 10:30:00 前有效)。此前团期已进入「待出发」,正常不可再配车。"
|
||
- `reason`: 管理员必填的开窗原因(如"团期特殊变更")
|
||
- `extra`: 结构化数据
|
||
```json
|
||
{
|
||
"requirementId": "需求行主键(字符串)",
|
||
"requirementVersion": 需求行版本号(整数),
|
||
"blockedStage": "PENDING_DEPARTURE",
|
||
"windowMinutes": 120,
|
||
"expiresAt": "2026-09-20T12:30:00",
|
||
"scopeGroupCodes": ["乘车分组编号数组"],
|
||
"scopeDates": ["2026-09-21", "2026-09-22"]
|
||
}
|
||
```
|
||
|
||
**BATCH_VEHICLE_DISPATCH_RECONFIGURED 事件**
|
||
- `eventType`: `"BATCH_VEHICLE_DISPATCH_RECONFIGURED"`
|
||
- `eventTypeName`: `"团车受控重配·车辆安排已变更"`
|
||
- `changeType`: `"DATA"` (状态内数据变更,fromStatus = toStatus)
|
||
- `operatorType`: `"SYSTEM"`
|
||
- `operatorId`: **`null`**(⚠️ **2026-09-20 网关实测订正**:原写 `"SYSTEM"`,实测为 `null`)
|
||
- `operatorName`: `"系统"`
|
||
- `content`: "本团车辆安排已被重新排定:车务在受控重配窗口内完成换车,新的配车计划(版本 20260920_v1)已生效,原计划作废。开窗授权人:张三;具体经办人见车务配车记录。"
|
||
- `reason`: 开窗时管理员填写的原因(沿用窗口的 reason,非重配操作人的备注)
|
||
- `extra`: 结构化数据
|
||
```json
|
||
{
|
||
"requirementId": "需求行主键(字符串)",
|
||
"requirementVersion": 需求行版本号(整数),
|
||
"planVersion": "fleet 配车计划版本号(字符串)",
|
||
"reconfigureOpenedBy": "开窗管理员 ID(与前一条事件的 operatorId 对应)",
|
||
"reconfigureOpenedAt": "2026-09-20T10:15:30"
|
||
}
|
||
```
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
GET /v3/admin/order/group-batch/8000001/status-logs
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": [
|
||
{
|
||
"logId": "12345",
|
||
"groupBatchId": "8000001",
|
||
"changeType": "DATA",
|
||
"eventType": "BATCH_VEHICLE_REQUIREMENT_REOPENED",
|
||
"eventTypeName": "团车受控重开·已开配车窗口",
|
||
"fromStatus": "PENDING_DEPARTURE",
|
||
"fromStatusName": "待出发",
|
||
"toStatus": "PENDING_DEPARTURE",
|
||
"toStatusName": "待出发",
|
||
"content": "开启受控重配窗口:允许车务在 120 分钟内重新安排本团车辆(授权 2 个乘车分组 / 3 个行程日,2026-09-20T12:30:00 前有效)。此前团期已进入「待出发」,正常不可再配车。",
|
||
"reason": "临时增加3人,需调整座位分组与用车方案",
|
||
"operatorType": "ADMIN",
|
||
"operatorId": "1001",
|
||
"operatorName": "李四",
|
||
"extra": {
|
||
"requirementId": "7000001",
|
||
"requirementVersion": 2,
|
||
"blockedStage": "PENDING_DEPARTURE",
|
||
"windowMinutes": 120,
|
||
"expiresAt": "2026-09-20T12:30:00",
|
||
"scopeGroupCodes": ["GRP001", "GRP002"],
|
||
"scopeDates": ["2026-09-21", "2026-09-22", "2026-09-23"]
|
||
},
|
||
"changedAt": "2026-09-20T10:30:00"
|
||
},
|
||
{
|
||
"logId": "12346",
|
||
"groupBatchId": "8000001",
|
||
"changeType": "DATA",
|
||
"eventType": "BATCH_VEHICLE_DISPATCH_RECONFIGURED",
|
||
"eventTypeName": "团车受控重配·车辆安排已变更",
|
||
"fromStatus": "PENDING_DEPARTURE",
|
||
"fromStatusName": "待出发",
|
||
"toStatus": "PENDING_DEPARTURE",
|
||
"toStatusName": "待出发",
|
||
"content": "本团车辆安排已被重新排定:车务在受控重配窗口内完成换车,新的配车计划(版本 20260920_v1)已生效,原计划作废。开窗授权人:李四;具体经办人见车务配车记录。",
|
||
"reason": "临时增加3人,需调整座位分组与用车方案",
|
||
"operatorType": "SYSTEM",
|
||
"operatorId": null,
|
||
"operatorName": "系统",
|
||
"extra": {
|
||
"requirementId": "7000001",
|
||
"requirementVersion": 2,
|
||
"planVersion": "20260920_v1",
|
||
"reconfigureOpenedBy": "1001",
|
||
"reconfigureOpenedAt": "2026-09-20T10:30:00"
|
||
},
|
||
"changedAt": "2026-09-20T11:45:00"
|
||
}
|
||
],
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": [],
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 589500,
|
||
"message": "团期不存在",
|
||
"success": false,
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 接口返回的时间线按 `changedAt` 升序排列(从最早到最新)
|
||
- 两个新事件都属于 `DATA` 类(状态内数据变更),不涉及团期主状态 `batch_status` 变化
|
||
- 库里若有历史脏值或未知 event_type,后端 eventTypeLabel 返回 null,前端需兜底显示原始 code 值
|
||
|
||
---
|
||
|
||
## 四、契约约束与正确调用方式
|
||
|
||
> 本节说明后端接受请求、返回响应的规则。
|
||
|
||
### ✅ 正确 / ❌ 错误请求对照
|
||
|
||
| 场景 | 请求示例 | 结果 |
|
||
|------|---------|------|
|
||
| ✅ 查询既存团期的时间线 | `GET /v3/admin/order/group-batch/8000001/status-logs` | 200 + 时间线列表 |
|
||
| ✅ 无流水时返回空列表 | `GET /v3/admin/order/group-batch/9999999/status-logs`(不存在团期) | 200 + `data: []` |
|
||
| ❌ 团期 ID 非数字 | `GET /v3/admin/order/group-batch/abc/status-logs` | 400 |
|
||
|
||
### 字段约束与兼容性规则
|
||
|
||
- **eventType 兼容性**:库里可能存在本版本未定义的历史 event_type 值 → 后端 eventTypeLabel 返回 null → 前端应兜底显示原始 code 值,而不是报错
|
||
- **extra 字段**:根据 eventType 不同而变化(见下文详解);前端需根据 eventType 判断应读取哪些 extra 字段,勿假设所有事件的 extra 结构相同
|
||
- **operatorType 非 ADMIN 时**:⚠️ **2026-09-20 网关实测订正**:**`operatorId` 是 `null`、`operatorName` 是 `"系统"`——两个字段形态不同,不要当成同一回事**(原文把它们合并描述成「可能不是真实人名」,会让前端以为 `operatorId` 也有值) → 前端应按类型判断如何渲染,勿硬编码为"管理员"
|
||
- **reason 字段**:可为 null(仅新增事件强制填写,既有历史事件可能无此字段) → 前端应做 null check
|
||
|
||
---
|
||
|
||
## 六.5、枚举:新增事件类型
|
||
|
||
### eventType(GroupBatchLogEventType)
|
||
|
||
**所属字段**: `GroupBatchStatusLogItemVO.eventType` | **类型**: `String` | **枚举类**: `com.hulalv.order.groupbatch.enums.GroupBatchLogEventType`
|
||
|
||
新增两个枚举值(共有 N+2 个值):
|
||
|
||
| 值 | 中文名 | 说明 | changeType | 何时写入 |
|
||
|----|------|------|-----------|---------|
|
||
| `BATCH_VEHICLE_REQUIREMENT_REOPENED` | 团车受控重开·已开配车窗口 | 管理员显式开启受控重配窗口,允许车务在时间限制内重新排车 | DATA | 调用 POST `/v3/admin/order/group-batch/{id}/vehicle-requirement/reopen` 时 |
|
||
| `BATCH_VEHICLE_DISPATCH_RECONFIGURED` | 团车受控重配·车辆安排已变更 | fleet 就绪回调确认重配生效,新车辆计划已应用,原计划作废 | DATA | fleet Outbox 回调解除受控重开阻断时 |
|
||
|
||
### operatorType(操作人类型)
|
||
|
||
**所属字段**: `GroupBatchStatusLogItemVO.operatorType` | **类型**: `String`
|
||
|
||
既有三种值,新增事件涉及的分别是:
|
||
|
||
| 值 | 中文名 | 说明 | 示例事件 |
|
||
|----|------|------|---------|
|
||
| `ADMIN` | 管理员 | 真实的管理后台登录用户 | BATCH_VEHICLE_REQUIREMENT_REOPENED |
|
||
| `SYSTEM` | 系统 | 无 HTTP 请求上下文的自动操作(MQ / 定时任务 / 回调) | BATCH_VEHICLE_DISPATCH_RECONFIGURED |
|
||
| `MQ` | MQ 事件 | MQ 消费触发(如支付/退款回调) | 既有其他事件 |
|
||
|
||
---
|
||
|
||
## 四、前端需求与缺口说明
|
||
|
||
### ① eventTypeLabel 映射(前端渲染分支)
|
||
|
||
**后端实现**(`GroupBatchStatusLogService.java:237-248`):
|
||
```java
|
||
private String eventTypeLabel(String value) {
|
||
if (value == null) {
|
||
return null;
|
||
}
|
||
for (GroupBatchLogEventType type : GroupBatchLogEventType.values()) {
|
||
if (type.getValue().equals(value)) {
|
||
return type.getLabel();
|
||
}
|
||
}
|
||
return null; // 未知值返回 null
|
||
}
|
||
```
|
||
|
||
**当前状况**:
|
||
- 后端遍历枚举查找 event_type,找到则返回 label,否则返回 null
|
||
- 前端收到的 eventTypeName 字段值为 null 时,该行事件在时间线上**显示为空白**(既不报错、也不显示原始值,这是现状)
|
||
|
||
**前端必须新增的映射**:
|
||
```typescript
|
||
// 示例伪码
|
||
const eventTypeLabels = {
|
||
"BATCH_VEHICLE_REQUIREMENT_REOPENED": "团车受控重开·已开配车窗口",
|
||
"BATCH_VEHICLE_DISPATCH_RECONFIGURED": "团车受控重配·车辆安排已变更",
|
||
// ... 既有其他 event_type ...
|
||
};
|
||
```
|
||
|
||
**不加分支的后果**:这两类事件在管理后台团期时间线上无法显示其事件名称,运营只看得到时刻/操作人/备注,但看不出"这是什么事件",影响运营追溯。
|
||
|
||
### ② 重配事件的操作人恒为 SYSTEM(不能拿 extra 字段顶替)
|
||
|
||
**源码事实**(`GroupBatchService.java:1089-1118`,recordReconfigureTrail 方法):
|
||
```java
|
||
// 就绪回调中,无 HTTP 请求上下文,operatorResolver.resolve() 返回 SYSTEM
|
||
Operator operator = operatorResolver.resolve(); // → Operator.system()
|
||
groupBatchStatusLogService.recordDataChange(groupBatchId,
|
||
GroupBatchLogEventType.BATCH_VEHICLE_DISPATCH_RECONFIGURED,
|
||
batch == null ? null : GroupBatchStatus.fromCode(batch.getBatchStatus()),
|
||
content, window.reason(), extra);
|
||
```
|
||
|
||
**重配事件的字段值**:
|
||
- `operatorType`: `"SYSTEM"`
|
||
- `operatorId`: **`null`**(⚠️ **2026-09-20 网关实测订正**:原写 `"SYSTEM"`,实测为 `null`)
|
||
- `operatorName`: `"系统"`
|
||
- `extra.reconfigureOpenedBy`: 开窗管理员 ID(**不是**重配操作人)
|
||
- `extra.reconfigureOpenedAt`: 开窗时刻(**不是**重配时刻,重配时刻在 changedAt)
|
||
|
||
**这不是 bug,是设计**:
|
||
- 重配的真实经办人(谁在车务后台点的"重配"按钮)只存在于 fleet 侧的 `fleet_group_dispatch.updated_by`
|
||
- order-v3 是接收者,fleet 回调进来时没有经办人载荷
|
||
- `extra.reconfigureOpenedBy` 答的是"**谁授权开的窗**"(过去的管理动作),与"**谁换的车**"(当前的车务动作)是两个不同的人、不同的时刻
|
||
|
||
**前端若把 `extra.reconfigureOpenedBy` 显示成"经办人",就是把一个错误的责任人写到审计页面上**。
|
||
- ✅ 正确做法:operatorName 显示"系统",content 或 extra.reconfigureOpenedBy 里可补充说明"开窗人是谁"
|
||
- ❌ 错误做法:拿 extra.reconfigureOpenedBy 冒充 operatorId/operatorName 展示成经办人
|
||
|
||
### ③ 开窗事件的操作人是真实人(与重配事件不同)
|
||
|
||
**源码事实**(`GroupVehicleRequirementService.java:2305-2347`,recordReopenTrail 方法):
|
||
```java
|
||
public GroupVehicleReopenRespVO doReopen(Long groupBatchId,
|
||
GroupVehicleRequirementReopenReqVO req,
|
||
String operatorId) {
|
||
// ... 业务逻辑 ...
|
||
recordReopenTrail(groupBatchId, batch, active, req, windowMinutes, expiresAt);
|
||
}
|
||
|
||
private void recordReopenTrail(...) {
|
||
// recordDataChange 内部调 operatorResolver.resolve(),因为这是 HTTP 请求上下文
|
||
// 会从 AdminAuthInterceptor 注入的 AdminContext 里取当前登录管理员
|
||
groupBatchStatusLogService.recordDataChange(groupBatchId,
|
||
GroupBatchLogEventType.BATCH_VEHICLE_REQUIREMENT_REOPENED,
|
||
GroupBatchStatus.fromCode(batch.getBatchStatus()), content, req.getReason(), extra);
|
||
}
|
||
```
|
||
|
||
**开窗事件的操作人**:
|
||
- `operatorType`: `"ADMIN"`(不是 SYSTEM)
|
||
- `operatorId`: 调用 reopen 端点的管理员 ID(真实人)
|
||
- `operatorName`: 管理员真名
|
||
|
||
**两个事件的操作人不同的原因**:
|
||
- 开窗:用户通过管理后台 HTTP 端点主动触发 → 上下文有 AdminContext → operatorResolver 返回真实人
|
||
- 重配:fleet 异步回调 → 无 HTTP 请求上下文 → operatorResolver 返回 SYSTEM
|
||
|
||
**前端不能混淆**:别以为所有时间线事件的 operatorName 都是真实人,重配这条特殊。
|
||
|
||
---
|
||
|
||
## 五、数据库行为
|
||
|
||
新增行落在 `group_batch_status_log` 表:
|
||
|
||
| 事件 | 写入时机 | from_status | to_status | change_type | 示例 |
|
||
|------|----------|------------|-----------|-------------|------|
|
||
| BATCH_VEHICLE_REQUIREMENT_REOPENED | 调用 POST `/v3/admin/order/group-batch/{id}/vehicle-requirement/reopen` 时 | PENDING_DEPARTURE | PENDING_DEPARTURE | DATA | 管理员张三在 2026-09-20 10:30 开窗 |
|
||
| BATCH_VEHICLE_DISPATCH_RECONFIGURED | fleet 就绪回调解除受控重开阻断时 | PENDING_DEPARTURE | PENDING_DEPARTURE | DATA | fleet 确认换车后在 2026-09-20 11:45 生效 |
|
||
|
||
**关键**:两个事件都不改变团期 batch_status,`from_status` 与 `to_status` 相同。
|
||
|
||
---
|
||
|
||
## 六、边界行为
|
||
|
||
- **库里存在历史脏值** → 后端 eventTypeLabel 返回 null → 前端时间线该行显示空白或兜底处理
|
||
- **并发写入** → 开窗事务与重配事务独立,都跟随各自的外层事务(不丢失)
|
||
- **幂等性** → 开窗已有生效窗口时幂等返回既有令牌(不重复写库);普通首配回调、常规刷新回调若无受控重开阻断时 → CAS 0 行返回 → 不落本事件(常态)
|
||
|
||
---
|
||
|
||
## 六.6、修改前后对比
|
||
|
||
### 接口行为对比
|
||
|
||
| 行为 | 改前 | 改后 |
|
||
|------|------|------|
|
||
| 时间线中开窗操作的可见性 | 三个字段存库但页面不展示 | 新增一条 DATA 类事件,时间线上可见 |
|
||
| 时间线中重配操作的可见性 | 无记录 | 新增一条 DATA 类事件,标明"车已换、谁开的窗、老计划作废" |
|
||
| 事件类型枚举 | 共 N 种 | 新增 2 种,共 N+2 种 |
|
||
| 前端事件渲染 | 既有 N 种 event_type 的映射 | 需新增 BATCH_VEHICLE_REQUIREMENT_REOPENED / BATCH_VEHICLE_DISPATCH_RECONFIGURED 两条映射 |
|
||
|
||
---
|
||
|
||
## 六.7、影响评估
|
||
|
||
- **是否破坏向后兼容**: 否。既存接口返回值扩展,老 event_type 不变
|
||
- **前端是否必须同步上线**: **是**。不新增渲染映射会导致新增两类事件在时间线上显示为空白
|
||
- **前端 workaround 清理点**:
|
||
- 若前端已有"event_type 未知时显示原始 code"的兜底逻辑,可临时显示 `BATCH_VEHICLE_REQUIREMENT_REOPENED` 等原始值
|
||
- 但正式上线必须使用中文标签,不能长期显示原始值(运营不认识)
|
||
|
||
---
|
||
|
||
## 七、不影响范围
|
||
|
||
- **仅影响**: 管理后台 - 团期详情页 - 状态流水/时间线面板(新增两类事件的渲染)
|
||
- **零影响**:
|
||
- 团期主状态推进(batch_status 机制不变)
|
||
- 用车需求状态机(需求行读写逻辑不变)
|
||
- 订单详情、C 端行程页面(无此数据源)
|
||
- 旧团期数据(存量团期的时间线不会补充新事件)
|
||
|
||
---
|
||
|
||
## 八、测试环境已验证
|
||
|
||
后端逻辑验证(提交 ab92377c7):
|
||
|
||
```
|
||
POST /v3/admin/order/group-batch/8000001/vehicle-requirement/reopen
|
||
请求体: { "scopeGroupCodes": ["GRP001"], "scopeDates": ["2026-09-21"], "reason": "test reason" }
|
||
→ 200, group_batch_status_log 新增 1 行 BATCH_VEHICLE_REQUIREMENT_REOPENED ✓
|
||
|
||
fleet 就绪回调(模拟)
|
||
request: {"groupBatchId": 8000001, "requirementId": 7000001, "requirementVersion": 2, "planVersion": "v1"}
|
||
→ 200, group_batch_status_log 新增 1 行 BATCH_VEHICLE_DISPATCH_RECONFIGURED ✓
|
||
|
||
GET /v3/admin/order/group-batch/8000001/status-logs
|
||
→ 200, 返回值中 eventTypeName 正确映射(后端已验证) ✓
|
||
|
||
前端渲染验证:**待前端补充映射后联调**
|
||
```
|
||
|
||
---
|
||
|
||
## 九、相关历史 PR
|
||
|
||
| PR | Issue | 说明 | 是否仍有效 |
|
||
|-------|-------|------|---------|
|
||
| #7986 | #7986 | 受控重配窗口核心机制(团车需求侧) | ✅ 有效 |
|
||
| #8039 | #7987 | 本 PR - 补团期时间线留痕 | ✅ 最新 |
|
||
|
||
---
|
||
|
||
## 十、相关文档
|
||
|
||
- **后端枚举定义**: [`hl-order-service-v3/src/main/java/com/hulalv/order/groupbatch/enums/GroupBatchLogEventType.java`](https://git.1814.love:8443/wx/HL/blob/dev-v3/hl-order-service-v3/src/main/java/com/hulalv/order/groupbatch/enums/GroupBatchLogEventType.java)
|
||
- BATCH_VEHICLE_REQUIREMENT_REOPENED 定义:第 314-344 行
|
||
- BATCH_VEHICLE_DISPATCH_RECONFIGURED 定义:第 347-376 行
|
||
|
||
- **时间线服务**: [`hl-order-service-v3/src/main/java/com/hulalv/order/groupbatch/service/GroupBatchStatusLogService.java`](https://git.1814.love:8443/wx/HL/blob/dev-v3/hl-order-service-v3/src/main/java/com/hulalv/order/groupbatch/service/GroupBatchStatusLogService.java)
|
||
- eventTypeLabel 实现:第 237-248 行
|
||
|
||
- **开窗留痕**: [`hl-order-service-v3/src/main/java/com/hulalv/order/groupbatch/service/GroupVehicleRequirementService.java`](https://git.1814.love:8443/wx/HL/blob/dev-v3/hl-order-service-v3/src/main/java/com/hulalv/order/groupbatch/service/GroupVehicleRequirementService.java)
|
||
- recordReopenTrail 方法:第 2329-2387 行
|
||
|
||
- **重配留痕**: [`hl-order-service-v3/src/main/java/com/hulalv/order/groupbatch/service/GroupBatchService.java`](https://git.1814.love:8443/wx/HL/blob/dev-v3/hl-order-service-v3/src/main/java/com/hulalv/order/groupbatch/service/GroupBatchService.java)
|
||
- recordReconfigureTrail 方法:第 1089-1118 行
|
||
|
||
---
|
||
|
||
## 关联 / 联系人
|
||
|
||
### 链接
|
||
|
||
- **Issue**: [#7987](https://git.1814.love:8443/wx/HL/issues/7987)
|
||
- **PR**: [#8039](https://git.1814.love:8443/wx/HL/pulls/8039)
|
||
- **Merge commit**: [ab92377c7](https://git.1814.love:8443/wx/HL/commit/ab92377c7)
|
||
|
||
### 联系人
|
||
|
||
- **后端负责人**: @wx
|
||
- **前端对接人**: @mmg(hl-ui 仓库,需新增 eventTypeLabel 映射)
|