--- schema: "hl-changelog/v2" ticket: "7441" title: "团期用车内部回调——团车配完/清零双向回写户级需求状态(内部接口分册)" consumer: "internal" author: "wx(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "not_required" frontend_owner: "" frontend_ref: "" target_release: "" verified_at: "" status_note: "两个端点均为 /v3/internal/** Feign 接口,经网关访问返回 403(JwtAuthFilter 按设计拒绝),取证走直连 order-v3 服务端口(192.168.100.236:8086)+ X-Internal-Token,与 hl-fleet-service 实际调用路径一致。正向(vehicle-ready)与反向(vehicle-ready-reset)均有四次真实调用的完整前后快照(S1-S4,见第八节,见工单 #7441 验收评论)。被测服务:order-v3 = dev-v3 64c3f72a3(2026-09-15 12:06:01 部署),行为在 870610927(含 PR-3)上未再变化。" updated_at: "2026-09-15" base: "dev-v3" --- # order-v3: 团期用车内部回调——团车完成双向回写(内部接口分册) > **本文件是分册。** 本单(`#7441`)同批还有 3 份面向前端的 changelog(正式用车需求声明四端点、确认行程清单房车安排短路判定、结算核单车侧闸门升级),本册收件人是后端与 fleet 侧,**不是 mmg**——两个端点均为跨服务 Feign 调用,hl-ui 调不到。拆分依据 `BACKEND_CHANGELOG_DELIVERY_GUIDE.md` 2.5 节:`/v3/internal/*` 必须单独成篇,不许和 admin/mp 接口塞同一份。 > > **服务**: hl-order-service-v3 (端口 8083) > **消费方**: hl-fleet-service(Feign,`/v3/internal/**`,不经 JWT,靠网关不暴露该前缀做隔离) > **PR**: #7742(PR-2) > **Issue**: #7441 > **日期**: 2026-09-15 > **影响范围**: 既有内部端点 `POST /v3/internal/group-batch/{groupBatchId}/vehicle-ready` 与 `POST .../vehicle-ready-reset`,路径/参数/响应结构/错误码均未改,新增户级用车需求状态双向回写副作用 --- ## ⚠️ 关键变化 1. **团级配车完成回调(`vehicle-ready`)从此会连带把在团户的行程用车(TRAVEL)需求状态回写为完成**——改前该回调只置团级 `vehicle_ready=true` 一列,户级需求行一行不动;改后同一次调用内,未经逐户 DAILY_V3 快照完成的在团户,会被批量推到"已完成"状态并同步订单镜像列。 2. **团级配车清零回调(`vehicle-ready-reset`)从此会连带把"由团车路径完成"的户打回"处理中"**——只接管由本回调正向产出的那批户,已经由逐户 DAILY_V3 快照完成的户、以及完成来源标识为空的历史行,一律不动。 3. **两个端点自身的请求/响应契约完全不变**(`Result`,团期不存在也返回 200 幂等友好),fleet 侧调用代码无需任何改动。 --- ## 一、背景 fleet 团级配车完成后经 Outbox → Feign 回调 `vehicle-ready`;团级清零/释放(取消成团、流团、fleet 侧整团释放)后回调 `vehicle-ready-reset`。改前这两个回调只维护团期聚合根自己的 `vehicle_ready` 标志位,户级用车需求(`order_vehicle_requirement`)一行都不碰——全仓唯一把户级需求置完成的生产者是逐户 DAILY_V3 派车快照回调,而团期用车此时已经走团级配车(不逐户派车),于是"整团车已配好、各户需求仍停在待处理",被 `#7445` 的结算闸拦死且无任何运营操作能解开。本单补上这个双向回写。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 回填配车就绪(正向) | POST | `/v3/internal/group-batch/{groupBatchId}/vehicle-ready` | 行为变化 | 新增在团户 TRAVEL 需求批量完成回写,契约不变 | | 2 | 重置配车就绪(反向) | POST | `/v3/internal/group-batch/{groupBatchId}/vehicle-ready-reset` | 行为变化 | 新增"由团车完成"的户打回处理中,契约不变 | --- ## 三、接口详情 ### 1. 回填配车就绪(正向) `POST /v3/internal/group-batch/{groupBatchId}/vehicle-ready` **VO**: `无请求体 → Result` #### 使用场景 fleet-service 整团配车完成后回调。仅限内部 Feign 调用,不经网关(`JwtAuthFilter` 按设计拒绝 `/v3/internal/**`),fleet 侧走服务发现直连。幂等:重复调用安全。回填成功后内部自动检测四项资源就绪门,满足则推进团期状态。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | 是 | - | 团期主订单 ID(不变) | (无请求体,不变。) #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | data | null | 结构完全不变,`Result` | #### 请求示例 ```http POST /v3/internal/group-batch/2099716821799047169/vehicle-ready HTTP/1.1 X-Internal-Token: *** ``` (无请求体。测试环境经服务端口直连,生产环境由 fleet-service 经 Feign LB 直连。) #### 响应示例 (测试服真实响应,2026-09-15 12:28:19,直连 order-v3:8086,见工单 #7441 验收评论) ```json { "code": 200, "message": "成功", "data": null, "traceId": null, "success": true } ``` #### 空数据 / 降级响应 团期不存在时仍返回上述成功结构(幂等友好,不抛错),仅记 WARN 日志,不做任何户级回写。 ```json { "code": 200, "success": true, "data": null } ``` #### 错误响应 本单不新增错误码。团期不存在按幂等成功处理(见上),不返回错误响应;其余异常沿用全局异常处理,无本单专属错误码。 ```json { "code": 500, "message": "系统异常,请稍后重试", "success": false, "data": null } ``` #### 业务边界 - 同一事务内先置团级 `vehicle_ready=true`,再取在团户,把 `kind=TRAVEL` 且状态在"待处理/处理中"的 active 需求 CAS 推进为"已完成",并同步订单镜像列;团期状态推进在事务外进行。 - **已由逐户 DAILY_V3 快照完成的户跳过、不覆盖**:这类户是更权威的完成来源(带逐日车辆/司机明细),团车回写不覆盖它。 - **仍停在"待审核"(配车后才入团、尚未经整团放行)的户跳过并记 WARN,不强推**。 - **重复回调幂等**:已完成的户读侧直接跳过、不发 CAS;CAS 命中 0 行(读到未完成后被并发推进)记 WARN 跳过,不抛错。 - 团期不存在只记 WARN,不抛错,不影响 Outbox 重投语义。 - 本次回写**只改状态与完成来源标识两列,不写任何逐户配车明细列**(不产生逐户车辆/司机数据)——车与司机在团级承担,任何逐户配车行都只可能是历史残留。 --- ### 2. 重置配车就绪(反向) `POST /v3/internal/group-batch/{groupBatchId}/vehicle-ready-reset` **VO**: `无请求体 → Result` #### 使用场景 fleet-service 整团清零、或取消成团/流团释放后回调,把 `vehicle_ready` 重置为与当前有效计划一致的值。仅限内部 Feign 调用,同上不经网关。幂等:重复调用安全。不触发任何团期状态推进(与置位方向相反)。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | 是 | - | 团期主订单 ID(不变) | (无请求体,不变。) #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | data | null | 结构完全不变,`Result` | #### 请求示例 ```http POST /v3/internal/group-batch/2099716821799047169/vehicle-ready-reset HTTP/1.1 X-Internal-Token: *** ``` #### 响应示例 (测试服真实响应,2026-09-15 12:28:20,直连 order-v3:8086,见工单 #7441 验收评论) ```json { "code": 200, "message": "成功", "data": null, "traceId": null, "success": true } ``` #### 空数据 / 降级响应 团期不存在时仍返回成功结构,仅记 WARN,不做任何户级回写。 ```json { "code": 200, "success": true, "data": null } ``` #### 错误响应 本单不新增错误码,异常处理同「三、1」。 ```json { "code": 500, "message": "系统异常,请稍后重试", "success": false, "data": null } ``` #### 业务边界 - 同一事务内先重置团级 `vehicle_ready=false`,再取在团户逐一按行锁 + 锁内复读判定资格后打回"处理中":**只接管"完成来源标识为团车"且"非逐户 DAILY_V3 契约版本"的户**——这是必要条件,不是可选加强。 - **两类户一律跳过、三列一字不改**:①已由逐户 DAILY_V3 快照完成的户;②完成来源标识为空的历史 legacy 完成行(这类行即使表面状态与团车形态相同,也不会被本回调碰)。测试服实测两类对照户在四步(基线 → reset → ready)全程逐列不变。 - **反向不清"完成来源"标识列**:只把状态打回"处理中",完成来源标识保留,供下一次团车正向回写复用同一判据(幂等)。 - **锁内复读**:判定资格必须在拿到需求行锁之后重新读取,不能用取锁前的内存值判断——避免与并发的逐户 DAILY 回调交错时把刚完成的快照户误判打回。 - 团期不存在只记 WARN,不抛错。 --- ## 四、契约约束与正确调用方式 fleet-service 侧调用代码**无需任何改动**——两个端点的路径、方法、请求体(均无)、响应结构(均为 `Result`)与调用时机(配车完成后调正向、清零释放后调反向)全部不变。本单的行为变化完全由 order-v3 内部消化,不要求消费方感知或配合。 --- ## 五、数据库行为 正向回调新增的写行为:同一事务内,在置团级就绪标志之后,把符合条件的在团户行程用车需求状态由"待处理/处理中"推进为"已完成",同步一列"完成来源"标识,并同步订单侧的用车控制状态镜像列;不写入任何逐户配车明细(车辆/司机/座位等)。反向回调新增的写行为:把"完成来源标识为团车"且"非逐户快照契约"的户由"已完成"打回"处理中",同步镜像列;"完成来源"标识本身不清空。两个方向均在各自既有事务内完成,不额外引入跨服务同步写。 --- ## 六、边界行为 - 团期不存在 → 两个端点均只记 WARN,返回成功结构,不抛错(不变) - 已由逐户 DAILY_V3 快照完成的户 → 正向跳过不覆盖,反向跳过不动 - 完成来源标识为空的历史 legacy 完成行 → 反向跳过不动(正向不适用,因为该状态本就不是"待处理/处理中") - 仍在"待审核"的户 → 正向跳过并记 WARN,不强推 - 重复调用(Outbox 重投)→ 两个方向均幂等,不抛错、不重复产生副作用 --- ## 六.6、修改前后对比 ### 字段级对比 本单不改两个端点的请求/响应字段结构。 ### 行为级对比 | 场景 | 改前 | 改后 | |------|------|------| | 团车配完回调(正向) | 只置团级 vehicle_ready=true,户级需求一行不动 | 同一事务内追加:在团户 TRAVEL 需求批量推进为已完成,同步镜像列 | | 户已由逐户 DAILY_V3 快照完成 | 不适用(正向改前不碰户级) | 跳过、不覆盖 | | 户仍在"待审核" | 不适用 | 跳过并记 WARN,不强推 | | 团车清零回调(反向) | 只置团级 vehicle_ready=false | 同一事务内追加:把"由团车完成"的户打回处理中 | | 逐户 DAILY_V3 快照户 / 历史 legacy 完成行 | 不适用(反向改前不碰户级) | 一律跳过,三列不变 | | 重复回调 | 幂等(改前无户级副作用可言) | 仍幂等:已完成的户直接跳过不发 CAS | --- ## 六.7、影响评估 - **是否破坏向后兼容**: 否,端点契约完全不变,fleet 侧零改动。 - **前端是否必须同步上线**: 不适用(`frontend_status: not_required`,本册收件人是后端与 fleet 侧,hl-ui 调不到这两个端点)。 - **前端 workaround 清理点**: 无。 --- ## 七、不影响范围 - **仅影响**: 两个内部端点在"团车配完/清零"场景下的户级用车需求状态副作用,端点本身契约不变。 - **零影响**: - `GET /v3/internal/group-batch/{groupBatchId}/dispatch-baseline`——同 Controller 第三个端点,本单未改。 - 逐户 DAILY_V3 派车快照回调链路——完全独立,本单不改其任何代码。 - fleet-service 自身代码——零改动,仅消费方数据(户级需求状态)随之变化。 - `hl-gateway` 路由——`/v3/internal/**` 本就不上网关,本单不涉及网关配置。 --- ## 八、测试环境已验证 **取证环境**:order-v3 = dev-v3 `64c3f72a3`(2026-09-15 12:06:01 部署),经直连服务端口 `192.168.100.236:8086` + `X-Internal-Token` 调用(与 fleet-service 实际调用路径一致;经 `hl-gateway` 访问 `/v3/internal/**` 返回 403,非本单行为,属既有设计)。 四组户级快照对照(S1→S4,见工单 #7441 验收评论): | 快照 | 操作 | 团车户(GV/MIX) | DAILY_V3 快照户(DV) | |---|---|---|---| | S1→S2 | 正向 `vehicle-ready` | 待处理 → 已完成,完成来源写入 | 逐列不变(含 update_time) | | S2→S3 | 反向 `vehicle-ready-reset` | 已完成 → 处理中,完成来源保留 | 仅 update_time 变化(取锁刷新),其余不变 | | S3→S4 | 再次正向 `vehicle-ready` | 处理中 → 已完成,完成来源仍为团车 | 幂等跳过,逐列不变 | 历史 legacy 完成行(LEG 户,SQL 构造成"已完成+完成来源为空")在同一组四步全程三列逐字不变,反向回调按"跳过并记 WARN"处理,日志打出了具体判定值。 --- ## 十、相关文档 - 关联 Issue: [wx/HL#7441](https://git.1814.love:8443/wx/HL/issues/7441) - 关联 PR: [#7742](https://git.1814.love:8443/wx/HL/pulls/7742) - 前置依赖:`#7439`(户级需求表与状态枚举)、`#5319`(vehicle-ready-reset 端点首次交付)、`#3857`(vehicle-ready 端点首次交付) - 相关分册:`#7441` 正式用车需求声明四端点、确认行程清单房车安排短路判定(消费本单产出的完成状态)、结算核单车侧闸门升级(同样消费本单产出的完成状态) ## 关联 / 联系人 ### 链接 - **Issue**: [#7441](https://git.1814.love:8443/wx/HL/issues/7441) - **PR**: [#7742](https://git.1814.love:8443/wx/HL/pulls/7742) ### 联系人 - **后端负责人**: @wx