9.1 KiB
退款待办工作台后端(统计 + 列表增强 + 失败重新发起)
- 端类型:管理后台
- 变更类型:新增接口(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
响应:
{
"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=李
响应(节选一项):
{
"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
响应:
{ "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:wx/HL#4226
- PR:wx/HL#4245
- 后端负责人:腰苏图
- 已部署测试服并网关实调验证通过(stats 返回真实数据 / page 新字段已填充 / resubmit 守卫生效)。