From fb009895453ac43e42b8de286099f175a1275757 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Thu, 24 Sep 2026 09:57:31 +0800 Subject: [PATCH] =?UTF-8?q?changelog(8294):=20=E5=9B=A2=E6=9C=9F=E9=85=8D?= =?UTF-8?q?=E8=BD=A6=E5=B0=B1=E7=BB=AA=E6=A3=80=E6=9F=A5=E5=BA=A7=E4=BD=8D?= =?UTF-8?q?=E4=B8=8D=E8=B6=B3=E9=BB=84=E7=89=8C=E6=89=A3=E5=8F=B8=E6=9C=BA?= =?UTF-8?q?=E5=BA=A7=EF=BC=8C=E6=96=B0=E5=A2=9E=20passengerSeatTotal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 接口:GET /admin/fleet/group-dispatch/batches/{groupBatchId}/readiness - WARN_SEAT_SHORTAGE 判据改为「可载客数合计 < 该日用车人数」(每车扣 1 个司机座); - 新增响应字段 passengerSeatTotal;gap 改按它计算;seatTotal 语义与数值不变; - message 改写,写明「含司机座」与「已扣司机座后可载客 N 人」; - 会新增黄牌:座位合计 >= 用车人数 > 可载客数合计。 Refs #8294 --- ...‰Œ扣司机座,新增-passengerSeatTotal-修改接口-管理后台.md | 306 ++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 changelogs-v2/2026-09/24_8294_团期配车就绪检查座位不足黄牌扣司机座,新增-passengerSeatTotal-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/24_8294_团期配车就绪检查座位不足黄牌扣司机座,新增-passengerSeatTotal-修改接口-管理后台.md b/changelogs-v2/2026-09/24_8294_团期配车就绪检查座位不足黄牌扣司机座,新增-passengerSeatTotal-修改接口-管理后台.md new file mode 100644 index 00000000..f99b8581 --- /dev/null +++ b/changelogs-v2/2026-09/24_8294_团期配车就绪检查座位不足黄牌扣司机座,新增-passengerSeatTotal-修改接口-管理后台.md @@ -0,0 +1,306 @@ +--- +schema: "hl-changelog/v2" +ticket: "8294" +title: "团期配车就绪检查「座位不足」黄牌改为扣司机座,新增 passengerSeatTotal" +consumer: "admin" +author: "wx(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "PR #8305 已合并 dev-v3(2a3b9df59)。部署:hl-fleet-service dev-v3 @ 2a3b9df59,2026-09-24 09:48:52 起滚,09:50:13 完成,8087/8187 两实例均 UP;实测 09:52 晚于部署完成时刻。测试服网关真实 GET /admin/fleet/group-dispatch/batches/{groupBatchId}/readiness:团期 2101514348969226242 返回 WARN_SEAT_SHORTAGE,seatTotal=7、passengerSeatTotal=6、headcount=40、gap=34(7-1=6 证明扣座、40-6=34 证明 gap 按新口径);团期 2102066067272826881 座位合计 0 时 passengerSeatTotal=0 不为负;团期 2101997326354862082(座位合计 5/可载客 4/人数 4)与 2101690789438570497(7/6/6)warned=false,证明取等号时不误报。fleet 定向单测 778/0/0(含 GroupDispatchReadinessServiceTest 26、VehicleSeatCapacityTest 4、FleetRedLineArchTest 18)+ spotless:check 绿;回退判据后 3 个用例转红(含「20 座车 20 人应报缺 1 座」),恢复后转绿。gateway_status: verified —— 路径本就在既有 admin-fleet-service 路由 Path=/admin/fleet/** 下,本次零路由改动,并已通过真实网关实测。frontend_status: pending —— 前端需读新字段 passengerSeatTotal,且 gap 的语义已变(改为 用车人数 − 已扣司机座的可载客数),若仍按「用车人数 − seatTotal」渲染会差 1×车数。" +updated_at: "2026-09-24" +base: "dev-v3" +--- + +# fleet 团期配车就绪检查: 座位不足黄牌改为扣司机座 + +> **存放目录**: 二期(v3) → `changelogs-v2/2026-09/` +> +> **服务**: hl-fleet-service(团期配车读口) +> **PR**: #8305 +> **Issue**: #8294 +> **日期**: 2026-09-24 +> **影响范围**: 管理后台「团期详情 → 配车」页的就绪检查提示(黄牌文案与字段) + +--- + +## ⚠️ 关键变化(非必须,本版与上版行为不同 / 纠错 / 撤销时必写) + +- 本次变化:`GET /admin/fleet/group-dispatch/batches/{groupBatchId}/readiness` 响应里 `warnings[]` 中 `code=WARN_SEAT_SHORTAGE` 的条目,**判据由「座位合计(含司机座)< 该日用车人数」改为「可载客数合计 < 该日用车人数」**。每车可载乘客数 = 座位数 − 1(扣 1 个司机座)。口径与团级用车需求 809116(#8278,PR #8296)、fleet 单车派车完全一致。 +- 前端以前以为的:`seatTotal` 就是「能坐多少人」,`gap = headcount − seatTotal`。**现在 `gap` 不再按 `seatTotal` 算**——同一响应里 `seatTotal + gap ≠ headcount` 是正常的,必须叠加新字段 `passengerSeatTotal` 才闭合(`passengerSeatTotal + gap = headcount`)。 +- 实际现在的行为:判据字段是 **`passengerSeatTotal`**;`seatTotal` **原义与数值都未变**(仍是含司机座的座位合计,可以直接继续展示「N 座车」)。 +- **会新增黄牌**:`座位合计 ≥ 用车人数 > 可载客数合计` 这个区间以前不报、现在报。最小例子:1 辆 20 座车、该日 20 人——改前不报,改后报「缺 1 座」(司机没座)。存量数据不重算,下一次读取即按新口径。 +- `message` 文案改写(**前端如果做文本匹配会失效**):旧「`2027-06-10 分组 MAIN 座位合计 7,该日用车人数 40,缺 33 座`」→ 新「`2027-06-10 分组 MAIN 座位合计 7 座(含司机座),已扣司机座后可载客 6 人,该日用车人数 40,缺 34 座`」。 +- **黄牌语义不变**:仍然只提醒、不阻断,`ready` 与 `warned` 仍互相独立,`ready=true && warned=true` 依然合法。 + +--- + +## 一、背景(选填) + +#8278 已定案:团级用车需求的容量一律按「每车扣 1 个司机座」算(`VehicleSeatCalculator`),订单子级与 fleet 单车派车(预检告警、候选容量)本来就是这个口径。唯一没对齐的是 fleet 的**团级**就绪检查——它直接累加 `vehicle.getSeats()`,把「20 座车塞 20 人」判成够。同一份排法在团级需求侧已被 809116 拦下,在配车就绪页却显示「够」,运营无法判断该信哪一个。本单让第三处(也是最后一处)向已有口径看齐,而不是放松另外两处。 + +最强反例(评审已确认,留档):「刚好坐满、司机另开一辆车」这类排法在新口径下会多出一条黄牌。它只是提醒不是硬拦,且这类配置在业务上本就不成立(司机座不能卖给乘客);被提醒是纠正而不是误伤。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 团期配车就绪检查 | GET | `/admin/fleet/group-dispatch/batches/{groupBatchId}/readiness` | 响应新增字段 + 字段语义变更 + 文案变更 | `warnings[]` 中 `WARN_SEAT_SHORTAGE` 条目新增 `passengerSeatTotal`,`gap` 改按它计算,`message` 改写 | + +**本次只动这一个端点**(同一控制器的其余端点未改)。 + +--- + +## 三、接口详情 + +### 1. 团期配车就绪检查 `GET /admin/fleet/group-dispatch/batches/{groupBatchId}/readiness` + +**VO**: `GroupDispatchReadinessRespVO → GroupDispatchReadinessItemVO[]` + +#### 使用场景 + +团期详情「配车」页进入时拉取,用于展示硬拦(不能置 vehicle_ready)与黄牌(能发车但有缺口)。请求参数与响应整体结构均不变,本条只改「只提醒」数组里座位不足那一档的字段与文案。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|---|---|---|---|---|---| +| `groupBatchId` | Path | Long | 是 | 团期主订单 ID | 路径参数 | + +#### 出参 `Result` + +**顶层字段**(本次未变): + +| 字段 | 类型 | 说明 | +|---|---|---| +| `groupBatchId` | String(Long) | 团期主订单 ID | +| `requirementId` | String(Long) | 判定所依据的正式需求 ID | +| `requirementVersion` | Integer | 判定所依据的需求版本 | +| `planVersion` | Long | fleet 侧当前计划版本(该团尚无任何计划行时为 null) | +| `ready` | Boolean | 硬拦三项是否全过(`= blockers.isEmpty()`) | +| `warned` | Boolean | 是否有只提醒项(`= !warnings.isEmpty()`) | +| `blockers` | Array | 硬拦未过项 | +| `warnings` | Array | 只提醒项 | +| `groups` | Array | 逐组覆盖明细(与配车写口 `coverage` 同源,逐字段可比对) | +| `shareGroupCount` | Integer | 本团 active 同团车辆共用关系数(只供展示,不参与判定) | + +**`warnings[]` 元素(`GroupDispatchReadinessItemVO`)**: + +| 字段 | 类型 | 说明 | +|---|---|---| +| `code` | String | 判定项代码:`WARN_SEAT_SHORTAGE` / `WARN_DRIVER_MISSING` | +| `message` | String | 中文描述(本次改写,见下表) | +| `tripDate` | LocalDate | 相关行程日 | +| `groupCode` | String | 相关乘车分组码 | +| `seatTotal` | Integer | 该日该组的座位合计(含司机座);仅座位不足档有值 | +| `passengerSeatTotal` | Integer | **新增**:该日该组的可载客数合计(Σ max(0, seats − 1));仅座位不足档有值 | +| `headcount` | Integer | 该日该组的用车人数;仅座位不足档有值 | +| `gap` | Integer | 座位缺口(本次改按 `passengerSeatTotal` 计算);仅座位不足档有值 | +| `dispatchId` | String(Long) | 相关配车行 ID;仅司机缺失档有值 | + +**本次逐字段变化(仅 `WARN_SEAT_SHORTAGE` 档)**: + +| 字段 | 类型 | 本次变化 | 说明 | +|---|---|---|---| +| `code` | String | 不变 | `WARN_SEAT_SHORTAGE` / `WARN_DRIVER_MISSING` | +| `message` | String | **改写** | 座位不足档现在写明「含司机座」与「已扣司机座后可载客 N 人」 | +| `tripDate` | LocalDate | 不变 | 相关行程日 | +| `groupCode` | String | 不变 | 相关乘车分组码 | +| `seatTotal` | Integer | **语义不变** | 该日该组活跃配车行的**座位合计(含司机座)**,数值与改前逐字相同 | +| `passengerSeatTotal` | Integer | **新增** | 该日该组的**可载客数合计** = Σ `max(0, seats − 1)`,座位数取不到的车按 0 计,恒 ≥ 0 | +| `headcount` | Integer | 不变 | 该日该组用车人数 | +| `gap` | Integer | **语义变更** | 由 `headcount − seatTotal` 改为 `headcount − passengerSeatTotal`(恒 ≥ 1) | +| `dispatchId` | String(Long) | 不变 | 仅 `WARN_DRIVER_MISSING` 有值 | + +以上与座位相关的五项**仅 `WARN_SEAT_SHORTAGE` 档有值**;`WARN_DRIVER_MISSING` 档这五项均为 `null`(含新增的 `passengerSeatTotal`)。 + +#### 请求示例 + +```http +GET /admin/fleet/group-dispatch/batches/2101514348969226242/readiness +Authorization: Bearer +``` + +#### 响应示例 + +测试服真实响应(2026-09-24 09:52,`hl-fleet-service` dev-v3 @ `2a3b9df59`,节选该接口 `warnings` 内容): + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "groupBatchId": "2101514348969226242", + "ready": true, + "warned": true, + "planVersion": 4, + "warnings": [ + { + "code": "WARN_SEAT_SHORTAGE", + "message": "2027-06-10 分组 MAIN 座位合计 7 座(含司机座),已扣司机座后可载客 6 人,该日用车人数 40,缺 34 座", + "tripDate": "2027-06-10", + "groupCode": "MAIN", + "seatTotal": 7, + "passengerSeatTotal": 6, + "headcount": 40, + "gap": 34, + "dispatchId": null + } + ] + } +} +``` + +对照:`seatTotal(7) + gap(34) = 41 ≠ headcount(40)`;`passengerSeatTotal(6) + gap(34) = 40 = headcount` —— **前端做守恒校验请用后者**。 + +#### 空数据 / 降级响应 + +- 无缺口时 `warnings: []`、`warned: false`,其余字段照常返回。 +- 该团未声明任何乘车分组或基线不可用:`code=602113`,**失败关闭**,绝不返回 `ready=true`。 +- 入参非法:`code=602114`。 + +#### 错误响应 + +```json +{ "code": 602113, "message": "...", "success": false, "data": null } +``` + +#### 业务边界 + +- 座位不足**只提醒**:`ready` 只看 `blockers`,不受本档影响;两条写路径(配车方向判定、就绪意图发射)只调 `hardGatesPass`,该入口完全不看座位。 +- 扣座是**逐车**的:N 辆车扣 N 个司机座(两辆 20 座车 / 39 人 ⇒ 可载客 38、缺 1 座),不是全团只扣 1 个。 +- 座位数取不到(车已软删 / 车型未录座位数)的车按 0 计,可载客数**不出现负数**。 +- 按「组 + 行程日」逐格比:同一组不同日期分别判定,不同组分别判定。 + +--- + +## 四、契约约束与正确调用方式(接口类必写) + +### ✅ 正确 / ❌ 错误用法对照 + +| 场景 | ✅ 正确 | ❌ 错误 | +|---|---|---| +| 判断「够不够」 | 后端已给结论:`warned` / `warnings` 是否为空 | 前端自己用 `seatTotal >= headcount` 重算 | +| 展示缺口 | 直接展示 `gap`,或展示 `headcount − passengerSeatTotal` | 用 `headcount − seatTotal` 现算(会少 1×车数) | +| 展示运力 | `passengerSeatTotal`(可载客)与 `seatTotal`(车辆座位规格)分开显示 | 把 `seatTotal` 当成可载客数展示 | +| 文案 | 直接展示后端 `message` | 按旧文案做字符串匹配/替换 | + +### 切换状态时的必要动作 + +无。本接口是只读 GET,不改变任何状态,也不触发重算。 + +--- + +## 五、数据库行为(涉及写操作时必写) + +无写入。判据全部基于既有列在读取时现算(活跃配车行 × 车辆座位数 × 需求逐日人数),**不落库、不新增列、无迁移**。 + +--- + +## 六、边界行为 + +| 边界 | 行为 | +|---|---| +| `headcount = 0` | 不报(`0 >= 0`) | +| 座位数取不到(车软删 / 未录) | 该车按 0 计,缺口如实报出 | +| 座位数为 0 或 1 | 可载客数 0,不出现负数 | +| 恰好坐满(可载客数 == 人数) | 不报(取等号判「够」) | +| 该日没有排车 | 该日可载客数 0;整组没排车由硬拦 `BLOCK_GROUP_MISSING` 承接 | +| 需求已 DONE | 仍返回 `ready=true`(DONE 比 CONFIRMED/DISPATCHED 更靠后) | + +--- + +## 六.5、枚举 / 数据字典(接口出现枚举时必写) + +`warnings[].code` 取值不变,仍为两档: + +| 取值 | 含义 | 相关字段 | +|---|---|---| +| `WARN_SEAT_SHORTAGE` | 该日该组可载客数不足 | `tripDate` / `groupCode` / `seatTotal` / `passengerSeatTotal` / `headcount` / `gap` | +| `WARN_DRIVER_MISSING` | 配车行未排司机(消息带车牌) | `tripDate` / `groupCode` / `dispatchId`,其余为 `null` | + +**无新增错误码。** + +--- + +## 六.6、修改前后对比(修改/删除类接口必写,新增跳过) + +### 字段级对比(`warnings[]` 中 `WARN_SEAT_SHORTAGE` 条目) + +| 字段 | 修改前 | 修改后 | +|---|---|---| +| `seatTotal` | 座位合计(含司机座) | **不变**(同值同义) | +| `passengerSeatTotal` | 不存在 | **新增**,`Σ max(0, seats − 1)` | +| `gap` | `headcount − seatTotal` | `headcount − passengerSeatTotal` | +| `message` | `{日期} 分组 {组} 座位合计 {N},该日用车人数 {M},缺 {K} 座` | `{日期} 分组 {组} 座位合计 {N} 座(含司机座),已扣司机座后可载客 {P} 人,该日用车人数 {M},缺 {G} 座` | + +### 行为级对比 + +| 行为 | 修改前 | 修改后 | +|---|---|---| +| 1 辆 20 座车 / 该日 20 人 | 不报(20 ≥ 20) | **报,缺 1 座** | +| 1 辆 20 座车 / 该日 19 人 | 不报 | 不报(可载客 19 ≥ 19) | +| 2 辆 20 座车 / 该日 39 人 | 不报(40 ≥ 39) | **报,缺 1 座**(可载客 38) | +| 1 辆 7 座车 / 该日 40 人 | 报,`seatTotal=7`、`gap=33` | 报,`seatTotal=7`、`passengerSeatTotal=6`、`gap=34` | +| 座位数为 0 / 取不到 | 报,`seatTotal=0`、`gap=人数` | 报,`passengerSeatTotal=0`、`gap=人数`(不为负) | +| `ready` | 不受本档影响 | **不变**(仍只提醒) | + +--- + +## 六.7、影响评估(修改/删除类必写) + +- **变宽**:`座位合计 ≥ 用车人数 > 可载客数合计` 这个区间由「不报」变「报」。区间宽度恰是「该日排的车数」(每车多算 1 个司机座),所以车越多、越容易落进来。 +- **不变**:`gap` 的**值**在「座位取不到」和「原本就严重不足」的场景里可能不变;但在「刚好卡边界」的场景会 +1×车数。任何拿 `gap` 做阈值判断的前端逻辑都要复核。 +- **后端消费方**:`getSeatTotal()` / `getGap()` 在 HL 全仓(Java)生产代码中消费方为 **0**(只有测试引用),本接口的消费方是管理后台前端。 +- **存量数据不重算**:不落库,下一次读取即按新口径;测试服现有 116 条活跃配车行、34 个团期中,**没有**落在新增黄牌区间(`用车人数 == 座位合计`)的活跃分组,本次口径变更对既有数据的可见影响为 0。 +- 前置依赖:本单与 #8278 是同一口径的第三处收口,**不改变** 809116、子订单级校验、候选容量的任何行为。 + +--- + +## 七、不影响范围(显式声明, 帮前端/QA 缩小排查面) + +- `blockers[]` 三档与 `ready` 的判定逻辑:**零改动**。 +- `groups[]`、`shareGroupCount`、`planVersion`、`requirementVersion` 等字段:**零改动**。 +- 同一控制器的其余端点(`/pending-batches`、`/batches/{id}/overview`、`/resource-schedule`):**零改动**。 +- 配车写口(提交 / 确认 / 改派 / 删除)与 `coverage`:**零改动**。 +- 单车派车的预检告警、候选容量、候选页 `passengerCapacity` 字段:**行为零改动**(只把内部重复实现收口到一份工具方法)。 +- 团级用车需求 809116(#8278 已交付)与 order-v3:**行为零改动**(仅同步了两处已失效的注释)。 +- 数据库:无迁移、无新列。 + +--- + +## 八、测试环境已验证 + +- **定向测试**:`mvn -o -pl hl-fleet-service -am test -Dtest='GroupDispatchReadinessServiceTest*,VehicleSeatCapacityTest*,AssignmentCandidateServiceTest*,AssignmentServiceTest*,AssignmentServiceNoVehicleDeclarationTest*,AssignmentServicePickupDropoffTest*,AssignmentServiceClearCancelledOccupancyTest*,AssignmentServiceResolveExceptionTest*,FleetRedLineArchTest' -DfailIfNoTests=false -Dhl.surefire.failIfNoTests=false` → **778 / 0 / 0,BUILD SUCCESS**(逐类核对 `Tests run`,报告文件时间均晚于本轮起跑)。 +- **分辨力**:把判据临时还原为旧口径后 3 个用例转红(含「20 座车 20 人应报缺 1 座」),恢复后 30/0/0 转绿。 +- **网关验证**:`GET https://api.test.1814.love/admin/fleet/group-dispatch/batches/{groupBatchId}/readiness`(fleet dev-v3 @ `2a3b9df59`,09:52 实测)——团期 `2101514348969226242` 返回 `WARN_SEAT_SHORTAGE`(`seatTotal=7`、`passengerSeatTotal=6`、`headcount=40`、`gap=34`);团期 `2102066067272826881` 座位合计 0 时 `passengerSeatTotal=0`;团期 `2101997326354862082`(5/4/4)与 `2101690789438570497`(7/6/6)`warned=false`。全程只读 GET。 +- **兼容性结论**:`seatTotal` 语义与数值不变,新增字段为增量、老前端不会因缺字段崩溃;但**任何用 `seatTotal` 反算缺口的旧逻辑会差 1×车数**,必须改读 `passengerSeatTotal`(或直接用 `gap`)。 + +--- + +## 十、相关文档 + +- 接口契约:`hl-fleet-service/src/main/java/com/hulalv/fleet/dispatch/controller/GroupDispatchQueryController.java` +- 响应结构:`hl-fleet-service/src/main/java/com/hulalv/fleet/dispatch/vo/GroupDispatchReadinessItemVO.java` +- 口径工具:`hl-fleet-service/src/main/java/com/hulalv/fleet/common/util/VehicleSeatCapacity.java` +- 同口径前置单:#8278 / PR #8296(团级 809116 扣司机座),本单是其 `changelogs-v2/2026-09/23_8278_团级用车分组座位校验扣司机座-修改接口-管理后台.md` 里声明的「fleet 团级就绪检查仍不扣座、由 #8294 跟踪」的收口。 + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#8294](https://git.1814.love/wx/HL/issues/8294) +- **PR**: [#8305](https://git.1814.love/wx/HL/pulls/8305) +- **Merge commit**: [2a3b9df59](https://git.1814.love/wx/HL/commit/2a3b9df591255be691694023812b3878debfb70e) + +### 联系人 + +- **后端负责人**: @wx