diff --git a/changelogs-v2/2026-08/03_5364_连续执行段司机确认隔离-修改接口-管理后台.md b/changelogs-v2/2026-08/03_5364_连续执行段司机确认隔离-修改接口-管理后台.md new file mode 100644 index 0000000..518f0af --- /dev/null +++ b/changelogs-v2/2026-08/03_5364_连续执行段司机确认隔离-修改接口-管理后台.md @@ -0,0 +1,174 @@ +--- +schema: "hl-changelog/v2" +ticket: "5364" +title: "连续执行段司机通知、回复与整组确认隔离" +consumer: "admin" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "not_required" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "后端 PR #5420 已 squash 合并到 dev-v3(e543a41035);最终本地 Fleet 2820 项与 Order 7261 项 reactor verify 均通过。本工单冻结范围不要求运行时部署或网关验证;前端尚未领取,保持 pending。" +updated_at: "2026-08-03" +base: "dev-v3" +--- + +# 连续执行段司机通知、回复与整组确认隔离(#5364) + +> **服务**:`hl-fleet-service`、`hl-order-service-v3` +> +> **后端 PR**:[#5420](https://git.1814.love:8443/wx/HL/pulls/5420) +> +> **Backend Issue**:[#5364](https://git.1814.love:8443/wx/HL/issues/5364) +> +> **Frontend tracking**:[#5366](https://git.1814.love:8443/wx/HL/issues/5366) + +## 变更接口 + +| 接口 | 方法 | 路径 | 变更类型 | +|---|---|---|---| +| 查询车务派车订单详情 | `GET` | `/admin/fleet/board/orders/:orderId` | additive 响应字段 | +| 登记司机确认 | `POST` | `/admin/fleet/assignments/:assignmentId/driver-confirmation` | additive 请求字段与段级约束 | +| 用车需求整组原子确认 | `POST` | `/admin/fleet/assignments/requirements/:requirementId/confirm` | 新增接口 | +| 旧单派车确认 | `POST` | `/admin/fleet/assignments/:assignmentId/confirm` | multi-group 行为收紧,singleton 兼容 | +| 司机 H5 行程单 JSON | `GET` | `/app/h5/itinerary/:token` | additive/versioned 响应字段与段级裁剪 | + +## 1. Board 详情字段 + +`GET /admin/fleet/board/orders/:orderId` 的既有字段保持不变,新增字段均为 additive/null-safe。 + +### 1.1 顶层需求快照 + +| 字段 | JSON 类型 | 可空 | 说明 | +|---|---|---|---| +| `requirementId` | `String(Long)` | 是 | 当前有效用车需求 ID | +| `requirementVersion` | `Integer` | 是 | 当前需求版本 | +| `requirementSha256` | `String` | 是 | 当前需求 canonical SHA-256;整组确认时原样回传 | +| `driverConfirmationSummary` | `Object` | 否 | 当前 generation 的服务端守恒汇总 | + +`driverConfirmationSummary`: + +| 字段 | JSON 类型 | 说明 | +|---|---|---| +| `dispatchPlanGeneration` | `String(Long)` / `null` | 当前最终派车方案代际 | +| `requiredSegmentCount` | `Integer` | 当前需司机确认的 HOLD 执行段数 | +| `confirmedSegmentCount` | `Integer` | 已满足有效确认谓词的执行段数 | +| `pendingSegmentCount` | `Integer` | 待确认且非歧义段数 | +| `rejectedSegmentCount` | `Integer` | 当前拒绝且待改派段数 | +| `ambiguousSegmentCount` | `Integer` | 通知结果歧义段数 | +| `allDriverConfirmed` | `Boolean` | 所有 required 段是否均确认 | +| `allExecutionConfirmed` | `Boolean` | 全部实际用车切片是否均确认执行 | + +守恒规则:`confirmedSegmentCount + pendingSegmentCount + rejectedSegmentCount + ambiguousSegmentCount = requiredSegmentCount`。消费者不得使用 `currentAssignment` 或任一代表司机自行计算整组完成。 + +### 1.2 `activeAssignments[]` 执行段字段 + +每项按独立 `assignmentGroupId` 返回;同槽多段、同司机非连续多段不得合并。 + +| 字段 | JSON 类型 | 可空 | 语义 | +|---|---|---|---| +| `assignmentGroupId` | `String(Long)` | 否 | 当前连续执行段身份 | +| `assignmentSlotId` | `String(Long)` | 否 | 稳定车辆槽位身份 | +| `serviceDates[]` | `String(date)[]` | 否 | 本段精确服务日,不补日期洞 | +| `holdNotificationGeneration` | `String(Long)` | 是 | 当前段通知代际 | +| `holdNotificationStatus` | `String` | 否 | `NOT_REQUIRED/PENDING/SENT/FAILED/AMBIGUOUS/INVALIDATED` | +| `holdSentAt` | `String(date-time)` | 是 | 有可信发送成功事实时才返回 | +| `driverConfirmedAt` | `String(date-time)` | 是 | 当前段司机确认时间 | +| `driverReplyNote` | `String` | 是 | 回复摘要 | +| `driverReplySource` | `String` | 是 | `NOTIFICATION/MANUAL_CONTACT`;历史未知保持 `null` | +| `driverReplyOperatorId` | `String(Long)` | 是 | 人工登记操作人;历史未知可空 | +| `driverReplyRecordedAt` | `String(date-time)` | 是 | 回复事实登记时间 | +| `driverConfirmationEvidencePresent` | `Boolean` | 否 | 是否存在确认凭证 | +| `driverConfirmationEvidenceCount` | `Integer` | 否 | 凭证数量,不新增文件元数据 | + +`SENT` 只表示系统发送成功,不表示通道已提供送达回执;前端不得展示为 `DELIVERED`。 + +## 2. 登记司机确认 + +`POST /admin/fleet/assignments/:assignmentId/driver-confirmation` 请求新增: + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `driverReplySource` | `String` | 否 | 仅允许 `NOTIFICATION` 或 `MANUAL_CONTACT`;人工电话/微信确认必须显式传 `MANUAL_CONTACT` | + +既有 `requestId`、`driverReplyNote`、`evidenceFileIds[]` 保持兼容。登记只更新 `assignmentId` 所属当前 group;旧 group、旧 generation、已改派/取消或资源快照漂移均 fail closed,不得跟随 slot 更新替代组。 + +## 3. 用车需求整组原子确认 + +新增 `POST /admin/fleet/assignments/requirements/:requirementId/confirm`。 + +请求体: + +```json +{ + "orderId": "2080000000000000001", + "requestId": "requirement-confirm-5364-001", + "expectedRequirementVersion": 7, + "expectedRequirementSha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "expectedPlanGeneration": "12", + "groups": [ + { + "assignmentGroupId": "2080000000000000101", + "sendItinerarySms": true + } + ] +} +``` + +- `groups[]` 必须是 Board 当前 generation 中全部 used execution groups 的精确集合;按 `assignmentGroupId` 去重。 +- 服务端重读 exact slot×service-date topology 并一次提交;失败不产生部分状态推进、receipt 或重复 Outbox。 +- `(requirementId, requestId)` 是 durable 幂等键:同 canonical 请求返回首次稳定结果;同键不同载荷返回 `605059`。 +- 旧 `/:assignmentId/confirm` 在 multi-group 当前 generation 返回 `605057`;singleton 继续委托同一原子引擎。 + +主要业务错误: + +| code | 含义 | +|---|---| +| `605055` | 派车方案 generation 已变化 | +| `605056` | 执行段精确集合已变化 | +| `605057` | multi-group 必须使用需求级整组确认 | +| `605058` | 至少一个当前执行段不满足最终确认条件 | +| `605059` | 幂等 requestId 已用于不同确认载荷 | + +## 4. H5 段级最小化 + +`GET /app/h5/itinerary/:token` 新增/versioned 字段: + +| 字段 | 类型 | 说明 | +|---|---|---| +| `contractVersion` | `String` | 当前为 `V2` | +| `segmentDays` | `Integer` | token 所绑定执行段天数 | +| `serviceDates[]` | `String(date)[]` | 本段精确服务日期 | +| `segmentRoute` | `String` / `null` | 仅由本段逐日标题生成 | + +既有 `days`、`route`、`daily`、`transport` 字段不删除且原语义不静默改写。V2 token 同时绑定 group、slot、plan generation、notification generation、driver、vehicle、精确日期摘要和 audience snapshot;任一漂移都返回失效,禁止按 slot 跳转到新司机或新 group。 + +## 5. 前端消费动作 + +1. Board 按 `activeAssignments[].assignmentGroupId` 渲染独立执行段,不以 `currentAssignment`、司机 ID 或连续日期猜测合并。 +2. 整组确认提交 Board 返回的 `requirementVersion/requirementSha256/dispatchPlanGeneration` 与全部 group 精确集合;收到 `605055/605056` 后刷新,不做部分重试。 +3. 人工确认显式传 `driverReplySource=MANUAL_CONTACT`;展示通知状态与确认来源为两个独立事实。 +4. H5 使用 `serviceDates/segmentDays/segmentRoute` 展示本段范围;保留对既有字段的兼容读取。 +5. 所有 JSON Long 按字符串处理,禁止转 JavaScript `Number`。 + +## 6. Internal Feign / shared Java + +Fleet 与 order-v3 新增内部 reservation/release 契约: + +- `POST /v3/internal/order/orders/:orderId/requirement/vehicle/final-confirmation-reservations` +- `POST /v3/internal/order/orders/:orderId/requirement/vehicle/final-confirmation-reservations/release` + +共享 DTO 采用 additive 字段,Fleet reservation、事务内 durable receipt、release Outbox 与 Order holder/fence 共同保证失败回滚和可重放释放。oasdiff 与 Spring Cloud Contract 均未配置,不能标记为工具 PASS;本次以 Controller JSON、producer/consumer 源码对比及两个 reactor verify 作为人工回退证据。 + +## 验证证据 + +- Fleet:`mvn -pl hl-fleet-service -am verify`,2,820 tests,0 failures,0 errors,5 skipped,BUILD SUCCESS。 +- Order:`mvn -pl hl-order-service-v3 -am verify`,7,261 tests,0 failures,0 errors,36 skipped,BUILD SUCCESS。 +- Fleet Spotless:BUILD SUCCESS;`git diff --check dev-v3`:PASS。 +- 最终独立 P0–P2 review:GO,无可复现阻断项。 +- 后端 PR #5420 已 squash 合并到 `dev-v3@e543a41035`;按 changelog 发布门禁记录 `backend_status=deployed`,本工单不包含运行时测试部署。 +- 本工单冻结范围不执行部署或网关调用,`gateway_status=not_required`。 +- 前端未领取,`frontend_status=pending`;changelog 发布不代表前端已实现、发布或页面验证。