docs(changelog): 车务派车候选接口显式返回 selectable(#5850 / PR #5861)
所有检测均成功
changelog-filename-gate / validate (push) Successful in 2s

这个提交包含在:
API Changelog Bot 2026-08-11 17:38:27 +08:00
父节点 231f2e0411
当前提交 05f0f548d2

查看文件

@ -0,0 +1,111 @@
---
schema: "hl-changelog/v2"
ticket: "5850"
title: "派车候选接口显式返回 selectable统一选车弹窗空白复发的后端根治四条组装路径全覆盖"
consumer: "admin"
change_type: "修改接口"
author: "wx(GIT)"
backend_status: "deployed"
gateway_status: "not_required"
frontend_status: "not_required"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "PR #5861 已合并 dev-v3 并部署测试服,网关 9443 实测:候选车辆 20 行 / 司机 20 行 selectable 全部非 null19/20 车 true,1 行因阻断性冲突为 false。前端 not_required——mmg 已于 313609db 把 normalizeCandidateVehicle 的缺省口径改成「undefined 视为可选、显式 false 才禁用」,与本次后端显式下发兼容;本条属于把前端的兜底假设升级成后端契约保证,前端零改动即受益。"
updated_at: "2026-08-11"
base: "dev-v3"
---
# 车务:派车候选接口显式返回 `selectable`#5850
> **服务**: hl-fleet-service
> **端点**: `POST /admin/fleet/assignments/candidates`
> **性质**: 新增响应字段(向后兼容,无破坏性变更)
---
## 一、为什么改
「统一选择车辆/司机」弹窗**反复出现整片空白**(后端返回 200、数据齐全,列表却渲染不出来
根因在前后端口径断层:
- 前端统一选车入口(`scope=slot``availableOnly=true`)用 `isFullyAvailableCandidate()` 过滤候选,该函数**要求 `selectable === true`**;
- 但后端从来**没有下发过 `selectable` 字段**(全仓零命中),前端 `normalize` 出来是 `undefined`
- `undefined === true` 为 false → **整列被过滤成空**。单日入口 `availableOnly=false` 不走该过滤,所以只在统一选车弹窗复发。
前端已在 `313609db` 做了兜底(缺省视为可选)。但「弹窗空白」这个故障此前已复发多次,只靠前端约定"没有字段就当可选"是脆的——本次由后端把这个字段变成**显式契约**,从源头消除断层。
---
## 二、变更接口
| 方法 | 路径 | 变更 |
|------|------|------|
| `POST` | `/admin/fleet/assignments/candidates` | 响应新增 `selectable` 字段(车辆候选项 + 司机候选项,共 4 条组装路径) |
## 三、契约变更
`AssignmentCandidateRespVO` 的车辆候选项与司机候选项各**新增一个字段**
| 字段 | 类型 | 说明 |
|------|------|------|
| `selectable` | `Boolean` | 本次选车场景下该候选是否可被选中。**恒非 null** |
### 与已有 `available` 的区别(务必分清)
| 字段 | 口径 |
|------|------|
| `available` | **纯资源维度**:该车/该司机在所选服务日期窗内是否空闲 |
| `selectable` | 在 `available` 为真的基础上,**再排除阻断性冲突** |
后端判定式:
```
selectable = available && conflicts 中不存在阻断项
```
其中「阻断项」= `conflicts[].blocking` **不是显式 `false`** 的项一律按阻断处理null/缺省从严)。
> ⚠️ 「已被本弹窗其他槽位选中」属于**前端选车上下文**,后端不感知,**不纳入** `selectable` 口径。前端如需这层排除,仍按自己的选中态叠加过滤。
### 覆盖范围
四条候选组装路径**全部**下发该字段,不存在只有部分路径带字段的情况:
1. `vehicles.records[]` —— 车辆候选分页列表
2. `drivers.records[]` —— 司机候选分页列表
3. `selectedDriverResidentVehicles[]` —— 选定司机的常驻车辆
4. `selectedVehicleResidentDriver` —— 选定车辆的常驻司机
---
## 四、前端影响
**无破坏性变更,前端可零改动。** 但建议按下面的口径收敛:
- 渲染禁用态时**直接读 `selectable`**,不要再自己用 `available` + `conflicts` 二次推导——推导口径与后端不一致正是这次故障的来源。
- 保留「`undefined` 视为可选」的兜底(`313609db` 已做)作为防御,但正常情况下该字段恒有值。
- `selectable === false` 的行建议**置灰并展示 `availabilityReason`**,而不是从列表里过滤掉——车管需要知道"这辆车为什么不能选"。
---
## 五、验证证据测试环境实测,2026-08-11,网关 9443,车务管理员 token
```
POST /admin/fleet/assignments/candidates
```
| 项 | 实测 |
|----|------|
| `vehicles.records[]` | 20 行,`selectable` **全部非 null**;19 行 `true`,1 行 `false`(存在阻断性冲突) |
| `drivers.records[]` | 20 行,`selectable` 全部非 null |
| 兼容性 | `available` / `availabilityReasonCode` / `availabilityReason` / `conflicts` 语义与取值**均未变化** |
---
## 六、关联
- 工单 #5850、PR #5861
- 前端条目:`11_frontend_派单Step2需求展示区与统一选车弹窗空白-前端缺陷-管理后台.md`(同一故障的前端侧修复 `313609db`