diff --git a/changelogs-v2/2026-09/29_8518_派单看板列表下发用车需求类别-修改接口-管理后台.md b/changelogs-v2/2026-09/29_8518_派单看板列表下发用车需求类别-修改接口-管理后台.md new file mode 100644 index 00000000..b3d56acc --- /dev/null +++ b/changelogs-v2/2026-09/29_8518_派单看板列表下发用车需求类别-修改接口-管理后台.md @@ -0,0 +1,379 @@ +--- +schema: "hl-changelog/v2" +ticket: "8518" +title: "派单看板列表与汇总下发用车需求类别 requirementKind,支持按类别筛选" +consumer: "admin" +author: "wx(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "PR #8534(fix(fleet): 派单看板列表下发用车需求类别并支持按类别筛,关联 #8518)已合并 dev-v3,滚动部署测试服 hl-fleet-service @ dfb5db832(2026-09-29 17:48:30)。requirementKind/requirementKindLabel 两字段与同名可选筛选参数已实测:基线 47 条,TRAVEL 37/TRANSFER 10,两档相加等于基线且交集为空,requirementId 集合与基线一致;非法值返 100001。" +updated_at: "2026-09-29" +base: "dev-v3" +--- + +# hl-fleet-service: 派单看板下发用车需求类别 `requirementKind` + +**服务**: hl-fleet-service +**PR**: #8534(已合入 `dev-v3`,squash `dfb5db832`) +**Issue**: #8518 +**日期**: 2026-09-29 +**影响范围**: 管理后台车务「派单看板」的列表与汇总两个读口 + +--- + +## ⚠️ 关键变化 + +- 派单看板「团期订单 / 全部订单」列表上,同一订单若同时存在行程用车与接送机两条用车需求,会出**两张卡**——两张卡的订单号、团号、客户、定制师、行程日期、人数**逐字相同**,此前没有任何字段能分辨哪张是接送机。 +- `GET /admin/fleet/board/orders` 的 `data.records[]` **新增 `requirementKind` / `requirementKindLabel` 两个字段**,恒成对非空:`requirementKind` 取值 `TRAVEL`(行程用车)/ `TRANSFER`(接送机),`requirementKindLabel` 是对应中文标签,由后端下发,前端不要自己做 `kind → 中文` 的映射。 +- `GET /admin/fleet/board/orders` 与 `GET /admin/fleet/board/summary` **新增同名可选查询参数 `requirementKind`**,两个接口共用同一入参 VO。不传或传空串 = 不过滤,两类都返。 +- `requirementKind` 与既有的 `orderKind` 是**两个互不相交的维度**:`orderKind` 分订单归属(`ALL`/`NORMAL`/`GROUP`),`requirementKind` 分需求类别。两者可同传按 AND 组合,串用不会报错,只会筛出错误的行数,详见「四、契约约束与正确调用方式」。 +- **非法取值不被静默容忍**:非 `TRAVEL`/`TRANSFER` 且非空一律 **HTTP 200 + body `code=100001`**,`data=null`、`success=false`。 +- `GET /admin/fleet/board/summary` 的 `statusCounts` 与 `statusOptions[].count` 随 `requirementKind` **一起收窄**;`idleVehicleCount` / `idleDriverCount` 是全局物理资源指标,不受该筛选影响。 +- **`statusCounts` 里的 `unassignedUrgent` / `holdingUrgent` 是 `unassigned` / `holding` 的子集**(`statusOptions` 里以 `urgentCount` 形式出现),不是独立状态桶,前端加总时不要重复计入。 + +--- + +## 一、背景 + +车务派单看板存在同订单出两张卡、字段完全相同、无法分辨哪张是接送机的问题(wx/HL#8518)。本次为每条 record 补充需求类别下发(`requirementKind`/`requirementKindLabel`),并给列表与汇总两个读口各加一个同名可选筛选参数,用于把两类需求分列展示或过滤。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 派单看板列表 | GET | `/admin/fleet/board/orders` | 新增出参字段 + 可选入参 | 新增 `requirementKind`/`requirementKindLabel` 出参字段;新增可选筛选参数 `requirementKind`,非法值返 `100001` | +| 2 | 派单看板汇总 | GET | `/admin/fleet/board/summary` | 新增可选入参 | 新增可选筛选参数 `requirementKind`(与列表共用同一入参 VO),`statusCounts`/`statusOptions[].count` 随之收窄 | + +--- + +## 三、接口详情 + +### 1. 派单看板列表 `GET /admin/fleet/board/orders` + +**VO**: `BoardOrderPageReqVO → BoardOrderRecordVO` + +#### 使用场景 + +车务「派单看板」主列表。同一订单同时存在行程用车与接送机两条用车需求时会各出一张卡,此前两卡逐字段相同、无法分辨。本次每条 record 补充需求类别,前端可据此区分两张卡,或用新增的 `requirementKind` 查询参数直接按类别筛选。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| requirementKind | Query | String | 否 | `TRAVEL` / `TRANSFER`;其余非空值返 `100001` | 🆕 本次新增。用车需求类别筛选:`TRAVEL`=只看行程用车,`TRANSFER`=只看接送机。不传或传空串=不过滤,两类都返。与既有 `orderKind`(订单归属维度)互不相交,可同传按 AND 组合 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| data.records[].orderId | String | 订单 ID | +| data.records[].requirementId | String | 用车需求 ID | +| data.records[].teamNo | String | 团号 | +| data.records[].virtualPending | Boolean | 是否为还没有任何派车行的虚拟待派卡片 | +| data.records[].requirementKind | String | 🆕 本次新增。用车需求类别:`TRAVEL` 行程用车 / `TRANSFER` 接送机。取本条记录**所属需求自身**的类别,恒非空 | +| data.records[].requirementKindLabel | String | 🆕 本次新增。类别中文标签:`行程用车` / `接送机`。由后端下发,前端不要自己做 `kind→中文` 的映射,与 `requirementKind` 恒成对非空 | + +#### 请求示例 + +```http +GET /admin/fleet/board/orders?orderKind=ALL&pageSize=100&requirementKind=TRANSFER HTTP/1.1 +Host: <测试服网关> +Authorization: Bearer + +(GET 无请求体) +``` + +#### 响应示例 + +以下为按 `requirementKind=TRANSFER` 过滤后(实测命中 10 条)摘录其中 1 条,仅列本次相关字段与几个已知存在的字段(完整响应还含既有其余字段,本文档未逐一核对不重复列出);`orderId` 为 2026-09-29 17:48 测试服实测命中的真实并存订单之一(该订单同时存在 TRAVEL、TRANSFER 两条记录),`requirementId`/`teamNo` 为示意值: + +```json +{ + "code": 200, + "message": "成功", + "data": { + "records": [ + { + "orderId": "2101566624467419137", + "requirementId": "<示意值,真实用车需求 ID>", + "teamNo": "<示意值,真实团号>", + "virtualPending": false, + "requirementKind": "TRANSFER", + "requirementKindLabel": "接送机" + } + ], + "total": 10, + "page": 1, + "pageSize": 100 + }, + "traceId": null, + "success": true +} +``` + +#### 空数据 / 降级响应 + +本次测试窗口内 `requirementKind=TRAVEL`(37 条)与 `requirementKind=TRANSFER`(10 条)均非空,未专门验证 0 命中场景。`requirementKind` 非法取值走「错误响应」,不属于本节的空数据场景。 + +order-v3 整体不可达、取不到需求身份时的降级口径:`requirementKind` 回退 `TRAVEL`,与该场景下特殊诉求/备注回退快照同属既有降级口径。 + +#### 错误响应 + +`requirementKind` 非法(非 `TRAVEL`/`TRANSFER` 且非空),2026-09-29 测试服实测原文: + +```json +{ + "code": 100001, + "message": "参数非法: requirementKind 仅支持 TRAVEL/TRANSFER,传入非法值:BOGUS", + "data": null, + "traceId": null, + "success": false +} +``` + +HTTP 状态行仍是 **200**,判据在 body 的 `code` / `success`,不要只看状态码。 + +#### 业务边界 + +- 真实卡与**虚拟待派卡**(`virtualPending=true`)一律非空——实测 47 条里 10 条虚拟待派卡两字段全部有值。 +- **纯接送机订单**(没有 active 行程用车需求)如实返 `TRANSFER`,不受「顶层 `requirementId` 恒指 `TRAVEL`」那条既有契约影响。 +- 类别取自卡片自身归属的那条需求,不读订单级单值字段——单值恒取身份列表首项(两类并存时是 `TRAVEL`)。 +- order-v3 降级取不到需求身份时回退 `TRAVEL`(与该场景下特殊诉求/备注回退快照同属既有降级口径)。 +- `requirementKind` 与 `orderKind` 是两个互不相交的维度,串用不会报错,只会筛出错误的行数,详见「四、契约约束与正确调用方式」。 + +--- + +### 2. 派单看板汇总 `GET /admin/fleet/board/summary` + +**VO**: `BoardOrderPageReqVO → BoardSummaryVO` + +#### 使用场景 + +派单看板顶部的状态页签计数来源,与列表接口共用同一套筛选参数(同一入参 VO)。切换需求类别筛选时要和列表接口同步传同一个 `requirementKind`,否则会出现「列表条数与状态页签计数对不上」的界面表现。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| requirementKind | Query | String | 否 | `TRAVEL` / `TRANSFER`;其余非空值返 `100001` | 🆕 本次新增,与列表接口同名同取值、同缺省语义,两接口共用同一入参 VO `BoardOrderPageReqVO` | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| data.statusCounts | Object | 各状态桶计数,随 `requirementKind` 一起收窄 | +| data.statusCounts.unassigned | Integer | 待派车状态桶计数,随 `requirementKind` 收窄 | +| data.statusCounts.unassignedUrgent | Integer | `unassigned` 的**子集**(加急),不是独立状态桶 | +| data.statusCounts.holding | Integer | 排车中状态桶计数,随 `requirementKind` 收窄 | +| data.statusCounts.holdingUrgent | Integer | `holding` 的**子集**(加急),不是独立状态桶 | +| data.statusOptions[].count | Integer | 状态下拉选项计数,随 `requirementKind` 一起收窄,与 `statusCounts` 同口径 | +| data.statusOptions[].urgentCount | Integer | 该状态下的加急子集计数 | +| data.idleVehicleCount | Integer | 空闲车辆数:全局物理资源指标,**不受** `requirementKind` 影响 | +| data.idleDriverCount | Integer | 空闲司机数:全局物理资源指标,**不受** `requirementKind` 影响 | + +#### 请求示例 + +```http +GET /admin/fleet/board/summary?orderKind=ALL&requirementKind=TRAVEL HTTP/1.1 +Host: <测试服网关> +Authorization: Bearer + +(GET 无请求体) +``` + +#### 响应示例 + +以下为字段结构示意,`statusCounts` 内部逐桶数值为**示意拆分**(本次仅验证 `requirementKind=TRAVEL` 六桶之和 = 37,与列表 `total=37` 对齐,未逐桶记录具体读数;真实数值见「八、测试环境已验证」): + +```json +{ + "code": 200, + "message": "成功", + "data": { + "statusCounts": { + "unassigned": "<示意值,六桶之和已实测=37>", + "unassignedUrgent": "<示意值,unassigned 的子集>", + "holding": "<示意值>", + "holdingUrgent": "<示意值,holding 的子集>" + }, + "statusOptions": [ + { "count": "<示意值>", "urgentCount": "<示意值>" } + ], + "idleVehicleCount": "<全局值,不随 requirementKind 变化>", + "idleDriverCount": "<全局值,不随 requirementKind 变化>" + }, + "traceId": null, + "success": true +} +``` + +#### 空数据 / 降级响应 + +本次测试窗口内 `requirementKind=TRAVEL`/`TRANSFER` 两档六桶合计均非空(37/10)。降级口径与列表接口相同:order-v3 整体不可达时 `requirementKind` 判定回退 `TRAVEL`。 + +#### 错误响应 + +`requirementKind` 非法取值时与列表接口同一错误码,2026-09-29 测试服实测原文: + +```json +{ + "code": 100001, + "message": "参数非法: requirementKind 仅支持 TRAVEL/TRANSFER,传入非法值:BOGUS", + "data": null, + "traceId": null, + "success": false +} +``` + +#### 业务边界 + +- **`statusCounts` 的 `unassignedUrgent` / `holdingUrgent` 是 `unassigned` / `holding` 的子集**(`statusOptions` 里以 `urgentCount` 出现),**不是独立状态桶**,前端加总统计时不要重复计入。 +- `idleVehicleCount` / `idleDriverCount` 是全局物理资源指标,切换 `requirementKind` 时这两个数字不受影响。 +- 切换需求类别筛选页签时,务必与列表接口同步传同一个 `requirementKind`,否则会出现列表条数与状态页签计数对不上的情况。 +- `requirementKind` 非法取值的错误码、报文格式与列表接口完全一致。 + +--- + +## 四、契约约束与正确调用方式 + +### `requirementKind` 与 `orderKind` 是两个互不相交的维度 + +| 维度 | `orderKind` | `requirementKind` | +|------|-------------|--------------------| +| 问题 | 这个订单当前属不属于某个运营团期 | 这条用车需求本身是行程用车还是接送机 | +| 取值 | `ALL` / `NORMAL` / `GROUP` | `TRAVEL` / `TRANSFER` | +| 缺省 | 不传或空串 = `ALL`(不过滤) | 不传或空串 = 不过滤(两类都返) | +| 组合方式 | 与 `requirementKind` 按 AND 组合 | 同左 | + +两者语义完全独立,**串用不会报错,只会筛出错误的行数**——例如把 `requirementKind` 误传成了 `orderKind` 的取值(如 `orderKind=TRANSFER`),不会命中任何非法校验(`TRANSFER` 不在 `orderKind` 枚举内,会被 `orderKind` 自己的校验拦成 `100001`),但如果误把 `orderKind` 的取值传给 `requirementKind`(如 `requirementKind=GROUP`),同样会被 `requirementKind` 自己的校验拦截,报文里的字段名与传入值都能定位到问题,不会静默放行成一个"看似合理"的过滤结果。 + +### 非法取值处理 + +`requirementKind` 非 `TRAVEL`/`TRANSFER` 且非空 → `code=100001`,报文格式固定为 `参数非法: requirementKind 仅支持 TRAVEL/TRANSFER,传入非法值:<原始传入值>`,HTTP 状态行仍是 200,判据在 body。 + +### 两接口需同步传参 + +`GET /admin/fleet/board/orders` 与 `GET /admin/fleet/board/summary` 共用同一入参 VO(`BoardOrderPageReqVO`)。切换需求类别页签时必须把 `requirementKind` 同时传给两个接口,否则会出现「列表 10 条、状态页签写着 47 条」这类界面对不上的情况。 + +--- + +## 五、数据库行为 + +本次变更的两个接口都是只读 `GET`,**零数据库写入**,不产生任何落库副作用。`requirementKind` 只影响查询结果的过滤范围,不改写任何行。 + +--- + +## 六、边界行为 + +- 真实卡与虚拟待派卡(`virtualPending=true`)在 `requirementKind`/`requirementKindLabel` 两个新字段上**一律非空**——实测 47 条里 10 条虚拟待派卡两字段全部有值。 +- 纯接送机订单(没有 active 行程用车需求)如实返 `TRANSFER`,不受「顶层 `requirementId` 恒指 `TRAVEL`」那条既有契约影响。 +- 需求类别取自卡片自身归属的那条需求,不读订单级单值字段——订单级单值字段恒取身份列表首项(两类并存时是 `TRAVEL`)。 +- order-v3 整体不可达、取不到需求身份时,`requirementKind` 回退 `TRAVEL`,与该场景下特殊诉求/备注回退快照同属既有降级口径。 +- `requirementKind` 大小写敏感:只有精确的 `TRAVEL`/`TRANSFER` 合法。 +- `requirementKind` 与既有全部筛选条件(含 `orderKind`、`groupBatchId`、`statuses`、日期、车型、`consultantId`、`keyword`)都是 AND 组合。 + +--- + +## 六.5、枚举 / 数据字典 + +### requirementKind + +**所属字段**: `requirementKind`(两个接口共用的查询参数,同名出现在列表响应的 `data.records[].requirementKind`) | **类型**: `String` + +| 值 | 中文标签(`requirementKindLabel`) | 说明 | +|----|-----------------------------------|------| +| `TRAVEL` | 行程用车 | 常规行程用车需求 | +| `TRANSFER` | 接送机 | 接送机用车需求 | + +不传、传空串 = 不过滤,两类都返。其余任何取值(含大小写不符)返 `100001`。 + +--- + +## 六.6、修改前后对比 + +### 字段级对比 + +| 字段 | 改前 | 改后 | +|------|------|------| +| `requirementKind`(两个接口的 query) | 不存在,传了被忽略 | 可选参数,`TRAVEL`/`TRANSFER`,缺省不过滤,非法值返 `100001` | +| `data.records[].requirementKind`(列表响应) | 不存在 | 🆕 新增字段,恒非空,取该卡片自身归属需求的类别 | +| `data.records[].requirementKindLabel`(列表响应) | 不存在 | 🆕 新增字段,中文标签,与 `requirementKind` 恒成对非空 | +| `statusCounts` / `statusOptions[].count`(汇总响应) | 不随需求类别过滤 | 随 `requirementKind` 一起收窄 | + +### 行为级对比 + +| 行为 | 改前 | 改后 | +|------|------|------| +| 同订单行程用车+接送机并存 | 两张卡逐字段相同,前端无法分辨哪张是接送机 | 两张卡各自携带 `requirementKind`/`requirementKindLabel`,可据此区分 | +| 按需求类别筛选看板 | 不支持 | 支持 `requirementKind=TRAVEL`/`TRANSFER` 直接筛 | +| 传了不识别的 `requirementKind` | 参数不存在,被忽略 | 返 `code=100001`,不静默放行 | + +--- + +## 六.7、影响评估 + +- **是否破坏向后兼容**: 否。不传 `requirementKind` 的旧调用行为与改前完全一致(不过滤,两类都返),响应只新增字段,不删改任何既有字段。 +- **前端是否必须同步上线**: 视需求而定——若要解决「同订单两张卡无法区分接送机」这个问题,需要前端读取新字段渲染区分,或使用新参数筛选;不读取新字段时界面行为与改动前完全一致,不会报错。 +- **前端 workaround 清理点**: 此前前端没有任何字段可用于区分两类需求;若曾用行程备注、行程日期或别的间接线索猜测哪张卡是接送机,可以改用 `requirementKind` 精确判断。 + +--- + +## 七、不影响范围 + +- **仅影响**: `GET /admin/fleet/board/orders` 与 `GET /admin/fleet/board/summary` 两个读口。 +- **零影响**: + - `orderKind` 维度及其既有筛选行为(本次未改动该维度任何逻辑); + - 派车、改派、取消等所有写口(本次改动只涉及看板列表与汇总两个读口); + - `idleVehicleCount` / `idleDriverCount` 全局物理资源指标; + - 小程序端全部接口(派单看板为管理后台专属能力)。 + +--- + +## 八、测试环境已验证 + +测试服 hl-fleet-service 已部署 `dfb5db832`(2026-09-29 17:48:30)。窗口 `orderKind=ALL&pageSize=100` 实测: + +``` +GET /admin/fleet/board/orders?orderKind=ALL&pageSize=100 → 200,基线 47 条 + requirementKind 分布 {TRAVEL: 37, TRANSFER: 10} + requirementKindLabel 分布 {行程用车: 37, 接送机: 10} + 两字段 47 条全部非空(含 10 条虚拟待派卡) +GET /admin/fleet/board/orders?orderKind=ALL&pageSize=100&requirementKind=TRAVEL → 200,返回 37 条,全为 TRAVEL +GET /admin/fleet/board/orders?orderKind=ALL&pageSize=100&requirementKind=TRANSFER → 200,返回 10 条,全为 TRANSFER + 37 + 10 = 47(两档相加等于基线,requirementId 集合与基线完全一致) +GET /admin/fleet/board/orders?orderKind=ALL&pageSize=100&requirementKind= → 200,返回 47 条,与不传一致 +GET /admin/fleet/board/orders?orderKind=ALL&pageSize=100&requirementKind=BOGUS → 200 + code 100001 +GET /admin/fleet/board/summary?orderKind=ALL → 六个状态桶合计 47 +GET /admin/fleet/board/summary?orderKind=ALL&requirementKind=TRAVEL → 六个状态桶合计 37 +GET /admin/fleet/board/summary?orderKind=ALL&requirementKind=TRANSFER → 六个状态桶合计 10 + 三档与列表 total 逐一对齐(47/37/10) +``` + +同一订单出两张卡(TRAVEL + TRANSFER 并存)的订单实测 4 个,例如 `2101566624467419137`、`2101146798373339137`。 + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#8518](https://git.1814.love/wx/HL/issues/8518) +- 关联 PR: [wx/HL#8534](https://git.1814.love/wx/HL/pulls/8534) + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#8518](https://git.1814.love/wx/HL/issues/8518) +- **PR**: [#8534](https://git.1814.love/wx/HL/pulls/8534) + +### 联系人 + +- **后端责任人**: wx(GIT) +- **问题反馈**: wx/HL Issue #8518 评论区