docs(changelog): #7442 补登 reconfigure 写口与就绪回写两级判定(PR-A / PR-C1)
changelog-filename-gate / validate (push) Failing after 2s
changelog-filename-gate / validate (push) Failing after 2s
这两个 PR 合入时都没写交接件,是排查 #7442 AC-22(要求交接件含全部端点契约) 时发现的既有缺口: - PR-A #7844 的 `POST /admin/fleet/group-dispatch/batches/{id}/reconfigure` 是本单最核心的写口,此前无任何 changelog 记录 - PR-C1 #7923 的就绪回写带身份两级判定同样缺 两份都按实际合入的代码写,不照工单原文(该单正文被订正过多次)。 准入按角色门禁写:`X-Admin-Role ∈ {VEHICLE_MANAGER, SUPER_ADMIN}` (`FleetAdminRoleGuardInterceptor`)——工单里写的权限点 `fleet:group-dispatch:write` 全仓零命中,只存在于一行 javadoc 注释里,不是落地的权限模型。 backend_status=deployed:两个提交均为 4cbccc26b 祖先,测试服七服务已回读确认。 gateway_status=verified:两份清单里的端点均已在测试服取得实测请求/响应。 Refs #7442 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -0,0 +1,322 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "7442"
|
||||
title: "团期配车分组写口 reconfigure(PR-A 补登)"
|
||||
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-A(#7844,merge commit 8eb8e13cdfd4a8cc004f95cfb53f7e4b81904461,2026-09-16 合入 dev-v3)首次交付本端点时漏写了交接件,导致 #7442 AC-22(要求『5 个新增接口 + 4 个改造接口的完整契约』)按字面一直不可能达成;本单是对这个缺口的补登。backend_status=deployed 的依据:8eb8e13cd 已在 dev-v3,测试服 2026-09-19 01:15 已滚动部署到 4cbccc26b(晚于 8eb8e13cd 与后续 c6aa1224f,均已实测回读 commit 确认在链上)。gateway_status 先记 pending,网关实测由 #7442 取证车道另行补。本文档按端点当前(2026-09-19)的完整契约撰写,其中 requirementId/requirementVersion/demands[].assignments[].groupId 等分组相关字段是 PR-A 首次引入的核心内容;reconfigureWindowToken 字段与 602011/602012/602013 三个错误码是随后 PR-C2(#7957)追加到同一端点的字段,本文档一并如实标注,避免把 PR-C2 之后的完整契约错当 PR-A 原状描述。"
|
||||
updated_at: "2026-09-19"
|
||||
base: "dev-v3"
|
||||
---
|
||||
|
||||
# fleet: 团期配车分组写口 reconfigure(PR-A 补登)
|
||||
|
||||
> **存放目录**: changelogs-v2/{YYYY-MM}/
|
||||
>
|
||||
> **服务**: hl-fleet-service (端口 8082)
|
||||
> **PR**: [#7844](https://git.1814.love:8443/wx/HL/pulls/7844)(PR-A,2026-09-16 已合入;本端点此后又被 [#7957](https://git.1814.love:8443/wx/HL/pulls/7957) PR-C2 追加一个字段,详见下方标注)
|
||||
> **Issue**: #7442(AC-22)
|
||||
> **日期**: 2026-09-19(补登;端点实际上线于 2026-09-16)
|
||||
> **影响范围**: 团期配车页——车务提交整团逐日配车计划的核心写口
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化
|
||||
|
||||
1. **这是补登,不是新功能上线通知**:本端点已在测试服跑了 3 天(2026-09-16 起),**mmg 可能已经在对接它**——
|
||||
本文档只是把此前漏写的交接件补齐,不代表这是新上线的东西,请勿据此重新走一遍"新接口接入"流程。
|
||||
2. **本单是团级配车从"零调用方"到"有真实写口"的分水岭**:改前,`GroupDispatchStatus` 相关的团级配车引擎
|
||||
虽已存在,但生产调用方为零,车务只能靠车管手工建单兜底;改后,车务可在团期配车页把整团逐日计划直接提交。
|
||||
3. **`reconfigureWindowToken` 字段与 602011/602012/602013 三个错误码不属于 PR-A**:它们是随后 PR-C2
|
||||
(2026-09-18 合入)追加到本端点的,服务「受控重开窗口」流程(见
|
||||
`19_7442_团期配车受控重开窗口计划刷新状态收口-新增接口-管理后台.md`)。本文档按端点**当前**的完整契约
|
||||
撰写(两次改动都已部署),但在字段表/错误码表里标注了各自的来源批次,避免误以为这是 PR-A 一次性交付的。
|
||||
4. **`missingGroupCodes` 字段恒为空列表,不要依赖它判断缺组**:响应 `coverage.missingGroupCodes`
|
||||
在任何路径上都只能是 `[]`——真的缺组时走的是抛 602002 异常的路径,根本不产生响应体,「缺组」这个结论
|
||||
永远不会通过这个字段表达出来。前端如果要展示"缺哪些组",请用捕获到的 602002 错误消息(点名到组),
|
||||
或改查 `GET /internal/fleet/dispatch/group-batch/{id}/coverage`(内部接口,见
|
||||
`19_7442_团期配车受控重开窗口计划刷新状态收口-新增接口-管理后台.md` 第 4 条,那个端点的同名字段才有
|
||||
非空的可能)。
|
||||
|
||||
---
|
||||
|
||||
## 二、变更接口清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 整团逐日配车提交 | POST | `/admin/fleet/group-dispatch/batches/{groupBatchId}/reconfigure` | 新增(补登;PR-A 已部署 3 天) | 车务按乘车分组提交整团逐日配车计划,服务端与现状差量比对 |
|
||||
|
||||
---
|
||||
|
||||
## 三、接口详情
|
||||
|
||||
### 1. 整团逐日配车提交 `POST /admin/fleet/group-dispatch/batches/{groupBatchId}/reconfigure`
|
||||
|
||||
**VO**: `GroupDispatchReconfigureReqVO → GroupDispatchReconfigureRespVO`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
车务在团期配车页提交整团逐日配车计划(差量重配:多删少补,旧记录软删留痕,新需求新写,不整团作废重配)。
|
||||
服务端以 order-v3 提供的权威团期基线(服务日、生命周期、权威乘车分组清单)校验:完整覆盖、无越界、无重复
|
||||
且团期可配才允许提交。
|
||||
|
||||
#### 入参字段表
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | 是 | - | 团期主订单 ID |
|
||||
| requirementId | Body | Long | 是 | - | 本次计划照着哪一份正式团级用车需求排;与基线不一致抛 602005 |
|
||||
| requirementVersion | Body | Integer | 是 | - | 本次计划照着需求的哪一版排;落后于基线当前版本抛 602005(fail-closed,不接受"反正车没变") |
|
||||
| clearAll | Body | Boolean | 否 | 默认 `false` | true=显式整团清零,软删该团全部活跃配车并释放车辆/司机占用,`demands` 可为空 |
|
||||
| reconfigureWindowToken | Body | String | 条件必填 | - | 【**PR-C2 追加**】受控重开窗口令牌,团期仍在 `RESOURCE_PREPARING` 时可不传;团期过了资源准备阶段后必填,且必须与 order-v3 下发的窗口令牌一致,否则 602012 |
|
||||
| demands | Body | Array | 条件必填 | `clearAll=false` 时非空 | 逐日配车需求;行程日期不可重复(600003) |
|
||||
| demands[].tripDate | Body | LocalDate | 是 | - | 行程日期 |
|
||||
| demands[].assignments | Body | Array | 是 | 至少一条 | 当日全部车/司机组合,非空(600004) |
|
||||
| demands[].assignments[].groupId | Body | String | 是 | ≤64 字符 | 乘车分组键(=需求侧 `group_code`,如 `BUS`);空抛 602000,不在基线清单抛 602001 |
|
||||
| demands[].assignments[].vehicleId | Body | Long | 是 | - | 派出车辆 ID;同日重复抛 600006(车辆被占) |
|
||||
| demands[].assignments[].driverId | Body | Long | 否 | - | 派出司机 ID;可空=仅排车未排司机;同日重复抛 600007(司机被占) |
|
||||
| demands[].assignments[].remark | Body | String | 否 | ≤200 字符 | 备注 |
|
||||
|
||||
#### 出参字段表
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| groupBatchId | String(雪花 ID) | 团期主订单 ID |
|
||||
| requirementId | String(雪花 ID) | 本次计划所依据的正式团级用车需求 ID |
|
||||
| requirementVersion | Integer | 本次计划所依据的需求版本 |
|
||||
| planVersion | Long | 本次落库后的团期计划版本;幂等短路时为当前版本,不递增 |
|
||||
| addedCount | Integer | 新增派车记录数 |
|
||||
| removedCount | Integer | 软删派车记录数(减员) |
|
||||
| keptCount | Integer | 保留未变派车记录数 |
|
||||
| updatedCount | Integer | 就地更新(换组/换司机/改备注)派车记录数 |
|
||||
| aliveCount | Integer | 提交后整团存活派车记录总数 |
|
||||
| addedDispatchIds | Array\<String\>(雪花 ID) | 新增派车记录主键列表 |
|
||||
| idempotentShortCircuit | Boolean | 本次是否被计划去重短路;`true`=计划与上次完全一致、本次未落库——**这是幂等成功,不是失败**,前端不要按错误提示 |
|
||||
| coverage | Object | 按乘车分组的覆盖明细,见下 |
|
||||
| coverage.groups[] | Array | 按组的覆盖明细 |
|
||||
| coverage.groups[].groupCode | String | 分组键 |
|
||||
| coverage.groups[].vehicleType | String | 车型文本/字典值 |
|
||||
| coverage.groups[].requiredDates | Array\<LocalDate\> | 本组权威服务日 |
|
||||
| coverage.groups[].coveredDates | Array\<LocalDate\> | 本次计划为本组实际排车的日期 |
|
||||
| coverage.groups[].missingDates | Array\<LocalDate\> | 本组缺失的服务日 |
|
||||
| coverage.groups[].outOfRangeDates | Array\<LocalDate\> | 本组越界的日期 |
|
||||
| coverage.groups[].satisfied | Boolean | 本组是否已满足 |
|
||||
| coverage.missingGroupCodes | Array\<String\> | **恒为空列表**(见「⚠️ 关键变化」第 4 条),不要依赖它判断缺组 |
|
||||
| coverage.wholeBatchSatisfied | Boolean | 全团行程日整体覆盖是否成立 |
|
||||
| legacyGroupRowCount | Integer | 本团存活派车行里无分组键的历史行数(非错误,仅留痕,见 #7442 AC-32) |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
POST /admin/fleet/group-dispatch/batches/1934567890123456800/reconfigure
|
||||
{
|
||||
"requirementId": 1934567890123456789,
|
||||
"requirementVersion": 3,
|
||||
"clearAll": false,
|
||||
"demands": [
|
||||
{
|
||||
"tripDate": "2026-09-12",
|
||||
"assignments": [
|
||||
{"groupId": "BUS", "vehicleId": 1001, "driverId": 2001, "remark": "大巴组"},
|
||||
{"groupId": "SUV", "vehicleId": 1002, "driverId": 2002}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"groupBatchId": "1934567890123456800",
|
||||
"requirementId": "1934567890123456789",
|
||||
"requirementVersion": 3,
|
||||
"planVersion": 7,
|
||||
"addedCount": 2,
|
||||
"removedCount": 0,
|
||||
"keptCount": 0,
|
||||
"updatedCount": 0,
|
||||
"aliveCount": 2,
|
||||
"addedDispatchIds": ["99011", "99012"],
|
||||
"idempotentShortCircuit": false,
|
||||
"coverage": {
|
||||
"groups": [
|
||||
{
|
||||
"groupCode": "BUS",
|
||||
"vehicleType": "宇通33座大巴",
|
||||
"requiredDates": ["2026-09-12"],
|
||||
"coveredDates": ["2026-09-12"],
|
||||
"missingDates": [],
|
||||
"outOfRangeDates": [],
|
||||
"satisfied": true
|
||||
}
|
||||
],
|
||||
"missingGroupCodes": [],
|
||||
"wholeBatchSatisfied": true
|
||||
},
|
||||
"legacyGroupRowCount": 0
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
`clearAll=true` 时 `demands` 可为空数组,属正常请求形态(整团清零),响应仍返回完整对象,
|
||||
`removedCount` 反映本次软删的行数、`aliveCount=0`:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"groupBatchId": "1934567890123456800",
|
||||
"requirementId": "1934567890123456789",
|
||||
"requirementVersion": 3,
|
||||
"planVersion": 8,
|
||||
"addedCount": 0,
|
||||
"removedCount": 2,
|
||||
"keptCount": 0,
|
||||
"updatedCount": 0,
|
||||
"aliveCount": 0,
|
||||
"addedDispatchIds": [],
|
||||
"idempotentShortCircuit": false,
|
||||
"coverage": { "groups": [], "missingGroupCodes": [], "wholeBatchSatisfied": false },
|
||||
"legacyGroupRowCount": 0
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 602002,
|
||||
"message": "以下乘车分组整组未排车: SUV",
|
||||
"data": null,
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
可能的错误码:
|
||||
- `600001` - 团期批次 ID 不能为空
|
||||
- `600002` - 逐日配车需求不能为空
|
||||
- `600003` - 逐日需求存在重复行程日期
|
||||
- `600004` - 单日排车列表不能为空
|
||||
- `600005` - 排车车辆 ID 不能为空
|
||||
- `600006` - 车辆已被占用
|
||||
- `600007` - 司机已被占用
|
||||
- `600008` - 团期配车已被并发修改,请刷新后重试
|
||||
- `600009` - 团期配车权威基线不可用,请稍后重试或检查团期状态
|
||||
- `600010` - 团期当前状态不可配车
|
||||
- `600011` - 配车计划未完整覆盖团期服务日
|
||||
- `602000` - 排车项缺少乘车分组(PR-A)
|
||||
- `602001` - 乘车分组不存在于本团正式需求(PR-A)
|
||||
- `602002` - 以下乘车分组整组未排车(PR-A,**点名到组**,不是笼统的"缺日")
|
||||
- `602003` - 乘车分组的服务日未排满(PR-A)
|
||||
- `602004` - 乘车分组排了本组服务范围外的日期(PR-A)
|
||||
- `602005` - 用车需求已更新,请刷新后重新配车(PR-A)
|
||||
- `602006` - 正式用车需求当前状态不允许配车(PR-A)
|
||||
- `602009` - 无法取得本团的权威乘车分组清单(PR-A,**失败关闭**,两种成因:该团从没提交过正式用车需求,或需求存在但声明了整团免车)
|
||||
- `602010` - 配车入参非法(PR-A)
|
||||
- `602011` - 受控重开窗口内不允许整团清零配车(**PR-C2 追加**)
|
||||
- `602012` - 受控重开窗口校验不通过(**PR-C2 追加**,缺/错/过期令牌)
|
||||
- `602013` - 本次配车改动越出重开窗口授权范围(**PR-C2 追加**)
|
||||
- `809100` / `809101` - order-v3 经 Feign 解包透出(无活跃需求 / 需求状态不允许)
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- **鉴权**: `X-Admin-Role` 须为 `VEHICLE_MANAGER` 或 `SUPER_ADMIN`(`FleetAdminRoleGuardInterceptor` 角色门禁,**不是权限点**——工单原文与部分源码 javadoc 写的"权限点 `fleet:group-dispatch:write`"在数据库层从未注册,全仓 `*.sql` 搜不到这个字符串,见 `19_7444_团期配车就绪门禁与同团车辆共用关系-新增接口-管理后台.md`「关键变化」第 2 条的详细核实过程)
|
||||
- **防重与幂等是两件事,不要混读**:①10 秒内对同一份计划重复提交会被 `@Idempotent` 防重窗口**拒绝**
|
||||
(返 100502「团期配车重配处理中,请勿重复提交」),前端按"稍后重试"处理;②窗口**之外**重复提交同一份
|
||||
计划会正常受理并返回 `idempotentShortCircuit=true`——**那是成功**(计划未变、未落库、未产生新意图),
|
||||
不要按错误提示。这两条机制彼此独立:前者按 10 秒时间窗判重,后者按计划内容摘要(`planDigest`)判重,
|
||||
没有时间窗限制
|
||||
- **各组服务日范围可以不同**:A 组走全程、B 组只用三天车时,B 组在第四、五天没有排车行是**正确的**,
|
||||
不报缺日——这条既有口径未被 602003 削弱
|
||||
- **不校验"是否能确认"**:本端点只管排车,"确认整团配车"是另一个独立端点
|
||||
(`POST .../confirm`,见 `17_7442_团级确认态-需求已发车务回写-新增接口-管理后台.md`)
|
||||
|
||||
---
|
||||
|
||||
## 四、契约约束与正确调用方式
|
||||
|
||||
> 本节只写后端接受/拒绝 payload 的规则,不写 UI 渲染建议。
|
||||
|
||||
| 场景 | 做法 |
|
||||
|------|------|
|
||||
| 提交计划前需要拿团期权威分组清单 | 该清单在基线里,本端点自己不暴露一个单独的查询口;前端通常从团期需求/配车总览页拿到 |
|
||||
| 团期在 `RESOURCE_PREPARING` 阶段提交 | `reconfigureWindowToken` 可不传 |
|
||||
| 团期已过 `RESOURCE_PREPARING`(`MATERIAL_PREPARING`/`PENDING_DEPARTURE`)提交 | 必须先经受控重开端点(`POST .../vehicle-requirement/reopen`)拿到 `windowToken` 并原样带回,否则 602012 |
|
||||
| 需要整团清零 | 传 `clearAll=true`,`demands` 可省略 |
|
||||
| 判断"提交成功但计划没变" | 看 `idempotentShortCircuit`,为 `true` 时是成功,不是失败 |
|
||||
|
||||
---
|
||||
|
||||
## 五、数据库行为
|
||||
|
||||
| 操作 | 数据库影响 |
|
||||
|------|----------|
|
||||
| 正常提交(有增/删/改) | `fleet_group_dispatch` 差量写入:新增行 INSERT、软删行 `status` 置软删并留痕、就地更新行按需变更;`fleet_group_dispatch_plan.plan_version` CAS +1 |
|
||||
| `clearAll=true` | 该团全部活跃 `fleet_group_dispatch` 行软删,释放车辆/司机占用,`vehicle_ready` 重置为 false |
|
||||
| 幂等短路(`idempotentShortCircuit=true`) | 零写入,`plan_version` 不变 |
|
||||
|
||||
---
|
||||
|
||||
## 六、边界行为
|
||||
|
||||
- **无权威分组清单时失败关闭**(602009):不会因为拿不到分组就放行一份没有分母的计划
|
||||
- **同日重复车辆/司机拒绝**(600006/600007):同一天同一辆车/同一名司机出现在两条排车项里直接拒绝,不做去重合并
|
||||
- **服务日部分覆盖不是缺陷**:各组服务日范围可以不同,短组在超出自己范围的日子没有排车行是合法的
|
||||
|
||||
---
|
||||
|
||||
## 七、不影响范围
|
||||
|
||||
- **仅影响**: 团期配车页的整团逐日提交动作
|
||||
- **零影响**:
|
||||
- 确认整团配车 `POST .../confirm`(见 `17_7442_团级确认态-需求已发车务回写-新增接口-管理后台.md`)
|
||||
- 受控重开/计划刷新流程的其余 4 个端点(见 `19_7442_团期配车受控重开窗口计划刷新状态收口-新增接口-管理后台.md`)
|
||||
- 团期配车就绪判定、同团车辆共用关系(见 `19_7444_团期配车就绪门禁与同团车辆共用关系-新增接口-管理后台.md`)
|
||||
|
||||
---
|
||||
|
||||
## 八、测试环境已验证
|
||||
|
||||
> ⚠️ 本节暂无网关实测数据,PR-A 虽已部署但本文档撰写时未另行发起网关请求;gateway_status 待 #7442 取证车道回填。
|
||||
|
||||
---
|
||||
|
||||
## 十、相关文档
|
||||
|
||||
- 关联 Issue: [wx/HL#7442](https://git.1814.love:8443/wx/HL/issues/7442)
|
||||
- 关联 PR: [wx/HL#7844](https://git.1814.love:8443/wx/HL/pulls/7844)(PR-A,本端点首次交付);
|
||||
[wx/HL#7957](https://git.1814.love:8443/wx/HL/pulls/7957)(PR-C2,追加 `reconfigureWindowToken` 字段)
|
||||
- 相关文档:
|
||||
- `changelogs-v2/2026-09/17_7442_团级确认态-需求已发车务回写-新增接口-管理后台.md`
|
||||
- `changelogs-v2/2026-09/19_7442_团期配车受控重开窗口计划刷新状态收口-新增接口-管理后台.md`
|
||||
|
||||
## 关联 / 联系人
|
||||
|
||||
### 链接
|
||||
|
||||
- **Issue**: [#7442](https://git.1814.love:8443/wx/HL/issues/7442)
|
||||
- **PR**: [#7844](https://git.1814.love:8443/wx/HL/pulls/7844)
|
||||
- **Merge commit**: [8eb8e13cdfd4a8cc004f95cfb53f7e4b81904461](https://git.1814.love:8443/wx/HL/commit/8eb8e13cdfd4a8cc004f95cfb53f7e4b81904461)
|
||||
|
||||
### 联系人
|
||||
|
||||
- **后端负责人**: @wx
|
||||
在新工单中引用
屏蔽一个用户