--- schema: "hl-changelog/v2" ticket: "8593" title: "待配车团期清单 transferPendingCount 由硬编码 0 改为真值,取不到给 null" 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 /admin/fleet/group-dispatch/pending-batches 响应体每行新增真值语义:records[].transferPendingCount 此前恒为硬编码 0,本次改为按团期实际接送机声明缺口计算的真值,且新增 null 语义——取不到时返回 null 而不是 0,前端必须把 null 渲染成未知态(如「—」),不得折算成 0;0 表示查过了确无缺口,null 表示本团有没有缺口未知。该字段与团期配车总览端点(GET /admin/fleet/group-dispatch/batches/{groupBatchId}/overview)的 transferPendingTotal 走同一判定方法,服务端保证两者恒等。声明数据经内部 Feign 端点(order-v3 提供,仅供服务间调用,非管理后台直接可调)按页批量整取,不做逐团 N+1 请求;该内部读口不可达时不会静默返回空列表,会返回失败结果,使 transferPendingCount 整页退化为 null,不影响该页其余字段(包括 unreadCount,是另一个独立软依赖,user-service 不可达时退化为 0)。字段类型未变(仍是 Integer),仅新增 null 作为合法取值;若前端此前对该字段做过兜底成 0 或完全未渲染,需要补上 null 分支与展示逻辑。清单本身的分页/过滤/排序、其余字段与错误码(600012/600013/401)均未变化。backend_status=deployed:hl-fleet-service 已部署测试服并与本条目所依据的源码提交一致(deploy-status.sh 实测 COMMIT=d57498d38,STATE=ok,2026-09-30 05:34:07 部署);该路由已实测可达(未登录态返 200 信封 code=401)。" updated_at: "2026-09-30" base: "dev-v3" --- # 车务团期配车:待配车团期清单接送机未配计数改为真值 > **存放目录**: 二期(order-v3/fleet)→ `changelogs-v2/2026-09/` > > **服务**: hl-fleet-service(端口 8087) > **PR**: [#8617](https://git.1814.love:8443/wx/HL/pulls/8617) > **Issue**: [#8593](https://git.1814.love:8443/wx/HL/issues/8593) > **日期**: 2026-09-30 > **影响范围**: 管理后台「待配车团期清单」列表页的 `transferPendingCount` 一列 --- ## ⚠️ 关键变化 「待配车团期清单」(`pending-batches`)每行的 `transferPendingCount` 字段,此前**恒为硬编码 0**,不反映任何真实数据——前端如果曾据此判断「所有团都没有接送机缺口」,这个判断从一开始就是假的。本次改为**真实计算值**,并引入 **`null` 语义**:取不到声明数据时返回 `null` 而不是 `0`。**`0` 与 `null` 含义不同,不能互相折算**:`0` = 查过了、确无接送机缺口;`null` = 这一刻没查到、本团有没有缺口未知。前端如果沿用旧的「反正恒为 0,不用管」的假设,现在会看到非零真值和偶发 `null`,必须补上渲染逻辑。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 待配车团期清单 | GET | `/admin/fleet/group-dispatch/pending-batches` | 修改接口 | `transferPendingCount` 由硬编码 0 改为真值+null 语义 | --- ## 三、接口详情 ### 1. 待配车团期清单 `GET /admin/fleet/group-dispatch/pending-batches` **VO**: `GroupDispatchPendingBatchPageReqVO → PageResult` #### 使用场景 车务在「团期配车」列表页查看尚未完成配车(`requirementConfirmed=true` 且 `vehicleReady=false`)的团期,支持按出发日区间、团号/团名关键词、配车进度过滤。本次改动只影响列表行里的 `transferPendingCount` 一列,接口路径、分页参数、其余字段均未变化。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | departDateFrom | Query | LocalDate(ISO,`yyyy-MM-dd`) | - | 不传=不限 | 出发日下界(含) | | departDateTo | Query | LocalDate(ISO,`yyyy-MM-dd`) | - | 不传=不限 | 出发日上界(含) | | keyword | Query | String | - | ≤50 字符 | 团号/团名模糊关键词 | | dispatchProgress | Query | String | - | 仅 `NOT_STARTED`/`PARTIAL`/`FULL` | 配车进度过滤(fleet 侧内存过滤,先分页后过滤) | | page | Query | Integer | - | ≥1,默认 1 | 页码 | | pageSize | Query | Integer | - | 1-100,默认 20 | 每页条数 | #### 出参 `Result>` | 字段 | 类型 | 说明 | |------|------|------| | records | Array | 团期行列表,见下 | | records[].groupBatchId | String(Long 转字符串) | 团期主订单 ID | | records[].batchNo | String | 团号 | | records[].batchName | String | 团名 | | records[].batchStatus | String | 团期状态 | | 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[].**transferPendingCount** | Integer(可空) | **本次变更字段**:本团接送机未配计数;`null`=未取到(前端须渲染未知态),`0`=确无缺口 | | records[].unreadCount | Integer | 团期车务会话团队未读数(软依赖,取不到退 0) | | total | Integer | 总记录数(`dispatchProgress` 过滤前) | | page | Integer | 当前页码 | | pageSize | Integer | 每页条数 | #### 请求示例 ```http GET /admin/fleet/group-dispatch/pending-batches?departDateFrom=2026-09-01&departDateTo=2026-09-30&keyword=T26-8867&dispatchProgress=PARTIAL&page=1&pageSize=20 ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "records": [ { "groupBatchId": "1934567890123456789", "batchNo": "T26-8867", "batchName": "额吉的故乡 9/12 团", "batchStatus": "RESOURCE_PREPARING", "departDate": "2026-09-12", "endDate": "2026-09-16", "serviceDayCount": 5, "enrolledOrders": 6, "enrolledPeople": 17, "requirementConfirmed": true, "vehicleReady": false, "dispatchedDayCount": 2, "dispatchProgress": "PARTIAL", "transferPendingCount": 2, "unreadCount": 3 } ], "total": 1, "page": 1, "pageSize": 20 }, "success": true } ``` #### 空数据 / 降级响应 当没有满足过滤条件的团期时,返回 `records: [], total: 0`(HTTP 200,非错误)。当 order-v3 的接送机声明批量读口不可达时,**本页所有行的 `transferPendingCount` 一律返回 `null`**(不是 0,也不会让整个请求失败)——前端必须把 `transferPendingCount=null` 渲染成未知态(如「—」),不能当作「确认无缺口」折算成 0。`unreadCount` 是另一个独立的软依赖:user-service 不可达时退化为 0,清单其余字段照常返回,不受影响。 #### 错误响应 ```json { "code": 600013, "message": "参数非法: 页码必须≥1", "success": false, "data": null } ``` 其余可能返回的错误码: | code | 触发条件 | message | |---|---|---| | 600012 | order-v3 团期候选基线不可达(降级/返错),**不会静默返空列表** | `团期配车基线不可达,请稍后重试` | | 600013 | 日期区间倒置、分页越界、关键词超长(>50 字)、`dispatchProgress` 枚举非法 | `参数非法: {具体原因}` | | 401 | 未登录(网关统一信封,HTTP 状态码仍是 200,信封内 `code=401`) | `缺少有效的 Authorization 头` | #### 业务边界 - `dispatchProgress` 是 fleet 侧内存过滤(order-v3 侧没有配车事实,无法下推),是「先分页再过滤」——单页返回条数可能少于 `pageSize`,`total` 是过滤**前**的总数。 - `transferPendingCount` 与团期配车总览端点(`GET /admin/fleet/group-dispatch/batches/{groupBatchId}/overview`)的 `transferPendingTotal` 走**同一个判定方法**,服务端保证两者恒等——清单页与详情页的这个数字不会对不上。 - 声明数据经内部批量读口整页一次取齐(每页最多按团期数一次 Feign 调用),不做逐团 N+1 请求。 - `transferPendingCount` 的 `null` 与 `unreadCount` 的「退 0」是两种不同的降级策略,分别对应各自读口的可靠性设计,不要混用同一套判空逻辑处理。 - 主候选数据(团期本身)取不到时整个请求失败关闭(600012),不会把「后端没拿到」渲染成「该团没有需求」;这与 `transferPendingCount` 单列退化为 `null`(其余字段正常返回)是两个不同粒度的降级,不要合并处理。 --- ## 四、契约约束与正确调用方式 ### 正确渲染 `transferPendingCount` 的方式 | 取值 | 含义 | 渲染建议 | |------|------|----------| | `0` | 查过了,确无接送机缺口 | 正常展示 `0` | | 正整数 | 查过了,有对应数量的缺口 | 正常展示数值,可高亮提醒 | | `null` | 本次没有取到该团的声明数据,缺口未知 | 渲染成未知态(如「—」),**不要**当作 `0` | ❌ 错误用法:`transferPendingCount ?? 0` 或任何把 `null` 静默折算成 `0` 的写法——这会把「未知」误报成「已确认无缺口」,反而比改动前的硬编码 0 更危险(因为界面上看起来像是「查过了」)。 --- ## 六、边界行为 - 未登录 → 网关统一信封 `code=401`(HTTP 状态码 200,非 HTTP 401) - 无匹配团期 → `records: [], total: 0`,HTTP 200 - `dispatchProgress` 过滤导致单页为空 → `records: []`,但 `total` 仍是过滤前总数,不为 0 - order-v3 团期候选基线不可达 → 600012,整个请求失败,不返回部分数据 - order-v3 接送机声明批量读口不可达 → 请求仍然成功,仅 `transferPendingCount` 整页退化为 `null` - user-service 不可达 → 请求仍然成功,仅 `unreadCount` 退化为 `0` --- ## 六.5、枚举 / 数据字典 ### dispatchProgress(配车进度) **所属字段**: `records[].dispatchProgress`(`GroupDispatchPendingBatchRespVO`),同名字段也用于入参过滤 | **类型**: `String` | 值 | 中文 | 说明 | |----|------|------| | `NOT_STARTED` | 未开始 | 已排车天数为 0 | | `PARTIAL` | 部分配车 | 已排车天数大于 0 但未盖满全部服务日 | | `FULL` | 已配齐 | 已排车天数盖满全部权威服务日 | --- ## 六.6、修改前后对比 ### 字段级对比 | 字段 | 改前 | 改后 | |------|------|------| | `transferPendingCount` | 恒为 `0`(硬编码占位符,从未反映真实缺口) | 真实计算值;取不到声明数据时为 `null`(不是 `0`) | ### 行为级对比 | 行为 | 改前 | 改后 | |------|------|------| | 声明数据获取方式 | 未获取,字段硬编码为 0 | 按页批量调用 order-v3 内部读口一次取齐(非逐团 N+1),取不到时该列整体退化为 `null` | | 与 overview 端点的一致性 | 无法比较(清单侧恒 0,overview 侧是真值,两者结构性不可能相等) | 清单与 overview 走同一判定方法,服务端保证恒等 | | 字段类型 | `Integer`,实际恒非空 | `Integer`,新增合法取值 `null` | ## 六.7、影响评估 - **是否破坏向后兼容**: 否——字段名与类型(`Integer`)未变,只是语义从「恒定占位符」变为「真实业务值 + 可空」。原本恒为 0 意味着这个字段此前对使用方没有任何信息量,语义上不存在"旧行为被依赖"的合理场景。 - **前端是否必须同步上线**: 是——如果前端此前完全没有渲染这个字段(因为它恒为 0、没有展示价值),现在需要补充展示逻辑,包括 `null` 的未知态处理;如果前端此前渲染了这个字段但做了 `?? 0` 之类的兜底,需要去掉这个兜底、改为区分 `0` 与 `null`。 - **前端 workaround 清理点**: 若前端此前因为「这个字段没用、永远是 0」而完全跳过读取或做了防御性兜底,需要重新接入并按上方「正确渲染方式」处理;无其它 workaround。 --- ## 七、不影响范围 - **仅影响**: 「待配车团期清单」列表每行的 `transferPendingCount` 字段 - **零影响**: - 清单接口的分页参数、过滤参数(`departDateFrom`/`departDateTo`/`keyword`/`dispatchProgress`)语义 - `records[]` 内除 `transferPendingCount` 外的其余字段 - `unreadCount` 字段的取值逻辑(软依赖降级策略本身未变) - 团期配车总览端点 `GET /admin/fleet/group-dispatch/batches/{groupBatchId}/overview` 的路径、参数、响应结构(其 `transferPendingTotal` 此前就已经是真值,本次不涉及该端点改动,只是清单侧现在与它口径一致) - 错误码 600012/600013/401 的触发条件与数值 --- ## 八、测试环境已验证 - `deploy-status.sh`(测试服现状表)实测:`hl-fleet-service` COMMIT=`d57498d38`、STATE=ok,2026-09-30 05:34:07 部署——与本条目所依据的源码提交(含 #8593 合并提交 `d57498d381`)一致。 - 测试服内网 `curl` 实测路由已挂载且鉴权前置生效: ``` GET http://127.0.0.1:8080/admin/fleet/group-dispatch/pending-batches?page=1&pageSize=1 (无 Authorization 头) → HTTP 200 {"code":401,"message":"缺少有效的 Authorization 头","data":null,"traceId":"e0e9988b33394199","success":false} ``` --- ## 十、相关文档 - 关联 Issue: [wx/HL#8593](https://git.1814.love:8443/wx/HL/issues/8593) - 关联 PR: [wx/HL#8617](https://git.1814.love:8443/wx/HL/pulls/8617) ## 关联 / 联系人 ### 链接 - **Issue**: [#8593](https://git.1814.love:8443/wx/HL/issues/8593) - **PR**: [#8617](https://git.1814.love:8443/wx/HL/pulls/8617) - **Merge commit**: [`d57498d381`](https://git.1814.love:8443/wx/HL/commit/d57498d38138fd37ce7e844b6f2b01c190706950) ### 联系人 - **后端负责人**: @wx