From e813ea774d3c856eb83174dd51d55395bff6c5be Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Sun, 20 Sep 2026 20:00:02 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20#7990=20=E5=80=99=E9=80=89?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=A7=BD=E4=BD=8D=E9=9C=80=E6=B1=82=E9=94=9A?= =?UTF-8?q?=E7=82=B9=E6=8C=89=20requirementId=20=E5=8F=8D=E6=9F=A5?= =?UTF-8?q?=EF=BC=88AC-8=20=E4=BF=AE=E5=A4=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本单名下已有一份 changelog(看板身份三元组,PR #8048),但按内容 grep 那份已推送 文件,requirementMatched / seatsEnough / requirementMismatchReasonCode / 候选 / 座位 六个关键词全部 0 命中——AC-8 的候选面行为变化一个字都没有。 这正是 #7443 被重开的形态:按单号数文件全绿,按内容查全空。所以另写一份, 而不是让那份顶数。 行为变化(POST /admin/fleet/assignments/candidates): TRANSFER-only 订单上,seatsEnough / requirementMatched / requirementMismatchReasonCode 由「恒空/退化」变为按 TRANSFER 需求真实判定。 修复前 AssignmentCandidateService.resolveSlotRequirement 的车型/座位锚点恒取 TRAVEL 需求:两类并存时接送机槽锚到 TRAVEL 需求的第 fleetItemIndex 项;TRANSFER-only 单上 取到 null、锚点整块退空、座位判据回退到整单 headcount,于是 5 座 SUV 反而「够」, 只剩车型一条提示(少报一半)。 两种失败出口都是 SlotRequirement.empty() 这条「仅提示不限制」的正常路径——不抛错、 不打日志。车务只看到提示不对,没有任何信号能顺藤摸瓜。这条静默属性已写进正文。 网关实测(2026-09-20 22:0x,同一次请求两辆车判定截然相反): 7 座 SUV 蒙A-U1557 → seatsEnough=true / requirementMatched=true / reasonCode=null; 5 座 SUV 蒙P321A → seatsEnough=false / requirementMatched=false / reasonCode=SEATS。 修复前两车都会是 requirementMatched=null。这组阴性对照排除了「返回一堆候选但根本 没过滤」这个竞争解释。 取证前先补了部署缺口:开测时 fleet 停在 c35251b07,而 bf4fba5a2(#8062) 触及 fleet 却未部署,重新部署到 0/N 才开测,否则会测到旧字节。 请求契约完全不变:修法是按 requirementId 匹配式反查 kind,不是给请求补 kind 位。 Co-Authored-By: Claude Opus 5 (1M context) --- ...±‚锚点按requirementId反查修复-修改接口-管理后台.md | 331 ++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 changelogs-v2/2026-09/20_7990_候选资源槽位需求锚点按requirementId反查修复-修改接口-管理后台.md diff --git a/changelogs-v2/2026-09/20_7990_候选资源槽位需求锚点按requirementId反查修复-修改接口-管理后台.md b/changelogs-v2/2026-09/20_7990_候选资源槽位需求锚点按requirementId反查修复-修改接口-管理后台.md new file mode 100644 index 00000000..e415f17d --- /dev/null +++ b/changelogs-v2/2026-09/20_7990_候选资源槽位需求锚点按requirementId反查修复-修改接口-管理后台.md @@ -0,0 +1,331 @@ +--- +schema: "hl-changelog/v2" +ticket: "7990" +title: "候选资源槽位需求锚点按 requirementId 反查需求类别,TRANSFER-only 订单座位/车型判定不再静默退化" +consumer: "admin" +author: "wx(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "mmg" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "PR #8062(squash bf4fba5a2)已合入 dev-v3(origin/dev-v3 祖先关系已核,本地 worktree HEAD 落后但不影响判断);fleet 已部署 TEST,deploy-status.sh 复核 0/N。2026-09-20 22:0x 经网关实测同一次候选查询里两辆车判定截然相反(蒙A-U1557 7座 seatsEnough=true/requirementMatched=true;蒙P321A 5座同车型 seatsEnough=false/requirementMatched=false/reasonCode=SEATS),坐实修复对 TRANSFER-only 订单确有生效。本条与本目录已推送的 20_7990_车务看板订单详情按需求各返一组身份三元组-修改接口-管理后台.md(PR #8048)是#7990 下两个独立 AC:那份讲看板详情接口新增 requirementIdentities 身份三元组,按 requirementMatched/seatsEnough/requirementMismatchReasonCode/候选/座位 六词逐一核对全部 0 命中,未覆盖本条候选面座位/车型判定行为,故另立本文档,不是重复推送。" +updated_at: "2026-09-20" +base: "dev-v3" +--- + +# fleet: 候选资源槽位需求锚点按 requirementId 反查需求类别(TRANSFER-only 订单座位/车型判定修复) + +> **存放目录**: 二期(v3) → `changelogs-v2/{YYYY-MM}/` +> +> **服务**: hl-fleet-service (端口 8009) +> **PR**: [#8062](https://git.1814.love:8443/wx/HL/pulls/8062) +> **Issue**: [#7990](https://git.1814.love:8443/wx/HL/issues/7990) AC-8 +> **日期**: 2026-09-20 +> **影响范围**: 管理后台车务派单弹窗「查询派单候选资源」——候选车辆列表的车型/座位匹配提示(`seatsEnough` / `requirementMatched` / `requirementMismatchReasonCode` / `requirementMismatchMessage` 四字段),**仅当订单挂有 TRANSFER(接送机)需求时**取值受影响 + +--- + +## ⚠️ 关键变化 + +**这不是新字段,是既有字段在特定订单形态下的取值口径修复**:`seatsEnough` / `requirementMatched` / `requirementMismatchReasonCode` / `requirementMismatchMessage` 四个字段本身早已存在(#5871/#5788),本次改的是它们内部用来比对的「槽位需求锚点」在**接送机相关订单**上取错了对象。 + +- **两类需求并存的订单**(同一订单既有行程用车又有接送机):接送机派车/改派时,候选面锚点此前恒取**行程用车**那条需求的对应车型项,合适的接送机用车会被误标「需求不匹配」。 +- **只有接送机需求的订单(TRANSFER-only)**:此前锚点直接取不到值,整块退化成 `SlotRequirement.empty()` 这条「仅提示不限制」的正常路径——**不抛错、不打日志**,座位判据悄悄回退到整单 `headcount`,车务只会看到提示文案不对,没有任何信号可循。 +- 本目录已推送的 `20_7990_车务看板订单详情按需求各返一组身份三元组-修改接口-管理后台.md`(PR #8048)讲的是同一工单下**另一个 AC**(看板详情接口新增 `requirementIdentities`),按 `requirementMatched`/`seatsEnough`/`requirementMismatchReasonCode`/候选/座位六个关键词逐一核对,那份正文**全部 0 命中**——它没有覆盖、也不能替代本条修复的说明。 + +--- + +## 一、背景 + +同一订单可以同时挂 TRAVEL(行程用车)与 TRANSFER(接送机)两条活跃需求(#7443 起的既有形态)。候选弹窗查询车辆/司机时,会按请求携带的 `fleetItemIndex` 在「当前生效需求」的 `fleet[]` 展开序列里定位一项,取它的车型与座位作为本槽位的判定锚点(`resolveSlotRequirement`,#5871)。 + +缺陷在于:这一步此前调用的是 `OrderQueryFacade.getCurrentVehicleRequirementOrNull(orderId)`——**不带 kind 参数的重载**,而 order-v3 侧对应 Feign 端点的 `kind` 默认值就是 `TRAVEL`。也就是说"不传 kind"在这里不是"不限类别",而是**钉死取 TRAVEL**: + +| 订单形态 | 锚点实际取到的需求 | 后果 | +|---|---|---| +| TRAVEL + TRANSFER 并存 | 恒为 TRAVEL 那条 | 接送机槽位锚到另一条需求的车型/座位,合适的车被误判「需求不匹配」 | +| 仅 TRANSFER(TRANSFER-only) | `null`(该订单没有 TRAVEL 需求) | 锚点整块退空,座位判据回退到整单 `headcount`,车型判据回退前端传参 | + +两种失败出口都是 `SlotRequirement.empty()`,这是这个方法「需求不可达时仅提示不限制」的**既有正常降级路径**——不抛错、不打日志。车务侧能观察到的只是候选面提示与实际情况对不上,没有任何错误码或日志可以顺藤摸瓜。 + +**修法**:改用 `RequirementKindResolver.resolveForMutationByRequirementId(orderId, requirementId, travelSupplier)` 做**匹配式反查**——请求本身已带 `requirementId`(改派场景必填),先用它去比对 TRAVEL 的当前需求 id,命中即用(TRAVEL 槽位逐字不变,且不产生任何额外远程读);不命中再去只读探测 TRANSFER 的当前需求 id 是否与之相等,相等才认;两边都对不上则原样退回 TRAVEL 分支的结果,与改动之前完全一致。**没有新增请求参数**,前端契约不变。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 查询派单候选资源 | POST | `/admin/fleet/assignments/candidates` | 修改接口 | TRANSFER 相关订单上 `seatsEnough`/`requirementMatched`/`requirementMismatchReasonCode`/`requirementMismatchMessage` 四字段的判定锚点由「恒取 TRAVEL/退空」改为「按 requirementId 匹配式反查真实 kind」 | + +--- + +## 三、接口详情 + +### 1. 查询派单候选资源 `POST /admin/fleet/assignments/candidates` + +**VO**: `AssignmentCandidateReqVO → AssignmentCandidateRespVO` + +#### 使用场景 + +车务派单弹窗 Step 2「选车 / 选司机」列表,接送机(TRANSFER)派车或改派时同样调用本端点。前端据候选项的 `seatsEnough` / `requirementMatched` / `requirementMismatchReasonCode` / `requirementMismatchMessage` 渲染「这辆车是否满足本槽位需求」的提示——**这四个字段只是提示,不限制能不能选车**(`requirementMatched=false` 时 `available`/`selectable` 不受影响)。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| orderId | body | number(string) | 改派/接送机排除自身时必填 | 雪花 ID | 当前订单 ID;**本次判定锚点新依赖它与 requirementId 联用** | +| requirementId | body | number(string) | 改派排除自身时必填 | 雪花 ID | 当前用车需求 ID;**本次起是反查需求 kind 的唯一依据**(不匹配任何 kind 的当前需求时退回 TRAVEL 分支,逐字兼容旧行为) | +| fleetItemIndex | body | integer | 否 | `@Deprecated`,仅用于回退推断车型/座位提示,不参与归属校验 | 需求车型项展开序号;反查到的需求 `fleet[]` 按 `count` 展开后,本序号落在哪项区间决定锚定到哪一项的车型/座位 | +| headcount | body | integer | 否 | ≥0 | 乘客人数;**槽位需求不可达时**座位判据回退到本字段(本次修复前 TRANSFER-only 订单必走这条回退,修复后仅在两类需求都反查不到时才走) | +| startDate / endDate | body | string(date) | 是 | `yyyy-MM-dd` | 本次派车服务起止日 | +| requiredVehicleType | body | string | 否 | — | 前端传参车型;仅用于槽位需求不可达时回退,**槽位锚点解析成功时会压过它**(既有语义,本次未变) | + +> 其余字段(`excludeAssignmentId`/`assignmentGroupId`/`vehicleKeyword`/分页参数等)本次**未作任何改动**,不重复列出。**请求体没有新增任何字段**——修复完全发生在服务端内部的需求反查逻辑。 + +#### 出参 + +| 字段 | 类型 | 说明 | +|------|------|------| +| data.vehicles.records[].seatsEnough | boolean | **【本次取值口径修复】** 可载客数是否满足本槽位座位需求;有槽位需求时按「车辆座位数 ≥ 槽位要求座位数」判,无槽位需求(锚点不可达)时回退整单 `headcount` | +| data.vehicles.records[].requirementMatched | boolean | **【本次取值口径修复】** 车型和座位是否同时符合本槽位需求;`false` 仅提示,不影响 `available`/`selectable` | +| data.vehicles.records[].requirementMismatchReasonCode | string | **【本次取值口径修复】** 不匹配的具体维度,枚举 `VEHICLE_TYPE` / `SEATS` / `VEHICLE_TYPE_AND_SEATS`;`requirementMatched=true` 或槽位需求彻底不可达时为 `null`(详见「六.5」) | +| data.vehicles.records[].requirementMismatchMessage | string | **【本次取值口径修复】** 人读文案,以「本槽位需求为 XX」为主语;匹配时为 `null` | +| data.vehicles.records[].vehicleTypeKey / seats | string / integer | 车辆自身车型大类 key、核定座位数(未变,仅作为对照参与前面四字段的计算) | + +> 其余字段(`vehicleId`/`plate`/`available`/`selectable`/`conflicts[]`/司机候选等)本次一律未变,不重复列出。 + +#### 请求示例 + +TRANSFER-only 订单(该单无行程用车需求,只有一条接送机需求)查询候选车辆: + +```json +{ + "orderId": "2101580000000000001", + "requirementId": "2101580000000000009", + "fleetItemIndex": 0, + "startDate": "2026-09-25", + "endDate": "2026-09-25", + "headcount": 4, + "vehiclePage": 1, + "vehiclePageSize": 20 +} +``` + +#### 响应示例 + +2026-09-20 22:0x 经网关对同一 TRANSFER-only 订单的同一次候选查询,两辆车判定截然相反(车牌、座位数、`seatsEnough`/`requirementMatched`/`requirementMismatchReasonCode` 三字段取自当次实测;`vehicleId`/`orderId`/`requirementId` 未在交接材料中给出具体数值,此处用示意 ID,不代表真实雪花 ID): + +```json +{ + "code": 200, + "message": "成功", + "data": { + "vehicles": { + "total": 2, + "records": [ + { + "vehicleId": "2101590000000000101", + "plate": "蒙A-U1557", + "vehicleTypeKey": "suv", + "vehicleTypeName": "SUV", + "seats": 7, + "passengerCapacity": 6, + "seatsEnough": true, + "requirementMatched": true, + "requirementMismatchReasonCode": null, + "requirementMismatchMessage": null, + "available": true, + "selectable": true + }, + { + "vehicleId": "2101590000000000102", + "plate": "蒙P321A", + "vehicleTypeKey": "suv", + "vehicleTypeName": "SUV", + "seats": 5, + "passengerCapacity": 4, + "seatsEnough": false, + "requirementMatched": false, + "requirementMismatchReasonCode": "SEATS", + "requirementMismatchMessage": "本槽位需求座位数为7,该车核定座位不足", + "available": true, + "selectable": true + } + ] + } + }, + "success": true +} +``` + +**修复前**这两辆车的 `requirementMatched` 都会是 `null`(锚点退空、仅提示不限制),看不出蒙P321A 座位不足。 + +#### 空数据 / 降级响应 + +- 订单 `requirementId` 对应的需求在 TRAVEL、TRANSFER 两个 kind 上都反查不到(如需求已被撤销)→ 原样退回 TRAVEL 分支取到的值(`null` 时锚点整块为空)——**与本次改动之前完全一致**的降级路径,`seatsEnough` 回退整单 `headcount`,`requirementMatched`/`requirementMismatchReasonCode`/`requirementMismatchMessage` 均为 `null`。 +- `orderId` 或 `requirementId` 任一为空 → 不触发反查,直接走既有 TRAVEL 分支(未变)。 +- 候选车辆本身为空 → `records` 为空数组、`total=0`,恒 `code=200`。 + +#### 错误响应 + +本端点是只读咨询,业务上恒 `code=200`,匹配结果在 `data` 体现,不会因为需求反查失败而报错。仅入参非法时报错,形态未变: + +```json +{ + "code": 605010, + "message": "排除派单不属于当前订单", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- 只读,不写任何业务数据;反查是「匹配式」的,只读一次 TRANSFER 需求(TRAVEL 先命中即返回,不发生),不产生任何额外写操作。 +- `requirementMatched=false` **只是提示**,不影响 `available`/`selectable`,最终一致仍以创建/改派锁内重校验为准。 +- `fleetItemIndex` 已 `@Deprecated`,但候选面座位/车型回退推断仍在读它;新前端应尽快切到 `assignmentGroupId` 归属校验(#7067),本字段随其全量切换后一并删除。 +- 换版保留行(`supersededByRequirementId` 非空)与未派车占位行(`UNASSIGNED`)走的是「锚行自身快照」分支,不经过本次改动涉及的反查逻辑(既有行为,未变)。 + +--- + +## 四、契约约束与正确调用方式 + +> 本节只写后端接受/拒绝 payload 的规则,不写 UI 渲染建议。 + +### 4.1 反查依据 `requirementId`,不是新增 `kind` 参数 + +**不要给请求体加 `kind` 字段去"帮忙"指定需求类别**——后端不认,加了也不会生效。判定逻辑完全依赖已有的 `requirementId`:它唯一确定一条需求行,服务端据此反查其归属的 TRAVEL/TRANSFER 类别,前端无需也不应该猜测或传递类别信息。 + +### 4.2 改派/接送机场景 `requirementId` 必须是当前有效需求的 ID + +反查是**逐字匹配**的:`requirementId` 必须与某个 kind 的**当前生效**需求 id 完全相等才会命中该 kind 的槽位锚点。传一个已过期/已换版的旧 `requirementId`,反查两边都不命中,原样退回 TRAVEL 分支的结果(可能是 `null` 锚点、也可能是另一条 TRAVEL 需求的值)——**不会报错**,前端界面上看到的是「提示不准」而不是接口失败,联调时如遇候选面提示怪异,先核对传的 `requirementId` 是否为当前有效需求。 + +### ✅ 正确 / ❌ 错误 payload 对照 + +| 场景 | payload | 结果 | +|------|---------|------| +| ✅ TRANSFER 改派,带当前有效需求 ID | `{ "orderId": "...", "requirementId": "<当前 TRANSFER 需求 id>", "fleetItemIndex": 0 }` | 锚点反查命中 TRANSFER,座位/车型判定按接送机需求算 | +| ✅ TRAVEL 派车,带当前有效需求 ID | `{ "orderId": "...", "requirementId": "<当前 TRAVEL 需求 id>", "fleetItemIndex": 0 }` | TRAVEL 先命中即返回,逐字与改动前一致,无额外远程读 | +| ❌ 给请求体加 `kind` 字段 | `{ "orderId": "...", "requirementId": "...", "kind": "TRANSFER" }` | `kind` 字段被后端忽略(`AssignmentCandidateReqVO` 无此字段),判定仍按 `requirementId` 反查 | +| ⚠️ 传过期的旧 `requirementId` | `{ "orderId": "...", "requirementId": "<已换版旧 id>" }` | 两个 kind 都反查不到,原样退回 TRAVEL 分支(可能是空锚点),不报错 | + +--- + +## 五、数据库行为 + +**无任何数据库写入。** 本端点是只读咨询: + +- 反查 TRANSFER 需求走的是 Feign 只读端点(`OrderQueryFacade.getCurrentVehicleRequirementOrNull`),不落库、不写 outbox。 +- TRAVEL 分支命中时**不发生**这次远程读(先命中即返回)。 +- 无建表、无改表、无 Flyway 脚本、无索引变更。 + +--- + +## 六、边界行为 + +1. **TRAVEL 分支优先且短路**:先比对 TRAVEL 的当前需求 id,命中即返回,不会为 TRAVEL 派车多打一次 TRANSFER 的远程读(单测 `query_travelRequirementSlot_unchangedAndNoTransferProbe` 钉住这一点)。 +2. **反查失败静默退回旧行为**:`orderId`/`requirementId` 为空,或两个 kind 都反查不到,一律原样退回 TRAVEL 分支的结果——这是「更严」不是「更松」的降级方向,不会把一条没核对过的需求放行。 +3. **`fleetItemIndex` 越界或需求 `fleet[]` 为空**:仍返回 `SlotRequirement.empty()`(既有行为未变),四个判定字段回退到「仅提示不限制」语义。 +4. **换版保留行/未派车占位行**:直接读派单行自身快照,不进入本次改动涉及的反查分支(既有行为未变)。 +5. **该反查方法本是为写路径设计**(方法名带 `ForMutation`),本次是它第 4 个调用点、也是唯一的只读调用点;它内部只做一次匹配式只读探测,不调用任何写端点、不走接送机派车的四步前置(回填/分流/GET/校验),在只读事务里复用是安全的。 + +## 六.5、枚举 / 数据字典 + +### requirementMismatchReasonCode(`AssignmentCandidateRespVO.VehicleCandidateVO.requirementMismatchReasonCode`) + +**所属字段**: `data.vehicles.records[].requirementMismatchReasonCode` | **类型**: `String` + +| 值 | 中文 | 说明 | +|----|------|------| +| `VEHICLE_TYPE` | 车型不符 | 车型大类与槽位需求不一致,但座位数满足 | +| `SEATS` | 座位不足 | 车型大类匹配,但核定座位数 < 槽位要求座位数 | +| `VEHICLE_TYPE_AND_SEATS` | 车型与座位均不符 | 两个维度都不满足 | +| `null` | 匹配 / 需求不可判定 | `requirementMatched=true` 时为 `null`;槽位需求彻底不可达(两个 kind 均反查不到且 TRAVEL 分支本身也取不到值)时同样为 `null`,此时只报 `requirementMatched=false`、不下发具体维度——**这是本次修复要减少但不能完全消除的降级形态**,仅当 `requirementId` 对应的需求确实已不存在于任何 kind 下才会触发 | + +--- + +## 六.6、修改前后对比 + +### 字段级对比 + +本次**没有新增或删除任何字段**,改的是既有四个字段在特定订单形态下的计算口径: + +| 字段 | 改前(TRANSFER 相关订单上) | 改后 | +|------|------|------| +| `seatsEnough` | 两类需求并存:按 TRAVEL 需求座位比;TRANSFER-only:回退整单 `headcount` | 按 `requirementId` 反查到的真实 kind(TRAVEL 或 TRANSFER)需求座位比 | +| `requirementMatched` | 两类需求并存:可能误判为 `false`;TRANSFER-only:恒 `null`(退化态) | 按真实 kind 需求计算,`true`/`false` 均可能,TRANSFER-only 不再恒退化 | +| `requirementMismatchReasonCode` | 两类需求并存:可能报错误维度;TRANSFER-only:恒 `null` | 按真实 kind 需求计算 | +| `requirementMismatchMessage` | 同上 | 同上 | +| TRAVEL-only 订单上以上四字段 | 未受影响 | **逐字不变**(回归对照见「八」) | + +### 行为级对比 + +| 场景 | 改前 | 改后 | +|------|------|------| +| 两类需求并存,接送机改派,槽位应为 7 座商务车 | 锚到 TRAVEL 需求(如 SUV 5 座),误标「本槽位需求为SUV5座,该车为商务车且核定座位不足」 | 锚到 TRANSFER 需求(商务车 7 座),匹配、无提示 | +| TRANSFER-only 订单,槽位需求 7 座、乘客 4 人,候选 5 座 SUV | 锚点退空,座位回退整单 `headcount(4)`,5 座「够」,只报车型不符一条提示(少报一半) | 锚到 TRANSFER 需求(7 座),报 `VEHICLE_TYPE_AND_SEATS`,车型与座位都不符 | +| TRAVEL 派车/改派(无 TRANSFER 需求共存) | 锚到 TRAVEL 需求 | **逐字不变**,且不多发一次远程读 | + +## 六.7、影响评估 + +- **是否破坏向后兼容**: 否。请求契约零变化;响应字段名与类型均未变,只是特定订单形态下取值更准确。 +- **前端是否必须同步上线**: 视既有实现而定——**若前端曾针对 TRANSFER-only 订单这几个字段"总是空/总是不可信"做过规避性渲染(如判断到恒 `null` 就不显示提示区)**,现在这些字段会按真实判定给出 `true`/`false`/具体原因码,需要改回按字段值正常渲染;若前端一直按字面语义渲染(`requirementMatched=false` 就显示 `requirementMismatchMessage`),**无需改动**,字段口径变准确只会让提示更贴合实际。 +- **前端 workaround 清理点**: 若存在"TRANSFER-only 订单不渲染候选面车型/座位提示"的特判逻辑,可以撤销。 + +--- + +## 七、不影响范围 + +- **仅影响**: 管理后台车务派单弹窗候选查询接口,且仅限订单挂有 TRANSFER 需求的场景。 +- **零影响**: + - **纯 TRAVEL 订单**(无 TRANSFER 需求共存)——四个判定字段逐字不变,有单测 `query_travelRequirementSlot_unchangedAndNoTransferProbe` 钉住,且不产生额外远程调用。 + - 候选面其余字段:`available`/`selectable`/`conflicts[]`/司机候选/价格日历/常驻关系等——一律未变。 + - `POST /admin/fleet/assignments/precheck` 预校验冲突端点——未涉及本次改动路径,未变。 + - 派单创建/改派/取消/撤销取消等**所有写接口**——本次只改了只读候选查询里的一处需求反查,不改任何写口逻辑。 + - 请求参数:无新增/删除/改名,无新增必填项。 + - 数据库:无写入、无迁移、无索引变更。 + - 小程序端——不涉及。 + +--- + +## 八、测试环境已验证 + +- **代码合入**:PR [#8062](https://git.1814.love:8443/wx/HL/pulls/8062),squash 提交 `bf4fba5a2357cf6a137105e6b175b48446ef0cc0`。已核实该提交是 `origin/dev-v3` 的祖先(`git merge-base --is-ancestor bf4fba5a2 origin/dev-v3` 为真);本地 worktree HEAD 当时落后于它,不影响该判断。 +- **部署状态**:fleet 已部署 TEST,`deploy-status.sh` 复核 `0/N`(本条由任务发起方在部署机上核实)。 +- **网关实测**(2026-09-20 22:0x,`api.test.1814.love:9443`,TRANSFER-only 订单,同一次候选查询): + - 蒙A-U1557(7 座 SUV)→ `seatsEnough=true` / `requirementMatched=true` / `requirementMismatchReasonCode=null`; + - 蒙P321A(5 座 SUV,与蒙A-U1557 同车型不同座位)→ `seatsEnough=false` / `requirementMatched=false` / `requirementMismatchReasonCode=SEATS`; + - 同一次请求里两辆车判定截然相反,坐实锚点已切换到真实生效的 TRANSFER 需求(修复前这两辆车都会是 `requirementMatched=null`)。 +- **源码层面核实**(本文档撰写时直接核对 `origin/dev-v3` 上的实现): + - `AssignmentCandidateService#resolveSlotRequirement` 已改为调用 `RequirementKindResolver#resolveForMutationByRequirementId`,不再直接调不带 kind 的 `OrderQueryFacade#getCurrentVehicleRequirementOrNull(orderId)`; + - `AssignmentCandidateServiceTest` 新增三条单测覆盖:两类需求并存锚到 TRANSFER、TRANSFER-only 订单锚点可解析、TRAVEL 场景逐字不变且不多发远程读(用例名分别为 `query_transferRequirementSlot_anchorsOnTransferRequirement`、`query_transferOnlyOrder_slotAnchorStillResolved`、`query_travelRequirementSlot_unchangedAndNoTransferProbe`)——本次撰写时通过 `git show` 直接读取测试源码确认断言存在,未在本会话内重新执行该测试类。 +- 说明:本文档撰写者(变更通知员角色)未在本次会话中持有部署机 SSH 访问权限,`deploy-status.sh` 的 `0/N` 结论与网关实测数据表由任务发起方提供并采纳;源码 diff、squash 提交号与 `origin/dev-v3` 祖先关系均为本次会话独立核实。 + +--- + +## 十、相关文档 + +- 工单 [#7990](https://git.1814.love:8443/wx/HL/issues/7990) AC-8(本单) +- `changelogs-v2/2026-09/20_7990_车务看板订单详情按需求各返一组身份三元组-修改接口-管理后台.md`(同一工单 #7990 下另一 AC,PR #8048,讲看板详情接口新增 `requirementIdentities`,与本文档描述的候选面座位/车型判定修复互不重叠) +- 工单 [#7443](https://git.1814.love:8443/wx/HL/issues/7443)(TRAVEL/TRANSFER 需求可并存这一形态的起源) +- `changelogs-v2/2026-09/20_7443_TRANSFER派车行确认改派基线复核修复-修改接口-管理后台.md`(同族缺陷:其它端点"恒取 TRAVEL 当基线"的修复) +- `changelogs-v2/2026-09/20_8004_共用关系两个读口契约收口拆出shareEligible-修改接口-管理后台.md`(同一候选查询端点近期的另一处字段变更,互不重叠) + +--- + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#7990](https://git.1814.love:8443/wx/HL/issues/7990) +- **PR**: [#8062](https://git.1814.love:8443/wx/HL/pulls/8062) +- **Merge commit**: [bf4fba5a2](https://git.1814.love:8443/wx/HL/commit/bf4fba5a2357cf6a137105e6b175b48446ef0cc0)(squash 直接提交 dev-v3) + +### 联系人 + +- **后端负责人**: @wx