hl-api-changelog/changelogs-v2/2026-06/23_4265_发票列表page结构调整-修改接口-管理后台.md
yaosutu 2a24c42ba5 feat(发票模块): 新增发票详情接口 + 发票列表 page 结构升级(管理后台·#4258/#4265)
- 新增 GET /v3/admin/order/invoice/{id} 发票详情接口(全量字段含专票四项/开票痕迹/推送痕迹)
- GET /v3/admin/order/invoice/page 列表行新增 9 个订单冗余字段
- tab 入参新增 NONE(未申请,代客申请候选)
- tabCounts 和 stats 从扁平对象改为数组(破坏性)
2026-06-23 12:37:57 +08:00

15 KiB

发票列表 page 结构调整(破坏性变更)

变更类型:修改接口(破坏性) 端类型:管理后台 日期2026-06-23 | Issue#4265 / #4271 | PR#4267 / #4271 / #4272 | 服务hl-order-service-v3

破坏性变更tabCountsstats 两个字段的数据结构均已从扁平对象改为数组,前端 tab 角标读取、统计卡渲染、列表行新增字段全部需要改动,上线时前后端需同步发布。


接口背景

财务发票管理列表本轮进行了三项结构升级:

  1. 列表行新增订单冗余字段(产品、出行人数、定制师等),让财务在列表页即可看到关键订单信息
  2. 新增 NONE tab(未申请),支持代客申请场景——展示已完成但尚未申请发票的订单
  3. tabCountsstats 从扁平对象改为数组,便于前端遍历渲染、后端动态扩展 tab 和统计维度

变更清单

# 变更类型 具体内容
1 出参新增字段 records[] 行新增 orderNo(补回填)、productNametierNamecontactNamecustomizerNamedepartureDateadultCountchildCountyoungChildCount 共 9 个字段
2 入参新增枚举值 tab 参数新增 NONE(未申请),返回「已完成且无有效发票」的订单行
3 出参结构破坏性变更 tabCounts 从扁平对象改为数组 TabCountItem[]
4 出参结构破坏性变更 stats 从扁平对象改为数组 StatItem[]

接口详情

说明
方法 + 路径 GET /v3/admin/order/invoice/page
接口名 财务发票管理列表(分页)
描述 分页查询发票列表,支持多 tab 过滤、统计卡汇总、tab 计数角标
认证 管理后台 JWTBearer Token
幂等性 只读,天然幂等
限流 无特殊限流

接口入参

Query 参数

参数名 类型 必填 说明
tab string Tab 过滤,见枚举节;默认 ALL本次新增 NONE
pageNo integer 页码,默认 1
pageSize integer 每页条数,默认 20,最大 100
keyword string 关键词搜索(订单号、客户名、定制师名)
invoiceType string 发票类型过滤,见枚举
startDate string 申请日期起,格式 YYYY-MM-DD
endDate string 申请日期止,格式 YYYY-MM-DD

请求体

无。


出参字段

返回结构:Result<PageResult<AdminInvoicePageRespVO>>,分页对象顶层附带 tabCounts(数组)和 stats(数组)。

分页包装层

字段名 类型 说明
total integer 总条数
pageNo integer 当前页码
pageSize integer 每页大小
records array 当前页数据行,见下表
tabCounts array 各 tab 计数数组(已改为数组),见 TabCountItem
stats array 统计卡汇总数组(已改为数组),见 StatItem

records[] 行字段

字段名 类型 说明
id string / null 发票 ID;NONE tab 行为 null
orderId string 订单 ID
orderNo string 订单编号(本次补充回填,原定义但无值)
invoiceType string / null 发票类型;NONE tab 行为 null
invoiceTypeText string / null 发票类型中文名;NONE tab 行为 null
titleName string / null 发票抬头;NONE tab 行为 null
amount string / null 开票金额,字符串;NONE tab 行为 null
status string 发票状态;NONE tab 行固定为 "NONE"
statusText string 发票状态中文名;NONE tab 行固定为 "未申请"
requestedAt string / null 申请时间;NONE tab 行为 null
productName string 产品名称(新增)
tierName string / null 产品档次名称(新增)
contactName string 客户联系人姓名(新增)
customizerName string / null 定制师姓名(新增)
departureDate string 出发日期,格式 YYYY-MM-DD(新增)
adultCount integer 成人数(新增)
childCount integer 儿童数(新增)
youngChildCount integer 婴儿数(新增)

出行人数展示:将 adultCount / childCount / youngChildCount 三值组合为「2成人1儿童」等文案,数量为 0 的维度可省略。

TabCountItem 字段

字段名 类型 说明
code string Tab 枚举值
name string Tab 中文名
count integer 该 tab 下的条数

StatItem 字段

字段名 类型 说明
code string 统计维度枚举值
name string 统计维度中文名(直接渲染,无需前端自行拼接)
count integer 该维度数量
amount string 该维度金额,单位分,字符串,保留两位小数,如 "98600.00"

枚举 / 数据字典

tab 入参枚举(含新增 NONE

枚举值 说明 records 行特征
REQUESTED 待开票 正常发票行,发票字段有值
ISSUED 待推送(已开票) 正常发票行,fileUrl 有值
PUSHED 已推送 正常发票行,pushedAt 有值
NONE 未申请(新增) 订单行,id / invoiceType 等发票字段均为 null,status="NONE"
ALL 全部 混合行,包含 NONE 行和发票行

tabCounts 数组 code 枚举

code name
REQUESTED 待开票
ISSUED 待推送
PUSHED 已推送
NONE 未申请
ALL 全部

stats 数组 code 枚举

code name 备注
REQUESTED 待开票 待处理发票的金额汇总
ISSUED 已开票·待推送 含义与 tabCounts 中 ISSUED 相同,语境不同
PUSHED 已推送 已推送给客户的金额汇总
CURRENT_MONTH_ISSUED 本月已开票 当月开票汇总(跨 ISSUED + PUSHED

ISSUED 在 tabCounts 中文名是「待推送」,在 stats 中文名是「已开票·待推送」,含义相同但语境不同。后端按语境在 name 字段直接返回正确文案,前端直接渲染 name,不要按 code 自行拼文案

发票类型invoiceType

枚举值 中文名
VAT_NORMAL 增值税普通发票
VAT_SPECIAL 增值税专用发票
ELECTRONIC 电子发票

错误码

错误码 含义 触发场景
401 未授权 未携带有效 JWT
403 无权限 当前账号无发票管理权限
400 参数错误 tab 传入不存在的枚举值

示例

典型成功ALL tab,返回数组结构

请求:

GET /v3/admin/order/invoice/page?tab=ALL&pageNo=1&pageSize=20
Authorization: Bearer <token>

响应:

{
  "code": 200,
  "msg": "success",
  "data": {
    "total": 12,
    "pageNo": 1,
    "pageSize": 20,
    "records": [
      {
        "id": "1934567890123456789",
        "orderId": "1920000000000000001",
        "orderNo": "ORD2026062300001",
        "invoiceType": "VAT_NORMAL",
        "invoiceTypeText": "增值税普通发票",
        "titleName": "呼籁科技有限公司",
        "amount": "98600",
        "status": "REQUESTED",
        "statusText": "待开票",
        "requestedAt": "2026-06-20T14:30:00",
        "productName": "云南香格里拉深度游5日",
        "tierName": "标准档",
        "contactName": "李四",
        "customizerName": "王五",
        "departureDate": "2026-07-10",
        "adultCount": 2,
        "childCount": 1,
        "youngChildCount": 0
      }
    ],
    "tabCounts": [
      { "code": "REQUESTED", "name": "待开票", "count": 3 },
      { "code": "ISSUED", "name": "待推送", "count": 2 },
      { "code": "PUSHED", "name": "已推送", "count": 2 },
      { "code": "NONE", "name": "未申请", "count": 5 },
      { "code": "ALL", "name": "全部", "count": 12 }
    ],
    "stats": [
      { "code": "REQUESTED", "name": "待开票", "count": 3, "amount": "1000.00" },
      { "code": "ISSUED", "name": "已开票·待推送", "count": 2, "amount": "98600.00" },
      { "code": "PUSHED", "name": "已推送", "count": 2, "amount": "50000.00" },
      { "code": "CURRENT_MONTH_ISSUED", "name": "本月已开票", "count": 4, "amount": "148600.00" }
    ]
  }
}

边界情况NONE tab,发票字段为 null 的订单行)

请求:

GET /v3/admin/order/invoice/page?tab=NONE&pageNo=1&pageSize=20
Authorization: Bearer <token>

响应:

{
  "code": 200,
  "msg": "success",
  "data": {
    "total": 5,
    "pageNo": 1,
    "pageSize": 20,
    "records": [
      {
        "id": null,
        "orderId": "1920000000000000099",
        "orderNo": "ORD2026062200099",
        "invoiceType": null,
        "invoiceTypeText": null,
        "titleName": null,
        "amount": null,
        "status": "NONE",
        "statusText": "未申请",
        "requestedAt": null,
        "productName": "西藏拉萨朝圣7日",
        "tierName": null,
        "contactName": "钱八",
        "customizerName": "孙九",
        "departureDate": "2026-06-15",
        "adultCount": 4,
        "childCount": 0,
        "youngChildCount": 1
      }
    ],
    "tabCounts": [
      { "code": "REQUESTED", "name": "待开票", "count": 3 },
      { "code": "ISSUED", "name": "待推送", "count": 2 },
      { "code": "PUSHED", "name": "已推送", "count": 2 },
      { "code": "NONE", "name": "未申请", "count": 5 },
      { "code": "ALL", "name": "全部", "count": 12 }
    ],
    "stats": [
      { "code": "REQUESTED", "name": "待开票", "count": 3, "amount": "1000.00" },
      { "code": "ISSUED", "name": "已开票·待推送", "count": 2, "amount": "98600.00" },
      { "code": "PUSHED", "name": "已推送", "count": 2, "amount": "50000.00" },
      { "code": "CURRENT_MONTH_ISSUED", "name": "本月已开票", "count": 4, "amount": "148600.00" }
    ]
  }
}

业务失败tab 枚举值不存在)

请求:

GET /v3/admin/order/invoice/page?tab=INVALID
Authorization: Bearer <token>

响应:

{
  "code": 400,
  "msg": "参数错误tab 枚举值不合法",
  "data": null
}

业务边界

适用场景

  • 财务管理后台「发票管理」列表页
  • 代客申请场景:切换到 NONE tab 查看「已完成但未申请发票」的订单,点击进入代客申请流程

不适用场景

  • 小程序端查看发票状态(走小程序端专属接口)
  • 查询单张发票全量明细(走 GET /v3/admin/order/invoice/{id} 详情接口)

特殊边界

  • NONE tab 行是订单行,不是发票行:id 为 null,发票字段均为 null,status 固定为字符串 "NONE",前端需按 status 判断是否展示发票操作按钮
  • ALL tab 混合 NONE 行和发票行:遍历 records 时需按 status === "NONE" 区分行类型
  • amount 单位为分,字符串格式,前端展示时除以 100 转为元

修改前后对比

tabCounts 字段

旧(扁平对象):

{
  "tabCounts": {
    "requestedCount": 3,
    "issuedCount": 2,
    "pushedCount": 2,
    "allCount": 12,
    "noneCount": 5
  }
}

新(数组,现行):

{
  "tabCounts": [
    { "code": "REQUESTED", "name": "待开票", "count": 3 },
    { "code": "ISSUED", "name": "待推送", "count": 2 },
    { "code": "PUSHED", "name": "已推送", "count": 2 },
    { "code": "NONE", "name": "未申请", "count": 5 },
    { "code": "ALL", "name": "全部", "count": 12 }
  ]
}

stats 字段

旧(扁平对象):

{
  "stats": {
    "requestedCount": 3,
    "requestedAmount": 1000,
    "issuedCount": 2,
    "issuedAmount": 98600,
    "pushedCount": 2,
    "pushedAmount": 50000,
    "currentMonthIssuedCount": 4,
    "currentMonthIssuedAmount": 148600
  }
}

新(数组,现行):

{
  "stats": [
    { "code": "REQUESTED", "name": "待开票", "count": 3, "amount": "1000.00" },
    { "code": "ISSUED", "name": "已开票·待推送", "count": 2, "amount": "98600.00" },
    { "code": "PUSHED", "name": "已推送", "count": 2, "amount": "50000.00" },
    { "code": "CURRENT_MONTH_ISSUED", "name": "本月已开票", "count": 4, "amount": "148600.00" }
  ]
}

tab 入参新增值

旧可选值 新增值
REQUESTED / ISSUED / PUSHED / ALL 新增 NONE(未申请)

records[] 行新增字段

字段 变化
orderNo 原已定义,本次补充回填(实际有值)
productName 新增
tierName 新增
contactName 新增
customizerName 新增
departureDate 新增
adultCount 新增
childCount 新增
youngChildCount 新增

影响评估 / 回滚

破坏兼容性评估

模块 是否影响 必须改动
Tab 角标数字渲染 旧代码读 tabCounts.requestedCount 等扁平字段会 undefined,需改为遍历数组按 code 取值
统计卡渲染 旧代码读 stats.requestedCount / stats.requestedAmount 等扁平字段会 undefined,需改为遍历数组按 codecount / amount
列表行渲染 需新增 新增 9 个字段需接线渲染(产品名、出行人数组合、定制师、出发日期等)
NONE tab 处理 需新增 NONE tab 下行无发票 id,代客申请按钮需按 status==="NONE" 判断
ALL tab 混合行 需新增 ALL tab 下需区分 NONE 行和正常发票行

前端同步上线要求

本次变更前后端需同步发布。旧前端读新后端的数组结构,tab 角标和统计卡均会渲染为空。

回滚方案

如需回滚后端,通知前端同步回滚对应代码;两端数据结构不匹配会导致 tab 角标消失和统计卡白屏。


注意事项

  1. tabCountsstats 均已变为数组,旧的扁平对象键名已全部废弃,前端读取方式必须更新
  2. NONE tab 行中 status 是字符串 "NONE",而不是 null,前端用 status === "NONE" 判断行类型
  3. stats 中的 amount 单位为,字符串格式,保留两位小数,前端展示时除以 100 转为元
  4. ISSUED 在 tabCounts 和 stats 中 name 不同,直接渲染 name 字段即可,不要自行拼接
  5. adultCount / childCount / youngChildCount 为整数,0 的维度建议展示时省略

关联 / 联系人

内容
Issue #4265 发票列表 NONE tab / #4271 列表订单冗余字段
PR #4267 / #4271 / #4272 stats 拍平
后端负责人 yst