# 【⚠️修改接口·管理后台】终止行程接口改造(资源级退款计算) (#3556) > **PR**: #3557 | **服务**: hl-order-service-v3 | **更新时间**: 2026-06-06 > ⚠️ **破坏性变更**:入参 / 出参结构均调整,前端必须同步改造。 ## 1. 接口背景 终止行程原先只让定制师手填一个返还金额,既不准确也没真正进入核单结算。现改为**资源级退款计算**:定制师在弹框里选择「停在第几天」并逐项标记资源是否已用,提交时后端按实际成交价重新核算退款额并写入核单返还。 ## 2. 变更清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 终止行程 | POST | `/v3/admin/order/{id}/terminate` | 修改接口 | 入参从「手填返还额」改为「资源已用判定 + 调整额」;出参补退款明细字段 | ## 3. 接口详情 ### 3.1 终止行程 - **使用场景**:出行中订单提前终止时调用,配合「终止退款预览」接口(见同日新增接口 changelog)使用 - **认证**:需要管理端 JWT(`Authorization` 头) - **幂等性**:是(同一订单二次调用因状态已变为「已完成」返回 581018) - **限流**:无 ## 4. 接口入参 ### 4.1 路径参数 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | id | String | ✅ | 订单 ID | ### 4.2 请求体字段 | 字段 | 类型 | 必填 | 说明 | 校验规则 | |------|------|------|------|----------| | cancelReason | String | ✅ | 终止原因(调整金额的说明也写在此处,不再单列字段) | 非空 | | endDayNumber | Integer | ✅ | 停在第几天 | 1 ≤ 值 ≤ 行程总天数 | | rooms | Array<{refId, used}> | ✅ | 住宿各行已用判定 | — | | tickets | Array<{refId, used}> | ✅ | 门票各行已用判定 | — | | vehicles | Array<{refId, dayNumber, used}> | ✅ | 用车各行已用判定(同车跨天靠 dayNumber 区分) | — | | adjustAmount | BigDecimal | ❌ | 人工调整额(正=多退,负=少退),默认 0 | — | > `rooms[].refId` / `tickets[].refId` / `vehicles[].refId` 取自「终止退款预览」接口返回的 `refId`。 > 请求**只需传**资源是否已用(`used`)+ 调整额,**不传任何金额单价**——退款额由后端按预览同源的成交价重新核算(防篡改)。保险不在入参,由后端自动按锁定不退处理。 ### 4.2.1 rooms / tickets 行(ResourceUsedItem) | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | refId | String | ✅ | 资源记录 ID | | used | Boolean | ✅ | 该项是否已使用(true=已用不退 / false=未用可退) | ### 4.2.2 vehicles 行(VehicleUsedItem) | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | refId | String | ✅ | 用车记录 ID | | dayNumber | Integer | ✅ | 第几天 | | used | Boolean | ✅ | 当天该车是否已使用 | ## 5. 出参(响应) ### 5.1 响应字段(`data`) | 字段 | 类型 | 说明 | |------|------|------| | terminateRefundId | String | 终止退款记录 ID | | baselineRefund | BigDecimal | 系统按已用资源算出的建议退款额 | | adjustAmount | BigDecimal | 人工调整额(回显入参) | | finalRefund | BigDecimal | 最终退款额(= baselineRefund + adjustAmount,最低 0) | | settlementRefundId | String | 核单返还记录 ID(已写入核单 Step5 返还,供财务复核展示) | | newStatus | String | 终止后订单状态(`COMPLETED`=已完成) | | newFlowStatus | String | 终止后流程状态(`PENDING_REVIEW`=待核单) | ## 6. 枚举 / 数据字典 ### 6.1 newStatus(订单状态) **所属字段**:`newStatus` | **类型**:`String` | 值 | 中文 | 说明 | |----|------|------| | `COMPLETED` | 已完成 | 终止后订单进入完成态,转入核单流程 | ### 6.2 newFlowStatus(流程状态) **所属字段**:`newFlowStatus` | **类型**:`String` | 值 | 中文 | 说明 | |----|------|------| | `PENDING_REVIEW` | 待核单 | 终止后等待核单录入与结算复核 | ## 7. 错误码 | code | 含义 | 触发场景 | |------|------|----------| | 581018 | 出行中取消仅适用于出行中订单 | 订单当前状态非「出行中」时调用(含二次终止) | ## 8. 示例 ### 8.1 典型成功 **请求**: ``` POST /v3/admin/order/60001234567890/terminate Authorization: Bearer {token} ``` ```json { "cancelReason": "客户中途高反送医终止;林芝段酒店已付全款不可退,调整 -300", "endDayNumber": 3, "rooms": [ {"refId": "96011", "used": true}, {"refId": "96014", "used": false} ], "tickets": [ {"refId": "97001", "used": true} ], "vehicles": [ {"refId": "98001", "dayNumber": 1, "used": true}, {"refId": "98001", "dayNumber": 2, "used": true}, {"refId": "98001", "dayNumber": 3, "used": true} ], "adjustAmount": -300.00 } ``` **响应**: ```json { "code": 200, "message": "ok", "success": true, "data": { "terminateRefundId": "9610000001", "baselineRefund": 2264.00, "adjustAmount": -300.00, "finalRefund": 1964.00, "settlementRefundId": "9620000001", "newStatus": "COMPLETED", "newFlowStatus": "PENDING_REVIEW" } } ``` ### 8.2 边界情况 **场景说明**:不填调整额(默认 0),退款额 = 基线额。 **请求(片段)**: ```json { "cancelReason": "客户主动终止", "endDayNumber": 5, "rooms": [], "tickets": [], "vehicles": [] } ``` **响应(片段)**: ```json { "code": 200, "data": { "baselineRefund": 0.00, "adjustAmount": 0.00, "finalRefund": 0.00, "newStatus": "COMPLETED", "newFlowStatus": "PENDING_REVIEW" } } ``` ### 8.3 业务失败(异常) **场景说明**:订单非出行中(如已确认待出发或已终止过)。 **响应**: ```json { "code": 581018, "message": "出行中取消仅适用于出行中订单", "success": false } ``` ## 9. 业务边界 - ✅ **适用场景**:订单当前状态 = 出行中(TRAVELLING) - ❌ **不适用场景**:未出发订单请走「取消订单」流程;已终止/已完成订单二次调用返回 581018 - ⚠️ **特殊边界**: - `finalRefund` 最低为 0,不会出现负数 - 退款额最终进入核单返还,由财务复核环节确认放款 - 保险恒不退,无需在入参体现 ## 10. 修改前后对比 ### 10.1 字段级对比 **入参**: | 字段 | 改前 | 改后 | |------|------|------| | cancelReason | ✅ 终止原因 | ✅ 终止原因(+ 调整说明并入此处) | | returnAmount | ✅ 手填返还金额(必填) | ❌ **删除**(改由后端按资源核算) | | returnRemark | ✅ 返还备注(必填) | ❌ **删除**(说明并入 cancelReason) | | endDayNumber | — | ✅ **新增**,停在第几天 | | rooms / tickets / vehicles | — | ✅ **新增**,各资源行已用判定 | | adjustAmount | — | ✅ **新增**(可选),人工调整额 | **出参**: | 字段 | 改前 | 改后 | |------|------|------| | settlementRefundId | ✅(此前恒为 null) | ✅ 真实核单返还记录 ID | | returnAmount | ✅ 返还额(回显入参) | ❌ **删除** | | newStatus | ✅ | ✅(不变) | | terminateRefundId | — | ✅ **新增** | | baselineRefund / adjustAmount / finalRefund | — | ✅ **新增**,退款明细金额 | | newFlowStatus | — | ✅ **新增** | ### 10.2 行为级对比 | 行为 | 改前 | 改后 | |------|------|------| | 退款额来源 | 定制师手填一个数 | 按已用资源 + 实际成交价由后端核算 | | 退款是否进核单 | 否(仅记录,未落账) | 是(写入核单返还,财务复核放款) | | 配合接口 | 无 | 需先调「终止退款预览」拿资源清单 | ## 11. 影响评估 / 回滚 ### 11.1 影响评估 - **是否破坏向后兼容**:是。入参删除 `returnAmount` / `returnRemark`,新增多个必填字段;出参删除 `returnAmount`。前端按旧契约调用会校验失败。 - **前端是否必须同步上线**:是。终止行程页面需改造为「预览拉清单 → 选结束天 + 勾选已用 → 提交」流程。 - **前端 workaround 清理点**:若此前有「手填返还额」输入框,应替换为资源勾选界面;旧的 returnAmount/returnRemark 入参与回显逻辑可删除。 ### 11.2 回滚方案 - **回滚方式**:revert PR #3557 - **回滚后清理**:无需数据迁移(新功能,无历史数据依赖) ## 12. 注意事项 - ⚠️ 本接口为**破坏性改动**,前端务必在后端上线同期改造,否则终止行程功能不可用。 - 必须配合同日「终止退款预览」新增接口使用:预览拿全部资源清单 → 前端本地按结束天标已用/可退 → 提交时只回传 `used` 判定与 `adjustAmount`。 ## 13. 关联 / 联系人 ### 13.1 链接 - **Issue**: [#3556](https://git.1814.love:8443/wx/HL/issues/3556) - **PR**: [#3557](https://git.1814.love:8443/wx/HL/pulls/3557) - **Merge commit**: [773dcdacf](https://git.1814.love:8443/wx/HL/commit/773dcdacf) - **配套新增接口**: 终止退款预览(同日 changelog) ### 13.2 联系人 - **后端负责人**: @yst - **前端对接(管理后台)**: 终止行程弹框页面