417 行
14 KiB
Markdown
417 行
14 KiB
Markdown
# 财务发票管理列表 - ALL tab 改订单视角(破坏性变更)
|
||
|
||
- **接口**:GET /v3/admin/order/invoice/page
|
||
- **变更类型**:修改接口 破坏性变更(tab=ALL 列表语义 + tabCounts.ALL 计数口径均变)
|
||
- **端类型**:管理后台
|
||
- **日期**:2026-06-24
|
||
- **Issue**:[#4337](https://git.1814.love:8443/wx/HL/issues/4337)
|
||
- **PR**:[#4338](https://git.1814.love:8443/wx/HL/pulls/4338)
|
||
|
||
---
|
||
|
||
## 1 接口背景
|
||
|
||
财务发票管理列表原先以发票记录为视角。ALL tab 只返回已有发票记录的订单行,tabCounts.ALL 计数只统计发票记录总数,导致:
|
||
|
||
- ALL 数量 < NONE(未申请)数量,前端展示出现矛盾
|
||
- ALL 列表无法让财务在一个页面纵览所有已完成订单的发票状态
|
||
|
||
本次将 ALL tab 切换为**订单视角**:以全部 COMPLETED(已完成)订单为数据源,有发票的行展示发票字段,无发票的行 status=NONE,对齐前端原型设计。
|
||
|
||
---
|
||
|
||
## 2 变更清单
|
||
|
||
| # | 变更项 | 变更前 | 变更后 |
|
||
|---|--------|--------|--------|
|
||
| 1 | tabCounts.ALL 计数口径 | 仅统计发票记录数(REQUESTED+ISSUED+PUSHED),不含未申请 | 统计全部 COMPLETED 订单数(含未申请),永远 >= 任何单个 tab |
|
||
| 2 | tab=ALL 列表数据源 | 只返回有发票记录的订单行 | 返回全部 COMPLETED 订单,无发票行 status=NONE |
|
||
| 3 | titleName/requestedBy 在 ALL/NONE tab 的行为 | 未定义(可能干扰结果) | 静默忽略(两字段仅在 REQUESTED/ISSUED/PUSHED 三个发票视角 tab 生效) |
|
||
|
||
---
|
||
|
||
## 3 接口详情
|
||
|
||
| 属性 | 值 |
|
||
|------|----|
|
||
| 方法 | GET |
|
||
| 路径 | /v3/admin/order/invoice/page |
|
||
| 描述 | 财务发票管理列表(分页),含 tab 计数与汇总统计 |
|
||
| 认证 | Bearer JWT(管理员) |
|
||
| 幂等性 | 只读,天然幂等 |
|
||
| 限流 | 无特殊限制 |
|
||
|
||
---
|
||
|
||
## 4 接口入参
|
||
|
||
### 4.1 Query 参数
|
||
|
||
| 参数名 | 类型 | 必填 | 说明 |
|
||
|--------|------|------|------|
|
||
| tab | string | 否 | 当前 tab,枚举值见第 6 节;默认 ALL |
|
||
| pageNo | integer | 否 | 页码,默认 1 |
|
||
| pageSize | integer | 否 | 每页条数,默认 20 |
|
||
| keyword | string | 否 | 关键词搜索(订单号 / 产品名 / 联系人),所有 tab 均生效 |
|
||
| titleName | string | 否 | 发票抬头名称,**仅 REQUESTED/ISSUED/PUSHED tab 生效,ALL/NONE tab 静默忽略** |
|
||
| requestedBy | string | 否 | 申请人(定制师),**仅 REQUESTED/ISSUED/PUSHED tab 生效,ALL/NONE tab 静默忽略** |
|
||
| departureStartDate | string | 否 | 出发日期起,格式 YYYY-MM-DD |
|
||
| departureEndDate | string | 否 | 出发日期止,格式 YYYY-MM-DD |
|
||
|
||
### 4.2 请求体
|
||
|
||
无(GET 接口)
|
||
|
||
---
|
||
|
||
## 5 出参字段
|
||
|
||
响应外层结构:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"tabCounts": [...],
|
||
"stats": [...],
|
||
"records": [...],
|
||
"total": 0,
|
||
"pageNo": 1,
|
||
"pageSize": 20
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.1 tabCounts(5 项固定顺序)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| code | string | tab 枚举值(见第 6 节) |
|
||
| name | string | tab 中文标签 |
|
||
| count | integer | 该 tab 对应的记录数 |
|
||
|
||
固定顺序:REQUESTED → ISSUED → PUSHED → NONE → ALL
|
||
|
||
> **ALL.count 语义已变**:现为全部 COMPLETED 订单数(含未申请);之前仅含发票记录数。
|
||
|
||
### 5.2 stats(4 项固定顺序)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| code | string | 枚举值(见第 6 节) |
|
||
| name | string | 统计项中文标签 |
|
||
| count | integer | 数量 |
|
||
| amount | number | 金额(元,JSON number) |
|
||
|
||
固定顺序:REQUESTED → ISSUED → PUSHED → CURRENT_MONTH_ISSUED
|
||
|
||
### 5.3 records 列表行(InvoicePageItemRespVO)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| id | string | 发票记录 ID(雪花);**无发票行为 null** |
|
||
| orderId | string | 订单 ID(雪花) |
|
||
| orderNo | string | 订单号 |
|
||
| productName | string | 产品名称 |
|
||
| tierName | string | 档期名称 |
|
||
| contactName | string | 联系人姓名 |
|
||
| customizerName | string | 定制师姓名 |
|
||
| departureDate | string | 出发日期,格式 YYYY-MM-DD |
|
||
| adultCount | integer | 成人数 |
|
||
| childCount | integer | 儿童数 |
|
||
| youngChildCount | integer | 婴幼儿数 |
|
||
| invoiceType | string | 发票类型枚举(见第 6 节);无发票行为 null |
|
||
| invoiceTypeText | string | 发票类型中文;无发票行为 null |
|
||
| titleType | string | 抬头类型枚举(见第 6 节);无发票行为 null |
|
||
| titleName | string | 抬头名称;无发票行为 null |
|
||
| taxNo | string | 税号;无发票行为 null |
|
||
| amount | number | 发票金额(元);无发票行为 null |
|
||
| email | string | 接收邮箱;无发票行为 null |
|
||
| status | string | 发票状态枚举(见第 6 节);**无发票行固定为 NONE** |
|
||
| statusText | string | 发票状态中文;**无发票行固定为 未申请** |
|
||
| requestedBy | string | 申请人(定制师);无发票行为 null |
|
||
| requestedAt | string | 申请时间 ISO 8601;无发票行为 null |
|
||
| issuedAt | string | 开票时间 ISO 8601;无发票行为 null |
|
||
| issuedBy | string | 开票人;无发票行为 null |
|
||
| invoiceNo | string | 发票号;无发票行为 null |
|
||
| fileUrl | string | 发票文件 URL;无发票行为 null |
|
||
| pdfName | string | PDF 文件名;无发票行为 null |
|
||
| pdfSize | string | PDF 文件大小(字符串,如 128KB);无发票行为 null |
|
||
|
||
---
|
||
|
||
|
||
## 6 枚举 / 数据字典
|
||
|
||
### 6.1 tab 枚举(查询参数 & tabCounts.code)
|
||
|
||
| code | name | 说明 |
|
||
|------|------|------|
|
||
| REQUESTED | 待开票 | 已申请、待财务开票 |
|
||
| ISSUED | 待推送 | 已开票、待推送给客户 |
|
||
| PUSHED | 已推送 | 已推送给客户 |
|
||
| NONE | 未申请 | 已完成订单但尚未申请发票 |
|
||
| ALL | 全部 | 订单视角:全部 COMPLETED 订单(含无发票行) |
|
||
|
||
### 6.2 stats.code 枚举
|
||
|
||
| code | name |
|
||
|------|------|
|
||
| REQUESTED | 待开票 |
|
||
| ISSUED | 已开票(待推送) |
|
||
| PUSHED | 已推送 |
|
||
| CURRENT_MONTH_ISSUED | 本月已开票 |
|
||
|
||
### 6.3 发票状态枚举(records.status)
|
||
|
||
| code | statusText | 说明 |
|
||
|------|------------|------|
|
||
| REQUESTED | 待开票 | 已申请 |
|
||
| ISSUED | 待推送 | 已开票 |
|
||
| PUSHED | 已推送 | 已推送 |
|
||
| NONE | 未申请 | tab=ALL/NONE 时无发票行专用 |
|
||
|
||
### 6.4 发票类型枚举(invoiceType)
|
||
|
||
| code | 说明 |
|
||
|------|------|
|
||
| ELECTRONIC | 电子发票 |
|
||
| PAPER | 纸质发票 |
|
||
|
||
### 6.5 抬头类型枚举(titleType)
|
||
|
||
| code | 说明 |
|
||
|------|------|
|
||
| PERSONAL | 个人 |
|
||
| COMPANY | 企业 |
|
||
|
||
---
|
||
|
||
## 7 错误码
|
||
|
||
| 错误码 | 含义 | 前端处理建议 |
|
||
|--------|------|-------------|
|
||
| 200 | 成功 | 正常渲染 |
|
||
| 401 | 未认证 | 跳登录 |
|
||
| 403 | 无权限 | 提示无访问权限 |
|
||
| 500 | 服务端异常 | 通用错误提示 |
|
||
|
||
---
|
||
|
||
|
||
|
||
## 8 示例
|
||
|
||
### 8.1 典型成功——tab=ALL,混合行(有发票 + 无发票)
|
||
|
||
**请求**
|
||
```
|
||
GET /v3/admin/order/invoice/page?tab=ALL&pageNo=1&pageSize=20
|
||
```
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"tabCounts": [
|
||
{"code": "REQUESTED", "name": "待开票", "count": 3,},
|
||
{"code": "ISSUED", "name": "待推送", "count": 1,},
|
||
{"code": "PUSHED", "name": "已推送", "count": 5,},
|
||
{"code": "NONE", "name": "未申请", "count": 12,},
|
||
{"code": "ALL", "name": "全部", "count": 21}
|
||
],
|
||
"stats": [
|
||
{"code": "REQUESTED", "name": "待开票", "count": 3, "amount": 8800.00},
|
||
{"code": "ISSUED", "name": "已开票", "count": 1, "amount": 3200.00},
|
||
{"code": "PUSHED", "name": "已推送", "count": 5, "amount": 15600.00},
|
||
{"code": "CURRENT_MONTH_ISSUED", "name": "本月已开票", "count": 4, "amount": 12000.00}
|
||
],
|
||
"records": [
|
||
{
|
||
"id": "1923456789012345678",
|
||
"orderId": "1823456789012345678",
|
||
"orderNo": "HL20260620001",
|
||
"productName": "云南深度游7日",
|
||
"tierName": "2026-07-01班期",
|
||
"contactName": "张三",
|
||
"customizerName": "李定制",
|
||
"departureDate": "2026-07-01",
|
||
"adultCount": 2,
|
||
"childCount": 1,
|
||
"youngChildCount": 0,
|
||
"invoiceType": "ELECTRONIC",
|
||
"invoiceTypeText": "电子发票",
|
||
"titleType": "COMPANY",
|
||
"titleName": "北京科技有限公司",
|
||
"taxNo": "91110000123456789X",
|
||
"amount": 6800.00,
|
||
"email": "finance@example.com",
|
||
"status": "REQUESTED",
|
||
"statusText": "待开票",
|
||
"requestedBy": "李定制",
|
||
"requestedAt": "2026-06-20T10:30:00+08:00",
|
||
"issuedAt": null,
|
||
"issuedBy": null,
|
||
"invoiceNo": null,
|
||
"fileUrl": null,
|
||
"pdfName": null,
|
||
"pdfSize": null
|
||
},
|
||
{
|
||
"id": null,
|
||
"orderId": "1823456789012345679",
|
||
"orderNo": "HL20260619002",
|
||
"productName": "西藏圣地朝圣8日",
|
||
"tierName": "2026-06-25班期",
|
||
"contactName": "王五",
|
||
"customizerName": "赵定制",
|
||
"departureDate": "2026-06-25",
|
||
"adultCount": 4,
|
||
"childCount": 0,
|
||
"youngChildCount": 0,
|
||
"invoiceType": null,
|
||
"invoiceTypeText": null,
|
||
"titleType": null,
|
||
"titleName": null,
|
||
"taxNo": null,
|
||
"amount": null,
|
||
"email": null,
|
||
"status": "NONE",
|
||
"statusText": "未申请",
|
||
"requestedBy": null,
|
||
"requestedAt": null,
|
||
"issuedAt": null,
|
||
"issuedBy": null,
|
||
"invoiceNo": null,
|
||
"fileUrl": null,
|
||
"pdfName": null,
|
||
"pdfSize": null
|
||
}
|
||
],
|
||
"total": 21,
|
||
"pageNo": 1,
|
||
"pageSize": 20
|
||
}
|
||
}
|
||
```
|
||
|
||
### 8.2 边界情况——tab=ALL 传了 titleName(被静默忽略)
|
||
|
||
**请求**
|
||
```
|
||
GET /v3/admin/order/invoice/page?tab=ALL&titleName=北京科技&pageNo=1&pageSize=20
|
||
```
|
||
|
||
**说明**:titleName 参数不参与过滤,接口正常返回全量 COMPLETED 订单分页。records 中仍会出现 status=NONE 的无发票行,响应结构与 8.1 相同,此处不重复。
|
||
|
||
### 8.3 业务失败——无已完成订单时 tab=ALL 返回空列表
|
||
|
||
**请求**
|
||
```
|
||
GET /v3/admin/order/invoice/page?tab=ALL&pageNo=1&pageSize=20
|
||
```
|
||
|
||
**响应**(无 COMPLETED 订单时)
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"tabCounts": [
|
||
{"code": "REQUESTED", "name": "待开票", "count": 0},
|
||
{"code": "ISSUED", "name": "待推送", "count": 0},
|
||
{"code": "PUSHED", "name": "已推送", "count": 0},
|
||
{"code": "NONE", "name": "未申请", "count": 0},
|
||
{"code": "ALL", "name": "全部", "count": 0}
|
||
],
|
||
"stats": [
|
||
{"code": "REQUESTED", "name": "待开票", "count": 0, "amount": 0},
|
||
{"code": "ISSUED", "name": "已开票", "count": 0, "amount": 0},
|
||
{"code": "PUSHED", "name": "已推送", "count": 0, "amount": 0},
|
||
{"code": "CURRENT_MONTH_ISSUED", "name": "本月已开票", "count": 0, "amount": 0}
|
||
],
|
||
"records": [],
|
||
"total": 0,
|
||
"pageNo": 1,
|
||
"pageSize": 20
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 9 业务边界
|
||
|
||
**适用**:
|
||
- 订单状态为 COMPLETED(已完成)的订单才出现在 ALL/NONE tab
|
||
- REQUESTED/ISSUED/PUSHED 三个 tab 仍以发票记录为视角,只返回有对应状态发票的订单行
|
||
|
||
**不适用**:
|
||
- 进行中(未完成)订单不出现在任何 tab
|
||
- 已取消订单不出现
|
||
|
||
**特殊边界**:
|
||
- ALL tab 下:tabCounts.ALL.count = REQUESTED + ISSUED + PUSHED + NONE,四者无重叠
|
||
- 同一订单只有一张有效发票记录,不会重复出现
|
||
- titleName/requestedBy 在 ALL/NONE tab 传入后静默忽略,不报错、不影响结果
|
||
|
||
---
|
||
|
||
## 10 修改前后对比
|
||
|
||
### 字段级对比
|
||
|
||
| 字段 | 变更前 | 变更后 |
|
||
|------|--------|--------|
|
||
| tabCounts.ALL.count | 仅 REQUESTED+ISSUED+PUSHED 发票记录数之和 | 全部 COMPLETED 订单数(含无发票订单) |
|
||
| records(tab=ALL 时) | 仅返回有发票记录的订单行,无 status=NONE 行 | 返回全部 COMPLETED 订单,无发票行 status=NONE |
|
||
| records[].id(tab=ALL 时) | 所有行均有值 | 无发票行为 null |
|
||
| records[].amount(tab=ALL 时) | 所有行均有值 | 无发票行为 null |
|
||
|
||
### tab=ALL 计数对比(假设 2 张发票 + 12 个未申请)
|
||
|
||
变更前:
|
||
```
|
||
ALL.count = 2 (仅发票记录,ALL < NONE,矛盾)
|
||
NONE.count = 12
|
||
```
|
||
|
||
变更后:
|
||
```
|
||
ALL.count = 14 (= 2 + 12,ALL >= NONE,正确)
|
||
NONE.count = 12
|
||
```
|
||
|
||
---
|
||
|
||
## 11 影响评估 / 回滚
|
||
|
||
**破坏兼容性**:是
|
||
|
||
- 前端若假设 tab=ALL 列表行的 id 一定不为 null,需修改:无发票行 id 为 null
|
||
- 前端若用 id !== null 判断是否显示开票按钮,需改为判断 status 是否为 NONE
|
||
- tabCounts.ALL.count 现在总是 >= 之前的值,若前端有基于此的断言需同步更新
|
||
- stats 块不受影响,无需改动
|
||
|
||
**前端同步上线**:建议与本次后端部署同期上线,避免显示数据矛盾窗口期。
|
||
|
||
**回滚方案**:回滚后端至上一个版本 jar 即可恢复旧行为;若前端已适配新结构则需同步回滚前端。
|
||
|
||
---
|
||
|
||
## 12 注意事项
|
||
|
||
1. **无发票行的 id 字段为 null**,前端不可用 id 做有无发票判断,应改用 status 字段值是否为 NONE。
|
||
2. **amount 字段类型为 JSON number(元)**,不是字符串,渲染时直接用数值格式化。
|
||
3. **titleName/requestedBy 在 ALL/NONE tab 下静默忽略**,搜索框可保留,后端不过滤,行为符合预期。
|
||
4. **stats 块不受 tab 切换影响**,始终返回全局四项汇总统计,与当前 tab 无关。
|
||
5. **tabCounts 固定 5 项、顺序不变**,前端可按 code 匹配或按索引渲染。
|
||
|
||
---
|
||
|
||
## 13 关联 / 联系人
|
||
|
||
- **Issue**:[#4337 发票管理列表 ALL tab 改订单视角](https://git.1814.love:8443/wx/HL/issues/4337)
|
||
- **PR**:[#4338](https://git.1814.love:8443/wx/HL/pulls/4338)
|
||
- **后端负责人**:腰苏图(yaosutu)
|