# 退款待办工作台后端（统计 + 列表增强 + 失败重新发起）

- 端类型：管理后台
- 变更类型：新增接口（2）+ 修改接口（1）
- 关联 Issue：#4226　PR：#4245
- 日期：2026-06-22

---

## ① 接口背景

订单控制台「退款待办」是财务/管理员的退款审批+监控工作台，按状态分组（待审批 / 退款中 / 失败待处理 / 已完成）展示并处理退款申请。本次后端在已有退款申请（refund_application）能力上补齐三块：顶部统计卡片、列表展示字段与搜索增强、失败退款的重新发起。基于现有退款链路（审批 → 自动原路退回 → 微信回调 → 对账），无新建主流程。

---

## ② 变更清单

| # | 方法 | 路径 | 类型 |
|---|---|---|---|
| 1 | GET | `/v3/admin/refund/application/stats` | 🆕 新增 |
| 2 | POST | `/v3/admin/refund/record/{refundId}/resubmit` | 🆕 新增 |
| 3 | GET | `/v3/admin/refund/application/page` | ✏️ 修改（出参增字段 + 入参增搜索） |

统一响应包装 `Result<T>`：`{ code, message, data, traceId, success }`，`code=200` 为成功。

---

## ③ 接口详情

### 1. 退款待办统计　`GET /v3/admin/refund/application/stats`
返回各状态分组数量+金额 + 本月已退款汇总，供工作台顶部卡片。

### 2. 重新发起退款　`POST /v3/admin/refund/record/{refundId}/resubmit`
对「失败待处理」的退款**记录**重新发起退款。仅 record 状态为 `FAILED`（微信退款执行失败终态）可重发；后端新建一条退款记录 + 新商户退款单号重走微信退款，旧 FAILED 记录保留作审计。
> ⚠️ 维度是**退款记录(refund_record)**，不是退款申请。一个退款申请可能对应多条记录（按支付交易拆，定金+尾款等）。

### 3. 退款申请分页（增强）　`GET /v3/admin/refund/application/page`
已有列表接口，本次**出参新增 6 字段、入参新增 3 个搜索条件**。

---

## ④ 入参

### 接口1 stats
无入参。

### 接口2 resubmit
| 参数 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| refundId | path | long | 是 | 退款记录 ID（FAILED 记录） |

无 body。

### 接口3 page（本次新增的搜索字段，其余原有字段不变）
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| customerName | string | 否 | 客户名（联系人）模糊搜索（经 OrderService 反查 orderId 过滤） |
| consultantName | string | 否 | 定制师姓名模糊搜索（经 OrderService 反查 orderId 过滤） |
| reasonText | string | 否 | 退款原因模糊搜索 |

> 原有入参（不变）：orderId / orderNo / applicantName / applicantId / status(多选) / auditStatus / createTimeFrom / createTimeTo / refundedAtFrom / refundedAtTo / sortField / sortOrder / pageNo / pageSize。

---

## ⑤ 出参

### 接口1 stats　`Result<RefundStatsRespVO>`
| 字段 | 类型 | 说明 |
|---|---|---|
| pendingCount | long | 待审批数 |
| pendingAmount | string | 待审批合计应退金额 |
| refundingCount | long | 退款中数 |
| refundingAmount | string | 退款中合计应退金额 |
| failedCount | long | 失败待处理数 |
| failedAmount | string | 失败待处理合计应退金额 |
| completedCount | long | 已完成数 |
| completedAmount | string | 已完成合计实退金额 |
| currentMonthRefundedAmount | string | 本月已退款金额 |
| currentMonthRefundedCount | long | 本月已退款单数 |

> 金额字段均为**字符串**（BigDecimal 序列化，避免精度丢失）。

### 接口2 resubmit　`Result<Void>`
成功返回 `{ "code":200, "message":"成功", "data":null, "success":true }`。

### 接口3 page　列表项 `RefundApplicationPageItemRespVO` 本次**新增**字段
| 字段 | 类型 | 说明 |
|---|---|---|
| productName | string | 产品名称（经 OrderService 批量填充，可能为空） |
| customerName | string | 客户名（联系人，经 OrderService 批量填充） |
| consultantName | string | 定制师姓名（经 OrderService 批量填充） |
| deduction | string | 扣除金额（派生 = 已付 − 应退，无扣除时可能为 null） |
| refundChannel | string | 退款渠道（当前固定值「原路退回（微信支付）」） |
| reasonDetail | string | 退款原因详情（申请时填写的详细描述） |

> 原有项字段（不变）：applicationId / orderId / orderNo / refundType / reasonText / paidAmount / calculatedAmount / actualAmount / applicantType / applicantId / applicantName / status / statusText / auditStatus / reviewerNameLast / refundRecordCount / refundedAt / createTime / updateTime。

---

## ⑥ 枚举 / 数据字典

**退款申请状态 status / statusText**：
| 值 | 含义 |
|---|---|
| PENDING | 待审核 |
| APPROVED | 已通过 |
| REJECTED | 已拒绝 |
| REFUNDING | 退款中 |
| REFUNDED | 已退款 |
| CANCELLED | 已取消 |
| ABNORMAL | 退款异常 |

**统计分组口径**（前端 4 张卡片对应）：
- 待审批 = status `PENDING`
- 退款中 = status `APPROVED` + `REFUNDING`
- 失败待处理 = 底下有退款记录 record.status=`FAILED` 的申请
- 已完成 = status `REFUNDED`
- 本月已退款 = `REFUNDED` 且退款完成时间(refundedAt)在当月

---

## ⑦ 错误码

接口2 resubmit（段位 530500-530599）：
| code | message |
|---|---|
| 530501 | 仅 FAILED 终态的退款记录可重新发起 |
| 530502 | 该退款申请已全额退款成功，无需重新发起 |
| 530503 | 超退校验失败：已退¥{0}，本次¥{1}，已付¥{2} |
| 530504 | 退款记录不存在 |

---

## ⑧ 示例

### 典型：统计
请求：`GET /v3/admin/refund/application/stats`
响应：
```json
{
  "code": 200, "message": "成功", "success": true,
  "data": {
    "pendingCount": 0, "pendingAmount": "0.00",
    "refundingCount": 0, "refundingAmount": "0.00",
    "failedCount": 0, "failedAmount": "0.00",
    "completedCount": 9, "completedAmount": "4610.50",
    "currentMonthRefundedAmount": "4610.50", "currentMonthRefundedCount": 9
  }
}
```

### 典型：列表（按定制师搜索）
请求：`GET /v3/admin/refund/application/page?pageNo=1&pageSize=10&consultantName=李`
响应（节选一项）：
```json
{
  "code": 200, "success": true,
  "data": { "total": 13, "list": [
    { "applicationId": "...", "orderNo": "...", "status": "REFUNDED", "statusText": "已退款",
      "productName": "测试核心产品-单档-固定订金", "customerName": "孙磊", "consultantName": "腰苏图",
      "paidAmount": "...", "actualAmount": "...", "deduction": null,
      "refundChannel": "原路退回（微信支付）", "reasonDetail": "..." }
  ] }
}
```

### 异常：重新发起非 FAILED / 不存在的记录
请求：`POST /v3/admin/refund/record/999999999/resubmit`
响应：
```json
{ "code": 530504, "message": "退款记录不存在", "data": null, "success": false }
```
（对非 FAILED 记录返回 530501；已全额成功返回 530502；超退返回 530503）

---

## ⑨ 业务边界

- **「重新发起」≠「重新申请」**：重新发起是对已审批通过、但调微信退款执行失败的记录重试（财务工作台）；审批被驳回的退款要客户走「重新申请」（小程序新建退款申请），不在本工作台。
- **可重发状态**：仅退款记录 `FAILED` 终态显示「重新发起」；退款中 / 已成功 / 审批驳回不可重发。
- **账户侧失败**：如「原微信账户已注销」，微信只能原路退回，重新发起原路仍会失败——前端可据失败原因提示走线下，不应无限重试。
- 重新发起做了幂等锁（同一记录并发只发一次）+ 超退校验（已退+本次≤已付）。

---

## ⑩ 修改前后对比（接口3 page）

| | 修改前 | 修改后 |
|---|---|---|
| 列表项字段 | 无产品名/客户/定制师/扣除/渠道/原因详情 | 新增 productName / customerName / consultantName / deduction / refundChannel / reasonDetail |
| 搜索维度 | orderId/orderNo/申请人/状态/机审/时间 | 增加 customerName / consultantName / reasonText |

接口1、2 为纯新增，无对比。

---

## ⑪ 影响评估 / 回滚

- 接口3 为**出参增字段 + 入参增可选搜索**，向后兼容，前端不改也不报错（新字段不读即可）。
- 新增字段经 OrderService 批量填充（防 N+1），不影响原列表性能口径。
- 回滚：前端不调用新接口、不读新字段即可；后端回滚 PR #4245。

---

## ⑫ 注意事项

- 金额字段（stats 各 amount、deduction、paidAmount/actualAmount 等）均为**字符串**，前端按字符串处理金额，勿用 number 解析以免精度丢失。
- deduction 可能为 `null`（无扣除或已付=应退），前端兜底显示「-」。
- refundChannel 当前固定「原路退回（微信支付）」，后续多渠道再扩展。
- resubmit 是**记录(record)维度**，列表/详情若按申请聚合，重发入口需定位到具体 FAILED 记录。

---

## ⑬ 关联 / 联系人

- Issue：https://git.1814.love:8443/wx/HL/issues/4226
- PR：https://git.1814.love:8443/wx/HL/pulls/4245
- 后端负责人：腰苏图
- 已部署测试服并网关实调验证通过（stats 返回真实数据 / page 新字段已填充 / resubmit 守卫生效）。
