# admin 列表筛选纪律全面治理 (8 处)

> **服务**: hl-product-service-v2 (8093) + hl-order-service-v2 (8094)
> **PR**: #1908 (Closes #1907)
> **日期**: 2026-05-09
> **影响范围**: 管理端列表分页接口

---

## ⚠️ 关键变化

admin 端 8 个列表分页接口的「下拉筛选」行为统一治理。**API 路径/请求参数/响应字段不变**，仅治理两类范式不健壮：

| 类别 | Before | After |
|------|--------|-------|
| A. ProductLine 筛选字段类型 | `productType` 单值 String 派生 `ProductTypeList` 不通 → 筛选不生效 | `List<String>` + 自定义 setter, 兼容 4 种入参 |
| B. status/type/productType/platform 筛选 | Service 调 `XxxEnum.of(req.getStatus())`, 字典+1 enum 不+1 时抛 IAE → 500 | String 直接透传 + `eqIfPresent + StrUtil.isNotBlank`, 字典已有合法值 → 正常筛选; 非法/未知值 → 视同未筛选返回全量 |

---

## 一、影响接口清单

### A. ProductLine 筛选字段不通 (1 处)

| 接口 | 方法 | 路径 | 变更 |
|------|------|------|------|
| 主题列表 | GET | `/admin/product/line/list` | `productType` 后端接收**兼容性扩大**到 4 种入参形式 |

**4 种兼容入参** (前端任一方式都能正确接住):
- `?productType=GROUP`(单值)
- `?productType=CORE,GROUP`(逗号分隔)
- `?productType=CORE&productType=GROUP`(重复 key)
- POST body `{"productType":["CORE","GROUP"]}`(JSON 数组)

### B. enum.of() IAE 隐患 → String 透传 (7 处)

| # | 接口 | 方法 | 路径 | 治理字段 |
|---|------|------|------|----------|
| 1 | admin 订单列表 | GET | `/admin/order/list` | status / processStatus / productType |
| 2 | designer 订单列表 | GET | `/admin/order/designer-orders` | status |
| 3 | 工单列表 | GET | `/admin/workorder/page` | status / type |
| 4 | 保险单列表 | GET | `/admin/insurance-order/page` | status |
| 5 | 定制需求列表 | GET | `/admin/customize-request/page` | status |
| 6 | 合同列表 | GET | `/admin/contract/page` | status / platform |
| 7 | 发票列表 | GET | `/admin/invoice/page` | status |

---

## 二、行为变化（前端关注）

| 场景 | Before | After |
|------|--------|-------|
| 字典已有的合法值, 如 `status=PAID` | 正常筛选 | 正常筛选(行为不变) |
| 字典新增了值 enum 还没同步, 如未来加 `status=AFTER_SALES` | **HTTP 500 IllegalArgumentException** | 正常筛选(直接 SQL eq) |
| 传字典里完全没有的值, 如 `status=XXX_NOT_EXIST` | HTTP 500 | HTTP 200 但返回空列表 |
| 不传 / 空字符串 | 不筛选 | 不筛选(行为不变) |

**前端不需要任何改动**, 但建议:
- 已有字典 fallback 逻辑可保留也可移除(后端不再 500)
- 主题列表前端的「重复请求互相 cancel」BUG 详见同日 `09_frontend_notice_admin_topic-filter-dup-request.md`

---

## 三、内部范式

参照 `RefundApplicationMapper.selectPage` 既有正确范式:

```java
// Before (Mapper)
default IPage<XxxDO> selectPage(... String status, ...) {
    return selectPage(page, new LambdaQueryWrapperX<XxxDO>()
            .eq(XxxStatus.of(status))   // 字典+1 enum 不+1 → IAE
            ...);
}

// After (Mapper)
default IPage<XxxDO> selectPage(... String status, ...) {
    return selectPage(page, new LambdaQueryWrapperX<XxxDO>()
            .eqIfPresent(XxxDO::getStatus, StrUtil.isNotBlank(status) ? status : null)
            ...);
}
```

---

## 四、不变化

- DB schema / 字典表 / Flyway: **不动**
- enum 类型本身: **不删**, 仍可作字面量使用 (如 `if (PAID.name().equals(status))`)
- 前端 VO 字段名/类型契约: **不变**
- 排序/分页逻辑: **不变**
- 数据权限/角色控制: **不变**

---

## 五、测试

- hl-product-service-v2: `ProductLineMapperTest 13 / ProductLinePageReqVOTest 9 / ProductPricingServiceTest 64 / ProductPricingServiceMpQuoteTest 5 / MpProductServiceTest 69 = 160 全绿`
- hl-order-service-v2: `ContractQueryServiceTest 27 / OrderInvoiceServiceTest 31 / OrderListQueryServiceTest 19 / OrderInfoMapperTest 13 / GroupOrderStrategyTest 10 = 100 全绿`
- 新增治理范式 case (字典+enum 不一致 / null 透传 / 4 种入参兼容性) 共 13 个

---

## 六、关联

- 前端 BUG 通知: `09_frontend_notice_admin_topic-filter-dup-request.md` (同日新建)
- 历史前端 BUG 通知: `07_frontend_notice_topic-filter-empty.md` (本日 DevTools 新证据已纠正其根因推断)
- 单房差功能: 完全解耦, PR #1900 / #1901 / #1904 不受影响 (治理过程中误删已回滚)
