---
schema: "hl-changelog/v2"
ticket: "5595"
title: "全程行换司机不再被605909误拦（历史跨日派单迁移判定放宽 + 存量迁移工具）"
consumer: "admin"
change_type: "修改接口"
author: "wx(GIT)"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "not_required"
frontend_owner: ""
frontend_ref: ""
target_release: "v2.1"
verified_at: "2026-08-06"
status_note: "后端完成：PR #5602/#5607/#5609 已合并 dev-v3 并部署 TEST（15:52 滚动 DONE）；迁移工具已执行（5/5 存量行 group_id 回清）；网关验证三轮换司机全 PASS：change 200（修复前 605909）、旧行 canceled、新行 assigned 全程形态、退旧投新占用正确。前端无需配合。"
updated_at: "2026-08-06"
base: "dev-v3"
generated: "2026-08-06T16:10:00+08:00"
---

# 全程行换司机不再被 605909 误拦（历史跨日派单迁移判定放宽 + 存量迁移工具）

> **服务**: hl-fleet-service
> **PR**: [#5602](https://git.1814.love:8443/wx/HL/pulls/5602)、[#5607](https://git.1814.love:8443/wx/HL/pulls/5607)、[#5609](https://git.1814.love:8443/wx/HL/pulls/5609)
> **Issue**: [#5595](https://git.1814.love:8443/wx/HL/issues/5595)
> **日期**: 2026-08-06
> **影响**: 🟢 **缺陷修复**，无契约变更（`POST /admin/fleet/assignments/{assignmentId}/change` 行为修复）。已派/待确认**全程行**（#5562 全程槽模型，`service_date` 为 NULL、`start_date~end_date` 覆盖整个服务期）此前 change 被 605909「发现未完成迁移的历史跨日派单」拦截，本次修复后换车/换司机/退旧投新全链路可用。前端无需改代码。

## 背景

#5595（P1）E2E 实测：档期冲突A 已派全程行（`2085213644705927170`，assigned，8/20~8/23）换司机 → 605909 拦截，换人走不通。

**根因**（两条链叠加）：
1. #5589 在 `consumePlaceholderToActive` 把全程行升级回填 `assignment_group_id=自身行 id`，使 #5562 全程槽模型的全程行（`group_id` 应为 NULL）被 `ensureDailySlices`/`normalizeActiveSlotRows` 误判为「历史跨日聚合行」→ change/confirm 触发 605909；
2. 即使迁移清回 `group_id`，change 整槽替换产物（`group_id=新组`、`service_date` NULL）在**再次 change** 时仍因 `isFullTripRow`（要求 `group_id IS NULL`）与 `cancelActiveSlotRange`（全程行分支要求 `group_id IS NULL`）被误判 → 605909/605020。

## 修复内容（内部，无接口/字段变化）

1. **占位消费不再回填组 id**：`consumePlaceholderToActive` 移除 `assignment_group_id=自身行 id` 回填，全程行升级保持 `group_id IS NULL`（#5562 模型一致）；HOLD 通知单行组命中由 `selectByAssignmentGroupId`/`ForUpdate`/`markHoldNotificationSent` 的 `assignment_id + group_id IS NULL` 兜底保证（#5589 能力保留）；
2. **全程形态行判定放宽**：`isFullTripRow` 从「`group_id IS NULL` 且多日」放宽为「`service_date IS NULL` 且 `start/end` 完整」——change 整槽替换产物（带新组 id）同样按全程行处理；`ensureDailySlices`/`normalizeActiveSlotRows` 对全程形态行豁免拆天，不再触发 605909；
3. **改派保持全程形态**：`buildReplacementSlice` 对全程形态行保持 `service_date` NULL、`start/end` 覆盖整个服务期（修复前被压成 effectiveDate 单日切片，丢服务期剩余日期）；change 冲突检查/日志范围覆盖整个服务期；
4. **整槽取消放宽**：`cancelActiveSlotRange` 全程行分支由「`service_date IS NULL AND group_id IS NULL`」放宽为「`service_date IS NULL`」（外层已限定槽位匹配）；
5. **存量迁移工具**：`hl-workflow/tools/migrate_5595_fulltrip_single_row_group.py`——把 #5589 回填产生的「单行组全程行」（`group_id=自身`、`service_date` NULL、多日、in-flight）幂等回清 `group_id=NULL`，带 manifest 回滚；TEST 已执行 5/5。

## 接口行为

- `POST /admin/fleet/assignments/{assignmentId}/change`（换车/换司机）：全程形态行（assigned/holding）**200**，旧行 canceled、新行 assigned 且保持全程形态（`service_date` NULL、`start/end` 覆盖全程），退旧投新占用正确（旧司机释放、新司机建立）。
- 历史跨日聚合行（`service_date` 非 NULL 的旧版多日行）仍按 605909 保护（#5318 语义保留）。
