---
schema: "hl-changelog/v2"
ticket: "5613"
title: "派单恢复取消500修复：DAILY_V3快照拓扑按最终方案实际覆盖槽位"
consumer: "admin"
author: "wx"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "not_required"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: ""
status_note: "后端完成：PR #5623 合并 dev-v3（c10aea0f0）并部署 TEST（hl-fleet-service + hl-order-service-v3）；网关实测派单 2085286010924527618 取消后 restore-cancel 返回 200 assignmentStatus=assigned，DAILY_V3 快照 revision 1 同步成功，order 侧 requirement DONE、planFleetItemIndexes=[1]、planTopologySize=4。"
updated_at: "2026-08-07"
base: "dev-v3"
generated: "2026-08-06T19:45:00+08:00"
---

# 派单恢复取消500修复：DAILY_V3快照拓扑按最终方案实际覆盖槽位

> 服务端已部署 TEST 并网关验证；管理后台契约不变，无强制前端改动。

## 关联 / 联系人

### 链接

- **Issue**: [#5613](https://git.1814.love:8443/wx/HL/issues/5613)
- **PR**: [#5623](https://git.1814.love:8443/wx/HL/pulls/5623)
- **Merge commit**: [c10aea0f0](https://git.1814.love:8443/wx/HL/commit/c10aea0f0)

### 联系人

- **后端负责人**: @wx

## 变更接口

| 方法 | 路径 | 来源 | 变更 |
|---|---|---|---|
| `POST` | `/admin/fleet/assignments/<assignmentId>/restore-cancel` | `AssignmentController`（fleet） | **语义修复**：多槽需求只保留部分槽位时恢复取消不再 500（修复前 `IllegalStateException: DAILY_V3 snapshot invalid: daily rows do not cover declared topology`，事务回滚、状态保持 canceled）；恢复成功返回 `assignmentStatus=assigned`、车辆/司机占用回 busy。接口出入参不变 |

> 内部契约（同步放宽，随本单一起部署）：`POST /v3/internal/order/vehicle-assignment/callback`（DAILY_V3 配车快照回写）的 `planFleetItemIndexes` 校验由「必须等于需求声明全槽」放宽为「需求全槽的非空子集」。即车务最终方案只保留部分槽位（未保留槽位按「车务最终实派方案未保留该车辆槽位」取消）时，快照拓扑只含实际保留槽位，订单侧正常接受并回写 requirement 快照摘要。

## 契约影响文件

- `hl-fleet-service/src/main/java/com/hulalv/fleet/assignment/snapshot/service/DailyVehicleAssignmentSnapshotFactory.java`（快照拓扑按最终方案实际覆盖槽位收窄；每保留槽×每服务日一行、至少一个用车 cell 等 fail-closed 校验不变）
- `hl-order-service-v3/src/main/java/com/hulalv/order/requirement/service/DailyVehicleAssignmentSnapshotService.java`（`validateAgainstRequirement`：ASSIGNED 快照 `planFleetItemIndexes` 允许为需求全槽的非空子集；拓扑完整性仍由 `isDailySnapshotValid` 强制）

## 根因与修复说明

- **根因**：需求声明多槽（如 suv+mpv × 4 天 = 拓扑 8），但车务最终方案允许只保留部分槽位——未保留槽位的 unassigned 占位按「车务最终实派方案未保留该车辆槽位」取消且 `dispatch_plan_finalized=0`，不参与当前最终代。恢复取消（restore-cancel）在事务 `BEFORE_COMMIT` 触发 DAILY_V3 快照生成时，快照工厂仍按 `requirement.fleet` 全槽拓扑校验当前 final 行（4 行 ≠ 8）→ `IllegalStateException` → 500，恢复事务回滚，取消后无法恢复。
- **修复**：① fleet 快照工厂的拓扑以当前最终方案**实际覆盖的槽位**为准（`planFleetItemIndexes`/`planTopologySize`/`dailyAssignments` 一致），未保留槽位不再要求行覆盖；② order 侧回调校验同步接受需求全槽的非空子集。
- **影响范围**：仅影响「多槽需求 + 部分槽位最终方案」场景（修复前该场景任何快照生成都会失败，不止恢复取消）；全槽最终方案与 NO_VEHICLE_REQUIRED 行为不变。

## 前端/调用方动作

1. **无强制改动**：restore-cancel 接口与出入参不变；修复前该场景直接 500，现在正常返回。
2. **展示留意**：多槽需求只派部分槽位时，订单侧快照摘要 `assignmentPlanFleetItemIndexes` 现在会正常回写为保留槽子集（修复前该场景快照从不成功、需求卡 PROCESSING）。若管理后台/订单详情按需求全槽渲染车辆槽位，可结合快照拓扑判断哪些槽位实际派车、哪些未保留。

## 验证证据

- 定向测试：fleet `DailyVehicleAssignmentSnapshotFactoryTest` 11/11（+2：部分槽位快照成功 / 保留槽缺日仍 fail-closed）；order `DailyVehicleAssignmentSnapshotServiceTest` 14/14（+2：子集 APPLIED / 超集拒绝）
- 回归：`AssignmentServiceTest` 376、`DailyVehicleSnapshotGenerationServiceTest` 15、`RequirementServiceTest` 198、`InternalRequirementControllerTest` 14、`VehicleAssignmentSnapshotContractRedTest` 7、`E2eVehicleAssignmentFinalizeServiceTest` 46 全绿
- fleet 全量 `mvn -pl hl-fleet-service -am verify`（含 spotless）BUILD SUCCESS；order-v3 verify 的 11 个失败类经与 #5610 worktree 基线报告逐类对比完全一致（Testcontainers/MyBatis 环境问题），非本单引入
- 网关验证（TEST，经 api.test.1814.love:9443）：派单 2085286010924527618（需求 2 槽 × 08-20~23、最终方案只保留 mpv 槽）取消后 `POST /admin/fleet/assignments/2085286010924527618/restore-cancel` → **200** `assignmentStatus=assigned`，4 行恢复 assigned 且 `canceled_at` 清空；DAILY_V3 快照 outbox revision 1 投递 SUCCESS；order 侧 requirement 回 DONE、`planFleetItemIndexes=[1]`、`planTopologySize=4`、`usedVehicleDayCount=4`，`order_vehicle_assignment` 4 行（mpv 槽 08-20~23，车辆/司机/费用齐全）
