# 退款待办工作台后端(统计 + 列表增强 + 失败重新发起) - 端类型:管理后台 - 变更类型:新增接口(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`:`{ 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` | 字段 | 类型 | 说明 | |---|---|---| | pendingCount | long | 待审批数 | | pendingAmount | string | 待审批合计应退金额 | | refundingCount | long | 退款中数 | | refundingAmount | string | 退款中合计应退金额 | | failedCount | long | 失败待处理数 | | failedAmount | string | 失败待处理合计应退金额 | | completedCount | long | 已完成数 | | completedAmount | string | 已完成合计实退金额 | | currentMonthRefundedAmount | string | 本月已退款金额 | | currentMonthRefundedCount | long | 本月已退款单数 | > 金额字段均为**字符串**(BigDecimal 序列化,避免精度丢失)。 ### 接口2 resubmit `Result` 成功返回 `{ "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 守卫生效)。