hl-api-changelog/changelogs-v2/2026-08/03_5379_终止行程车辆退款权威口径-修改接口-管理后台.md
API Changelog Bot 76f18dcbec
一些检查失败了
changelog-filename-gate / validate (push) Failing after 2s
docs(changelog): 联系人章节补全 yst 完整格式——链接(Issue+PR+Merge commit)+ 联系人
2026-08-04 14:13:26 +08:00

152 行
9.6 KiB
Markdown

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

---
schema: "hl-changelog/v2"
ticket: "5379"
title: "终止行程车辆退款改为 Fleet 服务日权威判定"
consumer: "admin"
change_type: "修改接口"
author: "wx(GIT)"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "Pi"
frontend_ref: "v2.1@49f38c7cfb4d5859a13c6ab1a7fea540cb524de5"
target_release: ""
verified_at: "2026-08-04"
status_note: "后端实现完成:双模块 verify 通过order 7402 全绿 / fleet 3010 仅 Docker 环境型 1 error,本单新增代码行覆盖率 91.7%,PR #5417+#5445 已合并 dev-v3 并部署 TESTfleet→order-v3 顺序,网关已验证预览成功路径15 行 VEHICLE+terminateDate wire key、终止提交成功COMPLETED+PENDING_REVIEW+settlement refund 落库)、幂等重放(同 terminateRefundId、阻断路径581018 COMPLETED 拒绝/584100 费用不可用 fail-closed、空集路径无需求允许空车辆成本;管理后台已由 Pi 领取并开始兼容适配。"
updated_at: "2026-08-04"
base: "dev-v3"
---
# 订单: 终止行程车辆退款改为 Fleet 服务日权威判定
> **服务**: hl-order-service-v3
> **PR**: #5417
> **Issue**: #5379
> **日期**: 2026-08-03
> **影响范围**: 管理后台订单终止退款预览及终止提交
---
## ⚠️ 关键变化
终止行程时,车辆是否已发生不再采用客户端提交的 `vehicles[].used`;后端按 Fleet 返回的 `serviceDate``endDayNumber` 对应终止日权威判定。`vehicles[].used` 仍为必填兼容字段,仅参与请求重放一致性校验。
## 变更接口
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 终止行程退款预览 | POST | `/v3/admin/order/:id/terminate/refund-preview` | 响应语义修改 | 车辆行 `defaultUsed` 仅供初始化展示,最终提交以后端权威事实为准 |
| 2 | 终止行程 | POST | `/v3/admin/order/:id/terminate` | 请求字段语义修改 | `vehicles[].used` 保持必填,但不再参与车辆退款金额计算 |
## 二、接口详情
### 1. 终止行程退款预览 `POST /v3/admin/order/:id/terminate/refund-preview`
**出参**: `Result<OrderTerminateRefundPreviewRespVO>`
### refundLines[] 统一资源行(新逻辑主用,提交 `lineUsages` 按 `lineKey` 回传)
`refundLines``List<TerminateRefundItemVO>`,住宿/门票/活动/服务/交通/备品/用车/保险全部资源统一展平为行,已按 `dayNumber` 展开(用车每天一行)。
| 字段 | 类型 | 说明 | 示例 |
|------|------|------|------|
| `refundLines[].lineKey` | String | 统一退款资源行 key,提交 `lineUsages` 时原样回传 | `ITINERARY_NODE:97010:2` |
| `refundLines[].categoryCode` | String | **资源类型 discriminator**`ROOM`/`TICKET`/`ACTIVITY`/`SERVICE`/`TRANSPORT`/`SUPPLIES`/`VEHICLE`/`INSURANCE`,前端据此判断每行资源类型 | `SERVICE` |
| `refundLines[].categoryName` | String | 资源分类名称(与 categoryCode 对应) | `服务` |
| `refundLines[].sourceType` | String | **资源来源类型 discriminator**`HOTEL_ASSIGNMENT`/`ITINERARY_NODE`/`BATCH_SUPPLIES`/`PRODUCT_SUPPLIES`/`VEHICLE_ASSIGNMENT`/`INSURANCE_ORDER`,决定 `sourceId` 语义 | `ITINERARY_NODE` |
| `refundLines[].sourceId` | String(Long) | 资源来源 ID,语义由 `sourceType` 决定Long 序列化为 String 防 JS 精度丢失) | `97010` |
| `refundLines[].refId` | String(Long) | 资源配单记录 IDString 防 JS 精度丢失) | `96011` |
| `refundLines[].name` | String | 资源名称快照(酒店名/景点名/车型) | `拉萨瑞吉·大床房` |
| `refundLines[].dayNumber` | Integer | 第几天(住宿/门票/用车每天行有值;保险为 null | `1` |
| `refundLines[].dealPrice` | String(BigDecimal) | 结算单价(住宿=settlementPrice/间·晚;门票按节点价格口径;用车=dailyFee | `1280.00` |
| `refundLines[].quantity` | Integer | 数量(住宿=间数;门票=张数;用车=车辆数;保险=1 | `1` |
| `refundLines[].billingType` | String | 计费方式快照,备品等资源使用 | `PER_QUANTITY` |
| `refundLines[].totalAmount` | String(BigDecimal) | 行金额合计 = dealPrice × quantity | `1280.00` |
| `refundLines[].defaultUsed` | Boolean | 前端初始化展示值;车辆行最终是否已发生由提交时 Fleet `serviceDate` 与终止日重新判定,非车辆行按提交 `used` 重算 | `false` |
| `refundLines[].locked` | Boolean | 是否锁定不退true=保险等不可退资源) | `false` |
| `refundLines[].lockedReason` | String | 锁定原因(`locked=true` 时有值) | `保险已生效不退` |
### 旧分组字段(兼容保留,与 refundLines 并存)
`rooms`/`tickets`/`services`/`supplies`/`vehicles`/`insurance` **继续返回**,类型均为 `List<TerminateRefundItemVO>`(与 `refundLines` 同构,字段集完全一致):
| 字段 | 类型 | 说明 |
|------|------|------|
| `rooms` | List&lt;TerminateRefundItemVO&gt; | 住宿清单(每晚×每组房一行) |
| `tickets` | List&lt;TerminateRefundItemVO&gt; | 门票/活动清单(景点/活动节点,含套餐内) |
| `services` | List&lt;TerminateRefundItemVO&gt; | 服务清单SERVICE/TRANSPORT 节点) |
| `supplies` | List&lt;TerminateRefundItemVO&gt; | 备品清单hasCost=true 的备品,无天维度整单一行) |
| `vehicles` | List&lt;TerminateRefundItemVO&gt; | 用车清单(每段车按 totalDays 展开成每天一行) |
| `insurance` | List&lt;TerminateRefundItemVO&gt; | 保险(整单 1 行,locked=true 不可退) |
> 前端可任选一种渲染:新逻辑按 `refundLines[]` + `categoryCode`/`sourceType` 区分资源;旧分组字段不会删除,兼容期可继续使用。
### days 与其余顶层字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `days[].terminateDate` | String(date) | 结束日期选项 wire key 固定为 `terminateDate`(原 `date` 字段改名),值 = departDate + (dayNumber-1);`dayNumber`/`label`/`isCurrent` 不变 |
### 2. 终止行程 `POST /v3/admin/order/:id/terminate`
**VO**: `OrderTerminateTripReqVO`
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| `endDayNumber` | Body | Integer | ✅ | 1-based,必须落在订单行程范围内 | 用于确定终止日;预览返回的 `days[].terminateDate` 即对应日期 |
| `vehicles[].refId` | Body | Long/String ID | ✅ | 有效配车记录 ID | 同段车跨天可重复 |
| `vehicles[].dayNumber` | Body | Integer | ✅ | >= 1 | 区分同段车辆的服务日 |
| `vehicles[].used` | Body | Boolean | ✅ | 不可省略 | 兼容及重放摘要字段,不参与车辆退款金额计算 |
| `lineUsages[].used` | Body | Boolean | ✅ | 非车辆资源按该值重算 | 不覆盖车辆 Fleet 事实 |
## 三、契约约束与正确调用方式
| 场景 | 后端行为 |
|------|----------|
| `vehicles[].used` 与 Fleet 服务日事实不同 | 车辆退款采用 Fleet 服务日事实;客户端值只保留在请求摘要中 |
| 相同终止边界和相同正文重试 | 返回既有终止结果 |
| 已终止订单以不同 `endDayNumber` 或不同正文重试 | 拒绝,错误码 `581049` |
| `endDayNumber` 超出订单行程 | 拒绝,错误码 `581047` |
| Fleet `serviceDate` 无法映射到订单行程 | 拒绝,错误码 `581048` |
| Fleet 车辆事实不可用或不完整 | fail closed,错误码 `584100`,不按零车费继续 |
前端调用要求:
1. 继续提交完整的 `vehicles[].refId/dayNumber/used`,不要删除 `used` 字段。
2. 不要根据 `vehicles[].used` 自行推导最终车辆退款金额;以接口返回的 `baselineRefund``finalRefund` 为准。
3. 收到上述错误码时保留用户输入并提示重试或刷新事实,不要按“无车辆费用”继续。
## 四、边界行为与兼容性
- 请求字段、类型和必填性未删除,旧客户端 payload 可继续解析。
- `vehicles[].used` 的金额语义发生变化,但仍参与幂等重放摘要;重试时必须复用首次正文。
- 非车辆资源仍按既有 `lineUsages[].used` 规则处理。
- 后端修复尚未合并、部署或经网关验证;本文件不声明 TEST 环境可用。
## 验证证据
- 定向单元测试覆盖 Fleet 服务日权威判定、越界/不一致 fail-closed、重放冲突及车辆 `used` 不覆盖事实。
- 真实独立 Redis 验证覆盖 owner-token 在事务提交前被替换时,订单状态、终止退款和 Fleet Outbox 三类本地写入全部回滚SettlementFleetPlanLockCommitRedisIntegrationTest,本机 Redis 7.4 真实运行通过)。
- Fleet 真表 H2 IT 覆盖 reservation 生命周期与 terminate 截断隔离5 个;order-v3 真表 IT 覆盖 Outbox 最新命令因果查询。
- 双模块 reactor verifyorder-v3 7334 全绿;fleet 2914,仅 #5374 Docker MySQL 测试环境型失败(本机无 Docker
- 本单核心新增代码 changed-line coverage 90.5%≥90%;evidence 包含 raw JaCoCo、provider receipts 与 SHA-256 索引。
- MySQL 8.0.33 gate 凭据不可恢复,未运行(如实记录,不冒充通过)。
- PR 合并、TEST 部署与网关验收仍待完成,因此 `backend_status``gateway_status` 保持 `pending`
## 六、相关文档
- 关联 Issue: [wx/HL#5379](https://git.1814.love:8443/wx/HL/issues/5379)
- 关联 PR: [wx/HL#5417](https://git.1814.love:8443/wx/HL/pulls/5417)
## 关联 / 联系人
### 链接
- **Issue**: [#5379](https://git.1814.love:8443/wx/HL/issues/5379)
- **PR**: [#5417](https://git.1814.love:8443/wx/HL/pulls/5417)
- **Merge commit**: [39dfd80ae0](https://git.1814.love:8443/wx/HL/commit/39dfd80ae0)
### 联系人
- **后端负责人**: @wx