docs(changelog): 调整订单「车辆安排」页同页提交行程用车+接送机用车两类需求(#7443)
后端已合入 dev-v3(PR #8024 -> 920f29d76)并部署测试服,四条真实网关调用取证: 读口双槽(带全 null 阳性对照) / 一次 submit 同交两份落两条 active 行 / 两条 label 原文不同的调整记录 / 无大交通时 809002。 service_dates 两个 kind 分别派生成行程三天与接机送机两天,前端一个日期都不传。 覆盖边界写在正文与 status_note 里: 本文只验到「提交」为止。同一次实测观测到 TRANSFER 需求同步车务的 outbox 恒失败 605905(fleet AssignmentService:11492 取当前需求不带 kind),前端可并行开工但端到端尚未打通。
这个提交包含在:
@@ -0,0 +1,162 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "7443"
|
||||||
|
title: "调整订单「车辆安排」页同页提交 行程用车(TRAVEL) + 接送机用车(TRANSFER) 两类需求"
|
||||||
|
consumer: "admin"
|
||||||
|
author: "wx(GIT)"
|
||||||
|
change_type: "修改接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "verified"
|
||||||
|
frontend_status: "pending"
|
||||||
|
frontend_owner: "mmg"
|
||||||
|
frontend_ref: ""
|
||||||
|
target_release: ""
|
||||||
|
verified_at: "2026-09-20"
|
||||||
|
status_note: "后端已合入 dev-v3(PR #8024,squash 提交 920f29d76)并部署测试服:deploy-status.sh 回读 hl-order-service-v3 = dev-v3 @ 920f29d76、BEHIND=0/N、STATE=ok,两实例滚动重启均 UP,判据 git merge-base --is-ancestor 920f29d76 920f29d76 = true。gateway_status=verified 的依据是四条真实网关调用而非源码推断:①读口双槽(含阳性对照:未提交需求的订单两个字段都是 null,证明不是恒有值)②一次 submit 同时提交两份 → HTTP 200,落库两条 active 行,fleet 分别为 bus/19座/1台 与 mpv/7座/2台 ③调整记录两条 label 原文不同 ④无大交通时 809002。🔴 一条必须连着读的限定:本文只验证了「提交」这一段。同一次实测观测到 TRANSFER 需求同步给车务的 outbox 命令持续失败(order_fleet_command_outbox command_type=RECONCILE,TRAVEL=SUCCEEDED 而 TRANSFER=PENDING/retry_count=2,last_error_message='Fleet 用车需求换版失败: code=605905, message=需求版本过期'),根因是 hl-fleet-service AssignmentService:11492 requireCurrentVehicleRequirementForMutation 取当前需求时不带 kind、恒取 TRAVEL,与 TRANSFER 的 requirementId 比对必然不等。即:前端按本文接完即可正常提交并回显,但提交出去的接送机需求在修复该缺陷之前到不了车务侧,端到端业务尚未打通。该缺陷已在修(属 #7990 那一族),修好后另发交接件,不影响本文的前端契约。"
|
||||||
|
updated_at: "2026-09-20"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# order-v3: 调整订单「车辆安排」页同页提交 行程用车 + 接送机用车 两类需求
|
||||||
|
|
||||||
|
> **服务**: hl-order-service-v3(adjustment 层,唯一变化点)
|
||||||
|
> **影响范围**: 管理后台「调整订单 → 车辆安排」页。此前该页**结构上只能提交行程用车**,本次补上接送机用车的读口与写口。
|
||||||
|
|
||||||
|
## 这次修的是什么
|
||||||
|
|
||||||
|
「车辆安排」页此前只有一个用车需求槽。页面上那张接送机卡片(航班号、抵离时间、接送备注)
|
||||||
|
来自 `vehicleTransportSummary`,它是**只读的大交通摘要**——没有车型、座位数、数量输入,
|
||||||
|
也没有对应的提交字段。于是业务上「接机用什么车、用几辆」**没有任何录入通道**。
|
||||||
|
|
||||||
|
后端的用车需求模型本身早就支持两类并存:表 `order_vehicle_requirement` 的
|
||||||
|
`requirement_kind` 取 `TRAVEL`(行程用车,服务日=行程日)/ `TRANSFER`(接送机,服务日=航班日),
|
||||||
|
唯一键 `(order_id, active_kind)` ⇒ **同一订单两类 active 需求合法并存,各有自己的 `fleet` 与 `service_dates`**。
|
||||||
|
缺口只在 adjustment 这一层把 kind 焊死成了 TRAVEL。本次把它透出来。
|
||||||
|
|
||||||
|
## 接口变更
|
||||||
|
|
||||||
|
### 1. `GET /v3/admin/order/{orderId}/adjustment/snapshot`(读)
|
||||||
|
|
||||||
|
**新增响应字段 `transferRequirement`**,类型与既有 `vehicleRequirement` 完全相同。
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| `vehicleRequirement` | **行为不变**,仍是 `TRAVEL`(行程用车)。⚠️ 字段名与类型均未改动,现网页面无需调整 |
|
||||||
|
| `transferRequirement` | **新增**,`TRANSFER`(接送机用车)。该订单没有接送机需求时为 `null` |
|
||||||
|
|
||||||
|
两个字段的结构(同一个 VO):
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | String | 需求 id |
|
||||||
|
| `version` | Integer | 版本号 |
|
||||||
|
| `status` | String | 需求状态(如 `PENDING_REVIEW`) |
|
||||||
|
| `fleet` | Array | **车辆组合**,元素 `{vehicleType, seats, count}` —— 即「用什么车、几座、几辆」 |
|
||||||
|
| `specialTags` | Array<String> | 特殊诉求标签 |
|
||||||
|
| `pickupRequired` | Boolean | 是否需要平台接机/接站 |
|
||||||
|
| `dropoffRequired` | Boolean | 是否需要平台送机/送站 |
|
||||||
|
| `remark` | String | 备注 |
|
||||||
|
| `claimerId` / `claimerName` / `claimedAt` | - | 抢单人信息 |
|
||||||
|
| `vehicleType` / `requiredSeats` | - | ⚠️ **旧兼容字段,恒为 `null`**。车型与座位已迁入 `fleet`,不要再读这两个 |
|
||||||
|
|
||||||
|
`vehicleTransportSummary`(大交通摘要)**保持原样不变**,仍是只读展示用。
|
||||||
|
|
||||||
|
### 2. `POST /v3/admin/order/{orderId}/adjustment/submit`(写)
|
||||||
|
|
||||||
|
**`updates` 下新增 `transferRequirement`**,结构与既有 `updates.vehicleRequirement` 完全相同
|
||||||
|
(`fleet` / `specialTags` / `pickupRequired` / `dropoffRequired` / `remark`)。
|
||||||
|
|
||||||
|
- 只传 `vehicleRequirement` → **行为与改动前逐字节一致**(行程用车)
|
||||||
|
- 只传 `transferRequirement` → 只提交接送机用车
|
||||||
|
- **两个都传 → 一次提交落两条需求**,这正是「同页提交」要的效果
|
||||||
|
|
||||||
|
调整记录(`adjustment-record`)里两者会产出**可区分的两条** `VEHICLE_REQ` 条目:
|
||||||
|
`行程用车需求已调整` / `接送机用车需求已调整`。改动前恒为「车辆需求已调整」,两条无法区分。
|
||||||
|
|
||||||
|
🔴 **`serviceDates` 前端不传**。服务日由后端派生:`TRAVEL` 取行程日,`TRANSFER` 取**大交通的航班/车次日期**
|
||||||
|
(客人可能提前一天到、返程后一天走,所以允许落在行程日窗口之外)。
|
||||||
|
|
||||||
|
## 🔴 前端必须处理的前置:没有大交通时提交接送机需求会失败
|
||||||
|
|
||||||
|
`TRANSFER` 的服务日来自大交通,**空集合不会兜底成行程日**(那样会把航班日写错、
|
||||||
|
派车日期落到需求单声明范围之外)。因此该订单没有录入大交通行程时,
|
||||||
|
提交 `transferRequirement` 会返回 **`809002`**。
|
||||||
|
|
||||||
|
⇒ **建议前端在没有大交通时把接送机那一段的输入置灰**,并提示「请先录入大交通行程」,
|
||||||
|
不要让用户填完再吃一个错误码。判断依据用同一份快照里的 `vehicleTransportSummary.hasPickupTime`
|
||||||
|
(为 `false` 时它还会给出 `emptyText`,当前文案是「暂无接送机时间」)。
|
||||||
|
|
||||||
|
⚠️ 两份同时提交而接送机这半抛 809002 时,**整笔调整回滚**(一次 submit = 一笔原子调整),
|
||||||
|
行程用车那半也不会落库。所以置灰比事后补救重要。
|
||||||
|
|
||||||
|
## 环境开关
|
||||||
|
|
||||||
|
`hl.order.requirement.transfer-kind-submit-enabled`
|
||||||
|
- **代码默认 `false`**;生产环境未开,提交 `kind=TRANSFER` 返 `809009`
|
||||||
|
- **测试服已置 `true`**(2026-09-19 15:33:32 发布,随 order-v3 重启生效)
|
||||||
|
- ⚠️ 该配置无 `@RefreshScope`,改完必须重启服务才生效
|
||||||
|
|
||||||
|
## 联调样本
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| 订单 | `2101219133700952066`(订单号 `HL20260919155734730`,散客单) |
|
||||||
|
| 特征 | 已录大交通 2 条;`TRAVEL` 与 `TRANSFER` 两类需求**均已存在**,可直接用来验双槽回显 |
|
||||||
|
|
||||||
|
## 实测读数(2026-09-20,测试服真实网关调用)
|
||||||
|
|
||||||
|
后端版本:`hl-order-service-v3` @ **`920f29d76`**(PR #8024 squash 合入 dev-v3)。
|
||||||
|
|
||||||
|
**① 读口双槽 —— 带阳性对照**
|
||||||
|
|
||||||
|
| 订单 | `vehicleRequirement` | `transferRequirement` |
|
||||||
|
|---|---|---|
|
||||||
|
| `2101566624467419137`(提交前) | `null` | `null` |
|
||||||
|
| `2101219133700952066`(两类都有) | `{id:2101219223517659138, fleet:[{mpv,7,1}]}` | `{id:2101219393722634242, fleet:[{mpv,7,1}]}` |
|
||||||
|
|
||||||
|
⚠️ 第二行两者的 `fleet` 恰好相同(样本单本身如此),**所以"两个字段不同"这件事不能拿来证明按 kind 取数生效**;
|
||||||
|
真正证明它的是第一行那个全 `null` 的阳性对照,以及两者 `requirementId` 不同。
|
||||||
|
|
||||||
|
**② 同页提交(本文的核心)**
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /v3/admin/order/2101566624467419137/adjustment/submit
|
||||||
|
updates.vehicleRequirement = {fleet:[{vehicleType:"bus", seats:19, count:1}]}
|
||||||
|
updates.transferRequirement = {fleet:[{vehicleType:"mpv", seats:7, count:2}]}
|
||||||
|
→ HTTP 200 {"code":200,"data":{"success":true}}
|
||||||
|
```
|
||||||
|
|
||||||
|
落库 `order_vehicle_requirement` 两条 active 行:
|
||||||
|
|
||||||
|
| `active_kind` | `fleet` | `service_dates` |
|
||||||
|
|---|---|---|
|
||||||
|
| `TRAVEL` | `[{"count":1,"seats":19,"vehicleType":"bus"}]` | `["2026-10-20","2026-10-21","2026-10-22"]`(行程三天) |
|
||||||
|
| `TRANSFER` | `[{"count":2,"seats":7,"vehicleType":"mpv"}]` | `["2026-10-20","2026-10-23"]`(**接机日 + 送机日**) |
|
||||||
|
|
||||||
|
🔴 **两个 `service_dates` 一个都不是前端传的**,全部由后端按 kind 派生——这是「前端不传日期」这条约定的硬证据。
|
||||||
|
|
||||||
|
**③ 调整记录可区分**
|
||||||
|
|
||||||
|
`GET /v3/admin/order/{id}/adjustment-record` → 本次 `changeCount:2`,两条 `type=VEHICLE_REQ`,
|
||||||
|
`label` 分别是 `行程用车需求已调整` / `接送机用车需求已调整`。改动前两条 label 完全相同、事后分不出改的是哪一份。
|
||||||
|
|
||||||
|
**④ 无大交通的失败形态**
|
||||||
|
|
||||||
|
订单 `2101567681155207169`(同产品、已付款、**无大交通**),只提交 `transferRequirement`:
|
||||||
|
→ HTTP **200** + `{"code":809002,"message":"接送机需求缺少服务日期,请先补齐大交通信息"}`
|
||||||
|
|
||||||
|
(提交前已确认测试服 Nacos `hl.order.requirement.transfer-kind-submit-enabled: true`,
|
||||||
|
所以这个 809002 不是被开关挡住的假象。)
|
||||||
|
|
||||||
|
## 🔴 本文的覆盖边界:只验到「提交」为止
|
||||||
|
|
||||||
|
同一次实测里观测到:TRANSFER 需求同步给车务的 outbox 命令**持续失败**
|
||||||
|
(`order_fleet_command_outbox`,`command_type=RECONCILE`:TRAVEL `SUCCEEDED`,
|
||||||
|
**TRANSFER `PENDING` / `retry_count=2` / `last_error_message="Fleet 用车需求换版失败: code=605905, message=需求版本过期"`**)。
|
||||||
|
|
||||||
|
⇒ **前端按本文接完即可正常提交并回显,但提交出去的接送机需求目前到不了车务侧。**
|
||||||
|
该缺陷在 fleet(`AssignmentService:11492` 取当前需求不带 kind、恒取 TRAVEL),已在修,
|
||||||
|
修好后另发交接件。**它不改变本文的前端契约**,可以并行开工。
|
||||||
|
|
||||||
|
⚠️ 写下这段是因为「四条实测全达成」这个汇总句**丢掉边界之后会变强**——
|
||||||
|
会被读成「接送机用车已经端到端可用」,而那句话今天还不成立。
|
||||||
在新工单中引用
屏蔽一个用户