# 发票管理列表搜索补齐（订单号接线 + 客户名 + 跨 tab 完整搜索）（管理后台）

- **接口**：GET /v3/admin/order/invoice/page
- **变更类型**：修改接口（新增入参 `contactName`；`orderNo` 由声明变为生效；搜索跨全部 tab）
- **端类型**：管理后台
- **日期**：2026-06-25
- **Issue**：[#4383](https://git.1814.love:8443/wx/HL/issues/4383)
- **PR**：[#4384](https://git.1814.love:8443/wx/HL/pulls/4384)

发票管理列表搜索原为半成品：`orderNo` 声明了但未生效，`titleName`/`requestedBy` 仅部分 tab 生效，ALL/NONE 不支持搜索。本次补齐为四维度（订单号 / 客户名 / 开票抬头 / 申请人）跨全部 tab 完整生效。

---

## 1 接口背景

发票管理列表 `GET /v3/admin/order/invoice/page` 支持按 tab（待开票/待推送/已推送/未申请/全部）分页查询。原搜索能力残缺：

- `orderNo`（订单号）入参已声明但后端未接线（无效字段）。
- `titleName`（开票抬头）/ `requestedBy`（申请人）仅在 REQUESTED/ISSUED/PUSHED 三个 tab 生效。
- ALL（全部）/ NONE（未申请）两个 tab 完全不支持搜索。
- 无「客户名」搜索入参。

本次补齐为四维度搜索，跨全部 tab 完整生效。

---

## 2 变更清单

| # | 变更项 | 变更前 | 变更后 |
|---|--------|--------|--------|
| 1 | 入参 `contactName` | 无 | **新增**，客户名模糊搜索 |
| 2 | 入参 `orderNo` | 已声明但未生效 | **生效**，订单号模糊搜索 |
| 3 | 入参 `titleName` | 仅状态 tab 生效 | 全部相关 tab 生效（NONE 除外，无发票） |
| 4 | 入参 `requestedBy` | 仅状态 tab 生效 | 全部相关 tab 生效（NONE 除外，无发票） |
| 5 | ALL / NONE tab 搜索 | 不支持 | 支持（订单号/客户名直接搜；开票抬头/申请人对 ALL 跨表生效） |
| 6 | 角标计数 tabCounts / 顶部统计 stats | 全局 | **保持全局**，不随搜索过滤（仅列表 records 过滤） |

---

## 3 接口详情

| 属性 | 值 |
|------|----|
| 方法 | GET |
| 路径 | /v3/admin/order/invoice/page |
| 描述 | 发票管理列表分页查询（tab 过滤 + 四维度模糊搜索 + 角标计数 + 顶部统计） |
| 认证 | Bearer JWT（管理员） |
| 幂等性 | 查询接口，幂等 |
| 限流 | 无特殊限制 |

---

## 4 接口入参（Query 参数）

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| pageNo | int | 否 | 页码，默认 1 |
| pageSize | int | 否 | 每页条数，默认 20 |
| tab | string | 否 | tab 过滤：REQUESTED / ISSUED / PUSHED / NONE / ALL，默认 ALL |
| orderNo | string | 否 | **订单号模糊搜索（本次接线生效）** |
| contactName | string | 否 | **客户名模糊搜索（本次新增）** |
| titleName | string | 否 | 开票抬头模糊搜索 |
| requestedBy | string | 否 | 申请人模糊搜索 |

> 四个搜索字段均为模糊匹配，可任意组合；为空的字段自动跳过。

### 搜索维度按 tab 生效矩阵

| 维度 | 字段来源 | REQUESTED/ISSUED/PUSHED | ALL | NONE |
|------|---------|:---:|:---:|:---:|
| orderNo（订单号） | 订单 | ✅ | ✅ | ✅ |
| contactName（客户名） | 订单 | ✅ | ✅ | ✅ |
| titleName（开票抬头） | 发票 | ✅ | ✅ | ⛔（未申请无发票，命中即空） |
| requestedBy（申请人） | 发票 | ✅ | ✅ | ⛔（未申请无发票，命中即空） |

---

## 5 出参字段

响应为分页结构（`InvoicePageRespVO`），字段同改前未变：

| 字段 | 类型 | 说明 |
|------|------|------|
| records | array | 当前页发票/订单行列表 |
| total | long | 符合搜索条件的总条数 |
| page | int | 当前页码 |
| pageSize | int | 每页条数 |
| tabCounts | array | 各 tab 角标计数（**全局口径，不随搜索过滤**） |
| stats | array | 顶部统计（**全局口径，不随搜索过滤**） |

> 行字段（orderNo / contactName / customizerName / titleName / status / productName / productCoverImg 等）维持原有结构，本次不变。

---

## 6 枚举 / 数据字典

### 6.1 tab 取值

| code | 含义 |
|------|------|
| REQUESTED | 待开票 |
| ISSUED | 待推送（已开票） |
| PUSHED | 已推送 |
| NONE | 未申请（已完成出行、尚未申请发票的订单） |
| ALL | 全部 |

本次无新增枚举/字典。

---

## 7 错误码

本次无新增错误码。查询接口异常走全局统一处理。

---

## 8 示例

### 8.1 典型成功——按订单号搜索（任意 tab）

请求
```
GET /v3/admin/order/invoice/page?pageNo=1&pageSize=20&tab=ALL&orderNo=HL20260620152823663
Authorization: Bearer <token>
```
响应（命中 1 条）
```json
{"code":200,"msg":"success","data":{"records":[{"orderNo":"HL20260620152823663","contactName":"冯雷","titleName":"张三","status":"PUSHED"}],"total":1,"page":1,"pageSize":20,"tabCounts":[],"stats":[]}}
```

### 8.2 边界——客户名搜索（一个客户多单）

请求
```
GET /v3/admin/order/invoice/page?tab=ALL&contactName=冯雷
```
响应（命中 2 条，同客户多单）
```json
{"code":200,"msg":"success","data":{"records":[{"contactName":"冯雷"},{"contactName":"冯雷"}],"total":2}}
```

### 8.3 跨表搜索——发票视角 tab 按订单号过滤

说明：PUSHED 等状态 tab 走发票表，订单号属订单表，后端自动反查订单 ID 限定发票结果。

请求
```
GET /v3/admin/order/invoice/page?tab=PUSHED&orderNo=HL20260620152823663
```
响应（命中 1 条）
```json
{"code":200,"msg":"success","data":{"records":[{"orderNo":"HL20260620152823663","status":"PUSHED"}],"total":1}}
```

### 8.4 无命中——返回空列表（非报错）

请求
```
GET /v3/admin/order/invoice/page?tab=ALL&orderNo=NOSUCH999
```
响应
```json
{"code":200,"msg":"success","data":{"records":[],"total":0}}
```

---

## 9 业务边界

- 四个搜索字段可任意组合，均为模糊匹配，空字段跳过。
- NONE（未申请）tab 不含发票，传 `titleName` / `requestedBy` 时结果为空（属预期，非报错）。
- 搜索仅影响列表 `records` 与 `total`；`tabCounts`（角标计数）和 `stats`（顶部统计）保持全局口径，不随搜索过滤。
- 无命中返回空列表 + total=0，HTTP 200，不报错。

---

## 10 修改前后对比

### 入参对比

| 字段 | 变更前 | 变更后 |
|------|--------|--------|
| contactName | 无 | 新增，客户名模糊搜索 |
| orderNo | 声明但无效 | 生效，订单号模糊搜索 |
| titleName / requestedBy | 仅状态 tab 生效 | 全部相关 tab 生效（NONE 除外） |

### 搜索覆盖对比

| tab | 变更前可搜字段 | 变更后可搜字段 |
|-----|---------------|---------------|
| REQUESTED/ISSUED/PUSHED | titleName / requestedBy | orderNo / contactName / titleName / requestedBy |
| ALL | 无 | orderNo / contactName / titleName / requestedBy |
| NONE | 无 | orderNo / contactName |

---

## 11 影响评估 / 回滚

破坏兼容性：否（纯增强，新增可选入参 + 原无效入参生效）

- 前端可在发票管理列表搜索框接入四个维度（订单号 / 客户名 / 开票抬头 / 申请人），任意组合提交。
- 原已传 `titleName`/`requestedBy` 的前端不受影响，行为兼容并扩展到更多 tab。
- 角标计数/顶部统计为全局口径，前端若期望随搜索联动，请另行沟通（当前不联动）。

回滚方案：回滚后端至本 PR 前版本，`contactName` 入参失效、`orderNo` 回到无效状态、ALL/NONE 不支持搜索。前端入参传了也无副作用（被忽略）。

---

## 12 注意事项

1. `contactName` 搜的是订单「客户名」（联系人），与「申请人 requestedBy」是两个维度，勿混。
2. NONE tab 传开票抬头/申请人会得到空列表，属预期——未申请订单尚无发票记录。
3. 无命中是空列表 + total=0，不是错误码；前端按空态展示即可。
4. 角标计数/统计为全局，搜索后角标数字与列表条数可能不一致，属设计预期。

---

## 13 关联 / 联系人

- **Issue**：[#4383 发票管理列表搜索补齐](https://git.1814.love:8443/wx/HL/issues/4383)
- **PR**：[#4384](https://git.1814.love:8443/wx/HL/pulls/4384)
- **后端负责人**：腰苏图（yaosutu）
