文件
hl-api-changelog/changelogs-v2/2026-09/13_7439_团期车务地基-用车需求分家-修改接口-管理后台.md
T
Mimingguang ac12dabe20
changelog-filename-gate / validate (push) Failing after 2s
docs(changelog): #7443 C 车务侧+13_7439/18_7443/20_7990 前端已交付 verified(hl-admin v2.1 662310ea/6c091ef24)
18_7443 挂起期回头补落地(派车弹窗 kind 切换+batch/pickup-dropoff-config 显式 kind);
20_7990 requirementIdentities 已消费;13_7439 硬契约点 A+B 已补(809008/显式 kind/reject 走 query);
20_7443 AC-24 维持 not_required 仅补 C 段实证
2026-09-21 17:52:22 +08:00

34 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 7439 团期车务地基——用车需求按 kind 分家、服务日派生、手录行双身份 admin wx(GIT) 修改接口 deployed verified verified mmg 6c091ef2486e0844d5bf1e39c705e43bec875e4f v2.1 2026-09-21 本文件是 #7439 的对外分册(7 个 /v3/admin 端点)。4 个 /v3/internal 端点已按 BACKEND_CHANGELOG_DELIVERY_GUIDE.md 2.5 节拆出为内部分册 13_7439_团期车务地基-内部接口-修改接口-管理后台.md,两份同批交付。【前端 2026-09-13 判 not_required】本单为接送机(TRANSFER)打地基,但开关 transfer-kind-submit-enabled 默认 false、本期不产生 TRANSFER 行、不传 kind 服务端按 TRAVEL 处理,现网代码不改继续工作——用户拍板本期不动、等 TRANSFER 开放。已 grep 实证前端 orderV2.js 已封装 vehicle-requirement 各端点与 step3/vehicles 读写,均按 TRAVEL 落、未传 kind。⚠️ TRANSFER 开放后必须回头补的硬契约点:双需求并存时结算手录行 requirementKind 必填(缺失返 809008)、vehicle-requirement 写口建议显式传 kind、reject/supplier-reject 的 kind 走 query 不进 body、我的接单列表按 kind 分栏/筛选。详见正文末「六.边界行为」处置口径表。【mmg 2026-09-21 交付,not_required 翻 verified】「TRANSFER 开放后回头补」硬契约点已落地:A+B 订单侧(hl-admin v2.1 6c091ef24)结算 step3 手录车行 requirementKind(双需求并存缺归属前置拦截防 809008,FLEET 省略/MANUAL 手选+回显带回)、putVehicleRequirement 显式 kind 进 body、rejectVehicleRequirement 的 kind 走 query 不进 body(防 Jackson 静默忽略);C 车务侧(662310ea)batch/pickup-dropoff-config 显式 kind=TRANSFER。supplier-reject 前端无封装(仅房务有)、我的接单 vehicle 列表前端无该页面,两项无面可改,随未来建设接入。 2026-09-13 dev-v3

order-v3: 团期车务地基

服务: hl-order-service-v3
PR: #7551(f035b85be)、#7572(600430303)、#7595(fdebc58c4)、#7597(bc75393fd)
Issue: #7439

本文件是分册,不是全部。 #7439 共改 11 个端点,本册是面向 hl-ui 的 7 个对外端点 (/v3/admin/**);另外 4 个 /v3/internal/** Feign 端点在 13_7439_团期车务地基-内部接口-修改接口-管理后台.md。 拆分依据交付指南 2.5 节「/v3/internal/* 必须单独成篇,不许和 admin/mp 接口塞同一份」。 前端无需读那一册——那 4 个端点前端调不到。


⚠️ 关键变化

本单为团期车务打地基:

  1. 用车需求按 kind 分家:同订单可并存 TRAVEL(行程)与 TRANSFER(接送机)两类活跃需求,各自独立流转状态与版本序列
  2. 服务日服务端派生:TRANSFER 的 service_dates 由大交通自动派生,前端不传也不能传
  3. 手录行双身份归属:结算手录车费行新增 requirementKind 字段(双需求时必填)
  4. 配车写口 kind 感知:提交/放行/打回等操作都新增 kind 参数

关键限制:本单不产生 TRANSFER 行;开关 hl.order.requirement.transfer-kind-submit-enabled 默认 false;提交返 809009。


一、背景

团期车务从单一用车需求(行程)扩展到双需求并存(+接送机)。本单涉及定制师端口、团期管理员端口、车控配车端口、结算查看端口、内部派生接口的 kind 感知改造。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 提交/修改用车需求 PUT /v3/admin/order/{id}/vehicle-requirement 改造 定制师提交/修改/调整需求,请求体新增可选 kind
2 放行用车需求 POST /v3/admin/order/{id}/vehicle-requirement/dispatch 改造 团期管理员按 kind 放行至车务
3 打回用车需求(定制师) POST /v3/admin/order/{id}/vehicle-requirement/reject 改造 团期管理员按 kind 打回定制师
4 打回用车需求(车控) POST /v3/admin/order/{id}/vehicle-requirement/supplier-reject 改造 车控按 kind 打回上游
5 我的接单列表 GET /v3/admin/order/grab-pool/my-claims/vehicle 改造 列表新增 kind 过滤与回显
6 车辆核单——保存 PUT /v3/admin/order/{orderId}/settlement/step3/vehicles 改造 手录行新增 requirementKind 归属字段
7 车辆核单——查看 GET /v3/admin/order/{orderId}/settlement/step3/vehicles 改造 回显逐行新增 requirementKind

三、接口详情

1. 提交/修改用车需求 PUT /v3/admin/order/{id}/vehicle-requirement

VO: VehicleRequirementReqVO → Result<VehicleRequirementRespVO>

使用场景

定制师在后台提交、修改、调整团期或核心订单的用车需求。

入参字段表

字段 位置 类型 必填 约束 说明
id Path Long ✅ - 订单 ID
kind Body String ❌ TRAVEL/TRANSFER 需求类型,不传按 TRAVEL 处理(建议显式传,理由见六.7 影响评估);非法值实际返 400(Bean Validation 拦截,不是 809000,见下方错误响应说明)
fleet Body List ✅ @NotEmpty 车型配置,两类需求都必填
pickupRequired Body Boolean ❌ - 需接机
dropoffRequired Body Boolean ❌ - 需送机

出参字段表

字段 类型 说明
kind String 本次落库的 kind(TRAVEL/TRANSFER)
serviceDates List 派生出的服务日;TRAVEL=行程日,TRANSFER=航班日(可落在行程窗外)

请求示例

{
  "kind": "TRANSFER",
  "fleet": [{"vehicleType": "中巴", "seats": 35, "count": 1}]
}

响应示例

{
  "code": 200,
  "data": {
    "kind": "TRANSFER",
    "serviceDates": ["2026-09-11", "2026-09-18"],
    "fleet": [{"vehicleType": "中巴", "seats": 35, "count": 1}]
  },
  "success": true
}

空数据 / 降级响应

无。

错误响应

{
  "code": 400,
  "message": "用车需求类别只能是 TRAVEL 或 TRANSFER",
  "success": false
}

本端点非法 kind 实测返回的是上面这个 400,不是 809000。原因:VehicleRequirementReqVO.kind 字段带 Pattern 校验(regexp=TRAVEL|TRANSFER,见 VehicleRequirementReqVO.java 第27行),Bean Validation 在到达业务代码之前就已拦截,809000 分支对本端点的 body 参数不可达(2026-09-13 网关实测确认)。809000 这个业务错误码本身仍然存在,走的是同一处校验方法 VehicleRequirementKind.of():内部接口 GET /v3/internal/order/vehicle-requirements?kind=BOGUS 实测会返回它(见内部接口分册端点1)。同一处校验逻辑在两个不同入口有两种不同表现——走 Body+Bean Validation 的入口(本端点)只会看到 400,走 Query+业务层解析的入口才会看到 809000。mmg 若两个入口都对接,两条分支都要处理。

{
  "code": 809001,
  "message": "订单已存在同类型的活跃需求,请勿并发提交",
  "success": false
}
{
  "code": 809002,
  "message": "接送机服务日派生失败:大交通计划未填写或时间为空",
  "success": false
}
{
  "code": 809009,
  "message": "接送机需求提交尚未开放,请稍候",
  "success": false
}

业务边界

  • 同订单可并存两类活跃需求
  • 服务日随版本冻结,改签需重新提交

2. 放行用车需求 POST /v3/admin/order/{id}/vehicle-requirement/dispatch

VO: Long + DispatchReqVO → Result<Void>

使用场景

团期管理员把用车需求提交给车务处理。

入参字段表

字段 位置 类型 必填 约束 说明
id Path Long ✅ - 子订单 ID
kind Query String ❌ TRAVEL/TRANSFER 指定放行哪一类,默认 TRAVEL

出参字段表

字段 类型 说明
- - 无返回体(Result<Void>),仅以 HTTP 层 success 标记成败

请求示例

POST /v3/admin/order/1234567890123456789/vehicle-requirement/dispatch?kind=TRANSFER

响应示例

{
  "code": 200,
  "data": null,
  "success": true
}

空数据 / 降级响应

无。

错误响应

{
  "code": 809000,
  "message": "用车需求类别非法",
  "success": false
}

业务边界

  • 放行指定 kind,另一类不受影响

3. 打回用车需求(定制师) POST /v3/admin/order/{id}/vehicle-requirement/reject

VO: RejectReqVO → Result<Void>

使用场景

团期管理员在后台打回定制师提交的车需求(PENDING_REVIEW / PENDING → REJECTED_TO_CONSULTANT),仅对团期订单生效。

入参字段表

字段 位置 类型 必填 约束 说明
id Path Long ✅ - 订单 ID
returnRemark Body String ✅ @NotBlank,≤500字符 打回备注(定制师重新提交时会创建新需求)
kind Query String ❌ TRAVEL/TRANSFER 🆕 指定打回哪一类需求,默认 TRAVEL;注意:不在请求体 RejectReqVO 里,必须拼进 URL 的 query string;放进 JSON body 会被静默忽略,不报错也不返回 400,服务端按默认值 TRAVEL 处理

出参字段表

字段 类型 说明
- - 无返回体(Result<Void>),仅以 HTTP 层 success 标记成败

请求示例

POST /v3/admin/order/60123456789013/vehicle-requirement/reject?kind=TRANSFER
{
  "returnRemark": "行程未安排司机休息时间,请重新确认车型"
}

注意:kind 必须拼进 URL 的 query string(如上),不能放进 JSON body。RejectReqVO 没有 kind 字段,放进 body 会被 Jackson 静默忽略、服务端按默认值 TRAVEL 处理——不报错、不返回 400,打回操作可能悄悄落到错误的需求类型上(2026-09-13 核实代码:VehicleRequirementAdminController.java 的 rejectVehicleRequirement 用 @RequestParam(defaultValue="TRAVEL") String kind 接收;RejectReqVO.java 只有 returnRemark 一个字段)。

响应示例

{
  "code": 200,
  "data": null,
  "success": true
}

空数据 / 降级响应

无。

错误响应

{
  "code": 809000,
  "message": "用车需求类别非法",
  "success": false
}
{
  "code": 582083,
  "message": "需求状态不允许此操作,请检查当前状态",
  "success": false
}

业务边界

  • 仅对团期订单生效,订单非团期或状态非 PENDING_REVIEW/PENDING 时返 582083(REQUIREMENT_STATUS_TRANSITION_INVALID)
  • 打回同时清团级 requirement_confirmed 确认标记并写团级时间线 BATCH_REQUIREMENT_REJECT
  • 两类需求各自独立流转态、各自版本序列,打回一类不影响另一类

4. 打回用车需求(车控) POST /v3/admin/order/{id}/vehicle-requirement/supplier-reject

VO: RejectReqVO → Result<Void>

使用场景

车控配车面板打回车需求(配不出退上游)。returnTarget 按订单类型服务端自动派生:核心订单→定制师,团期订单→团期管理员。

入参字段表

字段 位置 类型 必填 约束 说明
id Path Long ✅ - 订单 ID
returnRemark Body String ✅ @NotBlank,≤500字符 打回备注
kind Query String ❌ TRAVEL/TRANSFER 🆕 指定打回哪一类需求,默认 TRAVEL;注意:不在请求体 RejectReqVO 里,必须拼进 URL 的 query string;放进 JSON body 会被静默忽略,不报错也不返回 400,服务端按默认值 TRAVEL 处理

出参字段表

字段 类型 说明
- - 无返回体(Result<Void>),仅以 HTTP 层 success 标记成败

请求示例

POST /v3/admin/order/60123456789013/vehicle-requirement/supplier-reject?kind=TRANSFER
{
  "returnRemark": "车型库存不足,无法配出"
}

注意:kind 必须拼进 URL 的 query string(如上),不能放进 JSON body。RejectReqVO 没有 kind 字段,放进 body 会被 Jackson 静默忽略、服务端按默认值 TRAVEL 处理——不报错、不返回 400,打回操作可能悄悄落到错误的需求类型上(2026-09-13 核实代码:VehicleRequirementAdminController.java 的 supplierRejectVehicleRequirement 用 @RequestParam(defaultValue="TRAVEL") String kind 接收;RejectReqVO.java 只有 returnRemark 一个字段)。

响应示例

{
  "code": 200,
  "data": null,
  "success": true
}

空数据 / 降级响应

无。

错误响应

{
  "code": 809000,
  "message": "用车需求类别非法",
  "success": false
}
{
  "code": 582083,
  "message": "需求状态不允许此操作,请检查当前状态",
  "success": false
}

业务边界

  • returnTarget 完全由服务端按订单类型派生,前端不传、也不接受前端指定
  • 按 kind 定位待打回的那条需求,另一 kind 的需求状态不受影响

5. 我的接单列表 GET /v3/admin/order/grab-pool/my-claims/vehicle

VO: MyClaimsQueryReqVO → Result<PageResult<VehicleClaimItemVO>>

使用场景

车控视角"我的接单"分页列表;claimerId 由后端从 JWT 派生,前端不传。

入参字段表

字段 位置 类型 必填 约束 说明
status Query String ❌ PROCESSING/DONE/ALL 状态过滤,默认 ALL
page Query Integer ❌ ≥1,默认1 页码
pageSize Query Integer ❌ 1-100,默认20 每页条数
kind Query String ❌ TRAVEL/TRANSFER 🆕 不传=不过滤,两类都返

出参字段表

字段 类型 说明
records List 记录列表,既有字段不变:requirementId/orderId/productName/vehicleTypeSummary/specialTags/submittedAt/consultantName/consultantId/status/claimedAt/isAdjustment
total Integer 总条数
page Integer 当前页码
pageSize Integer 每页条数
records[].kind String 🆕 需求类别(TRAVEL/TRANSFER)

请求示例

GET /v3/admin/order/grab-pool/my-claims/vehicle?status=PROCESSING&page=1&pageSize=20&kind=TRANSFER

响应示例

{
  "code": 200,
  "data": {
    "records": [
      {
        "requirementId": 90022334455,
        "orderId": 60123456789013,
        "productName": "长白山自由行5日",
        "vehicleTypeSummary": "商务车×1",
        "specialTags": ["儿童安全座椅"],
        "submittedAt": "2026-09-10T14:35:10",
        "consultantName": "陈定制师",
        "consultantId": "10086",
        "status": "PROCESSING",
        "claimedAt": "2026-09-10T16:20:00",
        "isAdjustment": false,
        "kind": "TRANSFER"
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  },
  "success": true
}

空数据 / 降级响应

{
  "code": 200,
  "data": {"records": [], "total": 0, "page": 1, "pageSize": 20},
  "success": true
}

错误响应

{
  "code": 809000,
  "message": "用车需求类别非法",
  "success": false
}

业务边界

  • 不传 kind 时两类需求混列返回,前端需按 kind 分栏或加筛选(改前列表只可能是行程用车,改后可能混入接送机需求)
  • claimerId 由后端从 JWT 派生,不接受前端指定他人

6. 车辆核单——保存 PUT /v3/admin/order/{orderId}/settlement/step3/vehicles

VO: SettlementVehicleFeesSaveReqVO → Result<SettlementVehicleFeesRespVO>

使用场景

结算侧全量保存车辆核单草稿(含运营手录行),R3c/D-A27① 改造。

入参字段表

字段 位置 类型 必填 约束 说明
orderId Path Long ✅ @Min(1) 订单 ID
items Body List ✅ @Valid @NotNull 全量明细,覆盖式保存
items[].id Body Long ❌ @Positive 明细行 ID
items[].sourceType Body String ✅ FLEET/MANUAL 来源类型
items[].serviceDate Body LocalDate ✅ @NotNull 服务日期
items[].vehicleId Body Long ❌ @Positive 车辆 ID
items[].vehiclePlate Body String ❌ ≤64字符 车牌
items[].vehicleModelId Body Long ❌ @Positive 车型 ID
items[].vehicleModelName Body String ❌ ≤128字符 车型名
items[].driverId Body Long ❌ @Positive 司机 ID
items[].driverName Body String ❌ ≤64字符 司机姓名
items[].amount Body BigDecimal ✅ ≥0.00,整数10位小数2位 车费金额
items[].paymentMethod Body String ✅ CASH_PAID/SIGNED/COMPANY_PAID 付款方式
items[].settlementConfirmStatus Body String ✅ UNCONFIRMED/CONFIRMED 核单确认状态
items[].remark Body String ❌ ≤500字符 备注
items[].voucherUrls Body List ❌ ≤9条,每条≤1024字符,需 http/https 开头 凭证URL
items[].requirementKind Body String 条件必填 TRAVEL/TRANSFER 🆕 需求归属:sourceType=MANUAL 且该单两类活跃需求并存时必填(缺失返 809008);只有一条活跃需求时可省,服务端从实际活跃身份正向解析唯一解;sourceType=FLEET 时必须不传

出参字段表

字段 类型 说明
orderId Long 订单ID(字符串序列化,避免精度丢失)
totalAmount BigDecimal 当前明细总金额
allConfirmed Boolean 非空行是否全部已确认;合法空集为true
settlementReady Boolean 车辆费用是否已具备完成核单条件
blockReasonCode String 不可完成核单的机器可读原因,可完成时为null
items[].id 等既有字段 - sourceType/sourceTypeName/serviceDate/vehicleId/vehiclePlate/vehicleModelId/vehicleModelName/driverId/driverName/amount/paymentMethod/paymentMethodName/settlementConfirmStatus/settlementConfirmStatusName/remark/voucherUrls,结构不变
items[].requirementKind String 🆕 逐行需求归属(由 requirement_id 反查所属 kind;requirement_id 为空一律回显 TRAVEL),供下次全量提交原样带回

请求示例

{
  "items": [
    {
      "sourceType": "MANUAL",
      "serviceDate": "2026-09-12",
      "vehicleId": 1001,
      "vehiclePlate": "蒙A12345",
      "amount": 800.00,
      "paymentMethod": "CASH_PAID",
      "settlementConfirmStatus": "CONFIRMED",
      "requirementKind": "TRANSFER"
    }
  ]
}

响应示例

{
  "code": 200,
  "data": {
    "orderId": "60123456789013",
    "totalAmount": 800.00,
    "allConfirmed": true,
    "settlementReady": true,
    "blockReasonCode": null,
    "items": [
      {
        "id": 90011223344,
        "sourceType": "MANUAL",
        "sourceTypeName": "运营手录",
        "serviceDate": "2026-09-12",
        "vehicleId": 1001,
        "vehiclePlate": "蒙A12345",
        "amount": 800.00,
        "paymentMethod": "CASH_PAID",
        "paymentMethodName": "现金支付",
        "settlementConfirmStatus": "CONFIRMED",
        "settlementConfirmStatusName": "已确认",
        "requirementKind": "TRANSFER"
      }
    ]
  },
  "success": true
}

空数据 / 降级响应

{
  "code": 200,
  "data": {"orderId": "60123456789013", "totalAmount": 0, "allConfirmed": true, "settlementReady": false, "blockReasonCode": "VEHICLE_FEE_NOT_READY", "items": []},
  "success": true
}

错误响应

{
  "code": 809008,
  "message": "手录车费行未指定需求归属,订单同时存在 TRAVEL、TRANSFER 两类活跃需求",
  "success": false
}
{
  "code": 584100,
  "message": "车务车辆总车费暂时不可用,请稍后重试",
  "success": false
}

业务边界

  • 🔴 硬契约变更:hl-ui 不传 requirementKind 时,只有一条活跃需求的订单仍存得进去(服务端解析唯一解),但两类需求并存的订单整批存不进去(809008),前端必须在两类需求并存时让运营选归属
  • FLEET 行的归属由来源决定,不接受前端指定;MANUAL 行指定的 kind 在本单没有活跃需求时复用 584100(FLEET_VEHICLE_FEE_UNAVAILABLE),与 809008 分工不同:584100=指错了、809008=没指定且有歧义
  • 严格模式:@JsonIgnoreProperties(ignoreUnknown=false) + @JsonAnySetter,多传未知字段当场被拒
  • 手录行"冻得下去"依赖另一开关 hl.order.settlement.vehicle-fee-audit-v3-write-enabled,为 off 时含手录行的订单调 finalize 返 584100(有意的失败关闭)

7. 车辆核单——查看 GET /v3/admin/order/{orderId}/settlement/step3/vehicles

VO: Long → Result<SettlementVehicleFeesRespVO>

使用场景

结算侧查询车辆核单草稿(回显口),本单仅改造响应新增字段,查询逻辑不变。

入参字段表

字段 位置 类型 必填 约束 说明
orderId Path Long ✅ @Min(1) 订单 ID

出参字段表

字段 类型 说明
orderId/totalAmount/allConfirmed/settlementReady/blockReasonCode/items[] 各既有字段 - 结构不变,同接口6出参
items[].requirementKind String 🆕 逐行需求归属(口径同接口6:requirement_id 为空一律回显 TRAVEL)

请求示例

GET /v3/admin/order/60123456789013/settlement/step3/vehicles

响应示例

{
  "code": 200,
  "data": {
    "orderId": "60123456789013",
    "totalAmount": 800.00,
    "allConfirmed": true,
    "settlementReady": true,
    "blockReasonCode": null,
    "items": [
      {
        "id": 90011223344,
        "sourceType": "MANUAL",
        "sourceTypeName": "运营手录",
        "serviceDate": "2026-09-12",
        "amount": 800.00,
        "paymentMethod": "CASH_PAID",
        "paymentMethodName": "现金支付",
        "settlementConfirmStatus": "CONFIRMED",
        "settlementConfirmStatusName": "已确认",
        "requirementKind": "TRANSFER"
      }
    ]
  },
  "success": true
}

空数据 / 降级响应

{
  "code": 200,
  "data": {"orderId": "60123456789013", "totalAmount": 0, "allConfirmed": true, "settlementReady": false, "blockReasonCode": "VEHICLE_FEE_NOT_READY", "items": []},
  "success": true
}

错误响应

{
  "code": 400,
  "message": "订单 ID 必须大于 0",
  "success": false,
  "data": null
}

业务边界

  • 回显里同时出现 TRAVEL 与 TRANSFER 两类行,前端须按 requirementKind 分栏或加筛选,并在全量提交时把该字段原样带回(MANUAL 行必带、FLEET 行不带)
  • 错误码零新增,与改前完全一致

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

场景 结果
提交 TRANSFER,客人未填大交通 返 809002
提交 TRANSFER,开关关闭(默认) 返 809009,零落库
双需求手录行不指定 requirementKind 返 809008,零落库

五、数据库行为

对前端无感(不改任何接口契约),但为免误读,如实列出:

  • order_vehicle_requirement 新增两列:requirement_kind(需求类别)与 active_kind(活跃标识,只在行处于活跃状态时取值,失活即清空——唯一键靠它区分「当前活跃版本」与历史版本)
  • 新增三张团期车务需求表:order_group_vehicle_requirement / order_group_vehicle_group / order_group_vehicle_group_day(团-分组-日 三层结构,本单只建表与写入口径,消费方在后续工单)
  • 无删表、无改列类型、无存量数据迁移——⚠️ 原计划的迁移脚本 V20260910_406(把勾了接送机的 TRAVEL 行迁成 TRANSFER 行)已从本单移除,所以存量行不会被本单改动

留痕:本节初稿写的是「无表创建/删除,仅新增列 requirement_kind 到已有表」,与事实不符(漏了三张新表与 active_kind 列)。2026-09-11 验收取证时发现并改写。


六、边界行为

每个码配一条前端处置口径——码本身不够,处置动作错了照样是线上问题。

错误码 触发条件 🖐 前端该怎么处置
809000 kind 传了 TRAVEL / TRANSFER 之外的值 参数错误,按常规表单校验处理;正常调用不该出现
809001 同一订单同一 kind 已存在活跃需求时又提交了一条(重复提交 / 并发双击) ⚠️ 不要自动重试——只要那条活跃需求还在,重试永远是这个码。提示「该订单已有进行中的用车需求」并引导去看板刷新确认;需求被打回或完成后才能再提
809002 提交 TRANSFER,但该单大交通信息为空,服务日派生不出来 ⚠️ 不要提示「参数错误」或「稍后重试」——这是数据缺口,必须给「先去补大交通」的引导,最好直接给一个跳到大交通填写页的入口。重试不会变好,补完大交通再提交才会好
809007 接送机需求的 service_dates 为空时被下游消费(存量迁移行未回填即被取用) 前端拿不到这个码——它在 /v3/internal 链路上由 fleet 侧触发,不经 /v3/admin。列在这里是为了错误码段位完整,以及说明它与 809002 的分工:809002 = 提交时派生不出来(前端可引导补大交通),809007 = 提交时没派生、事后被消费(属存量数据回填问题,需后端处理,前端无动作)。详见内部接口分册端点 3
809008 该单两类活跃需求并存时,手录车费行没指定 requirementKind 表单必填校验漏了;把该字段做成必选(两类并存时)即可
809009 提交 kind=TRANSFER,而开关 hl.order.requirement.transfer-kind-submit-enabled 仍是默认的 false ⚠️ 按「功能未开放」提示(例如「接送机需求提交尚未开放」),不得按参数错误处理、也不得提示「稍后重试」——开关不开,重试多少次都是这个码。本单交付时该开关就是关的,所以这是当前的正常返回,不是故障

809000 的可达性因入口而异:端点2(放行)/3(打回·定制师)/4(打回·车控)的 kind 是 Query 参数、走业务层 VehicleRequirementKind.of() 解析,非法值会返 809000(如上表);端点1(提交/修改用车需求)的 kind 是 Body 字段且带 Pattern 校验,非法值在到达业务代码前就被拦成 400,809000 对该端点不可达,详见端点1的错误响应小节。前端不要为端点1的非法 kind 编写 809000 处置分支。

⚠️ 另有一条与本单无关但同页可见的既有文案问题:584100「车务车辆总车费暂时不可用,请稍后重试」(见第 6 个端点的错误响应示例)其实对应的是永久性数据形态,重试不会好。已另行报备,前端暂按原样透出即可。


六.5、枚举

kind

值 说明
TRAVEL 行程用车
TRANSFER 接送机

六.6、修改前后对比

维度 改前 改后
同订单活跃需求 最多 1 条 最多 2 条
服务日来源 行程日 TRAVEL=行程日,TRANSFER=大交通
手录行识别 无需求信息 必须指定 kind
配车操作 操作唯一需求 按 kind 指定操作对象

六.7、影响评估

  • 向后兼容性:部分。不传 kind 时服务端按 TRAVEL 处理,现网代码可以不改;但建议 hl-ui 一律显式传 kind——一旦该订单出现两类活跃需求,「不传」的语义就从「就是行程用车」变成了「碰巧落到行程用车上」,而这两者在代码里长得一模一样。双需求时手录行的 requirementKind 则是必须指定(缺失返 809008)
  • 前端同步上线:是

七、不影响范围

  • 房务及其他非车务
  • 单订单 TRAVEL 流程(透明升级)
  • 接送机正式开放前

八、测试环境已验证

部署:2026-09-13 13:17:42 执行 deploy-backend.sh hl-order-service-v3 hl-fleet-service hl-gateway,三次 git sync done HEAD 均为 a82367e15(dev-v3)。经 SSH root@192.168.100.236 跑 bash /opt/hulalv/scripts/deploy-status.sh 复核(下同,内部分册引用同一次结果):

SERVICE                  BRANCH                       COMMIT    BEHIND    DEPLOYED_AT         BY
hl-order-service-v3      dev-v3                       a82367e15 2/N       2026-09-13 13:18:39 root@192.168.100.168
hl-fleet-service         dev-v3                       a82367e15 2/N       2026-09-13 13:19:22 root@192.168.100.168
hl-gateway               dev-v3                       a82367e15 2/N       2026-09-13 13:19:45 root@192.168.100.168

三行 BEHIND 均为 2/N:落后 origin/dev-v3 2 个提交,N=未触及本服务/本单依赖模块。之后合入的 PR #7631(squash 1c8984042)为 4 个文件的纯 src/test 改动,不影响运行时字节,无需重新部署。

网关实测(2026-09-13 14:10 左右,账号 admin 登录后切 ADMIN 角色,经 https://api.test.1814.love:9443):除纯读端点外,一律用不存在的订单 ID 99999999999999999(或非法枚举值)触发早期业务校验,用真实业务错误码证明路由与业务代码均已触达,全程未写入或修改任何真实数据行:

# 方法 路径 HTTP code message 判定
1 PUT /v3/admin/order/{id}/vehicle-requirement(kind=BOGUS) 200 400 用车需求类别只能是 TRAVEL 或 TRANSFER 路由通、业务代码触达;见下方发现①
2 POST /v3/admin/order/{id}/vehicle-requirement/dispatch?kind=TRAVEL(不存在orderId) 200 581007 订单不存在 路由通、业务代码触达
3 POST /v3/admin/order/{id}/vehicle-requirement/reject(不存在orderId) 200 581007 订单不存在 路由通、业务代码触达;见下方发现②
4 POST /v3/admin/order/{id}/vehicle-requirement/supplier-reject(不存在orderId) 200 581007 订单不存在 路由通、业务代码触达;见下方发现②
5 GET /v3/admin/order/grab-pool/my-claims/vehicle?kind=TRANSFER 200 200 成功(真实查询,0 条) 路由通、业务代码触达
6 PUT /v3/admin/order/{orderId}/settlement/step3/vehicles(不存在orderId,items为空) 200 581007 订单不存在 路由通、业务代码触达
7 GET /v3/admin/order/{orderId}/settlement/step3/vehicles(不存在orderId) 200 581007 订单不存在 路由通、业务代码触达

7/7 端点经网关路由至 hl-order-service-v3 并返回真实业务响应(无 404/502/网关鉴权错误)。造数声明:本轮全部使用不存在的订单 ID 或空 items,服务在到达任何写操作前即因订单不存在或参数非法而拒绝,未产生任何真实数据行,因此本次验证无需写 remark 溯源标记。

发现的两处正文与实现不符(已在 §二/§三直接订正正文,不再另存一份可能漂移的描述):

  1. 端点 1(提交/修改用车需求)的非法 kind 实际返回 400,不是原正文写的 809000——§三端点1 的入参表与错误响应小节已改成实测结果,并说明了 809000 为什么对本端点不可达。§六边界行为表也同步加了这条分入口说明。
  2. 端点 3(打回·定制师)与端点 4(打回·车控)的 kind 实际是 Query 参数、不在 RejectReqVO 里——§三对应端点的入参表、请求示例已改正,并显式警告 kind 放进 JSON body 会被静默忽略、服务端按默认值 TRAVEL 处理(不报错、不返回 400)。

九、相关历史 PR

PR Issue 说明
#7551 #7439 本单主体,2026-09-11 squash 合并进 dev-v3(合并提交 f035b85be);⚠️ 初稿误填过 #7504,那是 #7446 的 PR
#7572 #7439 结算侧三条缺陷修复(合并提交 600430303):本册端点 6 / 7 的手录行双身份归属靠它才完整
#7595 #7439 需求域两条契约缺陷修复(合并提交 fdebc58c4):影响的是内部分册的 1、2 两个端点,本册契约不变
#7597 #7439 本册端点 6 / 7 的 items[].requirementKind 靠它才真的返回(合并提交 bc75393fd):修前响应 VO 里根本没有这个字段,而本文件出参表与响应示例都写了它

十、相关文档

  • Issue: #7439
  • 内部接口分册: changelogs-v2/2026-09/13_7439_团期车务地基-内部接口-修改接口-管理后台.md
  • PR: #7551 / #7572 / #7595(均已合并,当前基线 fdebc58c4)

关联 / 联系人

链接

  • Issue: #7439
  • PR: #7551(已合并,f035b85be)、#7572(已合并,600430303)、#7595(已合并,fdebc58c4)

联系人

  • 后端负责人: @wx