chore(changelog): 团期配车就绪门禁与清零释放内部契约 (#5319)
一些检查失败了
changelog-filename-gate / validate (push) Failing after 1s
一些检查失败了
changelog-filename-gate / validate (push) Failing after 1s
这个提交包含在:
父节点
69327dce00
当前提交
9f69cfcd25
507
.tmp-user-62_4938.md
普通文件
507
.tmp-user-62_4938.md
普通文件
@ -0,0 +1,507 @@
|
||||
# 【#4938 前端对接·管理后台】车务创建、修改与最终确认返回订单调整基线差异
|
||||
|
||||
> Issue: [wx/HL#4938](https://git.1814.love:8443/wx/HL/issues/4938)
|
||||
>
|
||||
> PR: 待补充
|
||||
>
|
||||
> 服务: `hl-fleet-service`
|
||||
>
|
||||
> 日期: 2026-07-19
|
||||
>
|
||||
> 影响范围: 车务直接派车、直接改派、`holding → assigned` 最终确认,以及订单调整后的日期、人数、车辆容量差异处理
|
||||
>
|
||||
> 状态: 源码静态契约已核对,待代码评审、合并、部署与测试环境验收
|
||||
|
||||
## 一、前端对接结论
|
||||
|
||||
以下三个既有管理后台接口现在共用业务码 `605041` 返回结构化逐日差异:
|
||||
|
||||
| 操作 | 接口 | 触发 605041 的条件 |
|
||||
|---|---|---|
|
||||
| 创建派单 | `POST /admin/fleet/assignments` | `holdMode=0` 直接派车时最终基线不一致 |
|
||||
| 修改派单 | `POST /admin/fleet/assignments/{assignmentId}/change` | `holdMode=0` 直接改派时最终基线不一致 |
|
||||
| 最终确认 | `POST /admin/fleet/assignments/{assignmentId}/confirm` | 订单、需求、行程、逐日派单、人数或容量基线不一致 |
|
||||
|
||||
前端必须遵守以下判断:
|
||||
|
||||
1. 三个接口的 `605041` 当前均返回 HTTP 200,但统一响应体为 `code=605041`、`success=false`,不能只判断 HTTP 状态。
|
||||
2. `605041` 的 `data` 不为空:
|
||||
- create 返回 `AssignmentWriteRespVO`,保证 `data.dailyDifferences` 可读;
|
||||
- change 返回 `ChangeAssignmentRespVO`,保证 `data.dailyDifferences` 可读;
|
||||
- confirm 返回 `ConfirmRespVO`,保证 `data.confirmed=false` 和 `data.dailyDifferences` 可读。
|
||||
3. `605041` 不会提交派单及其关联业务状态写入:
|
||||
- create 不会新增或激活派单;
|
||||
- change 不会取消旧派单,也不会生成可用的新派车版本;
|
||||
- confirm 不会推进派单状态或确认时间;
|
||||
- 三者都不会触发车辆/司机占用、保险、对账或订单派定结果变化。
|
||||
4. 收到 `605041` 后保持操作前页面状态,展示逐日差异,并重新拉取最新订单和车务详情。
|
||||
5. 所有派单、车辆槽位、派车组、订单、车辆和司机雪花 ID 均按 JSON String 发送和读取,禁止转为 JavaScript `Number`。金额字段也按 String 读取。
|
||||
|
||||
## 二、605041 公共响应契约
|
||||
|
||||
### 2.1 统一响应外层
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 605041,
|
||||
"message": "订单行程或用车派单已变化,请按逐日差异处理后重试",
|
||||
"data": {
|
||||
"dailyDifferences": [
|
||||
{
|
||||
"serviceDate": "2026-07-22",
|
||||
"differenceType": "CAPACITY_INSUFFICIENT",
|
||||
"assignmentId": null,
|
||||
"assignmentSlotId": null,
|
||||
"passengerCount": 8,
|
||||
"passengerCapacity": 5,
|
||||
"capacityGap": 3,
|
||||
"message": "车辆载客量不足,已按每车司机占一座计算"
|
||||
}
|
||||
]
|
||||
},
|
||||
"traceId": "7db459fd-0b8e-4f29-9c46-4938f09a001",
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
注意:
|
||||
|
||||
- `data` 的完整类型取决于调用的是 create、change 还是 confirm,不能跨接口复用成功响应模型。
|
||||
- 除本文件明确保证的失败字段外,其余成功态字段在 `605041` 时为空,前端不得用它们推断写入结果。
|
||||
- `traceId` 用于反馈和日志定位,不参与业务判断。
|
||||
|
||||
### 2.2 `dailyDifferences[]`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `serviceDate` | String/null | 发生差异的服务日,格式 `yyyy-MM-dd`;无法定位到单日时为空 |
|
||||
| `differenceType` | String | 差异类型,取值见下表 |
|
||||
| `assignmentId` | String/null | 可定位到具体派单时返回;仅用于定位差异,不代表本次写入成功 |
|
||||
| `assignmentSlotId` | String/null | 可定位到稳定车辆槽位时返回 |
|
||||
| `passengerCount` | Integer/null | 当前比对使用的乘客人数,不含司机 |
|
||||
| `passengerCapacity` | Integer/null | 当日车辆合计可载客人数,每辆车已扣除司机一座 |
|
||||
| `capacityGap` | Integer/null | 缺少座位数,等于 `passengerCount - passengerCapacity` |
|
||||
| `message` | String | 后端生成的差异说明,可直接辅助展示 |
|
||||
|
||||
差异类型:
|
||||
|
||||
| `differenceType` | 含义 |
|
||||
|---|---|
|
||||
| `REQUIREMENT_VERSION_MISMATCH` | 当前生效用车需求已变化,或订单/需求已不可继续派单 |
|
||||
| `ORDER_DATE_MISMATCH` | 订单当前日期与用车需求冻结日期不一致 |
|
||||
| `ITINERARY_DATE_MISMATCH` | 逐日行程日期与用车需求冻结日期不一致,或缺少逐日行程 |
|
||||
| `ASSIGNMENT_DATE_MISSING` | 某服务日缺少有效派单或车辆槽位 |
|
||||
| `ASSIGNMENT_DATE_EXTRA` | 派单仍包含已不属于当前需求的服务日 |
|
||||
| `HEADCOUNT_BASELINE_MISMATCH` | 订单当前人数、需求冻结人数或派单人数快照不一致 |
|
||||
| `CAPACITY_INSUFFICIENT` | 当日所有车辆合计载客量不足 |
|
||||
|
||||
`dailyDifferences` 可能同时包含多种类型、多条服务日记录。前端应遍历数组展示,不得只取第一条,也不得自行重算人数或车辆容量。
|
||||
|
||||
## 三、创建派单 create
|
||||
|
||||
### 3.1 请求
|
||||
|
||||
```http
|
||||
POST /admin/fleet/assignments
|
||||
Authorization: Bearer <admin-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"orderId": "2078001000000000101",
|
||||
"orderNo": "26-0719",
|
||||
"requirementId": "2078001000000000201",
|
||||
"fleetItemIndex": 1,
|
||||
"vehicleId": "2078001000000000301",
|
||||
"driverId": "2078001000000000401",
|
||||
"startDate": "2026-07-21",
|
||||
"endDate": "2026-07-23",
|
||||
"pickupAt": "海拉尔",
|
||||
"dropoffAt": "满洲里",
|
||||
"headcount": 8,
|
||||
"protocolPrice": "1300.00",
|
||||
"holdMode": 0,
|
||||
"fromEntry": "from-board",
|
||||
"skipCityJunctionException": false,
|
||||
"strictSeats": true,
|
||||
"confirmCrossResident": false,
|
||||
"requestId": "fleet-create-4938-20260719-001"
|
||||
}
|
||||
```
|
||||
|
||||
`605041` 只适用于 `holdMode=0`。原有 `holdMode=1` 排车锁定流程不执行本次最终基线门禁。
|
||||
|
||||
### 3.2 holdMode=0 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"id": "2078001000000000501",
|
||||
"assignmentGroupId": "2078001000000000601",
|
||||
"assignmentSlotId": "2078001000000000701",
|
||||
"assignmentStatus": "assigned",
|
||||
"stageCode": "assigned",
|
||||
"stageLabel": "已派车",
|
||||
"currentStep": 4,
|
||||
"skippedStepCodes": [
|
||||
"DRIVER_CONFIRMATION",
|
||||
"DRIVER_CONFIRMATION_EVIDENCE"
|
||||
],
|
||||
"protocolPrice": "1300.00",
|
||||
"holdSentAt": null,
|
||||
"confirmedAt": "2026-07-19 14:20:00",
|
||||
"sideEffects": {
|
||||
"vehicleStatusUpdated": "busy",
|
||||
"driverStatusUpdated": "busy",
|
||||
"reconPrepRowsCreated": 0,
|
||||
"reconPrepMarkedCanceled": null
|
||||
},
|
||||
"dailyDifferences": null
|
||||
},
|
||||
"traceId": "7db459fd-0b8e-4f29-9c46-4938c200001",
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
仅在 `code=200` 时把新派单加入页面;`id`、`assignmentGroupId`、`assignmentSlotId` 均按 String 保存。
|
||||
|
||||
### 3.3 605041 失败响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 605041,
|
||||
"message": "订单行程或用车派单已变化,请按逐日差异处理后重试",
|
||||
"data": {
|
||||
"id": null,
|
||||
"assignmentGroupId": null,
|
||||
"assignmentSlotId": null,
|
||||
"assignmentStatus": null,
|
||||
"stageCode": null,
|
||||
"stageLabel": null,
|
||||
"currentStep": null,
|
||||
"skippedStepCodes": null,
|
||||
"protocolPrice": null,
|
||||
"holdSentAt": null,
|
||||
"confirmedAt": null,
|
||||
"sideEffects": null,
|
||||
"dailyDifferences": [
|
||||
{
|
||||
"serviceDate": "2026-07-22",
|
||||
"differenceType": "CAPACITY_INSUFFICIENT",
|
||||
"assignmentId": null,
|
||||
"assignmentSlotId": null,
|
||||
"passengerCount": 8,
|
||||
"passengerCapacity": 5,
|
||||
"capacityGap": 3,
|
||||
"message": "车辆载客量不足,已按每车司机占一座计算"
|
||||
}
|
||||
]
|
||||
},
|
||||
"traceId": "7db459fd-0b8e-4f29-9c46-4938c605041",
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
此时不得把临时响应内容加入派单列表,不得本地占用车辆/司机;刷新后仍以服务端最新详情为准。
|
||||
|
||||
## 四、修改派单 change
|
||||
|
||||
### 4.1 请求
|
||||
|
||||
```http
|
||||
POST /admin/fleet/assignments/2078001000000000501/change
|
||||
Authorization: Bearer <admin-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"effectiveDate": "2026-07-22",
|
||||
"newVehicleId": "2078001000000000302",
|
||||
"newDriverId": "2078001000000000402",
|
||||
"holdMode": 0,
|
||||
"protocolPrice": "1688.00",
|
||||
"confirmCrossResident": false,
|
||||
"reason": "订单调整后更换车辆和司机",
|
||||
"requestId": "fleet-change-4938-20260719-001"
|
||||
}
|
||||
```
|
||||
|
||||
`newVehicleId`、`newDriverId` 至少传一个。`605041` 只适用于 `holdMode=0`;`holdMode=1` 仍按排车待司机确认流程处理。
|
||||
|
||||
### 4.2 holdMode=0 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"assignmentId": "2078001000000000502",
|
||||
"assignmentSlotId": "2078001000000000701",
|
||||
"previousAssignmentGroupId": "2078001000000000601",
|
||||
"newAssignmentGroupId": "2078001000000000602",
|
||||
"assignmentStatus": "assigned",
|
||||
"effectiveDate": "2026-07-22",
|
||||
"affectedDays": 2,
|
||||
"protocolPrice": "1688.00",
|
||||
"otherVehicleCount": 1,
|
||||
"warningCode": "ORDER_HAS_OTHER_VEHICLES",
|
||||
"warningMessage": "该订单另有1个车辆槽位,当前仅修改本车辆,请核对其它车辆安排",
|
||||
"otherVehicles": [
|
||||
{
|
||||
"assignmentSlotId": "2078001000000000702",
|
||||
"vehiclePlate": "蒙B-66666",
|
||||
"driverName": "李师傅",
|
||||
"startDate": "2026-07-21",
|
||||
"endDate": "2026-07-23"
|
||||
}
|
||||
],
|
||||
"dailyDifferences": null
|
||||
},
|
||||
"traceId": "7db459fd-0b8e-4f29-9c46-4938a200001",
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
change 成功响应没有 `confirmed` 和 `sideEffects` 字段。只有 `code=200` 时才能用新派车组替换页面中的旧版本;`warningCode=ORDER_HAS_OTHER_VEHICLES` 时继续保留既有强提示。
|
||||
|
||||
### 4.3 605041 失败响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 605041,
|
||||
"message": "订单行程或用车派单已变化,请按逐日差异处理后重试",
|
||||
"data": {
|
||||
"assignmentId": null,
|
||||
"assignmentSlotId": null,
|
||||
"previousAssignmentGroupId": null,
|
||||
"newAssignmentGroupId": null,
|
||||
"assignmentStatus": null,
|
||||
"effectiveDate": null,
|
||||
"affectedDays": null,
|
||||
"protocolPrice": null,
|
||||
"otherVehicleCount": null,
|
||||
"warningCode": null,
|
||||
"warningMessage": null,
|
||||
"otherVehicles": null,
|
||||
"dailyDifferences": [
|
||||
{
|
||||
"serviceDate": null,
|
||||
"differenceType": "HEADCOUNT_BASELINE_MISMATCH",
|
||||
"assignmentId": null,
|
||||
"assignmentSlotId": null,
|
||||
"passengerCount": 8,
|
||||
"passengerCapacity": null,
|
||||
"capacityGap": null,
|
||||
"message": "订单当前人数与用车需求冻结人数不一致"
|
||||
},
|
||||
{
|
||||
"serviceDate": "2026-07-22",
|
||||
"differenceType": "CAPACITY_INSUFFICIENT",
|
||||
"assignmentId": null,
|
||||
"assignmentSlotId": null,
|
||||
"passengerCount": 8,
|
||||
"passengerCapacity": 5,
|
||||
"capacityGap": 3,
|
||||
"message": "车辆载客量不足,已按每车司机占一座计算"
|
||||
}
|
||||
]
|
||||
},
|
||||
"traceId": "7db459fd-0b8e-4f29-9c46-4938a605041",
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
此时旧派单和原派车组仍是有效业务状态。不得用 `dailyDifferences[].assignmentId` 替换页面主键,也不得本地切换车辆、司机或状态。
|
||||
|
||||
## 五、最终确认 confirm
|
||||
|
||||
### 5.1 请求
|
||||
|
||||
```http
|
||||
POST /admin/fleet/assignments/2078001000000000501/confirm
|
||||
Authorization: Bearer <admin-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"requestId": "fleet-final-confirm-4938-20260719-001"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|---|---|---|---|---|---|
|
||||
| `assignmentId` | Path | String | 是 | 有效派单 ID | 当前派车组中的任一派单 ID |
|
||||
| `requestId` | Body | String | 是 | 非空,最长 64 | 最终确认幂等标识 |
|
||||
|
||||
### 5.2 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"confirmed": true,
|
||||
"assignmentStatus": "assigned",
|
||||
"stageCode": "assigned",
|
||||
"stageLabel": "已派车",
|
||||
"currentStep": 4,
|
||||
"assignmentGroupId": "2078001000000000601",
|
||||
"confirmedAt": "2026-07-19 14:30:00",
|
||||
"itineraryUrl": "https://h5.example.com/#/itinerary/<signed-token>",
|
||||
"sideEffects": {
|
||||
"vehicleStatusUpdated": "busy",
|
||||
"driverStatusUpdated": "busy",
|
||||
"reconPrepRowsCreated": 0,
|
||||
"reconPrepMarkedCanceled": null
|
||||
},
|
||||
"dailyDifferences": null
|
||||
},
|
||||
"traceId": "7db459fd-0b8e-4f29-9c46-4938f200001",
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
`itineraryUrl` 签发配置不可用时允许为空,不影响确认成功。前端只有在 `code=200 && data.confirmed===true` 时展示最终确认成功,并刷新派单详情、看板列表和汇总。
|
||||
|
||||
### 5.3 日期、人数与容量同时存在差异
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 605041,
|
||||
"message": "订单行程或用车派单已变化,请按逐日差异处理后重试",
|
||||
"data": {
|
||||
"confirmed": false,
|
||||
"assignmentStatus": null,
|
||||
"stageCode": null,
|
||||
"stageLabel": null,
|
||||
"currentStep": null,
|
||||
"assignmentGroupId": null,
|
||||
"confirmedAt": null,
|
||||
"itineraryUrl": null,
|
||||
"sideEffects": null,
|
||||
"dailyDifferences": [
|
||||
{
|
||||
"serviceDate": "2026-07-21",
|
||||
"differenceType": "ORDER_DATE_MISMATCH",
|
||||
"assignmentId": null,
|
||||
"assignmentSlotId": null,
|
||||
"passengerCount": null,
|
||||
"passengerCapacity": null,
|
||||
"capacityGap": null,
|
||||
"message": "订单当前日期与用车需求冻结日期不一致"
|
||||
},
|
||||
{
|
||||
"serviceDate": null,
|
||||
"differenceType": "HEADCOUNT_BASELINE_MISMATCH",
|
||||
"assignmentId": null,
|
||||
"assignmentSlotId": null,
|
||||
"passengerCount": 8,
|
||||
"passengerCapacity": null,
|
||||
"capacityGap": null,
|
||||
"message": "订单当前人数与用车需求冻结人数不一致"
|
||||
},
|
||||
{
|
||||
"serviceDate": "2026-07-22",
|
||||
"differenceType": "CAPACITY_INSUFFICIENT",
|
||||
"assignmentId": null,
|
||||
"assignmentSlotId": null,
|
||||
"passengerCount": 8,
|
||||
"passengerCapacity": 5,
|
||||
"capacityGap": 3,
|
||||
"message": "车辆载客量不足,已按每车司机占一座计算"
|
||||
}
|
||||
]
|
||||
},
|
||||
"traceId": "7db459fd-0b8e-4f29-9c46-4938f605041",
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 缺少某日车辆槽位
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 605041,
|
||||
"message": "订单行程或用车派单已变化,请按逐日差异处理后重试",
|
||||
"data": {
|
||||
"confirmed": false,
|
||||
"assignmentStatus": null,
|
||||
"stageCode": null,
|
||||
"stageLabel": null,
|
||||
"currentStep": null,
|
||||
"assignmentGroupId": null,
|
||||
"confirmedAt": null,
|
||||
"itineraryUrl": null,
|
||||
"sideEffects": null,
|
||||
"dailyDifferences": [
|
||||
{
|
||||
"serviceDate": "2026-07-23",
|
||||
"differenceType": "ASSIGNMENT_DATE_MISSING",
|
||||
"assignmentId": null,
|
||||
"assignmentSlotId": null,
|
||||
"passengerCount": null,
|
||||
"passengerCapacity": null,
|
||||
"capacityGap": null,
|
||||
"message": "该服务日缺少第2个车辆槽位派单"
|
||||
}
|
||||
]
|
||||
},
|
||||
"traceId": "7db459fd-0b8e-4f29-9c46-4938f605042",
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
## 六、前端统一处理顺序
|
||||
|
||||
1. 先判断业务 `code`,再读取端点自己的 `data`:
|
||||
- `code=200`:按对应成功模型处理;
|
||||
- `code=605041`:按对应失败模型读取 `dailyDifferences`;
|
||||
- 其他业务码:继续走既有错误处理。
|
||||
2. `605041` 时不要乐观更新:
|
||||
- create 不新增本地派单;
|
||||
- change 不替换旧派单;
|
||||
- confirm 不改为 `assigned` 或“已确认”。
|
||||
3. 展示顶层 `message`,并按 `dailyDifferences` 列出服务日、差异类型、人数、容量和缺口;字段为空时隐藏对应展示项。
|
||||
4. 重新请求最新订单和车务详情,避免继续使用操作前缓存。
|
||||
5. 用户修正订单日期、行程、人数或派单后,生成新的 `requestId` 再提交新动作;仅在同一次动作网络结果不确定时复用原 `requestId`。
|
||||
|
||||
## 七、其他直接错误分支
|
||||
|
||||
| 业务码 | 常见场景 | 前端处理 |
|
||||
|---|---|---|
|
||||
| `605009` | 派单不存在 | 刷新详情和看板,停止操作旧记录 |
|
||||
| `605020` | 当前状态不允许操作 | 刷新最新生命周期与操作能力 |
|
||||
| `605025` | 最终确认前缺少司机确认或有效凭证 | 引导先完成司机确认与凭证登记 |
|
||||
| `605001` / `605003` | 车辆或司机档期冲突 | 展示后端错误并重新选车/司机 |
|
||||
| `605036` | 跨常驻车未显式确认 | 二次提示后携带 `confirmCrossResident=true` 重试 |
|
||||
| `605041` | 订单、需求、行程、逐日派单、人数或容量基线不一致 | 使用当前端点的 `data.dailyDifferences` 展示并处理 |
|
||||
|
||||
请求字段为空、格式不正确或超过长度限制时走统一参数校验错误,前端应在发请求前完成同样约束。
|
||||
|
||||
## 八、不影响范围
|
||||
|
||||
- 不新增管理后台接口,三个接口的请求字段结构保持不变。
|
||||
- `holdMode=1` 的排车锁定、司机确认与凭证登记流程保持不变。
|
||||
- 不改变取消、司机拒接、驳回需求、撤销取消和提前完结的前端调用契约。
|
||||
- 不要求前端计算订单人数、逐日服务日期或车辆载客量,这些均由后端权威校验并返回差异。
|
||||
- 本文件只描述管理后台直接消费的 HTTP 契约,不包含服务间调用或发布实现细节。
|
||||
|
||||
## 九、验收状态与待补证据
|
||||
|
||||
已完成:
|
||||
|
||||
- 当前 worktree 中 create、change、confirm Controller 的 `605041` 强类型 `data` 静态核对。
|
||||
- `AssignmentWriteRespVO`、`ChangeAssignmentRespVO`、`ConfirmRespVO` 与 `dailyDifferences` 字段静态核对。
|
||||
- 三条失败路径不提交派单及其关联业务状态变化的源码顺序核对。
|
||||
|
||||
合并和部署后仍需补充:
|
||||
|
||||
- 三个接口在测试环境 OpenAPI 中的请求/响应模型截图或导出差异。
|
||||
- 经网关分别取得 create、change、confirm 的成功响应和 `605041` 真实响应,记录 HTTP 状态、业务码、`traceId` 与完整 `data`。
|
||||
- 对 `605041` 前后做测试业务数据对照,确认派单版本/状态、车辆司机占用、保险、对账和订单派定结果未发生变化。
|
||||
- 管理后台页面联调证据:差异列表展示、空字段处理、刷新行为、禁止乐观更新,以及修正后重新提交成功。
|
||||
@ -0,0 +1,58 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "5319"
|
||||
title: "团期配车就绪门禁与清零释放内部契约"
|
||||
consumer: "internal"
|
||||
change_type: "修改接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "not_required"
|
||||
frontend_status: "not_required"
|
||||
frontend_owner: ""
|
||||
frontend_ref: ""
|
||||
target_release: ""
|
||||
verified_at: ""
|
||||
status_note: "PR #5432 已合并 dev-v3(c3c78ea8f);fleet 4b1ab397 / order-v3 5bc5d247 已部署测试环境并经服务直连验证。internal 契约仅供服务间 Feign 消费,前端不涉及。"
|
||||
updated_at: "2026-08-03"
|
||||
base: "dev-v3"
|
||||
---
|
||||
|
||||
# 团期配车就绪门禁与清零释放内部契约(#5319)
|
||||
|
||||
> **服务**: hl-fleet-service(8087)/ hl-order-service-v3(8086)
|
||||
> **PR**: #5432
|
||||
> **Issue**: #5319
|
||||
> **日期**: 2026-08-03
|
||||
> **影响范围**: 团期配车重配就绪校验、整团清零/释放、vehicle_ready 双向回填(服务间 internal Feign)
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化
|
||||
|
||||
- Fleet `POST /internal/fleet/dispatch/group-batch/reconfigure` 重配前新增 order-v3 权威基线校验:
|
||||
- 服务日必须完整覆盖团期 departDate..endDate(缺首日/末日/中间日、越界、空洞原子失败)
|
||||
- 仅 `RESOURCE_PREPARING`(成团后资源配置阶段)可配,已流团/终态及出行后状态拒绝
|
||||
- 新增 `clearAll=true` 显式整团清零语义(软删全部活跃配车 + vehicle_ready=false)
|
||||
- `GROUP_READY` 意图按团期级 planVersion 去重(新表 fleet_group_dispatch_plan),相同计划重复提交不新增意图
|
||||
|
||||
## 变更接口
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 团期配车重配 | POST | `/internal/fleet/dispatch/group-batch/reconfigure`(Fleet,Feign 入向) | 修改接口 | 新增权威基线校验 + clearAll 清零;请求体新增 `clearAll` 字段(可空) |
|
||||
| 2 | 团期配车占用释放 | POST | `/internal/fleet/dispatch/group-batch/:groupBatchId/release`(Fleet,Feign 入向) | 新增接口 | 幂等软删全部活跃配车 + vehicle_ready=false |
|
||||
| 3 | 团期配车权威基线 | GET | `/v3/internal/group-batch/:groupBatchId/dispatch-baseline`(order-v3,Feign 出向) | 新增接口 | 返回服务日集合 + 生命周期可配判定;团期不存在返 589500 |
|
||||
| 4 | 重置配车就绪 | POST | `/v3/internal/group-batch/:groupBatchId/vehicle-ready-reset`(order-v3,Feign 出向) | 新增接口 | vehicle_ready=false 幂等重置 |
|
||||
| 5 | 回填配车就绪 | POST | `/v3/internal/group-batch/:groupBatchId/vehicle-ready`(order-v3,Feign 出向) | 修改接口 | 语义不变(置 true 幂等),配合清零/释放双向闭环 |
|
||||
|
||||
## 调用方动作(internal)
|
||||
|
||||
- order-v3 团期配车编排方:重配请求按权威服务日提交完整计划;需清零时置 `clearAll=true`(demands 可空)
|
||||
- 共享 DTO:`GroupBatchDispatchBaselineDTO`(hl-common-core)新增
|
||||
- 前端不消费任何 internal 端点,无前端动作
|
||||
|
||||
## 验证证据
|
||||
|
||||
- 定向测试:Fleet `GroupDispatchServiceTest`(29 用例)+ writer/effect/listener;order-v3 `GroupBatchServiceTest`/Outbox processor/service/listener/controller 契约
|
||||
- reactor verify:Fleet 2870 用例(仅 Testcontainers MySQL 无 Docker 环境失败,基线同样失败);order-v3 全量 verify 通过;fleet spotless:check 通过
|
||||
- 网关验证:internal 路径经网关 403 为设计行为(JwtAuthFilter 全局阻断);经服务直连验证 release=200、dispatch-baseline=589500(失败关闭)、vehicle-ready-reset=200、vehicle-ready=200(证据 `tmp/5319-gateway-evidence.md`)
|
||||
- 兼容性结论:既有 `vehicle-ready`(置 true)契约不变;重配请求在完整覆盖校验下语义收紧(此前不完整计划可提交),调用方须按权威服务日提交
|
||||
@ -0,0 +1,72 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "5336"
|
||||
title: "修改派单 requestId 持久幂等:同键重放冻结结果、不同载荷 605059"
|
||||
consumer: "admin"
|
||||
change_type: "修改接口"
|
||||
backend_status: "pending"
|
||||
gateway_status: "pending"
|
||||
frontend_status: "pending"
|
||||
frontend_owner: ""
|
||||
frontend_ref: ""
|
||||
target_release: ""
|
||||
verified_at: ""
|
||||
status_note: "后端 PR 待合并;部署与网关验证完成后更新状态。"
|
||||
updated_at: "2026-08-03"
|
||||
base: "dev-v3"
|
||||
---
|
||||
|
||||
# 修改派单 requestId 持久幂等:同键重放冻结结果、不同载荷 605059
|
||||
|
||||
> **服务**:`hl-fleet-service`
|
||||
>
|
||||
> **Issue**:[#5336](https://git.1814.love:8443/wx/HL/issues/5336)
|
||||
>
|
||||
> **影响范围**:管理后台「修改派单」接口的幂等语义与错误码
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化
|
||||
|
||||
- 以前:`change` 接口对 `requestId` 只有 Redis 10 秒快速防重;TTL 到期后**同一 requestId + 相同 payload 重放会再次执行改派**(取消旧切片、生成新 group/assignment ID 与生命周期副作用)。
|
||||
- 现在:`(operationType=CHANGE, requestId)` 在数据库持久唯一;成功后同键同 payload 重放**直接返回首次冻结结果,不再产生任何改派副作用**;同键不同 payload 返回 `605059`。
|
||||
- 前端/调用方以前以为的重放安全不再成立,**重试安全成为持久承诺**;任何新 `requestId` 的合法再次改派不受影响。
|
||||
|
||||
## 变更接口
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 修改派单 | POST | `/admin/fleet/assignments/:assignmentId/change` | 错误码与幂等语义 | 无请求/响应字段变化 |
|
||||
|
||||
## 变更详情
|
||||
|
||||
### 3.1 幂等语义(行为变化)
|
||||
|
||||
- 请求体字段(含 `requestId`)与响应字段**均不变**。
|
||||
- 首次成功后,相同 `requestId` + 相同业务载荷(规范化后)重放:返回首次冻结的完整响应(同 `assignmentId/assignmentSlotId/newAssignmentGroupId/...` 等),**不**生成新的 group/assignment、OperationLog、Outbox 或生命周期 intent。
|
||||
- Redis 10 秒快速防重仍保留,仅承担首次并发抑制,不再承担业务正确性;DB 唯一回执跨 TTL 兜底。
|
||||
- 同 `requestId` + 不同业务载荷:稳定失败,返回:
|
||||
|
||||
| code | 含义 |
|
||||
|---|---|
|
||||
| `605059` | 幂等请求标识已用于不同业务载荷 |
|
||||
|
||||
- `requestId` 大小写精确区分;首尾空白或超过 64 字符返回参数错误。
|
||||
- 新 `requestId` 的再次合法改派不受阻,正常形成新业务变更。
|
||||
|
||||
## 前端消费动作
|
||||
|
||||
1. 正常业务无需改动;重试逻辑可依赖持久幂等(同键同载荷重复提交不再重复改派)。
|
||||
2. 若收到 `605059`,说明同一 `requestId` 被用于不同内容,必须更换新 `requestId` 重新提交,不得原地重试。
|
||||
3. 前端双击/重放场景:响应内容与首次一致,无需重新拉取详情。
|
||||
|
||||
## 验证证据
|
||||
|
||||
- Fleet H2 集成:`AssignmentChangeReceiptServiceIntegrationTest` 8/8、迁移测试 2/2。
|
||||
- 真实 MySQL 8.0.33 + Redis + Spring AOP:`AssignmentChangeReceiptMysqlTest` 1/1(含跨 TTL 并发 exactly-once)。
|
||||
- `AssignmentServiceTest` 340/340;Fleet 全量 verify 结果以 PR 合入后工单回写为准。
|
||||
- oasdiff 与 Spring Cloud Contract 未配置,不能标记工具 PASS;以上为源码级字段对比与集成测试回退证据。
|
||||
|
||||
## Internal Feign / shared Java
|
||||
|
||||
无内部 Feign 或共享 Java 契约变化。
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户