文件
hl-api-changelog/changelogs-v2/2026-09/16_7441_团期整团确认接车侧-修改接口-管理后台.md
T
2026-09-16 09:31:17 +08:00

33 KiB
原始文件 Blame 文件历史

schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer author change_type backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 7441 团期整团确认改造接入车侧——预检/确认新增车侧字段,checkedResourceTypes 扩至用车,免车团确认不放行车侧 admin wx(GIT) 修改接口 deployed verified verified mmg a91d62f837637d54ce9814c8e4df64758e5e0419 2026-09-16 本单(#7441 PR-4)已由 PR #7778 squash 合入 dev-v3(合并提交 f0277a14f),测试服 order-v3 于 2026-09-16 01:21 部署该提交。confirm-check/confirm 两端点的核心场景(vehicleWaived 逃生口、ready 语义变化、809100/809103/809108/589533 收窄、重复确认幂等、PENDING_RECONFIRM 推进、CAS 回滚 809112)均经 hl-gateway 网关真实调用验证,见第八节。TRAVEL+TRANSFER 同户两类需求并存的场景(AC-13/14/21)因测试环境 TRANSFER 写侧全局开关关闭(809009,#7443 未上线的既有开关)未能验证,按源码核对列示,第八节已如实说明覆盖边界。mmg 2026-09-16 前端已交付:RequirementTab 预检区并列渲染 vehicleMissing 车侧缺失清单(detail 人话直显)+「本团整团免车」标识 + 预检文案两类缺失合并计数;确认成功提示补车侧放行条数(vehicleDispatchedCount 条数口径);直读 ready 不自算,809 段码走拦截器透 message;RequirementTab.spec +3 例 9/9,checkpoint 全绿。 2026-09-16 dev-v3

order-v3: 团期整团确认改造接入车侧

存放目录: changelogs-v2/{YYYY-MM}/(管理后台,二期 order-v3)

服务: hl-order-service-v3 (端口 8083) PR: #7778(squash 合入 dev-v3,合并提交 f0277a14f,同批含 PR-2d/PR-2e,另有一份 changelog 覆盖) Issue: #7441 日期: 2026-09-16 影响范围: 团期需求页「整体确认前预检」GET .../requirement/confirm-check 与「整体确认需求」POST .../requirement/confirm 两个既有管理后台端点,响应新增车侧字段,confirm 新增 4 个车侧错误码


⚠️ 关键变化(本版与上版行为不同,必读)

  1. checkedResourceTypes 从 ["HOTEL"] 变为 ["HOTEL","VEHICLE"]——#7535 当时明确承诺「扩到用车是另一张单」,本单就是那张单。若前端此前按「该数组恒为 ["HOTEL"]」写死判断,这里会翻转。
  2. ready 的必要条件变多:改前 ready = missing.isEmpty() && 阶段可确认;改后 ready = missing.isEmpty() && vehicleMissing.isEmpty() && 阶段可确认。同一个团可能出现 missing 为空数组但 ready=false 的情况,此时必须读新增的 vehicleMissing 才能知道原因,不能再假设「missing 空即可确认」。
  3. 确认响应新增 7 个车侧字段,3 个旧字段语义保持不变(dispatchedOrderIds/skippedOrderIds/dispatchedCount 仍然只统计住宿,不含车)。
  4. groupVehicleRequirementStatus 不再恒为 CONFIRMED:车侧已进入 DISPATCHED 或 DONE 的团再次确认时该字段回原状态(不倒退),这是正常态,不要渲染成异常。
  5. 免车团(vehicleWaived=true)整团确认不放行车侧:vehicleDispatchedCount=0,三个车侧 ID 列表为空数组,正式需求不推进——这不是漏放,是设计如此。
  6. 589533 触发条件收窄为「只管住宿」:改前住宿或车任一缺失都可能报 589533;本单起车侧缺失改抛 809 段专属码,589533 的 {0} 只统计住宿缺失户数。
  7. 背景信息(非本单改动,供理解字段含义):团期管理员可在需求页对整团声明「本团无需用车」(POST /v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement/waive,已上线)或撤销声明(POST .../vehicle-requirement/withdraw,已上线)。本单不改这两个端点的契约,只是预检/确认从此会读取它们产生的结果。声明免车曾经在「已有分组仍要声明免车」时报错码 809113;该码自 #7441 PR-3(已合并 dev-v3)起停用不再抛出(改为整份换版为免车版本),本单起进一步不出现 vehicleMissing 意义上的相关缺失项,前端若还留有 809113 专属提示文案,可以确认无需再触发但不必删除(错误码本身仍占位保留)。

一、背景

#7210 交付的「整体确认」原本只校验、只放行住宿:预检 GET .../requirement/confirm-check 只看住宿缺失清单,确认 POST .../requirement/confirm 只推进住宿需求。团期正式车需求(分组 × 逐日 × 成员,由 PUT/GET .../vehicle-requirement 两个已上线端点维护)与「本团无需用车」声明(waive/withdraw,已上线)此前完全不接入这两个端点——车侧需要逐单在订单详情页另行放行,管理员在团期需求页看不到车侧是否齐备。

本单(#7441 PR-4)让这两个已有端点在住宿之外对称接入车侧:预检同时给出车侧缺失清单,确认在住宿放行完成后,如果不是免车团,再推进正式车需求并批量放行在团户的车需求。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 整体确认前的缺失预检 GET /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check 响应新增字段 新增 5 个顶层字段 + 车侧缺失清单;ready/checkedResourceTypes 语义变化
2 整体确认需求(放行住宿+车) POST /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm 响应新增字段 + 新增错误码 车侧校验接入;免车团跳过车侧放行;新增 7 个响应字段 + 4 个车侧错误码;589533 收窄为只管住宿

三、接口详情

1. 整体确认前的缺失预检 GET /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check

VO: 无请求体 → Result<GroupBatchRequirementCheckRespVO>

使用场景

团期需求页进入「查看需求」Tab 时调用,以及点击「确认」按钮前调用,用于据 ready 置灰按钮、据 missing/vehicleMissing 展示缺哪些户/哪些车侧问题。只读,零副作用,可任意重复调用。本单起该端点同时覆盖住宿与车侧两类资源。

入参字段表

字段 位置 类型 必填 约束 说明
groupBatchId Path Long 是 - 团期聚合主键(不变)

(无查询参数、无请求体,本单未改动。)

出参字段表

字段 类型 说明
groupBatchId Long(序列化为 String) 团期聚合主键(不变)
batchStatus String 团期当前状态码(不变)
batchStatusName String 团期当前状态中文名(不变)
ready Boolean 语义变化:是否可以整体确认,改为 missing 为空 且 vehicleMissing 为空 且团期处于可确认阶段
missing List 改名为「住宿缺失清单」(字段名不变,含义收窄为只描述住宿),结构不变
checkedResourceTypes List 取值变化:恒为 ["HOTEL","VEHICLE"](改前恒为 ["HOTEL"]),服务端常量,非按团期配置动态算出
🆕 vehicleWaived Boolean 整团都不需要车时为 true,此时车侧校验整体跳过、vehicleMissing 恒为空数组。这是合法逃生口,不是异常
🆕 vehicleMissing List 车侧缺失清单(按校验顺序全部列出,不按户合并);vehicleWaived=true 时为空数组
🆕 groupVehicleRequirementId Long(序列化为 String) 当前活跃正式用车需求主键;无活跃正式需求时为 null
🆕 groupVehicleRequirementStatus String 当前活跃正式用车需求状态;无则 null。取值 DRAFT/CONFIRMED/DISPATCHED/DONE/PENDING_RECONFIRM——DISPATCHED 与 DONE 同样属于预检可通过的正常状态,不要渲染成异常
🆕 groupVehicleRequirementVersion Integer 当前活跃正式用车需求版本号;无则 null

missing[](MissingItem,结构不变,仅补充在本节自包含):

字段 类型 说明
orderId Long(序列化为 String) 子订单 ID
orderNo String 子订单号
customerName String 客户姓名
consultantId String 定制师 adminId
consultantName String 定制师姓名快照
reason String NOT_SUBMITTED/ROOM_CATEGORY_MISSING/INVALID_REQUIREMENT/NIGHTS_MISMATCH/DAY_NUMBER_INVALID
reasonName String 缺失原因中文名
dayNumber Integer 第几晚(仅 ROOM_CATEGORY_MISSING 有值)
segmentIndex Integer 第几段(仅 ROOM_CATEGORY_MISSING 有值)
expectedNights Integer 应住晚数(仅 NIGHTS_MISMATCH/DAY_NUMBER_INVALID 有值)
actualNights Integer 实际填写晚数(仅 NIGHTS_MISMATCH/DAY_NUMBER_INVALID 有值)

🆕 vehicleMissing[](VehicleMissingItem):

字段 类型 说明
reason String 取值见「六.5」,与 809 段错误码/809007 一一对应
groupCode String 涉及的乘车分组编码;无分组维度时为 null
tripDate LocalDate 涉及的日期;无日期维度时为 null
orderId Long(序列化为 String) 涉及的子订单 ID;无订单维度时为 null
orderNo String 子订单号快照
detail String 人话描述,与整团确认时抛出的错误报文逐字相同,可直接展示

请求示例

GET /v3/admin/order/group-batch/1867000000001/requirement/confirm-check HTTP/1.1
Authorization: Bearer {token}

(无请求体,仅 Path 参数 groupBatchId。)

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "groupBatchId": "1867000000001",
    "batchStatus": "RESOURCE_PREPARING",
    "batchStatusName": "资源准备中",
    "ready": false,
    "missing": [],
    "checkedResourceTypes": ["HOTEL", "VEHICLE"],
    "vehicleWaived": false,
    "vehicleMissing": [
      {
        "reason": "ORDER_DAY_UNCOVERED",
        "groupCode": null,
        "tripDate": null,
        "orderId": "60123456789001",
        "orderNo": "HL2606010001",
        "detail": "子订单 60123456789001 的 2026-09-13 没有被任何乘车分组覆盖"
      }
    ],
    "groupVehicleRequirementId": "1868000000001",
    "groupVehicleRequirementStatus": "DRAFT",
    "groupVehicleRequirementVersion": 3
  }
}

免车团示例(vehicleWaived=true):

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "groupBatchId": "1867000000002",
    "batchStatus": "RESOURCE_PREPARING",
    "batchStatusName": "资源准备中",
    "ready": true,
    "missing": [],
    "checkedResourceTypes": ["HOTEL", "VEHICLE"],
    "vehicleWaived": true,
    "vehicleMissing": [],
    "groupVehicleRequirementId": "1868000000005",
    "groupVehicleRequirementStatus": "CONFIRMED",
    "groupVehicleRequirementVersion": 1
  }
}

空数据 / 降级响应

团期尚未提交任何正式车需求(未编辑过 PUT .../vehicle-requirement、也未 waive)时,车侧三个身份字段(groupVehicleRequirementId/Status/Version)均为 null,vehicleMissing 按住宿同款缺失校验给出实际内容(不是空数组,除非该团确实零缺失或已免车)。本端点全程同步内存/DB 读取,不经 Feign/MQ,不产生降级分支。

{ "code": 200, "success": true, "data": { "groupVehicleRequirementId": null, "groupVehicleRequirementStatus": null, "groupVehicleRequirementVersion": null, "vehicleMissing": [] } }

错误响应

沿用既有码,本单未新增:

{
  "code": 589500,
  "message": "团期不存在",
  "success": false,
  "data": null
}

业务边界

  • 判权沿用既有 group-batch:demand:confirm(GroupBatchPermissionGuard.PERMISSION_DEMAND_CONFIRM),本单未改。
  • 车侧校验集合口径与保存草稿(PUT .../vehicle-requirement)、整团确认(见下条)完全共用一份校验内核,三处同一份数据得到同一个结论——本端点的 vehicleMissing 为空当且仅当真去点确认不会报车侧错误码(并发写入除外)。
  • vehicleWaived=true 时车侧校验整体跳过,与是否有历史违规无关。
  • 老数据兼容:本单不改任何已有字段的类型或序列化方式,存量前端若忽略新字段仍可正常渲染住宿部分;但 checkedResourceTypes 与 ready 的取值/语义已变,见「⚠️ 关键变化」。

2. 整体确认需求(放行住宿+车) POST /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm

VO: 无请求体 → Result<GroupBatchRequirementConfirmRespVO>

使用场景

团期需求页点击「确认」按钮时调用。改前只校验并放行住宿;本单起在住宿放行成功之后,非免车团额外推进正式车需求并批量放行在团户的车需求(行程用车 TRAVEL + 接送机 TRANSFER 两类)。

入参字段表

字段 位置 类型 必填 约束 说明
groupBatchId Path Long 是 - 团期聚合主键(不变)

(无请求体,本单未改动。)

出参字段表

字段 类型 说明
groupBatchId Long(序列化为 String) 团期聚合主键(不变)
requirementConfirmed Boolean 团期需求整体确认标记,成功后恒 true(不变)
dispatchedOrderIds List(String) 语义不变:仍只统计住宿,本次放行的住宿子订单
skippedOrderIds List(String) 语义不变:仍只统计住宿
dispatchedCount Integer 语义不变:仍只统计住宿(= dispatchedOrderIds.size())
🆕 vehicleDispatchedOrderIds List(String) 本次由「待审核」放行的【行程用车 TRAVEL】需求所属子订单
🆕 transferDispatchedOrderIds List(String) 本次由「待审核」放行的【接送机 TRANSFER】需求所属子订单
🆕 vehicleSkippedOrderIds List(String) 车侧本次未动的子订单(两类合并去重;该户该类车需求已非「待审核」)。整团免车时为空数组
🆕 vehicleDispatchedCount Integer 车侧本次放行的需求条数 = vehicleDispatchedOrderIds.size() + transferDispatchedOrderIds.size()。⚠️ 是条数不是户数,一户两类都放行计 2
🆕 groupVehicleRequirementId Long(序列化为 String) 本次确认所对应的正式车需求主键;整团免车且无正式需求(存量判据)时为 null
🆕 groupVehicleRequirementStatus String 确认后正式车需求的实际状态,不是恒为 CONFIRMED:源状态 DRAFT/PENDING_RECONFIRM → 回 CONFIRMED;CONFIRMED 保持 CONFIRMED;DISPATCHED/DONE 回原值(车侧不动、状态不倒退,属正常态)。免车且无正式需求时为 null
🆕 groupVehicleRequirementVersion Integer 确认后正式车需求版本号(推进到 CONFIRMED 时已 +1,原样返回时不变);免车且无正式需求时为 null

请求示例

POST /v3/admin/order/group-batch/1867000000001/requirement/confirm HTTP/1.1
Authorization: Bearer {token}

(无请求体,仅 Path 参数 groupBatchId。)

响应示例

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "groupBatchId": "1867000000001",
    "requirementConfirmed": true,
    "dispatchedOrderIds": ["60123456789001", "60123456789002", "60123456789003"],
    "skippedOrderIds": [],
    "dispatchedCount": 3,
    "vehicleDispatchedOrderIds": ["60123456789001", "60123456789002", "60123456789003", "60123456789004", "60123456789005"],
    "transferDispatchedOrderIds": [],
    "vehicleSkippedOrderIds": [],
    "vehicleDispatchedCount": 5,
    "groupVehicleRequirementId": "1868000000001",
    "groupVehicleRequirementStatus": "CONFIRMED",
    "groupVehicleRequirementVersion": 2
  }
}

免车团响应示例(vehicleDispatchedCount=0):

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "groupBatchId": "1867000000002",
    "requirementConfirmed": true,
    "dispatchedOrderIds": ["60123456789006"],
    "skippedOrderIds": [],
    "dispatchedCount": 1,
    "vehicleDispatchedOrderIds": [],
    "transferDispatchedOrderIds": [],
    "vehicleSkippedOrderIds": [],
    "vehicleDispatchedCount": 0,
    "groupVehicleRequirementId": "1868000000005",
    "groupVehicleRequirementStatus": "CONFIRMED",
    "groupVehicleRequirementVersion": 1
  }
}

空数据 / 降级响应

不存在空态:本端点是写操作,要么全部成功返回上述结构,要么整个事务回滚并抛错误码。没有部分成功或静默降级的分支。

{ "code": 200, "success": true, "data": { "vehicleDispatchedOrderIds": [], "transferDispatchedOrderIds": [], "vehicleSkippedOrderIds": [], "vehicleDispatchedCount": 0 } }

错误响应

码 符号 触发 本单
589501 GROUP_BATCH_STATUS_INVALID 团期非可确认阶段 不变
589533 GROUP_BATCH_REQUIREMENT_INCOMPLETE 语义收窄:只在住宿缺失时抛,{0} 仍是住宿缺失户数 收窄
🆕 809100 GROUP_VEHICLE_REQUIREMENT_NOT_FOUND 有在团需车户却无正式车需求 本单起会抛
🆕 809101 GROUP_VEHICLE_REQUIREMENT_STATUS_INVALID 正式车需求状态不在 {DRAFT,CONFIRMED,DISPATCHED,DONE,PENDING_RECONFIRM},或推进 CAS 落空 本单起会抛
🆕 809103-809110 车侧八条逐日/成员/人数校验 见「六.5」 本单起会抛
🆕 809007 TRANSFER_SERVICE_DATES_NOT_BACKFILLED 待放行的接送机需求未回填服务日;{0} 为该需求主键(数字,非字符串) 本单起会抛
🆕 809112 GROUP_VEHICLE_DISPATCH_CAS_FAILED 放行某户车需求时并发冲突(CAS 落空),整团事务回滚,零写入 本单起会抛
{
  "code": 589533,
  "message": "仍有 2 户未提交需求或需求不完整,无法整体确认,请先查看缺失清单",
  "success": false,
  "data": null
}

车侧违规示例(该团抛第一条命中的违规,取决于哪条违规先被发现):

{
  "code": 809109,
  "message": "子订单 60123456789001 的 2026-09-13 没有被任何乘车分组覆盖",
  "success": false,
  "data": null
}

业务边界

  • 顺序不可调换:① 阶段守卫(589501)→ ② 住宿缺失校验(589533,零写入)→ ③ 车侧校验(免车团整体跳过,否则抛第一条违规,零写入)→ ④ 置团级标记 + 写团级时间线 → ⑤ 逐户住宿放行 → ⑥ 非免车团:推进正式车需求 → ⑦ 非免车团:逐户放行车需求两类 → ⑧ 事务提交后异步通知房务。全部在同一个事务里,任一步失败整团零写入(809112 整团回滚即靠这一点)。
  • 住宿排在车侧之前:两边都缺时仍报 589533,与改造前一致;只有住宿齐备、车侧有问题时才会看到 809 段码。
  • 免车团(vehicleWaived=true)跳过 ⑥⑦ 两步:正式需求不推进(版本不 +1)、逐户车需求不放行(在团户残留的待审核车需求本次不动,需要就走逐单放行入口)。
  • 809007 排在放行动作之前抛:预检阶段(上一条端点)已经把这类需求列进 vehicleMissing,ready=true 时不会再遇到本码,两者结构性一致。
  • 重复确认不失败:正式车需求已是 CONFIRMED 时重复确认走幂等成功,不抛 809101;已放行的车需求本次计入 vehicleSkippedOrderIds 而不是报错——与住宿侧口径一致。
  • 只改住宿的再次确认必须成功:车侧已 DISPATCHED/DONE、只有住宿需求被打回重提时,再次点整团确认——正式车需求保持原状态不倒退,车侧逐户按「已放行」计入 vehicleSkippedOrderIds(vehicleDispatchedCount=0),住宿正常放行。改前这一场景会被状态白名单直接拒掉,团再也确认不了;本单起这条路走通。
  • 本端点不取车侧专属锁:与住宿放行共用团期需求锁,车侧两步不额外加锁(避免自锁)。
  • 判权沿用既有 group-batch:demand:confirm,本单未改。

四、契约约束与正确调用方式

正确 / 错误 调用结果对照

场景 结果
预检 ready=true 后立即点确认,期间无其他人并发改动(正确) confirm 成功,不因校验类错误码失败(CAS 类失败如 809101/809112 不在此等价关系内)
前端只判断 missing.isEmpty() 就以为可以确认(错误) 车侧仍可能缺失,点确认会报 809 段码;必须同时判 vehicleMissing.isEmpty()(或直接看 ready)
前端按「checkedResourceTypes 恒为 ["HOTEL"]」写死判断(错误) 本单起恒为 ["HOTEL","VEHICLE"],写死判断会得出错误结论
前端按「groupVehicleRequirementStatus 恒为 CONFIRMED」渲染确认结果(错误) 车侧已 DISPATCHED/DONE 时该字段回原值,不是 CONFIRMED;按恒等判断会误判为异常

切换状态时的必要动作

前端渲染「整体确认」按钮的可用性时,必须把 vehicleMissing.isEmpty() 并入判断(或直接读 ready,不要自己用 missing.isEmpty() 重新计算);确认成功后的提示文案不能只读旧三个字段(否则车侧放行结果对用户不可见)。


五、数据库行为

预检端点全程只读,不产生任何写入。确认端点在同一个事务里,除既有的「置团级确认标记 + 住宿放行」外,本单额外做两件事:①非免车团把当前活跃正式车需求的状态从「待确认」推进为「已确认」(若已经是「已确认」及之后的状态则保持不变,不会倒退);②非免车团把在团户的行程用车与接送机需求从「待审核」批量放行为「待处理」。任一步失败(含并发写入冲突)都会让本次确认动作(含住宿放行)整体回滚,不会出现部分成功。


六、边界行为

  • 未登录 → 401(网关拦截)
  • 无权限 → 沿用既有 group-batch:demand:confirm 判权(未改)
  • 团期不存在 → 589500(未改)
  • 团期非可确认阶段 → 589501(未改)
  • 住宿缺失 → 589533(收窄为只管住宿)
  • 车侧缺失(非免车团)→ 809 段码,见「三、2」错误响应表
  • 免车团 → 车侧校验/放行整体跳过,不产生任何车侧相关错误
  • 老数据兼容:存量团期第一次读取本端点时,若从未编辑过车需求,groupVehicleRequirementId/Status/Version 均为 null,不异常

六.5、枚举

vehicleMissing[].reason(服务端内部校验原因码,无独立 Java 枚举类,值见下表)

所属字段: vehicleMissing[].reason | 类型: String

值 对应错误码 说明
GROUP_REQUIREMENT_NOT_FOUND 809100 有在团需车户却无正式车需求
GROUP_REQUIREMENT_STATUS_INVALID 809101 正式车需求状态不在合法集合
NO_GROUP 809103 本团存在需要用车的子订单,却零乘车分组
GROUP_CODE_INVALID 809104 乘车分组重复或试图改名
DAY_OUT_OF_GROUP_RANGE 809105 分组逐日行不在本组服务日范围内或重复提交
DAY_GAP_IN_GROUP_RANGE 809106 分组缺少某日的用车人数
MEMBER_FOREIGN_ORDER 809107 子订单不属于本团期,不能作为乘车成员
MEMBER_DUPLICATE_DAY 809108 子订单在同一日同时属于两个分组
ORDER_DAY_UNCOVERED 809109 子订单某日没有被任何乘车分组覆盖
HEADCOUNT_LESS_THAN_MEMBERS 809110 分组当日用车人数小于当日成员户数
🆕 TRANSFER_SERVICE_DATES_NOT_BACKFILLED 809007 待放行的接送机需求未回填服务日;只带 orderId,groupCode/tripDate 为 null;处置是先回填服务日再确认;复用 #7439 既有码,非本单新增

groupVehicleRequirementStatus(com.hulalv.order.groupbatch.enums.GroupVehicleRequirementStatus)

所属字段: confirm-check/confirm 响应的 groupVehicleRequirementStatus | 类型: String

值 中文 说明
DRAFT 草稿 尚未提交审核
CONFIRMED 已确认 整团确认推进后的常见终态
DISPATCHED 已放行车务 团车已开始配车;确认时保持不动,不倒退
DONE 已完成 团车配完;确认时保持不动,不倒退
PENDING_RECONFIRM 待重确认 再次确认时可推进到 CONFIRMED

六.6、修改前后对比

字段级对比

字段 改前 改后
confirm-check.checkedResourceTypes 恒 ["HOTEL"] 恒 ["HOTEL","VEHICLE"]
confirm-check.ready missing.isEmpty() && 阶段可确认 missing.isEmpty() && vehicleMissing.isEmpty() && 阶段可确认
confirm-check 车侧字段 不存在 新增 vehicleWaived/vehicleMissing/groupVehicleRequirementId/Status/Version 5 个
confirm.dispatchedOrderIds/skippedOrderIds/dispatchedCount 语义为「住宿」 语义不变,仍只统计住宿
confirm 车侧字段 不存在 新增 7 个:见出参字段表
589533 触发条件 住宿或车任一缺失 只在住宿缺失时触发

行为级对比

行为 改前 改后
团期需求页点「确认」,住宿齐、车不齐 200 成功(车侧无感) 809 段码阻断(非免车团)
车侧已 DISPATCHED/DONE,住宿被打回重提后再次确认 809101 阻断,团再也确认不了 200 成功,车侧保持原状态、住宿正常放行
免车团点「确认」 (本端点改造前免车概念不影响本端点) 200 成功,车侧三个 ID 列表为空、vehicleDispatchedCount=0
预检时车侧有缺失 ready 不受车侧影响(预检本不检查车) ready=false,需读 vehicleMissing

六.7、影响评估

  • 是否破坏向后兼容: 是。此前 ready=true(只看住宿)的部分团在本单合并部署后,若车侧未齐备会变成 ready=false;589533 触发条件收窄,改前依赖它同时报告车缺失的前端提示会失真。
  • 前端是否必须同步上线: 是。按「⚠️ 关键变化」逐条改:checkedResourceTypes 判断、ready/vehicleMissing 联合判断、确认响应新增字段的展示、groupVehicleRequirementStatus 不再恒 CONFIRMED 的容错。
  • 前端 workaround 清理点: 无(新增字段与语义收紧,非清理旧逻辑)。

七、不影响范围

  • 仅影响: GET .../requirement/confirm-check 与 POST .../requirement/confirm 两个既有端点的响应体与 confirm 的错误码集合。
  • 零影响:
    • 路径、Path 参数、请求体(均无请求体)——完全不变。
    • 判权码 group-batch:demand:confirm——未改。
    • PUT/GET .../vehicle-requirement、POST .../vehicle-requirement/withdraw、POST .../vehicle-requirement/waive 四个已上线端点自身的契约——本单不改,只是被读取结果。
    • POST .../requirement/reject(按户打回)、GET .../requirement-summary(全团需求汇总)——本单未改,按户打回逐户需求的语义完全不动。
    • POST /v3/admin/order/{id}/vehicle-requirement/dispatch(逐单放行)——保留,与本单整团路径共用同一份副作用实现,未新增未删除。
    • hl-common-*、hl-fleet-service、hl-gateway 路由——本单只改 hl-order-service-v3,/v3/admin/** 路由沿用既有通配,未新增路由配置。

八、测试环境已验证

取证环境:order-v3 = dev-v3 f0277a14f(2026-09-16 01:21 部署,含本单 PR-4 全部改动),经 hl-gateway 网关真实调用,见工单 #7441 验收评论 54924(AC-8/9/10/11/12/15/18)与 54939(AC-16)。

GET .../requirement/confirm-check:

  • 免车逃生口(vehicleWaived=true):ready=true、missing=[]、vehicleMissing=[],车侧三个身份字段均为 null(团 2099918391610314754)。
  • 车侧正式需求缺失(vehicleWaived=false):vehicleMissing=[{"reason":"GROUP_REQUIREMENT_NOT_FOUND","detail":"团期 2099918391610314754 尚未形成正式用车需求"}]。
  • ready 语义变化:房齐备、车零分组的团返回 ready=false、missing=[]、vehicleMissing=[{"reason":"NO_GROUP",...}]——「missing 空但 ready=false」的形态经真实调用坐实。
  • 同日同户重复归属(数据经 SQL 直接向 order_group_vehicle_group/order_group_vehicle_group_day 造重叠行,读侧取证):vehicleMissing=[{"reason":"MEMBER_DUPLICATE_DAY","groupCode":"GB","tripDate":"2026-10-24","orderId":"2099919145398046721","orderNo":"HL20260916015153509"}]。
  • 房侧缺失清单:missing 含两条真实户(orderId 2099918640269627394 / 2099918650218516481,reason=NOT_SUBMITTED)。

POST .../requirement/confirm:

  • 免车团确认成功:code=200、vehicleDispatchedCount=0、groupVehicleRequirementId=null。
  • 车侧需求缺失:code=809100,message="团期 2099918596187598850 尚未形成正式用车需求"。
  • 房齐车零分组:code=809103,message="本团存在需要用车的子订单,至少要提交一个乘车分组"。
  • 房缺 2 户、车侧已免车:code=589533,message="仍有 2 户未提交需求或需求不完整,无法整体确认,请先查看缺失清单"({0}=2 坐实)。
  • 同日同户重复归属:三入口(PUT/confirm-check/confirm)均报 code=809108,message="子订单 2099919145398046721 在 2026-10-24 同时属于分组 GA、GB,同一户同一日只能属于一个分组"——PUT 由业务接口直接触发重叠数据被拒;confirm-check/confirm 因 PUT 会在写入前就拒绝重叠数据、结构上无法经业务路径把该状态存进库,按 SQL 造重叠行后走读侧验证。
  • 重复确认幂等:单户团 T0 confirm 成功(vehicleDispatchedOrderIds=["2099919607505596417"]、vehicleDispatchedCount=1、groupVehicleRequirementStatus=CONFIRMED、version=2),间隔 >5 秒后 T1 再次 confirm 成功且不抛 809101(vehicleSkippedOrderIds=["2099919607505596417"]、vehicleDispatchedCount=0,状态与版本不变)。
  • PENDING_RECONFIRM → CONFIRMED:正式需求经 SQL 置为 PENDING_RECONFIRM 后再次 confirm,成功且 groupVehicleRequirementStatus=CONFIRMED(version=3)。
  • CAS 落空整团回滚:并发导致放行第 3 户车需求时 CAS 落空,抛 809112,前 2 户车需求状态、房侧放行结果、正式需求状态、团期确认标记全部回滚,零写入(评论 54939)。

仅代码核对,未经该场景网关验证:AC-13/14/21 要求的「同一户同时提交 TRAVEL + TRANSFER 两类需求、confirm 一次响应内房 3 户 + 车 5 条同时出现」这一组合场景,因测试环境 TRANSFER 写侧全局开关关闭(既有错误码 809009,#7443 派车侧尚未上线的独立开关,与本单代码无关)未能取得;待该开关在测试环境临时打开后补测,结果回写工单 #7441 验收评论。已取得的替代证据:全员仅提交 TRAVEL 需求的团确认成功,vehicleDispatchedOrderIds 含 3 户、transferDispatchedOrderIds=[]、vehicleDispatchedCount=3——证明 TRAVEL 单类路径工作正常,但未覆盖两类需求同户并存、以及打回其中一类后另一类不受影响(POST .../vehicle-requirement/reject?kind=TRANSFER)的场景;相关字段的类型与计算口径已按源码核对(见「出参字段表」与「六.6」),这部分示例响应仍是按源码拼装的合理构造,不是该组合场景的实测原文。


十、相关文档

  • 关联 Issue: wx/HL#7441
  • 关联 PR: #7778(squash 合入 dev-v3,合并提交 f0277a14f)
  • 前置依赖:#7535(首次引入 checkedResourceTypes,本单是其承诺的「扩到用车」那张单)、#7210(confirm-check/confirm 首次交付,只管住宿)、#7441 PR-1/PR-2/PR-2b/PR-2c/PR-3(团期正式车需求声明、整团免车、团车完成回写、结算闸,均已合并 dev-v3,本单依赖它们提供的数据但不改它们的契约)
  • 关联文档:本单合并部署后的户级/团级联动效果(确认行程清单、待办、团期详情看板、物资门等)见同批另一份 changelog(#7441 PR-2d/PR-2e,同一个 PR #7778)
  • 验收口径边界:页面完成状态单独跟踪(前端归 mmg),不计入 #7441 验收;#7441 验收范围 = API 契约 + 状态流转 + 占用账本 + changelog 交接件。

关联 / 联系人

链接

联系人

  • 后端负责人: @wx
  • 前端负责人(收件人): @mmg