--- schema: "hl-changelog/v2" ticket: "7442" title: "团级确认态 + 需求已发车务回写" consumer: "admin" author: "wx(GIT)" change_type: "新增接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "b84a5d6bcab491b3ecc71b811762946af35cec09" target_release: "hl-ui@b84a5d6b" verified_at: "2026-09-17" status_note: "后端交付。新增 fleet 确认整团配车端点 + order-v3 内部回写端点,支持异步回写正式用车需求状态。前端需在团期配车页增加确认按钮。前端已交付(2026-09-17):配车总览抽屉并行拉总览+order-v3 用车需求,需求 CONFIRMED 且有乘车分组出「确认整团配车」(免车空分组/DRAFT 不出,DISPATCHED 显已确认 tag),remark≤200 空白剥离;幂等与 602005-602009 透 message;api spec 2+抽屉 spec 4 全过,checkpoint 全量绿。" updated_at: "2026-09-17" base: "dev-v3" --- # fleet/order-v3: 团级确认态 + 需求已发车务回写 **存放目录**: changelogs-v2/{YYYY-MM}/(管理后台,二期 fleet + order-v3) **服务**: hl-fleet-service (端口 8082)、hl-order-service-v3 (端口 8083) **PR**: #7862 **Issue**: #7442 PR-B **日期**: 2026-09-17 **影响范围**: 新增团期配车确认接口;新增需求状态异步回写链路 --- ## 关键变化 1. **新增确认接口**:fleet 侧新增 `POST /admin/fleet/group-dispatch/batches/{groupBatchId}/confirm`,车务在配车计划提交并通过后,可调用本端点把整团配车定稿,转入「已确认」态。 2. **异步回写链路**:确认成功后,fleet 侧登记一条 Outbox 意图,经 Outbox 异步投递调用 order-v3 内部端点 `POST /v3/internal/group-batch/{groupBatchId}/vehicle-requirement/dispatched`,把正式用车需求从 CONFIRMED 推进到 DISPATCHED。 3. **新增错误码**:`602007`(无可确认行)/ `602008`(覆盖不完整)用于确认端点的校验失败。 4. **部署顺序**:order-v3 先部署,fleet 后部署(后端实现细节)。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 确认整团配车 | POST | `/admin/fleet/group-dispatch/batches/{groupBatchId}/confirm` | 新增 | 车务确认配车方案落定 | | 2 | 回写需求已发车务 | POST | `/v3/internal/group-batch/{groupBatchId}/vehicle-requirement/dispatched` | 新增 | [内部接口] fleet 确认后异步回写需求状态 | --- ## 三、接口详情 ### 1. 确认整团配车 `POST /admin/fleet/group-dispatch/batches/{groupBatchId}/confirm` **VO**: `GroupDispatchConfirmReqVO → GroupDispatchConfirmRespVO` #### 使用场景 车务在看板完成团期配车计划提交后(配车行已过验证、状态为「已派车」),调用本端点把整团配车定稿、转为「已确认」态。确认成功后会异步推进正式用车需求的状态流转(CONFIRMED → DISPATCHED);需求页需自行刷新以获取最新状态。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | 是 | - | 团期主订单 ID | | requirementId | Body | Long | 是 | - | 本次确认所依据的正式团级用车需求 ID(字符串序列化) | | requirementVersion | Body | Integer | 是 | - | 本次确认所依据的需求版本号 | | remark | Body | String | 否 | ≤200 字符 | 确认备注(仅留痕,不写入配车行) | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | groupBatchId | String | 团期主订单 ID(字符串序列化) | | confirmedCount | Integer | 本次由「已派车」转为「已确认」的配车行数 | | alreadyConfirmedCount | Integer | 确认前已是「已确认」的配车行数 | | requirementId | String | 本次确认所依据的正式需求 ID(字符串序列化) | | requirementVersion | Integer | 本次确认所依据的需求版本 | | planVersion | Long | 当前团期计划版本(确认不改计划,不递增) | | requirementAdvanceIntent | String | 已登记的需求回写意图方向,恒为 CONFIRMED_TO_DISPATCHED | | coverage | Object | 按乘车分组的覆盖明细 | | legacyGroupRowCount | Integer | 无分组键的历史派车行数(不计入任何组的覆盖) | #### 请求示例 ```json POST /admin/fleet/group-dispatch/batches/1934567890123456800/confirm { "requirementId": 5501, "requirementVersion": 3, "remark": "与地接确认车辆无误" } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "groupBatchId": "1934567890123456800", "confirmedCount": 8, "alreadyConfirmedCount": 0, "requirementId": "5501", "requirementVersion": 3, "planVersion": 7, "requirementAdvanceIntent": "CONFIRMED_TO_DISPATCHED", "coverage": { "totalGroups": 2, "coveredGroups": 2, "incompleteGroups": [] }, "legacyGroupRowCount": 0 }, "success": true } ``` #### 空数据 / 降级响应 N/A(团期无可确认行时返 602007 错误)。 #### 错误响应 ```json { "code": 602008, "message": "配车尚未覆盖完整, 不能确认: 乘车分组 A 缺失 2026-05-08", "data": null, "success": false } ``` 可能的错误码: - `602005` - 用车需求已更新,请刷新后重新配车 - `602006` - 正式用车需求当前状态不允许配车 - `602007` - 本团没有可确认的配车行,请先提交配车计划 - `602008` - 配车尚未覆盖完整,不能确认 - `602009` - 无法取得本团的权威乘车分组清单 - `600008` - 并发修改 - `600009` - 基线不可用 #### 业务边界 - **鉴权**: 需 `fleet:group-dispatch:write` 权限 - **幂等性**: 重复确认返 confirmedCount=0、alreadyConfirmedCount=N(属幂等成功),HTTP 200 不是错误 - **防重提交**: 本端点无防重时间窗,连点多次都是幂等成功形态 - **异步回写**: 响应成功仅代表意图已登记,需求状态的实际推进可能稍后才发生;需求列表页需自行刷新 - **并发处理**: 同团的并发调用由服务端串行化处理 --- ### 2. 回写需求已发车务 `POST /v3/internal/group-batch/{groupBatchId}/vehicle-requirement/dispatched` ⚠️ **[内部接口,不对前端开放]** fleet 侧异步回写链路调用,通过 Feign 投递。 **VO**: `GroupBatchVehicleRequirementDispatchedReqDTO → GroupBatchVehicleRequirementDispatchedRespDTO` #### 使用场景 fleet 侧确认配车后,通过 Outbox 异步机制调用本端点,把正式用车需求从 CONFIRMED 推进到 DISPATCHED 状态。该端点不对前端暴露,仅供内部服务间通信。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | 是 | - | 团期主订单 ID | | requirementId | Body | Long | 是 | - | 车务确认所依据的正式需求 ID(字符串序列化) | | requirementVersion | Body | Integer | 是 | - | 车务确认所依据的需求版本 | | sourceRefNo | Body | String | 否 | - | 幂等追溯号(fleet Outbox 记录 ID,用于日志对账) | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | applied | Boolean | 本次是否真的推进了需求状态 | | discardReason | String | applied=false 时的原因常量 | | requirementStatus | String | 回写后提供方当前的需求状态 | | requirementVersion | Integer | 回写后提供方当前的需求版本 | #### 请求示例 ```json POST /v3/internal/group-batch/1934567890123456800/vehicle-requirement/dispatched { "requirementId": "5501", "requirementVersion": 3, "sourceRefNo": "880123" } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "applied": true, "discardReason": null, "requirementStatus": "DISPATCHED", "requirementVersion": 3 }, "success": true } ``` #### 空数据 / 降级响应 applied=false 时无新状态变化,仍返 200(幂等重放或需求已变版): ```json { "code": 200, "message": "成功", "data": { "applied": false, "discardReason": "IDENTITY_MISMATCH", "requirementStatus": "CONFIRMED", "requirementVersion": 4 }, "success": true } ``` #### 错误响应 真正的故障(DB 不可用、CAS 并发冲突)仍以异常形式返回失败 Result,由 Outbox 退避重试: ```json { "code": 500, "message": "数据库异常", "success": false, "data": null } ``` #### 业务边界 - **一律返 200**: 本端点由 Outbox 重试链路驱动,任何判定结论都再投无用,故用 applied+discardReason 标记 - **幂等性**: 重投同一条 sourceRefNo,结果保持一致 - **丢弃原因**: - `IDENTITY_MISMATCH` - 需求身份不一致 - `REQUIREMENT_NOT_FOUND` - 该团无活跃需求 - `ALREADY_DISPATCHED` - 需求已是完成态 - `STATUS_INVALID` - 需求状态不允许推进 --- ## 四、契约约束与正确调用方式 | 场景 | 做法 | |------|------| | 车务确认配车 | 调 POST /admin/fleet/group-dispatch/batches/{id}/confirm,返回 confirmedCount | | 处理重复确认 | confirmedCount=0 且 HTTP=200 为幂等成功 | | 等待需求更新 | 需求列表页需自行刷新 | --- ## 五、数据库行为 | 操作 | 数据库影响 | |------|----------| | 确认配车 | fleet_group_dispatch.dispatch_status → CONFIRMED | | Outbox 异步回写成功 | order_group_vehicle_requirement.status → DISPATCHED | --- ## 六、边界行为 - **无可确认行** → 602007(fail-closed) - **覆盖不完整** → 602008(fail-closed) - **需求版本不匹配** → 602005(fail-closed) - **并发确认** → 第二个调用见 confirmedCount=0(幂等成功) - **投递到达时需求已变** → applied=false + IDENTITY_MISMATCH --- ## 六.5 枚举 **需求回写的丢弃原因**: - `IDENTITY_MISMATCH` - 需求 ID/版本不符 - `REQUIREMENT_NOT_FOUND` - 团无活跃需求 - `ALREADY_DISPATCHED` - 需求已是完成态 - `STATUS_INVALID` - 需求状态不允许推进 --- ## 六.6 修改前后对比 | 端点 | 改前 | 改后 | |------|------|------| | /admin/fleet/.../confirm | 无 | 新增 POST | | /v3/internal/group-batch/.../dispatched | 无 | 新增 POST | --- ## 六.7 影响评估 - **向后兼容**: 是(新增端点) - **前端同步**: 是(需加确认按钮) - **清理点**: 无 --- ## 部署清单(本单改了 hl-common-core,CODE_RULES §16.6) 🔧 2026-09-19 订正:原文档遗漏本节(未写「同批滚」措辞、也未列模块清单),现补上。 本单在 `hl-common-core` 新增两个全新 DTO:`GroupBatchVehicleRequirementDispatchedReqDTO`(回写请求体)、 `GroupBatchVehicleRequirementDispatchedRespDTO`(回写响应体)。order-v3 是这两个 DTO 对应内部端点的提供方, fleet 是发起方(Outbox 异步调用)。依据: ``` git show --name-only --format='' dcbdacd894b7d134105e2dad4dec6003fe4d3ddb | grep '^hl-common' ``` 输出: ``` hl-common/hl-common-core/src/main/java/com/hulalv/common/dto/fleet/GroupBatchVehicleRequirementDispatchedReqDTO.java hl-common/hl-common-core/src/main/java/com/hulalv/common/dto/fleet/GroupBatchVehicleRequirementDispatchedRespDTO.java ``` ### 消费方清单不能按 `pom.xml` 直接依赖关系查 `grep -rl 'hl-common-core' */pom.xml` 只命中 `hl-gateway`/`hl-finance`,order-v3 与 fleet 都经 `hl-common-web` 传递引入,不在直接依赖清单里,但正是本单真正改了代码的两个服务。模块清单不凭印象列,用命令算,基点用 `git merge-base` 算(不用两点式直接 diff 两个可能不在同一祖先链上的点): ``` git merge-base a37bd669f2559a9a70f4104b64b2d14ba1d5c66c dcbdacd894b7d134105e2dad4dec6003fe4d3ddb # => a37bd669f2559a9a70f4104b64b2d14ba1d5c66c git diff --name-only a37bd669f2559a9a70f4104b64b2d14ba1d5c66c dcbdacd894b7d134105e2dad4dec6003fe4d3ddb \ | sed -E 's#^([^/]+)/.*#\1#' | sort -u ``` 输出: ``` hl-common hl-fleet-service hl-order-service-v3 ``` | 部署单位 | 与 `hl-common-core` 的依赖关系 | 是否需要本次一起滚 | |---|---|---| | hl-gateway | 直接依赖(`pom.xml` 命中) | 是 | | hl-user-service | 经 `hl-common-web` 传递依赖 | 是 | | hl-resource-service | 经 `hl-common-web` 传递依赖 | 是 | | hl-product-service-v2 | 经 `hl-common-web` 传递依赖 | 是 | | hl-order-service-v3 | 经 `hl-common-web` 传递依赖 | **是(本单直接改了这个服务的生产代码,且是新 DTO 的提供方)** | | hl-mp-service | 经 `hl-common-web` 传递依赖 | 是 | | hl-fleet-service | 经 `hl-common-web` 传递依赖 | **是(本单直接改了这个服务的生产代码,且是新 DTO 的发起方)** | | hl-finance | 直接依赖(`pom.xml` 命中) | 不单独部署——是 order-v3 的库依赖,随 order-v3 一起滚 | ⇒ **实际部署单位共 7 个**:gateway / user / resource / product-v2 / order-v3 / mp / fleet。正文第 37 条 「部署顺序:order-v3 先部署,fleet 后部署」的顺序结论不变(提供方先于发起方部署,与 §16.6 同批滚不矛盾, 两者是"同一批次内"与"批次内先后次序"两件事),本节只是补齐 §16.6 要求的模块清单与同批滚措辞。 --- ## 七、不影响范围 - **仅影响**: 团期配车确认流程、需求状态转移 - **零影响**: 配车提交流程、其他需求转移路径、派单列表、看板显示 --- ## 八、测试环境已验证 ``` POST /admin/fleet/group-dispatch/batches/xxx/confirm → 200 ✓ POST /v3/internal/group-batch/xxx/dispatched → 200 ✓ 重复确认 confirmedCount=0 ✓ 异步回写已发送 ✓ ``` --- ## 十、相关文档 - Issue: [#7442](https://git.1814.love:8443/wx/HL/issues/7442) - PR: [#7862](https://git.1814.love:8443/wx/HL/pulls/7862) - Merge: [a37bd669f](https://git.1814.love:8443/wx/HL/commit/a37bd669f) - 🔧 2026-09-19 订正:`a37bd669f` 实为 **PR #7866**(`fix(finance): #7396 应付款四入口接团期住宿 + 团期应付稳定付款身份与金额对账`,合入 dev-v3)的合并提交,与本 PR(#7862,fleet/order-v3 团级确认态)无关,系笔误指错 SHA。依据:`git show --no-patch --format='%H%n%s%n%an %ad' a37bd669f` 输出 `Merge pull request 'fix(finance): #7396 ...' (#7866) ... jw ...`;`git diff --name-only a37bd669f^1 a37bd669f | sed -E 's#^([^/]+)/.*#\1#' | sort -u` 输出仅 `docs`、`hl-finance` 两个模块,不含 `hl-fleet-service`/`hl-order-service-v3`。 本 PR #7862 真实的合并提交是 **`dcbdacd894b7d134105e2dad4dec6003fe4d3ddb`**(单亲提交,squash 合并,直接父提交即 `a37bd669f`,author wx,2026-09-17 11:13:06,message `feat(fleet,order-v3): #7442 PR-B 团级确认态 + 需求 CONFIRMED→DISPATCHED 回写`)。依据:`git show --no-patch --format='%H%n%s%n%an %ad%n parents:%P' dcbdacd894b7d134105e2dad4dec6003fe4d3ddb`;`git diff --name-only a37bd669f dcbdacd894b7d134105e2dad4dec6003fe4d3ddb | sed -E 's#^([^/]+)/.*#\1#' | sort -u` 输出 `hl-common`、`hl-fleet-service`、`hl-order-service-v3`,与本文档标题「确认整团配车」「回写需求已发车务」逐字对应(新增文件含 `GroupDispatchConfirmReqVO.java`/`GroupDispatchConfirmRespVO.java`/`GroupBatchResourceController.java` 等,均在本文档接口详情节出现)。merge-base 用 `git merge-base a37bd669f dcbdacd894` 算得 = `a37bd669f` 本身(因 `dcbdacd894` 是紧邻其后的单亲提交)。 - **正确 Merge**: [dcbdacd894b7d134105e2dad4dec6003fe4d3ddb](https://git.1814.love:8443/wx/HL/commit/dcbdacd894b7d134105e2dad4dec6003fe4d3ddb) ## 关联 / 联系人 ### 链接 - **Issue**: [#7442](https://git.1814.love:8443/wx/HL/issues/7442) - **PR**: [#7862](https://git.1814.love:8443/wx/HL/pulls/7862) ### 联系人 - **后端**: @wx