hl-api-changelog/changelogs-v2/2026-06/26_3556_终止行程接口终态汇总-修改接口-管理后台.md
yaosutu de1292823a docs(changelog-v2): 终止行程接口终态汇总(管理后台 #3556)
合并 #3556 系列 3 篇 + #3965 金额String化 为一份终态,供前端一处对接:
- POST /v3/admin/order/{id}/terminate/refund-preview 终止退款预览
- POST /v3/admin/order/{id}/terminate 终止行程(执行)
点明: 旧路径 cancel/on-trip 已删返404; 金额+LongID 全String化;
终态 COMPLETED+PENDING_REVIEW(非CANCELLED); 581007/581018 错误码。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 10:04:32 +08:00

12 KiB

订单详情「终止行程」接口终态汇总(出行中订单)—— 管理后台

  • 端类型:管理后台
  • 变更类型:修改接口(终态汇总篇,无新契约变化;合并 #3556 系列 3 篇 + #3965 金额 String 化)
  • 关联 Issue#3556 PR#3557主改造/ #3559预览状态校验加固/ #3965金额 String 化)
  • 日期2026-06-26

① 接口背景

「终止行程」用于出行中TRAVELLING订单提前中止:定制师按"停在第几天 + 逐项资源是否已用"提交,后端按实际成交价权威核算退款额、写入核单返还,订单转为「已完成→待核单」。

此前该能力的 changelog 分散在 3 篇(接口改造 / 退款预览新增 / 旧路径下线),且金额格式在 #3965 后由数字改为字符串。本篇为一处对接的终态汇总,前端按此一份即可对接,旧 3 篇仅作历史留档。

⚠️ 旧路径 POST /v3/admin/order/{id}/cancel/on-trip 已删除,调用返回 404,必须改调 /terminate⚠️ 所有 Long ID 与金额均字符串化返回(防 JS 精度丢失),请求传金额相关 ID 也用字符串。


② 变更清单(两个接口配套使用)

# 方法 路径 用途
1 POST /v3/admin/order/{id}/terminate/refund-preview 终止退款预览(拿全资源清单,前端本地算退款)
2 POST /v3/admin/order/{id}/terminate 终止行程(提交执行,落核单返还)

统一响应 Result<T>{ code, message, data, success }code=200 成功。认证:管理端 JWTAuthorization 头)。

对接流程:点「终止行程」→ 调①拿整趟资源清单 → 前端按所选结束天本地标已用/可退并算退款 → 调②只回传 used 判定 + adjustAmount(不传金额单价,后端权威重算防篡改)。


③ 接口 1终止退款预览

POST /v3/admin/order/{id}/terminate/refund-preview
  • 入参:仅 path id(订单 ID无请求体。一次性拿整趟全部资源,结束天选择/已用标记/退款计算全在前端本地完成,不需每选一天再调。
  • 幂等:是(只读,不落库)。

出参 Result<OrderTerminateRefundPreviewRespVO>

字段 类型 说明
paidAmount String(金额) 客户已付金额(前端算退款基线用)
tripDays Integer 行程总天数(画时间轴)
departDate String(date) 出发日期 yyyy-MM-dd(第 N 天日期 = 出发日 + N1
tripCurrentDay Integer 当前到第几天(时间轴默认选中点;出发前/未知=1
rooms Array<Item> 住宿清单(每晚×每组房一行)
tickets Array<Item> 门票清单(每景点每天一行)
vehicles Array<Item> 用车清单(整车按天展开,每天一行,同段车 refId 相同)
insurance Array<Item> 保险清单(整单 1 行,locked=true 锁定不退)

ItemTerminateRefundItemVO,四类资源行通用

字段 类型 说明
refId String 资源配单记录 ID提交终止时按此回传已用判定;用车多天共用同一 refId 靠 dayNumber 区分;保险可能为 null
name String 资源名称快照(酒店名/景点名/车型/保险产品名)
dayNumber Integer 对应第几天(保险为 null
dealPrice String(金额) 成交单价(住宿=每间每晚 / 门票=每张 / 用车=每天每辆日租 / 保险=保费)
quantity Integer 数量(住宿=间数 / 门票=张数 / 用车=车辆数 / 保险=1
totalAmount String(金额) 本行小计 = dealPrice × quantity
locked Boolean 是否锁定不退(保险恒 true,其余 false
lockedReason String 锁定原因(如"保险已生效不退";未锁定为 null

响应不含"是否已用"标记与"退款合计"——由前端按所选结束天(dayNumber ≤ 结束天 默认已用,之后默认可退)实时计算。


④ 接口 2终止行程执行

POST /v3/admin/order/{id}/terminate
Content-Type: application/json
  • 幂等:是(订单一旦终止变 COMPLETED,二次调用因状态非 TRAVELLING 返 581018

入参 BodyOrderTerminateTripReqVO

字段 类型 必填 说明 校验
cancelReason String 终止原因(调整额说明并入此处,不单列字段) 非空
endDayNumber Integer 停在第几天截断点,1-based ≥ 1 ≤ 行程总天数
rooms Array<{refId, used}> 住宿各行已用判定(列表非 null,可空数组
tickets Array<{refId, used}> 门票各行已用判定
vehicles Array<{refId, dayNumber, used}> 用车各行已用判定(同车跨天靠 dayNumber 区分)
adjustAmount String(金额) 人工调整额(±,正=多退 负=少退),默认 0

rooms / tickets 行ResourceUsedItemrefId(String,必填) + used(Boolean,必填 true=已用不退/false=未用可退) vehicles 行VehicleUsedItemrefId(String,必填) + dayNumber(Integer,必填≥1) + used(Boolean,必填)

refId 全部取自接口①返回值。入参只传 used 判定 + adjustAmount,不传任何金额单价——退款额由后端按预览同源成交价权威重算。保险不在入参,后端自动按锁定不退处理。

出参 Result<OrderTerminateTripRespVO>

字段 类型 说明
terminateRefundId String 终止退款主表 ID
baselineRefund String(金额) 系统建议基线 = max(0, paidAmount usedAmount)
adjustAmount String(金额) 人工调整额(回显入参,默认 0
finalRefund String(金额) 最终返还额 = max(0, baselineRefund + adjustAmount),最低 0
settlementRefundId String 核单返还记录 ID核单模块写入后回填
newStatus String 终止后订单粗状态(恒 COMPLETED
newFlowStatus String 终止后流程细状态(恒 PENDING_REVIEW

⑤ 枚举 / 数据字典

  • newStatusCOMPLETED 已完成(终止后进完成态转核单,不再是旧的 CANCELLED
  • newFlowStatusPENDING_REVIEW 待核单(等核单录入与结算复核)
  • 资源分类:通过 rooms/tickets/vehicles/insurance 四数组区分,无独立 resourceType 字段
  • lockedtrue=锁定不退(保险恒 true/ false=可退

⑥ 错误码

code 含义 触发场景
581007 订单不存在 id 无对应订单
581018 出行中取消仅适用于出行中订单 订单当前状态非 TRAVELLING未出发/已终止/已完成,含二次终止)

⑦ 示例

接口①响应(预览,节选)

{ "code": 200, "success": true, "data": {
  "paidAmount": "6000.00", "tripDays": 7, "departDate": "2026-05-12", "tripCurrentDay": 3,
  "rooms": [ {"refId":"96011","name":"拉萨瑞吉·大床房","dayNumber":1,"dealPrice":"1280.00","quantity":1,"totalAmount":"1280.00","locked":false,"lockedReason":null} ],
  "vehicles": [ {"refId":"98001","name":"丰田赛那 商务车","dayNumber":1,"dealPrice":"600.00","quantity":1,"totalAmount":"600.00","locked":false,"lockedReason":null} ],
  "insurance": [ {"refId":null,"name":"安联境内旅行险·尊享版","dayNumber":null,"dealPrice":"256.00","quantity":1,"totalAmount":"256.00","locked":true,"lockedReason":"保险已生效不退"} ]
} }

接口②请求(终止)

{
  "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"
}

接口②响应(终止)

{ "code": 200, "success": true, "data": {
  "terminateRefundId": "9610000001", "baselineRefund": "2264.00", "adjustAmount": "-300.00",
  "finalRefund": "1964.00", "settlementRefundId": "9620000001",
  "newStatus": "COMPLETED", "newFlowStatus": "PENDING_REVIEW" } }

边界(不填调整额,默认 0

请求: { "cancelReason":"客户主动终止","endDayNumber":5,"rooms":[],"tickets":[],"vehicles":[] }
响应: { "code":200,"data":{ "baselineRefund":"0.00","adjustAmount":"0.00","finalRefund":"0.00","newStatus":"COMPLETED","newFlowStatus":"PENDING_REVIEW" } }

异常 —— 调旧路径(前端联调易踩)

POST /v3/admin/order/{id}/cancel/on-trip  →  HTTP 404 Not Found

旧路径后端无任何映射,不做 301/308 跳转,必须改调 /terminate

异常 —— 订单非出行中

{ "code": 581018, "message": "出行中取消仅适用于出行中订单", "success": false }

⑧ 业务边界

  • 适用:订单当前粗状态 = TRAVELLING出行中
  • 不适用:未出发订单走「取消订单」流程(cancel/pre-trip);已终止/已完成二次调用返 581018
  • ⚠️ finalRefund 最低 0,不会负数;退款额进核单返还由财务复核放款;保险恒不退
  • ⚠️ 终止后订单是「已完成(待核单)」而非「已取消」,列表/详情状态展示需按新枚举对齐

⑨ 修改前后对比(相对最早 cancel/on-trip 旧实现)

维度 改前 改后(终态)
路径 POST /{id}/cancel/on-trip POST /{id}/terminate(旧路径 404
退款预览 无独立预览 POST /{id}/terminate/refund-preview(新增)
退款额来源 定制师手填一个数 后端按已用资源 + 成交价权威核算
退款是否进核单 否(仅记录) 是(写核单返还,财务复核放款)
入参 returnAmount/returnRemark 手填 endDayNumber + rooms/tickets/vehicles 已用判定 + adjustAmount
出参 returnAmount 回显 terminateRefundId/baselineRefund/adjustAmount/finalRefund/settlementRefundId/newFlowStatus
金额/ID 格式 数字 字符串(#3965 后)
终止后状态 CANCELLED 已取消 COMPLETED 已完成 → PENDING_REVIEW 待核单

⑩ 影响评估 / 回滚

  • 接口已稳定上线(#3557/#3559/#3965 均已合并部署),本篇为终态汇总,不含新后端改动,无回滚项。
  • 前端「终止行程」页面对接:① 进入调①拿资源清单 → ② 选结束天 + 勾选已用 + 可选调整额 → ③ 调②提交 → ④ 终态按「已完成/待核单」展示并刷新详情。
  • 旧 3 篇(06_3556_终止行程接口改造06_3556_终止退款预览11_3556_出行中取消路径下线)保留历史留档,对接以本篇为准。

⑪ 注意事项

  • Long ID + 金额terminateRefundId/baselineRefund/finalRefund/paidAmount/refId 等)均字符串化,请求/解析均按字符串。
  • 写操作需前端按钮防重复点击 + loading。
  • 必须先调预览拿 refId,再提交终止;提交不传金额单价后端权威重算

⑫ 关联 / 联系人

  • Issuewx/HL#3556
  • PRwx/HL#3557wx/HL#3559wx/HL#3965
  • 历史留档:changelogs-v2/2026-06/06_3556_终止行程接口改造-修改接口-管理后台.md 等 3 篇
  • 后端负责人:腰苏图
  • 接口已部署测试服,Knife4j 可见(/v3/admin/order/{id}/terminate/terminate/refund-preview)。