文件
hl-api-changelog/changelogs-v2/2026-09/30_8630_团期活跃子订单清零自动复位需求确认与整团用车需求-修复-管理后台.md
T
API Changelog Bot和Claude Opus 5 4445618696
changelog-filename-gate / validate (push) Failing after 1s
docs(changelog): #8629 幂等键补对象身份段、#8630 团期活跃子订单清零自动复位
- #8629 出行人/大交通新增的幂等键补上对象身份段,同订单录入第二个对象不再被误拒
- #8630 团期最后一户取消后自动复位 requirement_confirmed 与整团用车需求(CONFIRMED→DRAFT),
  并写 BATCH_REQUIREMENT_REOPENED 时间线(trigger=ALL_SUB_ORDERS_CANCELLED)

Refs #8629
Refs #8630

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-30 12:30:56 +08:00

149 行
10 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: "hl-changelog/v2"
ticket: "8630"
title: "团期活跃子订单清零后,自动复位团级需求确认与整团用车需求"
consumer: "admin"
author: "wx(GIT)"
change_type: "修复"
backend_status: "deployed"
gateway_status: "not_required"
frontend_status: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "hl-order-service-v3 dev-v3 合入提交 8e00cc98bc(PR #8640,同 PR 还含 #8631 的两处纯 javadoc 订正,与本单契约无关,未在本文档中提及)。本次改动不涉及任何接口的请求/响应结构变化,只影响既有字段在特定场景下的取值。"
updated_at: "2026-09-30"
base: "dev-v3"
---
# 团期活跃子订单清零后,自动复位团级需求确认与整团用车需求
> **存放目录**: 二期(order-v3/fleet)→ `changelogs-v2/2026-09/`
>
> **服务**: hl-order-service-v3
> **PR**: [#8640](https://git.1814.love:8443/wx/HL/pulls/8640)
> **Issue**: [#8630](https://git.1814.love:8443/wx/HL/issues/8630)
> **日期**: 2026-09-30
> **影响范围**: 团期详情页「需求确认」状态、整团用车需求状态;不涉及任何请求/响应字段结构变化
---
## ⚠️ 关键变化
**改前**:一个团期整体确认需求(`requirementConfirmed=true`)、且整团用车需求也已确认(`CONFIRMED`)之后,如果该团期名下的子订单被逐个取消,直到**最后一个活跃子订单也被取消**,系统没有任何收尾动作——`requirementConfirmed` 停留在 `true`,整团用车需求状态停留在 `CONFIRMED`。此时团期详情页、车务待办侧仍显示「需求已确认」「用车已确认」,而这个团实际上已经一户不剩,且这个不一致**不报错、不告警**,只能靠人工发现。
**改后**:当一个团期的活跃子订单数(`order_status != CANCELLED` 的子订单条数)由非零变为零时,系统自动做两件事:
1. 若整团用车需求当前处于 `CONFIRMED`,自动退回 `DRAFT`(不是 `PENDING_RECONFIRM`);
2. 若团级 `requirementConfirmed` 当前为 `true`,自动清为 `false`。
只要这两项里至少有一项真的发生了状态变化,就会在该团期的时间线写入一条 `BATCH_REQUIREMENT_REOPENED`(需求重开待确认)事件,`extra` 中携带 `trigger: "ALL_SUB_ORDERS_CANCELLED"`、`orderId`(触发收尾的最后一个取消子订单 ID)、`requirementConfirmedCleared`(布尔)、`groupVehicleRequirementWithdrawn`(布尔)。两项复位互相独立、各自按条件写,天然幂等;两项都无需变化时(例如本来就是 `false`/`DRAFT`)不写时间线、不产生噪声。
本次改动**不涉及任何请求体或响应体的字段增删/改类型**,纯粹是既有字段在「团期归零」这一新增场景下会被系统自动改写取值。
---
## 二、影响的字段与读取入口
以下字段的**读取路径未变**,本次改动只影响它们在「团期活跃子订单清零」这一时刻之后的取值:
| 字段 | 归属接口(示例) | 改前在团期归零后的取值 | 改后 |
|------|------|------|------|
| `requirementConfirmed` | `GET /v3/admin/order/group-batch/{groupBatchId}`(`GroupBatchDetailRespVO`)及房务看板系列 VO | 停留在归零前的最后取值(可能仍是 `true`) | 若归零前为 `true`,归零后自动变为 `false` |
| 整团用车需求 `status` | 整团用车需求相关读端点(`GroupVehicleRequirementRespVO.status`) | 停留在归零前的最后取值(可能仍是 `CONFIRMED`) | 若归零前为 `CONFIRMED`,归零后自动变为 `DRAFT` |
| 团期时间线 | 团期时间线读端点 | 归零无任何留痕 | 新增一条 `BATCH_REQUIREMENT_REOPENED` 事件(仅当至少一项真的被复位时才写) |
---
## 三、精确触发条件
- **"活跃子订单"的判据** = `order_status != CANCELLED`(含 `PENDING_PAY`、`COMPLETED` 等非取消状态均计入活跃;与既有人数计数、归团回填、对账口径一致)。**不是**看板上「免闸户不计」的统计口径——房务免闸的户在用车这一侧仍可能要车,因此不按闸门维度扣减分母。
- **收尾时点** = 该团期的活跃子订单数由非零变为零的那一刻(子订单取消事务提交之后)。覆盖以下所有取消入口:admin 出行前取消、C 端取消、退团审批、流团逐户取消、通用 `transition()` 的 CANCEL 分支、超时自动取消——这些入口最终都汇流到同一个内部取消事件,因此逐个入口都会触发本收尾逻辑。
- **不覆盖**的取消路径:
- `TERMINATE`(出行中终止行程 → `COMPLETED`)是与 `CANCEL`(→ `CANCELLED`)完全不同的状态路径,不会触发本收尾——出行中终止行程不代表这个团没有人,语义上也不应该清需求确认。
- 直接修改数据库、绕过应用层的取消不会触发(无代码路径可挂载)。
- **用车需求只在 `CONFIRMED` 这一档被自动退回**:`DRAFT`/`PENDING_RECONFIRM` 本来就不是已确认,无需处理;`DISPATCHED`(已发车务)/`DONE`(配车完成)**不会**被自动撤回——车务可能已经接单甚至配完车,自动撤回等于单方面掀掉车务在办的工作,这属于另一个业务决策,本次不做;`CANCELLED` 是流团终态,不会走到本路径。
---
## 六、边界行为(刻意不做的部分)
- **`batchStatus`(团期阶段)本次不变**:一个活跃子订单数归零的团期,其 `batchStatus` 可以继续停留在任意阶段(例如 `RESOURCE_PREPARING`「资源准备中」),不会被自动置为 `CANCELLED` 或退回 `RECRUITING`——自动改阶段涉及流团审批合规性判断,留给后续工单单独定案。前端据此判断"团是否还有效"时,**不能只看 `batchStatus`**,需要结合活跃子订单数或 `requirementConfirmed`/用车需求状态的复位来综合判断。
- **`DISPATCHED`/`DONE` 的用车需求不会被回退**:见上节"三、精确触发条件"。
- **房务就绪标记(`hotel_ready`)本次不动**:本收尾只处理 `requirementConfirmed` 与整团用车需求两项,房务侧的就绪标记不在本次收尾范围内,两者目前不对称——这是已知缺口,不在本单范围内一并解决。
- **不做历史数据回填**:已经处于"团期归零但需求确认/用车需求未复位"这种旧脏数据状态的历史团期,本次改动不会自动纠正,只对本次改动上线之后新发生的"归零"事件生效。
---
## 六.5、枚举 / 数据字典
整团用车需求状态 `GroupVehicleRequirementStatus`(本次改动涉及的部分状态,完整枚举 6 值):
| 值 | 中文名 | 说明 |
|------|------|------|
| DRAFT | 草稿 | 本次改动的复位目标 |
| CONFIRMED | 已确认 | 本次改动的复位起点(仅此档会被自动退回) |
| PENDING_RECONFIRM | 待重新确认 | 不受本次改动影响 |
| DISPATCHED | 已发车务 | 不受本次改动影响(明确不回退) |
| DONE | 配车完成 | 不受本次改动影响(明确不回退) |
| CANCELLED | 已取消 | 不受本次改动影响 |
团期时间线事件类型(本次涉及):
| 值 | 中文名 | 说明 |
|------|------|------|
| BATCH_REQUIREMENT_REOPENED | 需求重开待确认 | 复用既有事件类型(此前用于"定制师在整团确认后自行改需求"场景),本次新增一种触发来源;两种来源在 `extra.trigger` 字段区分,本次新增值为 `ALL_SUB_ORDERS_CANCELLED` |
---
## 六.6、修改前后对比
### 行为级对比
| 场景 | 改前 | 改后 |
|------|------|------|
| 团期活跃子订单数由非零变为零,此前 `requirementConfirmed=true` | 停留 `true`,无提示 | 自动变为 `false` |
| 团期活跃子订单数由非零变为零,此前整团用车需求 `CONFIRMED` | 停留 `CONFIRMED`,无提示 | 自动退回 `DRAFT` |
| 团期活跃子订单数由非零变为零,此前两项均已是"未确认"状态 | 无变化 | 无变化,不写时间线(幂等,无噪声) |
| 团期时间线 | 归零无任何记录 | 至少一项被复位时,新增一条 `BATCH_REQUIREMENT_REOPENED` 事件,`extra.trigger=ALL_SUB_ORDERS_CANCELLED` |
---
## 六.7、影响评估
- **是否破坏向后兼容**: 否——字段名称、类型、接口路径均未变,只是取值在新场景下会被后端自动改写
- **前端是否必须同步上线**: 视前端现有逻辑而定——若前端曾假设"一旦确认过就不会自动变回未确认"并据此做过缓存/跳过重复请求之类的优化,需要重新核对该假设在"团期归零"场景下不再成立
- **需要前端注意的读取口径变化**: `requirementConfirmed`、整团用车需求 `status` 在团期活跃子订单归零后可能被系统自动改写,不再只由人工操作(确认/打回)改变;`batchStatus` 不受此次自动复位联动,读取时不能用 `batchStatus` 代替对这两个字段的直接读取
---
## 七、不影响范围
- **仅影响**: 团期活跃子订单数归零这一时刻,`requirementConfirmed` 与整团用车需求 `CONFIRMED` 状态的自动复位
- **零影响**:
- 团期阶段 `batchStatus` 的取值与流转规则(见"六、边界行为")
- 房务就绪标记 `hotel_ready`
- 整团用车需求 `DISPATCHED`/`DONE`/`PENDING_RECONFIRM`/`CANCELLED` 四档的自动流转规则
- 所有接口的请求体、响应体字段结构(本次零新增、零删除、零改类型)
- 团期归零之前已经存在的历史脏数据(不做回填)
- 受控重配窗口相关的 `BATCH_VEHICLE_REQUIREMENT_REOPENED` 事件(另一独立事件类型,与本次复用的 `BATCH_REQUIREMENT_REOPENED` 不是同一个)
---
## 十、相关文档
- 关联 Issue: [wx/HL#8630](https://git.1814.love:8443/wx/HL/issues/8630)
- 关联 PR: [wx/HL#8640](https://git.1814.love:8443/wx/HL/pulls/8640)
## 关联 / 联系人
### 链接
- **Issue**: [#8630](https://git.1814.love:8443/wx/HL/issues/8630)
- **PR**: [#8640](https://git.1814.love:8443/wx/HL/pulls/8640)
- **Merge commit**: [8e00cc98bc](https://git.1814.love:8443/wx/HL/commit/8e00cc98bc)
### 联系人
- **后端负责人**: @wx