比较提交
| 作者 | SHA1 | 提交日期 | |
|---|---|---|---|
|
|
de960761df | ||
|
|
ee1c4a3167 | ||
|
|
7c5301db9f | ||
|
|
da2707f63b | ||
|
|
4a3ec16a3d | ||
|
|
ffd0983958 | ||
|
|
f52ef24dc1 | ||
|
|
12a331d7c7 | ||
|
|
86059ce5f3 | ||
|
|
272b23b251 | ||
|
|
54ee10aa8d | ||
|
|
b931167499 | ||
|
|
52d9b49daa | ||
|
|
3ffef86227 | ||
|
|
120aee2b40 | ||
|
|
e38807ccc8 | ||
|
|
bd7f5a5e19 | ||
|
|
85caef620c | ||
|
|
23ef052327 | ||
|
|
f8a161fdf6 | ||
|
|
0e95ebd933 | ||
|
|
07990e1578 | ||
|
|
6cab15bab7 | ||
|
|
cbabad9a44 | ||
|
|
8d5c6943a5 | ||
|
|
b883ce7aa8 | ||
|
|
236cf8ff19 | ||
|
|
e1f5c2b9a2 | ||
|
|
ec0ec9ede1 | ||
|
|
a7b750cede | ||
|
|
736a17f084 | ||
|
|
cdeb340e7c | ||
|
|
86d9356d72 | ||
|
|
c7c8a7299c | ||
|
|
bae6a58183 | ||
|
|
ed0a97326f | ||
|
|
fe3354cce2 | ||
|
|
8e0c18658f | ||
|
|
e64aa1a054 | ||
|
|
e66c76f96b | ||
|
|
3b4e095f26 | ||
|
|
e8fbd19fc5 | ||
|
|
1e2f39e76f | ||
|
|
4d7d0d5e5c | ||
|
|
93d013f1d4 | ||
|
|
9891fee76e | ||
|
|
7a3c0d70b7 | ||
|
|
4877596de1 | ||
|
|
a78b546cce | ||
|
|
f4ed055581 | ||
|
|
614cad68e6 | ||
|
|
12fa3b7489 | ||
|
|
e97a559737 | ||
|
|
1b0bbad5b4 | ||
|
|
7b66c073b9 | ||
|
|
00321aecdb | ||
|
|
00f6bfd4a5 | ||
|
|
2e6884262d | ||
|
|
1208626f69 | ||
|
|
55fe54afa0 | ||
|
|
e492376528 | ||
|
|
67bfaa3993 |
+5
-1
@@ -5,7 +5,11 @@ title: "派车按行程日标记车费日期"
|
||||
consumer: "admin"
|
||||
backend: "verified"
|
||||
gateway: "verified"
|
||||
frontend: "pending"
|
||||
frontend: "implemented"
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@5eb8a46bee9a5a101371313e2088decd9ea843f2"
|
||||
updated_at: "2026-07-25T03:03:38.490Z"
|
||||
base: "dev-v3"
|
||||
generated: "2026-07-22T18:00:00+08:00"
|
||||
---
|
||||
+6
@@ -1,3 +1,9 @@
|
||||
---
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@b309f1672f4587d11aa6b8e86d0dd4ba043274d1"
|
||||
updated_at: "2026-07-25T03:11:01.515Z"
|
||||
---
|
||||
# 【前端待处理·管理后台】#5160 一名司机可绑定多辆常驻车
|
||||
|
||||
> **服务**: `hl-fleet-service`
|
||||
+6
@@ -1,3 +1,9 @@
|
||||
---
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@28a4a78888777a50b11c37a69d4bb42d43d0e552"
|
||||
updated_at: "2026-07-25T03:14:52.995Z"
|
||||
---
|
||||
# 房务配房价格模型收口为协议价与结算价
|
||||
|
||||
> **服务**: hl-order-service-v3
|
||||
+5
-1
@@ -5,7 +5,11 @@ title: "用车手动加急与派车看板状态颜色"
|
||||
consumer: "admin"
|
||||
backend: "verified"
|
||||
gateway: "verified"
|
||||
frontend: "pending"
|
||||
frontend: "implemented"
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@d07506cd3aa8a23cb2aa90f891eb853e1b7dd13f"
|
||||
updated_at: "2026-07-25T03:18:55.871Z"
|
||||
base: "dev-v3"
|
||||
generated: "2026-07-23T10:00:00+08:00"
|
||||
---
|
||||
@@ -1,11 +1,18 @@
|
||||
---
|
||||
schema: "hl-changelog/v1"
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "5187"
|
||||
title: "多车辆槽位原子批量派车与价格日历带价"
|
||||
consumer: "admin"
|
||||
backend: "verified"
|
||||
gateway: "verified"
|
||||
frontend: "pending"
|
||||
change_type: "新增接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "verified"
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@6479adf1caf2a5caeea08a24a42bacecbaaabd6a"
|
||||
target_release: "hl-ui/v2.1"
|
||||
verified_at: ""
|
||||
status_note: "前端 v2.1 已实现按 fleetItemIndex 的多槽位选择、批量提交和重复车辆/司机禁选;测试环境 9527 已提供对应源码,尚待登录态页面实操验收。"
|
||||
updated_at: "2026-07-24"
|
||||
base: "dev-v3"
|
||||
generated: "2026-07-23T15:38:00+08:00"
|
||||
---
|
||||
@@ -40,7 +47,7 @@ generated: "2026-07-23T15:38:00+08:00"
|
||||
- 同一次提交及其网络重试必须复用同一个 `requestId`;用户修改选择后主动再次提交应生成新值。
|
||||
- `holdMode=1` 表示排车中等待司机确认,`holdMode=0` 表示直接派定;整批模式必须一致。
|
||||
|
||||
## 二、新增原子批量派单接口
|
||||
## 变更接口
|
||||
|
||||
```http
|
||||
POST /admin/fleet/assignments/batch
|
||||
@@ -196,6 +203,30 @@ selectedSlots[fleetItemIndex] = {
|
||||
- 禁止用 `for` 循环调用旧单派接口;那会在中途失败时留下半批状态。
|
||||
- 成功后一次关闭弹窗并刷新看板;不得每成功一辆刷新一次。
|
||||
|
||||
#### 当前消费差距
|
||||
|
||||
- `useVehicleDriverPicker.js` 仍只维护一组 `selVehicle/selDriver`。
|
||||
- `AssignModalFooter.vue` 仍只展示一组车辆和司机,并按这一组决定按钮是否可用。
|
||||
- `useAssignFlow.js` 仍只调用 `createAssignment`,没有构造 `items[]`。
|
||||
- `src/api/fleet/board.js` 尚未封装 `POST /fleet/assignments/batch`。
|
||||
|
||||
#### 展示矩阵
|
||||
|
||||
| 场景 | “已选车辆”区域 | 候选/司机联动 | 主操作 |
|
||||
| --- | --- | --- | --- |
|
||||
| 尚未选择 | 显示 `已选车辆 0/N` 和 N 个待选槽位 | 提示先选择车辆 | 禁用,显示未完成组数 |
|
||||
| 已选一辆 | 槽位 01 显示车牌、车型、司机和移除操作,并成为当前编辑槽位 | 已选车辆标记不可重复;司机只写入当前槽位 | 未完成全部槽位时保持禁用 |
|
||||
| 继续多选 | 新车辆进入下一个待选 `fleetItemIndex`;其他已选槽位保持不变 | 已被其他槽位使用的车辆和司机不可重复选择 | 全部槽位完整后启用 |
|
||||
| 切换槽位 | 高亮当前编辑槽位;允许单独更换车辆、司机和价格 | 候选与司机面板切换到该槽位上下文 | 完整度实时更新 |
|
||||
| 搜索/筛选/翻页 | 已选区域固定可见,集合不丢失 | 只改变候选列表 | 状态保持 |
|
||||
| HOLD 完整 | 显示 `已选择 N/N 辆,司机 N/N` | 每槽位独立司机 | `下一步 · 发送给 N 名司机` |
|
||||
| DIRECT 完整 | 显示 `已选择 N/N 辆,司机 N/N` | 每槽位独立司机 | `直接派定 N 辆车` |
|
||||
| 批量失败 | 保留全部选择;高亮 `failedFleetItemIndex` | 允许修正失败槽位 | 原批次不产生部分成功 |
|
||||
| 批量成功 | 清空选择并关闭弹窗 | 看板只统一刷新一次 | 仅发送一次批量请求 |
|
||||
|
||||
“已选车辆”应作为车辆筛选与候选列表之间持续可见的紧凑区域,不得只在底栏显示最后一辆。
|
||||
选择数量不得超过当前需求的待派车辆槽位数;移除某一槽位不得重排或清空其他槽位。
|
||||
|
||||
### 3.2 车型价格日历自动带价
|
||||
|
||||
候选接口 `vehicles[].protocolPrice` 已返回所选车辆车型在服务开始日的价格日历单价。当前页面
|
||||
@@ -238,7 +269,7 @@ selectedSlots[fleetItemIndex] = {
|
||||
- [ ] 主动筛选“待派车”及重置行为正确。
|
||||
- [ ] 增加多槽位状态管理、批量请求映射、车型切换带价和默认空筛选的组件/组合式函数测试。
|
||||
|
||||
## 六、后端验证证据
|
||||
## 验证证据
|
||||
|
||||
- `mvn -pl hl-fleet-service spotless:check` 通过。
|
||||
- `AssignmentControllerTest + AssignmentServiceTest`:281 项通过。
|
||||
|
||||
+5
-1
@@ -5,7 +5,11 @@ title: "用车需求增加独立接机送机选择"
|
||||
consumer: "admin"
|
||||
backend: "verified"
|
||||
gateway: "verified"
|
||||
frontend: "pending"
|
||||
frontend: "claimed"
|
||||
frontend_status: "claimed"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: ""
|
||||
updated_at: "2026-07-25T03:19:00.677Z"
|
||||
base: "dev-v3"
|
||||
generated: "2026-07-23T17:39:06+08:00"
|
||||
---
|
||||
+6
@@ -1,3 +1,9 @@
|
||||
---
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@b619849eb3c71f2e466dec4539f577213c060db0"
|
||||
updated_at: "2026-07-25T03:25:59.312Z"
|
||||
---
|
||||
# 调整订单行程节点时间回显与修改(修改接口)
|
||||
|
||||
> 日期:2026-07-24
|
||||
文件差异内容过多而无法显示
加载差异
+9
-6
@@ -1,15 +1,18 @@
|
||||
---
|
||||
schema: "hl-changelog/v1"
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "5216"
|
||||
title: "派车看板补充槽位接送路线与就绪摘要"
|
||||
consumer: "admin"
|
||||
backend: "verified"
|
||||
gateway: "verified"
|
||||
frontend: "implemented"
|
||||
change_type: "修改接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "verified"
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@41f307090eccfdf3d06deabce8bc4f3d2be9a99a"
|
||||
updated_at: "2026-07-24T07:25:59.029Z"
|
||||
frontend_ref: "mmg/hl-ui@cd493f83a7881401552494fc5a90fbb87395131b"
|
||||
target_release: "hl-ui/v2.1"
|
||||
verified_at: ""
|
||||
status_note: "后端与网关已验证;前端 implemented 状态由前端消费线程维护,本次仅迁移 schema。"
|
||||
updated_at: "2026-07-25T03:29:38.101Z"
|
||||
base: "dev-v3"
|
||||
generated: "2026-07-24T14:24:00+08:00"
|
||||
---
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "5226"
|
||||
title: "用车需求提交后实时刷新派单看板"
|
||||
consumer: "admin"
|
||||
change_type: "修改接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "verified"
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@716d5e81311628f42d2ac1945089755b4264d47e"
|
||||
target_release: "hl-ui/v2.1"
|
||||
verified_at: ""
|
||||
status_note: "2026-07-24T17:12:49+08:00 后端已部署且网关 SSE 契约已验证;管理台仍待消费 fleet-board-changed,前端状态保持 pending。"
|
||||
updated_at: "2026-07-24T09:29:05.220Z"
|
||||
base: "origin/dev-v3"
|
||||
generated: "2026-07-24T16:41:16+08:00"
|
||||
---
|
||||
|
||||
# 用车需求提交后实时刷新派单看板
|
||||
|
||||
> 自动草稿不会代表已验证;完成实际测试后再更新 frontmatter。
|
||||
|
||||
## 关联
|
||||
|
||||
- Issue: [wx/HL#5226](https://git.1814.love:8443/wx/HL/issues/5226)
|
||||
- PR: [wx/HL#5232](https://git.1814.love:8443/wx/HL/pulls/5232)
|
||||
|
||||
## 变更接口
|
||||
|
||||
| 方法 | 路径 | 来源 |
|
||||
|---|---|---|
|
||||
| `POST` | `/internal/notification/fleet-board/broadcast` | Fleet 事务提交后调用 user-service 的内部广播端点 |
|
||||
| `GET` | `/ws/admin-msg/stream` | 既有 SSE 流新增命名事件 `fleet-board-changed` |
|
||||
|
||||
## 契约影响文件
|
||||
|
||||
- `hl-fleet-service/src/main/java/com/hulalv/fleet/board/port/vo/FleetBoardChangedFeignReqVO.java`
|
||||
- `hl-user-service/src/main/java/com/hulalv/user/notification/sse/vo/FleetBoardChangedReqVO.java`
|
||||
|
||||
## 前端/调用方动作
|
||||
|
||||
管理台继续复用现有 `/ws/admin-msg/stream` 连接,不新建第二条 EventSource。全局 SSE
|
||||
组合式函数新增命名事件监听:
|
||||
|
||||
```js
|
||||
eventSource.addEventListener('fleet-board-changed', onFleetBoardChanged)
|
||||
```
|
||||
|
||||
事件数据示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "FLEET_BOARD",
|
||||
"targetRoleKey": "VEHICLE_MANAGER",
|
||||
"orderId": "2079000000000000001",
|
||||
"requirementId": "2079000000000000101"
|
||||
}
|
||||
```
|
||||
|
||||
- 该事件是“看板数据已失效”信令,不承载订单行数据;收到后重新查询当前看板。
|
||||
- 只刷新 `getBoardSummary` 和当前页 `getBoardOrders`,保留状态、日期、车型、关键词、页码和展开状态。
|
||||
- 事件可能短时间连续到达,必须合并刷新并避免并发请求覆盖;不得每个事件各发一组请求。
|
||||
- `orderId/requirementId` 只用于定位和诊断,按 String 保存,不能转为 Number。
|
||||
- 页面不可见时先标记 dirty,恢复可见或 SSE 重连成功后刷新一次。
|
||||
- 刷新失败保留现有列表,不清空页面;沿用现有错误提示与下一次事件重试。
|
||||
|
||||
### 展示矩阵
|
||||
|
||||
| 场景 | 汇总卡 | 看板列表 | 筛选/页码 | 请求策略 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 页面可见,收到一次事件 | 重新查询 | 重新查询当前页 | 完整保留 | 合并为一轮刷新 |
|
||||
| 短时间收到多次事件 | 最终值更新一次 | 最终值更新一次 | 完整保留 | debounce/coalesce,禁止并发覆盖 |
|
||||
| 刷新进行中又收到事件 | 当前请求完成后再补一次 | 同左 | 完整保留 | 最多保留一个 pending refresh |
|
||||
| 页面隐藏时收到事件 | 暂不请求 | 暂不请求 | 完整保留 | 标记 dirty,恢复可见后刷新一次 |
|
||||
| SSE 重连成功 | 重新查询 | 重新查询当前页 | 完整保留 | 主动补偿一次,覆盖断线窗口 |
|
||||
| 查询失败 | 保留旧值 | 保留旧列表 | 完整保留 | 展示既有错误提示,等待重试 |
|
||||
| 非车务当前角色 | 不收到事件 | 不刷新 | 不变 | 后端仅投递 `VEHICLE_MANAGER` |
|
||||
|
||||
## 验证证据
|
||||
|
||||
- Fleet 定向测试:254 项通过,0 failure / 0 error。
|
||||
- User 定向测试:40 项通过,0 failure / 0 error。
|
||||
- `mvn -f hl-fleet-service/pom.xml spotless:check` 通过。
|
||||
- `mvn -pl hl-fleet-service -am verify` 通过。
|
||||
- `mvn -pl hl-user-service -am verify` 通过。
|
||||
- 事务语义:只有用车需求展开事务 `AFTER_COMMIT` 才广播;回滚不发事件,广播失败不阻断主流程。
|
||||
- 路由语义:事件名固定为 `fleet-board-changed`,仅投递当前角色为 `VEHICLE_MANAGER` 的连接。
|
||||
- 测试环境部署:`hl-user-service` 任务 `6d567b7f`、`hl-fleet-service` 任务 `561b1051`
|
||||
均成功,两个滚动实例分别恢复健康。
|
||||
- 网关/SSE:`GET /ws/admin-msg/stream` 返回 HTTP 200 和 `text/event-stream`;
|
||||
`8081/8181` 两实例内部广播均返回业务码 200,未带内部令牌返回 403。
|
||||
- 实际事件:当前角色为 `VEHICLE_MANAGER` 的连接收到 `fleet-board-changed`,
|
||||
`type=FLEET_BOARD`,`targetRoleKey=VEHICLE_MANAGER`,订单与需求 ID 按 String 到达。
|
||||
- 脱敏证据:`5226-gateway-sse.json`,SHA-256
|
||||
`00f3a2d441e9993ea7df706924fa792497170f1d4efee7ca190f741ca5844936`。
|
||||
- 兼容性结论:既有 SSE 事件和看板查询接口不变;未消费新命名事件的前端保持原行为。
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "5236"
|
||||
title: "用车接送改由大交通默认驱动"
|
||||
consumer: "admin"
|
||||
change_type: "修改接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "verified"
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@85851ad68d427e161d9342525af4567c1d108d5f"
|
||||
target_release: ""
|
||||
verified_at: ""
|
||||
status_note: "后端 PR #5241 已合并至 dev-v3(9578f78d5),order/fleet 已部署测试环境(b57ce915/0da3b9e6),双实例 internal 契约与网关汇总/列表/详情已验证;前端仍为 pending,待删除车辆接送开关并改用大交通摘要。"
|
||||
updated_at: "2026-07-24T10:59:50.144Z"
|
||||
base: "dev-v3"
|
||||
---
|
||||
|
||||
# 用车接送改由大交通默认驱动
|
||||
|
||||
## 关联
|
||||
|
||||
- Issue: [wx/HL#5236](https://git.1814.love:8443/wx/HL/issues/5236)
|
||||
- Backend PR: [wx/HL#5241](https://git.1814.love:8443/wx/HL/pulls/5241)
|
||||
- Supersedes: [wx/HL#5193](https://git.1814.love:8443/wx/HL/issues/5193) 中“用车需求独立决定接送”的业务口径
|
||||
- 服务: `hl-order-service-v3`、`hl-fleet-service`
|
||||
- 前端仓库/分支: `mmg/hl-ui` / `v2.1`
|
||||
|
||||
## 关键变化
|
||||
|
||||
车辆安排不再让定制师重复选择“是否需要接机/接站”和“是否需要送机/送站”。
|
||||
接送结论由订单当前大交通批次直接决定:
|
||||
|
||||
- `ARRIVAL` 批次聚合接机/接站。
|
||||
- `DEPARTURE` 批次聚合送机/送站。
|
||||
- 同方向任一批 `pickupRequired=true`,该方向为需要接送。
|
||||
- 同方向全部批次均为 `false`,该方向为客人自理。
|
||||
- 没有该方向批次时返回 `null`,表示未知。
|
||||
- 新建大交通未传 `pickupRequired` 时默认保存为 `true`;显式 `false` 保持客人自理。
|
||||
|
||||
用车需求和订单调整中的 `pickupRequired`、`dropoffRequired` 字段暂不删除,继续兼容旧请求和回显,
|
||||
但不再覆盖实时大交通结论。
|
||||
|
||||
## 变更接口
|
||||
|
||||
| 方法 | 路径 | 变化 |
|
||||
|---|---|---|
|
||||
| `POST` | `/v3/admin/order/:id/transport-plan/add` | 新增大交通未传 `pickupRequired` 时默认 `true` |
|
||||
| `POST` | `/v3/admin/order/:id/transport-plan/batch` | 批量替换中每个未传值的批次默认 `true` |
|
||||
| `POST` | `/v3/admin/order/:id/transport-plan/:planId/edit` | 未传该字段时保留原值;显式值正常覆盖 |
|
||||
| `PUT` | `/v3/admin/order/:id/vehicle-requirement` | 两个接送字段改为兼容字段,不再是权威来源 |
|
||||
| `GET` | `/v3/admin/order/:id/adjustment/snapshot?scope=VEHICLE_REQ` | 继续通过 `vehicleTransportSummary` 返回大交通批次摘要 |
|
||||
| `POST` | `/v3/admin/order/:id/adjustment/submit` | `updates.vehicleRequirement` 中两个接送字段仅兼容接收 |
|
||||
| `GET` | `/admin/fleet/board/orders` | 卡片接送就绪状态改为按实时大交通方向聚合 |
|
||||
| `GET` | `/admin/fleet/board/orders/:orderId` | `transport.pickupRequired/dropoffRequired` 只取实时大交通聚合 |
|
||||
|
||||
小程序内部大交通新增与批量接口使用相同默认规则,但本 changelog 的前端处理范围仅为管理后台。
|
||||
|
||||
## 字段语义
|
||||
|
||||
### 大交通请求 `pickupRequired`
|
||||
|
||||
| 场景 | 入参 | 保存结果 |
|
||||
|---|---|---|
|
||||
| 新增单批/批量批次未传 | 字段省略或 `null` | `true` |
|
||||
| 新增单批/批量批次显式自理 | `false` | `false` |
|
||||
| 编辑既有批次未传 | 字段省略或 `null` | 保留原值 |
|
||||
| 编辑既有批次显式修改 | `true` / `false` | 按提交值覆盖 |
|
||||
|
||||
数据库列仍为 `TINYINT(1) NOT NULL`,仅把新记录的数据库默认值从 `0` 改为 `1`,不回填或改写历史行。
|
||||
|
||||
### 派单详情响应
|
||||
|
||||
| 字段 | 类型 | 空值 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `transport.pickupRequired` | `Boolean` | 无 ARRIVAL 批次时为 `null` | ARRIVAL 批次聚合 |
|
||||
| `transport.dropoffRequired` | `Boolean` | 无 DEPARTURE 批次时为 `null` | DEPARTURE 批次聚合 |
|
||||
| `transport.arrive/depart` | `Object/null` | 对应整团批次不存在时为 `null` | 到达/返程整团大交通 |
|
||||
| `transport.batches[]` | `Object[]` | 无分批时为空数组 | 分批大交通,保留方向、时间、站点和关联出行人 |
|
||||
|
||||
## 前端处理
|
||||
|
||||
1. 删除“调整订单 → 车辆安排”中的“是否需要接机/接站”和“是否需要送机/送站”两个开关。
|
||||
2. 提交用车需求或订单调整时,不再主动提交 `pickupRequired`、`dropoffRequired`。
|
||||
3. 车辆安排页直接展示 `vehicleTransportSummary.arrivals[]` 与 `departures[]`;继续使用其中的
|
||||
`direction`、`time`、`station`、`transportNo`、`pickupRequired`、`pickupRemark` 和
|
||||
`travelerNames[]`。
|
||||
4. 派单看板和详情不得回退到 `vehicleRequirement.pickupRequired/dropoffRequired`;
|
||||
使用看板接送摘要与详情 `transport.pickupRequired/dropoffRequired`。
|
||||
5. 雪花 ID 仍按字符串处理,本次没有字段删除、类型变化或新增错误码。
|
||||
|
||||
## 展示矩阵
|
||||
|
||||
| 大交通场景 | 接机/接站 | 送机/送站 | 页面展示 |
|
||||
|---|---:|---:|---|
|
||||
| ARRIVAL 任一批需要,DEPARTURE 全部自理 | `true` | `false` | 分方向显示“平台接 / 客人自理” |
|
||||
| ARRIVAL 全部自理,DEPARTURE 任一批需要 | `false` | `true` | 分方向显示“客人自理 / 平台送” |
|
||||
| 同方向多批混合 | `true` | 按返程批次聚合 | 明细保留每个批次及关联出行人 |
|
||||
| 只有 ARRIVAL | 按到达批次聚合 | `null` | 返程显示未提供,不回退旧用车需求 |
|
||||
| 只有 DEPARTURE | `null` | 按返程批次聚合 | 到达显示未提供,不回退旧用车需求 |
|
||||
| 完全无大交通 | `null` | `null` | 显示“暂无接送机时间” |
|
||||
|
||||
## 验证证据
|
||||
|
||||
- Order 定向测试 57 项通过。
|
||||
- Fleet `BoardOrderServiceTest` 50 项通过。
|
||||
- 调整/需求/出行人兼容链路 357 项通过。
|
||||
- 调整快照完整字段断言 `AdjustmentServiceTest` 10 项通过。
|
||||
- `mvn -pl hl-order-service-v3 -am verify` 通过。
|
||||
- Order 模块 Surefire 汇总 6646 项,0 失败、0 错误、28 跳过。
|
||||
- `mvn -pl hl-fleet-service -am verify` 通过:Fleet 模块 2361 项,0 失败、0 错误、1 跳过。
|
||||
- Fleet `spotless:check` 与 `git diff --check` 通过。
|
||||
- OpenAPI/oasdiff: `not_configured`,使用源码字段/语义比对和测试作为 fallback。
|
||||
- Spring Cloud Contract: `not_configured`,使用 order-v3 生产者与 Fleet 消费者测试作为 fallback。
|
||||
- 后端 PR #5241 已合并,merge commit 为 `9578f78d5f0241db502d94b22283cbff0a351c53`。
|
||||
- 测试环境部署任务:order `b57ce915`、fleet `0da3b9e6`。
|
||||
- `order_transport_plan.pickup_required` 已验证为 `TINYINT(1) NOT NULL DEFAULT 1`,Flyway
|
||||
`20260724.001` 执行成功。
|
||||
- order `8086/8186` 均通过 `/v3/internal/order/orders/:orderId/transport` 与批量看板上下文实测;
|
||||
`true/false/null` 三态及 ARRIVAL/DEPARTURE 分方向聚合符合字段语义。
|
||||
- 测试网关 `/admin/fleet/board/summary`、`/orders`、`/orders/:orderId` 均返回成功;
|
||||
详情连续 4 次通过,运行时证据为 `D:/work2/hl-workflow/.tmp/5236-gateway-evidence.json`。
|
||||
文件差异内容过多而无法显示
加载差异
@@ -0,0 +1,427 @@
|
||||
---
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@adff10ea74198f4e89a1488e631463bedbcd4eea"
|
||||
updated_at: "2026-07-25T03:37:10.934Z"
|
||||
---
|
||||
# 【修改接口·管理后台】酒店候选补齐房型结算价 (#5237)
|
||||
|
||||
> **PR**: #5240 | **服务**: hl-order-service-v3 | **更新时间**: 2026-07-25 10:58
|
||||
|
||||
## 1. 接口背景
|
||||
|
||||
管理后台酒店候选列表原来只在候选酒店顶层返回 `protoPrice`,前端无法确认这个价格来自哪个真实房型,也拿不到同一房型同一天的结算价。配房时如果只看房型列表或自行匹配最低价,容易把协议价和结算价口径拆到不同房型。
|
||||
|
||||
本次在候选酒店顶层补齐:
|
||||
|
||||
- `protoPriceRoomTypeId`:产生顶层 `protoPrice` 的真实房型 ID。
|
||||
- `settlementPrice`:与 `protoPriceRoomTypeId` 同一房型、同一天的结算价。
|
||||
|
||||
顶层 `protoPrice`、`protoPriceRoomTypeId`、`settlementPrice` 是同一代表房型口径。未维护结算价时 `settlementPrice = null`,不会用协议价兜底。
|
||||
|
||||
## 2. 变更清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 查询酒店候选(4 场景统一入口) | GET | `/v3/admin/hotel-candidates` | 修改接口 | 候选酒店项新增 `protoPriceRoomTypeId`、`settlementPrice` 两个出参字段;入参不变。 |
|
||||
|
||||
## 3. 接口详情
|
||||
|
||||
### 3.1 查询酒店候选(4 场景统一入口)
|
||||
|
||||
- **使用场景**:管理后台在订单维度查询某一晚的候选酒店,用于配房选酒店、回显当前已配酒店、按产品池/定制师点名/资源库候选排序。
|
||||
- **认证**:需要管理后台 JWT。
|
||||
- **幂等性**:只读查询,幂等。
|
||||
- **限流**:无接口级特殊限流;受网关与服务通用限流策略约束。
|
||||
|
||||
## 4. 接口入参
|
||||
|
||||
### 4.1 路径参数 / Query 参数
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `orderId` | String | 是 | 订单 ID。后端 Long,JSON/Query 建议按字符串传,避免长 ID 精度问题。 |
|
||||
| `dayNumber` | Integer | 否 | 第几天,从 1 开始;用于推算 `stayDate = departDate + dayNumber - 1`。最小值 1。 |
|
||||
| `stayDate` | String | 否 | 入住日期,格式 `yyyy-MM-dd`;直接指定时优先于 `dayNumber` 推算。 |
|
||||
| `city` | String | 否 | 城市代码或城市名;未传且非关键词模式时默认不按城市限制。 |
|
||||
| `keyword` | String | 否 | 关键词;非空时跨城/省匹配酒店名、城市、省份、地址,此时 `city` 可不传。 |
|
||||
| `limit` | Integer | 否 | 返回候选条数上限,默认 30,最小 1,最大 50。 |
|
||||
| `roomCategory` | String | 否 | 房型字典 code。 |
|
||||
| `roomCount` | Integer | 否 | 需要的房间数;最小 1。 |
|
||||
| `preferredHotelId` | String | 否 | 定制师指定的优先酒店 ID。后端 Long,建议字符串传。 |
|
||||
| `requirementId` | String | 否 | 用房需求 ID;传入后将该需求 days JSON 中当前天的酒店候选作为定制师指定候选。后端 Long,建议字符串传。 |
|
||||
|
||||
### 4.2 请求体字段
|
||||
|
||||
GET 接口无请求体。
|
||||
|
||||
## 5. 出参字段
|
||||
|
||||
统一响应结构:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `code` | Integer | 业务状态码,成功为 `200`。 |
|
||||
| `message` | String | 响应消息,成功为 `成功`。 |
|
||||
| `data` | Object | 酒店候选查询出参。 |
|
||||
| `traceId` | String | 链路追踪 ID,可能为空。 |
|
||||
| `success` | Boolean | `code == 200` 时为 `true`。 |
|
||||
|
||||
`data` 字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `stayDate` | String | 入住日期,格式 `yyyy-MM-dd`。 |
|
||||
| `city` | String / null | 本次查询使用的城市;关键词模式或默认不限城市时可为 `null`。 |
|
||||
| `productType` | String | 产品类型:`CORE` / `GROUP` / `CUSTOM`。 |
|
||||
| `candidates` | Array | 候选酒店列表,已按产品类型分流排序。 |
|
||||
|
||||
`data.candidates[]` 字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `hotelId` | String | 酒店 ID。 |
|
||||
| `hotelName` | String | 酒店名称。 |
|
||||
| `level` | String / null | 酒店等级。 |
|
||||
| `form` | String / null | 住宿形态。 |
|
||||
| `address` | String / null | 地址。 |
|
||||
| `tags` | Array<String> | 运营标签;无标签时为空数组或 `null`。 |
|
||||
| `contactPerson` | String / null | 联系人。 |
|
||||
| `contactWechat` | String / null | 联系微信。 |
|
||||
| `settleType` | String / null | 结算类型,取值见 §6.1。 |
|
||||
| `city` | String / null | 酒店所在城市。 |
|
||||
| `district` | String / null | 酒店所在区/县。 |
|
||||
| `roomTypes` | Array | 该酒店当日真实房型列表;无房型数据时为空数组。 |
|
||||
| `protoPrice` | String / null | 代表房型协议价。与 `protoPriceRoomTypeId`、顶层 `settlementPrice` 同一房型同一天。 |
|
||||
| `protoPriceRoomTypeId` | String / null | 产生顶层 `protoPrice` 的真实房型 ID。无有效可售协议价时为 `null`。 |
|
||||
| `settlementPrice` | String / null | 与 `protoPriceRoomTypeId` 同一房型、同一天的结算价。未维护时为 `null`,不会用 `protoPrice` 兜底。 |
|
||||
| `todayAvailable` | Integer / null | 今日全房型可用房数合计。 |
|
||||
| `availFreshness` | String / null | 可用数数据时效:`fresh` / `stale` / `never_checked`。 |
|
||||
| `lastCheckedAt` | String / null | 最近一次核房时间,格式 `yyyy-MM-dd'T'HH:mm:ss`。 |
|
||||
| `matchedRoomTypeAvailable` | Integer / null | 匹配房型今日可用数。 |
|
||||
| `matchedRoomTypeId` | String / null | 匹配的房型 ID。 |
|
||||
| `matchedRoomTypeLabel` | String / null | 匹配的房型中文。 |
|
||||
| `quickPickEnabled` | Boolean / null | 是否支持快速配房。 |
|
||||
| `quickPickDisabledReason` | String / null | 置灰原因。 |
|
||||
| `isPoolMatch` | Boolean / null | 是否产品池内。 |
|
||||
| `poolMatchBadge` | Object / null | 产品池内徽章。 |
|
||||
| `isConsultantRecommended` | Boolean / null | 是否被定制师点名。 |
|
||||
| `consultantRecommendBadge` | Object / null | 定制师点名徽章。 |
|
||||
| `historyMatchScore` | Number / null | 历史匹配度,范围 0-1。 |
|
||||
| `score` | Number / null | 排序分数。 |
|
||||
| `recommendation` | String / null | 推荐理由。 |
|
||||
| `recommended` | Boolean / null | 是否为推荐候选。 |
|
||||
| `recommendSource` | String / null | 推荐来源,见 §6.4。 |
|
||||
| `historyScoreStub` | Boolean / null | 历史命中分数是否为 stub。 |
|
||||
| `isCurrentlyAssigned` | Boolean / null | 是否为本天当前已配酒店。 |
|
||||
| `assignedRoomTypeId` | String / null | 本天当前已配的房型 ID;`isCurrentlyAssigned=true` 时可用于预填原房型。 |
|
||||
|
||||
`data.candidates[].roomTypes[]` 字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `roomTypeId` | String | 房型 ID。 |
|
||||
| `name` | String / null | 房型名称。 |
|
||||
| `roomCategory` | String / null | 房型分类字典值。 |
|
||||
| `bedType` | String / null | 床型,已按字典尽量翻译;字典缺失时可回退为 code。 |
|
||||
| `maxOccupancy` | Integer / null | 最大入住人数。 |
|
||||
| `available` | Integer / null | 今日可用房数;`unlimited=true` 时为 `null`,语义为不限。 |
|
||||
| `unlimited` | Boolean | 是否不限库存。 |
|
||||
| `stock` | Integer / null | 当前可用房;`unlimited=true` 时为 `null`。 |
|
||||
| `protocolPrice` | String / null | 该房型当日协议价。 |
|
||||
| `settlementPrice` | String / null | 该房型当日结算价。 |
|
||||
| `basePrice` | String / null | 标价/挂牌价。 |
|
||||
| `inventoryStatus` | String | 库存状态,见 §6.2。 |
|
||||
|
||||
徽章对象字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `label` | String | 中文徽章文字。 |
|
||||
| `color` | String | 徽章色,见 §6.5。 |
|
||||
| `tooltip` | String | 悬浮提示。 |
|
||||
|
||||
## 6. 枚举 / 数据字典
|
||||
|
||||
### 6.1 `settleType`
|
||||
|
||||
**所属字段**:`data.candidates[].settleType` | **类型**:String | **必填**:否
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
|----|------|------|
|
||||
| `cash` | 现付 | 到店或线下现金类结算。 |
|
||||
| `sign` | 签单 | 供应商签单结算。 |
|
||||
| `company` | 公司付 | 公司统一付款结算。 |
|
||||
|
||||
### 6.2 `inventoryStatus`
|
||||
|
||||
**所属字段**:`data.candidates[].roomTypes[].inventoryStatus` | **类型**:String | **必填**:是
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
|----|------|------|
|
||||
| `AVAILABLE` | 可售 | 有余量,或 `unlimited=true` 不限库存。 |
|
||||
| `FULL` | 满房 | 有日历记录,但库存为 0。 |
|
||||
| `CLOSED` | 未开放 | 无该日价格日历记录。 |
|
||||
|
||||
### 6.3 `availFreshness`
|
||||
|
||||
**所属字段**:`data.candidates[].availFreshness` | **类型**:String | **必填**:否
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
|----|------|------|
|
||||
| `fresh` | 最新 | 可用于快速配房判断。 |
|
||||
| `stale` | 过期 | 核房数据过期。 |
|
||||
| `never_checked` | 从未核房 | 无可用核房数据。 |
|
||||
|
||||
### 6.4 `recommendSource`
|
||||
|
||||
**所属字段**:`data.candidates[].recommendSource` | **类型**:String | **必填**:否
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
|----|------|------|
|
||||
| `PRODUCT_POOL` | 产品池 | 来自产品池候选。 |
|
||||
| `CONSULTANT` | 定制师点名 | 来自定制师指定候选。 |
|
||||
| `RESOURCE_LIB` | 资源库 | 来自资源库候选。 |
|
||||
|
||||
### 6.5 `Badge.color`
|
||||
|
||||
**所属字段**:`poolMatchBadge.color` / `consultantRecommendBadge.color` | **类型**:String | **必填**:否
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
|----|------|------|
|
||||
| `blue` | 蓝色 | 普通推荐或池内标识。 |
|
||||
| `gold` | 金色 | 高优先级推荐标识。 |
|
||||
| `gray` | 灰色 | 弱提示标识。 |
|
||||
|
||||
## 7. 错误码
|
||||
|
||||
| code | 含义 | 触发场景 |
|
||||
|------|------|----------|
|
||||
| `200` | 成功 | 查询成功。 |
|
||||
| `400` | 参数错误 | `orderId` 为空、`dayNumber < 1`、`limit` 超出 1-50、`roomCount < 1`、日期格式不是 `yyyy-MM-dd` 等参数绑定或校验失败。 |
|
||||
| `401` | 未认证 | JWT 缺失或无效。 |
|
||||
| `403` | 无权限 | 当前账号无权访问该管理后台接口或订单数据。 |
|
||||
| `581007` | 订单不存在 | `orderId` 对应订单不存在。 |
|
||||
| `500` | 服务内部错误 | 非预期异常。 |
|
||||
|
||||
## 8. 示例(3 组:典型 / 边界 / 异常)
|
||||
|
||||
### 8.1 典型成功
|
||||
|
||||
**请求**:
|
||||
|
||||
```http
|
||||
GET /v3/admin/hotel-candidates?orderId=100001&dayNumber=1&stayDate=2026-07-25&limit=30&roomCount=2 HTTP/1.1
|
||||
Authorization: Bearer <admin-jwt>
|
||||
```
|
||||
|
||||
无请求体。
|
||||
|
||||
**响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"stayDate": "2026-07-25",
|
||||
"city": null,
|
||||
"productType": "CORE",
|
||||
"candidates": [
|
||||
{
|
||||
"hotelId": "2023714929877450753",
|
||||
"hotelName": "测试酒店",
|
||||
"level": "舒适型",
|
||||
"form": "HOTEL",
|
||||
"address": "呼伦贝尔市海拉尔区测试路 1 号",
|
||||
"tags": ["协议酒店"],
|
||||
"contactPerson": "张经理",
|
||||
"contactWechat": "hotel_mgr",
|
||||
"settleType": "sign",
|
||||
"city": "呼伦贝尔市",
|
||||
"district": "海拉尔区",
|
||||
"protoPrice": "280.00",
|
||||
"protoPriceRoomTypeId": "2023727403196502017",
|
||||
"settlementPrice": "279.00",
|
||||
"todayAvailable": 7,
|
||||
"availFreshness": "fresh",
|
||||
"lastCheckedAt": null,
|
||||
"matchedRoomTypeAvailable": 7,
|
||||
"matchedRoomTypeId": "2023727403196502017",
|
||||
"matchedRoomTypeLabel": "豪华大床房",
|
||||
"quickPickEnabled": true,
|
||||
"quickPickDisabledReason": null,
|
||||
"isPoolMatch": true,
|
||||
"poolMatchBadge": {
|
||||
"label": "产品池内",
|
||||
"color": "blue",
|
||||
"tooltip": "本酒店在产品池内,优先推荐"
|
||||
},
|
||||
"isConsultantRecommended": false,
|
||||
"consultantRecommendBadge": null,
|
||||
"historyMatchScore": 0.85,
|
||||
"score": 1185.0,
|
||||
"recommendation": "池内 · 历史合作 8 单成功率 95%",
|
||||
"recommended": true,
|
||||
"recommendSource": "PRODUCT_POOL",
|
||||
"historyScoreStub": true,
|
||||
"isCurrentlyAssigned": false,
|
||||
"assignedRoomTypeId": null,
|
||||
"roomTypes": [
|
||||
{
|
||||
"roomTypeId": "2023727403196502017",
|
||||
"name": "豪华大床房",
|
||||
"roomCategory": "KING",
|
||||
"bedType": "大床",
|
||||
"maxOccupancy": 2,
|
||||
"available": 7,
|
||||
"unlimited": false,
|
||||
"stock": 7,
|
||||
"protocolPrice": "280.00",
|
||||
"settlementPrice": "279.00",
|
||||
"basePrice": "568.00",
|
||||
"inventoryStatus": "AVAILABLE"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"traceId": "trace-20260725-0001",
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 边界情况
|
||||
|
||||
**场景说明**:代表房型有协议价但未维护结算价,顶层 `settlementPrice` 返回 `null`,不使用 `protoPrice` 兜底。
|
||||
|
||||
**请求**:
|
||||
|
||||
```http
|
||||
GET /v3/admin/hotel-candidates?orderId=100001&stayDate=2026-07-25&keyword=%E6%B5%B7%E6%8B%89%E5%B0%94&limit=1 HTTP/1.1
|
||||
Authorization: Bearer <admin-jwt>
|
||||
```
|
||||
|
||||
无请求体。
|
||||
|
||||
**响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"stayDate": "2026-07-25",
|
||||
"city": null,
|
||||
"productType": "CUSTOM",
|
||||
"candidates": [
|
||||
{
|
||||
"hotelId": "2023714929877450753",
|
||||
"hotelName": "测试酒店",
|
||||
"settleType": "cash",
|
||||
"protoPrice": "280.00",
|
||||
"protoPriceRoomTypeId": "2023727403196502017",
|
||||
"settlementPrice": null,
|
||||
"roomTypes": [
|
||||
{
|
||||
"roomTypeId": "2023727403196502017",
|
||||
"name": "豪华大床房",
|
||||
"available": 7,
|
||||
"unlimited": false,
|
||||
"protocolPrice": "280.00",
|
||||
"settlementPrice": null,
|
||||
"basePrice": "568.00",
|
||||
"inventoryStatus": "AVAILABLE"
|
||||
}
|
||||
],
|
||||
"quickPickEnabled": true,
|
||||
"recommended": true,
|
||||
"recommendSource": "RESOURCE_LIB"
|
||||
}
|
||||
]
|
||||
},
|
||||
"traceId": "trace-20260725-0002",
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 业务失败(异常)
|
||||
|
||||
**场景说明**:`orderId` 未传,触发参数校验失败。
|
||||
|
||||
**请求**:
|
||||
|
||||
```http
|
||||
GET /v3/admin/hotel-candidates?stayDate=2026-07-25 HTTP/1.1
|
||||
Authorization: Bearer <admin-jwt>
|
||||
```
|
||||
|
||||
无请求体。
|
||||
|
||||
**响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "orderId 不能为空",
|
||||
"data": null,
|
||||
"traceId": "trace-20260725-0003",
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
## 9. 业务边界
|
||||
|
||||
- **适用场景**:管理后台按订单和入住日查询酒店候选;`stayDate` 可直接传,也可通过 `dayNumber` 和订单出发日推算。
|
||||
- **不适用场景**:不用于前端直接查询内部资源服务;本文只描述管理后台 `/v3/admin/hotel-candidates`。
|
||||
- **特殊边界**:顶层 `protoPrice`、`protoPriceRoomTypeId`、`settlementPrice` 必须按同一代表房型理解;`settlementPrice = null` 表示该代表房型当天未维护结算价。
|
||||
- **特殊边界**:`roomTypes[].settlementPrice` 是每个房型自己的当日结算价;顶层 `settlementPrice` 只对应 `protoPriceRoomTypeId` 指向的代表房型。
|
||||
|
||||
## 10. 修改前后对比
|
||||
|
||||
### 10.1 字段级对比
|
||||
|
||||
| 字段 | 改前 | 改后 |
|
||||
|------|------|------|
|
||||
| `data.candidates[].protoPriceRoomTypeId` | 不返回 | 返回产生顶层 `protoPrice` 的真实房型 ID;无有效可售协议价为 `null`。 |
|
||||
| `data.candidates[].settlementPrice` | 不返回 | 返回与 `protoPriceRoomTypeId` 同一房型、同一天的结算价;未维护为 `null`。 |
|
||||
| `data.candidates[].protoPrice` | 已返回,但无法判断来自哪个房型 | 仍返回原字段,并与新增的 `protoPriceRoomTypeId`、顶层 `settlementPrice` 组成同一代表房型口径。 |
|
||||
|
||||
### 10.2 行为级对比
|
||||
|
||||
| 行为 | 改前 | 改后 |
|
||||
|------|------|------|
|
||||
| 候选酒店顶层价格展示 | 只能拿到代表协议价 `protoPrice`。 | 可同时拿到代表协议价、代表房型 ID、该代表房型结算价。 |
|
||||
| 结算价为空 | 顶层没有结算价字段。 | 顶层 `settlementPrice` 返回 `null`;不使用 `protoPrice` 兜底。 |
|
||||
|
||||
## 11. 影响评估 / 回滚
|
||||
|
||||
### 11.1 影响评估
|
||||
|
||||
- **是否破坏向后兼容**:否。只新增出参字段,已有字段名、类型、入参不变。
|
||||
- **前端是否必须同步上线**:否。老前端可忽略新增字段;需要展示或回填结算价的页面可读取新增字段。
|
||||
- **影响已有数据**:无数据迁移要求;历史未维护结算价的房型按 `settlementPrice = null` 返回。
|
||||
|
||||
### 11.2 回滚方案
|
||||
|
||||
- **回滚方式**:回滚 PR #5240 后,顶层新增字段不再返回。
|
||||
- **回滚后清理**:无前端数据清理要求。
|
||||
- **回滚耗时**:按常规服务回滚流程处理。
|
||||
|
||||
## 12. 注意事项
|
||||
|
||||
- 前端读取顶层 `settlementPrice` 时,不要把 `null` 当作 `protoPrice`;`null` 表示未维护结算价。
|
||||
- 如需定位价格来自哪个房型,使用顶层 `protoPriceRoomTypeId` 去匹配 `roomTypes[].roomTypeId`。
|
||||
- 金额和长 ID 在响应 JSON 中按字符串处理,例如 `"280.00"`、`"2023727403196502017"`。
|
||||
|
||||
## 13. 关联 / 联系人
|
||||
|
||||
### 13.1 链接
|
||||
|
||||
- **Issue**: [#5237](https://git.1814.love:8443/wx/HL/issues/5237)
|
||||
- **PR**: [#5240](https://git.1814.love:8443/wx/HL/pulls/5240)
|
||||
- **Merge commit**: [dc6e2ef](https://git.1814.love:8443/wx/HL/commit/dc6e2ef6c2b49bd503353814f85723566d4413c6)
|
||||
|
||||
### 13.2 联系人
|
||||
|
||||
- **后端负责人**: @yst
|
||||
@@ -0,0 +1,388 @@
|
||||
---
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@5a155c42395a7abd66c78789b225d6af86bb7fbd"
|
||||
updated_at: "2026-07-25T03:42:03.625Z"
|
||||
---
|
||||
# 【修改接口·管理后台】核单门票来源类型统一 (#5238)
|
||||
|
||||
> **PR**: #5242 | **服务**: hl-order-service-v3 | **更新时间**: 2026-07-25 10:03
|
||||
|
||||
## 1. 接口背景
|
||||
|
||||
核单 Step2 门票/游玩项目页签中,手工补充的门票行此前在查询出参中使用 `CUSTOM_ASSIGNMENT`。为避免前端按不同 Tab 或来源类型做额外分支,本次将查询出参的手工门票来源统一为 `MANUAL`,中文名统一为 `手工项目`;保存接口同步允许直接提交 `MANUAL`。
|
||||
|
||||
## 2. 变更清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | Step 2 查询门票核单明细 | GET | `/v3/admin/order/{orderId}/settlement/step2` | 修改接口 | 手工/自定义门票行的 `sourceType` 统一返回 `MANUAL`,`sourceTypeName` 返回 `手工项目` |
|
||||
| 2 | Step 2 录门票核单明细 | PUT | `/v3/admin/order/{orderId}/settlement/step2` | 修改接口 | `items[].sourceType` 新增允许 `MANUAL`;旧 `CUSTOM_ASSIGNMENT` 入参继续兼容 |
|
||||
|
||||
## 3. 接口详情
|
||||
|
||||
### 3.1 Step 2 查询门票核单明细
|
||||
|
||||
- **方法**:GET
|
||||
- **路径**:`/v3/admin/order/{orderId}/settlement/step2`
|
||||
- **接口名**:`listTicket`
|
||||
- **ApiOperation**:Step 2 查询门票核单明细
|
||||
- **使用场景**:进入核单 Step2 门票/游玩项目页签,或保存成功后回读页面明细。
|
||||
- **认证**:需要管理后台 JWT。
|
||||
- **幂等性**:幂等,只读查询。
|
||||
- **限流**:无单接口额外限流。
|
||||
- **响应结构**:`data` 为 `TicketItemVO[]`。
|
||||
|
||||
### 3.2 Step 2 录门票核单明细
|
||||
|
||||
- **方法**:PUT
|
||||
- **路径**:`/v3/admin/order/{orderId}/settlement/step2`
|
||||
- **接口名**:`saveTicket`
|
||||
- **ApiOperation**:Step 2 录门票核单明细
|
||||
- **使用场景**:保存核单 Step2 门票/游玩项目明细,包含派生门票行和手工补充门票行。
|
||||
- **认证**:需要管理后台 JWT。
|
||||
- **幂等性**:全量替换保存;同一份 `items` 重复提交后,以最后一次提交结果为准。
|
||||
- **限流**:无单接口额外限流。
|
||||
- **请求体兼容**:推荐使用 `{ "items": [...] }`;历史数组 body `[...]` 仍兼容。
|
||||
- **响应结构**:`data` 为 `SettlementTicketSaveRespVO`。
|
||||
|
||||
## 4. 接口入参
|
||||
|
||||
### 4.1 路径参数 / Query 参数
|
||||
|
||||
| 接口 | 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|------|
|
||||
| GET / PUT | `orderId` | string | 是 | 订单 ID,长整型字符串 |
|
||||
|
||||
两个接口均无 Query 参数。
|
||||
|
||||
### 4.2 GET 请求体字段
|
||||
|
||||
GET 无请求体。
|
||||
|
||||
### 4.3 PUT 请求体字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|
||||
|------|------|------|------|----------|
|
||||
| `items` | array | 是 | 门票/游玩项目明细行数组,全量替换保存 | 不允许为 `null` |
|
||||
| `items[].id` | string | 否 | 已存在行 ID;新增行可不传 | 长整型字符串 |
|
||||
| `items[].sourceType` | string | 是 | 来源类型;手工门票推荐传 `MANUAL` | `SCENIC_ASSIGNMENT` / `ACTIVITY_ASSIGNMENT` / `MANUAL` / `CUSTOM_ASSIGNMENT` |
|
||||
| `items[].sourceTypeName` | string | 否 | 来源类型中文名,仅展示字段;保存时可不传 | 最大 32 字符 |
|
||||
| `items[].scenicAssignmentId` | string/null | 否 | 来源 assignment ID;手工项目传 `null` | 长整型字符串或 `null` |
|
||||
| `items[].dayNumber` | integer/null | 否 | 行程第几天;保存后以回读值为准 | 从 1 开始 |
|
||||
| `items[].dayDate` | string | 是 | 行程日期 | `yyyy-MM-dd` |
|
||||
| `items[].scenicName` | string | 是 | 景区/游玩项目名称 | 1-200 字符 |
|
||||
| `items[].specName` | string/null | 否 | 规格/票型名称 | 最大 128 字符 |
|
||||
| `items[].ticketCount` | integer | 是 | 实际购票数量;套餐含门票但无额外成本时可填 0 | 整数 |
|
||||
| `items[].ticketUnitPrice` | number/null | 否 | 参考成本单价,单位元 | 小数 |
|
||||
| `items[].sellPrice` | number/null | 否 | 客户成交单价,单位元 | `>= 0` |
|
||||
| `items[].totalAmount` | number/null | 否 | 客户成交小计,单位元 | `>= 0` |
|
||||
| `items[].plannedCost` | number | 是 | 计划成本,单位元 | `>= 0` |
|
||||
| `items[].actualCost` | number | 是 | 实际成本,单位元 | `>= 0` |
|
||||
| `items[].paymentMethod` | string | 否 | 付款方式;不传时按公司付款处理 | `SIGNED` / `COMPANY_PAID` / `CASH_PAID` |
|
||||
| `items[].paymentMethodName` | string | 否 | 付款方式中文名,仅展示字段;保存时可不传 | 最大 32 字符 |
|
||||
| `items[].voucherUrls` | array | 否 | 凭证图片 URL 数组 | 字符串数组 |
|
||||
| `items[].remark` | string/null | 否 | 备注 | 最大 500 字符 |
|
||||
|
||||
## 5. 出参字段
|
||||
|
||||
### 5.1 GET 响应字段:`TicketItemVO[]`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `code` | integer | 业务状态码,成功为 `200` |
|
||||
| `message` | string | 响应消息 |
|
||||
| `success` | boolean | 是否成功 |
|
||||
| `data` | array | 门票/游玩项目明细行数组 |
|
||||
| `data[].id` | string/null | 核单明细行 ID;未持久化派生行可能为 `null` |
|
||||
| `data[].sourceType` | string | 来源类型;手工/自定义门票行本次统一返回 `MANUAL` |
|
||||
| `data[].sourceTypeName` | string/null | 来源类型中文名;`MANUAL` 返回 `手工项目` |
|
||||
| `data[].scenicAssignmentId` | string/null | 来源 assignment ID;手工项目为 `null` |
|
||||
| `data[].dayNumber` | integer/null | 行程第几天 |
|
||||
| `data[].dayDate` | string | 行程日期,`yyyy-MM-dd` |
|
||||
| `data[].scenicName` | string | 景区/游玩项目名称 |
|
||||
| `data[].specName` | string/null | 规格/票型名称 |
|
||||
| `data[].ticketCount` | integer | 实际购票数量 |
|
||||
| `data[].ticketUnitPrice` | number/null | 参考成本单价,单位元 |
|
||||
| `data[].sellPrice` | number/null | 客户成交单价,单位元 |
|
||||
| `data[].totalAmount` | number/null | 客户成交小计,单位元 |
|
||||
| `data[].plannedCost` | number | 计划成本,单位元 |
|
||||
| `data[].actualCost` | number | 实际成本,单位元 |
|
||||
| `data[].paymentMethod` | string/null | 付款方式 |
|
||||
| `data[].paymentMethodName` | string/null | 付款方式中文名 |
|
||||
| `data[].voucherUrls` | array | 凭证图片 URL 数组 |
|
||||
| `data[].remark` | string/null | 备注 |
|
||||
|
||||
### 5.2 PUT 响应字段:`SettlementTicketSaveRespVO`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `code` | integer | 业务状态码,成功为 `200` |
|
||||
| `message` | string | 响应消息 |
|
||||
| `success` | boolean | 是否成功 |
|
||||
| `data.addedIds` | string[] | 本次保存新增的核单明细行 ID 列表 |
|
||||
| `data.updatedIds` | string[] | 本次保存更新的核单明细行 ID 列表;当前全量替换语义下通常为空数组 |
|
||||
| `data.deletedIds` | string[] | 本次保存删除的核单明细行 ID 列表;当前返回通常为空数组 |
|
||||
| `data.totalActualCost` | string | 保存后 Step2 实际成本合计,单位元 |
|
||||
|
||||
## 6. 枚举 / 数据字典
|
||||
|
||||
### 6.1 `sourceType`
|
||||
|
||||
**所属字段**:`items[].sourceType`、`data[].sourceType` | **类型**:String | **PUT 必填**:是 | **GET 必返**:是
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
|----|------|------|
|
||||
| `SCENIC_ASSIGNMENT` | 景区 | 景区派生来源行;查询和保存语义不变 |
|
||||
| `ACTIVITY_ASSIGNMENT` | 游玩项目 | 游玩项目派生来源行;查询和保存语义不变 |
|
||||
| `MANUAL` | 手工项目 | 本次推荐值;查询手工/自定义门票行统一返回该值,保存接口也允许提交该值 |
|
||||
| `CUSTOM_ASSIGNMENT` | 手工项目(旧入参兼容) | 仅用于兼容旧保存请求;查询响应不再返回该值 |
|
||||
|
||||
### 6.2 `sourceTypeName`
|
||||
|
||||
**所属字段**:`items[].sourceTypeName`、`data[].sourceTypeName` | **类型**:String | **必填**:否
|
||||
|
||||
| sourceType | sourceTypeName | 说明 |
|
||||
|------------|----------------|------|
|
||||
| `SCENIC_ASSIGNMENT` | `景区` | 景区派生来源行 |
|
||||
| `ACTIVITY_ASSIGNMENT` | `游玩项目` | 游玩项目派生来源行 |
|
||||
| `MANUAL` | `手工项目` | 手工/自定义门票行统一展示名 |
|
||||
| `CUSTOM_ASSIGNMENT` | `手工项目` | 旧保存请求兼容;保存成功后回读为 `MANUAL` / `手工项目` |
|
||||
| `null` / 未知值 | `null` | 查询行为不变,不新增兜底文案 |
|
||||
|
||||
### 6.3 `paymentMethod`
|
||||
|
||||
**所属字段**:`items[].paymentMethod`、`data[].paymentMethod` | **类型**:String | **必填**:否
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
|----|------|------|
|
||||
| `SIGNED` | 签单 | 现场签单 |
|
||||
| `COMPANY_PAID` | 公司付款 | 公司统一付款;未传 `paymentMethod` 时按该值处理 |
|
||||
| `CASH_PAID` | 现付 | 现场现金/线下现付 |
|
||||
|
||||
## 7. 错误码
|
||||
|
||||
| HTTP 状态 / code | 含义 | 触发场景 |
|
||||
|------------------|------|----------|
|
||||
| `200` / `200` | 成功 | GET 查询成功或 PUT 保存成功 |
|
||||
| `200` / `401` | 未授权 | 缺少有效的管理后台 `Authorization` 头 |
|
||||
| `400` / `400` | 请求参数非法 | `sourceType` 不在 `SCENIC_ASSIGNMENT` / `ACTIVITY_ASSIGNMENT` / `MANUAL` / `CUSTOM_ASSIGNMENT` 内,或请求体结构不符合要求 |
|
||||
| `200` / `584011` | 当前核单状态不允许录门票核单 | PUT 保存时订单不是可录门票核单的状态 |
|
||||
|
||||
### 7.1 错误结构
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "sourceType 必须是 SCENIC_ASSIGNMENT / ACTIVITY_ASSIGNMENT / MANUAL / CUSTOM_ASSIGNMENT 之一",
|
||||
"data": null,
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 示例(3 组:典型 / 边界 / 异常)
|
||||
|
||||
### 8.1 典型成功:GET 返回手工项目为 MANUAL
|
||||
|
||||
**请求**:
|
||||
|
||||
```http
|
||||
GET /v3/admin/order/2079576729147338754/settlement/step2
|
||||
Authorization: Bearer {token}
|
||||
```
|
||||
|
||||
GET 无请求体。
|
||||
|
||||
**响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "2080186487600025601",
|
||||
"sourceType": "MANUAL",
|
||||
"sourceTypeName": "手工项目",
|
||||
"scenicAssignmentId": null,
|
||||
"dayNumber": 2,
|
||||
"dayDate": "2026-07-22",
|
||||
"scenicName": "临时补充门票",
|
||||
"specName": "成人票",
|
||||
"ticketCount": 2,
|
||||
"ticketUnitPrice": 30.00,
|
||||
"sellPrice": 50.00,
|
||||
"totalAmount": 100.00,
|
||||
"plannedCost": 60.00,
|
||||
"actualCost": 60.00,
|
||||
"paymentMethod": "COMPANY_PAID",
|
||||
"paymentMethodName": "公司付款",
|
||||
"voucherUrls": [],
|
||||
"remark": "现场补充"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 边界成功:查询结果原样 PUT
|
||||
|
||||
**场景说明**:前端可把 GET 回来的 `MANUAL` 行原样放入 `items` 后提交;保存成功后再次 GET 仍返回 `MANUAL` / `手工项目`。
|
||||
|
||||
**请求**:
|
||||
|
||||
```http
|
||||
PUT /v3/admin/order/2079576729147338754/settlement/step2
|
||||
Authorization: Bearer {token}
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "2080186487600025601",
|
||||
"sourceType": "MANUAL",
|
||||
"sourceTypeName": "手工项目",
|
||||
"scenicAssignmentId": null,
|
||||
"dayNumber": 2,
|
||||
"dayDate": "2026-07-22",
|
||||
"scenicName": "临时补充门票",
|
||||
"specName": "成人票",
|
||||
"ticketCount": 2,
|
||||
"ticketUnitPrice": 30.00,
|
||||
"sellPrice": 50.00,
|
||||
"totalAmount": 100.00,
|
||||
"plannedCost": 60.00,
|
||||
"actualCost": 60.00,
|
||||
"paymentMethod": "COMPANY_PAID",
|
||||
"paymentMethodName": "公司付款",
|
||||
"voucherUrls": [],
|
||||
"remark": "现场补充"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"success": true,
|
||||
"data": {
|
||||
"addedIds": ["2080186500000000001"],
|
||||
"updatedIds": [],
|
||||
"deletedIds": [],
|
||||
"totalActualCost": "60.00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 业务失败:非法 sourceType
|
||||
|
||||
**场景说明**:`items[].sourceType` 传入未定义值时仍按参数非法处理。
|
||||
|
||||
**请求**:
|
||||
|
||||
```http
|
||||
PUT /v3/admin/order/2079576729147338754/settlement/step2
|
||||
Authorization: Bearer {token}
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"sourceType": "TAB_MANUAL",
|
||||
"scenicAssignmentId": null,
|
||||
"dayDate": "2026-07-22",
|
||||
"scenicName": "临时补充门票",
|
||||
"specName": "成人票",
|
||||
"ticketCount": 1,
|
||||
"ticketUnitPrice": 0,
|
||||
"sellPrice": 0,
|
||||
"totalAmount": 0,
|
||||
"plannedCost": 0,
|
||||
"actualCost": 0,
|
||||
"paymentMethod": "COMPANY_PAID",
|
||||
"voucherUrls": [],
|
||||
"remark": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "sourceType 必须是 SCENIC_ASSIGNMENT / ACTIVITY_ASSIGNMENT / MANUAL / CUSTOM_ASSIGNMENT 之一",
|
||||
"data": null,
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
## 9. 业务边界
|
||||
|
||||
- **适用场景**:核单 Step2 门票/游玩项目页签查询、保存门票明细时使用。
|
||||
- **手工项目保存**:新增或编辑手工门票行时,`items[].sourceType` 推荐传 `MANUAL`,`scenicAssignmentId` 可传 `null`。
|
||||
- **旧入参兼容**:旧页面继续传 `CUSTOM_ASSIGNMENT` 仍可保存;保存成功后再次查询会返回 `MANUAL`。
|
||||
- **查询结果原样提交**:GET 返回的 `MANUAL` 行可原样进入 PUT 的 `items`。
|
||||
- **未变化范围**:`SCENIC_ASSIGNMENT`、`ACTIVITY_ASSIGNMENT` 的查询和保存语义不变;`null` / 未知来源的查询兜底行为不变。
|
||||
- **不适用场景**:人员费用、住宿、餐食、其他支出接口没有本次契约变化。
|
||||
|
||||
## 10. 修改前后对比
|
||||
|
||||
### 10.1 字段级对比
|
||||
|
||||
| 字段 | 修改前 | 修改后 |
|
||||
|------|--------|--------|
|
||||
| GET `data[].sourceType` | 手工/自定义门票行返回 `CUSTOM_ASSIGNMENT` | 手工/自定义门票行统一返回 `MANUAL` |
|
||||
| GET `data[].sourceTypeName` | 手工/自定义门票行可能按旧来源展示 | 手工/自定义门票行统一返回 `手工项目` |
|
||||
| PUT `items[].sourceType` | 允许 `SCENIC_ASSIGNMENT` / `ACTIVITY_ASSIGNMENT` / `CUSTOM_ASSIGNMENT` | 允许 `SCENIC_ASSIGNMENT` / `ACTIVITY_ASSIGNMENT` / `MANUAL` / `CUSTOM_ASSIGNMENT` |
|
||||
|
||||
### 10.2 行为级对比
|
||||
|
||||
| 行为 | 修改前 | 修改后 |
|
||||
|------|--------|--------|
|
||||
| 查询手工门票行 | 前端需要识别 `CUSTOM_ASSIGNMENT` | 前端按 `MANUAL` 识别手工项目 |
|
||||
| 保存手工门票行 | 前端需要把手工 Tab 转成 `CUSTOM_ASSIGNMENT` | 前端可直接提交 `MANUAL` |
|
||||
| 查询结果原样保存 | GET 的旧来源值与页面手工 Tab 值可能不一致 | GET 结果可原样 PUT |
|
||||
| 旧请求兼容 | 旧 `CUSTOM_ASSIGNMENT` 入参可保存 | 继续可保存,回读统一为 `MANUAL` |
|
||||
|
||||
## 11. 影响评估 / 回滚
|
||||
|
||||
### 11.1 影响评估
|
||||
|
||||
- **是否破坏向后兼容**:否。PUT 继续兼容旧 `CUSTOM_ASSIGNMENT` 入参;GET 只统一手工门票来源的展示值。
|
||||
- **前端是否必须同步上线**:否。旧保存请求仍可用;但前端可清理 `MANUAL` 与 `CUSTOM_ASSIGNMENT` 互转逻辑。
|
||||
- **影响已有数据**:不需要前端处理历史数据;页面以后端返回的 `MANUAL` 为准。
|
||||
|
||||
### 11.2 回滚方案
|
||||
|
||||
- 如接口回滚,前端需恢复兼容 GET 返回 `CUSTOM_ASSIGNMENT` 的判断。
|
||||
- 回滚后不要把 GET 查询结果中的 `sourceType` 假定为一定可原样提交。
|
||||
|
||||
## 12. 注意事项
|
||||
|
||||
- 前端不要再按 Tab 名称把手工项目强制转换成 `CUSTOM_ASSIGNMENT`;新增手工行可以直接传 `MANUAL`。
|
||||
- 前端如有 `sourceType === "CUSTOM_ASSIGNMENT"` 才展示手工项目的判断,需要同步兼容或改为判断 `MANUAL`。
|
||||
- `CUSTOM_ASSIGNMENT` 仅作为旧保存请求兼容值保留,不应再作为新页面查询展示值。
|
||||
- `sourceTypeName` 是展示字段,保存时可不传;保存后以再次查询结果为准。
|
||||
- 非法 `sourceType` 仍会返回参数非法,不新增兜底保存。
|
||||
|
||||
## 13. 关联 / 联系人
|
||||
|
||||
### 13.1 链接
|
||||
|
||||
- **Issue**: [#5238](https://git.1814.love:8443/wx/HL/issues/5238)
|
||||
- **PR**: [#5242](https://git.1814.love:8443/wx/HL/pulls/5242)
|
||||
- **Merge commit**: [bfb28a258](https://git.1814.love:8443/wx/HL/commit/bfb28a258)
|
||||
|
||||
### 13.2 联系人
|
||||
|
||||
- **后端负责人**: @yst
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "5244"
|
||||
title: "派单详情分别返回接送说明与通用备注"
|
||||
consumer: "admin"
|
||||
change_type: "修改接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "verified"
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@ede7d025e2d6d0d90e570d0bfa5d90f588431842"
|
||||
target_release: ""
|
||||
verified_at: ""
|
||||
status_note: "后端 PR #5246 已合并并部署;前端需在派单 Step1 大交通卡片分别渲染两个字段。"
|
||||
updated_at: "2026-07-25T01:24:40.528Z"
|
||||
base: "dev-v3"
|
||||
generated: "2026-07-25T09:05:18+08:00"
|
||||
---
|
||||
|
||||
# 车务派单详情:分别返回接送说明与通用备注
|
||||
|
||||
> **服务**: `hl-order-service-v3`、`hl-fleet-service`
|
||||
>
|
||||
> **工单**: [wx/HL#5244](https://git.1814.love:8443/wx/HL/issues/5244)
|
||||
>
|
||||
> **后端 PR**: [wx/HL#5246](https://git.1814.love:8443/wx/HL/pulls/5246)
|
||||
>
|
||||
> **影响范围**: 车务管理 → 派车看板 → 派单弹窗 Step1 → 大交通
|
||||
|
||||
## 业务口径
|
||||
|
||||
`pickupRemark` 与 `remark` 是两个独立字段,不得合并、互相覆盖或只取其中一个:
|
||||
|
||||
- `pickupRemark`:接机/送机说明;ARRIVAL 展示为“接机说明”,DEPARTURE 展示为“送机说明”。
|
||||
- `remark`:大交通通用备注,展示为“备注”。
|
||||
- 整团 `arrive/depart` 与分批 `batches[]` 使用同一字段口径。
|
||||
- 任一字段为 `null` 或空白时,只隐藏该字段对应的展示行,不影响另一字段。
|
||||
|
||||
## 变更接口
|
||||
|
||||
### 管理后台
|
||||
|
||||
```http
|
||||
GET /admin/fleet/board/orders/:orderId
|
||||
```
|
||||
|
||||
`data.transport.arrive`、`data.transport.depart` 与 `data.transport.batches[]` 均包含:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `pickupRemark` | `String/null` | 否 | 接机/送机说明 |
|
||||
| `remark` | `String/null` | 否 | 大交通通用备注;既有字段继续保留 |
|
||||
|
||||
响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"transport": {
|
||||
"arrive": {
|
||||
"direction": "ARRIVAL",
|
||||
"pickupRemark": "到达出口举牌接机",
|
||||
"remark": "航班可能延误"
|
||||
},
|
||||
"depart": {
|
||||
"direction": "DEPARTURE",
|
||||
"pickupRemark": "提前三小时送机",
|
||||
"remark": "请再次确认航站楼"
|
||||
},
|
||||
"batches": [
|
||||
{
|
||||
"direction": "ARRIVAL",
|
||||
"pickupRemark": "分批接机说明",
|
||||
"remark": "分批通用备注"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 内部契约
|
||||
|
||||
```http
|
||||
GET /v3/internal/order/orders/:orderId/fleet-detail-context
|
||||
```
|
||||
|
||||
order-v3 → fleet 的共享 `OrderTransportForFleetDTO` 在整团段与分批段均独立传递
|
||||
`pickupRemark`、`remark`。这是兼容性增量:路径、HTTP 方法、既有字段、枚举、错误码及
|
||||
`pickupRequired` 三态口径均不变。
|
||||
|
||||
## 前端展示矩阵
|
||||
|
||||
| 方向/模式 | `pickupRemark` | `remark` | 页面展示 |
|
||||
| --- | --- | --- | --- |
|
||||
| ARRIVAL,整团或分批 | 有 | 有 | 分别显示“接机说明”和“备注” |
|
||||
| DEPARTURE,整团或分批 | 有 | 有 | 分别显示“送机说明”和“备注” |
|
||||
| 任一方向 | 有 | 空 | 只显示接机/送机说明 |
|
||||
| 任一方向 | 空 | 有 | 只显示备注 |
|
||||
| 任一方向 | 空 | 空 | 两行均不显示 |
|
||||
|
||||
前端不得根据 `pickupRequired` 推导说明文本,也不得用一个字段回填另一个字段。
|
||||
|
||||
## 前端处理清单
|
||||
|
||||
- [ ] 派单弹窗 Step1 大交通卡片读取 `pickupRemark`,按方向显示“接机说明”或“送机说明”。
|
||||
- [ ] 通用备注继续读取 `remark`,与接机/送机说明分行展示。
|
||||
- [ ] 同时覆盖 `arrive`、`depart`、`batches[]`。
|
||||
- [ ] 对 `null`、空字符串和纯空白字符串使用单字段空态规则。
|
||||
- [ ] 不显示 `travelerIds` 等内部关联字段;既有出行人脱敏规则不变。
|
||||
|
||||
## 契约验证状态
|
||||
|
||||
- OpenAPI/oasdiff:`not_configured`。项目当前未配置稳定 Swagger2 → OAS3 导出与 oasdiff 基线。
|
||||
- 消费者契约/Spring Cloud Contract:`not_configured`。项目当前未配置 SCC。
|
||||
- fallback:源码与 Codemap 影响比对、order-v3 生产者测试、fleet 消费者/Controller 测试以及完整 reactor 验证。
|
||||
- 本次没有临时安装 oasdiff 或 Spring Cloud Contract 依赖。
|
||||
|
||||
## 验证证据
|
||||
|
||||
- 合并提交:`ca3c5c7310ddc142398382644a40ab57d951248e`。
|
||||
- 定向生产者/消费者测试:88 项通过。
|
||||
- 影响范围测试:25 个 reactor 模块全部通过。
|
||||
- Fleet 完整验证:2361 项测试,0 失败、0 错误、1 跳过;Spotless 606 个 Java 文件通过。
|
||||
- 测试部署:
|
||||
- order-v3 任务 `a5916436`,8086/8186 双实例成功;
|
||||
- fleet 任务 `a198456e`,8087/8187 双实例成功。
|
||||
- 部署面板与 Nacos 均确认两个服务 2/2 running、healthy、enabled;部署后日志新增错误匹配为 0。
|
||||
- 经测试网关验证真实团单:列表与详情 HTTP/业务码均为 200,`relatedDetailReady=true`;
|
||||
ARRIVAL、DEPARTURE 均同时返回非空且取值不同的 `pickupRemark`、`remark`。
|
||||
|
||||
## 不影响范围
|
||||
|
||||
- 不修改 `D:/work2/hl-ui`。
|
||||
- 不修改大交通录入、接送默认值、接送需求聚合、派车状态机或历史数据。
|
||||
- 不新增 DDL,不清理、不回填存量大交通备注。
|
||||
|
||||
> 后端与网关已验证;`frontend_status: pending` 表示等待前端真实领取,不代表页面已实现、发布或验证。
|
||||
@@ -0,0 +1,218 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "5245"
|
||||
title: "行程短链预览与同槽位改派解析"
|
||||
consumer: "admin"
|
||||
change_type: "修改接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "verified"
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@cd8aff9b0c6499a1dee1b9c3ca00ddbceb4b5aed"
|
||||
target_release: ""
|
||||
verified_at: ""
|
||||
status_note: "后端已部署并完成网关验证;用户验收发现排车页缺少新增车辆槽位入口,前端已退回 claimed 继续修复。"
|
||||
updated_at: "2026-07-26T01:23:05.110Z"
|
||||
base: "dev-v3"
|
||||
---
|
||||
|
||||
# 车务:行程短链预览与同槽位改派解析
|
||||
|
||||
> **服务**: `hl-fleet-service`
|
||||
>
|
||||
> **工单**: [wx/HL#5245](https://git.1814.love:8443/wx/HL/issues/5245)
|
||||
>
|
||||
> **后端 PR**: [wx/HL#5249](https://git.1814.love:8443/wx/HL/pulls/5249)、
|
||||
> [wx/HL#5250](https://git.1814.love:8443/wx/HL/pulls/5250)
|
||||
>
|
||||
> **影响范围**: 车务管理 → 派车弹窗通知预览、车辆/司机批量选择、派单详情
|
||||
|
||||
## 关键变化
|
||||
|
||||
- 通知模板预览中的 `itinerary.url` 会为当前派车组即时创建或复用稳定短链,
|
||||
例如 `https://hr.example.com/s/Dabc1234`,不再把完整 HMAC token URL 或“派车后生成”占位文案放进预览正文。
|
||||
- 既有短链和完整 token 长链在原派车组失效后,只允许解析到同一订单、同一
|
||||
`assignmentSlotId` 的唯一当前有效派车组;跨订单、跨槽位、无有效派单或同槽位存在多个
|
||||
active 派车组时继续返回 `605308`。
|
||||
- 批量派单和详情多司机字段是既有契约,本次明确前端消费口径:一次提交 `items[]`,详情展示
|
||||
`activeAssignments[]`,不得只处理兼容代表字段 `currentAssignment`。
|
||||
- 排车页必须提供“+ 添加车辆槽位”入口。新增槽位不是替换“车辆槽位 1”,而是追加一个可独立
|
||||
选择车辆和司机的草稿槽位;多个槽位统一映射为批量派单 `items[]`。
|
||||
|
||||
## 变更接口
|
||||
|
||||
| 方法 | 路径 | 本次口径 |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/admin/fleet/message-templates/<templateId>/render` | 请求新增可选 `assignmentGroupId`;有效派车组即时创建/复用稳定短链;旧前端未传时仅在订单、车辆、司机唯一定位一个 active 组时兼容 |
|
||||
| `GET` | `/app/h5/s/<code>` | 继续生成短时 token 并重定向;同槽位改派后的解析由行程接口完成 |
|
||||
| `GET` | `/app/h5/itinerary/<token>` | 原组失效后仅回退同订单、同稳定槽位的唯一 active 派车组 |
|
||||
| `POST` | `/admin/fleet/assignments/batch` | 既有:按 `items[]` 一次提交多个车辆/司机槽位 |
|
||||
| `GET` | `/admin/fleet/board/orders/<orderId>` | 既有:按 `activeAssignments[]` 返回全部当前有效派车组 |
|
||||
|
||||
## 1. 通知模板预览
|
||||
|
||||
```http
|
||||
POST /admin/fleet/message-templates/<templateId>/render
|
||||
```
|
||||
|
||||
请求新增可选字段 `assignmentGroupId`,响应结构不变。前端在预览包含
|
||||
`itinerary.url` 或 `itinerary.code` 的模板时,应传入当前派车组 ID;后端仅为兼容旧前端,
|
||||
在 `orderId` + `vehicleId` + `driverId` 唯一定位一个 active 派车组时允许省略:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `orderId` | `string` | 是 | 订单雪花 ID |
|
||||
| `vehicleId` | `string` | 是 | 当前派车组车辆雪花 ID |
|
||||
| `driverId` | `string` | 是 | 当前派车组司机雪花 ID |
|
||||
| `assignmentGroupId` | `string` | 行程预览时强烈建议 | 派车组雪花 ID;取自批量派单响应,多车多司机场景必须按槽位传入 |
|
||||
|
||||
派车组有效时,预览会即时创建或复用该组短链:
|
||||
|
||||
```yaml
|
||||
code: 200
|
||||
data:
|
||||
renderedBody: "请查看行程:https://hr.example.com/s/Dabc1234"
|
||||
variablesUsed:
|
||||
- "itinerary.url"
|
||||
```
|
||||
|
||||
未传 `assignmentGroupId` 且订单、车辆、司机无法唯一定位 active 派车组,或显式派车组无效时,
|
||||
`itinerary.url` 使用“行程链接暂不可用,请联系车务确认”,`itinerary.code` 为空字符串。
|
||||
短链配置、注册或数据库失败时接口直接返回错误,不静默降级为占位文案;任何场景都不会回退或
|
||||
暴露完整 HMAC URL。同一派车组通过显式 ID 或兼容定位重复预览、发送、重试时复用同一短链。
|
||||
|
||||
## 2. 同稳定槽位改派后的旧链接
|
||||
|
||||
短链先通过 `/app/h5/s/<code>` 重定向到短时 token;短链与直接保存的完整 token 最终都进入
|
||||
`/app/h5/itinerary/<token>`,因此使用同一组回退规则:
|
||||
|
||||
| token 原派单与当前派单 | 结果 |
|
||||
| --- | --- |
|
||||
| 原派车组仍有 `holding` / `assigned` 服务日 | 使用原派车组当前 active 视图 |
|
||||
| 原组失效,同 `orderId` + 同 `assignmentSlotId` 恰有一个 active 组 | 使用当前改派组 |
|
||||
| 仅有其他订单或其他槽位的 active 组 | `605308` |
|
||||
| 同槽位无 active 组 | `605308` |
|
||||
| 同槽位存在多个 active 组 | `605308`,失败封闭 |
|
||||
|
||||
本次不改变 token 签名、有效期、短链 code 结构或错误码。
|
||||
|
||||
## 3. 前端多车辆/多司机消费
|
||||
|
||||
### 批量派单
|
||||
|
||||
```http
|
||||
POST /admin/fleet/assignments/batch
|
||||
```
|
||||
|
||||
每个已选车辆槽位生成一个 `items[]` 元素,所有槽位一次提交:
|
||||
|
||||
```yaml
|
||||
orderId: "2080000000000000001"
|
||||
requirementId: "2080000000000000002"
|
||||
startDate: "2026-07-29"
|
||||
endDate: "2026-07-31"
|
||||
holdMode: 1
|
||||
requestId: "assign-2080000000000000001-v1"
|
||||
items:
|
||||
- fleetItemIndex: 0
|
||||
vehicleId: "2080000000000000101"
|
||||
driverId: "2080000000000000201"
|
||||
- fleetItemIndex: 1
|
||||
vehicleId: "2080000000000000102"
|
||||
driverId: "2080000000000000202"
|
||||
```
|
||||
|
||||
- `fleetItemIndex` 从 0 开始,对应需求展开后的稳定车辆槽位。
|
||||
- `vehicleId`、`driverId` 必填;雪花 ID 全程按字符串处理。
|
||||
- `protocolPrice`、`messageTemplateId`、`customBody`、`confirmCrossResident` 是单槽位可选字段。
|
||||
- 前端维护可编辑槽位列表。初始槽位来自当前有效派车组或订单用车需求;点击
|
||||
“+ 添加车辆槽位”后追加一个空白草稿槽位,不得覆盖或复用既有槽位。
|
||||
- 每个草稿槽位独立选择一辆车和一名司机;未提交的新槽位允许删除,已有
|
||||
`holding` / `assigned` 槽位不得被“删除草稿”操作静默撤销。
|
||||
- 进入下一步前校验所有可提交槽位均已选择车辆和司机,并为每个槽位生成唯一
|
||||
`fleetItemIndex`。页面可见槽位数必须等于本次提交的 `items[]` 数量。
|
||||
- 不得为每辆车循环调用单条 `POST /admin/fleet/assignments` 代替批量接口。
|
||||
- 批量响应按 `data.assignments[].assignment.assignmentGroupId` 返回各槽位派车组 ID;
|
||||
前端逐项调用模板预览时传入对应 `assignmentGroupId`,不得只预览代表项。
|
||||
|
||||
### 派单详情
|
||||
|
||||
```http
|
||||
GET /admin/fleet/board/orders/<orderId>
|
||||
```
|
||||
|
||||
按 `data.activeAssignments[]` 渲染每个有效派车组,至少消费:
|
||||
|
||||
| 字段 | 用途 |
|
||||
| --- | --- |
|
||||
| `assignmentGroupId` | 派车组稳定展示 key |
|
||||
| `assignmentSlotId` | 同一需求车辆槽位的稳定身份 |
|
||||
| `fleetItemIndex` | 槽位顺序 |
|
||||
| `vehicleId` / `vehiclePlate` / `vehicleModel` | 车辆展示 |
|
||||
| `driverId` / `driverName` / `driverPhone` | 司机展示;电话已脱敏 |
|
||||
| `assignmentStatus` / `assignmentStatusLabel` | 当前有效状态 |
|
||||
| `lifecycleStageCode` | 生命周期阶段 |
|
||||
|
||||
`currentAssignment` 仅为兼容代表项,不能用来判断订单只有一辆车或只展示一名司机。
|
||||
`activeAssignments` 无数据时使用空列表空态,不复制代表项凑数。
|
||||
|
||||
## 前端展示矩阵
|
||||
|
||||
| 场景 | 数据源 | 页面行为 |
|
||||
| --- | --- | --- |
|
||||
| 通知预览传入有效派车组 | `assignmentGroupId` + `renderedBody` 中的 `itinerary.url` | 即时创建或复用并展示稳定短链 |
|
||||
| 旧前端未传派车组但订单、车辆、司机唯一定位 | `orderId` + `vehicleId` + `driverId` | 兼容定位并返回同一稳定短链 |
|
||||
| 派车组缺失、无效或定位不唯一 | “行程链接暂不可用,请联系车务确认” | 展示不可用态,不把文案当可发送链接 |
|
||||
| 已有车辆槽位 | `activeAssignments[]` 或当前排车草稿 | 按稳定槽位逐项展示;允许重选当前槽位的车辆或司机 |
|
||||
| 新增车辆槽位 | 前端草稿槽位列表 | 展示“+ 添加车辆槽位”;每次点击只追加一个空白槽位,不替换已有槽位 |
|
||||
| 新增槽位未选完整 | 草稿槽位的 `vehicleId` / `driverId` | 槽位显示未完成警示,禁用“下一步”;不生成可发送通知 |
|
||||
| 删除未提交槽位 | 前端草稿槽位列表 | 只删除新增且未提交的草稿槽位,不撤销已有有效派车组 |
|
||||
| 一单多个车辆槽位 | `items[]` | 每个槽位各选一辆车和一名司机,一次批量提交;可见槽位数与 `items[]` 数量守恒 |
|
||||
| 详情有多个 active 派车组 | `activeAssignments[]` | 按槽位逐项展示车辆、司机、脱敏电话和状态 |
|
||||
| 详情无 active 派车组 | `activeAssignments=[]` | 展示无有效派单空态 |
|
||||
|
||||
## 前端处理清单
|
||||
|
||||
- [ ] 排车页提供“+ 添加车辆槽位”入口,允许连续新增多个草稿槽位,不得只重选“车辆槽位 1”。
|
||||
- [ ] 每个新增槽位分别选择一辆车和一名司机,并支持删除未提交的草稿槽位。
|
||||
- [ ] “下一步”前校验所有槽位,按页面槽位顺序生成唯一 `fleetItemIndex`,可见槽位与
|
||||
`items[]` 一一对应。
|
||||
- [ ] 统一提交 `POST /admin/fleet/assignments/batch` 的 `items[]`,保留批次级 `requestId`。
|
||||
- [ ] 批量派单响应逐项保存 `assignmentGroupId`;通知预览传入当前槽位的
|
||||
`orderId`、`vehicleId`、`driverId`、`assignmentGroupId`,只把真实短链视为可发送链接。
|
||||
- [ ] 派单详情按 `activeAssignments[]` 展示全部车辆/司机,不只读 `currentAssignment`。
|
||||
- [ ] 司机电话使用后端脱敏值,雪花 ID 始终按字符串处理。
|
||||
- [ ] 覆盖无 active、多 active、短链不可用等空态/失败封闭场景。
|
||||
|
||||
## 前端验收反馈
|
||||
|
||||
- 2026-07-25 用户页面验收:排车页仅显示“车辆槽位 1”,只能在该槽位内重选车辆或司机,
|
||||
无法新增第二个槽位;当前前端提交不满足多车辆、多司机批量派单要求。
|
||||
- 状态因此由 `implemented` 回退为 `claimed`。前端完成新增槽位、逐槽位选择和批量提交后,
|
||||
应填写新的 `frontend_ref` 再迁移为 `implemented`。
|
||||
|
||||
## 验证证据
|
||||
|
||||
- OpenAPI/oasdiff:`not_configured`。项目未配置可复现的 Swagger2 → OAS3 导出与 oasdiff 基线;
|
||||
本次使用源码语义比对、Controller/Service 定向测试与测试网关证据兜底。
|
||||
- 消费者契约/Spring Cloud Contract:`not_required`。本次没有内部 Feign 或共享 Java DTO 变化。
|
||||
- 后端定向测试:41 项通过,0 失败、0 错误、0 跳过。
|
||||
- Fleet Spotless:606 个 Java 文件检查通过。
|
||||
- 完整 reactor `verify`:3209 项测试,0 失败、0 错误、1 跳过;其中 fleet 2373 项,
|
||||
0 失败、0 错误、1 跳过。
|
||||
- 后端 PR #5249 合并提交:`433ef238f09eba2258c996093b1d8cb2309a8e83`。
|
||||
- 后端 PR #5250 合并提交:`d939995bd266f11076eb79ea183e37a968e01afc`。
|
||||
- 测试部署任务:`8eae87b2`;`hl-fleet-service` 的 `8187`、`8087` 两实例均健康。
|
||||
- 测试网关已验证:显式 `assignmentGroupId` 与唯一兼容定位返回同一 7 位短码;
|
||||
重复预览保持稳定,短链 302、H5 JSON 与 HTML 均成功;失效组返回 `605308`,
|
||||
篡改签名返回 `605306`。脱敏证据已回写工单 #5245。
|
||||
|
||||
## 不影响范围
|
||||
|
||||
- 不修改或部署 `D:/work2/hl-ui`。
|
||||
- 除模板预览请求新增可选 `assignmentGroupId` 外,不删除 API 字段,不改变既有字段类型、
|
||||
必填性或枚举;模板预览响应结构不变。预览在命中有效派车组时会幂等写入短链记录。
|
||||
- 不修改批量派单事务、价格、跨常驻确认、保险或通知冻结规则。
|
||||
- 不新增 DDL,不清理、不回填存量数据。
|
||||
|
||||
> `frontend_status: claimed` 表示前端已领取但仍需修复“新增车辆槽位”;尚未形成可验收的完整实现。
|
||||
@@ -5,7 +5,11 @@ title: "车队独立管理及车队字典下线"
|
||||
consumer: "admin"
|
||||
backend: "verified"
|
||||
gateway: "verified"
|
||||
frontend: "pending"
|
||||
frontend: "implemented"
|
||||
frontend_status: "implemented"
|
||||
frontend_owner: "hl-ui-codex"
|
||||
frontend_ref: "mmg/hl-ui@ac4d5fd6292b13eb504f5393dfe07781800b9233"
|
||||
updated_at: "2026-07-25T01:18:23.686Z"
|
||||
base: "dev-v3"
|
||||
generated: "2026-07-22T10:46:00+08:00"
|
||||
---
|
||||
@@ -121,10 +125,30 @@ user-service 在“车务管理”目录下新增子菜单:
|
||||
|
||||
## 前端必须修改的范围
|
||||
|
||||
### 2026-07-24 页面复测反馈:列表列宽与暗色模式
|
||||
|
||||
测试环境 `/fleet/teams` 页面已经能展示负责人和脱敏电话,但当前样式仍需前端修正,本反馈不涉及后端接口或字段变化:
|
||||
|
||||
1. 表格列宽分配失衡。“车队名称”列占用过多空白,把“负责人 / 负责人电话”等核心联系人信息推到页面右侧,首屏信息密度过低。
|
||||
2. 暗色模式不能只替换页面背景。当前筛选区、表头、行分隔线、空值、状态标签和操作区的层级与对比度不足,部分边界难以辨认。
|
||||
3. 样式必须复用项目主题 token;禁止在本页写死仅适用于浅色模式的背景色、文字色或边框色。负责人电话仍只展示接口返回的脱敏值,样式调整不得绕过脱敏。
|
||||
|
||||
#### 展示矩阵
|
||||
|
||||
| 视口 / 主题 | 车队名称 | 负责人 / 负责人电话 | 其他列 | 验收表现 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `>= 1440px`,浅色 | 弹性列,限制最大占比;超长省略并可查看完整名称 | 建议分别保留约 `120px / 140px`,左对齐 | 类型、付款方式、数量、排序、状态和操作按内容定宽 | 联系人紧邻业务字段,首屏无大段无意义空白 |
|
||||
| `>= 1440px`,暗色 | 同浅色列宽规则 | 同浅色列宽规则 | 使用暗色主题 token | 页面、筛选区、表头、数据行、状态标签和操作区层级清楚 |
|
||||
| `1024px - 1439px`,浅色/暗色 | 优先收缩并省略,不能无限占宽 | 不压缩为空或挤出主要阅读区 | 保留操作列可用宽度 | 联系人信息仍可直接阅读 |
|
||||
| `< 1024px`,浅色/暗色 | 设置表格最小宽度 | 保持可读宽度 | 允许横向滚动 | 不通过隐藏关键列或强行挤压完成适配 |
|
||||
|
||||
空负责人和空电话统一显示 `—`。联系人文本左对齐;车辆数、排序、状态和操作居中。浅色与暗色模式都必须覆盖默认、悬停、聚焦、禁用和空数据状态;普通文本与背景建议至少达到 `4.5:1` 对比度,控件边界和状态提示应清晰可辨。
|
||||
|
||||
### 管理后台
|
||||
|
||||
1. 新增 `src/api/fleet/teams.js` 和 `src/views/fleet/teams/index.vue`,完成车队分页、新增、编辑、启停和删除:
|
||||
- “车队管理”必须显示在“车务管理”目录内,不得作为一级菜单处理。
|
||||
- 按上面的展示矩阵修正表格列宽和明暗主题样式,不能让“车队名称”列挤占联系人信息区域。
|
||||
- 仅 `vehicleCount === 0` 时展示/启用删除动作;调用删除接口后刷新列表。
|
||||
- 后端仍会独立校验车辆及未完结司机录入关联,返回 `601107` 时提示“请先完成车辆/司机转移”。
|
||||
- 编辑历史迁入车队时补齐负责人、负责人电话、付款方式和排序。
|
||||
@@ -189,6 +213,8 @@ DELETE FROM sys_dict_type WHERE dict_type = 'fleet_attribution';
|
||||
## 验收清单
|
||||
|
||||
- [ ] 独立车队菜单可分页、新增、编辑、启停,付款方式与资源页选项一致。
|
||||
- [ ] `/fleet/teams` 在桌面端不再由“车队名称”列制造大段空白,负责人和脱敏电话位于首屏连续阅读区;窄屏按展示矩阵滚动而不是隐藏或挤压关键列。
|
||||
- [ ] `/fleet/teams` 的浅色、暗色模式均使用主题 token,筛选区、表头、数据行、空值、状态标签和操作区在默认/悬停/聚焦/禁用状态下层级清晰。
|
||||
- [ ] “车队管理”位于“车务管理”目录下;空车队可删除,非空车队删除入口禁用或明确提示后端 `601107`。
|
||||
- [ ] 历史迁入车队可通过编辑补齐负责人、负责人电话、付款方式和排序,保存时不允许提交空资料。
|
||||
- [ ] 车辆新增/编辑/筛选/详情/导入均使用动态车队,不再出现固定三项。
|
||||
|
||||
@@ -135,6 +135,17 @@ POST /admin/fleet/assignments/{assignmentId}/confirm
|
||||
- 等待态:“等待司机回复确认”
|
||||
- 已登记态:“司机已确认接单,待车务确认执行”
|
||||
|
||||
### 2026-07-24 界面验收补充
|
||||
|
||||
当前“待确认”步骤中,“司机待确认通知 / 模板与预览均来自后端”标题区下方存在明显的
|
||||
大块空白,导致模板选择行和消息预览整体下移。模板标签及消息正文已经正常显示,因此
|
||||
这是前端布局问题,不是后端模板或渲染接口缺少数据。
|
||||
|
||||
- 移除标题区不必要的固定高度、最小高度或空占位,让高度由标题和副标题内容自然撑开。
|
||||
- 标题区与模板选择行保持正常紧凑间距,不要为未来内容预留不可见空白。
|
||||
- 常用桌面分辨率下,标题区底部到模板选择行的垂直空白不应超过 16px。
|
||||
- 本项不新增接口、不调整字段,也不要为修复布局重新维护前端本地模板。
|
||||
|
||||
## 前端处理清单
|
||||
|
||||
- [ ] 模板列表来自后端 `hold_notify` 模板,默认选中 `isDefault=true`,不再使用前端假模板。
|
||||
@@ -145,6 +156,7 @@ POST /admin/fleet/assignments/{assignmentId}/confirm
|
||||
- [ ] 无凭证时仍可成功登记司机确认并执行最终确认。
|
||||
- [ ] `605025` 时提示“请先登记司机已确认接单”,不提示“缺少凭证”。
|
||||
- [ ] 页面刷新后能按后端状态恢复待回复/已确认阶段。
|
||||
- [ ] 修复“司机待确认通知”标题区异常留白,模板选择与消息预览紧凑衔接。
|
||||
|
||||
## 验证证据
|
||||
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
# 🔧 出行人批量编辑:资料完整度与完成统计统一为订单级手机号语义(#5203)
|
||||
|
||||
> **PR**: [#5210](https://git.1814.love:8443/wx/HL/pulls/5210)
|
||||
> **Issue**: [#5203](https://git.1814.love:8443/wx/HL/issues/5203)
|
||||
> **日期**: 2026-07-24
|
||||
> **消费端**: 一期小程序 MP BFF
|
||||
> **接口**: `POST /mp/v3/order/{id}/traveler/batch-edit`
|
||||
|
||||
## 1. 变更背景
|
||||
|
||||
出行人资料完整度与订单签约、确认门禁此前存在两套手机号口径:逐人完成状态可能要求每名成人都有手机号,但订单门禁只要求整单至少一名出行人有手机号。
|
||||
|
||||
本次统一为:
|
||||
|
||||
- 单名出行人的资料完整度只检查 `name`、`gender`、`birthday`、`idType`、`idNo` 五项;
|
||||
- `phone` 不再影响该出行人的完成状态;
|
||||
- 订单整体仍必须至少有一名出行人填写手机号;
|
||||
- 接口字段名、类型和层级不变,但 `completedCount`、`pendingCount`、`allCompleted` 的统计结果可能变化。
|
||||
|
||||
## 2. 变更接口
|
||||
|
||||
| 接口 | 方法 | 路径 | 变更类型 |
|
||||
|---|---|---|---|
|
||||
| 客户批量补全出行人 | POST | `/mp/v3/order/{id}/traveler/batch-edit` | 响应字段语义修改 |
|
||||
|
||||
## 3. 完整接口契约
|
||||
|
||||
### 3.1 调用约束
|
||||
|
||||
| 项目 | 契约 |
|
||||
|---|---|
|
||||
| 认证 | 需要小程序登录态 |
|
||||
| 可编辑订单状态 | `PENDING_PAY`(待支付)、`CUSTOMIZING`(定制中) |
|
||||
| 订单归属 | 只能编辑当前登录用户自己的订单 |
|
||||
| 幂等 | 同一订单 3 秒内重复提交返回 `100502` |
|
||||
| 批量上限 | 每次 1~30 名出行人 |
|
||||
| 写入语义 | `id=null` 为新增,`id` 非空为更新;未出现在数组中的已有出行人不会被删除 |
|
||||
|
||||
### 3.2 路径参数
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `id` | Long | 是 | 订单 ID;建议以字符串形式传递,避免大整数精度丢失 |
|
||||
|
||||
### 3.3 请求体
|
||||
|
||||
请求类型:`TravelerBatchEditReqVO`
|
||||
|
||||
| 字段 | 类型 | 必填 | 约束与语义 |
|
||||
|---|---|---|---|
|
||||
| `travelers` | `TravelerEditItem[]` | 是 | 1~30 项 |
|
||||
| `travelers[].id` | Long / null | 否 | `null` 表示新增;非空表示更新,且必须属于路径中的订单 |
|
||||
| `travelers[].name` | String / null | 条件必填 | 新增项的 `name`、`idType`、`idNo` 至少一项非空;有值时长度 2~30,只允许中文、英文、中点 `·`、连字符 `-`、空格 |
|
||||
| `travelers[].gender` | String / null | 否 | `0`、`1`、`2`;该字段为空时资料状态为 `PENDING` |
|
||||
| `travelers[].birthday` | String | 是 | `yyyy-MM-dd`,不得晚于当天;用于派生出行人类型 |
|
||||
| `travelers[].idType` | String / null | 条件必填 | 取值见第 4 节;与 `idNo` 配套 |
|
||||
| `travelers[].idNo` | String / null | 条件必填 | `ID_CARD` 为 18 位数字或末位 `X/x`;其他证件为 5~30 位字母、数字或连字符 |
|
||||
| `travelers[].nationality` | String / null | 否 | 允许不传或传 `null`,不允许显式传空字符串 |
|
||||
| `travelers[].race` | String / null | 否 | 允许不传或传 `null`,不允许显式传空字符串 |
|
||||
| `travelers[].phone` | String / null | 否 | 有值时必须为 11 位数字;更新时 `null` 表示保留原值,空字符串表示清空 |
|
||||
| `travelers[].emergencyContact` | String / null | 否 | 出行人级紧急联系人姓名 |
|
||||
| `travelers[].emergencyPhone` | String / null | 否 | 有值时必须为 11 位数字 |
|
||||
| `travelers[].roomGroupNo` | Integer / null | 否 | 最小为 1,最大不超过订单声明总人数 |
|
||||
|
||||
更新已有出行人时,除必填的 `birthday` 外,其他可选字段传 `null` 表示保留原值。
|
||||
|
||||
### 3.4 响应
|
||||
|
||||
响应类型:`Result<TravelerBatchEditRespVO>`
|
||||
|
||||
统一响应字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `code` | Integer | `200` 表示成功,其他值见第 5 节 |
|
||||
| `message` | String | 响应文案 |
|
||||
| `data` | Object / null | 成功时为批量编辑统计,失败时通常为 `null` |
|
||||
| `traceId` | String / null | 链路追踪 ID,可能为空 |
|
||||
| `success` | Boolean | `code == 200` 时为 `true` |
|
||||
|
||||
`data` 字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `createdCount` | Integer | 本次请求中 `id=null` 的新增数量 |
|
||||
| `updatedCount` | Integer | 本次请求中 `id` 非空的更新数量 |
|
||||
| `completedCount` | Integer | 操作完成后,订单内五项资料均完整的出行人总数 |
|
||||
| `pendingCount` | Integer | 操作完成后,订单内五项资料仍有缺失的出行人总数 |
|
||||
| `allCompleted` | Boolean | 同时满足“订单声明人数大于 0、实际人数等于声明人数、`pendingCount=0`、整单至少一名出行人有手机号”时为 `true` |
|
||||
|
||||
五项资料指:`name`、`gender`、`birthday`、`idType`、`idNo`。手机号不计入单名出行人的完成状态,但仍计入 `allCompleted` 的订单级门禁。
|
||||
|
||||
## 4. 枚举与数据字典
|
||||
|
||||
### 4.1 `gender`
|
||||
|
||||
| 值 | 中文 | 完整度语义 |
|
||||
|---|---|---|
|
||||
| `0` | 未知 | 有值,满足 `gender` 完整度 |
|
||||
| `1` | 男 | 有值,满足 `gender` 完整度 |
|
||||
| `2` | 女 | 有值,满足 `gender` 完整度 |
|
||||
|
||||
### 4.2 资料完成状态
|
||||
|
||||
该状态不单独出现在本接口响应中,但直接决定 `completedCount` 和 `pendingCount`。
|
||||
|
||||
| 值 | 中文 | 判定 |
|
||||
|---|---|---|
|
||||
| `COMPLETED` | 已完善 | `name/gender/birthday/idType/idNo` 五项全部非空 |
|
||||
| `PENDING` | 待完善 | 上述五项任一为空 |
|
||||
|
||||
### 4.3 `idType`
|
||||
|
||||
| 值 | 中文 |
|
||||
|---|---|
|
||||
| `ID_CARD` | 身份证 |
|
||||
| `PASSPORT` | 护照 |
|
||||
| `HK_MACAU_PASS` | 港澳通行证 |
|
||||
| `HONGKONG_RESIDENT_PASS` | 回乡证 |
|
||||
| `TAIWAN_PASS` | 台湾通行证 |
|
||||
| `MILITARY_ID` | 军官证 |
|
||||
| `OTHER` | 其他 |
|
||||
|
||||
### 4.4 出行人类型
|
||||
|
||||
出行人类型不由请求体传入,而是根据 `birthday` 自动派生,并用于校验订单各类型人数配额。
|
||||
|
||||
| 年龄 | 值 | 中文 |
|
||||
|---|---|---|
|
||||
| 未满 2 周岁 | `BABY` | 幼童 |
|
||||
| 2~6 周岁 | `YOUNG_CHILD` | 小童 |
|
||||
| 7~17 周岁 | `CHILD` | 儿童 |
|
||||
| 18 周岁及以上 | `ADULT` | 成人 |
|
||||
|
||||
## 5. 错误码
|
||||
|
||||
| code | message / 含义 | 触发场景 |
|
||||
|---|---|---|
|
||||
| `401` | 未认证 | 未携带有效小程序登录态 |
|
||||
| `500` | 出行人服务不可用,请稍后重试 | BFF 无法调用出行人服务 |
|
||||
| `100001` | 参数非法 | `travelers` 为空、超过 30 项、缺少 `birthday`、日期格式错误等请求校验失败 |
|
||||
| `100502` | 出行人补全处理中,请勿重复提交 | 同一订单 3 秒内重复提交 |
|
||||
| `100503` | 资源被占用,请稍后重试 | 同一订单存在并发写入且未能取得操作权 |
|
||||
| `100701` | 姓名长度异常(2-30字符) | 非空姓名长度不在 2~30 字符 |
|
||||
| `100702` | 姓名含非法字符 | 姓名包含允许字符集之外的内容 |
|
||||
| `100703` | 姓名含敏感词 | 姓名命中敏感词 |
|
||||
| `100704` | 姓名格式不正确 | 同一字符连续重复 5 次及以上 |
|
||||
| `581101` | 12301 必报字段缺失(国籍 / 民族不能为空字符串) | `nationality` 或 `race` 显式传空字符串 |
|
||||
| `581102` | 订单不存在,无法编辑出行人 | 处理过程中订单不存在 |
|
||||
| `581103` | 性别编码不合法(应为 1=男/2=女/0=未知) | 非空 `gender` 不在 `0/1/2` |
|
||||
| `581104` | 同住分组号超出订单家庭数上限 | `roomGroupNo < 1` 或超过订单声明总人数 |
|
||||
| `581110` | 出行人 ID 不属于该订单 | 更新项的 `id` 不属于路径订单 |
|
||||
| `581111` | 已签电子合同后禁止修改证件号 | 已签约记录尝试修改 `idNo` |
|
||||
| `581112` | 证件号格式不合法,请检查证件类型与号码是否匹配 | `idType` 非法或 `idNo` 格式不匹配 |
|
||||
| `581113` | 手机号格式非法(应为 11 位数字) | 非空 `phone` 或 `emergencyPhone` 不是 11 位数字 |
|
||||
| `581114` | 出生日期不能晚于今天 | `birthday` 为未来日期 |
|
||||
| `581118` | 新增出行人缺少必填字段 | 新增项的 `name/idType/idNo` 全部为空 |
|
||||
| `581119` | 出行人证件号重复 | 同一请求或订单内出现重复证件号 |
|
||||
| `581122` | 订单不属于当前用户 | 订单不存在或不属于当前登录用户 |
|
||||
| `581145` | 订单已确认,出行人信息不可再经小程序修改,如需变更请联系定制师 | 订单状态不在 `PENDING_PAY/CUSTOMIZING` 白名单 |
|
||||
| `581149` | 出行人类型人数超出订单人数配置 | 根据生日派生后的某类出行人数超过订单声明配额 |
|
||||
|
||||
## 6. 示例
|
||||
|
||||
### 6.1 典型成功:两人五项完整,仅一人有手机号
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
POST /mp/v3/order/2079576729147338754/traveler/batch-edit
|
||||
Authorization: Bearer <mp-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"travelers": [
|
||||
{
|
||||
"id": "2079576729147338801",
|
||||
"name": "张三",
|
||||
"gender": "1",
|
||||
"birthday": "1990-01-01",
|
||||
"idType": "PASSPORT",
|
||||
"idNo": "P1234567",
|
||||
"nationality": "中国",
|
||||
"race": "汉族",
|
||||
"phone": "13800138000",
|
||||
"emergencyContact": null,
|
||||
"emergencyPhone": null,
|
||||
"roomGroupNo": 1
|
||||
},
|
||||
{
|
||||
"id": "2079576729147338802",
|
||||
"name": "李四",
|
||||
"gender": "2",
|
||||
"birthday": "1992-02-02",
|
||||
"idType": "PASSPORT",
|
||||
"idNo": "P7654321",
|
||||
"nationality": "中国",
|
||||
"race": "汉族",
|
||||
"phone": "",
|
||||
"emergencyContact": null,
|
||||
"emergencyPhone": null,
|
||||
"roomGroupNo": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"createdCount": 0,
|
||||
"updatedCount": 2,
|
||||
"completedCount": 2,
|
||||
"pendingCount": 0,
|
||||
"allCompleted": true
|
||||
},
|
||||
"traceId": "a1b2c3d4-e5f6-7890",
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 边界成功:五项全部完整,但整单没有手机号
|
||||
|
||||
假设订单声明人数和实际人数均为 2,且请求将最后一部手机号清空。
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
POST /mp/v3/order/2079576729147338754/traveler/batch-edit
|
||||
Authorization: Bearer <mp-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"travelers": [
|
||||
{
|
||||
"id": "2079576729147338801",
|
||||
"name": "张三",
|
||||
"gender": "1",
|
||||
"birthday": "1990-01-01",
|
||||
"idType": "PASSPORT",
|
||||
"idNo": "P1234567",
|
||||
"phone": ""
|
||||
},
|
||||
{
|
||||
"id": "2079576729147338802",
|
||||
"name": "李四",
|
||||
"gender": "2",
|
||||
"birthday": "1992-02-02",
|
||||
"idType": "PASSPORT",
|
||||
"idNo": "P7654321",
|
||||
"phone": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"createdCount": 0,
|
||||
"updatedCount": 2,
|
||||
"completedCount": 2,
|
||||
"pendingCount": 0,
|
||||
"allCompleted": false
|
||||
},
|
||||
"traceId": "a1b2c3d4-e5f6-7890",
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
这里 `completedCount=2` 表示两人的五项资料都完整;`allCompleted=false` 表示订单级“至少一名出行人有手机号”门禁未满足。
|
||||
|
||||
### 6.3 业务失败:订单已确认
|
||||
|
||||
**请求**
|
||||
|
||||
```http
|
||||
POST /mp/v3/order/2079576729147338754/traveler/batch-edit
|
||||
Authorization: Bearer <mp-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"travelers": [
|
||||
{
|
||||
"id": "2079576729147338801",
|
||||
"name": "张三",
|
||||
"gender": "1",
|
||||
"birthday": "1990-01-01",
|
||||
"idType": "PASSPORT",
|
||||
"idNo": "P1234567",
|
||||
"phone": "13800138000"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**响应**
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 581145,
|
||||
"message": "订单已确认,出行人信息不可再经小程序修改,如需变更请联系定制师",
|
||||
"data": null,
|
||||
"traceId": "a1b2c3d4-e5f6-7890",
|
||||
"success": false
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 修改前后对比
|
||||
|
||||
| 场景 | 修改前 | 修改后 |
|
||||
|---|---|---|
|
||||
| 单名成人五项完整但本人无手机号 | 可能计入 `pendingCount` | 计入 `completedCount` |
|
||||
| 同行人已有手机号 | 仍可能要求每名成人各自填写 | 整单手机号门禁已满足 |
|
||||
| 五项完整且整单无手机号 | 逐人完成状态与手机号门禁混合 | `completedCount` 可等于实际人数,但 `allCompleted=false` |
|
||||
| 请求 / 响应结构 | 现有字段 | 不变 |
|
||||
|
||||
## 8. 消费注意事项
|
||||
|
||||
- 不要按“每名成人必须有手机号”在本地重算资料完成状态。
|
||||
- `completedCount` 和 `pendingCount` 是操作后订单内的总量,不是本次请求中发生状态变化的行数。
|
||||
- 判断本接口是否已满足整单补全条件,以响应 `allCompleted` 为准;它已同时包含人数、五项资料和订单级手机号门禁。
|
||||
- `phone=null` 在更新场景表示保留原值;需要清空手机号时传空字符串。
|
||||
|
||||
## 9. 关联
|
||||
|
||||
- **Issue**: [#5203](https://git.1814.love:8443/wx/HL/issues/5203)
|
||||
- **PR**: [#5210](https://git.1814.love:8443/wx/HL/pulls/5210)
|
||||
- **Merge commit**: [88d0aec8375b56b5b8141984645a6998f8a42609](https://git.1814.love:8443/wx/HL/commit/88d0aec8375b56b5b8141984645a6998f8a42609)
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "5254"
|
||||
title: "订单侧已配置车辆补充车型车队服务日期与日单价"
|
||||
consumer: "admin"
|
||||
change_type: "修改接口"
|
||||
backend_status: "pending"
|
||||
gateway_status: "pending"
|
||||
frontend_status: "pending"
|
||||
frontend_owner: ""
|
||||
frontend_ref: ""
|
||||
target_release: ""
|
||||
verified_at: ""
|
||||
status_note: ""
|
||||
updated_at: "2026-07-26"
|
||||
base: "dev-v3"
|
||||
generated: "2026-07-26T09:57:21+08:00"
|
||||
---
|
||||
|
||||
# 订单侧已配置车辆补充车型车队服务日期与日单价
|
||||
|
||||
订单详情的已配置车辆补齐车型标题、车队、连续服务日期、服务天数和协议日单价。
|
||||
本记录只表示后端契约交接,`frontend_status` 在真实前端领取前保持 `pending`。
|
||||
|
||||
## 关联
|
||||
|
||||
- Issue: #5254
|
||||
- PR: 待补充
|
||||
|
||||
## 变更接口
|
||||
|
||||
| 方法 | 路径 | 来源 |
|
||||
|---|---|---|
|
||||
| GET | `/v3/admin/order/{id}/itinerary` | `data.vehicleGroup.assignments[]` |
|
||||
|
||||
### `data.vehicleGroup.assignments[]`
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `brand` | string | 否 | 兼容字段;Fleet 实时数据下回填车辆车型名称,前端标题可按 `brand \|\| vehicleType \|\| '—'` 展示 |
|
||||
| `fleetTeamId` | string | 否 | 车辆所属车队 ID;雪花 ID 按字符串返回 |
|
||||
| `fleetTeamName` | string | 否 | 车辆所属车队名称;归档车辆或历史快照无法补齐时为空 |
|
||||
| `startDate` | string(`yyyy-MM-dd`) | 否 | 车辆/司机连续服务段开始日 |
|
||||
| `endDate` | string(`yyyy-MM-dd`) | 否 | 车辆/司机连续服务段结束日 |
|
||||
| `serviceDays` | integer | 否 | 连续服务天数,首尾日期均计入 |
|
||||
| `plannedDailyFee` | string(decimal) | 否 | 协议日单价;连续段内每日协议价不一致或无价格时为空,不得按 0 元展示 |
|
||||
|
||||
既有 `vehicleType`、`licensePlate`、`seats`、`driverName` 和
|
||||
`driverPhoneMasked` 继续返回;手机号保持脱敏。
|
||||
|
||||
### 内部 Feign/shared Java
|
||||
|
||||
`OrderDriverVehicleCandidateDTO` 新增可空字段:
|
||||
|
||||
- `fleetTeamId: Long`(JSON 字符串)
|
||||
- `fleetTeamName: String`
|
||||
- `protocolPrice: BigDecimal`(JSON 字符串)
|
||||
|
||||
既有 `startDate`、`endDate` 本次开始映射到订单侧公开响应。新旧 Fleet/Order
|
||||
可滚动部署:旧消费者忽略新增字段,新消费者读取旧生产者时新增字段为空。
|
||||
|
||||
## 契约影响文件
|
||||
|
||||
- `hl-common/hl-common-core/src/main/java/com/hulalv/common/dto/fleet/OrderDriverVehicleCandidateDTO.java`
|
||||
- `hl-order-service-v3/src/main/java/com/hulalv/order/core/controller/admin/vo/detail/ItineraryVO.java`
|
||||
- `hl-order-service-v3/src/test/java/com/hulalv/order/core/controller/admin/OrderControllerTest.java`
|
||||
- `hl-order-service-v3/src/test/java/com/hulalv/order/fleet/feign/FleetDriverVehicleFeignContractTest.java`
|
||||
|
||||
## 前端/调用方动作
|
||||
|
||||
- `src/views/order-v2/detail/_shared/v3Adapter.js` 映射新增字段:
|
||||
`fleetTeamName`、`startDate`、`endDate`、`serviceDays`、`plannedDailyFee`。
|
||||
- 车型标题使用 `brand || vehicleType || '—'`;`brand === vehicleType` 时不要重复展示同一车型。
|
||||
- “用车安排”摘要展示车型、车牌、座位、司机、脱敏手机号和服务日期。
|
||||
- “已配置车辆”弹窗展示车型、车牌、座位、所属车队、司机、脱敏手机号、
|
||||
服务起止日期、服务天数和协议日单价。
|
||||
- `plannedDailyFee == null` 时显示 `—`,不得显示 0 元;无车辆时保持“暂无已配车”。
|
||||
- 多车/改派连续段按接口数组逐条渲染,不按车牌或司机姓名自行去重。
|
||||
|
||||
## 验证证据
|
||||
|
||||
- Fleet 定向测试:
|
||||
`mvn -pl hl-fleet-service -am -Dtest=OrderDriverVehicleQueryServiceTest,AssignmentConverterTest -Dsurefire.failIfNoSpecifiedTests=false test`
|
||||
(31 项通过)。
|
||||
- Order 消费者与内部契约:
|
||||
`mvn -pl hl-order-service-v3 -am -Dtest=OrderDetailServiceTest,FleetDriverVehicleFeignContractTest -Dsurefire.failIfNoSpecifiedTests=false test`
|
||||
(79 项通过)。
|
||||
- 前端 API 序列化:
|
||||
`mvn -pl hl-order-service-v3 -am -Dtest=OrderControllerTest#getItinerary_validId_returns200 -Dsurefire.failIfNoSpecifiedTests=false test`
|
||||
(1 项通过)。
|
||||
- Fleet Spotless:`mvn -pl hl-fleet-service spotless:check`(通过)。
|
||||
- 网关验证:待补充
|
||||
- 兼容性结论:仅新增可空响应字段并补齐既有空字段;内部 Feign JSON 双向兼容,
|
||||
不修改方法、路径、参数、必填项、枚举或错误码。
|
||||
在新工单中引用
屏蔽一个用户