- 新件 26_8373:GET /v3/admin/order/grab-pool/my-claims/vehicle 下线(改前对所有账号恒返回空页), 测试服两实例经网关实测改前 200 空页 → 改后 code=404,my-claims/hotel 阳性对照不变。 - 13_7439 §5 标题下加订正指针:kind 入参与 records[].kind 从未实现,随本件作废。 Refs wx/HL#8373 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
34 KiB
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 个端点前端调不到。
⚠️ 关键变化
本单为团期车务打地基:
- 用车需求按 kind 分家:同订单可并存 TRAVEL(行程)与 TRANSFER(接送机)两类活跃需求,各自独立流转状态与版本序列
- 服务日服务端派生:TRANSFER 的 service_dates 由大交通自动派生,前端不传也不能传
- 手录行双身份归属:结算手录车费行新增 requirementKind 字段(双需求时必填)
- 配车写口 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
2026-09-26 订正(#8373):本接口已下线,见 26_8373_下线车控我的接单接口-删除接口-管理后台.md
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(提交/修改用车需求)的非法 kind 实际返回 400,不是原正文写的 809000——§三端点1 的入参表与错误响应小节已改成实测结果,并说明了 809000 为什么对本端点不可达。§六边界行为表也同步加了这条分入口说明。
- 端点 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)
关联 / 联系人
链接
联系人
- 后端负责人: @wx