# 退款待办列表状态 Tab 后端驱动（PR #4303/#4308）

- **端类型**：管理后台
- **变更类型**：修改接口
- **日期**：2026-06-23
- **关联 Issue**：[#4303](https://git.1814.love:8443/wx/HL/issues/4303)
- **PR**：[#4308](https://git.1814.love:8443/wx/HL/pulls/4308)
- **后端负责人**：yaosutu

---

## ① 接口背景

退款待办工作台新增「分 Tab 展示」能力。统计接口出参由旧版扁平字段对象改为数组（每项含 code/name/count/amount），分页接口新增 `statusGroup` 入参，支持按 Tab 口径过滤。

前端改造要点：
- 统计接口出参结构已**破坏性变更**（旧字段全部废弃），前端读法必须修改。
- 分页接口新增可选参数 `statusGroup`，点 Tab 时传对应值即可，与既有 `status` 多选参数可并存（AND 关系）。

---

## ② 变更清单

| # | 接口 | 变更类型 | 影响 |
|---|------|----------|------|
| 1 | `GET /v3/admin/refund/application/stats` | ⚠️ 破坏性修改——出参由扁平对象改为数组 | 前端必须修改 |
| 2 | `GET /v3/admin/refund/application/page` | ✨ 新增可选入参 `statusGroup` | 前端按需传参 |

---

## ③ 接口详情

### 接口 1：退款待办统计

| 字段 | 值 |
|---|---|
| 方法 | GET |
| 路径 | `/v3/admin/refund/application/stats` |
| 描述 | 退款待办各状态分组统计（数量 + 金额），用于列表 Tab 角标与顶部卡片 |
| 认证 | 需要 JWT（管理后台 Bearer Token） |
| 幂等性 | 只读，天然幂等 |
| 限流 | 无特殊限流 |

### 接口 2：退款申请分页

| 字段 | 值 |
|---|---|
| 方法 | GET |
| 路径 | `/v3/admin/refund/application/page` |
| 描述 | 退款申请分页列表，新增 `statusGroup` 参数支持 Tab 口径过滤 |
| 认证 | 需要 JWT（管理后台 Bearer Token） |
| 幂等性 | 只读，天然幂等 |
| 限流 | 无特殊限流 |

---

## ④ 接口入参

### 接口 1：`GET /v3/admin/refund/application/stats`

无入参（Query 参数为空）。

---

### 接口 2：`GET /v3/admin/refund/application/page`
#### 4.1 Query 参数（全量，新增字段用 ✨ 标注）

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| `pageNo` | Integer | 是 | 页码，从 1 开始 |
| `pageSize` | Integer | 是 | 每页条数 |
| `statusGroup` | String | 否 | ✨ Tab 分组过滤，取值见枚举表。不传则不约束（等同全部）。与 `status` 多选参数 AND 叠加 |
| `status` | String[] | 否 | 精确状态多选，取值见 `RefundApplicationStatus` 枚举 |
| `keyword` | String | 否 | 关键词搜索（订单号/申请单号/客人姓名） |
| `createTimeStart` | String | 否 | 申请时间起，格式 `yyyy-MM-dd HH:mm:ss` |
| `createTimeEnd` | String | 否 | 申请时间止，格式 `yyyy-MM-dd HH:mm:ss` |

---

## ⑤ 出参字段

### 接口 1：`GET /v3/admin/refund/application/stats`

**返回类型**：`Result<List<RefundStatItemVO>>`

数组元素 `RefundStatItemVO`：

| 字段名 | 类型 | 说明 |
|--------|------|------|
| `code` | String | 分组唯一标识，见枚举表 |
| `name` | String | 分组中文名，可直接展示为 Tab 文案 |
| `count` | long | 该分组申请单数量 |
| `amount` | String | 该分组退款金额合计（元，2 位小数，如 `"1234.50"`） |

数组顺序固定，共 6 项：

| 顺序 | code | name |
|------|------|------|
| 1 | ALL | 全部 |
| 2 | PENDING | 待审批 |
| 3 | REFUNDING | 退款中 |
| 4 | FAILED | 失败待处理 |
| 5 | COMPLETED | 已完成 |
| 6 | CURRENT_MONTH_REFUNDED | 本月已退款 |

**前端使用建议**：列表 Tab 角标取前 5 项（ALL/PENDING/REFUNDING/FAILED/COMPLETED），顶部金额卡片另取第 6 项（CURRENT_MONTH_REFUNDED）。

---

### 接口 2：`GET /v3/admin/refund/application/page`

返回类型与原来相同（`Result<PageResult<RefundApplicationRespVO>>`），本次无出参字段变化，不赘述。

---

## ⑥ 枚举 / 数据字典

### `statusGroup` 枚举（新增，接口 1 和接口 2 共用同一套口径）

| code | 中文名 | 后端过滤口径 |
|------|--------|-------------|
| `ALL` | 全部 | status ∈ (PENDING, APPROVED, REFUNDING, REFUNDED)，**排除** REJECTED（已驳回）和 CANCELLED（已取消） |
| `PENDING` | 待审批 | status = PENDING |
| `REFUNDING` | 退款中 | status ∈ (APPROVED, REFUNDING)，**且**最新退款记录不为失败 |
| `FAILED` | 失败待处理 | 最新退款记录 = 失败（优先级最高，从退款中挪出；重发成功后不再算失败） |
| `COMPLETED` | 已完成 | status = REFUNDED |
| `CURRENT_MONTH_REFUNDED` | 本月已退款 | status = REFUNDED，且退款完成时间在本自然月内 |

**说明**：
- 五组互斥（ALL/PENDING/REFUNDING/FAILED/COMPLETED），ALL = 其余四组之和。
- FAILED 优先级最高：一笔申请只要最新退款记录是失败，无论 status 是 REFUNDING 还是 APPROVED，都归入 FAILED 而不在 REFUNDING 中计数。
- ALL 列表**包含** FAILED 的申请（status=REFUNDING 但最新记录失败），前端如需对失败单标红，自行判断最新退款记录状态。
- REJECTED（已驳回）和 CANCELLED（已取消）**不进**任何 Tab，属于终态归档，不在待办范围内。
---

## ⑦ 错误码

| 错误码 | 含义 | 触发条件 |
|--------|------|----------|
| `400` | 参数校验失败 | `statusGroup` 传了非法枚举值 |
| `401` | 未登录 | JWT 缺失或过期 |
| `403` | 无权限 | 非管理后台角色 |

---

## ⑧ 示例

### 8.1 典型成功：获取统计数组

**请求**
```
GET /v3/admin/refund/application/stats
Authorization: Bearer <token>
```

**响应**
```json
{
  "code": 200,
  "msg": "success",
  "data": [
    { "code": "ALL",                    "name": "全部",       "count": 38, "amount": "52340.00" },
    { "code": "PENDING",                "name": "待审批",     "count": 5,  "amount": "8800.00"  },
    { "code": "REFUNDING",              "name": "退款中",     "count": 12, "amount": "17200.00" },
    { "code": "FAILED",                 "name": "失败待处理", "count": 3,  "amount": "4100.00"  },
    { "code": "COMPLETED",              "name": "已完成",     "count": 18, "amount": "22240.00" },
    { "code": "CURRENT_MONTH_REFUNDED", "name": "本月已退款", "count": 11, "amount": "13500.00" }
  ]
}
```

---

### 8.2 边界情况：某 Tab 无数据

当 FAILED 分组无数据时，count=0、amount="0.00"，数组项仍然返回（不缺项）：

```json
{ "code": "FAILED", "name": "失败待处理", "count": 0, "amount": "0.00" }
```

---

### 8.3 业务失败：statusGroup 传了非法值

**请求**
```
GET /v3/admin/refund/application/page?statusGroup=UNKNOWN&pageNo=1&pageSize=10
Authorization: Bearer <token>
```

**响应**
```json
{
  "code": 400,
  "msg": "参数错误：statusGroup 枚举值不合法",
  "data": null
}
```
---

## ⑨ 业务边界

**适用场景**：
- 管理后台退款待办工作台 Tab 切换与角标显示。
- 顶部「本月已退款」金额卡片。

**不适用场景**：
- 小程序端（无此工作台）。
- `REJECTED` / `CANCELLED` 状态的申请不在任何 Tab 中，若需展示历史归档申请请单独传 `status=REJECTED` 或 `status=CANCELLED` 参数。

**特殊边界**：
- `FAILED` 判定依赖最新退款记录状态，与申请单自身的 `status` 字段不完全对应。点击「失败待处理」Tab 时后端已按此口径过滤，前端无需再做二次判断来决定哪些展示在该 Tab。
- `ALL` Tab 中包含 `FAILED` 的申请，前端展示全部时如需区分失败/正常退款中，可读申请单内的退款记录列表字段。

---

## ⑩ 修改前后对比

### 接口 1：统计接口出参结构对比

**旧出参（已废弃，前端需移除）**：
```json
{
  "code": 200,
  "data": {
    "pendingCount": 5,
    "pendingAmount": "8800.00",
    "refundingCount": 12,
    "refundingAmount": "17200.00",
    "failedCount": 3,
    "failedAmount": "4100.00",
    "completedCount": 18,
    "completedAmount": "22240.00",
    "currentMonthRefundedAmount": "13500.00",
    "currentMonthRefundedCount": 11
  }
}
```

**新出参（当前版本）**：
```json
{
  "code": 200,
  "data": [
    { "code": "ALL",                    "name": "全部",       "count": 38, "amount": "52340.00" },
    { "code": "PENDING",                "name": "待审批",     "count": 5,  "amount": "8800.00"  },
    { "code": "REFUNDING",              "name": "退款中",     "count": 12, "amount": "17200.00" },
    { "code": "FAILED",                 "name": "失败待处理", "count": 3,  "amount": "4100.00"  },
    { "code": "COMPLETED",              "name": "已完成",     "count": 18, "amount": "22240.00" },
    { "code": "CURRENT_MONTH_REFUNDED", "name": "本月已退款", "count": 11, "amount": "13500.00" }
  ]
}
```

字段级对比：

| 旧字段 | 新位置 | 说明 |
|--------|--------|------|
| `pendingCount` | `data[code=PENDING].count` | 已移至数组元素 |
| `pendingAmount` | `data[code=PENDING].amount` | 已移至数组元素 |
| `refundingCount` | `data[code=REFUNDING].count` | 已移至数组元素 |
| `refundingAmount` | `data[code=REFUNDING].amount` | 已移至数组元素 |
| `failedCount` | `data[code=FAILED].count` | 已移至数组元素 |
| `failedAmount` | `data[code=FAILED].amount` | 已移至数组元素 |
| `completedCount` | `data[code=COMPLETED].count` | 已移至数组元素 |
| `completedAmount` | `data[code=COMPLETED].amount` | 已移至数组元素 |
| `currentMonthRefundedAmount` | `data[code=CURRENT_MONTH_REFUNDED].amount` | 已移至数组元素 |
| `currentMonthRefundedCount` | `data[code=CURRENT_MONTH_REFUNDED].count` | 已移至数组元素 |
| —（新增） | `data[code=ALL].count/amount` | 新增「全部」汇总项（旧版无此项） |

### 接口 2：分页接口入参对比

| 字段 | 变化 | 说明 |
|------|------|------|
| `statusGroup` | 新增（可选） | 新增 Tab 口径过滤参数，不传等同旧行为 |
| 其余参数 | 不变 | 保持向后兼容 |
---

## ⑪ 影响评估 / 回滚

**破坏兼容**：
- 接口 1 出参结构**已破坏兼容**。旧代码读 `data.pendingCount` 等扁平字段将返回 `undefined`，Tab 角标全部显示为空。**前端必须同步改造**。

**前端同步上线**：
- 接口 1 读取逻辑需改为遍历数组，按 `code` 取值。
- 接口 2 点击 Tab 时加 `statusGroup` 参数，旧版不传时行为不变，**可兼容部署**（接口 2 无破坏）。

**回滚方案**：
- 若需回滚接口 1，后端可恢复旧版扁平结构；前端需配合回滚。
- 接口 2 的 `statusGroup` 参数为可选，后端回滚直接忽略此参数，前端不受影响。

---

## ⑫ 注意事项

1. **金额字段均为 String**（元，2 位小数），如 `"1234.50"`。禁止用 `Number()` / `parseFloat()` 解析后再做展示（防 JS 精度丢失）。直接展示即可。
2. **count 字段为 long（整数）**，无需特殊处理。
3. 数组顺序由后端固定，前端按顺序渲染 Tab 即可，无需按 code 排序。
4. `FAILED` 口径：最新退款记录失败即归入，与申请单 `status` 不完全对等，不要用 status === "REFUNDING" 来判断是否是「退款中」Tab（因为 FAILED 单的 status 也可能是 REFUNDING）。
5. 分页接口新增的 `statusGroup` 与既有 `status` 多选是 AND 关系，避免同时传两者造成无结果（如 `statusGroup=PENDING&status=REFUNDING` 必然为空）。

---

## ⑬ 关联 / 联系人

- **Issue**：[https://git.1814.love:8443/wx/HL/issues/4303](https://git.1814.love:8443/wx/HL/issues/4303)
- **PR**：[https://git.1814.love:8443/wx/HL/pulls/4308](https://git.1814.love:8443/wx/HL/pulls/4308)
- **后端负责人**：yaosutu
