---
schema: "hl-changelog/v2"
ticket: "4933"
title: "矩阵空闲格候选排除车辆日期冲突"
consumer: "admin"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "not_required"
frontend_status: "implemented"
frontend_owner: "hl-ui-pi"
frontend_ref: "27b5161696add9bab725611900888488b95ff510"
target_release: ""
verified_at: "2026-07-28T17:51:52+08:00"
status_note: "后端月度未派清单契约未变；本次交接前端按所选车辆空闲闭区间过滤候选。"
updated_at: "2026-07-28"
base: "dev-v3"
---

# 车务矩阵：空闲格候选必须排除车辆日期冲突

> **服务**：`hl-fleet-service`（既有接口，无后端代码变更）
>
> **Issue**：#4933 的矩阵空闲格直接派单前端实现回归
>
> **日期**：2026-07-28
>
> **影响范围**：管理后台车务矩阵点击车辆空闲格后的“选择当前矩阵可派订单”抽屉

## 关键结论

`GET /admin/fleet/matrix/unassigned-orders` 的既有语义是返回当前年月及车型筛选下的**全局未派订单池**，不是针对某辆车某段空闲日期计算后的候选集合。前端点击空闲格时已经持有 `vehicleId`、`clickedDate`、`startDate`、`endDate` 和该车辆矩阵占用，但当前抽屉只按“未绑定车辆”过滤，因此会展示完整用车区间不在空闲区间内、甚至与该车已有派车日期重叠的订单。

本次不要求后端新增字段或改变全局未派池语义。前端必须在打开空闲格抽屉时，根据点击上下文和当前矩阵车辆占用过滤候选；派单提交时仍由后端在资源锁内执行最终冲突重校验。

## 变更接口

| 方法 | 路径 | 后端结构变化 | 前端正确消费方式 |
|---|---|---|---|
| GET | `/admin/fleet/matrix/unassigned-orders` | 无；仍传 `year`、`month`、可选 `typeKeys[]`，仍返回当月全局有效未派行 | 仅作为候选数据源；进入某辆车空闲格抽屉前，结合点击上下文和该车矩阵占用做闭区间资格过滤 |
| GET | `/admin/fleet/matrix/grid` | 无 | 继续作为车辆、派车段和空闲区间的数据源，不新增前端猜测的返程日、缓冲日或状态 |

### 前端过滤不变量

1. 候选订单的全部有效 `serviceDateSegments[]` 必须完整落入所选空闲闭区间 `[startDate, endDate]`；只有后端未提供该数组字段时，才兼容使用订单 `startDate/endDate`。
2. 候选完整有效日期与所选车辆当前矩阵占用不得有任一日期重叠；首日、末日及边界同日均按占用处理。
3. 不允许为了让候选“可派”而把订单完整区间静默裁成空闲区间交集。候选不满足完整容纳条件时，应直接从抽屉排除。
4. 前端过滤不得替代创建或改派接口的后端锁内冲突校验；矩阵快照过期时以后端写侧拒绝为准。
5. 取消派车继续按矩阵现有有效性口径排除；不得自行增加返程后缓冲日或改变派车状态语义。

### 验收矩阵

以下日期均按闭区间判断：

| 场景 | 已有占用 | 候选完整区间 | 所选空闲区间 | 预期 |
|---|---|---|---|---|
| 候选起始日重叠 | 08-03～08-05 | 08-05～08-08 | 08-06～08-31 | 排除 |
| 候选结束日重叠 | 08-08～08-10 | 08-05～08-08 | 08-01～08-07 | 排除 |
| 候选包含已有占用 | 08-05～08-06 | 08-04～08-07 | 08-01～08-31 | 排除 |
| 候选被已有占用包含 | 08-03～08-09 | 08-04～08-07 | 08-01～08-31 | 排除 |
| 边界相邻但不重叠 | 08-03～08-05 | 08-06～08-07 | 08-06～08-31 | 保留 |
| 完整区间不在空闲区间 | 无额外占用 | 08-04～08-07 | 08-06～08-31 | 排除，不得裁成 08-06～08-07 |
| 无冲突且完整容纳 | 08-03～08-05 | 08-29～08-31 | 08-06～08-31 | 保留 |

## 运行复现

测试页面 `fleet/matrix` 的 2026-08 月矩阵中：

- 点击车辆 `蒙C05E05` 的 `2026-08-21` 空闲格；页面明确给出的空闲区间是 `2026-08-06～2026-08-31`。
- 抽屉仍显示订单 `26-4700`，完整区间为 `2026-08-04～2026-08-07`。
- 同一车辆既有订单 `26-4338` 的逐日用车详情显示 `2026-08-03`、`2026-08-04`、`2026-08-05` 均为已规划用车。
- 因而 `26-4700` 既未被空闲区间完整容纳，又在 08-04、08-05 与该车已有占用重叠，必须排除。
- 同抽屉订单 `26-5568` 的完整区间为 `2026-08-29～2026-08-31`，应继续保留。

## 验证证据

- 运行页面已复现当前失败：空闲格抽屉显示 `共 2 单`，其中包含不合格的 `26-4700`。
- 运行订单详情已核实车辆 `蒙C05E05` 在 08-03～08-05 的逐日用车事实，不是根据甘特位置猜测日期。
- `hl-ui origin/v2.1@e744c9096a6807439b3e4514a7687cf2f058b3fc`：
  - `useFleetMatrixData.js` 调用月度未派接口时仅传 `year/month/typeKeys`；
  - `index.vue` 的 `idleAssignableOrders` 仅过滤 `!order.vehicle`；
  - `matrixAssignmentContext.js` 已构造车辆和空闲区间上下文，但当前只在选中订单后用于派单弹窗。
- `wx/HL origin/dev-v3@883fc61a006f6d7e40ca1c8723ffc6d28e37bf75`：`MatrixUnassignedReqVO` 只有 `year/month/typeKeys`，`MatrixService#queryUnassignedOrders` 按月返回全局未派行，当前行为符合既有接口契约。
- 前端修复、上述七类单测、发布及页面复核尚未执行，因此 `frontend_status` 保持 `pending`。

## 不影响范围

- 不修改后端请求/响应字段、错误码或状态机。
- 不修改派单创建、改派的车辆锁、司机锁和闭区间冲突校验。
- 不修改全局“打开未派订单窗口”的月度未派池展示；本规则仅用于从具体车辆空闲格进入的候选抽屉。
- 不涉及生产或数据库写入。
