文件
hl-api-changelog/changelogs-v2/2026-09/23_8249_查看需求页未提交状态与户级用车预检-修改接口-管理后台.md
T
2026-09-23 19:28:20 +08:00

36 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 8249 团期「查看需求」:无需求行的户不再报 PENDING、户级未提交用车需求进预检清单、六条车务报文改写为可读名称 admin wx(GIT) 修改接口 deployed verified not_required 前端判 not_required:状态列 statusName||status||— 三级兜底 null 安全;PENDING 判等 12 处全无关;vehicleMissing 按 orderNo 结构化分流即契约推荐判据;809 报文零文本匹配分支(拦截器透 toast/detail 直显) 2026-09-23 dev-v3

order-v3: 团期「查看需求」页三处契约变更(含报文变更)

存放目录: changelogs-v2/{YYYY-MM}/

服务: hl-order-service-v3 (端口 8007) PR: #8286 Issue: #8249 日期: 2026-09-23 影响范围: 管理后台「团期详情 → 查看需求」tab 的子订单列表状态列、整团确认预检横幅、车务相关弹窗文案


⚠️ 关键变化

  1. 状态字段会返 null 了:hotelRequirementStatus / vehicleRequirementStatus 在「该户一条 active 需求行都没有」时返回 null。改前恒回落字符串 "PENDING",页面把一个根本没提交的户显示成「待房务配 / 待车队配」,与同屏预检横幅里的 NOT_SUBMITTED 自相矛盾。配对的 *StatusName 此时按该户是否需要该资源分叉:需要 → "未提交",不需要 → null。
  2. 预检清单多一类条目:confirm-check 的 vehicleMissing[] 新增 reason = "HOUSEHOLD_REQUIREMENT_NOT_SUBMITTED"(错误码 809122),表示「该户在团需车、却一条行程用车需求都没提交」。它与团级的 GROUP_REQUIREMENT_NOT_FOUND 并列出现,不互斥、不被团级缺席短路——改前车侧只查团级正式需求,「某户一行都没提交」在预检里没有任何位置能表达。
  3. 🔴 报文变更:六个 809 段错误码的渲染文本被改写(雪花 ID → 可读名称 / 直接删掉标识符),见「六.6、修改前后对比」。因为 GroupVehicleViolation.detail() 就是 toException().getMessage(),预检横幅上的 detail 与确认/保存/免车弹窗里的 message 是同一份字符串,两处同时改变。凡是对 vehicleMissing[].detail 或错误 message 做文本匹配 / 正则解析 / 截取订单号的前端逻辑,这次会失效;纯展示(原样渲染这段人话)不需要改。订单号已经由 vehicleMissing[].orderNo 结构化下发,不要再从 detail 里抠。

一、背景

「查看需求」页上同一个团期的同一户,三处读数互相矛盾:子订单列表说「待房务配」(=已提交、等资源侧接),预检横幅说「未提报」,而整团确认点下去报的是团级的「尚未形成正式用车需求」——运营据此去催车务,实际该催的是定制师。

维度 改前 改后
无 active 需求行时 *Status "PENDING"(借用了真实状态之一) null
无 active 需求行时 *StatusName 「待房务配」/「待车队配」 「未提交」(该户需要该资源时)/ null
车侧预检覆盖粒度 只有团级正式需求的六条校验 团级六条 + 逐户查行程用车需求是否提交 + 待放行接送机需求服务日回填
车务报文里的标识符 雪花 ID(团期 2100856430494973953、子订单 2102309919002943489) 可读名称(团期「第3期 10月8日出发团」、子订单 HL20260922161158291)或直接省略

二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 A3 团期下子订单列表 GET /v3/admin/order/group-batch/{groupBatchId}/orders 出参取值变更 四个需求状态字段在无 active 行时改为 null + 「未提交」
2 GB-ADM-012 整团确认需求缺失预检 GET /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check 出参枚举扩充 + 报文变更 vehicleMissing[].reason 新增 HOUSEHOLD_REQUIREMENT_NOT_SUBMITTED;detail 文案改写

三、接口详情

1. A3 团期下子订单列表 GET /v3/admin/order/group-batch/{groupBatchId}/orders

VO: Query 参数 → PageResult<GroupBatchOrderItemRespVO>

使用场景

管理后台「团期详情 → 查看需求 / 子订单」tab 打开时调用,渲染该团期下每一户的一行摘要(团号、客户、金额、房需求状态、车需求状态、定制师、出行人)。本次改动只影响其中四个需求状态字段的取值,分页与其余字段的契约不变。鉴权走团期权限码 group-batch:view,未登录由网关拦截。

入参

字段 位置 类型 必填 约束 说明
groupBatchId Path Long ✅ 雪花 ID,团期聚合主键 团期 ID
page Query Integer ❌ 缺省 1;小于 1 归一为 1 页码,从 1 起
pageSize Query Integer ❌ 缺省 20;小于 1 归一为 20;大于 200 截断为 200 每页条数
includeTravelers Query Boolean ❌ 缺省 true 是否附 travelers[];证件号/手机号一律不返回
includeNeeds Query Boolean ❌ 缺省 true 是否附 roomCount / roomType / specialNeeds
includeCancelled Query Boolean ❌ 缺省 false 是否含已取消子订单;缺省只返活跃集

出参 Result<PageResult<GroupBatchOrderItemRespVO>>

字段 类型 说明
data.total Long 总条数
data.page Integer 当前页码
data.pageSize Integer 每页条数
data.records[].orderId String 子订单 ID(雪花,JSON 里是字符串)
data.records[].orderNo String 订单编号
data.records[].teamNo String 团号;订金支付成功后生成,未付订金为 null
data.records[].customerName String 客户姓名
data.records[].participantCount Integer 出行人总数
data.records[].orderStatus String 订单状态码
data.records[].orderStatusName String 订单状态中文名
data.records[].payStatus String 支付状态(UNPAID / DEPOSIT_PAID / FULLY_PAID)
data.records[].payStatusName String 支付状态中文名
data.records[].contractStatus String 合同状态;无合同时 null
data.records[].contractStatusName String 合同状态中文名;无合同时 null
data.records[].insuranceStatus String 保险状态;无保险时 null
data.records[].insuranceStatusName String 保险状态中文名;无保险时 null
data.records[].paidAmount String 已支付金额(订金 + 尾款),两位小数字符串
data.records[].balanceAmount String 待支付尾款金额,两位小数字符串
data.records[].hotelRequirementStatus String 本次变更:房需求状态码;无 active 房需求行时为 null(改前回落 "PENDING")
data.records[].hotelRequirementStatusName String 本次变更:房需求状态中文名;hotelRequirementStatus 为 null 时该户需房则为 "未提交"、不需房则为 null
data.records[].vehicleRequirementStatus String 本次变更:车需求状态码;无 active 行时为 null(改前回落 "PENDING")
data.records[].vehicleRequirementStatusName String 本次变更:车需求状态中文名;vehicleRequirementStatus 为 null 时该户需车则为 "未提交"、不需车则为 null
data.records[].consultantName String 定制师姓名(创单时固化)
data.records[].totalPrice String 本户应收,两位小数字符串;取消单为 "0.00"
data.records[].tierCode String 档位码(形如 2A1C)
data.records[].tierName String 档位名(形如 2成人1儿童)
data.records[].travelerInfoComplete Boolean 出行人资料是否齐全
data.records[].roomCount Integer 房数;includeNeeds=true 时返回
data.records[].roomType String 房型文本;includeNeeds=true 时返回,无需求行时 null
data.records[].roomTypeName String 房型中文名;字典不可达时回落原值
data.records[].specialNeeds String 特殊需求;缺需求行时回落订单备注
data.records[].contactPhone String 联系人手机号(脱敏,前 3 后 4)
data.records[].groupChatUnreadCount Integer 「联系定制师」按钮未读角标
data.records[].travelers[].name String 出行人姓名
data.records[].travelers[].type String 出行人类型(ADULT / CHILD / YOUNG_CHILD / BABY)
data.records[].travelers[].age Integer 出团日时的周岁;缺出生日期时 null
data.records[].travelers[].birthdayInTrip Boolean 是否行程期间过生日

请求示例

GET /v3/admin/order/group-batch/2100856430494973953/orders HTTP/1.1
Host: <网关地址>
Authorization: Bearer <admin token>

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "records": [
      {
        "orderId": "2100856430239121409",
        "orderNo": "HL20260918155619496",
        "teamNo": "26-7060",
        "customerName": "王有亿",
        "participantCount": 3,
        "orderStatus": "CUSTOMIZING",
        "orderStatusName": "定制中",
        "payStatus": "DEPOSIT_PAID",
        "payStatusName": "已付定金",
        "contractStatus": null,
        "contractStatusName": null,
        "insuranceStatus": null,
        "insuranceStatusName": null,
        "paidAmount": "1500.00",
        "balanceAmount": "9440.00",
        "hotelRequirementStatus": "PENDING_REVIEW",
        "hotelRequirementStatusName": "待审核",
        "vehicleRequirementStatus": "PENDING_REVIEW",
        "vehicleRequirementStatusName": "待提交车务",
        "consultantName": "王骁",
        "totalPrice": "10940.00",
        "tierCode": "2A1C",
        "tierName": "2成人1儿童",
        "travelerInfoComplete": true,
        "roomCount": 1,
        "roomType": "DELUXE",
        "roomTypeName": "豪华房",
        "specialNeeds": "禁烟",
        "contactPhone": "137****1407",
        "groupChatUnreadCount": 0,
        "travelers": [
          { "name": "王有亿", "type": "ADULT", "age": 41, "birthdayInTrip": false },
          { "name": "李美丽", "type": "ADULT", "age": 36, "birthdayInTrip": false },
          { "name": "王小明", "type": "CHILD", "age": 8, "birthdayInTrip": false }
        ]
      },
      {
        "orderId": "2102309919002943489",
        "orderNo": "HL20260922161158291",
        "teamNo": "26-2355",
        "customerName": "王二麻子",
        "participantCount": 4,
        "orderStatus": "CUSTOMIZING",
        "orderStatusName": "定制中",
        "payStatus": "DEPOSIT_PAID",
        "payStatusName": "已付定金",
        "contractStatus": null,
        "contractStatusName": null,
        "insuranceStatus": null,
        "insuranceStatusName": null,
        "paidAmount": "2000.00",
        "balanceAmount": "13920.00",
        "hotelRequirementStatus": null,
        "hotelRequirementStatusName": "未提交",
        "vehicleRequirementStatus": null,
        "vehicleRequirementStatusName": "未提交",
        "consultantName": "刘畅",
        "totalPrice": "15920.00",
        "tierCode": "4A",
        "tierName": "4成人",
        "travelerInfoComplete": false,
        "roomCount": 2,
        "roomType": null,
        "roomTypeName": null,
        "specialNeeds": "干",
        "contactPhone": "185****0000",
        "groupChatUnreadCount": 0,
        "travelers": []
      }
    ],
    "total": 2,
    "page": 1,
    "pageSize": 20
  },
  "traceId": null,
  "success": true
}

空数据 / 降级响应

团期下没有活跃子订单(或 page 超出范围)时返回空页,信封与分页字段结构同上,records 为空数组,不返 404、不 500:

{
  "code": 200,
  "message": "成功",
  "data": { "records": [], "total": 0, "page": 1, "pageSize": 20 },
  "traceId": null,
  "success": true
}

错误响应

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

无团期权限或该团期不在本人名下:

{
  "code": 589507,
  "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
  "data": null,
  "traceId": null,
  "success": false
}

业务边界

  • 业务失败走 HTTP 200 + success:false 的信封;未登录由网关返 401,不在本接口的码集里。
  • hotelRequirementStatus / vehicleRequirementStatus 与各自的 *StatusName 必须成对读:Status 为 null 且 StatusName 为 "未提交",表示该户需要这项资源但一条需求行都没提交;两者都为 null 表示该户本来就不需要这项资源,页面渲染「—」。
  • 不要再用 status === 'PENDING' 判断「没提交」——PENDING 现在唯一含义是「已提交、等资源侧接单」。判「没提交」的唯一判据是 status == null。
  • *StatusName 在遇到枚举外的未知码时回落原 code,前端渲染保持 statusName || status || '—' 的三级兜底即可,不会拿到空串。
  • 本接口只读,不写库;重复调用无副作用。
  • includeCancelled=false(缺省)时已取消子订单不出现在结果里,total 同口径。

2. GB-ADM-012 整团确认需求缺失预检 GET /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check

VO: Path 参数 → GroupBatchRequirementCheckRespVO

使用场景

管理后台在点「整体确认需求」之前调用,用于把按钮置灰并把缺失清单摊开给运营看。它与 POST .../requirement/confirm 共用同一套校验:预检里出现的每一条,都会在确认时以对应错误码抛出且零写入。鉴权走 group-batch:demand:confirm。本次改动新增了车侧的户级校验,并改写了车侧 detail 的文案。

入参

字段 位置 类型 必填 约束 说明
groupBatchId Path Long ✅ 雪花 ID,团期聚合主键 团期 ID;无 Query、无 Body

出参 Result<GroupBatchRequirementCheckRespVO>

字段 类型 说明
data.groupBatchId String 团期聚合主键
data.batchStatus String 团期当前状态码
data.batchStatusName String 团期状态中文名;batchStatus 为 null 时为 null
data.ready Boolean 是否可整体确认:missing 空 且 vehicleMissing 空 且阶段可确认。missing 空而 ready=false 时原因在 vehicleMissing
data.missing[] Array 住宿缺失清单,按 orderId 升序,每户至多一条
data.missing[].orderId String 子订单 ID
data.missing[].orderNo String 子订单号
data.missing[].customerName String 客户姓名
data.missing[].consultantId String 定制师 adminId(字符串,便于拼跳转)
data.missing[].consultantName String 定制师姓名快照
data.missing[].reason String 住宿缺失原因码,见六.5
data.missing[].reasonName String 住宿缺失原因中文名
data.missing[].dayNumber Integer 第几天;无该维度时 null
data.missing[].segmentIndex Integer 段序;无该维度时 null
data.missing[].expectedNights Integer 期望晚数;无该维度时 null
data.missing[].actualNights Integer 实际晚数;无该维度时 null
data.checkedResourceTypes Array 本次预检覆盖的资源类型,恒为 ["HOTEL","VEHICLE"];两类均已覆盖到逐户粒度
data.vehicleWaived Boolean 整团免车时为 true,此时车侧校验整体跳过、vehicleMissing 恒为空数组
data.vehicleMissing[] Array 车侧缺失清单,按校验顺序全部列出、不按户合并;vehicleWaived=true 时为空数组
data.vehicleMissing[].reason String 本次扩充:车侧缺失原因码,新增 HOUSEHOLD_REQUIREMENT_NOT_SUBMITTED,见六.5
data.vehicleMissing[].groupCode String 涉及的乘车分组编码;无分组维度时 null
data.vehicleMissing[].tripDate String 涉及日期(yyyy-MM-dd);无日期维度时 null
data.vehicleMissing[].orderId String 涉及的子订单 ID;无订单维度时 null
data.vehicleMissing[].orderNo String 子订单号快照;订单不属于本团期时 null
data.vehicleMissing[].detail String 本次报文变更:人话描述,与整团确认抛出的错误报文逐字相同,可直接展示;户级条目不再带雪花 orderId
data.groupVehicleRequirementId String 当前活跃正式用车需求主键;无则 null
data.groupVehicleRequirementStatus String 活跃正式用车需求状态;无则 null。DISPATCHED / DONE 同属预检可通过的正常状态
data.groupVehicleRequirementVersion Integer 活跃正式用车需求版本号;无则 null
data.transferSubmitEnabled Boolean 当前环境是否开放接送机需求提交;false 时提交 kind=TRANSFER 会被 809004 拒
data.transferDeclaredWithoutRequirement[] Array 声明了接送机却没报 TRANSFER 需求的户(提示性,不进 ready、不阻断确认);无此类户时为空数组、不是 null

请求示例

GET /v3/admin/order/group-batch/2100856430494973953/requirement/confirm-check HTTP/1.1
Host: <网关地址>
Authorization: Bearer <admin token>

响应示例

{
  "code": 200,
  "message": "成功",
  "data": {
    "groupBatchId": "2100856430494973953",
    "batchStatus": "RESOURCE_PREPARING",
    "batchStatusName": "资源准备中",
    "ready": false,
    "missing": [
      {
        "orderId": "2102309919002943489",
        "orderNo": "HL20260922161158291",
        "customerName": "王二麻子",
        "consultantId": "2083111674486693889",
        "consultantName": "刘畅",
        "reason": "NOT_SUBMITTED",
        "reasonName": "未提报",
        "dayNumber": null,
        "segmentIndex": null,
        "expectedNights": null,
        "actualNights": null
      }
    ],
    "checkedResourceTypes": ["HOTEL", "VEHICLE"],
    "vehicleWaived": false,
    "vehicleMissing": [
      {
        "reason": "GROUP_REQUIREMENT_NOT_FOUND",
        "groupCode": null,
        "tripDate": null,
        "orderId": null,
        "orderNo": null,
        "detail": "团期「第3期 10月8日出发团」尚未形成正式用车需求"
      },
      {
        "reason": "HOUSEHOLD_REQUIREMENT_NOT_SUBMITTED",
        "groupCode": null,
        "tripDate": null,
        "orderId": "2102309919002943489",
        "orderNo": "HL20260922161158291",
        "detail": "该户尚未提交行程用车需求,请先让定制师提交后再整团提交车务"
      }
    ],
    "groupVehicleRequirementId": null,
    "groupVehicleRequirementStatus": null,
    "groupVehicleRequirementVersion": null,
    "transferSubmitEnabled": true,
    "transferDeclaredWithoutRequirement": []
  },
  "traceId": null,
  "success": true
}

空数据 / 降级响应

需求齐备、可以整体确认时,两份清单都是空数组(不是 null),ready 为 true;字段集与上例完全相同:

{
  "code": 200,
  "message": "成功",
  "data": {
    "groupBatchId": "2100856430494973953",
    "batchStatus": "RESOURCE_PREPARING",
    "batchStatusName": "资源准备中",
    "ready": true,
    "missing": [],
    "checkedResourceTypes": ["HOTEL", "VEHICLE"],
    "vehicleWaived": false,
    "vehicleMissing": [],
    "transferDeclaredWithoutRequirement": []
  },
  "traceId": null,
  "success": true
}

错误响应

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

无需求确认权限:

{
  "code": 589507,
  "message": "无操作权限(当前角色未授予团期权限,或该团期不在您名下)",
  "data": null,
  "traceId": null,
  "success": false
}

业务边界

  • 本接口只读,任何情况下不写库,可安全重复调用。
  • vehicleMissing 不按户合并、不短路:团级的 GROUP_REQUIREMENT_NOT_FOUND 与户级的 HOUSEHOLD_REQUIREMENT_NOT_SUBMITTED 会同时出现(实测响应即如此)。前者说「团级需求没形成」,后者说「是谁挡着它形成」,两条的处置对象不同,都要渲染出来。
  • 区分条目是团级还是户级的唯一结构化判据是 orderId / orderNo 是否为 null,不要靠解析 detail 文本。户级条目恒带 orderId 与 orderNo。
  • missing 为空但 ready=false 时,原因一定在 vehicleMissing;vehicleWaived=true 时车侧整体跳过,vehicleMissing 恒为空数组,这是合法逃生口不是异常。
  • transferDeclaredWithoutRequirement 是提示性名单,不进 ready、不阻断确认,也不并进 vehicleMissing;transferSubmitEnabled=false 时这份名单照报,此时该提示「当前环境未开放接送机需求提交」而不是隐藏名单。
  • 预检通过不等于确认一定成功:确认时的 CAS 冲突(809101 / 809112)只在写入阶段才可能发生,整团回滚、零写入。

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

本节只写后端返回什么、前端据什么判定,不写 UI 渲染建议。

✅ 正确 / ❌ 错误的判定写法

场景 判定写法
✅ 判「该户没提交房需求」 hotelRequirementStatus === null && hotelRequirementStatusName === '未提交',或直接 hotelRequirementStatus == null
✅ 判「该户不需要房」 hotelRequirementStatus == null && hotelRequirementStatusName == null
✅ 渲染状态列 statusName ?? status ?? '—'(三级兜底,null 安全)
❌ 判「没提交」用 status === 'PENDING' PENDING 现在唯一含义是「已提交、等资源侧接单」,这样写会把真正在排队的户也算成未提交
❌ 从 vehicleMissing[].detail 里正则抠订单号 报文已改写,户级条目的 detail 里不再有 ID;订单号读 vehicleMissing[].orderNo
✅ 判车侧缺失条目是团级还是户级 item.orderId == null → 团级(跳去团级用车需求编辑);否则户级(跳到该子订单)
❌ 假设 vehicleMissing 至多一条 团级与户级条目会并列出现,数组长度可以大于 1

报文变更涉及的调用口

下列写口在失败时抛出的 message 与预检 detail 同源,本次一起改变;它们的请求契约、字段名、错误码码值都没有变,变的只是给人看的那段文本:

方法 路径 涉及错误码 弹窗场景
POST /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm 809100 / 809103-809110 / 809122 / 809007 点「整体确认需求」,车侧不过时弹第一条违规的报文(零写入)
PUT /v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement 809107 / 809108 / 809109 / 809115 保存/提交乘车分组被校验拒绝
POST /v3/admin/order/group-batch/{groupBatchId}/vehicle-requirement/waive 809114 声明整团免车时车务已开工被拒
GET /v3/admin/order/group-batch/{groupBatchId}/requirement/confirm-check 同上全部 预检横幅里的 vehicleMissing[].detail

五、数据库行为

本次两个变更接口都是只读端点,零写入。

前端动作 写库行为
调 GET .../orders 无
调 GET .../requirement/confirm-check 无
调 POST .../requirement/confirm 且车侧校验不过(含新增的 809122) 无:阶段守卫 → 住宿缺失(589533)→ 车侧校验的顺序全部排在任何写入之前,抛出即整笔事务零写入

新增的 809122 不引入任何新表、新列、新状态值;它是对既有「该户有没有 active 行程用车需求行」的一次查询结果的表达。


六、边界行为

  • 未登录 → 401(网关拦截,不进业务码集)。
  • 团期不存在 → HTTP 200 + code=589500。
  • 无权限 / 团期不在本人名下 → HTTP 200 + code=589507。
  • 老数据兼容:历史上没有 active 需求行的户,改前读出 "PENDING"、改后读出 null,存量数据不迁移,同一条记录在两个版本上读数不同,差异来自读取时的回落逻辑而非库里的值。
  • 枚举外未知状态码:*StatusName 回落原 code,不抛异常、不返空串。
  • 该户不需要房 / 不需要车时,对应的 *Status 与 *StatusName 都是 null,前端渲染「—」。
  • vehicleMissing 与 transferDeclaredWithoutRequirement 在「一条都没有」时是空数组,不是 null。

六.5、枚举 / 数据字典

hotelRequirementStatus / vehicleRequirementStatus(RequirementStatus)

所属字段: GroupBatchOrderItemRespVO.hotelRequirementStatus / .vehicleRequirementStatus | 类型: String

值 中文(房 / 车) 说明
null 未提交 / null 本次新增取值:该户没有 active 需求行。该户需要这项资源 → *StatusName 为「未提交」;不需要 → *StatusName 也是 null
PENDING 待房务配 / 待车队配 已提交并放行,等资源侧接单
PROCESSING 配房中 / 配车中 资源侧处理中
DONE 配房完成 / 配车完成 资源侧完成
PENDING_REVIEW 待审核 / 待提交车务 定制师已报、等管理员动作;车侧本列恒为行程用车(TRAVEL),故用「待提交车务」(#8218)
REJECTED_TO_CONSULTANT 已驳回定制师 打回定制师重提
REJECTED_TO_ADMIN 已驳回管理员 打回管理员

vehicleMissing[].reason(车侧缺失原因)

所属字段: GroupBatchRequirementCheckRespVO.VehicleMissingItem.reason | 类型: String

值 中文 说明
GROUP_REQUIREMENT_NOT_FOUND 团级正式用车需求未形成 809100;团级维度,orderId / orderNo 为 null
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
HOUSEHOLD_REQUIREMENT_NOT_SUBMITTED 该户尚未提交行程用车需求 本次新增,809122;户级维度,恒带 orderId / orderNo;处置是催该户定制师提交
TRANSFER_SERVICE_DATES_NOT_BACKFILLED 待放行接送机需求未回填服务日 809007;只带 orderId

missing[].reason(住宿缺失原因)

所属字段: GroupBatchRequirementCheckRespVO.MissingItem.reason | 类型: String

值 中文 说明
NOT_SUBMITTED 未提报 该户需房但没有 active 房需求行(含被打回后未重提)
ROOM_CATEGORY_MISSING 缺房型 需求行存在但房型行缺失
INVALID_REQUIREMENT 需求结构不合法 days 结构不可用
NIGHTS_MISMATCH 晚数不符 实际晚数与期望晚数不一致
DAY_NUMBER_INVALID 天序不合法 dayNumber 越界或重复

六.6、修改前后对比

字段级对比

字段 改前 改后
GroupBatchOrderItemRespVO.hotelRequirementStatus 无 active 房需求行时回落 "PENDING" 无 active 行时为 null
GroupBatchOrderItemRespVO.hotelRequirementStatusName 相应显示「待房务配」 code 为 null 时:该户需房 → "未提交";不需房 → null
GroupBatchOrderItemRespVO.vehicleRequirementStatus 无 active 车需求行时回落 "PENDING" 无 active 行时为 null
GroupBatchOrderItemRespVO.vehicleRequirementStatusName 相应显示「待车队配」 code 为 null 时:该户需车 → "未提交";不需车 → null
GroupBatchRequirementCheckRespVO.vehicleMissing[].reason 11 个取值 追加 HOUSEHOLD_REQUIREMENT_NOT_SUBMITTED,共 12 个
GroupBatchRequirementCheckRespVO.checkedResourceTypes 值仍是 ["HOTEL","VEHICLE"],但车侧只覆盖团级 值不变;车侧实际覆盖扩到逐户(这是把字段宣称的覆盖补齐,不是取值变化)

报文变更(六个错误码的渲染文本)

错误码 改前渲染 改后渲染
809100 团期 2100856430494973953 尚未形成正式用车需求 团期「第3期 10月8日出发团」尚未形成正式用车需求;团期名与期号快照都缺时为 该团期尚未形成正式用车需求
809107 子订单 2102309919002943489 不属于本团期,不能作为乘车成员 该子订单不属于本团期,不能作为乘车成员
809108 子订单 2102309919002943489 在 2026-10-09 同时属于分组 BUS、SUV,同一户同一日只能属于一个分组 该子订单在 2026-10-09 同时属于分组 BUS、SUV,同一户同一日只能属于一个分组
809109 子订单 2102309919002943489 的 2026-09-13 没有被任何乘车分组覆盖 该子订单的 2026-09-13 没有被任何乘车分组覆盖
809114 车务已开工(团期 2100856430494973953:团级配车已就绪) / 车务已开工(子订单 2102309919002943489:用车需求已到 DISPATCHED) 车务已开工(团期「第3期 10月8日出发团」:团级配车已就绪) / 车务已开工(子订单 HL20260922161158291:用车需求已到 DISPATCHED);取不到订单号时为 某子订单
809115 团期 2100856430494973953 已声明整团免车,提交乘车分组前请先整份撤回(withdraw)回草稿 团期「第3期 10月8日出发团」已声明整团免车,提交乘车分组前请先整份撤回(withdraw)回草稿

码值一个都没变,变的只是 message / detail 的文本。809107 / 809108 / 809109 是直接删掉标识符:这三条只出现在预检清单上,而清单行已经由 orderNo 字段结构化带了订单号,文本里再带一个雪花 ID 就是同一行上的第二个标识符。809114 保留标识符只把 ID 换成订单号:它只落在免车声明弹窗上,弹窗外没有第二处说明是哪一户。

行为级对比

行为 改前 改后
团里有户一条需求都没提交时的列表显示 「待房务配 / 待车队配」(与同屏预检横幅的「未提报」矛盾) 「未提交」
车侧预检对「某户一行行程用车需求都没提交」 无任何表达位置,整团确认只报团级的 809100 vehicleMissing 里多一条 809122,带 orderId / orderNo
团级缺席时是否还检查户级 团级缺席即返回,不再往下查 两层并列输出,团级缺席不短路户级
整团确认在户级未提交时的结果 报 809100(运营据此去催车务,方向错了) 报 809122(催该户定制师提交),零写入
车务报文里的标识符 雪花 ID 团期可读名称 / 订单号 / 直接省略

六.7、影响评估

  • 是否破坏向后兼容: 是(部分)。*Status 新增 null 取值;对 detail / message 做文本匹配或抠 ID 的逻辑会失效。字段名、字段数量、错误码码值、请求契约均未变。
  • 前端是否必须同步上线: 否。已核对 hl-ui origin/v2.1 的 src/views/order-v2/batch/detail/components/RequirementTab.vue:状态列渲染为 r.hotelRequirementStatusName || r.hotelRequirementStatus || '—'(:812-814),对 null 安全;车侧缺失清单按 item.orderNo 是否存在分流(:654 vehicleReqMissing、:708-714 onVehicleMissingClick),809122 条目带 orderNo,会被正确归为户级并跳到该子订单。当前前端不改也能正确工作。
  • 前端 workaround 清理点: 若页面上有「把 PENDING 当作未提交」的本地兜底判断,可以撤掉——后端现在用 null 表达「未提交」,语义是唯一的。
  • 风险面: 唯一需要人工确认的是「是否存在对 vehicleMissing[].detail 做字符串解析的代码」。上述两个消费点都只做原样展示,未发现解析逻辑。

七、不影响范围

  • 仅影响: 管理后台「团期详情 → 查看需求」tab 的子订单状态列、整团确认预检横幅、车务相关弹窗文案。
  • 零影响:
    • 小程序端(mp)全部接口:本次改动只落在 /v3/admin/ 前缀下。
    • GET /v3/admin/order/group-batch/{groupBatchId}/requirement-summary(用房·汇总 / 用车·汇总):字段与取值一个没动。该页此前的「汇总加载失败」已定位为前端同 tick 重复请求被去重拦截器 abort 所致,mmg 已在 hl-ui 4b33ccbd9 修复并已在测试环境前端 dist 里(2026-09-23 18:06 构建),与本次后端改动无关。
    • 房需求的提交 / 打回 / 放行链路:missing[] 的口径、589533 的触发条件与报文一律不变。
    • 接送机(TRANSFER)相关:transferSubmitEnabled、transferDeclaredWithoutRequirement、809007 的语义与取值不变。
    • 订单列表 / 订单详情 / 金额相关接口:未触及。
    • 历史数据:存量不迁移;差异只来自读取时的回落逻辑。
    • 数据库:无 DDL、无 Flyway 脚本、无新列。

八、测试环境已验证

部署:hl-order-service-v3 @ dev-v3 fc81fff1f,jar 构建时间 2026-09-23 18:36:51,两个实例(8086 / 8186)均 UP。以下读数经网关实测:

GET /v3/admin/order/group-batch/2100856430494973953/orders
  → 200;26-2355(HL20260922161158291)hotelRequirementStatus=null / StatusName=未提交,
    vehicleRequirementStatus=null / StatusName=未提交 ✓
  → 200;26-7060(HL20260918155619496)hotelRequirementStatus=PENDING_REVIEW / 待审核,
    vehicleRequirementStatus=PENDING_REVIEW / 待提交车务(无回归)✓

GET /v3/admin/order/group-batch/2100856430494973953/requirement/confirm-check
  → 200;vehicleMissing 里 GROUP_REQUIREMENT_NOT_FOUND 与
    HOUSEHOLD_REQUIREMENT_NOT_SUBMITTED 两条同时出现(未被团级缺席短路)✓
  → 809100 的 detail 已是「团期「第3期 10月8日出发团」尚未形成正式用车需求」(无雪花 ID)✓
  → 809122 的条目带 orderId=2102309919002943489 / orderNo=HL20260922161158291 ✓
  → checkedResourceTypes=["HOTEL","VEHICLE"]、vehicleWaived=false、ready=false ✓

GET /v3/admin/order/group-batch/2100856430494973953/requirement-summary
  → 200 × 3 次(3.56s / 2.11s / 2.55s),服务端日志零 ERROR / Exception ✓

验证团期: groupBatchId=2100856430494973953(第3期 10月8日出发团,2 户活跃子订单)


十、相关文档

  • 关联 Issue: wx/HL#8249
  • 关联 PR: wx/HL#8286
  • 同页状态文案的上一次分叉(PENDING_REVIEW 车侧按 kind 分叉): changelogs-v2/2026-09/23_8218_行程用车状态文案按kind分叉-修改接口-管理后台.md

关联 / 联系人

链接

联系人

  • 后端负责人: @wx