changelog-filename-gate / validate (push) Failing after 2s
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 段实证
805 行
34 KiB
Markdown
805 行
34 KiB
Markdown
---
|
||
schema: "hl-changelog/v2"
|
||
ticket: "7439"
|
||
title: "团期车务地基——用车需求按 kind 分家、服务日派生、手录行双身份"
|
||
consumer: "admin"
|
||
author: "wx(GIT)"
|
||
change_type: "修改接口"
|
||
backend_status: "deployed"
|
||
gateway_status: "verified"
|
||
frontend_status: "verified"
|
||
frontend_owner: "mmg"
|
||
frontend_ref: "6c091ef2486e0844d5bf1e39c705e43bec875e4f"
|
||
target_release: "v2.1"
|
||
verified_at: "2026-09-21"
|
||
status_note: "本文件是 #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 列表前端无该页面,两项无面可改,随未来建设接入。"
|
||
updated_at: "2026-09-13"
|
||
base: "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<FleetItem> | ✅ | @NotEmpty | 车型配置,两类需求都必填 |
|
||
| pickupRequired | Body | Boolean | ❌ | - | 需接机 |
|
||
| dropoffRequired | Body | Boolean | ❌ | - | 需送机 |
|
||
|
||
#### 出参字段表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| kind | String | 本次落库的 kind(TRAVEL/TRANSFER) |
|
||
| serviceDates | List<LocalDate> | 派生出的服务日;TRAVEL=行程日,TRANSFER=航班日(可落在行程窗外) |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"kind": "TRANSFER",
|
||
"fleet": [{"vehicleType": "中巴", "seats": 35, "count": 1}]
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"kind": "TRANSFER",
|
||
"serviceDates": ["2026-09-11", "2026-09-18"],
|
||
"fleet": [{"vehicleType": "中巴", "seats": 35, "count": 1}]
|
||
},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无。
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"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 若两个入口都对接,两条分支都要处理。
|
||
|
||
```json
|
||
{
|
||
"code": 809001,
|
||
"message": "订单已存在同类型的活跃需求,请勿并发提交",
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 809002,
|
||
"message": "接送机服务日派生失败:大交通计划未填写或时间为空",
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"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` 标记成败 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
POST /v3/admin/order/1234567890123456789/vehicle-requirement/dispatch?kind=TRANSFER
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": null,
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无。
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"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` 标记成败 |
|
||
|
||
#### 请求示例
|
||
|
||
```http
|
||
POST /v3/admin/order/60123456789013/vehicle-requirement/reject?kind=TRANSFER
|
||
```
|
||
|
||
```json
|
||
{
|
||
"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 一个字段)。
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": null,
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无。
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 809000,
|
||
"message": "用车需求类别非法",
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"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` 标记成败 |
|
||
|
||
#### 请求示例
|
||
|
||
```http
|
||
POST /v3/admin/order/60123456789013/vehicle-requirement/supplier-reject?kind=TRANSFER
|
||
```
|
||
|
||
```json
|
||
{
|
||
"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 一个字段)。
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": null,
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无。
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 809000,
|
||
"message": "用车需求类别非法",
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"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<VehicleClaimItemVO> | 记录列表,既有字段不变:requirementId/orderId/productName/vehicleTypeSummary/specialTags/submittedAt/consultantName/consultantId/status/claimedAt/isAdjustment |
|
||
| total | Integer | 总条数 |
|
||
| page | Integer | 当前页码 |
|
||
| pageSize | Integer | 每页条数 |
|
||
| records[].kind | String | 🆕 需求类别(TRAVEL/TRANSFER) |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
GET /v3/admin/order/grab-pool/my-claims/vehicle?status=PROCESSING&page=1&pageSize=20&kind=TRANSFER
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"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
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {"records": [], "total": 0, "page": 1, "pageSize": 20},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"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<Item> | ✅ | @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<String> | ❌ | ≤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),供下次全量提交原样带回 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"items": [
|
||
{
|
||
"sourceType": "MANUAL",
|
||
"serviceDate": "2026-09-12",
|
||
"vehicleId": 1001,
|
||
"vehiclePlate": "蒙A12345",
|
||
"amount": 800.00,
|
||
"paymentMethod": "CASH_PAID",
|
||
"settlementConfirmStatus": "CONFIRMED",
|
||
"requirementKind": "TRANSFER"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"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
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {"orderId": "60123456789013", "totalAmount": 0, "allConfirmed": true, "settlementReady": false, "blockReasonCode": "VEHICLE_FEE_NOT_READY", "items": []},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 809008,
|
||
"message": "手录车费行未指定需求归属,订单同时存在 TRAVEL、TRANSFER 两类活跃需求",
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"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) |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
GET /v3/admin/order/60123456789013/settlement/step3/vehicles
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"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
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {"orderId": "60123456789013", "totalAmount": 0, "allConfirmed": true, "settlementReady": false, "blockReasonCode": "VEHICLE_FEE_NOT_READY", "items": []},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"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](https://git.1814.love:8443/wx/HL/pulls/7551) | #7439 | 本单主体,2026-09-11 squash 合并进 dev-v3(合并提交 `f035b85be`);⚠️ 初稿误填过 #7504,那是 #7446 的 PR |
|
||
| [#7572](https://git.1814.love:8443/wx/HL/pulls/7572) | #7439 | 结算侧三条缺陷修复(合并提交 `600430303`):本册端点 6 / 7 的手录行双身份归属靠它才完整 |
|
||
| [#7595](https://git.1814.love:8443/wx/HL/pulls/7595) | #7439 | 需求域两条契约缺陷修复(合并提交 `fdebc58c4`):影响的是内部分册的 1、2 两个端点,本册契约不变 |
|
||
| [#7597](https://git.1814.love:8443/wx/HL/pulls/7597) | #7439 | **本册端点 6 / 7 的 `items[].requirementKind` 靠它才真的返回**(合并提交 `bc75393fd`):修前响应 VO 里根本没有这个字段,而本文件出参表与响应示例都写了它 |
|
||
|
||
---
|
||
|
||
## 十、相关文档
|
||
|
||
- Issue: [#7439](https://git.1814.love:8443/wx/HL/issues/7439)
|
||
- 内部接口分册: `changelogs-v2/2026-09/13_7439_团期车务地基-内部接口-修改接口-管理后台.md`
|
||
- PR: [#7551](https://git.1814.love:8443/wx/HL/pulls/7551) / [#7572](https://git.1814.love:8443/wx/HL/pulls/7572) / [#7595](https://git.1814.love:8443/wx/HL/pulls/7595)(均已合并,当前基线 `fdebc58c4`)
|
||
|
||
## 关联 / 联系人
|
||
|
||
### 链接
|
||
|
||
- **Issue**: [#7439](https://git.1814.love:8443/wx/HL/issues/7439)
|
||
- **PR**: [#7551](https://git.1814.love:8443/wx/HL/pulls/7551)(已合并,`f035b85be`)、[#7572](https://git.1814.love:8443/wx/HL/pulls/7572)(已合并,`600430303`)、[#7595](https://git.1814.love:8443/wx/HL/pulls/7595)(已合并,`fdebc58c4`)
|
||
|
||
### 联系人
|
||
|
||
- **后端负责人**: @wx
|