From 5e25e03897f07395a3dca87195e743340b387881 Mon Sep 17 00:00:00 2001 From: jw Date: Sat, 3 Oct 2026 18:59:12 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20#8746=20=E5=87=BA=E5=9B=A2?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E4=B9=A6=E5=8F=AF=E4=B8=8B=E5=8F=91=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E3=80=8C=E9=98=B6=E6=AE=B5=20+=20=E5=9B=9B=E9=A1=B9?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E3=80=8D=E5=B9=B6=E6=96=B0=E5=A2=9E=20releas?= =?UTF-8?q?eBlockers=EF=BC=8C=E9=BB=98=E8=AE=A4=E8=BD=A6=E8=BE=86=E5=B8=A6?= =?UTF-8?q?=E5=8F=B8=E6=9C=BA=EF=BC=8C=E4=BF=9D=E5=AD=98=E7=95=99=E7=97=95?= =?UTF-8?q?=EF=BC=88=E4=BF=AE=E6=94=B9=E6=8E=A5=E5=8F=A3=C2=B7=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=90=8E=E5=8F=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- ...¥书下发门缺项与车辆带司机-修改接口-管理后台.md | 441 ++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 changelogs-v2/2026-10/03_8746_出团通知书下发门缺项与车辆带司机-修改接口-管理后台.md diff --git a/changelogs-v2/2026-10/03_8746_出团通知书下发门缺项与车辆带司机-修改接口-管理后台.md b/changelogs-v2/2026-10/03_8746_出团通知书下发门缺项与车辆带司机-修改接口-管理后台.md new file mode 100644 index 00000000..005399b8 --- /dev/null +++ b/changelogs-v2/2026-10/03_8746_出团通知书下发门缺项与车辆带司机-修改接口-管理后台.md @@ -0,0 +1,441 @@ +--- +schema: "hl-changelog/v2" +ticket: "8746" +title: "出团通知书:可下发改为「阶段 + 四项资源」并新增缺项清单 releaseBlockers,默认车辆信息带司机,保存留痕" +consumer: "admin" +author: "jw(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "mmg" +frontend_ref: "" +target_release: "v2.1" +verified_at: "2026-10-03" +status_note: "已合并 dev-v3(b90969493)并部署 TEST,自签 token 经网关实测:招募中 / 待出发 / 资源准备中 / 已流团 / 脏值逐态缺项、配房配车回落与恢复、缺资源仍可保存、默认车辆带司机且手机全脱敏、一户派车数据异常时跳过该户接口仍 200、保存留痕与撞版本不留痕、无权限角色 589507 零写入。前端待改:通知书弹窗按 releasable 置灰「打印 / 存 PDF」并展示 releaseBlockers[].name。" +updated_at: "2026-10-03" +base: "dev-v3" +--- + +# order-v3: 出团通知书可下发判定补四项资源,新增 releaseBlockers + +**服务**: hl-order-service-v3 +**PR**: `#8770`(已合入 `dev-v3`,合并提交 `b90969493`) +**Issue**: #8746 + +--- + +## ⚠️ 关键变化 + +🔴 **`releasable` 口径变了**:原来团期到「待出发」及之后就是 `true`;现在还要求房 / 车 / 导 / 摄四项资源全部就绪。待出发后房务或车务回落的团,`releasable` 会变成 `false`。 + +🟢 **新增出参 `releaseBlockers`**(GET / PUT 响应都有):不可下发时列出缺哪几项,可下发时为空数组。 + +🟢 **默认车辆信息 `defaults.bus` 带上司机**:每辆车「车型 车牌 司机 姓名 脱敏手机」。 + +🟢 入参、路径、判权、错误码全部不变;保存仍然不卡下发门。 + +--- + +## 一、背景 + +出团通知书「打印 / 存 PDF」按钮靠 `releasable` 置灰。原口径只看团期阶段:团期进入待出发前要先过「四项资源配齐」,但进入之后房务、车务回落不会把团期退回去,于是资源已经缺了的团照样能打印,页面也不知道缺什么。另外默认车辆信息只有车型和车牌,没有司机。 + +本单把「四项资源就绪」加进 `releasable`,并新增 `releaseBlockers` 告诉页面缺哪一项;默认车辆信息补上司机。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 读出团通知书 | GET | `/v3/admin/order/group-batch/:groupBatchId/docs/notice` | 修改 | `releasable` 新口径;新增 `releaseBlockers`;`defaults.bus`(及未保存时正文 `bus`)带司机 | +| 2 | 保存出团通知书 | PUT | `/v3/admin/order/group-batch/:groupBatchId/docs/notice` | 修改 | 响应同上;保存成功在团期时间线新增「保存出团通知书」一条 | + +--- + +## 三、接口详情 + +**`releasable` 规则**(两个接口相同):团期状态是 待出发 `PENDING_DEPARTURE` / 出行中 `TRAVELLING` / 待核单 `PENDING_REVIEW` / 核单中 `REVIEWING` / 已结算 `SETTLED` 之一,**且**配房、配车、配导游、配摄影四项都已完成,才为 `true`。 + +**`releaseBlockers[]` 取值**(按下表顺序排列,`releasable=true` 时为 `[]`,从不为 `null`): + +| key | name | 何时出现 | +|---|---|---| +| `STAGE` | 团期未到待出发 | 团期状态不在上面五个之内 | +| `HOTEL` | 配房未完成 | 已成团且配房未完成 | +| `VEHICLE` | 配车未完成 | 已成团且配车未完成(整团免车算已完成) | +| `GUIDE` | 配导游未完成 | 已成团且配导游未完成(不需要导游的团算已完成) | +| `PHOTOGRAPHER` | 配摄影未完成 | 已成团且配摄影未完成(不需要摄影的团算已完成) | + +- 未成团(招募中 `RECRUITING`、已流团 `CANCELLED`)**只列 `STAGE`**,不列资源项。 +- 已成团但未到待出发(资源准备中、物料准备中):`STAGE` + 缺的资源项。 +- 以后可能追加取值(团车司机,#8767)。遇到不认识的 `key`,按 `name` 展示即可。 + +**`bus` 默认值格式**:每辆车 `车型 车牌 司机 姓名 脱敏手机`,例 `33 座大巴 蒙A·88888 司机 王师傅 138****8888`。 +- 同一辆车多日换过司机,司机之间用 ` / ` 分隔:`33 座大巴 蒙A·88888 司机 王师傅 138****1234 / 赵师傅 136****9999`。 +- 车与车之间仍用 `、`。 +- 司机没有手机号时只有姓名;手机号一律脱敏。 +- 整团派车(团车)的团,车辆信息暂不出现在默认值里(#8767 补)。 +- 已保存的正文不会自动刷新,只有 `defaults` 是实时值。 + +### 1. 读出团通知书 `GET /v3/admin/order/group-batch/:groupBatchId/docs/notice` + +**VO**: `GroupBatchNoticeRespVO`(入参只有路径参数)→ `Result` + +#### 使用场景 + +团期详情「出团通知书」弹窗打开时调用。按 `releasable` 置灰「打印 / 存 PDF」,`releasable=false` 时把 `releaseBlockers[].name` 列给用户看。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | 团期 ID | **不变** | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| releasable | Boolean | 🔄 是否允许打印 / 下发:阶段在可下发集合内且四项资源都已完成(规则见上) | +| releaseBlockers | Object[] | 🆕 不可下发的缺项,可下发时为 `[]` | +| releaseBlockers[].key | String | 🆕 缺项编码:`STAGE` / `HOTEL` / `VEHICLE` / `GUIDE` / `PHOTOGRAPHER` | +| releaseBlockers[].name | String | 🆕 缺项中文名,见上表 | +| bus | String | 🔄 未保存过(`saved=false`)时等于 `defaults.bus`,格式见上;已保存时为保存的原文 | +| defaults.bus | String | 🔄 实时默认车辆信息,每辆车带司机,格式见上 | +| title / greeting / meetTime / meetPlace / leader / contacts / service / bring | String | **不变** | +| saved / version / updateTime / defaults 其余字段 | — | **不变** | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/2106331639531601921/docs/notice HTTP/1.1 +Authorization: Bearer <管理员 token> +``` + +#### 响应示例 + +示例:待出发团期配房回落、从未保存过(草稿)——`releasable=false`,缺项只有 `HOTEL`,`bus` 默认值带司机(缺项与车辆串取自 TEST 验收读数,两步读数拼成一个示例)。 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "title": "冻干粉发短信给 · 出团通知书", + "greeting": "亲爱的团友,欢迎参加本次行程!", + "meetTime": "2026-11-20 08:30", + "meetPlace": "", + "leader": "", + "bus": "mpv 蒙A-G8888 司机 阿拉坦 135****5019、suv 蒙A-E2E01 司机 宝音德力格尔 135****5009、suv 蒙P301A 司机 P3测试司机01 139****0001 / P3测试司机21 139****0021", + "contacts": "", + "service": "", + "bring": "", + "saved": false, + "releasable": false, + "releaseBlockers": [ + { + "key": "HOTEL", + "name": "配房未完成" + } + ], + "version": 0, + "updateTime": null, + "defaults": { + "title": "冻干粉发短信给 · 出团通知书", + "greeting": "亲爱的团友,欢迎参加本次行程!", + "meetTime": "2026-11-20 08:30", + "meetPlace": "", + "leader": "", + "bus": "mpv 蒙A-G8888 司机 阿拉坦 135****5019、suv 蒙A-E2E01 司机 宝音德力格尔 135****5009、suv 蒙P301A 司机 P3测试司机01 139****0001 / P3测试司机21 139****0021", + "contacts": "", + "service": "", + "bring": "" + } + } +} +``` + +四项都已完成时 `releasable=true`、`releaseBlockers=[]`。 + +#### 空数据 / 降级响应 + +- 团内没有逐户派车、或由团车承担:`defaults.bus` 为 `""`。 +- 某一户的派车数据异常:跳过该户,其余户照常拼出车辆信息,接口仍返回 `code=200`。 +- 司机手机号取不到:该司机只显示姓名。 + +#### 错误响应 + +| code | 条件 | +|---|---| +| `589500` | 团期不存在或已删除 | +| `589507` | 当前角色没有 `group-batch:docs` 权限(不变) | + +```json +{ + "code": 589500, + "message": "团期不存在", + "data": null +} +``` + +#### 业务边界 + +- `releasable` 只管「能否打印 / 下发」,不影响读取与保存。 +- 招募中的团只返回 `STAGE` 一项,即使资源都没配。 +- 同一个团在不同时间读,`releasable` 与 `releaseBlockers` 可能不同(房务、车务回落或补齐后会变)。 + +### 2. 保存出团通知书 `PUT /v3/admin/order/group-batch/:groupBatchId/docs/notice` + +**VO**: `GroupBatchNoticeSaveReqVO` → `Result` + +#### 使用场景 + +运营编辑通知书后保存。入参不变;响应与读接口同一个结构,同样带 `releasable` 与 `releaseBlockers`。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| groupBatchId | Path | Long | ✅ | 团期 ID | **不变** | +| title | Body | String | ✅ | ≤128 字 | **不变** | +| greeting | Body | String | ✅ | ≤512 字 | **不变** | +| meetTime | Body | String | ✅ | ≤64 字 | **不变** | +| meetPlace | Body | String | ✅ | ≤256 字 | **不变** | +| leader | Body | String | ✅ | ≤256 字 | **不变** | +| bus | Body | String | ✅ | ≤256 字 | **不变**;默认值带司机后变长,车与司机组合很多时原样保存可能超长,需删减后再存 | +| contacts | Body | String | ✅ | ≤256 字 | **不变** | +| service | Body | String | ✅ | ≤1024 字 | **不变** | +| bring | Body | String | ✅ | ≤1024 字 | **不变** | +| expectedVersion | Body | Integer | ✅ | ≥0 | **不变**,取读接口的 `version` | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| releasable / releaseBlockers | — | 🔄 / 🆕 同读接口 | +| version | Integer | **不变**,保存后的新版本 | +| 其余字段 | — | **不变** | + +#### 请求示例 + +```json +{ + "title": "呼伦贝尔亲子研学 6 日 · 出团通知书", + "greeting": "亲爱的团友,欢迎参加本次行程!", + "meetTime": "2026-11-20 08:30", + "meetPlace": "海拉尔东山国际机场 T1 到达厅 3 号门", + "leader": "李雪梅", + "bus": "33 座大巴 蒙A·88888 司机 王师傅 138****8888", + "contacts": "李雪梅 139****2756", + "service": "含 3 早 6 正餐、全程用车、景区门票", + "bring": "防晒霜、驱蚊液、厚外套", + "expectedVersion": 0 +} +``` + +#### 响应示例 + +示例:招募中团期首次保存(`expectedVersion=0`)——保存成功、`version=1`,缺项只有 `STAGE`(保存不卡下发门)。 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "title": "呼伦贝尔亲子研学 6 日 · 出团通知书", + "greeting": "亲爱的团友,欢迎参加本次行程!", + "meetTime": "2026-11-20 08:30", + "meetPlace": "海拉尔东山国际机场 T1 到达厅 3 号门", + "leader": "李雪梅", + "bus": "33 座大巴 蒙A·88888 司机 王师傅 138****8888", + "contacts": "李雪梅 139****2756", + "service": "含 3 早 6 正餐、全程用车、景区门票", + "bring": "防晒霜、驱蚊液、厚外套", + "saved": true, + "releasable": false, + "releaseBlockers": [ + { + "key": "STAGE", + "name": "团期未到待出发" + } + ], + "version": 1, + "updateTime": "2026-10-03T18:33:10", + "defaults": { + "title": "冻干粉发短信给 · 出团通知书", + "greeting": "亲爱的团友,欢迎参加本次行程!", + "meetTime": "2026-11-20 08:30", + "meetPlace": "", + "leader": "", + "bus": "", + "contacts": "", + "service": "", + "bring": "" + } + } +} +``` + +#### 空数据 / 降级响应 + +- 不涉及;保存成功即返回保存后的全量。 + +#### 错误响应 + +| code | 条件 | +|---|---| +| `589500` | 团期不存在 | +| `589507` | 当前角色没有 `group-batch:docs` 权限(不变) | +| `589585` | 版本冲突(别人先保存了),请重读后再存(不变) | +| `589587` | 正文含证件号形态的数字(不变) | +| 参数校验失败 | 字段超长或缺失(不变) | + +```json +{ + "code": 589585, + "message": "通知书已被他人修改,请刷新后重试", + "data": null +} +``` + +#### 业务边界 + +- 保存**不卡**下发门:招募中、资源未齐都能保存,响应里照样带缺项。 +- 保存成功后,团期时间线(`GET /v3/admin/order/group-batch/:groupBatchId/status-logs`)新增一条「保存出团通知书」,内容如「保存出团通知书(第 3 版)」。保存失败(版本冲突、证件号拦截、参数校验)不新增。 +- 打印不经后端,不留痕。 + +--- + +## 四、契约约束与正确调用方式 + +- 「打印 / 存 PDF」按 `releasable` 置灰;`releasable=false` 时展示 `releaseBlockers[].name`。判断用 `key`,不要用中文名。 +- 不要自己根据团期状态推算能否打印,以 `releasable` 为准。 +- 遇到不认识的 `key`(以后会加团车司机),按 `name` 展示。 +- 车辆信息里的司机拼在 `bus` 字符串里,不需要新输入框。 + +--- + +## 五、数据库行为 + +- 零表结构变更、零数据迁移。 +- 读接口零写入。 +- 保存接口:正文写入不变;成功后额外在团期时间线新增一条「保存出团通知书」记录。 + +--- + +## 六、边界行为 + +- 团期状态是历史脏值(不在已知状态内):`releasable=false`,只列 `STAGE`,不报错。 +- 四项资源中任何一项为空值(历史数据)按「未完成」处理。 +- 时间线记录写失败不影响保存结果。 + +## 六.6、修改前后对比 + +| 场景 | 改前 | 改后 | +|---|---|---| +| 待出发,四项都已完成 | `releasable=true` | `releasable=true`,`releaseBlockers=[]` | +| 待出发,配房回落未完成 | `releasable=true`(照样能打印) | `releasable=false`,`[HOTEL]` | +| 招募中 | `releasable=false` | `releasable=false`,`[STAGE]` | +| 资源准备中,配车未完成 | `releasable=false` | `releasable=false`,`[STAGE, VEHICLE]` | +| 默认车辆信息 | `33 座大巴 蒙A·88888` | `33 座大巴 蒙A·88888 司机 王师傅 138****8888` | +| 保存成功 | 时间线无记录 | 时间线新增「保存出团通知书(第 N 版)」 | + +## 六.7、影响评估 + +- **是否破坏向后兼容**:`releaseBlockers` 是纯新增字段;`releasable` 在「待出发后资源回落」时由 `true` 变 `false`,旧页面会置灰按钮但看不到原因。 +- **前端是否必须同步上线**:建议同步展示 `releaseBlockers`;不改也不会报错。 +- **回滚**:revert PR #8770 后重新部署 order-v3。 + +--- + +## 七、不影响范围 + +- 两个接口的路径、入参、判权(`group-batch:docs`)、错误码:不变。 +- 已保存的通知书正文:不会被改写。 +- 团期时间线读接口的结构:不变,只是多了一种事件「保存出团通知书」。 +- 小程序端:无影响。 + +--- + +## 八、测试环境已验证 + +**环境**:TEST(`https://api.test.1814.love`) **验证时间**:2026-10-03 18:14~18:46 +**构建身份**:order-v3 部署 `dev-v3 @ b90969493`(本单合并提交),18:08:56 完成;部署状态表 order-v3 行为 `dev-v3 b90969493 ok`。零写入判据:部署后连查 8 次读接口,每次响应都带 `releaseBlockers` 键(旧字节没有)。 +**身份**:自签 token 直打网关,用 TEST 真实账号 ID 配对应角色。 + +### 8.1 造数 + +载体产品「冻干粉发短信给」上新建班期「11月20日海拉尔-额尔古纳4日团」(出发 2026-11-20),下两单(各 2 成人)进同一团期 `2106331639531601921`。待出发 / 资源回落用 SQL 改团期状态与四项资源完成标记模拟(与房务、车务回落的落库效果相同),每步后恢复。 + +### 8.2 下发门与缺项 + +| 团期状态 | 未完成项 | `releasable` | `releaseBlockers` | +|---|---|---|---| +| 招募中 | 四项全未完成 | `false` | `[STAGE]` | +| 待出发 | 无 | `true` | `[]`(4 次读一致) | +| 待出发 | 配房 | `false` | `[HOTEL]`,恢复后回到 `true` / `[]` | +| 待出发 | 配车 | `false` | `[VEHICLE]` | +| 资源准备中 | 配摄影 | `false` | `[STAGE, PHOTOGRAPHER]` | +| 已流团 | 配房、配车 | `false` | `[STAGE]` | +| 历史脏值 `PENDING_TRIP` | — | `false` | `[STAGE]`,接口 200 | + +缺资源时保存照常成功(`version` 2 → 3,响应带 `[HOTEL]`)。 + +### 8.3 默认车辆带司机 + +把真实派车数据复制到两户名下(手机号按新订单重新加密):`defaults.bus` 出 3 辆车、4 名司机,8 次读一致;4 个手机号均为 `ddd****dddd` 形态,前三后四与独立解密结果一致;同一辆车两天两名司机用「 / 」并列。两个实例在时间窗内的日志明文手机号零命中。 + +### 8.4 一户派车数据异常 + +两户中一户的派车数据改为不自洽:8 次读全部 `code=200`,`bus` 只含正常户的车;两个实例各记 4 条「已跳过该户」告警,无事务回滚异常。清理后 `bus` 回到空。 + +### 8.5 保存留痕 + +| 操作 | 结果 | +|---|---| +| 首次保存 | `version=1`;时间线新增「保存出团通知书(第 1 版)」,操作人 jw | +| 用旧版本号再存 | `589585`,时间线条数与版本号不变 | +| 用新版本号再存 | `version=2`;新增「第 2 版」 | + +### 8.6 判权 + +| 调用方 | 读 | 存 | +|---|---|---| +| 不带 token | 网关 `401` | 网关 `401` | +| 车务、财务(无 `group-batch:docs`) | `589507` | `589507`,零写入 | +| 管理员、团期管理员 | `200` | `200`,版本 +1 | + +### 本地证据 + +| 项 | 读数 | +|---|---| +| 定向 3 类(含 5 个内嵌类) | 63/0/0 | +| 合并提交复跑 6 类 | 97/0/0 | +| 团期包 + archunit 包 + 全模块架构测试(有 Docker) | 3851 例 5 失败 2 错误,全部在基底 `d778c9a71` 干净工作区逐条复现,本单零新增 | + +### 未覆盖 + +- TEST 上没有「待出发、逐户派车、在团户未取消」的现成团,默认车辆带司机用复制的真实派车数据验证。 +- 房务、车务回落用 SQL 模拟落库效果,真实回落业务路径未走。 +- 团车(整团派车)的车辆与司机不在本单(#8767)。 + +--- + +## 十、相关文档 + +- Issue `#8746`;PR `#8770` +- 拆出:Issue `#8767`(团车车辆与司机进通知书、`DRIVER` 缺项) +- 前置:Issue `#7532`(出团通知书首版) + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#8746](https://git.1814.love/wx/HL/issues/8746) +- **PR**: [#8770](https://git.1814.love/wx/HL/pulls/8770) +- **Merge commit**: [b90969493](https://git.1814.love/wx/HL/commit/b90969493) + +### 联系人 + +- **后端负责人**: @jw