docs(changelog): #8767 出团通知书默认车辆补团车、下发缺项新增 DRIVER / DRIVER_UNAVAILABLE(修改接口·管理后台)+ 车务团车活跃派车行内部读口(新增接口·internal)
changelog-filename-gate / validate (push) Failing after 2s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
这个提交包含在:
jw
2026-10-04 17:16:16 +08:00
共同撰写人 Claude Opus 5.5
父节点 b4f2e3da14
当前提交 e08991a588
共修改 2 个文件,包含 650 行新增和 0 行删除
@@ -0,0 +1,426 @@
---
schema: "hl-changelog/v2"
ticket: "8767"
title: "出团通知书:默认车辆信息补上团车(整团派车)的车辆与司机,releaseBlockers 新增「车辆未派司机」DRIVER 与「司机信息暂不可用」DRIVER_UNAVAILABLE"
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: "已合并 dev-v3(PR #8794,merge commit a281744c8)并部署 TEST(order-v3 与 fleet 同为 dev-v3 a281744c8),自签 token 经网关实测:团车团 defaults.bus 与车务派车逐字一致且手机全脱敏;团车有车没派司机时 releasable=false、缺项恰为 DRIVER,补派后恢复;车务服务读不到时缺项为 DRIVER_UNAVAILABLE、接口仍 200;无团车的团读数与部署前逐项一致;判权不变。纯加取值,入参与路径不变;页面按 #8746 约定用 releaseBlockers[].name 展示即可,无需新适配。"
updated_at: "2026-10-04"
base: "dev-v3"
---
# order-v3: 出团通知书默认车辆补团车,下发缺项新增 DRIVER / DRIVER_UNAVAILABLE
**服务**: hl-order-service-v3(读团车经 hl-fleet-service 内部读口,见同日 04_8767 新增接口那份)
**PR**: `#8794`(已合入 `dev-v3`,合并提交 `a281744c8`)
**Issue**: #8767
---
## ⚠️ 关键变化
🔴 **`releasable` 又多一个条件**:在 #8746「阶段 + 四项资源」之上,还要求**团车(整团派车)的车辆都已派司机**。团车有车没派司机的团,`releasable` 由 `true` 变 `false`。
🟢 **`releaseBlockers` 追加两个取值**:`DRIVER`「车辆未派司机」、`DRIVER_UNAVAILABLE`「司机信息暂不可用」,排在 `PHOTOGRAPHER` 之后,二者互斥。
🟢 **默认车辆信息 `defaults.bus` 补上团车**:先列团车,再列逐户派车;格式仍是「车型 车牌 司机 姓名 脱敏手机」。
🟢 入参、路径、判权、错误码全部不变;保存仍然不卡下发门。
---
## 一、背景
#8746 给出团通知书加了「下发门缺项」和「默认车辆带司机」,但只覆盖逐户派车。团车(整团派车)的车辆与司机只在车务服务里,通知书读不到:团车团的默认车辆信息是空的,团车「排了车没排司机」也查不出来——车务那边判团车就绪的硬门不含司机,`vehicle_ready=true` 不代表司机已派。
本单让通知书向车务读团车的活跃派车,补进默认车辆信息,并把「车辆未派司机」加进下发门。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 读出团通知书 | GET | `/v3/admin/order/group-batch/:groupBatchId/docs/notice` | 修改 | `releaseBlockers` 新增 `DRIVER` / `DRIVER_UNAVAILABLE`;`releasable` 加「团车都已派司机」;`defaults.bus`(及未保存时正文 `bus`)含团车 |
| 2 | 保存出团通知书 | PUT | `/v3/admin/order/group-batch/:groupBatchId/docs/notice` | 修改 | 响应同上 |
---
## 三、接口详情
**`releasable` 规则**(两个接口相同):团期状态是 待出发 / 出行中 / 待核单 / 核单中 / 已结算 之一,**且**配房、配车、配导游、配摄影四项都已完成,**且**团车的车辆都已派司机,才为 `true`。
**`releaseBlockers[]` 取值**(按下表顺序排列,`releasable=true` 时为 `[]`,从不为 `null`):
| key | name | 何时出现 |
|---|---|---|
| `STAGE` | 团期未到待出发 | 团期状态不在上面五个之内(不变) |
| `HOTEL` | 配房未完成 | 已成团且配房未完成(不变) |
| `VEHICLE` | 配车未完成 | 已成团且配车未完成(不变) |
| `GUIDE` | 配导游未完成 | 已成团且配导游未完成(不变) |
| `PHOTOGRAPHER` | 配摄影未完成 | 已成团且配摄影未完成(不变) |
| `DRIVER` | 车辆未派司机 | 🆕 已成团,且团车有车没派司机(按车务「团期配车总览」口径,已取消的派车不算) |
| `DRIVER_UNAVAILABLE` | 司机信息暂不可用 | 🆕 已成团,且这次没能从车务读到团车信息(车务服务不可用或超时);稍后重读即可 |
- 未成团(招募中、已流团)仍**只列 `STAGE`**,不判资源也不判司机。
- `DRIVER` 与 `VEHICLE` 各判各的:配车已完成的团照样可能缺司机。
- `DRIVER` 与 `DRIVER_UNAVAILABLE` 不会同时出现。
- 只用逐户派车(没有团车)的团不会出现 `DRIVER`:逐户派车的车一定带司机。
- 车务读不到时按「不可下发」处理:这期间所有已成团的团都会带 `DRIVER_UNAVAILABLE`,接口本身照常返回 `code=200`。
**`bus` 默认值**:先列团车,再列逐户派车,每辆车 `车型 车牌 司机 姓名 脱敏手机`。
- 团车的车型是车辆型号名,例 `丰田考斯特 蒙A-K1999 司机 巴特尔 135****5020`。
- 同一辆车(车型 + 车牌相同)在团车和逐户派车里都出现时只列一次,司机去重合并。
- 多日换过司机用 ` / ` 并列;车与车之间用 `、`。
- 团车全程没派司机的车只有车型车牌,例 `丰田埃尔法 蒙A-E5555`。
- 车务读不到时团车部分不出现,逐户派车部分照常。
- 手机号一律脱敏;已保存的正文不会自动刷新,只有 `defaults` 是实时值。
### 1. 读出团通知书 `GET /v3/admin/order/group-batch/:groupBatchId/docs/notice`
**VO**: `GroupBatchNoticeRespVO`(入参只有路径参数)→ `Result<GroupBatchNoticeRespVO>`
#### 使用场景
团期详情「出团通知书」弹窗打开时调用。按 `releasable` 置灰「打印 / 存 PDF」,`releasable=false` 时把 `releaseBlockers[].name` 列给用户看(与 #8746 相同,新取值无需单独处理)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | 团期 ID | **不变** |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| releasable | Boolean | 🔄 再加「团车都已派司机」(规则见上) |
| releaseBlockers[].key | String | 🔄 新增取值 `DRIVER` / `DRIVER_UNAVAILABLE` |
| releaseBlockers[].name | String | 🔄 新增「车辆未派司机」/「司机信息暂不可用」 |
| bus | String | 🔄 未保存过(`saved=false`)时等于 `defaults.bus`,含团车;已保存时为保存的原文 |
| defaults.bus | String | 🔄 先团车、再逐户派车,格式见上 |
| 其余字段 | — | **不变** |
#### 请求示例
```http
GET /v3/admin/order/group-batch/2106331639531601921/docs/notice HTTP/1.1
Authorization: Bearer <管理员 token>
```
#### 响应示例
示例:待出发、四项资源都已完成、团车两辆车里一辆没派司机,从未保存过(草稿)——取自 TEST 验收读数。
```json
{
"code": 200,
"message": "成功",
"data": {
"title": "冻干粉发短信给 · 出团通知书",
"greeting": "亲爱的团友,欢迎参加本次行程!",
"meetTime": "2026-11-20 08:30",
"meetPlace": "",
"leader": "",
"bus": "丰田考斯特 蒙A-K1999 司机 巴特尔 135****5020、丰田埃尔法 蒙A-E5555",
"contacts": "",
"service": "",
"bring": "",
"saved": false,
"releasable": false,
"releaseBlockers": [
{
"key": "DRIVER",
"name": "车辆未派司机"
}
],
"version": 0,
"updateTime": null,
"defaults": {
"title": "冻干粉发短信给 · 出团通知书",
"greeting": "亲爱的团友,欢迎参加本次行程!",
"meetTime": "2026-11-20 08:30",
"meetPlace": "",
"leader": "",
"bus": "丰田考斯特 蒙A-K1999 司机 巴特尔 135****5020、丰田埃尔法 蒙A-E5555",
"contacts": "",
"service": "",
"bring": ""
}
}
}
```
给那辆车派上司机后:`releasable=true`、`releaseBlockers=[]`,`bus` 变为 `丰田考斯特 蒙A-K1999 司机 巴特尔 135****5020、丰田埃尔法 蒙A-E5555 司机 乌力吉 135****5015`。
#### 空数据 / 降级响应
- 没有团车、也没有逐户派车:`defaults.bus` 为 `""`。
- 车务服务读不到:接口仍 `code=200`;已成团的团 `releaseBlockers` 带 `DRIVER_UNAVAILABLE`、`releasable=false`;`defaults.bus` 只含逐户派车部分。形如:
```json
{
"code": 200,
"data": {
"releasable": false,
"releaseBlockers": [
{
"key": "DRIVER_UNAVAILABLE",
"name": "司机信息暂不可用"
}
]
}
}
```
#### 错误响应
| code | 条件 |
|---|---|
| `589500` | 团期不存在或已删除(不变) |
| `589507` | 当前角色没有 `group-batch:docs` 权限(不变) |
```json
{
"code": 589507,
"message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
"data": null
}
```
#### 业务边界
- `releasable` 只管「能否打印 / 下发」,不影响读取与保存。
- 同一个团在不同时间读,`releaseBlockers` 可能不同(车务补派司机、或车务恢复可读后会变)。
- `DRIVER_UNAVAILABLE` 是暂时状态,不代表真缺司机;重读即可。
### 2. 保存出团通知书 `PUT /v3/admin/order/group-batch/:groupBatchId/docs/notice`
**VO**: `GroupBatchNoticeSaveReqVO` → `Result<GroupBatchNoticeRespVO>`
#### 使用场景
运营编辑通知书后保存。入参不变;响应与读接口同一个结构,`releasable` / `releaseBlockers` 按上面的新规则计算。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| groupBatchId | Path | Long | ✅ | 团期 ID | **不变** |
| bus | Body | String | ✅ | ≤256 字 | **不变**;默认值含团车后可能更长,车与司机组合很多时需删减后再存 |
| title / greeting / meetTime / meetPlace / leader / contacts / service / bring | Body | String | ✅ | 同 #7532 | **不变** |
| expectedVersion | Body | Integer | ✅ | ≥0 | **不变**,取读接口的 `version` |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| releasable / releaseBlockers | — | 🔄 同读接口 |
| version | Integer | **不变**,保存后的新版本 |
| 其余字段 | — | **不变** |
#### 请求示例
```json
{
"title": "冻干粉发短信给 · 出团通知书",
"greeting": "亲爱的团友,欢迎参加本次行程!",
"meetTime": "2026-11-20 08:30",
"meetPlace": "海拉尔东山国际机场 T1 到达厅 3 号门",
"leader": "",
"bus": "丰田考斯特 蒙A-K1999 司机 巴特尔 135****5020、丰田埃尔法 蒙A-E5555",
"contacts": "",
"service": "",
"bring": "",
"expectedVersion": 4
}
```
#### 响应示例
示例:团车一辆车没派司机时保存——保存成功、版本 4 → 5,响应同样带 `DRIVER`(保存不卡下发门)。
```json
{
"code": 200,
"message": "成功",
"data": {
"bus": "丰田考斯特 蒙A-K1999 司机 巴特尔 135****5020、丰田埃尔法 蒙A-E5555",
"meetPlace": "海拉尔东山国际机场 T1 到达厅 3 号门",
"saved": true,
"releasable": false,
"releaseBlockers": [
{
"key": "DRIVER",
"name": "车辆未派司机"
}
],
"version": 5
}
}
```
#### 空数据 / 降级响应
- 车务服务读不到时保存照常成功,响应缺项为 `DRIVER_UNAVAILABLE`。
#### 错误响应
| code | 条件 |
|---|---|
| `589500` | 团期不存在(不变) |
| `589507` | 当前角色没有 `group-batch:docs` 权限(不变) |
| `589585` | 版本冲突,请重读后再存(不变) |
| `589587` | 正文含证件号形态的数字(不变) |
```json
{
"code": 589585,
"message": "通知书已被他人修改,请刷新后重试",
"data": null
}
```
#### 业务边界
- 保存**不卡**下发门:缺司机、车务读不到都能保存。
- 保存成功仍在团期时间线新增「保存出团通知书(第 N 版)」(#8746 口径不变)。
---
## 四、契约约束与正确调用方式
- 「打印 / 存 PDF」按 `releasable` 置灰,`releasable=false` 时展示 `releaseBlockers[].name`;判断用 `key`,不要用中文名。
- 按 #8746 约定「遇到不认识的 `key` 按 `name` 展示」实现的页面,本单零适配。
- 不要自己根据车辆、司机数据推算能否打印,以 `releasable` 为准。
- 看到 `DRIVER_UNAVAILABLE` 时可以提示「稍后重试」,它不是业务缺项。
---
## 五、数据库行为
- 零表结构变更、零数据迁移。
- 读接口零写入;保存接口行为与 #8746 相同。
- 团车数据在保存前、写事务之外读取。
---
## 六、边界行为
- 团车派车行里车已被删除、取不到车型和车牌的,不进默认车辆信息,但仍参与「是否派了司机」的判断。
- 派过司机但司机档案已删除的,视为已派司机(不报 `DRIVER`),默认车辆信息里不显示该司机。
- 车务返回的手机号本已脱敏,通知书侧再脱敏一次,不会出现明文。
## 六.6、修改前后对比
| 场景 | 改前(#8746) | 改后 |
|---|---|---|
| 团车团默认车辆信息 | `""` | `丰田考斯特 蒙A-K1999 司机 巴特尔 135****5020` |
| 待出发、四项已完成、团车有车没派司机 | `releasable=true`,`[]` | `releasable=false`,`[DRIVER]` |
| 同上,补派司机后 | `releasable=true` | `releasable=true`,`[]` |
| 车务服务不可用 | 无影响(读不到团车) | 已成团的团带 `[DRIVER_UNAVAILABLE]`,`releasable=false` |
| 只用逐户派车的团 | — | 与改前相同 |
## 六.7、影响评估
- **是否破坏向后兼容**:`releaseBlockers` 只是多了两个取值;`releasable` 在「团车缺司机」与「车务读不到」时由 `true` 变 `false`。按 `name` 通用展示的页面无需改动。
- **前端是否必须同步上线**:不需要。
- **回滚**:revert PR #8794 后重新部署 order-v3 与 fleet。
---
## 七、不影响范围
- 两个接口的路径、入参、判权(`group-batch:docs`)、错误码:不变。
- 只用逐户派车的团:读数与改前一致(TEST 6 个团前后逐项比对一致)。
- 已保存的通知书正文:不会被改写。
- 小程序端:无影响。
---
## 八、测试环境已验证
**环境**:TEST(`https://api.test.1814.love`) **验证时间**:2026-10-04 17:04~17:13
**构建身份**:order-v3、fleet 均部署 `dev-v3 @ a281744c8`(本单合并提交);`releaseBlockers` 出现 `DRIVER` / `DRIVER_UNAVAILABLE` 新取值只可能来自新字节,fleet 两实例新内部读口返回 200。
**身份**:自签 token 直打网关,用 TEST 真实账号 ID 配对应角色。
### 8.1 团车团默认车辆(只读,3 个现成团车团)
| 团期 | `defaults.bus`(6 次读一致) | 与车务派车推算 |
|---|---|---|
| T27-5637 | `坦克300 蒙P318A 司机 P3测试司机18 139****0018、别克GL8 C0927T01 司机 测B0927司机甲 199****0001` | 一致 |
| T26-3963 | `丰田考斯特 蒙A-K1999 司机 巴特尔 135****5020` | 一致 |
| T26-0352 | `丰田埃尔法 蒙A-E5555 司机 乌力吉 135****5015` | 一致 |
部署前这三个团的 `defaults.bus` 都是 `""`。
### 8.2 车辆未派司机(自建团期,待出发、四项已完成,团车两辆、一辆没派司机)
| 步骤 | `releasable` | `releaseBlockers` | `defaults.bus` |
|---|---|---|---|
| 一辆没派司机 | `false` | `[DRIVER]` | `丰田考斯特 蒙A-K1999 司机 巴特尔 135****5020、丰田埃尔法 蒙A-E5555` |
| 补派司机后 | `true` | `[]` | `… 丰田埃尔法 蒙A-E5555 司机 乌力吉 135****5015` |
| 撤回司机后保存 | `false` | `[DRIVER]`(PUT 响应) | 版本 4 → 5,时间线新增「保存出团通知书(第 5 版)」 |
每步各读 6 次,读数一致。
### 8.3 车务服务读不到
只对通知书读团车这一条调用临时压 1ms 超时(17:02~17:08,验完还原配置并核对一致):4 个团各读 6 次全部 `code=200`;已成团的团都带 `DRIVER_UNAVAILABLE`(如资源准备中的团为 `[STAGE, HOTEL, DRIVER_UNAVAILABLE]`、核单中的团为 `[HOTEL, DRIVER_UNAVAILABLE]`),`defaults.bus` 不含团车;两个实例各记录 12 条降级告警,无系统异常。
### 8.4 只用逐户派车的团
6 个没有团车的团(资源准备中 4 个、核单中 1 个、已结算 1 个),部署前后 `releasable`、`releaseBlockers`、`bus`、`defaults.bus` 逐项一致,均未出现 `DRIVER`。
### 8.5 判权与日志
| 调用方 | 读 | 存 |
|---|---|---|
| 不带 token | 网关 `401` | 网关 `401` |
| 车务、财务(无 `group-batch:docs`) | `589507` | `589507`,零写入 |
| 管理员、团期管理员 | `200` | `200` |
验收时间窗内 order-v3、fleet 四个实例的日志,按 4 名司机脱敏号的前三后四检索明文手机号,零命中(同窗口内本轮请求的团期号四个实例均有命中,窗口有效)。
### 本地证据
| 项 | 读数 |
|---|---|
| 定向 7 类 | 125/0/0 |
| order-v3 `groupbatch` + `fleet` + `archunit` 包 + 全模块架构测试(有 Docker) | 3799/0/0 |
| fleet `dispatch` 包 + 红线架构测试 | 517/0/0,跳过 2(需显式开启的容器类) |
| 变基到最新 `dev-v3` 后重测(定向 + 流团相关 + 上下文 IT + 全部架构测试) | 315/0/0 |
### 未覆盖
- TEST 上没有「未删除团期 + 未取消订单 + 逐户派车快照」的现成团,逐户派车与团车同车合并只由单测覆盖。
- 团车有车没派司机用临时插入的派车行模拟,车务真实排车流程未走;验完已删除。
---
## 十、相关文档
- Issue `#8767`;PR `#8794`
- 拆单来源:Issue `#8746`(下发门缺项、逐户派车带司机)
- 团车读口:同日 `04_8767` 新增接口(内部)那份
- 接口文档:`docs/group/团期模块接口文档-v2.0.html` GB-ADM-081 / 082
## 关联 / 联系人
### 链接
- **Issue**: [#8767](https://git.1814.love/wx/HL/issues/8767)
- **PR**: [#8794](https://git.1814.love/wx/HL/pulls/8794)
- **Merge commit**: [a281744c8](https://git.1814.love/wx/HL/commit/a281744c8)
### 联系人
- **后端负责人**: @jw