# 退款申请管理接口说明（管理后台）

- **日期**: 2026-06-22
- **端类型**: 管理后台
- **接口路径前缀**: /v3/admin/refund
- **服务**: hl-order-service-v3（端口 8086，网关前缀 /v3）
- **说明**: 本文档为现有已上线接口的对接说明，非新版本改动

---

## 1. 接口背景

退款申请管理模块提供客服/管理员全生命周期操作退款申请的能力，包括代客户发起退款、分页查询、审核（支持多级审核，当前配置为单级）、触发实退、手动完成退款（测试/线下场景）、查退款执行记录、查状态流水。

存在两套退款通道：

- 申请单通道（/v3/admin/refund/application/*）：标准退款流程，含政策计算、多级审核、自动实退
- 直接退款通道（/v3/admin/refund/orders/{orderId} 和 /{refundId} 和 /order/{orderId}/list）：绕过申请单直接调微信退款 API，需 SUPER_ADMIN/ADMIN/FINANCE 角色，用于紧急场景

---

## 2. 变更清单

| 类型 | 接口 | 说明 |
|------|------|------|
| 接口说明 | POST /v3/admin/refund/application | 客服代下退款申请 |
| 接口说明 | GET /v3/admin/refund/application/page | 退款申请分页查询（14 个筛选维度） |
| 接口说明 | GET /v3/admin/refund/application/{id} | 退款申请详情（含审核/执行记录） |
| 接口说明 | POST /v3/admin/refund/review | 提交审核（APPROVED/REJECTED/PARTIAL） |
| 接口说明 | GET /v3/admin/refund/review/list/{appId} | 查审核历史 |
| 接口说明 | POST /v3/admin/refund/execute/{appId} | 触发实退（APPROVED->REFUNDING） |
| 接口说明 | PUT /v3/admin/refund/application/{applicationId}/complete | 手动完成退款（不调微信） |
| 接口说明 | GET /v3/admin/refund/application/{id}/status-log | 查退款状态流水 |
| 接口说明 | POST /v3/admin/refund/orders/{orderId} | 直接退款（绕申请单，需 FINANCE 角色） |
| 接口说明 | GET /v3/admin/refund/{refundId} | 退款记录详情 |
| 接口说明 | GET /v3/admin/refund/order/{orderId}/list | 按订单查询退款记录列表 |

---

## 3. 接口详情（通用）

- **认证**: JWT，Header Authorization: Bearer token，需管理员身份
- **直接退款通道**（POST /orders/{orderId}）额外需要角色：SUPER_ADMIN / ADMIN / FINANCE（由 token 中 role 字段判断）
- **幂等性**: 客服代下退款申请使用 DB 唯一索引防重（同一订单仅允许一条活跃申请，状态 PENDING/APPROVED/REFUNDING 为活跃）；触发实退支持 CAS 防重复触发；直接退款使用 @Idempotent 基于 orderId 的时间窗去重
- **Content-Type**: application/json
- **响应格式**: 统一 { "code": 200, "data": ..., "msg": "success" }

---

## 4. 接口入参

### 4.1 客服代下退款申请

POST /v3/admin/refund/application

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| orderId | Long(String) | 是 | 订单 ID |
| refundType | String | 否 | 退款类型，默认 FULL。可选值见第 6 节 RefundType |
| reasonId | Long | 否 | 退款原因 ID（从退款原因列表选择） |
| reasonText | String | 否 | 退款原因文本（选了 reasonId 时自动填充，也可手填） |
| reasonDetail | String | 否 | 原因详情/备注，自由文本 |
| policyId | Long | 否 | 指定退款政策 ID；不传则按订单绑定政策计算，政策为空时全额退款 |
| departureDate | String | 否 | 出发日期（yyyy-MM-dd），按政策计算时必填，用于计算距出发天数 |
| paidAmount | BigDecimal | 是 | 已付金额快照，用于政策计算基数，需 > 0 |
| requestedAmount | BigDecimal | 否 | 申请退款金额，PARTIAL 退款类型时必填 |
| mediaTraceIds | Array[String] | 否 | 媒体内容 traceId 列表（微信内容安全机审使用） |

### 4.2 退款申请分页查询

GET /v3/admin/refund/application/page（Query 参数）

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| pageNo | Integer | 否 | 页码，默认 1 |
| pageSize | Integer | 否 | 每页条数，默认 10 |
| orderId | Long | 否 | 订单 ID 精确匹配 |
| orderNo | String | 否 | 订单号模糊匹配 |
| applicantName | String | 否 | 申请人姓名模糊匹配 |
| applicantId | Long | 否 | 申请人 ID 精确匹配 |
| status | Array[String] | 否 | 退款状态多选，如 status=PENDING&status=APPROVED |
| auditStatus | String | 否 | 机审状态过滤，如 MANUAL_REVIEW |
| createTimeFrom | String | 否 | 创建时间起，格式 yyyy-MM-ddTHH:mm:ss，如 2026-04-01T00:00:00 |
| createTimeTo | String | 否 | 创建时间止 |
| refundedAtFrom | String | 否 | 退款完成时间起 |
| refundedAtTo | String | 否 | 退款完成时间止 |
| sortField | String | 否 | 排序字段：createTime/refundedAt/actualAmount，默认 createTime |
| sortOrder | String | 否 | 排序方向：ASC/DESC，默认 DESC |

### 4.3 退款申请详情

GET /v3/admin/refund/application/{id}

| 参数 | 位置 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| id | Path | Long(String) | 是 | 退款申请 ID |

### 4.4 提交审核

POST /v3/admin/refund/review

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| applicationId | Long(String) | 是 | 退款申请 ID |
| decision | String | 是 | 决议：APPROVED（全额通过）/ REJECTED（拒绝）/ PARTIAL（部分通过） |
| approvedAmount | BigDecimal | PARTIAL 时必填 | 同意退款金额；PARTIAL 时必填，APPROVED 时可选（不填按政策计算金额全退） |
| remark | String | REJECTED 时必填 | 审核备注/拒绝原因，合规要求 |

### 4.5 查审核历史

GET /v3/admin/refund/review/list/{appId}

| 参数 | 位置 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| appId | Path | Long(String) | 是 | 退款申请 ID |

### 4.6 触发实退

POST /v3/admin/refund/execute/{appId}

| 参数 | 位置 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| appId | Path | Long(String) | 是 | 退款申请 ID，必须为 APPROVED 状态 |

### 4.7 手动完成退款

PUT /v3/admin/refund/application/{applicationId}/complete

| 参数 | 位置 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| applicationId | Path | Long(String) | 是 | 退款申请 ID，必须为 REFUNDING 状态 |

### 4.8 查退款状态流水

GET /v3/admin/refund/application/{id}/status-log

| 参数 | 位置 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| id | Path | Long(String) | 是 | 退款申请 ID |

### 4.9 直接退款（绕申请单）

POST /v3/admin/refund/orders/{orderId}，需角色 SUPER_ADMIN / ADMIN / FINANCE。

| 参数/字段 | 位置 | 类型 | 必填 | 说明 |
|-----------|------|------|------|------|
| orderId | Path | Long(String) | 是 | 订单 ID |
| refundAmount | Body BigDecimal | 是 | 退款金额（元） |
| reason | Body String | 否 | 退款原因说明 |

### 4.10 退款记录详情

GET /v3/admin/refund/{refundId}

| 参数 | 位置 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| refundId | Path | Long(String) | 是 | 退款记录 ID |

### 4.11 按订单查询退款记录列表

GET /v3/admin/refund/order/{orderId}/list

| 参数 | 位置 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| orderId | Path | Long(String) | 是 | 订单 ID |

---

## 5. 出参字段

### 5.1 RefundApplicationDetailRespVO（申请详情，管理端完整版）

| 字段 | 类型 | 说明 |
|------|------|------|
| applicationId | String | 退款申请 ID（Long->String） |
| orderId | String | 订单 ID（Long->String） |
| refundType | String | 退款类型：FULL/PARTIAL/DEPOSIT/BALANCE（见第 6 节） |
| reasonId | String | 退款原因 ID |
| reasonText | String | 退款原因文本（快照） |
| reasonDetail | String | 退款原因详情 |
| paidAmount | String | 已付金额（String，前端用 BigNumber.js 处理） |
| calculatedAmount | String | 按政策计算退款金额 |
| actualAmount | String | 实际退款金额（最终决议） |
| policyId | String | 退款政策 ID |
| policyName | String | 退款政策名称（快照） |
| departureDate | String | 出发日期（yyyy-MM-dd） |
| daysBeforeDept | Integer | 距出发天数 |
| refundRatio | Integer | 退款比例（百分比） |
| applicantType | String | 申请人类型：USER/ADMIN/SYSTEM |
| applicantId | String | 申请人 ID |
| applicantName | String | 申请人姓名 |
| status | String | 申请状态枚举值（见第 6 节） |
| statusText | String | 状态中文展示，如「待审核」 |
| refundedAt | String | 退款完成时间 ISO |
| auditStatus | String | 机审状态：PENDING/APPROVED/MANUAL_REVIEW/REJECTED |
| auditStatusText | String | 机审状态中文 |
| mediaTraceIds | Array[String] | 媒体 traceId 列表 |
| createBy | String | 创建人 |
| updateBy | String | 更新人 |
| createTime | String | 创建时间 |
| updateTime | String | 更新时间 |
| orderInfo | Object | 订单概要快照（见下） |
| reviews | Array | 审核记录列表（按 reviewedAt ASC，见下） |
| records | Array | 退款执行记录列表（按 createTime ASC，见下） |

orderInfo（订单概要快照）：

| 字段 | 类型 | 说明 |
|------|------|------|
| orderNo | String | 订单号 |
| productName | String | 产品名称 |
| departureDate | String | 出发日期 |
| contactName | String | 联系人姓名 |
| contactPhone | String | 联系电话（管理端明文） |

reviews[i]（审核记录项）：

| 字段 | 类型 | 说明 |
|------|------|------|
| reviewId | String | 审核 ID |
| reviewLevel | Integer | 审核级别：1/2/3（当前单级=1） |
| reviewerId | String | 审核人 ID |
| reviewerName | String | 审核人姓名 |
| reviewerRole | String | 审核人角色：CS/SUPERVISOR/EXEC |
| decision | String | 决议：APPROVED/REJECTED/PARTIAL |
| decisionText | String | 决议中文，如「已通过」 |
| approvedAmount | String | 同意退款金额（PARTIAL 时有值） |
| remark | String | 审核备注 |
| reviewedAt | String | 审核时间 |
| createTime | String | 记录创建时间 |

records[i]（退款执行记录项）：

| 字段 | 类型 | 说明 |
|------|------|------|
| refundId | String | 退款记录 ID |
| applicationId | String | 退款申请 ID |
| transactionId | String | 关联支付交易 ID |
| orderNo | String | 订单号 |
| paymentNo | String | 关联支付单号 |
| refundChannel | String | 退款渠道：WECHAT/OFFLINE/BALANCE（见第 6 节） |
| refundChannelText | String | 退款渠道中文，如「微信退款」 |
| mchId | String | 商户号 |
| outRefundNo | String | 商户退款单号 |
| refundIdWx | String | 微信退款单号 |
| refundAmount | String | 本次退款金额（元，String） |
| totalAmount | String | 该交易总金额（元，String） |
| status | String | 退款记录状态：PENDING/SUCCESS/FAILED/ABNORMAL/CLOSED（见第 6 节） |
| statusText | String | 状态中文，如「已成功」 |
| failReason | String | 失败原因（status=FAILED 时有值） |
| refundedAt | String | 退款完成时间 |
| createTime | String | 创建时间 |

### 5.2 RefundApplicationPageItemRespVO（分页列表项，瘦身版）

| 字段 | 类型 | 说明 |
|------|------|------|
| applicationId | String | 退款申请 ID |
| orderId | String | 订单 ID |
| orderNo | String | 订单号 |
| refundType | String | 退款类型 |
| reasonText | String | 退款原因文本 |
| paidAmount | String | 已付金额 |
| calculatedAmount | String | 政策计算金额 |
| actualAmount | String | 实际退款金额 |
| applicantType | String | 申请人类型 |
| applicantId | String | 申请人 ID |
| applicantName | String | 申请人姓名 |
| status | String | 申请状态枚举 |
| statusText | String | 状态中文 |
| auditStatus | String | 机审状态 |
| reviewerNameLast | String | 最后审核人姓名（摘要） |
| refundRecordCount | Integer | 退款执行记录数量 |
| refundedAt | String | 退款完成时间 |
| createTime | String | 创建时间 |
| updateTime | String | 更新时间 |

分页响应外层结构：{ "code": 200, "data": { "list": [...], "total": 100, "pageNo": 1, "pageSize": 10 } }

### 5.3 RefundStatusLogRespVO（状态流水）

| 字段 | 类型 | 说明 |
|------|------|------|
| total | Integer | 总记录数 |
| records | Array | 流水列表（按 changedAt ASC） |
| records[i].logId | String | 日志 ID |
| records[i].applicationId | String | 退款申请 ID |
| records[i].fromStatus | String | 变更前状态枚举 |
| records[i].fromStatusText | String | 变更前状态中文 |
| records[i].toStatus | String | 变更后状态枚举 |
| records[i].toStatusText | String | 变更后状态中文 |
| records[i].eventType | String | 事件类型（见第 6 节 RefundLogEventType） |
| records[i].eventTypeText | String | 事件类型中文 |
| records[i].operatorType | String | 操作人类型：USER/ADMIN/SYSTEM/OA |
| records[i].operatorId | String | 操作人 ID |
| records[i].operatorName | String | 操作人姓名 |
| records[i].reason | String | 理由/备注 |
| records[i].extra | Object | 附加快照（按 eventType 路由，见第 6 节 extra 约定） |
| records[i].changedAt | String | 变更时间 |

### 5.4 RefundRecordVO（直接退款记录）

| 字段 | 类型 | 说明 |
|------|------|------|
| refundId | Long | 退款 ID（此处为 Long，前端接收需注意精度，建议当 String 处理） |
| transactionId | Long | 关联交易 ID |
| orderId | Long | 订单 ID |
| orderNo | String | 订单号 |
| mchId | String | 商户号 |
| outRefundNo | String | 商户退款单号 |
| refundIdWx | String | 微信退款单号 |
| refundAmount | BigDecimal | 退款金额（元） |
| totalAmount | BigDecimal | 订单总金额（元） |
| reason | String | 退款原因 |
| status | String | 退款状态：PENDING/SUCCESS/FAILED/ABNORMAL |
| successTime | String | 退款成功时间 |
| createTime | String | 创建时间 |

---

## 6. 枚举 / 数据字典

### 6.1 RefundApplicationStatus（退款申请状态）

| 枚举值 | 中文 | 说明 |
|--------|------|------|
| PENDING | 待审核 | 申请已提交，等待审核 |
| APPROVED | 已通过 | 审核通过，可触发实退 |
| REJECTED | 已拒绝 | 审核拒绝，终态 |
| REFUNDING | 退款中 | 实退已触发，等待微信回调 |
| REFUNDED | 已退款 | 退款完成，终态 |
| CANCELLED | 已取消 | 申请被撤回，终态 |
| ABNORMAL | 退款异常 | 实退异常（微信接口失败等），需人工处理 |

活跃状态：PENDING + APPROVED + REFUNDING。同一订单只允许存在一条活跃申请（DB 唯一索引保证），重复提交返回 530004。

### 6.2 RefundType（退款类型）

| 枚举值 | 中文 | 说明 |
|--------|------|------|
| FULL | 全额退款 | 退全部已付金额 |
| PARTIAL | 部分退款 | 部分退款，需指定金额 |
| DEPOSIT | 订金退款 | 仅退定金部分 |
| BALANCE | 尾款退款 | 仅退尾款部分 |

### 6.3 RefundApplicantType（申请人类型）

| 枚举值 | 中文 | 说明 |
|--------|------|------|
| USER | 用户 | C 端用户自主申请 |
| ADMIN | 管理员 | 客服代下 |
| SYSTEM | 系统 | 订单取消时系统自动发起 |

### 6.4 退款渠道（refundChannel）

| 值 | 中文 | 说明 |
|----|------|------|
| WECHAT | 微信退款 | 原路退回微信支付 |
| OFFLINE | 线下退款 | 线下转账退款 |
| BALANCE | 余额退款 | 退到用户平台余额 |

### 6.5 退款记录状态（refund_record.status）

| 值 | 中文 | 说明 |
|----|------|------|
| PENDING | 处理中 | 退款请求已发送，等待微信回调 |
| SUCCESS | 已成功 | 微信回调确认退款成功 |
| FAILED | 已失败 | 微信退款失败 |
| ABNORMAL | 异常 | 异常状态，等待对账 Job 补偿 |
| CLOSED | 已关闭 | 微信退款单被关闭，资金原路退回，终态 |

### 6.6 RefundLogEventType（状态流水事件类型）及 extra 约定

| 枚举值 | 中文 | extra 字段内容 |
|--------|------|----------------|
| APPLY | 发起申请 | { "reasonId": "xxx", "calculatedAmount": "800.00", "refundRatio": 80, "mediaTraceIds": [] } |
| REVIEW | 审核 | { "reviewId": "xxx", "level": 1, "decision": "APPROVED", "approvedAmount": "800.00", "remark": "" } |
| OA_CALLBACK | OA 回调 | { "approvalNo": "xxx", "spStatus": 2, "approvedAmount": "800.00", "approverName": "张三" } |
| REFUND_EXECUTE | 退款执行 | { "recordIds": ["111", "222"], "totalAmount": "800.00" } |
| AUDIT_UPDATE | 机审状态更新 | { "newAuditStatus": "APPROVED", "traceIds": [] } |

### 6.7 审核决议（decision）

| 值 | 中文 | 说明 |
|----|------|------|
| APPROVED | 已通过 | 全额通过（按政策计算金额） |
| REJECTED | 已拒绝 | 拒绝，需填 remark |
| PARTIAL | 部分通过 | 同意部分金额，需填 approvedAmount |

---

## 7. 错误码

| 错误码 | 说明 | 触发场景 |
|--------|------|----------|
| 530001 | 无效的退款类型 | refundType 传了非枚举值 |
| 530002 | 该订单暂无可退款金额 | 订单已付金额为 0 |
| 530003 | 无权操作此订单 | 管理员无权限操作该订单 |
| 530004 | 该订单已有退款申请处理中，请勿重复提交 | 同订单存在 PENDING/APPROVED/REFUNDING 状态申请 |
| 530005 | 无权操作此退款申请 | 越权操作 |
| 530101 | 无权查看此退款申请 | 越权查询 |
| 530201 | 当前状态不允许审批，仅待审核状态可审批 | 非 PENDING 状态时尝试审核 |
| 530202 | 实际退款金额不能超过已付金额 | approvedAmount 超出 paidAmount |
| 530203 | 累计退款金额超过已付金额 | 多次退款累计超出已付 |
| 530204 | 仅退款中状态可手动完成退款 | 非 REFUNDING 状态时调 complete 接口 |
| 530401 | 退款申请当前状态不允许审核，仅 PENDING 状态可审核 | 重复审核或状态不符 |
| 530402 | 审核决议必填 | decision 为空 |
| 530403 | PARTIAL 决议必须填写同意退款金额 | PARTIAL 时未填 approvedAmount |
| 530404 | 审核同意金额超出可退额度 | approvedAmount > 可退余额 |
| 530410 | 当前退款申请状态不允许触发实退，仅已通过的申请可触发 | 非 APPROVED 状态触发 execute |
| 530411 | 退款已触发，请勿重复操作 | CAS 防重，execute 接口重复点击 |
| 530412 | 退款状态转换非法 | 状态机约束，非法状态流转 |
| 530417 | 模拟退款通道已禁用 | 生产环境不允许调模拟退款 |

---

## 8. 示例

### 8.1 典型成功：客服代下退款申请

请求：POST /v3/admin/refund/application，Header Authorization: Bearer admin-token

请求体：



响应：



### 8.2 边界情况：分页查询，筛选待审核+机审需人工

请求：GET /v3/admin/refund/application/page?pageNo=1&pageSize=20&status=PENDING&auditStatus=MANUAL_REVIEW&sortField=createTime&sortOrder=DESC

响应：



### 8.3 业务失败：重复提交退款申请

请求：POST /v3/admin/refund/application，orderId 已有活跃申请

响应：



---

## 9. 业务边界

适用场景：
- 客服在管理后台代客户发起退款，系统自动按政策计算可退金额
- 管理员在退款列表页审核（单级，当前无需多级流程）
- 金融人员/管理员使用直接退款通道处理紧急退款场景
- 手动完成退款仅用于测试订单或线下退款，正常流程不应使用

不适用场景：
- 小程序 C 端退款走 /v3/mp/refund/application（独立接口，强 IDOR 校验）
- 退款详情接口（/application/{id}）禁止直接转发给小程序（含联系电话等管理端明文信息）

特殊边界：
- 审核通过（APPROVED）后，系统自动异步触发实退（调微信退款 API），通常不需手动点「触发实退」按钮；手动触发作为补充入口
- PARTIAL 审核通过后实退金额以 approvedAmount 为准，不是 calculatedAmount
- 同一订单仅允许一条活跃申请（PENDING/APPROVED/REFUNDING），已完结（REFUNDED/REJECTED/CANCELLED）的申请不占用，可再次申请
- 审核通过后发起的退款按支付交易拆分多条 refund_record，records 列表可能有多条

---

## 10. 修改前后对比

本文档为对接说明文档，无改动历史，跳过本节。

---

## 11. 影响评估 / 回滚

本文档为对接说明文档，跳过本节。

---

## 12. 注意事项

1. 金额字段均为 String 类型（申请单通道）：paidAmount / calculatedAmount / actualAmount / refundAmount / approvedAmount 在 JSON 中均为 String，请用 BigNumber.js 处理，不要用 JS 原生 Number
2. 直接退款通道（/orders/{orderId}）的 RefundRecordVO 中 refundId / transactionId / orderId 为 Long 类型，如值超过 JS 安全整数范围需前端注意
3. 状态流水 extra 字段：各 eventType 对应不同结构（见第 6.6 节），前端按 eventType 路由渲染对应展示逻辑
4. 触发实退是异步的：POST /execute/{appId} 返回成功表示已触发，实际退款完成通过微信回调异步通知，状态会从 REFUNDING 变为 REFUNDED；前端列表页需刷新查看最终状态
5. 测试环境模拟退款：测试服开启了 hulai.refund.simulate-enabled=true，退款不真实调微信；生产环境关闭，调用真实微信退款 API

---

## 13. 关联 / 联系人

- **服务**: hl-order-service-v3（端口 8086）
- **后端负责人**: 腰苏图
- **Gitea 项目**: https://git.1814.love:8443/wx/HL