--- schema: "hl-changelog/v2" ticket: "5263" title: "最终确认按车选择发送行程短信" consumer: "admin" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "implemented" frontend_owner: "hl-ui-pi" frontend_ref: "mmg/hl-ui@138136e50c4bbf9931ee020bd28d40977264b64e" target_release: "" verified_at: "" status_note: "前端已在 138136e5 接入 HOLD 最终确认逐车短信必选、权威状态查询和 FAILED 受控重试;定向 7 个文件 62 项及 verify:changed 全量验证通过。尚未发布或完成真实页面联调。" updated_at: "2026-07-27" base: "dev-v3" generated: "2026-07-26T17:33:00+08:00" --- # 最终确认按车选择发送行程短信 ## 关联 - Issue: [wx/HL#5263](https://git.1814.love:8443/wx/HL/issues/5263) - Changelog PR: [wx/hl-api-changelog#41](https://git.1814.love:8443/wx/hl-api-changelog/pulls/41) - 服务:`hl-fleet-service`、`hl-user-service` - 前端仓库:`mmg/hl-ui`(本文仅交接,不代表已修改前端) - 前置车费契约:[#5262 派车逐日车费、只读总价与核单实时接口](./26_5262_派车逐日车费与核单实时接口-修改接口-前端待处理-管理后台.md) ## 关键变化 1. 仅在 HOLD 排车的最终确认阶段,每个车辆组必须显式选择“发送短信”或“不发送短信”,没有默认值。 2. 选择发送时,确认事务只落可靠发送意图;短信异步发送失败不会回滚已完成的派车确认。 3. 短信只发给该车辆组当前师傅,包含订单摘要、接送摘要和签名行程短链,不包含客户手机号。 4. 选择不发送时只完成派车确认,不创建行程短信事件。 5. 多车订单逐车独立选择、独立投递、独立查询状态和受控重试。 6. 已派定后的订单人数基线复核只能沿用原选择,不允许借复核修改选择或重复发送。 7. 直接派车流程不受影响;#5262 已废弃的手工车辆总价入参仍然禁止提交。 ## 变更接口 | 方法 | 路径 | 变化 | | --- | --- | --- | | `POST` | `/admin/fleet/assignments/:assignmentId/confirm` | 请求新增必填 `sendItinerarySms`;响应新增短信选择、事件与状态 | | `GET` | `/admin/fleet/assignments/:assignmentId/itinerary-sms` | 新增单车/车辆组短信审计状态查询 | | `POST` | `/admin/fleet/assignments/:assignmentId/itinerary-sms/retry` | 新增明确失败后的车务受控重试 | ## 最终确认 ### `POST /admin/fleet/assignments/:assignmentId/confirm` #### 请求字段 | 字段 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | `sendItinerarySms` | `Boolean` | 是 | `true` 发送;`false` 不发送;省略或 `null` 返回参数错误 | | `requestId` | `String` | 是 | 最长 64 字符的幂等请求标识 | | `vehicleFeeTotal` | `Decimal` | 否 | 历史兼容字段;非空即拒绝,最终总车费继续由 #5262 逐日车费只读合计 | | `vehicleFeeAdjustmentReason` | `String` | 否 | 历史兼容字段;非空即拒绝 | 请求示例: ```json { "sendItinerarySms": true, "requestId": "fleet-final-confirm-26-8411-car-1" } ``` #### 新增响应字段 | 字段 | 类型 | 说明 | | --- | --- | --- | | `sendItinerarySms` | `Boolean` | 本车辆组最终确认时保存的选择 | | `itinerarySmsEventId` | `String/null` | 可靠短信事件 ID;不发送时为空 | | `itinerarySmsStatus` | `String` | 首次确认返回 `PENDING` 或 `NOT_SENT`;已派定复核回显真实状态 | ## 短信状态 ### `GET /admin/fleet/assignments/:assignmentId/itinerary-sms` 响应 `data`: | 字段 | 类型 | 说明 | | --- | --- | --- | | `assignmentId` | `String` | 派单 ID | | `assignmentGroupId` | `String` | 跨服务日车辆组 ID | | `assignmentSlotId` | `String` | 稳定车辆槽位 ID | | `sendItinerarySms` | `Boolean/null` | 未最终确认或历史数据时为 `null` | | `status` | `String` | `NOT_APPLICABLE` / `NOT_SENT` / `PENDING` / `SENT` / `FAILED` / `CANCELED` | | `eventId` | `String/null` | 可靠短信事件 ID | | `retryCount` | `Integer` | 已发生的失败重试次数 | | `canRetry` | `Boolean` | 当前是否允许受控重试 | | `sentAt` | `LocalDateTime/null` | 供应商确认的真实发送时间 | | `lastError` | `String/null` | 已脱敏的最近失败或待对账原因 | 前端以 `status` 为权威,不得仅凭最终确认接口成功就显示“短信已发送”。 ## 受控重试 ### `POST /admin/fleet/assignments/:assignmentId/itinerary-sms/retry` 请求: ```json { "reason": "短信通道配置已恢复,车务确认重发", "requestId": "retry-sms-26-8411-car-1" } ``` 规则: - 仅 `FAILED` 且 `canRetry=true` 时展示并调用重试。 - `SENT`、`PENDING`、待供应商对账、`CANCELED`、未选择发送时禁止重试。 - 重试复用原事件与供应商幂等键,不新建并行短信事件。 - 司机或车辆组身份已变化时,旧事件收敛为 `CANCELED`,不得发给旧师傅。 ## 短信与隐私约束 短信模板参数固定为: | 参数 | 内容 | | --- | --- | | `summary` | 脱敏订单摘要 | | `transfer` | 接送摘要 | | `code` | 签名行程短链 | 短信正文及模板参数不得包含客户手机号。真实联系人信息仅在既有签名行程 H5 中按授权展示。 ## 页面展示矩阵 | 区域/状态 | 数据源 | 展示 | 空态/禁用 | 颜色 | 守恒规则 | | --- | --- | --- | --- | --- | --- | | 最终确认车辆卡片 | 本地待提交选择 | “发送短信”/“不发送短信”二选一 | 未选择时禁止确认并提示必选 | 发送蓝色,不发送中性灰 | 每个车辆组恰好一个选择 | | 确认后状态 | `GET .../itinerary-sms.status` | 待发送/已发送/发送失败/已取消/未发送 | 历史数据为“不适用” | 待发送蓝、已发送绿、失败红、取消灰、未发送中性灰 | 不以确认成功冒充发送成功 | | 失败操作 | `canRetry` | “重试短信” | `canRetry=false` 时隐藏或禁用 | 可重试橙色 | 同一事件串行重试 | | 多车订单 | 每个 `assignmentGroupId` | 每车独立选择与状态 | 不做订单级统一默认 | 各卡片独立 | 一车选择不得覆盖另一车 | ## 前端处理清单 - [ ] 最终确认页按车辆组渲染无默认值的短信二选一。 - [ ] 未完成选择时不提交确认请求,并展示明确校验提示。 - [ ] 确认请求始终显式提交 `sendItinerarySms`,不再依赖后端默认值。 - [ ] 确认后通过状态接口展示真实投递状态。 - [ ] 仅在 `FAILED && canRetry=true` 时允许填写原因并调用重试。 - [ ] 已派定复核回显原选择并保持只读,不提供改选入口。 - [ ] 短信状态按展示矩阵处理空态、颜色及多车独立性。 - [ ] 确认和复核请求继续不提交 #5262 已废弃的手工总车费字段。 ## 验证证据 - OpenAPI/oasdiff:`not_configured`;已完成 Controller/VO 源码比对和接口测试回退证据。 - 消费者契约:`not_required`;未修改 internal Feign 或共享 Java DTO。 - 代码与测试:PR `wx/HL#5270` 已合入 `dev-v3`,merge commit 为 `f0a96c10124c3188e60e1291e7f28d768af50e3a`;`mvn -pl hl-user-service,hl-fleet-service -am test`、`mvn -pl hl-fleet-service spotless:check`、`mvn -pl hl-fleet-service -am verify` 均通过。 - 测试部署:`hl-user-service` 与 `hl-fleet-service` 已从合并后的 `dev-v3` 完成双实例滚动部署并通过健康检查。 - 网关:显式“不发送”业务验收 11/11 通过;合并后只读复验 3/3 通过,最终状态为 `assigned + NOT_SENT`,无短信事件且不可重试。 - `frontend_status`:`pending`;真实领取后再迁移为 `claimed` 并填写 `frontend_owner`。