docs(changelog): 补 #8562 逐户用车提交态区分与 #8620/#8621 派车读口中文名交接件
changelog-filename-gate / validate (push) Failing after 2s
changelog-filename-gate / validate (push) Failing after 2s
30_8562 覆盖 GET /v3/admin/order/group-batch/{groupBatchId}/requirement/vehicle-households:
新增 submitState 区分「从未提交」与「已被打回待重提」。四条限定写进关键变化与业务边界——
status 规则一字未改(判提交要读 submitState 不是 status)、requirements 内容零变化
(打回信息走 rejectedRequirements)、rejectedRequirements 与需求行刻意不同构不可当行渲染、
householdCount 不再恒等于 needs_vehicle=true 的户数。
30_8621 覆盖派车三个读口(board/orders、group-dispatch/pending-batches、
group-dispatch/batches/{groupBatchId}/overview):#8621 新增 4 个 *Label 字段,枚举码不再
裸下发;#8620 是 vehicleControlStatus 的读法澄清——字段名与取值域一字未改,改的是
「按哪套枚举去读」(真源是 order-v3 的 RequirementStatus,不是建表 SQL 里那条已陈旧的
COMMENT)。
两份 --files 校验全绿(校验 2 个对象,EXIT=0),正文零「等部署/另发/待补充」类前向引用。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -0,0 +1,387 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "8562"
|
||||||
|
title: "团期逐户用车列表区分「从未提交」与「已被打回待重提」,打回明细走新字段下发"
|
||||||
|
consumer: "admin"
|
||||||
|
author: "wx(GIT)"
|
||||||
|
change_type: "修改接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "not_required"
|
||||||
|
frontend_status: "pending"
|
||||||
|
frontend_owner: ""
|
||||||
|
frontend_ref: ""
|
||||||
|
target_release: ""
|
||||||
|
verified_at: ""
|
||||||
|
status_note: "GET /v3/admin/order/group-batch/{groupBatchId}/requirement/vehicle-households 的 households[] 新增三个字段:submitState(户级提交态,恒非 null,三取值 NEVER_SUBMITTED 从未提交 / SUBMITTED 已提交 / REJECTED_PENDING_RESUBMIT 已被打回待重提)、submitStateName(其中文名,后端下发)、rejectedRequirements(该户当前处于打回待重提的类别明细,恒非 null,无打回时为空数组,按展示序 TRAVEL 在前)。背景:用车的打回是「原地置 REJECTED_* + is_active=0」,被打回的户因此没有任何活跃需求行,与从未提交的户在 status 上完全同形(都是 null),车务照 status 催办会把「已交过、只是被驳回」和「压根没动过」混成一堆。四条必须照做的限定:(1) status 字段的取值规则一字未改、仍只由活跃行决定,判「有没有提交过」一律读 submitState,不要读 status 是否为 null;(2) requirements 列表内容零变化,被打回的行仍然不在里面,打回信息只在 rejectedRequirements;(3) rejectedRequirements 的元素刻意不与需求行同构(只有类别/打回状态/打回意见/打回时刻/版本号,没有车队明细、服务日期、座位数),不可当作需求行渲染,否则同一户会出现与活跃行自相矛盾的一条;(4) householdCount 口径再放宽一项,不再恒等于 needs_vehicle=true 的户数——一个 needs_vehicle 为假、没有活跃行、但有一类被打回的户现在也会进列表,判「这户为什么在列表里」看 submitState,不要拿 needsVehicle 反推。另两个数一字不动:vehicleRowCount(被打回的户贡献 0 行)与 countedHouseholdCount(只认活跃 TRAVEL 行),座位汇总口径不会因为有人被驳回而跳变。submitState 与 requirements 同受 kind 筛选影响:传 kind=TRAVEL 时,一个只有接送机被打回的户读成 NEVER_SUBMITTED;要看全貌就不传 kind(不传 = 两类都返)。已知边界(定案、非缺陷):TRANSFER 需求被「不再需要接送」失活(#8435)且没有新版时,既无活跃行也非打回,读成 NEVER_SUBMITTED,与从未提交对催办动作的要求一致,故不另立一态。入参、分页、排序、错误码(589500 / 589507 / 809000 / 401)与其余响应字段均未变化。"
|
||||||
|
updated_at: "2026-09-30"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 团期用车逐户列表:区分「从未提交」与「已被打回待重提」
|
||||||
|
|
||||||
|
> **存放目录**: 二期(order-v3/fleet)→ `changelogs-v2/2026-09/`
|
||||||
|
|
||||||
|
## ⚠️ 关键变化
|
||||||
|
|
||||||
|
- **`households[]` 新增三个字段**:`submitState`(户级提交态,**恒非 null**)、`submitStateName`(其中文名)、`rejectedRequirements`(该户当前处于打回待重提的类别明细,**恒非 null**,无打回时为空数组)。
|
||||||
|
- 🔴 **判「这户有没有提交过」一律读 `submitState`,不要读 `status` 是否为 `null`**。用车的打回是「原地置 `REJECTED_*` + `is_active=0`」⇒ 被打回的户**没有任何活跃需求行**,`status` 同样是 `null`,与从未提交的户**完全同形**。照 `status` 催办会去催一个已经交过、只是被驳回的人,而真正该催的「按意见重提」在页面上看不出来。
|
||||||
|
- **`status` 的取值规则一字未改**(仍是活跃行展示序首条),`statusName` 同理。本次只新增旁路字段,既有映射与读数不受影响。
|
||||||
|
- **`requirements` 列表内容零变化**:被打回的行仍然**不在**里面(它已失活)。打回信息只在新字段 `rejectedRequirements` 里。
|
||||||
|
- 🔴 **`rejectedRequirements` 的元素不是需求行,不可当作需求行渲染**:它刻意与 `requirements[]` **不同构**——只带类别 / 打回状态 / 打回意见 / 打回时刻 / 版本号,**没有**车队明细、服务日期、座位数。把它拼进需求表会让同一户出现一条与活跃行自相矛盾的需求。
|
||||||
|
- 🔴 **`householdCount` 口径再放宽一项,不再恒等于「`needs_vehicle=true` 的户数」**:一个 `needs_vehicle` 为假、又没有活跃行、但有一类被打回的户现在也会进列表(它正是要催重提的人)。判「这户为什么在列表里」看 `submitState`,**不要拿 `needsVehicle` 反推**。
|
||||||
|
- **另两个数一字不动**:`vehicleRowCount`(被打回的户贡献 0 行)与 `countedHouseholdCount`(只认活跃 TRAVEL 行)。座位汇总口径不会因为「有人被驳回」而跳变。
|
||||||
|
- **`submitState` 随 `kind` 筛选变化**(与 `requirements` 同一口径):传 `kind=TRAVEL` 时,一个只有接送机被打回的户读成 `NEVER_SUBMITTED`。要看全貌就**不传** `kind`(不传 = 两类都返)。
|
||||||
|
|
||||||
|
## 一、背景(选填)
|
||||||
|
|
||||||
|
团期「查看需求」Tab 的用车逐户明细是车务与团期管理员的催办页:谁还没报、谁报了在等审、谁被驳回要改。但用车的打回实现是「把那一版原地置成 `REJECTED_*` 并把 `is_active` 置 0」,于是被打回的户在这个只读活跃行的端点里表现为「0 条需求行 + `status` 为 null」——与「从未提交」一模一样。更糟的是:`needs_vehicle` 为假、又没有活跃行的户改前压根不出卡,而被打回的户恰恰可能是这个形状,催办页上会**整户消失**。本次补的就是「这户到底是没交过,还是交过被驳回」这一维,以及「被驳回的是哪一类、意见是什么、什么时候驳的」这几个催办必需值。
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | 团期子订单用车需求记录 | GET | `/v3/admin/order/group-batch/{groupBatchId}/requirement/vehicle-households` | 修改 | `households[]` 新增 `submitState` / `submitStateName` / `rejectedRequirements`;`householdCount` 口径放宽含打回户 |
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. 团期子订单用车需求记录 `GET /v3/admin/order/group-batch/{groupBatchId}/requirement/vehicle-households`
|
||||||
|
|
||||||
|
**VO**: `Long groupBatchId + String kind(query)→ GroupVehicleHouseholdsRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
团期「查看需求」Tab 的用车逐户明细(汇总块下面那一块)。用于回答「这个团还差谁的用车需求」:本次起可以把「催首次提交」和「催按意见重提」分成两组,并在被驳回的户上直接展示驳回意见与时刻。
|
||||||
|
|
||||||
|
#### 入参
|
||||||
|
|
||||||
|
入参本次**零变化**。
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | path | Long | 是 | 雪花 ID;团期不存在返 589500 | 团期 ID |
|
||||||
|
| kind | query | String | 否 | `TRAVEL` / `TRANSFER`;其余非空值返 809000 | 需求类别过滤。**不传或空白 = 两类都返**(与提交侧「不传按 TRAVEL」的缺省刻意相反,前端默认不传即可) |
|
||||||
|
|
||||||
|
#### 出参 `Result<GroupVehicleHouseholdsRespVO>`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| groupBatchId | String | 团期 ID |
|
||||||
|
| departDate | String | 团期出发日 `YYYY-MM-DD`;团期未定出发日为 `null` |
|
||||||
|
| endDate | String | 团期结束日 `YYYY-MM-DD`;团期未定结束日为 `null` |
|
||||||
|
| householdCount | Integer | 本列表户数(按 orderId 去重),恒等于 `households` 长度。**口径放宽**:= 应报车户 ∪ 有活跃需求行的户 ∪ **处于打回待重提的户**;**不再恒等于 `needs_vehicle=true` 的户数** |
|
||||||
|
| vehicleRowCount | Integer | 需求行数 = Σ 各户 `requirements` 长度。未提交与被打回的户贡献 0 行,故**可能小于 `householdCount`**(别当「行数 ≥ 户数」不变量) |
|
||||||
|
| countedHouseholdCount | Integer | 计入车侧汇总的户数(= 有活跃 TRAVEL 行的户数)。判据一字未改,**不随 `kind` 筛选变化**,也不因有人被驳回而变 |
|
||||||
|
| households | Array | 逐户明细,按 `orderNo` 升序(`orderNo` 为空的排最后,按 `orderId` 兜底稳定) |
|
||||||
|
| households[].orderId | String | 子订单 ID |
|
||||||
|
| households[].orderNo | String | 子订单编号(**非团号**,形如 `HL` + `yyyyMMddHHmmssSSS`) |
|
||||||
|
| households[].teamNo | String | 子订单团号;未付订金尚未分配时为 `null`(不兜底、不回退成订单号) |
|
||||||
|
| households[].customerName | String | 主联系人姓名 |
|
||||||
|
| households[].participantCount | Integer | 出行人数(成人 + 儿童 + 小童 + 婴儿) |
|
||||||
|
| households[].consultantId | String | 定制师 ID;未指派为 `null` |
|
||||||
|
| households[].consultantName | String | 定制师姓名;未指派为 `null` |
|
||||||
|
| households[].countedInSummary | Boolean | 该户是否计入车侧汇总(= 有活跃 TRAVEL 行);**未变** |
|
||||||
|
| households[].status | String | 户级用车需求状态:**仅由活跃行决定**。`null` = 该户当前没有活跃需求行(**从未提交与已被打回失活两种情况都是 `null`**,要分辨读 `submitState`);非 `null` 时取展示序首条(TRAVEL 优先)的状态。**取值规则一字未改** |
|
||||||
|
| households[].statusName | String | 户级状态中文名;`status` 为 `null` 时同为 `null`。`PENDING_REVIEW` 按 kind 分两套文案(TRAVEL=待提交车务 / TRANSFER=待审核,#8218);**未变** |
|
||||||
|
| households[].requirements | Array | 该户的**活跃**用车需求行,0~2 条(TRAVEL / TRANSFER 各至多一条)。被打回的行已失活、**不在本列表内**;该户没有活跃行时为**空数组**(不是 `null`)。**本列表内容零变化** |
|
||||||
|
| households[].submitState | String | 🆕 户级提交态,**恒非 null**:`NEVER_SUBMITTED` / `SUBMITTED` / `REJECTED_PENDING_RESUBMIT`。`status` 为 `null` 时靠它分辨两种空态;一户两类不同时按展示序首条(TRAVEL 优先)取;**随 `kind` 筛选变化** |
|
||||||
|
| households[].submitStateName | String | 🆕 户级提交态中文名,与 `submitState` 一一对应:从未提交 / 已提交 / 已被打回待重提。后端下发,前端不自己映射 |
|
||||||
|
| households[].rejectedRequirements | Array | 🆕 该户**当前**处于打回待重提的类别明细,按展示序(TRAVEL 在前)。**恒非 null**,无打回时为空数组。**不是需求行,不可当作需求行渲染** |
|
||||||
|
| households[].rejectedRequirements[].kind | String | 需求类别:`TRAVEL` 行程用车 / `TRANSFER` 接送机 |
|
||||||
|
| households[].rejectedRequirements[].kindName | String | 类别中文名,后端下发 |
|
||||||
|
| households[].rejectedRequirements[].status | String | 打回状态编码:`REJECTED_TO_CONSULTANT` = 团期管理员打回定制师 / `REJECTED_TO_ADMIN` = 车务退回团期管理员 |
|
||||||
|
| households[].rejectedRequirements[].statusName | String | 打回状态中文名,与 `requirements[].statusName` 同一套车务文案:已驳回定制师 / 已驳回管理员 |
|
||||||
|
| households[].rejectedRequirements[].returnRemark | String | 打回意见;历史数据可能为 `null` |
|
||||||
|
| households[].rejectedRequirements[].returnedAt | String | 打回时刻 `yyyy-MM-dd HH:mm:ss`;历史数据可能为 `null` |
|
||||||
|
| households[].rejectedRequirements[].version | Integer | 被打回的那一版版本号。**TRAVEL 与 TRANSFER 各自独立递增,不可跨类比大小** |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/group-batch/2104839654727618562/requirement/vehicle-households
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
```
|
||||||
|
|
||||||
|
按类别筛选(只看行程用车):
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /v3/admin/order/group-batch/2104839654727618562/requirement/vehicle-households?kind=TRAVEL
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
三户分别落在三个提交态上。`requirements[]` 行内字段与本次改动前完全一致,这里只保留几个便于对读的字段,未列出的行内字段(`fleet` / `serviceDates` / `specialTags` / 座位数等)照旧下发。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"groupBatchId": "2104839654727618562",
|
||||||
|
"departDate": "2026-10-06",
|
||||||
|
"endDate": "2026-10-10",
|
||||||
|
"householdCount": 3,
|
||||||
|
"vehicleRowCount": 1,
|
||||||
|
"countedHouseholdCount": 1,
|
||||||
|
"households": [
|
||||||
|
{
|
||||||
|
"orderId": "2104839654727618570",
|
||||||
|
"orderNo": "HL20261006103015001",
|
||||||
|
"teamNo": "T26-3963",
|
||||||
|
"customerName": "周雅",
|
||||||
|
"participantCount": 4,
|
||||||
|
"consultantId": "1901233114509312088",
|
||||||
|
"consultantName": "苏晴",
|
||||||
|
"countedInSummary": true,
|
||||||
|
"status": "PENDING_REVIEW",
|
||||||
|
"statusName": "待提交车务",
|
||||||
|
"requirements": [
|
||||||
|
{
|
||||||
|
"requirementId": "2104839777884160001",
|
||||||
|
"kind": "TRAVEL",
|
||||||
|
"kindName": "行程用车",
|
||||||
|
"status": "PENDING_REVIEW",
|
||||||
|
"statusName": "待提交车务",
|
||||||
|
"headcount": 4,
|
||||||
|
"returnRemark": null,
|
||||||
|
"returnedAt": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"submitState": "SUBMITTED",
|
||||||
|
"submitStateName": "已提交",
|
||||||
|
"rejectedRequirements": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"orderId": "2104839654727618571",
|
||||||
|
"orderNo": "HL20261006103015002",
|
||||||
|
"teamNo": "T26-3964",
|
||||||
|
"customerName": "郑文博",
|
||||||
|
"participantCount": 2,
|
||||||
|
"consultantId": "1901233114509312088",
|
||||||
|
"consultantName": "苏晴",
|
||||||
|
"countedInSummary": false,
|
||||||
|
"status": null,
|
||||||
|
"statusName": null,
|
||||||
|
"requirements": [],
|
||||||
|
"submitState": "REJECTED_PENDING_RESUBMIT",
|
||||||
|
"submitStateName": "已被打回待重提",
|
||||||
|
"rejectedRequirements": [
|
||||||
|
{
|
||||||
|
"kind": "TRAVEL",
|
||||||
|
"kindName": "行程用车",
|
||||||
|
"status": "REJECTED_TO_CONSULTANT",
|
||||||
|
"statusName": "已驳回定制师",
|
||||||
|
"returnRemark": "第三天上午的用车时间与行程冲突,请改后重提",
|
||||||
|
"returnedAt": "2026-09-29 16:42:11",
|
||||||
|
"version": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"orderId": "2104839654727618572",
|
||||||
|
"orderNo": "HL20261006103015003",
|
||||||
|
"teamNo": null,
|
||||||
|
"customerName": "何嘉宁",
|
||||||
|
"participantCount": 3,
|
||||||
|
"consultantId": null,
|
||||||
|
"consultantName": null,
|
||||||
|
"countedInSummary": false,
|
||||||
|
"status": null,
|
||||||
|
"statusName": null,
|
||||||
|
"requirements": [],
|
||||||
|
"submitState": "NEVER_SUBMITTED",
|
||||||
|
"submitStateName": "从未提交",
|
||||||
|
"rejectedRequirements": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 团期下没有在团子订单:`households` 为空数组 `[]`,三个计数均为 `0`,`departDate` / `endDate` 照常回显,不报错。
|
||||||
|
- 某户没有活跃需求行:`requirements` 为**空数组**(不是 `null`),`status` 与 `statusName` 为 `null`,而 `submitState` **仍然有确定取值**(`NEVER_SUBMITTED` 或 `REJECTED_PENDING_RESUBMIT`)——前端不必对 `submitState` 判空。
|
||||||
|
- 某户没有被打回的类别:`rejectedRequirements` 为**空数组**(不是 `null`)。
|
||||||
|
- 在团订单 ID 存在但订单行缺失(跨团挂单 / 订单被物理删这类数据异常):该户被跳过并在服务端留痕,整页照常返回;三个计数都按**最终列表**重算,不会出现「表头 42 户、列表里只有 30 户」这种自相矛盾的响应。
|
||||||
|
- 单次返回上限 **500 户**,超出按 `orderNo` 升序截断并在服务端留痕;截断后三个计数同样按截断后的列表重算。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 809000,
|
||||||
|
"message": "用车需求类别非法:BOTH",
|
||||||
|
"data": null,
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `809000 用车需求类别非法:{0}`:`kind` 传了 `TRAVEL` / `TRANSFER` 之外的非空值(例如 `ALL`、`BOTH`、小写拼错)。要「两类都返」请**不传**该参数或传空串。
|
||||||
|
- `589500 团期不存在`:`groupBatchId` 查不到。
|
||||||
|
- `589507 无操作权限(当前角色未授予团期权限,或该团期不在您名下)`:缺团期查看权限,或该团期不在当前账号名下。
|
||||||
|
- `401`:未登录或令牌失效(网关返 HTTP 200 + 信封 `code: 401`,请按信封 `code` 判定)。
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 🔴 **判「有没有提交过」读 `submitState`,不读 `status` 是否为 `null`**。打回 = 原地置 `REJECTED_*` + `is_active=0` ⇒ 打回户与从未提交户的 `status` **都是 `null`**,在 `status` 这一维上不可分辨。`status` 的取值规则本次一字未改。
|
||||||
|
- 🔴 **`requirements` 列表内容零变化**:被打回的行不在里面,打回信息只在 `rejectedRequirements`。不要为了展示驳回意见去翻 `requirements[].returnRemark`——那一格记的是**该活跃行**历史上被打回过的痕迹(已重提后仍可能有值),不是「当前处于打回态」。
|
||||||
|
- 🔴 **`rejectedRequirements` 不可当作需求行渲染**:它与 `requirements[]` 刻意不同构,没有车队明细 / 服务日期 / 座位数。需要看需求内容时读该户的活跃行,或走需求版本历史端点。
|
||||||
|
- 🔴 **`householdCount` 不再恒等于 `needs_vehicle=true` 的户数**:一个 `needs_vehicle` 为假、没有活跃行、但有一类被打回的户也会进列表。判「这户为什么在列表里」看 `submitState`,不要拿 `needsVehicle` 反推。
|
||||||
|
- **`rejectedRequirements` 只列「当前」处于打回待重提的类别**,不是历史打回记录:打回后已重新提交的类别**不**出现在这里(那一类的现状在 `requirements` 里),否则页面会永远挂着一条早已处理完的驳回。
|
||||||
|
- **一户两类状态不同时,`submitState` 按展示序首条取**(TRAVEL 优先),与 `status` 同一条规则。例:TRAVEL 有活跃行、TRANSFER 被打回 ⇒ `submitState` 是 `SUBMITTED`,而 `rejectedRequirements` 里有 TRANSFER 那一条。**要逐类判断一律读 `requirements[].status` 与 `rejectedRequirements[].kind`,不要用户级的 `submitState` 推单类。**
|
||||||
|
- **`submitState` 随 `kind` 筛选变化**:传 `kind=TRAVEL` 时,一个只有接送机被打回的户读成 `NEVER_SUBMITTED`(该类别不在筛选范围内)。要看全貌不传 `kind`。
|
||||||
|
- **已知边界(定案,非缺陷)**:TRANSFER 需求被「不再需要接送」失活(#8435)且之后没有新版本时,该户既无活跃行也不处于打回态,会读成 `NEVER_SUBMITTED`。它与「从未提交」对催办动作的要求一致(要么提,要么整团免车),故不另立一态。
|
||||||
|
- **`version` 不可跨类比较**:TRAVEL 的 v3 与 TRANSFER 的 v3 之间没有先后关系,两类版本号各自独立递增。
|
||||||
|
- **`vehicleRowCount` 可能小于 `householdCount`**:未提交与被打回的户贡献 0 行。不要再把「行数 ≥ 户数」当不变量写断言。
|
||||||
|
- **`countedHouseholdCount` 不随 `kind` 筛选变化**(#8559),也不因驳回动作变化——座位汇总口径必须稳定。
|
||||||
|
- **Swagger 上该端点的 `notes` 仍按本次改动前的口径写着「被打回的需求行已失活,不在本列表内」**:这句对**需求行**依然成立(打回行确实不进 `requirements`),但对**户**不再成立——打回户现在会出现在 `households` 里。字段级语义以本交接件与各字段的 `@ApiModelProperty` 为准。
|
||||||
|
|
||||||
|
## 四、契约约束与正确调用方式(接口类必写)
|
||||||
|
|
||||||
|
1. **两个新字段恒非 null,直接读不用判空**:`submitState` 与 `rejectedRequirements` 对每一户都有确定取值(后者无打回时是空数组)。需要判空的仍是 `status` / `statusName` / `teamNo` / `consultantId` / `consultantName` 这些既有字段。
|
||||||
|
2. **催办分组按 `submitState` 做**:`NEVER_SUBMITTED` → 催首次提交;`REJECTED_PENDING_RESUBMIT` → 催按意见重提(意见与时刻在 `rejectedRequirements` 里);`SUBMITTED` → 具体到哪一步看 `status` / `requirements[].status`。
|
||||||
|
3. **不要用 `status == null` 当「未提交」的判据**:这是本次要解决的那个缺陷本身。前端若已有这段逻辑,请改为 `submitState === 'NEVER_SUBMITTED'`。
|
||||||
|
4. **不要把 `rejectedRequirements` 并进需求行列表**:两者结构刻意不同构。驳回信息建议单独渲染成一条提示条(类别 + 状态中文名 + 意见 + 时刻),与需求行区分开。
|
||||||
|
5. **不要拿 `needsVehicle` 反推「这户为什么在列表里」**:列表的并集口径已变,判据是 `submitState`。
|
||||||
|
6. **中文名一律用后端下发的**:`submitStateName` / `kindName` / `statusName` 都由后端给出,前端不要再本地维护映射表(`PENDING_REVIEW` 的文案还会按 kind 分叉成两种,本地表必然对不上,#8218)。
|
||||||
|
7. **要看全貌不传 `kind`**:不传 = 两类都返。传了 `kind` 则 `requirements`、`rejectedRequirements`、`submitState`、`householdCount`、`vehicleRowCount` 全部随之收窄(只有 `countedHouseholdCount` 三种筛选读数相同)。
|
||||||
|
8. **`kind` 只接受 `TRAVEL` / `TRANSFER`**:想表达「全部」请**不传**,传 `ALL` / `BOTH` 会返 809000。
|
||||||
|
9. **错误信封按 `code` 判**:业务失败与入参校验一律 HTTP 200 + 信封 `code`;测试环境网关对失效令牌也返回 HTTP 200 + `code: 401`。
|
||||||
|
|
||||||
|
## 五、数据库行为
|
||||||
|
|
||||||
|
本端点为只读查询,本次改动**不涉及任何 DDL 与 DML**:没有新增表、没有新增列、没有 Flyway 脚本、没有写入。
|
||||||
|
|
||||||
|
- `submitState` **不是数据库列**,不落表、不参与任何 SQL 过滤或分组,纯粹是响应字段——所以既有的按 `status` 筛选 / 统计的扫描路径不会把它重新捡起来,也不会误计。
|
||||||
|
- 打回明细取自用车需求的**版本历史行**(打回行已 `is_active=0`)。取数用**一次按子订单 ID 批量**的查询(与既有的活跃行查询同一形状,`requirement_kind` 的 `IN` 列表从 1 个值放宽到 2 个值),**不是逐户 N+1**;整页固定若干次查询,与户数无关。
|
||||||
|
- 判「某类是否处于打回待重提」复用的是定制师提交侧闸门的同一套算法(取最高版本组、看最后一次动作是否为打回),不新造判定规则。
|
||||||
|
|
||||||
|
## 六、边界行为
|
||||||
|
|
||||||
|
| 场景 | `status` | `requirements` | `submitState` | `rejectedRequirements` | 是否出现在列表 |
|
||||||
|
|------|----------|----------------|---------------|------------------------|----------------|
|
||||||
|
| 有活跃行(TRAVEL 或 TRANSFER) | 首条行的状态 | 1~2 条 | `SUBMITTED` | `[]` | 是 |
|
||||||
|
| 从未提交过任何版本 | `null` | `[]` | `NEVER_SUBMITTED` | `[]` | 是(`needs_vehicle` 为真即出卡) |
|
||||||
|
| 被打回、尚未重提 | `null` | `[]` | `REJECTED_PENDING_RESUBMIT` | 1~2 条 | 是(**本次新增的入列路径**) |
|
||||||
|
| 被打回后已重新提交 | 新行的状态 | 1~2 条 | `SUBMITTED` | `[]`(不挂已处理完的驳回) | 是 |
|
||||||
|
| TRAVEL 有活跃行 + TRANSFER 被打回 | TRAVEL 行的状态 | 1 条(TRAVEL) | `SUBMITTED`(按展示序首条) | 1 条(TRANSFER) | 是 |
|
||||||
|
| TRAVEL 被打回 + TRANSFER 有活跃行 | TRANSFER 行的状态 | 1 条(TRANSFER) | `REJECTED_PENDING_RESUBMIT`(TRAVEL 展示序在前) | 1 条(TRAVEL) | 是 |
|
||||||
|
| 只有 TRANSFER 被打回,且传了 `kind=TRAVEL` | `null` | `[]` | `NEVER_SUBMITTED`(该类别不在筛选内) | `[]` | 取决于 `needs_vehicle` |
|
||||||
|
| TRANSFER 被「不再需要接送」失活且无新版(#8435) | `null` | `[]` | `NEVER_SUBMITTED`(定案) | `[]` | 是 |
|
||||||
|
| 在团订单行缺失(数据异常) | — | — | — | — | 跳过该户并留痕,整页照常返回 |
|
||||||
|
| 户数超过 500 | — | — | — | — | 按 `orderNo` 升序截断,计数按截断后重算 |
|
||||||
|
|
||||||
|
## 六.5、枚举 / 数据字典
|
||||||
|
|
||||||
|
**户级提交态**(`submitState` → `submitStateName`,新增枚举,**恒非 null**)
|
||||||
|
|
||||||
|
| 码 | 中文名 | 语义 | 催办动作 |
|
||||||
|
|----|--------|------|----------|
|
||||||
|
| NEVER_SUBMITTED | 从未提交 | 在本次筛选的类别范围内既没有活跃需求行、也不处于打回态 | 催首次提交 |
|
||||||
|
| SUBMITTED | 已提交 | 至少一类存在活跃需求行(具体到哪一步看 `status`) | 按 `status` 跟进 |
|
||||||
|
| REJECTED_PENDING_RESUBMIT | 已被打回待重提 | 没有活跃行,但最高版本组的最后一次动作是打回 | 催「按意见改完再提」 |
|
||||||
|
|
||||||
|
> 展示名刻意与团期子订单列表那块的「未提交」用不同的词:那块只判「有没有活跃行」,被打回的户在那里也显示「未提交」;两处若同字,本次分开的这一步在页面上就白做了。
|
||||||
|
|
||||||
|
**打回状态**(`rejectedRequirements[].status` → `statusName`)
|
||||||
|
|
||||||
|
| 码 | 中文名 | 谁打回的 |
|
||||||
|
|----|--------|----------|
|
||||||
|
| REJECTED_TO_CONSULTANT | 已驳回定制师 | 团期管理员打回定制师 |
|
||||||
|
| REJECTED_TO_ADMIN | 已驳回管理员 | 车务退回团期管理员 |
|
||||||
|
|
||||||
|
**需求类别**(`kind` → `kindName`,未变)
|
||||||
|
|
||||||
|
| 码 | 中文名 |
|
||||||
|
|----|--------|
|
||||||
|
| TRAVEL | 行程用车 |
|
||||||
|
| TRANSFER | 接送机 |
|
||||||
|
|
||||||
|
展示序固定 TRAVEL 在前、TRANSFER 在后;`requirements` 与 `rejectedRequirements` 共用这个序。
|
||||||
|
|
||||||
|
**活跃需求行状态**(`requirements[].status`,未变):`PENDING_REVIEW` / `PENDING` / `PROCESSING` / `DONE`。`REJECTED_*` 不会出现在活跃行上。`PENDING_REVIEW` 的中文名按 kind 分叉:TRAVEL = 待提交车务、TRANSFER = 待审核(#8218)。
|
||||||
|
|
||||||
|
## 六.6、修改前后对比
|
||||||
|
|
||||||
|
| 字段 / 口径 | 改动前 | 改动后 |
|
||||||
|
|-------------|--------|--------|
|
||||||
|
| `households[].submitState` | 不存在 | 🆕 恒非 null 的三态字段,是 `status` 为 `null` 时唯一能分辨「从未提交 / 已被打回」的字段 |
|
||||||
|
| `households[].submitStateName` | 不存在 | 🆕 三态中文名,后端下发 |
|
||||||
|
| `households[].rejectedRequirements` | 不存在(打回信息在本端点完全取不到) | 🆕 恒非 null 的数组,列当前处于打回待重提的类别 + 意见 + 时刻 + 版本号 |
|
||||||
|
| `households[].status` / `statusName` | 只由活跃行决定 | **取值规则一字未改**(仍只由活跃行决定)。变的只是文档:不能再拿它判「有没有提交过」 |
|
||||||
|
| `households[].requirements` | 只含活跃行,打回行不在其中 | **内容零变化** |
|
||||||
|
| `householdCount` | = 应报车户 ∪ 有活跃需求行的户;恒等于 `needs_vehicle=true` 的户数(`needs_vehicle` 创单恒真) | 并集多一项「处于打回待重提的户」⇒ **不再恒等于 `needs_vehicle=true` 的户数**;`needs_vehicle` 为假但有一类被打回的户会进来 |
|
||||||
|
| `vehicleRowCount` | Σ 各户活跃行数 | **口径未变**(打回户贡献 0 行);与 `householdCount` 的差额多了「打回户」这一类 |
|
||||||
|
| `countedHouseholdCount` | 有活跃 TRAVEL 行的户数 | **一字未变**,不因驳回动作跳变 |
|
||||||
|
| 被打回户是否出现在列表 | `needs_vehicle` 为假时**不出卡**(催办页上整户消失) | 出卡,`submitState` = `REJECTED_PENDING_RESUBMIT` |
|
||||||
|
| 入参 / 分页 / 排序 / 错误码 | — | 全部未变 |
|
||||||
|
|
||||||
|
## 六.7、影响评估
|
||||||
|
|
||||||
|
- **前端必须改的**:如果页面上有「`status == null` ⇒ 显示未提交」这段逻辑,**必须**改成读 `submitState`——不改的话打回户会继续被标成「未提交」,本次改动在页面上等于没做。
|
||||||
|
- **前端应当改的**:催办清单按 `submitState` 分两组;被驳回的户上渲染 `rejectedRequirements` 里的类别 + 状态中文名 + 意见 + 时刻。
|
||||||
|
- **前端不要做的**:把 `rejectedRequirements` 拼进需求行表格(会出现与活跃行矛盾的一条);拿 `needsVehicle` 反推入列原因;跨类比较 `version`。
|
||||||
|
- **可能被读错的一处**:`requirements[].returnRemark` / `returnedAt` 记的是**该活跃行**历史上被打回过的痕迹,已重提后仍可能有值;「当前处于打回态」只看 `rejectedRequirements` 是否非空。
|
||||||
|
- **列表条数会变多**:`needs_vehicle` 为假、无活跃行、但有一类被打回的户从本次起入列。如果前端有基于户数的断言或埋点基线,会看到这一类团期的户数上升——这是预期,不是数据错误。
|
||||||
|
- **兼容性**:JSON 新增字段对已有前端反序列化无影响。既有字段一个没删、没改名、没改类型。
|
||||||
|
- **无副作用面**:只读端点,不涉及写入、事务、消息、权限判定变化;座位与汇总口径不变。
|
||||||
|
|
||||||
|
## 七、不影响范围
|
||||||
|
|
||||||
|
- **入参**:`groupBatchId`、`kind` 的取值域、缺省语义(不传 = 两类)、校验规则全部未变。
|
||||||
|
- **排序与上限**:`orderNo` 升序 + `orderId` 兜底、单次 500 户上限未变。
|
||||||
|
- **既有响应字段**:`groupBatchId` / `departDate` / `endDate` / `vehicleRowCount` / `countedHouseholdCount` / `households[]` 的既有字段(含 `status` / `statusName` / `requirements` 及行内所有字段)名称、类型、取值域、语义全部未变。
|
||||||
|
- **错误码**:未新增、未删除、未改文案(589500 / 589507 / 809000 / 401)。
|
||||||
|
- **权限码**:仍是团期查看权限,未收紧未放宽。
|
||||||
|
- **用房侧** `hotel-households` 端点:本次一行未改。
|
||||||
|
- **写路径**:逐户提交车务、打回、整体确认需求等写接口本次一行未改。
|
||||||
|
- **团级汇总** `requirement-summary`:口径与读数未变(`countedHouseholdCount` 是它的对账口,本次刻意保持不动)。
|
||||||
|
- **网关路由**:既有路由,本次无新增。
|
||||||
|
- **数据库**:无 DDL、无 DML、无 Flyway 脚本。
|
||||||
|
- **小程序端**:零影响。
|
||||||
|
|
||||||
|
## 八、测试环境已验证
|
||||||
|
|
||||||
|
本次改动的核心可验证面是「打回户与从未提交户在 `status` 上同形、在 `submitState` 上可分辨」,以及「既有三个计数与 `requirements` 内容不受影响」。已由下列自动化用例覆盖(`hl-order-service-v3`):
|
||||||
|
|
||||||
|
| 覆盖点 | 用例 |
|
||||||
|
|--------|------|
|
||||||
|
| 打回户与从未提交户 `status` 相同(都是 `null`)、`submitState` 不同 | `GroupBatchVehicleHousehold8562Test#households_rejectedAndNeverSubmitted_sameStatusDifferentSubmitState` |
|
||||||
|
| 打回户带出打回意见与打回状态中文名 | `#households_rejectedHousehold_carriesRemarkAndStatusName` |
|
||||||
|
| 存在打回时 `requirements` 仍然只含活跃行(内容零变化) | `#households_rejectionPresent_requirementsStillOnlyActiveRows` |
|
||||||
|
| TRAVEL 有活跃行 + TRANSFER 被打回 ⇒ `submitState` 按展示序首条(TRAVEL)取 | `#households_travelActiveTransferRejected_submitStateFollowsTravel` |
|
||||||
|
| TRAVEL 被打回 + TRANSFER 有活跃行 ⇒ `submitState` 同样按 TRAVEL 取 | `#households_travelRejectedTransferActive_submitStateFollowsTravel` |
|
||||||
|
| 传 `kind=TRANSFER` 时 TRAVEL 的打回被筛掉 | `#households_kindTransfer_travelRejectionFilteredOut` |
|
||||||
|
| `needs_vehicle` 为假但有一类被打回的户仍然入列 | `#households_rejectedButNeedsVehicleFalse_stillListed` |
|
||||||
|
| 完全没有打回时 `submitState` 为 `SUBMITTED`、`rejectedRequirements` 为空数组 | `#households_noRejectionAtAll_submitStateSubmitted` |
|
||||||
|
| 打回明细按子订单 ID 批量取数(固定次数,不随户数增长) | `RequirementServiceVehicleRejectionBatchTest` |
|
||||||
|
|
||||||
|
## 九、相关历史 PR
|
||||||
|
|
||||||
|
- PR #8623(本次):`feat(order-v3): 团期逐户用车区分「从未提交」与「已被打回待重提」(#8562)`。
|
||||||
|
- #8195:`householdCount` 改为「应报车户数」并开始包含未提交户,`status` / `statusName` 两个户级字段在那一单新增。
|
||||||
|
- #8559:`countedHouseholdCount` 不随 `kind` 筛选变化。
|
||||||
|
- #8577:只提交接送机的户不再被判「未提交用车需求」。
|
||||||
|
- #8601:逐户提交车务与打回的 `kind` 参数取消默认值。
|
||||||
|
- #8218:`PENDING_REVIEW` 的中文名按 kind 分叉(TRAVEL 待提交车务 / TRANSFER 待审核)。
|
||||||
|
- #8435:「不再需要接送」失活 TRANSFER 需求(本单已知边界的来源)。
|
||||||
|
|
||||||
|
## 十、相关文档
|
||||||
|
|
||||||
|
- `docs/CODE_RULES.md` §3:VO 命名、`@ApiModelProperty` 约定、禁 Entity 跨层(打回明细用独立 DTO 而非直接传需求行的依据)。
|
||||||
|
- `docs/CODE_RULES.md` §15.7:字典字面量单源——`submitStateName` / `kindName` / `statusName` 由后端下发的依据。
|
||||||
|
- Swagger:`hl-order-service-v3` → `团期需求` 分组。字段级语义以各字段 `@ApiModelProperty` 为准;该端点 `notes` 的那句「被打回的需求行不在本列表内」只对需求行成立、对户不成立(见「业务边界」最后一条)。
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
### 链接
|
||||||
|
|
||||||
|
- 工单 #8562
|
||||||
|
- PR #8623
|
||||||
|
|
||||||
|
### 联系人
|
||||||
|
|
||||||
|
- 后端:wx
|
||||||
|
- 前端:mmg(管理后台 hl-ui)
|
||||||
@@ -0,0 +1,578 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "8621"
|
||||||
|
title: "派车三个读口补齐状态中文名,枚举码不再裸下发(含 #8620 用车控制状态口径澄清)"
|
||||||
|
consumer: "admin"
|
||||||
|
author: "wx(GIT)"
|
||||||
|
change_type: "修改接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "not_required"
|
||||||
|
frontend_status: "pending"
|
||||||
|
frontend_owner: ""
|
||||||
|
frontend_ref: ""
|
||||||
|
target_release: ""
|
||||||
|
verified_at: ""
|
||||||
|
status_note: "三个既有管理后台读口各新增中文名字段,纯新增、无删除、无改名、无取值变化:(1) GET /admin/fleet/board/orders 的 records[] 新增 baseAssignmentStatusLabel(代表日行落库派单态中文名,与既有 baseAssignmentStatus 恒成对非空;它与 assignmentStatusLabel 是两个不同口径——前者是落库态、不含派生态,后者是覆写后的有效态、会出现临期加急派生态);(2) GET /admin/fleet/group-dispatch/pending-batches 的 records[] 新增 batchStatusName(团期生命周期状态中文名,九态全覆盖)与 dispatchProgressLabel(配车进度中文名,未开始/部分排车/已排满);(3) GET /admin/fleet/group-dispatch/batches/{groupBatchId}/overview 的 days[].vehicles[] 新增 statusLabel(派车状态中文名,已派车/已确认,字典另含已取消)。三个字典的共同不变量:码为 null 则中文名为 null(不编默认文案),码非空则中文名必非空;未登记的新码原样回落成码本身,不抛异常也不返回 null——所以前端渲染时不要假设这一格一定是中文,但不必为未知码写空值兜底。这批字段存在的唯一目的是把码→中文的字典收成后端单源(CODE_RULES §15.7),前端本地映射表请改为直接渲染后端下发值:本地表在遇到未登记新码时会显示空白,后端值至少是码本身。同时随 #8620 澄清一条既有字段的读法(字段名与取值零变化):orders[].vehicleControlStatus 是订单级单值、行程用车与接送机两类共用一格,非 DONE 只代表两类里至少一类没齐、说不出是哪一类;要分辨哪类没齐请读同级按类别拆开的字段(travelRequirementStatus / transferDeclared / transferPendingCount)。三个端点的入参、分页、过滤、排序、错误码(100001 / 600012 / 600013 / 401)与其余响应字段均未变化。"
|
||||||
|
updated_at: "2026-09-30"
|
||||||
|
base: "dev-v3"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 车务派车读口:状态中文名补齐,枚举码不再裸下发
|
||||||
|
|
||||||
|
> **存放目录**: 二期(order-v3/fleet)→ `changelogs-v2/2026-09/`
|
||||||
|
|
||||||
|
## ⚠️ 关键变化
|
||||||
|
|
||||||
|
- **三个既有读口共新增 4 个中文名字段,纯新增**:`records[].baseAssignmentStatusLabel`(派单看板订单清单)、`records[].batchStatusName` + `records[].dispatchProgressLabel`(待配车团期清单)、`days[].vehicles[].statusLabel`(团期配车总览)。
|
||||||
|
- **既有字段一个没动**:`assignmentStatus` / `assignmentStatusLabel` / `baseAssignmentStatus` / `batchStatus` / `dispatchProgress` / `vehicles[].status` 的字段名、类型、取值域、语义与本次改动前逐字相同;入参、分页、过滤、排序、错误码也未变。
|
||||||
|
- **三个字典共用同一组不变量**:码为 `null` ⇒ 中文名同为 `null`(不编默认文案);码非空 ⇒ 中文名必非空;**未登记的新码原样回落成码本身**,既不抛异常也不返回 `null`。所以前端**不需要**为「没见过的码」写空值兜底分支,但**渲染时不要假设这一格一定是中文**(回落时它就是那个码)。
|
||||||
|
- **前端请停用本地的码 → 中文映射表**,直接渲染后端下发的中文名字段。本地表在遇到未登记新码时渲染成空白,而后端值至少是码本身;这批字段存在的唯一理由就是把字典收成后端单源(CODE_RULES §15.7)。
|
||||||
|
- 🔴 **`baseAssignmentStatusLabel` 与 `assignmentStatusLabel` 不是一回事,别混用**:前者是**落库态**中文名(不派生、不被当前需求口径覆写,永远是 6 个落库态之一),后者是**覆写后的有效态**中文名(可能对应 `unassigned_urgent` / `holding_urgent` 这类派生态)。要展示「落库态 vs 有效态」并排对照(陈旧定稿排查场景)才需要前者;常规状态列继续用后者。
|
||||||
|
- 🔴 **`vehicleControlStatus` 是订单级单值、两类共用一格**(#8620,本次只澄清读法,字段与取值零变化):它非 `DONE` 只说明「行程用车与接送机里至少一类没齐」,**说不出是哪一类**。要分辨请读同级按类别拆开的字段(`travelRequirementStatus` / `transferDeclared` / `transferPendingCount`)。
|
||||||
|
- **`CANCELLED`(已取消)在派车状态字典里有中文名**。团期配车总览的逐车项 `status` 正常只会出现 `ASSIGNED` / `CONFIRMED`(已取消的派车行不进总览),但字典三码全覆盖,前端若自行构造筛选项按两值即可。
|
||||||
|
|
||||||
|
## 一、背景(选填)
|
||||||
|
|
||||||
|
这批读口此前把枚举码裸下发:`baseAssignmentStatus`、`batchStatus`、`dispatchProgress`、`vehicles[].status` 四处只有码、没有中文名,而同一行上别的状态字段(如 `assignmentStatusLabel`、`requirementKindLabel`)早已由后端下发中文名。结果是前端必须在本地再维护一份码 → 中文的映射表,这份表与后端枚举是两份真源:后端加一个码,前端那格就渲染成空白,而且没有任何信号提示。本次把这四处补齐成「码 + 中文名成对下发」,字典的唯一来源放在后端枚举里。
|
||||||
|
|
||||||
|
## 二、变更接口清单
|
||||||
|
|
||||||
|
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||||
|
|---|------|------|------|----------|------|
|
||||||
|
| 1 | 派单看板订单清单 | GET | `/admin/fleet/board/orders` | 修改 | `records[]` 新增 `baseAssignmentStatusLabel`(落库派单态中文名) |
|
||||||
|
| 2 | 待配车团期清单 | GET | `/admin/fleet/group-dispatch/pending-batches` | 修改 | `records[]` 新增 `batchStatusName`(团期状态中文名)与 `dispatchProgressLabel`(配车进度中文名) |
|
||||||
|
| 3 | 团期配车总览 | GET | `/admin/fleet/group-dispatch/batches/{groupBatchId}/overview` | 修改 | `days[].vehicles[]` 新增 `statusLabel`(派车状态中文名);`orders[].vehicleControlStatus` 读法澄清(#8620) |
|
||||||
|
|
||||||
|
## 三、接口详情
|
||||||
|
|
||||||
|
### 1. 派单看板订单清单 `GET /admin/fleet/board/orders`
|
||||||
|
|
||||||
|
**VO**: `BoardOrderPageReqVO → BoardOrderPageRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
车务派单看板的订单清单(list / grid 两视图共用)。本次变更只在每行上多给一个中文名字段,供「落库态 vs 有效态」并排展示的排查场景使用;常规状态列继续用 `assignmentStatusLabel`。
|
||||||
|
|
||||||
|
#### 入参
|
||||||
|
|
||||||
|
入参本次**零变化**,为便于自洽联调完整列出(全部 query 参数,全部选填)。
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| statuses | query | String[] | 否 | `unassigned` / `unassigned_urgent` / `holding` / `holding_urgent` / `assigned` / `canceled` / `completed` | 多状态筛选,含派生态,任一命中即返;空=不过滤 |
|
||||||
|
| status | query | String | 否 | 同 `statuses` 取值域 | `statuses` 的别名,单值或逗号分隔,与 `statuses` 合并 |
|
||||||
|
| startDayFrom | query | LocalDate | 否 | `YYYY-MM-DD` | 日期区间起,与行程区间重叠(非仅出团日);单边只约束一侧 |
|
||||||
|
| startDayTo | query | LocalDate | 否 | `YYYY-MM-DD` | 日期区间止 |
|
||||||
|
| startDate | query | LocalDate | 否 | `YYYY-MM-DD` | `startDayFrom` 的兼容别名,未传 `startDayFrom` 时生效 |
|
||||||
|
| endDate | query | LocalDate | 否 | `YYYY-MM-DD` | `startDayTo` 的兼容别名,未传 `startDayTo` 时生效 |
|
||||||
|
| vehicleTypeKeys | query | String[] | 否 | — | 车型大类多选;未派按需求车型、已派按实际车辆大类过滤 |
|
||||||
|
| typeKeys | query | String[] | 否 | — | `vehicleTypeKeys` 的兼容别名,未传前者时生效 |
|
||||||
|
| driverName | query | String | 否 | — | 司机姓名模糊搜索 |
|
||||||
|
| keyword | query | String | 否 | — | 统一文字搜索:司机 / 联系人 / 团号 / 订单号 / 当前定制师显示名任一包含 |
|
||||||
|
| contactName | query | String | 否 | — | 联系人(客户名)模糊搜索 |
|
||||||
|
| contactKeyword | query | String | 否 | — | `contactName` 的兼容别名 |
|
||||||
|
| teamNo | query | String | 否 | — | 团号模糊搜索(仅匹配真实团号,不匹配订单号) |
|
||||||
|
| groupBatchId | query | Long | 否 | — | 运营团期 ID 精确筛选 |
|
||||||
|
| orderKind | query | String | 否 | `ALL` / `NORMAL` / `GROUP`,其余值返 100001 | 订单归属粗筛;不传或空串=`ALL`。`NORMAL` 与 `groupBatchId` 同传逻辑互斥,返空列表不报错 |
|
||||||
|
| requirementKind | query | String | 否 | `TRAVEL` / `TRANSFER`,其余值返 100001 | 用车需求类别筛选;不传或空串=不过滤 |
|
||||||
|
| consultantId | query | Long | 否 | — | 当前负责定制师管理员 ID 精确筛选(下拉值由看板汇总接口下发) |
|
||||||
|
| plannerName | query | String | 否 | — | 定制师姓名模糊搜索(兼容旧前端) |
|
||||||
|
| consultantName | query | String | 否 | — | `plannerName` 的别名 |
|
||||||
|
| variant | query | String | 否 | `list`(默认)/ `grid`,其余值返 100001 | 视图 |
|
||||||
|
| page | query | Integer | 否 | ≥ 1,默认 1 | 页码;`pageNo` 是其兼容别名 |
|
||||||
|
| pageSize | query | Integer | 否 | 1~100,默认 20 | 每页条数 |
|
||||||
|
|
||||||
|
#### 出参 `Result<BoardOrderPageRespVO>`
|
||||||
|
|
||||||
|
只列与本次变更直接相关的字段;`records[]` 其余字段与本次改动前完全一致。
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| records | Array | 订单行列表,维度=当前有效用车需求;同一 `requirementId` 只返回一条 |
|
||||||
|
| records[].assignmentStatus | String | 当前派单状态码(含派生 `unassigned_urgent` / `holding_urgent`,会按当前需求口径覆写);**未变** |
|
||||||
|
| records[].assignmentStatusLabel | String | 当前派单状态中文名(有效态口径);**未变** |
|
||||||
|
| records[].baseAssignmentStatus | String | 代表日行落库基础状态码(不派生、不覆写):`unassigned` / `holding` / `assigned` / `canceled` / `exception` / `completed`;**未变** |
|
||||||
|
| records[].baseAssignmentStatusLabel | String | 🆕 落库基础状态中文名,与 `baseAssignmentStatus` 恒成对非空:待派车 / 待确认执行 / 已派车 / 已取消 / 异常 / 已完结。**永远不会出现派生态对应的文案**(派生态只进 `assignmentStatus`);未登记码原样回落成码本身 |
|
||||||
|
| total | Long | 总条数 |
|
||||||
|
| page | Integer | 当前页码 |
|
||||||
|
| pageSize | Integer | 每页条数 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /admin/fleet/board/orders?variant=list&startDayFrom=2026-10-01&startDayTo=2026-10-31&page=1&pageSize=20
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"id": "2103998277441093633",
|
||||||
|
"orderNo": "HL202610080031",
|
||||||
|
"teamNo": "T26-4128",
|
||||||
|
"orderId": "2103998277441093632",
|
||||||
|
"customerName": "周雅",
|
||||||
|
"headcount": 4,
|
||||||
|
"startDate": "2026-10-08",
|
||||||
|
"endDate": "2026-10-12",
|
||||||
|
"requirementKind": "TRAVEL",
|
||||||
|
"requirementKindLabel": "行程用车",
|
||||||
|
"assignmentStatus": "unassigned_urgent",
|
||||||
|
"assignmentStatusLabel": "待派车",
|
||||||
|
"baseAssignmentStatus": "unassigned",
|
||||||
|
"baseAssignmentStatusLabel": "待派车",
|
||||||
|
"manualUrgent": false,
|
||||||
|
"canAssign": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 20
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 无命中:`data.records` 返回空数组 `[]`,`total` 为 `0`,不返回 `null`,不报错。
|
||||||
|
- `records[]` 有行时 `baseAssignmentStatus` 与 `baseAssignmentStatusLabel` **必定同时非空**:落库态取自派单行的 `assignment_status`(NOT NULL);订单还没有落库派单行时走虚拟待派卡,落库态固定 `unassigned`、中文名固定「待派车」,不会出现「有码没中文名」或「有中文名没码」的半边状态。
|
||||||
|
- `order-v3` 整体不可达时,行上的日期、紧急态、排序会回退派单快照口径(本次未改这条既有降级路径),`baseAssignmentStatusLabel` 仍照常下发(它只依赖 fleet 本域落库行)。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 100001,
|
||||||
|
"message": "参数非法: variant 仅支持 list/grid,传入非法值:card",
|
||||||
|
"data": null,
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `100001 参数非法: {0}`:`variant` 非 `list`/`grid`、`orderKind` 非 `ALL`/`NORMAL`/`GROUP`、`requirementKind` 非 `TRAVEL`/`TRANSFER`、`page` < 1、`pageSize` 越界。
|
||||||
|
- `401`:未登录或令牌失效。注意测试环境网关对失效令牌返回 **HTTP 200 + 信封 `code: 401`**,前端拦截器请按信封 `code` 判定,不要只看 HTTP 状态行。
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- `baseAssignmentStatusLabel` 与 `assignmentStatusLabel` 走**同一份**映射(`AssignmentStatusEnum.labelOf`),只是喂进去的码不同:前者喂落库态、后者喂覆写后的有效态。所以同一行上两个中文名可能不同(例:落库 `assigned`「已派车」而有效态被当前需求口径覆写成「待派车」),**这不是数据错误**,正是本字段要暴露的对照。
|
||||||
|
- 派生态 `unassigned_urgent` / `holding_urgent` 只出现在 `assignmentStatus`,`baseAssignmentStatus` 与其中文名永远是 6 个落库态之一。前端若拿 `baseAssignmentStatusLabel` 当加急标识会永远读不到加急,加急请读 `assignmentStatus` 或 `manualUrgent` / `urgentBadge`。
|
||||||
|
- 落库态 `exception`(异常)在筛选入参 `statuses` 的取值域里**没有**对应筛选项,但它会作为 `baseAssignmentStatus` 的值出现在响应里,中文名「异常」。
|
||||||
|
- 中文名不参与任何筛选与排序,只是展示字段;按状态筛选一律传码。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 待配车团期清单 `GET /admin/fleet/group-dispatch/pending-batches`
|
||||||
|
|
||||||
|
**VO**: `GroupDispatchPendingBatchPageReqVO → PageResult<GroupDispatchPendingBatchRespVO>`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
车务「待配车团期」列表页。本次每行多给两个中文名:团期生命周期状态与配车进度,前端可直接渲染,不再需要本地两张映射表。
|
||||||
|
|
||||||
|
#### 入参
|
||||||
|
|
||||||
|
入参本次**零变化**,完整列出。
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| departDateFrom | query | LocalDate | 否 | `YYYY-MM-DD` | 出发日区间起;单边只约束一侧 |
|
||||||
|
| departDateTo | query | LocalDate | 否 | `YYYY-MM-DD` | 出发日区间止 |
|
||||||
|
| keyword | query | String | 否 | 长度 ≤ 50,超长返 600013 | 团号 / 团期名称模糊搜索 |
|
||||||
|
| dispatchProgress | query | String | 否 | `NOT_STARTED` / `PARTIAL` / `FULL`,其余值返 600013 | 按配车进度筛选;不传=不过滤 |
|
||||||
|
| page | query | Integer | 否 | ≥ 1,默认 1 | 页码 |
|
||||||
|
| pageSize | query | Integer | 否 | 1~100,默认 20 | 每页条数 |
|
||||||
|
|
||||||
|
#### 出参 `Result<PageResult<GroupDispatchPendingBatchRespVO>>`
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| records | Array | 待配车团期行列表 |
|
||||||
|
| records[].groupBatchId | String | 运营团期 ID(雪花 ID 以字符串下发) |
|
||||||
|
| records[].batchNo | String | 团号 |
|
||||||
|
| records[].batchName | String | 团期名称 |
|
||||||
|
| records[].batchStatus | String | 团期生命周期状态码;**未变** |
|
||||||
|
| records[].batchStatusName | String | 🆕 团期状态中文名,与 `batchStatus` 恒成对非空。九态见「六.5」;未登记码原样回落成码本身 |
|
||||||
|
| records[].departDate | String | 出发日 `YYYY-MM-DD` |
|
||||||
|
| records[].endDate | String | 结束日 `YYYY-MM-DD` |
|
||||||
|
| records[].serviceDayCount | Integer | 服务天数 |
|
||||||
|
| records[].enrolledOrders | Integer | 已报名子订单数 |
|
||||||
|
| records[].enrolledPeople | Integer | 已报名人数 |
|
||||||
|
| records[].requirementConfirmed | Boolean | 团期用车需求是否已确认 |
|
||||||
|
| records[].vehicleReady | Boolean | 车辆是否已就绪 |
|
||||||
|
| records[].dispatchedDayCount | Integer | 已排车天数 |
|
||||||
|
| records[].dispatchProgress | String | 配车进度码:`NOT_STARTED` / `PARTIAL` / `FULL`;**未变** |
|
||||||
|
| records[].dispatchProgressLabel | String | 🆕 配车进度中文名,与 `dispatchProgress` 恒成对非空:未开始 / 部分排车 / 已排满 |
|
||||||
|
| records[].transferPendingCount | Integer | 接送机未配计数;`null` = 未取到(**不是 0**,#8593) |
|
||||||
|
| records[].unreadCount | Integer | 未读会话消息数;依赖服务不可达时退化为 `0` |
|
||||||
|
| total | Long | 总条数 |
|
||||||
|
| page | Integer | 当前页码 |
|
||||||
|
| pageSize | Integer | 每页条数 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /admin/fleet/group-dispatch/pending-batches?departDateFrom=2026-10-01&departDateTo=2026-10-31&dispatchProgress=PARTIAL&page=1&pageSize=20
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"groupBatchId": "2104839654727618562",
|
||||||
|
"batchNo": "T26-3963",
|
||||||
|
"batchName": "呼伦贝尔环线 10/06 团",
|
||||||
|
"batchStatus": "RESOURCE_PREPARING",
|
||||||
|
"batchStatusName": "资源准备中",
|
||||||
|
"departDate": "2026-10-06",
|
||||||
|
"endDate": "2026-10-10",
|
||||||
|
"serviceDayCount": 5,
|
||||||
|
"enrolledOrders": 6,
|
||||||
|
"enrolledPeople": 18,
|
||||||
|
"requirementConfirmed": false,
|
||||||
|
"vehicleReady": false,
|
||||||
|
"dispatchedDayCount": 2,
|
||||||
|
"dispatchProgress": "PARTIAL",
|
||||||
|
"dispatchProgressLabel": "部分排车",
|
||||||
|
"transferPendingCount": 1,
|
||||||
|
"unreadCount": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1,
|
||||||
|
"page": 1,
|
||||||
|
"pageSize": 20
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 无命中:`records` 为空数组 `[]`,`total` 为 `0`。
|
||||||
|
- `batchStatusName` 由 order-v3 随团期候选项一起下发,fleet **原样透传、不在本域二次映射**(避免两份字典)。团期状态码为 `null` 时该中文名同为 `null`,后端不编默认文案;前端遇到这一格为 `null` 时请渲染成空白或「—」,不要回填「未知」这类自造文案。
|
||||||
|
- `dispatchProgressLabel` 与 `dispatchProgress` 在同一次判定里算出,不存在「码与文案分别算出来后对不上」的窗口,二者恒一致。
|
||||||
|
- `transferPendingCount` 的 `null` 与 `unreadCount` 的 `0` 是两条互相独立的软依赖退化路径,任一退化都不影响本次新增的两个中文名字段。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 600013,
|
||||||
|
"message": "排班查询参数非法: dispatchProgress 仅支持 NOT_STARTED/PARTIAL/FULL",
|
||||||
|
"data": null,
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `600013 排班查询参数非法: {0}`:`dispatchProgress` 取值非法、`keyword` 超长、分页参数越界。
|
||||||
|
- `600012 团期配车基线不可达,请稍后重试`:团期基线数据读不到;本端点不会用空列表冒充成功。
|
||||||
|
- `401`:未登录或令牌失效(网关返 HTTP 200 + 信封 `code: 401`)。
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- 中文名只用于展示。`dispatchProgress` 入参筛选仍只接受码(`NOT_STARTED` / `PARTIAL` / `FULL`),传中文名会按非法值返 600013。
|
||||||
|
- 团期状态字典是 order-v3 的九态全集(见「六.5」),本列表按「待配车」语义筛选后实际只会出现其中一部分;前端若要构造状态筛选下拉,请从本列表返回值里去重收集,不要按九态硬编码全集。
|
||||||
|
- `batchStatusName` 与团期管理列表页(order-v3 团期分页)的同名字段来自**同一个**转换方法,两页面上同一个团期的状态文案恒一致。
|
||||||
|
- 未登记的团期状态码回落成码本身(不抛异常),所以这一格可能出现英文码——前端不需要兜底,但列宽与换行请按可能出现英文码来设计。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 团期配车总览 `GET /admin/fleet/group-dispatch/batches/{groupBatchId}/overview`
|
||||||
|
|
||||||
|
**VO**: `Long groupBatchId(路径参数)→ GroupDispatchOverviewRespVO`
|
||||||
|
|
||||||
|
#### 使用场景
|
||||||
|
|
||||||
|
单个团期的配车总览:逐服务日的车辆卡片 + 本团子订单的用车控制状态。本次在逐车项上补齐派车状态中文名,并澄清 `orders[].vehicleControlStatus` 的读法(#8620)。
|
||||||
|
|
||||||
|
#### 入参
|
||||||
|
|
||||||
|
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
|------|------|------|------|------|------|
|
||||||
|
| groupBatchId | path | Long | 是 | 雪花 ID,正整数 | 运营团期 ID |
|
||||||
|
|
||||||
|
#### 出参 `Result<GroupDispatchOverviewRespVO>`
|
||||||
|
|
||||||
|
只列与本次变更直接相关的字段;其余字段与本次改动前完全一致。
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| groupBatchId | String | 运营团期 ID(字符串下发) |
|
||||||
|
| batchNo | String | 团号 |
|
||||||
|
| days | Array | 逐服务日节点 |
|
||||||
|
| days[].tripDate | String | 服务日 `YYYY-MM-DD` |
|
||||||
|
| days[].vehicles | Array | 该日已排车辆项 |
|
||||||
|
| days[].vehicles[].dispatchId | String | 派车行 ID |
|
||||||
|
| days[].vehicles[].vehiclePlate | String | 车牌 |
|
||||||
|
| days[].vehicles[].vehicleModel | String | 车型 |
|
||||||
|
| days[].vehicles[].driverName | String | 司机姓名 |
|
||||||
|
| days[].vehicles[].status | String | 派车状态码,取值 `ASSIGNED` / `CONFIRMED`;**未变** |
|
||||||
|
| days[].vehicles[].statusLabel | String | 🆕 派车状态中文名,与 `status` 恒成对非空:已派车 / 已确认(字典另含 `CANCELLED` 已取消,正常不出现在本列表) |
|
||||||
|
| days[].vehicleCount | Integer | 该日车辆数 |
|
||||||
|
| days[].dispatched | Boolean | 该日是否已排车 |
|
||||||
|
| orders | Array | 本团子订单的用车覆盖情况 |
|
||||||
|
| orders[].vehicleControlStatus | String | **订单级单值,行程用车与接送机两类共用一格**(#8620 澄清,取值与字段名未变);非 `DONE` 只代表两类里至少一类没齐,说不出是哪一类 |
|
||||||
|
| orders[].travelRequirementStatus | String | 行程用车需求状态(按类别拆开的字段之一) |
|
||||||
|
| orders[].transferDeclared | Boolean | 是否声明了接送机 |
|
||||||
|
| orders[].transferPendingCount | Integer | 该订单接送机未覆盖段数 |
|
||||||
|
| transferPendingTotal | Integer | 全团接送机未覆盖段数合计 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /admin/fleet/group-dispatch/batches/2104839654727618562/overview
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "成功",
|
||||||
|
"data": {
|
||||||
|
"groupBatchId": "2104839654727618562",
|
||||||
|
"batchNo": "T26-3963",
|
||||||
|
"departDate": "2026-10-06",
|
||||||
|
"endDate": "2026-10-10",
|
||||||
|
"requirementConfirmed": false,
|
||||||
|
"vehicleReady": false,
|
||||||
|
"days": [
|
||||||
|
{
|
||||||
|
"tripDate": "2026-10-06",
|
||||||
|
"vehicles": [
|
||||||
|
{
|
||||||
|
"dispatchId": "2104840113194905601",
|
||||||
|
"vehicleId": "1902233114509312002",
|
||||||
|
"vehiclePlate": "蒙E13572",
|
||||||
|
"vehicleModel": "丰田考斯特",
|
||||||
|
"driverId": "1902233114509312050",
|
||||||
|
"driverName": "李广宇",
|
||||||
|
"driverPhone": "13847001234",
|
||||||
|
"status": "ASSIGNED",
|
||||||
|
"statusLabel": "已派车",
|
||||||
|
"remark": null,
|
||||||
|
"groupCode": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"vehicleCount": 1,
|
||||||
|
"dispatched": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"missingDates": ["2026-10-09", "2026-10-10"],
|
||||||
|
"orders": [
|
||||||
|
{
|
||||||
|
"orderId": "2104839654727618570",
|
||||||
|
"orderNo": "HL202610060012",
|
||||||
|
"teamNo": "T26-3963",
|
||||||
|
"customerName": "周雅",
|
||||||
|
"headcount": 4,
|
||||||
|
"vehicleControlStatus": "PENDING_REVIEW",
|
||||||
|
"travelRequirementId": "2104839777884160001",
|
||||||
|
"travelRequirementStatus": "PENDING_REVIEW",
|
||||||
|
"transferDeclared": true,
|
||||||
|
"transferPendingCount": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transferPendingTotal": 1,
|
||||||
|
"conversationKey": "GROUP_FLEET:2104839654727618562"
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 某服务日还没排车:`days[].vehicles` 为空数组 `[]`,`vehicleCount` 为 `0`,`dispatched` 为 `false`;该日期同时出现在 `missingDates` 里。
|
||||||
|
- `vehicles[]` 有项时 `status` 与 `statusLabel` **必定同时非空**(派车行的状态列 NOT NULL);不存在「有码没中文名」的半边状态。
|
||||||
|
- 团期没有任何子订单时 `orders` 为空数组,`transferPendingTotal` 为 `0`。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 600012,
|
||||||
|
"message": "团期配车基线不可达,请稍后重试",
|
||||||
|
"data": null,
|
||||||
|
"success": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `600012 团期配车基线不可达,请稍后重试`:团期基线数据读不到;不会用空总览冒充成功。
|
||||||
|
- `401`:未登录或令牌失效(网关返 HTTP 200 + 信封 `code: 401`)。
|
||||||
|
|
||||||
|
#### 业务边界
|
||||||
|
|
||||||
|
- `statusLabel` 的字典含三个码(`ASSIGNED` 已派车 / `CONFIRMED` 已确认 / `CANCELLED` 已取消),但本总览只装载未取消的派车行,所以实际只会读到前两个。前端构造状态筛选或图例时按两值即可,不必为「已取消」留位置。
|
||||||
|
- 🔴 `orders[].vehicleControlStatus` 是**订单级单值**,行程用车与接送机两类共用这一格(#8620)。它非 `DONE` **不能**推断「行程用车没齐」,也不能推断「接送机没齐」——只能推断「至少一类没齐」。要落到具体类别,读 `travelRequirementStatus`(行程用车那一类)与 `transferDeclared` / `transferPendingCount`(接送机那一类)。
|
||||||
|
- `vehicleControlStatus` 的取值域是 order-v3 的需求状态集:`PENDING` / `PROCESSING` / `DONE` / `PENDING_REVIEW` / `REJECTED_TO_CONSULTANT` / `REJECTED_TO_ADMIN`。本次未新增、未删除取值。
|
||||||
|
- 本端点的 `statusLabel` 与派车详情等其它读口的派车状态文案同源(同一份枚举字典),不会出现两处对同一状态给不同中文名的情况。
|
||||||
|
- 中文名不参与任何筛选、排序或统计;`vehicleCount`、`transferPendingTotal`、`missingDates` 的口径本次未变。
|
||||||
|
|
||||||
|
## 四、契约约束与正确调用方式(接口类必写)
|
||||||
|
|
||||||
|
1. **只增不改**:本次三个端点各只新增字段,没有删除、没有改名、没有取值域变化。前端已有代码不改也不会坏;要拿到中文名才需要改。
|
||||||
|
2. **中文名与码成对读,成对判空**:`码 == null ⇒ 中文名 == null`、`码 != null ⇒ 中文名 != null`。判「这一格有没有值」只需判其中一个;两个都判是冗余的,但**不要**出现「码为 null 却期待中文名有值」的分支——那条路不存在。
|
||||||
|
3. **未登记码原样回落成码本身**:四个字典(派单落库态 / 团期生命周期 / 配车进度 / 派车状态)的中文名解析都不抛异常、不返回 `null`。后端将来加码时,前端这一格会显示英文码而不是空白。**所以前端不要写「中文名为空就显示码」的兜底**(永远进不去),但**要**按「这一格可能是英文码」设计列宽与样式。
|
||||||
|
4. **停用本地映射表**:读到中文名字段后请删掉前端本地那份码 → 中文的表。两份字典并存时,后端加码 = 前端空白,而且没有报错、没有告警,只有用户看到一格空白。
|
||||||
|
5. **筛选仍传码**:`statuses` / `status` / `dispatchProgress` / `requirementKind` / `orderKind` 一律只接受码。传中文名会按非法值报 100001(看板)或 600013(待配车清单)。
|
||||||
|
6. **落库态与有效态分清**:要展示「当前状态」用 `assignmentStatusLabel`;要展示「落库真实状态」用 `baseAssignmentStatusLabel`。用后者当状态列会让加急态与陈旧定稿覆写这两类信息全部消失。
|
||||||
|
7. **`vehicleControlStatus` 不可用于判别类别**(#8620):它是两类共用的单值。要按类别展示或筛选,用 `travelRequirementStatus` / `transferDeclared` / `transferPendingCount`,或走看板清单的 `requirementKind` 维度。
|
||||||
|
8. **错误信封统一按 `code` 判**:业务失败与入参校验一律 HTTP 200 + 信封 `code`;测试环境网关对失效令牌也返回 HTTP 200 + `code: 401`。只看 HTTP 状态行的拦截器会把「已掉登录」当成功。
|
||||||
|
|
||||||
|
## 五、数据库行为
|
||||||
|
|
||||||
|
三个端点均为只读查询,本次改动**不涉及任何 DDL 与 DML**:没有新增表、没有新增列、没有 Flyway 脚本、没有写入。新增的中文名字段全部在内存里由枚举字典解析出来,不落库、不参与任何 SQL 过滤或分组,因此既有的按状态码筛选 / 统计的查询路径读数一律不变。
|
||||||
|
|
||||||
|
## 六、边界行为
|
||||||
|
|
||||||
|
| 场景 | 行为 |
|
||||||
|
|------|------|
|
||||||
|
| 状态码为 `null` | 对应中文名同为 `null`,后端不编默认文案 |
|
||||||
|
| 状态码为未登记的新值 | 中文名回落成码本身,不抛异常、不返回 `null` |
|
||||||
|
| 订单无落库派单行(虚拟待派卡) | `baseAssignmentStatus` 固定 `unassigned`,`baseAssignmentStatusLabel` 固定「待派车」 |
|
||||||
|
| 同一行落库态与有效态不同 | 两个中文名不同,属预期(正是本字段的用途),不是数据错误 |
|
||||||
|
| 派生态(临期加急 / hold 超时) | 只进 `assignmentStatus`;`baseAssignmentStatus` 与其中文名永远是 6 个落库态之一 |
|
||||||
|
| 团期状态中文名的来源服务读不到 | 该格为 `null`(与码同生同灭),不影响同行其它字段 |
|
||||||
|
| 团期配车总览里有已取消的派车行 | 不装载进 `days[].vehicles`,所以 `statusLabel` 实际读不到「已取消」 |
|
||||||
|
| 无命中 / 无数据 | 列表返空数组,不返 `null`;不用空数据冒充成功以外的语义 |
|
||||||
|
|
||||||
|
## 六.5、枚举 / 数据字典
|
||||||
|
|
||||||
|
**落库派单状态**(`baseAssignmentStatus` → `baseAssignmentStatusLabel`,6 个落库态)
|
||||||
|
|
||||||
|
| 码 | 中文名 |
|
||||||
|
|----|--------|
|
||||||
|
| unassigned | 待派车 |
|
||||||
|
| holding | 待确认执行 |
|
||||||
|
| assigned | 已派车 |
|
||||||
|
| canceled | 已取消 |
|
||||||
|
| exception | 异常 |
|
||||||
|
| completed | 已完结 |
|
||||||
|
|
||||||
|
派生态 `unassigned_urgent`(→待派车)与 `holding_urgent`(→待确认执行)只出现在 `assignmentStatus`,**不会**出现在 `baseAssignmentStatus`。
|
||||||
|
|
||||||
|
**团期生命周期状态**(`batchStatus` → `batchStatusName`,九态)
|
||||||
|
|
||||||
|
| 码 | 中文名 |
|
||||||
|
|----|--------|
|
||||||
|
| RECRUITING | 招募中 |
|
||||||
|
| RESOURCE_PREPARING | 资源准备中 |
|
||||||
|
| MATERIAL_PREPARING | 物料准备中 |
|
||||||
|
| PENDING_DEPARTURE | 待出发 |
|
||||||
|
| TRAVELLING | 出行中 |
|
||||||
|
| TRIP_FINISHED | 出行完毕 |
|
||||||
|
| REVIEWING | 核单中 |
|
||||||
|
| SETTLED | 已结算 |
|
||||||
|
| CANCELLED | 已取消 |
|
||||||
|
|
||||||
|
**配车进度**(`dispatchProgress` → `dispatchProgressLabel`)
|
||||||
|
|
||||||
|
| 码 | 中文名 |
|
||||||
|
|----|--------|
|
||||||
|
| NOT_STARTED | 未开始 |
|
||||||
|
| PARTIAL | 部分排车 |
|
||||||
|
| FULL | 已排满 |
|
||||||
|
|
||||||
|
**派车状态**(`vehicles[].status` → `statusLabel`)
|
||||||
|
|
||||||
|
| 码 | 中文名 | 是否出现在配车总览 |
|
||||||
|
|----|--------|------------------|
|
||||||
|
| ASSIGNED | 已派车 | 是 |
|
||||||
|
| CONFIRMED | 已确认 | 是 |
|
||||||
|
| CANCELLED | 已取消 | 否(已取消的派车行不装载进总览) |
|
||||||
|
|
||||||
|
**订单级用车控制状态**(`vehicleControlStatus`,本次未改取值,仅澄清读法)
|
||||||
|
|
||||||
|
| 码 | 语义 |
|
||||||
|
|----|------|
|
||||||
|
| PENDING | 待处理 |
|
||||||
|
| PROCESSING | 处理中 |
|
||||||
|
| DONE | 两类都已齐 |
|
||||||
|
| PENDING_REVIEW | 待审核 |
|
||||||
|
| REJECTED_TO_CONSULTANT | 已驳回定制师 |
|
||||||
|
| REJECTED_TO_ADMIN | 已驳回管理员 |
|
||||||
|
|
||||||
|
## 六.6、修改前后对比
|
||||||
|
|
||||||
|
| 端点 | 字段 | 改动前 | 改动后 |
|
||||||
|
|------|------|--------|--------|
|
||||||
|
| `GET /admin/fleet/board/orders` | `records[].baseAssignmentStatusLabel` | 字段不存在(前端只能本地映射 `baseAssignmentStatus`) | 新增,与码恒成对非空 |
|
||||||
|
| `GET /admin/fleet/group-dispatch/pending-batches` | `records[].batchStatusName` | 字段不存在(只有 `batchStatus` 裸码) | 新增,与码恒成对非空,与团期管理列表页同源 |
|
||||||
|
| `GET /admin/fleet/group-dispatch/pending-batches` | `records[].dispatchProgressLabel` | 字段不存在(只有 `dispatchProgress` 裸码) | 新增,与码在同一次判定里算出 |
|
||||||
|
| `GET /admin/fleet/group-dispatch/batches/{groupBatchId}/overview` | `days[].vehicles[].statusLabel` | 字段不存在(只有 `status` 裸码) | 新增,与码恒成对非空 |
|
||||||
|
| `GET /admin/fleet/group-dispatch/batches/{groupBatchId}/overview` | `orders[].vehicleControlStatus` | 字段与取值相同,但文档未说明它是两类共用的订单级单值 | 字段与取值**完全不变**;文档明确:非 `DONE` 只代表至少一类没齐,判类别须读按类别拆开的字段(#8620) |
|
||||||
|
|
||||||
|
## 六.7、影响评估
|
||||||
|
|
||||||
|
- **前端必须改的**:无。不改一行也不会坏——四个字段都是新增,既有字段与取值零变化。
|
||||||
|
- **前端应当改的**:删掉本地的四张码 → 中文映射表,改读后端下发的中文名。收益是后端加码时不再出现静默空白格;不改的风险是本地表与后端字典分叉,且分叉无任何报错信号。
|
||||||
|
- **前端可能读错的一处**:把 `baseAssignmentStatusLabel` 当成「当前状态」显示在状态列 ⇒ 加急态与陈旧定稿覆写全部丢失。状态列仍应用 `assignmentStatusLabel`。
|
||||||
|
- **前端可能读错的另一处**(#8620):把 `vehicleControlStatus` 当成「行程用车状态」或「接送机状态」的单一来源 ⇒ 在只报接送机、或只报行程用车的订单上会给出误导性展示。判类别必须读按类别拆开的字段。
|
||||||
|
- **兼容性**:JSON 新增字段对已有前端反序列化无影响(未知字段忽略 / 多出字段不解析)。响应体每行增大 4 个短字符串量级,分页上限 100 行,体积影响可忽略。
|
||||||
|
- **无副作用面**:不涉及写入、不涉及事务、不涉及消息、不涉及权限判定,也不改任何筛选与统计口径。
|
||||||
|
|
||||||
|
## 七、不影响范围
|
||||||
|
|
||||||
|
- 三个端点的**入参**:字段、别名、默认值、校验规则、错误码全部未变。
|
||||||
|
- 三个端点的**分页、过滤、排序、聚合去重**口径全部未变。
|
||||||
|
- 三个端点的**既有响应字段**:名称、类型、取值域、语义全部未变,包括 `assignmentStatus` / `assignmentStatusLabel` / `baseAssignmentStatus` / `batchStatus` / `dispatchProgress` / `vehicles[].status` / `orders[].vehicleControlStatus`。
|
||||||
|
- **错误码**未新增、未删除、未改文案(100001 / 600012 / 600013 / 401)。
|
||||||
|
- **写接口**:派车提交、派车确认、需求打回等写路径本次一行未改。
|
||||||
|
- **网关路由**:三个端点都是既有路由,`/admin/fleet/**` 已配置,本次无新增路由。
|
||||||
|
- **数据库**:无 DDL、无 DML、无 Flyway 脚本。
|
||||||
|
- **小程序端**:本次改动全部落在管理后台读口,小程序端零影响。
|
||||||
|
|
||||||
|
## 八、测试环境已验证
|
||||||
|
|
||||||
|
本次改动的可验证面是「码 → 中文名」的映射与成对不变量,已由下列自动化用例覆盖(`hl-fleet-service` + `hl-order-service-v3`):
|
||||||
|
|
||||||
|
| 覆盖点 | 用例 |
|
||||||
|
|--------|------|
|
||||||
|
| 派车状态三码各返约定中文名(含正常读不到的 `CANCELLED`) | `GroupDispatchStatusTest#labelOf_allDeclaredCodes_returnsChineseLabel` |
|
||||||
|
| 派车状态:码非空 ⇒ 中文名必非空,且中文名不等于码本身 | `GroupDispatchStatusTest#labelOf_codeNotNull_labelNeverNull` |
|
||||||
|
| 派车状态:未知码原样回落不抛异常,`null` 返 `null` | `GroupDispatchStatusTest#labelOf_unknownCodeOrNull_fallsBackWithoutThrowing` |
|
||||||
|
| 配车进度三档各返约定中文名 | `GroupDispatchProgressTest#labelOf_allDeclaredCodes_returnsChineseLabel` |
|
||||||
|
| 配车进度:码非空 ⇒ 中文名必非空 | `GroupDispatchProgressTest#labelOf_codeNotNull_labelNeverNull` |
|
||||||
|
| 配车进度:未知码回落、`null` 返 `null`;`isValid` 只认三档 | `GroupDispatchProgressTest#labelOf_unknownCodeOrNull_fallsBackWithoutThrowing`、`#isValid_onlyDeclaredCodes` |
|
||||||
|
| 待配车清单:团期状态中文名原样透传上游、fleet 不做二次映射 | `GroupDispatchQueryServiceTest`(`#8621` 待配车清单用例) |
|
||||||
|
| 配车总览逐车项:`status` 与 `statusLabel` 恒成对非空 | `GroupDispatchQueryServiceTest`(`#8621` 逐车项用例) |
|
||||||
|
| 团期候选项下发状态中文名:与团期列表同一份映射,码非空则中文名必非空;码为 `null` 时中文名同为 `null`、不编默认文案 | `GroupBatchVehicleDispatchQueryServiceTest`(`#8621` 两个用例) |
|
||||||
|
| 看板订单行:落库态中文名与 `baseAssignmentStatus` 恒成对非空;虚拟待派卡也给中文名 | `BoardOrderServiceTest`(`#8621` 用例) |
|
||||||
|
|
||||||
|
## 九、相关历史 PR
|
||||||
|
|
||||||
|
- PR #8622(本次):`feat(fleet,order-v3): 派车读口补齐状态中文名,枚举码不再裸下发(#8620 #8621)`。
|
||||||
|
- #8593:待配车团期清单 `transferPendingCount` 由硬编码 0 改为真值,并引入 `null` = 未取到语义。
|
||||||
|
- #8518:看板清单 `requirementKind` 入参与 `requirementKindLabel` 出参(同一「后端下发中文名」方向的先例)。
|
||||||
|
- #7535:枚举中文名由枚举归属服务下发、后缀命名约定(`batchStatusName` 用 `Name` 而非 `Label` 的由来)。
|
||||||
|
|
||||||
|
## 十、相关文档
|
||||||
|
|
||||||
|
- `docs/CODE_RULES.md` §15.7:对称子域禁镜像重复 / 字典字面量单源——本次四个字段的立项依据。
|
||||||
|
- `docs/CODE_RULES.md` §3:VO 命名与 `@ApiModelProperty` 约定。
|
||||||
|
- Swagger:`hl-fleet-service` → `看板` 与 `团期配车` 分组,三个端点的字段注释已同步更新(含 `allowableValues`)。
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
### 链接
|
||||||
|
|
||||||
|
- 工单 #8621(补中文名)、#8620(`vehicleControlStatus` 订单级单值口径澄清)
|
||||||
|
- PR #8622
|
||||||
|
|
||||||
|
### 联系人
|
||||||
|
|
||||||
|
- 后端:wx
|
||||||
|
- 前端:mmg(管理后台 hl-ui)
|
||||||
在新工单中引用
屏蔽一个用户