# 【修改接口·管理后台】终止行程·退款计算弹窗:预览补字段 + 提交下界校验 (#4548 #4563) > **PR**: #4551 #4564 | **服务**: hl-order-service-v3 | **更新时间**: 2026-06-28 ## 1. 接口背景 「终止行程·退款计算」弹窗(出行中订单点终止时打开)涉及两个接口:预览(拉资源清单 + 选结束日)、提交(确认终止 + 算退款)。本次两点优化: 1. **预览接口**:弹窗需要渲染「客户在哪天结束行程」的日期按钮排(第 1 天 / 第 2 天 …),以及头部展示订单基本信息(订单号 / 客户 / 产品 / 团号 / 定制师)。原先这些都要前端自己拼,现在后端直接给。 2. **提交接口**:原先后端不校验「结束日之前的资源是否标记已用」,可能把已经发生的天数算成可退款。现在加了下界校验兜底。 ## 2. 变更清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 终止退款预览 | POST | `/v3/admin/order/{id}/terminate/refund-preview` | 修改接口 | 出参新增 `days` 数组 + 5 个订单基本信息字段 + `outstandingBalance` 应收尾款 | | 2 | 终止行程提交 | POST | `/v3/admin/order/{id}/terminate` | 修改接口 | 新增错误码 581044 + 入参约束「结束日前的资源必须标记已用」 + 新增必填入参 `onsiteBalance` 现场实收尾款 | > 两接口入参主体结构不变,均为出参/校验增量,向后兼容。 --- ## 3. 接口详情 ### 3.1 终止退款预览(POST `/v3/admin/order/{id}/terminate/refund-preview`) - **使用场景**:出行中订单点「终止行程」打开弹窗时调用,拉取资源清单、结束日选项、订单基本信息。 - **认证**:需要管理后台 JWT。 - **前置**:订单状态必须为「出行中(TRAVELLING)」,否则返回错误。 - **本次变更**:纯出参新增,无破坏性。 **入参**(不变) | 位置 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------|------| | path | `id` | Long | ✅ | 订单 ID | 无请求体。 **出参新增字段** | 字段 | 类型 | 说明 | |------|------|------| | `orderNo` | String | 订单号 | | `customerName` | String | 客户名 | | `productName` | String | 产品名称 | | `batchNo` | String | 团号(团期订单有值;**核心订单为 `null`**) | | `consultantName` | String | 定制师 | | `outstandingBalance` | String | 应收尾款(= 应收总额 − 净已付,clamp ≥ 0;已取消单为 0)。"该收多少",与提交时录入的现场实收 `onsiteBalance` 对照 | | `days` | Array | 结束日期选项(见下表),范围 `1..tripCurrentDay`,**不含「未出行」** | `days[]` 元素字段 | 字段 | 类型 | 说明 | |------|------|------| | `dayNumber` | Integer | 第几天(从 1 开始) | | `label` | String | 中文展示名,如「第 1 天」 | | `date` | String(日期) | 该天日期(= 出发日期 + dayNumber - 1) | | `isCurrent` | Boolean | 是否当前天(= tripCurrentDay),前端默认选中此项 | > 原有字段 `paidAmount` / `tripDays` / `departDate` / `tripCurrentDay` / `rooms` / `tickets` / `vehicles` / `insurance` 全部保留不变。 **响应示例**(典型成功,团期订单 6 天行程、当前到第 3 天) ```json { "code": 200, "message": "success", "data": { "orderNo": "HL202605120001", "customerName": "张三", "productName": "西藏拉林环线 6 日定制", "batchNo": "GB20260512-008", "consultantName": "李顾问", "paidAmount": "6000.00", "outstandingBalance": "0.00", "tripDays": 6, "departDate": "2026-05-12", "tripCurrentDay": 3, "days": [ { "dayNumber": 1, "label": "第 1 天", "date": "2026-05-12", "isCurrent": false }, { "dayNumber": 2, "label": "第 2 天", "date": "2026-05-13", "isCurrent": false }, { "dayNumber": 3, "label": "第 3 天", "date": "2026-05-14", "isCurrent": true } ], "rooms": [], "tickets": [], "vehicles": [], "insurance": [] }, "success": true } ``` > 核心订单(非团期)`batchNo` 为 `null`,其余结构一致。 **业务边界** - ✅ 订单状态 = 出行中(TRAVELLING)时可调。 - ❌ 非出行中订单调用 → 返回「出行中取消仅适用于出行中订单」错误。 - ⚠️ `days` 不含「未出行(第 0 天)」:未出行属于「取消订单」链路,不在终止行程范围内。 --- ### 3.2 终止行程提交(POST `/v3/admin/order/{id}/terminate`) - **使用场景**:弹窗内确认终止,提交各资源「是否已用」+ 结束天,后端算退款并终止订单。 - **认证**:需要管理后台 JWT。 - **本次变更**:新增一条下界校验 + 对应错误码;入参字段结构不变。 **入参约束变更(重点)** 新增校验:**结束日之前(`dayNumber < endDayNumber`)的资源行,`used` 必须为 `true`**;否则返回错误码 **581044**。 - `dayNumber < endDayNumber`(结束日之前已发生的天)→ `used` 必须 `true`(不可退)。 - `dayNumber >= endDayNumber`(结束当天及之后)→ `used` 自由(默认 `false`,也可手动标 `true`,如当天酒店已入住)。 > 与现有前端交互一致:前端选定结束天后「自动标记前 N 天资源为已用」,天然满足此约束,正常不会触发 581044。仅当传入与结束天矛盾的 `used` 时才会被拒。 **入参字段表**(结构不变,列出供对照) | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `cancelReason` | String | ✅ | 终止原因 + 调整说明 | | `endDayNumber` | Integer | ✅ | 停在第几天(≥ 1) | | `rooms` | Array | ✅ | 住宿已用判定 `[{refId, used}]` | | `tickets` | Array | ✅ | 门票已用判定 `[{refId, used}]` | | `vehicles` | Array | ✅ | 用车每天已用判定 `[{refId, dayNumber, used}]` | | `adjustAmount` | Decimal | ❌ | 人工调整额(默认 0,允许负) | | `onsiteBalance` | Decimal | ✅ | **现场实收尾款**(定制师终止时现场实收的尾款,必填)。纯记录:不参与退款计算、不影响已付金额,仅留存供核单/财务对照(与预览的 `outstandingBalance` 应收尾款相对) | **错误码** | code | 含义 | 触发场景 | |------|------|----------| | 581044 | 终止行程:结束日之前的资源必须标记为已使用 | 提交时存在 `dayNumber < endDayNumber` 的资源行被标 `used=false` | **示例** 8.1 业务失败(异常)——触发 581044 **请求**(结束天=2,但第 1 天住宿被标未用): ```json { "cancelReason": "QA测试", "endDayNumber": 2, "rooms": [ { "refId": "96011", "used": false } ], "tickets": [], "vehicles": [] } ``` **响应**: ```json { "code": 581044, "message": "终止行程:结束日之前的资源必须标记为已使用", "success": false } ``` > 触发 581044 时订单不会被终止、不产生退款(校验在落库前拦截)。 **业务边界** - ✅ 结束当天及之后的资源可自由标记已用 / 未用。 - ❌ 结束日之前的资源标记未用 → 581044。 - ⚠️ 保险行不参与此校验(保险整单不可退,由后端固定处理,无需前端传入)。 --- ## 11. 影响评估 - **是否破坏向后兼容**:否。预览为出参增量;提交为新增校验,与现有「前 N 天自动标已用」前端交互一致,正常流程不受影响。 - **前端是否必须同步上线**:否(预览新字段不消费则忽略;提交保持现有标记逻辑即可)。建议前端接入 `days` 后撤掉自行用 `tripDays + departDate` 构造日期按钮的逻辑。 ## 12. 注意事项 - 前端可清理 workaround:原先用 `tripDays + departDate` 循环构造结束日按钮、自行算每天日期的逻辑,可改为直接渲染后端返回的 `days` 数组。 - `batchNo` 在核心订单为 `null`,前端头部展示需做空值兜底。 ## 13. 关联 / 联系人 ### 13.1 链接 - **Issue**: [#4548](https://git.1814.love:8443/wx/HL/issues/4548) / [#4563](https://git.1814.love:8443/wx/HL/issues/4563) - **PR**: [#4551](https://git.1814.love:8443/wx/HL/pulls/4551) / [#4564](https://git.1814.love:8443/wx/HL/pulls/4564) - **Merge commit**: [a66e78143](https://git.1814.love:8443/wx/HL/commit/a66e78143) / [37e52315d](https://git.1814.love:8443/wx/HL/commit/37e52315d) ### 13.2 联系人 - **后端负责人**: @yst - **前端对接(管理后台)**: 终止行程·退款计算弹窗