# 订单列表 Tab 分组计数与分组过滤 — 新增接口 — 管理后台

> 变更类型：✨ 新增接口 + 修改接口（列表新增入参）
> 端类型：管理后台
> 日期：2026-06-17
> 服务：hl-order-service-v3
> PR：https://git.1814.love:8443/wx/HL/pulls/3939

---

## 1. 接口背景

**功能页面**：管理后台「订单列表」页的**顶部 Tab 栏**（出行前 / 出行中 / 核单 / 异常·取消 / 全部）。

**用在哪**：

1. **Tab 计数徽标**：页面加载 / 筛选条件变更时，调 `GET /v3/admin/order/status-group-counts` 取每个 Tab 上显示的订单数量角标（如"出行前 33"、"异常/取消 5"）。
2. **点击 Tab 过滤列表**：用户点某个 Tab，前端只需把该 Tab 的 `group` 值作为 `statusGroup` 参数传给列表接口（`GET /v3/admin/order`），后端内部把 group 展开为对应状态集合过滤，前端无需自己维护"哪个 Tab 对应哪些状态"的映射。

**设计意图**：把 6 个粗状态按出行阶段折叠成 4 组+全部，让定制师快速聚焦当前阶段的订单，Tab 计数单独一个端点供徽标独立刷新，点击 Tab 与列表接口通过 `statusGroup` 解耦。

---

## 2. 变更清单

| 变更类型 | 接口 | 说明 |
|---|---|---|
| ✨ 新增 | `GET /v3/admin/order/status-group-counts` | Tab 分组计数，返回 5 项（ALL + 4 组），每项含 group/label/orderCount |
| ⚠️ 修改 | `GET /v3/admin/order`（列表） | 新增可选入参 `statusGroup`，前端传 group key 触发 Tab 过滤 |

---

## 3. 接口详情

### 3.1 Tab 分组计数

- **方法 + 路径**：`GET /v3/admin/order/status-group-counts`
- **接口名**：订单列表 Tab 分组计数
- **认证**：需要 JWT（管理后台登录 token）
- **幂等性**：查询接口，天然幂等
- **限流**：无独立限流规则
- **语义**：计数值尊重其他筛选条件（keyword/出发日期/定制师名/来源/标签），但与当前选中 Tab 无关（faceted 计数，ALL 恒含已取消）

### 3.2 订单列表（新增 statusGroup 入参）

- **方法 + 路径**：`GET /v3/admin/order`（别名 `GET /v3/admin/order/list`）
- **接口名**：订单列表
- **认证**：需要 JWT
- **幂等性**：查询接口，天然幂等
- **限流**：无独立限流规则

---

## 4. 接口入参

### 4.1 Tab 分组计数 Query 参数（复用列表筛选条件，不传 orderStatus / statusGroup）

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `keyword` | String | 否 | 关键字模糊搜索（团号/客户姓名/产品名/订单号） |
| `departureDateFrom` | LocalDate | 否 | 出发日期起始（格式 `yyyy-MM-dd`） |
| `departureDateTo` | LocalDate | 否 | 出发日期结束（格式 `yyyy-MM-dd`） |
| `consultantName` | String | 否 | 定制师姓名 LIKE 匹配 |
| `createSource` | String | 否 | 订单来源（`CONSULTANT` / `CUSTOMER`） |
| `tagNames` | List\<String\> | 否 | 标签名列表过滤（AND） |

> 注：不传 `orderStatus`（按 group 内部统计）、不传 `statusGroup`（计数接口自己统计所有组）、不传 `page`/`pageSize`（计数接口无分页）。

### 4.2 订单列表新增入参

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `statusGroup` | String | 否 | Tab 分组 key，取值见下方枚举表；后端内部展开为 orderStatus 集合；与 `orderStatus` 下拉并存时取 AND；不传或传 `ALL` 时含已取消 |

---

## 5. 出参字段

### 5.1 Tab 分组计数 StatusGroupCountVO

返回类型：`Result<List<StatusGroupCountVO>>`，共 5 项，ALL 在首位，后跟 4 组，按固定顺序。

| 字段 | 类型 | 说明 | 示例 |
|---|---|---|---|
| `group` | String | 分组标识 key，对应 `statusGroup` 入参 | `"BEFORE_TRIP"` |
| `label` | String | 分组中文标签，用于 Tab 展示 | `"出行前"` |
| `orderCount` | Long | 该分组订单数（满足其他筛选条件的计数） | `33` |

### 5.2 订单列表（本次只新增入参，出参字段不变，无需修改）

订单列表 `OrderListItemRespVO` 出参字段无变化（参考现有接口文档）。

---

## 6. 枚举 / 数据字典

### Tab 分组 group 值（statusGroup 参数取值 + 分组计数返回 group 值）

| group 值 | 中文标签 | 含义（包含的 orderStatus） |
|---|---|---|
| `ALL` | 全部 | 全部状态（含已取消） |
| `BEFORE_TRIP` | 出行前 | `PENDING_PAY`（待支付）+ `CUSTOMIZING`（定制中）+ `PENDING_DEPARTURE`（待出行） |
| `ON_TRIP` | 出行中 | `TRAVELLING`（出行中） |
| `SETTLEMENT` | 核单 | `COMPLETED`（已完成，横跨核单+结算两子阶段） |
| `ABNORMAL` | 异常/取消 | `CANCELLED`（已取消） |

> 前端只需感知 group 值，状态枚举的内部映射由后端维护，前端不用硬编码。

---

## 7. 错误码

| code | 含义 |
|---|---|
| `200` | 成功 |
| `401` | 未登录 / token 过期 |
| `400` | `statusGroup` 传了非法值（后端宽松处理：未知 group 忽略，不抛 400；但建议传枚举定义值） |

---

## 8. 示例

### 8.1 典型成功——获取 Tab 分组计数

```http
GET /v3/admin/order/status-group-counts
Authorization: Bearer <token>
```

```json
{
  "code": 200,
  "data": [
    { "group": "ALL",        "label": "全部",      "orderCount": 38 },
    { "group": "BEFORE_TRIP","label": "出行前",    "orderCount": 33 },
    { "group": "ON_TRIP",    "label": "出行中",    "orderCount": 0  },
    { "group": "SETTLEMENT", "label": "核单",      "orderCount": 0  },
    { "group": "ABNORMAL",   "label": "异常/取消", "orderCount": 5  }
  ]
}
```

### 8.2 边界——带筛选条件的计数（Faceted，计数随筛选条件变化）

```http
GET /v3/admin/order/status-group-counts?departureDateFrom=2026-07-01&departureDateTo=2026-07-31
```

返回值中每组 `orderCount` 仅统计出发日期在 7 月的订单，即使当前 Tab 已选 BEFORE_TRIP，ALL 和其他组也只统计 7 月内的。

```json
{
  "code": 200,
  "data": [
    { "group": "ALL",        "label": "全部",      "orderCount": 12 },
    { "group": "BEFORE_TRIP","label": "出行前",    "orderCount": 10 },
    { "group": "ON_TRIP",    "label": "出行中",    "orderCount": 1  },
    { "group": "SETTLEMENT", "label": "核单",      "orderCount": 0  },
    { "group": "ABNORMAL",   "label": "异常/取消", "orderCount": 1  }
  ]
}
```

### 8.3 业务失败——点击 Tab 后列表 statusGroup 传了未知值

```http
GET /v3/admin/order?statusGroup=UNKNOWN_GROUP&page=1&pageSize=10
```

```json
{
  "code": 200,
  "data": {
    "total": 38,
    "list": [ ... ]
  }
}
```

> 后端宽松处理：未知 group 忽略，等同于不传 statusGroup（返回默认不含已取消的全部订单）。前端避免传枚举定义之外的值。

---

## 9. 业务边界

**适用**：
- 管理后台订单列表顶部 Tab 计数徽标渲染。
- 用户点击 Tab 触发列表按分组过滤。

**不适用**：
- 分组计数不适用于精确状态统计（`SETTLEMENT` 组包含 `COMPLETED`，横跨核单和结算两个子阶段，不区分子状态）。
- 分组计数的 `ALL` 恒含已取消订单（不可排除）；其他接口参数 `cancelled=false` 对分组计数无效。

**特殊边界**：
- `statusGroup=ALL` 与不传 `statusGroup` 行为不同：传 `ALL` 时含已取消；不传时默认不含已取消（`cancelled` 默认 `false`）。
- `statusGroup` 与 `orderStatus` 下拉可同时传，取 AND 逻辑（例：`statusGroup=BEFORE_TRIP&orderStatus=CUSTOMIZING` 只返定制中的订单）。
- 分组计数与当前选中 Tab 的 `statusGroup` 无关（Faceted 设计），每次只要其他筛选条件变了就重新调计数接口。

---

## 12. 注意事项

- **计数接口与列表接口分开调**：Tab 徽标数字调 `status-group-counts`，Tab 点击过滤调 `GET /v3/admin/order?statusGroup=...`，两者分开请求。
- **`statusGroup=ALL` 传参覆盖默认排除取消**：前端若需要"全部"含取消，用 `statusGroup=ALL` 而不是 `cancelled=true`（两者效果等同，但 `statusGroup=ALL` 更语义明确）。
- **分组映射由后端维护**：前端不要在代码里硬编码"BEFORE_TRIP = [PENDING_PAY, CUSTOMIZING, PENDING_DEPARTURE]"这样的映射表——这是后端内部逻辑，后端调整分组时前端自动跟随。
- 已实测（2026-06-17，测试服 dev-v3）：`statusGroup=BEFORE_TRIP` 返 33 单、无越界；`ALL` 组计数 = 4 组之和。

---

## 13. 关联 / 联系人

- Issue：https://git.1814.love:8443/wx/HL/issues/3938
- PR：https://git.1814.love:8443/wx/HL/pulls/3939
- Commit（refactor review 后）：https://git.1814.love:8443/wx/HL/commit/5eca8d67b0432dfc19e2b6d2c67ef7e6eac6ec02
- 后端负责人：腰苏图
