--- schema: "hl-changelog/v2" ticket: "8796" title: "发票管理列表出参移除顶部 stats 统计块(records / tabCounts 不变)" consumer: "admin" author: "yst" change_type: "修改接口" backend_status: "merged" gateway_status: "not_required" frontend_status: "implemented" frontend_owner: "mmg" frontend_ref: "86db824e26ce4eeeb1717e3f1c7b9e76a91c88b9" target_release: "v2.1" verified_at: "2026-10-05" status_note: "产品决定下线发票管理列表顶部 4 张统计卡片;出参根级 stats 字段已删,tabCounts 角标计数保留。已合 dev-v3。前端需删除顶部统计卡片渲染逻辑。前端已交付(2026-10-05):index.vue 删卡片模板/STAT_BAR_MAP/stats ref/syncStats/全部 invoices-stat 样式,invoice.js JSDoc 同步,spec mock 清理,提交 86db824e2。" updated_at: "2026-10-05" base: "dev-v3" --- # order-v3 invoice:发票管理列表出参移除 stats 统计块(管理后台) > 注意:**破坏性变更(删字段)**:`GET /v3/admin/order/invoice/page` 出参根级 `stats` 字段已删除。前端若仍引用 `stats` 渲染顶部统计卡片,取到的将是 `undefined`,卡片会空白或异常,**必须同步下线该卡片渲染**。 ## 1. 接口背景 管理后台「财务 → 发票管理」列表页顶部原有 4 张统计卡片:待开票 / 已开票·待推送 / 已推送 / 本月已开票,数据来自列表接口出参根级的 `stats` 数组。 产品决定不要这 4 张卡片了(#8796)。后端随之把 `stats` 统计块从列表接口出参整体移除,并清掉后端只为它服务的统计查询逻辑。列表行数据、tab 角标计数均不受影响。 ## 2. 变更清单 | # | 接口 | 变更点 | 类型 | |---|------|--------|------| | 1 | `GET /v3/admin/order/invoice/page` | 出参根级删除 `stats` 字段(原 InvoiceStatItemVO 数组:4 项统计卡片数据) | 出参字段删除 | 入参、路径、`records` 列表行结构、`tabCounts` 角标计数**全部不变**。 ## 3. 接口详情 | 项 | 说明 | |---|---| | 服务 | hl-order-service-v3(端口 8086) | | 路径 | `GET /v3/admin/order/invoice/page` | | 使用场景 | 管理后台 → 财务 → 发票管理列表(分页 + tab 过滤 + 搜索) | | 认证 | 管理后台登录态(JWT);网关既有路由 `/v3/admin/**`,无新增网关配置 | | 幂等性 | 纯查询,天然幂等 | | 限流 | 无特殊限流 | ## 4. 接口入参 **入参零变化**(Query 参数): | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | pageNo | int | 否 | 页码,默认 1 | | pageSize | int | 否 | 每页条数,默认 10 | | tab | string | 否 | tab 过滤:REQUESTED(待开票)/ ISSUED(待推送)/ PUSHED(已推送)/ NONE(未申请)/ ALL(全部),默认 ALL | | orderNo | string | 否 | 订单号模糊搜索 | | contactName | string | 否 | 客户名(主联系人)模糊搜索 | | titleName | string | 否 | 开票抬头模糊搜索(NONE tab 传此参数结果置空) | | requestedBy | string | 否 | 申请人(定制师/用户姓名)模糊搜索 | ## 5. 出参字段 响应统一包装 `Result`,`data` 结构如下。 ### 5.1 顶层结构(变更后现状) | 字段 | 类型 | 说明 | |------|------|------| | records | array | 发票列表行(结构见 5.2) | | total | long | 总条数 | | page | int | 当前页码 | | pageSize | int | 每页条数 | | tabCounts | array | 各 tab 计数(**保留不变**,见 5.3) | | ~~stats~~ | ~~array~~ | **已删除**,不再返回 | 部署后实测出参顶层 keys = `["records", "total", "page", "pageSize", "tabCounts"]`,已无 `stats`。 ### 5.2 records 列表行(InvoicePageItemRespVO,本次未动) | 字段 | 类型 | 说明 | |------|------|------| | id | string | 发票 ID(字符串防 JS 精度丢失) | | orderId | string | 订单 ID(字符串) | | orderNo | string | 订单号 | | productName | string | 产品名 | | productCoverImg | string | 产品封面图 URL | | tierName | string | 档位名 | | contactName | string | 客户名(主联系人) | | customizerName | string | 定制师名 | | departureDate | string(yyyy-MM-dd) | 出发日期 | | adultCount | int | 成人数 | | childCount | int | 儿童数 | | youngChildCount | int | 婴儿/小童数 | | invoiceType | string | 发票类型(VAT_NORMAL / VAT_SPECIAL) | | invoiceTypeText | string | 发票类型文案 | | titleType | string | 抬头类型(COMPANY / PERSONAL) | | titleName | string | 开票抬头 | | taxNo | string | 税号 | | amount | number | 开票金额(元) | | email | string | 收件邮箱 | | status | string | 发票状态(REQUESTED / ISSUED / PUSHED / VOIDED) | | statusText | string | 状态文案(待开票 / 已开票 / 已推送 / 已作废) | | requestedBy | string | 申请人(定制师名或用户名) | | requestedAt | string | 申请时间 | | issuedAt | string | 开票时间(ISSUED 后有值) | | issuedBy | string | 开票人(财务员工名) | | invoiceNo | string | 发票号(财务登记) | | fileUrl | string | 发票文件 URL(ISSUED/PUSHED 后有值) | | pdfName | string | 发票 PDF 文件名 | | pdfSize | long | 文件大小(字节) | | teamNo | string | 团号 | | createTime | string | 订单创建时间 | | orderStatus | string | 订单状态 | | orderStatusText | string | 订单状态文案 | | flowStatus | string | 流程状态 | | flowStatusText | string | 流程状态文案 | | orderAmount | number | 订单应收总额(元) | | paidAmount | number | 累计已收(元) | | refundedAmount | number | 累计已退(元) | | settlementAmount | number | 核单金额(元) | ### 5.3 tabCounts 项(InvoiceTabCountVO,保留不变) 固定 5 项,顺序:REQUESTED / ISSUED / PUSHED / NONE / ALL: | 字段 | 类型 | 说明 | |------|------|------| | code | string | 状态编码(REQUESTED / ISSUED / PUSHED / NONE / ALL) | | name | string | 中文名(待开票 / 待推送 / 已推送 / 未申请 / 全部) | | count | int | 计数 | tabCounts 为全局全量计数,**不受搜索条件影响**(此行为未变)。 ## 6. 枚举 / 数据字典 本次无枚举变化。涉及枚举均为既有值: | 枚举 | 取值 | |------|------| | tab | REQUESTED / ISSUED / PUSHED / NONE / ALL | | status(发票) | REQUESTED(待开票)/ ISSUED(已开票)/ PUSHED(已推送)/ VOIDED(已作废) | | invoiceType | VAT_NORMAL / VAT_SPECIAL | | titleType | COMPANY / PERSONAL | 原 `stats[].code` 的 4 个取值(REQUESTED / ISSUED / PUSHED / CURRENT_MONTH_ISSUED)随字段一并废弃,不再有消费方。 ## 7. 错误码 本接口为查询接口,正常情况无业务错误码。公共错误(未登录 / 无权限)走网关与全局异常处理。发票域业务错误码(5815xx 段,如 581500 发票不存在等)本次无变化,且本列表接口不抛出。 ## 8. 示例 ### 8.1 典型成功(REQUESTED tab,第一页) 请求: ``` GET /v3/admin/order/invoice/page?tab=REQUESTED&pageNo=1&pageSize=10 ``` 响应(注意顶层已无 stats): ```json { "code": 0, "data": { "records": [ { "id": "1975812345678901234", "orderId": "1960123456789012345", "orderNo": "2026092800001", "productName": "呼伦贝尔草原 5 日定制游", "productCoverImg": "https://oss.example.com/cover/abc.jpg", "tierName": "舒适档", "contactName": "张三", "customizerName": "李定制", "departureDate": "2026-10-15", "adultCount": 2, "childCount": 1, "youngChildCount": 0, "invoiceType": "VAT_NORMAL", "invoiceTypeText": "增值税普通发票", "titleType": "COMPANY", "titleName": "北京某某科技有限公司", "taxNo": "91110108MA01C8XH3B", "amount": 12800.00, "email": "zhangsan@example.com", "status": "REQUESTED", "statusText": "待开票", "requestedBy": "李定制", "requestedAt": "2026-10-01 10:23:45", "issuedAt": null, "issuedBy": null, "invoiceNo": null, "fileUrl": null, "pdfName": null, "pdfSize": null, "teamNo": "T2026101501", "createTime": "2026-09-28 14:02:11", "orderStatus": "COMPLETED", "orderStatusText": "已完成", "flowStatus": "SETTLED", "flowStatusText": "已核单", "orderAmount": 12800.00, "paidAmount": 12800.00, "refundedAmount": 0.00, "settlementAmount": 12800.00 } ], "total": 1, "page": 1, "pageSize": 10, "tabCounts": [ { "code": "REQUESTED", "name": "待开票", "count": 1 }, { "code": "ISSUED", "name": "待推送", "count": 3 }, { "code": "PUSHED", "name": "已推送", "count": 12 }, { "code": "NONE", "name": "未申请", "count": 45 }, { "code": "ALL", "name": "全部", "count": 61 } ] }, "msg": "" } ``` ### 8.2 边界情况(搜索无命中,空列表) 请求: ``` GET /v3/admin/order/invoice/page?tab=ALL&titleName=不存在的公司名&pageNo=1&pageSize=10 ``` 响应(records 空数组、total=0,**tabCounts 仍为全局计数**,无 stats): ```json { "code": 0, "data": { "records": [], "total": 0, "page": 1, "pageSize": 10, "tabCounts": [ { "code": "REQUESTED", "name": "待开票", "count": 1 }, { "code": "ISSUED", "name": "待推送", "count": 3 }, { "code": "PUSHED", "name": "已推送", "count": 12 }, { "code": "NONE", "name": "未申请", "count": 45 }, { "code": "ALL", "name": "全部", "count": 61 } ] }, "msg": "" } ``` ### 8.3 业务失败(未登录 / token 失效) 请求: ``` GET /v3/admin/order/invoice/page?tab=ALL (不带 Authorization 头) ``` 响应: ```json { "code": 401, "data": null, "msg": "未登录或登录已过期" } ``` ## 9. 业务边界 - **适用**:财务在管理后台发票管理列表做分页浏览、tab 切换、按订单号/客户名/抬头/申请人搜索;tab 角标计数展示。 - **不适用**:任何需要「待开票 / 已开票 / 已推送 / 本月已开票」汇总卡片数据的场景 —— 后端已不再提供该汇总,前端**不要**自行用 tabCounts 拼凑原 stats 语义(原 stats 含金额合计与「本月已开票」口径,tabCounts 只有张数且无金额维度,CURRENT_MONTH_ISSUED 当月维度无任何替代数据源)。 - **特殊边界**:VOIDED(已作废)发票不进任何 tab,也不计入 tabCounts;NONE tab 传 titleName / requestedBy 搜索时结果强制为空(既有行为,未变)。 ## 10. 修改前后对比 ### 10.1 出参顶层结构对比 | 顶层字段 | 修改前 | 修改后 | |---------|--------|--------| | records | 有 | 有(不变) | | total | 有 | 有(不变) | | page | 有 | 有(不变) | | pageSize | 有 | 有(不变) | | tabCounts | 有 | 有(不变) | | stats | 有(4 项统计数组) | **已删除** | ### 10.2 被删除的 stats 项结构(原 InvoiceStatItemVO,仅作存档) 原 `stats` 为固定 4 项数组,顺序 REQUESTED / ISSUED / PUSHED / CURRENT_MONTH_ISSUED: | 字段 | 类型 | 说明 | |------|------|------| | code | string | 状态编码(REQUESTED / ISSUED / PUSHED / CURRENT_MONTH_ISSUED) | | name | string | 中文名(待开票 / 已开票·待推送 / 已推送 / 本月已开票) | | count | int | 张数 | | amount | number | 合计金额(元) | ### 10.3 行为级对比 | 场景 | 修改前 | 修改后 | |------|--------|--------| | 列表查询性能 | 每次查询额外执行统计聚合(按状态计数 + 金额合计、本月口径) | 不再执行统计聚合,查询更轻 | | tabCounts 值 | 全量计数不受搜索影响 | 同左(不变) | | 前端顶部卡片 | 可用 stats 渲染 4 张卡片 | 数据源消失,卡片必须下线 | ## 11. 影响评估 / 回滚 ### 11.1 影响评估 - **破坏兼容**:是(删出参字段)。 - 前端若仍引用 `data.stats` → 取到 `undefined`,顶部卡片空白或渲染异常(接口本身正常返回,不会报错)。 - **需要前端同步上线**:是。发票管理页须删除顶部统计卡片渲染逻辑。上线顺序建议:前端先删卡片上线(安全,后端 stats 仍在但无人消费),后端版本随后生效;反向顺序则会出现卡片空白窗口。 - tab 角标、列表行、搜索、分页逻辑零改动。 ### 11.2 回滚方案 - 后端回滚 = revert PR #8797(恢复 stats 字段与统计查询)。 - 前端如已删卡片渲染,回滚后端后卡片无数据渲染,需同步回滚前端或保持卡片下线(产品决定下线,原则上不回滚)。 ## 12. 注意事项 1. **前端必做**:删除发票管理列表页顶部 4 张统计卡片(待开票 / 已开票·待推送 / 已推送 / 本月已开票)的全部渲染逻辑与相关样式,清理对 `data.stats` / `stats[]` 的所有引用与类型定义。 2. 不要试图用 `tabCounts` 还原原统计卡片:原 stats 含金额合计(amount)与「本月已开票」(CURRENT_MONTH_ISSUED)口径,tabCounts 无此数据。 3. 若产品后续需要恢复统计卡片,属新需求,需重新走 Issue 排期,不是回滚本变更。 4. 本变更只影响 `GET /v3/admin/order/invoice/page` 一个接口;发票详情(`GET /v3/admin/order/invoice/{id}`)、开票 / 重传 / 推送等接口均未动。 ## 13. 关联 / 联系人 - Issue:https://git.1814.love/wx/HL/issues/8796 - PR:https://git.1814.love/wx/HL/pulls/8797 - Commit:https://git.1814.love/wx/HL/commit/ba01a0719a - 后端负责人:@yst