--- schema: "hl-changelog/v2" ticket: "invoice-page-add-order-fields" title: "发票管理列表行补订单侧字段(团号/创建时间/订单状态/流程状态/金额 4 项)" consumer: "admin" author: "yst(GIT)" change_type: "修改接口" backend_status: "merged" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "de19fb776ea6272b1fd0bebad4f4e0a492960212" target_release: "" verified_at: "2026-09-17" status_note: "发票管理列表 GET /v3/admin/order/invoice/page 行级 records[] 纯新增 10 个订单侧字段(teamNo/createTime/orderStatus/orderStatusText/flowStatus/flowStatusText/orderAmount/paidAmount/refundedAmount/settlementAmount),入参/既有出参/错误码零变化,NONE tab 同样回填。后端已合并 dev-v3。[mmg 2026-09-17 交付] 发票管理列表(order/invoice)接入原挂起三列:团号 teamNo(订单号后,空显-)/订单总额 orderAmount/退款金额 refundedAmount(开票金额后,右对齐 yuanDisplay 无值显-);其余 7 字段按页面「砍无效字段突出扫读」既有取舍不铺,注释留痕契约已备(orderStatusText/flowStatusText 脏数据可为 null,后续接入须兜底)。spec 列构成断言更新+#7897 三列渲染专项,定向 7/7,scoped checkpoint 4 项全绿。" updated_at: "2026-09-17" base: "dev-v3" --- # 发票管理列表行补订单侧字段(管理后台) > **服务**: hl-order-service-v3(invoice 模块) > **类型**: 🔧 修改接口(出参纯新增字段,向后兼容) > **日期**: 2026-09-17 > **影响范围**: 管理后台财务域「发票管理」列表页 > **关联 Issue**: #7897(PR #7899) --- ## 一、接口背景 发票管理列表(`GET /v3/admin/order/invoice/page`)此前行数据只有发票侧字段 + 少量订单字段(订单号/产品名/客户名等),财务对账时需要反复跳订单详情看团号、订单状态、金额。本次在**行级 `records[]` 元素上纯新增 10 个订单侧字段**,让发票列表一行即可看全订单关键信息。 - 纯新增,**不删不改任何既有字段**,前端不接入也不影响现有功能 - **NONE tab(未申请发票)的行也照常回填**这些订单侧字段(无发票实体的行与有发票的行走同一填充逻辑) - 入参、其余出参(stats / tabCounts / 分页元数据)、错误码**均无变化** --- ## 二、变更清单 | # | 变更 | 类型 | 说明 | |---|------|------|------| | 1 | `GET /v3/admin/order/invoice/page` 响应 `records[]` 新增 10 字段 | 🔧 修改 | 团号 / 订单创建时间 / 订单状态码+文案 / 流程状态码+文案 / 应收总额 / 累计已收 / 累计已退 / 核单金额 | --- ## 三、接口详情 | 项 | 说明 | |---|---| | 方法 + 路径 | `GET /v3/admin/order/invoice/page` | | 接口名 | 财务发票管理列表(分页 + tab + 统计 + 搜索) | | 使用场景 | 管理后台财务域发票管理列表页,按 tab(待开票/待推送/已推送/未申请/全部)分页查看,顶部带统计卡与 tab 计数 | | 认证 | 管理后台 JWT(网关 `/v3/admin/**` 鉴权) | | 幂等性 | 只读查询,天然幂等 | | 限流 | 无特殊限流,走网关默认 | --- ## 四、接口入参 ### 4.1 Query 参数(@ModelAttribute 绑定,无请求体) | 参数 | 类型 | 必填 | 默认 | 说明 | |------|------|------|------|------| | `page` | Integer | 否 | 1 | 页码,最小 1(兼容别名 `pageNo`) | | `pageSize` | Integer | 否 | 20 | 每页条数,1-100 | | `tab` | String | 否 | ALL | tab 过滤:`REQUESTED`(待开票)/ `ISSUED`(待推送)/ `PUSHED`(已推送)/ `NONE`(未申请)/ `ALL`(全部) | | `orderNo` | String | 否 | - | 订单号模糊搜索 | | `contactName` | String | 否 | - | 客户名模糊搜索 | | `titleName` | String | 否 | - | 开票抬头模糊搜索(发票侧字段;NONE tab 传了发票关键词则结果置空) | | `requestedBy` | String | 否 | - | 申请人(定制师/用户姓名)模糊搜索(口径同 titleName) | ### 4.2 请求体字段 无(GET,无 body)。 **本次入参零变化。** --- ## 五、出参字段 外层统一响应 `Result`(`code` / `msg` / `data`),`data` 结构: | 字段 | 类型 | 说明 | |------|------|------| | `records` | Array\ | 发票列表行(本次变更点,见下行级字段表) | | `total` | Long | 总记录数 | | `page` | Integer | 当前页码 | | `pageSize` | Integer | 每页条数 | | `stats` | Array | 顶部统计(顺序固定:REQUESTED / ISSUED / PUSHED / CURRENT_MONTH_ISSUED;每项含 `code`/`name`/`count`/`amount`) | | `tabCounts` | Array | 各 tab 计数(顺序固定:REQUESTED / ISSUED / PUSHED / NONE / ALL;每项含 `code`/`name`/`count`) | ### 5.1 行级 `records[]` 字段表(完整自包含,🔧 标记 = 本次新增) | 字段 | 类型 | 说明 | |------|------|------| | `id` | String | 发票 ID(Long 序列化为字符串防 JS 精度丢失;NONE 行无发票实体,为 null) | | `orderId` | String | 订单 ID(Long 序列化为字符串) | | `orderNo` | String | 订单号 | | `productName` | String | 产品名 | | `productCoverImg` | String | 产品封面图 URL | | `tierName` | String | 档位名 | | `contactName` | String | 客户名(主联系人) | | `customizerName` | String | 定制师名 | | `departureDate` | String | 出发日期(yyyy-MM-dd) | | `adultCount` | Integer | 成人数 | | `childCount` | Integer | 儿童数 | | `youngChildCount` | Integer | 婴儿/小童数 | | `invoiceType` | String | 发票类型(`VAT_NORMAL` / `VAT_SPECIAL`;NONE 行为 null) | | `invoiceTypeText` | String | 发票类型文案(NONE 行为 null) | | `titleType` | String | 抬头类型(`COMPANY` / `PERSONAL`;NONE 行为 null) | | `titleName` | String | 开票抬头(NONE 行为 null) | | `taxNo` | String | 税号(NONE 行为 null) | | `amount` | Number | 开票金额,单位元(NONE 行为 null) | | `email` | String | 收件邮箱(NONE 行为 null) | | `status` | String | 发票状态(`REQUESTED` / `ISSUED` / `PUSHED` / `VOIDED`;NONE 行固定为 `NONE`) | | `statusText` | String | 状态文案(待开票 / 已开票 / 已推送 / 已作废;NONE 行固定为「未申请」) | | `requestedBy` | String | 申请人(定制师名或用户名;NONE 行为 null) | | `requestedAt` | String | 申请时间(NONE 行为 null) | | `issuedAt` | String | 开票时间(ISSUED 后有值) | | `issuedBy` | String | 开票人(财务员工名) | | `invoiceNo` | String | 发票号(财务登记) | | `fileUrl` | String | 发票文件 URL(ISSUED/PUSHED 后有值) | | `pdfName` | String | 发票 PDF 文件名 | | `pdfSize` | Long | 文件大小(字节) | | `teamNo` 🔧 | String | **团号**(订单所属团;无团号为 null) | | `createTime` 🔧 | String | **订单创建时间**(yyyy-MM-dd HH:mm:ss) | | `orderStatus` 🔧 | String | **订单状态码**(枚举见 §6.1) | | `orderStatusText` 🔧 | String | **订单状态文案**(枚举 label;码值为 null 或脏值时为 null,见 §九容错) | | `flowStatus` 🔧 | String | **流程状态码**(枚举见 §6.2) | | `flowStatusText` 🔧 | String | **流程状态文案**(枚举 label;容错同 orderStatusText) | | `orderAmount` 🔧 | Number | **订单应收总额**,单位元 | | `paidAmount` 🔧 | Number | **累计已收**,单位元 | | `refundedAmount` 🔧 | Number | **累计已退**,单位元 | | `settlementAmount` 🔧 | Number | **核单金额**,单位元(未核单为 null 或 0,以订单实际值为准) | --- ## 六、枚举 / 数据字典 ### 6.1 `orderStatus` 订单状态码(OrderStatus 枚举,共 6 值) | 码值 | 文案(orderStatusText) | |------|------| | `PENDING_PAY` | 待支付 | | `CUSTOMIZING` | 定制中 | | `PENDING_DEPARTURE` | 待出行 | | `TRAVELLING` | 出行中 | | `COMPLETED` | 已完成 | | `CANCELLED` | 已取消 | ### 6.2 `flowStatus` 流程状态码(OrderFlowStatus 枚举,共 12 值) | 码值 | 文案(flowStatusText) | |------|------| | `AWAITING_PAY` | 待支付 | | `AWAITING_PROFILE` | 待补全信息 | | `RESOURCE_PREPARING` | 资源准备 | | `PENDING_CONFIRM` | 待确认 | | `PENDING_DEPARTURE` | 待出行 | | `TRAVELLING` | 出行中 | | `PENDING_REVIEW` | 待核单 | | `REVIEWING` | 核单中 | | `PENDING_SETTLE` | 待结算 | | `SETTLED` | 已结算 | | `COMPLETED` | 已完成 | | `CANCELLED` | 已取消 | ### 6.3 既有枚举(未变,仅列全) - `status` 发票状态:`REQUESTED`(待开票)/ `ISSUED`(已开票)/ `PUSHED`(已推送)/ `VOIDED`(已作废);NONE 行固定 `NONE` - `invoiceType`:`VAT_NORMAL`(增值税普通发票)/ `VAT_SPECIAL`(增值税专用发票) - `titleType`:`COMPANY`(公司)/ `PERSONAL`(个人) - `tab`:`REQUESTED` / `ISSUED` / `PUSHED` / `NONE` / `ALL` --- ## 七、错误码 本接口为只读分页查询,**本次无新增错误码**。参数校验失败(如 `page=0`、`pageSize=101`)走统一参数校验响应(HTTP 400 / code=400)。invoice 模块错误码段位 581500-581599 中本接口不抛任何业务错误码。 --- ## 八、示例 ### 8.1 典型成功(REQUESTED tab,行已回填订单侧字段) 请求: ``` GET /v3/admin/order/invoice/page?tab=REQUESTED&page=1&pageSize=20 ``` 响应: ```json { "code": 0, "msg": "success", "data": { "records": [ { "id": "1956789012345678901", "orderId": "1956000000000000001", "orderNo": "2026091000001", "productName": "呼伦贝尔草原 5 日定制游", "productCoverImg": "https://oss.example.com/cover/xxx.jpg", "tierName": "舒适档", "contactName": "张三", "customizerName": "李定制", "departureDate": "2026-10-01", "adultCount": 2, "childCount": 1, "youngChildCount": 0, "invoiceType": "VAT_NORMAL", "invoiceTypeText": "增值税普通发票", "titleType": "COMPANY", "titleName": "北京某某科技有限公司", "taxNo": "91110108MA01XXXX2K", "amount": 12000.00, "email": "finance@example.com", "status": "REQUESTED", "statusText": "待开票", "requestedBy": "李定制", "requestedAt": "2026-09-15 10:23:45", "issuedAt": null, "issuedBy": null, "invoiceNo": null, "fileUrl": null, "pdfName": null, "pdfSize": null, "teamNo": "T20261001-003", "createTime": "2026-08-20 14:05:30", "orderStatus": "COMPLETED", "orderStatusText": "已完成", "flowStatus": "SETTLED", "flowStatusText": "已结算", "orderAmount": 12800.00, "paidAmount": 12800.00, "refundedAmount": 0.00, "settlementAmount": 9500.00 } ], "total": 1, "page": 1, "pageSize": 20, "stats": [ { "code": "REQUESTED", "name": "待开票", "count": 1, "amount": 12000.00 }, { "code": "ISSUED", "name": "已开票·待推送", "count": 3, "amount": 36000.00 }, { "code": "PUSHED", "name": "已推送", "count": 8, "amount": 88000.00 }, { "code": "CURRENT_MONTH_ISSUED", "name": "本月已开票", "count": 11, "amount": 124000.00 } ], "tabCounts": [ { "code": "REQUESTED", "name": "待开票", "count": 1 }, { "code": "ISSUED", "name": "待推送", "count": 3 }, { "code": "PUSHED", "name": "已推送", "count": 8 }, { "code": "NONE", "name": "未申请", "count": 5 }, { "code": "ALL", "name": "全部", "count": 17 } ] } } ``` ### 8.2 边界情况(NONE tab 行:无发票实体,订单侧字段照常回填) 请求: ``` GET /v3/admin/order/invoice/page?tab=NONE&page=1&pageSize=20 ``` 响应(records[] 单行): ```json { "id": null, "orderId": "1956000000000000002", "orderNo": "2026091200002", "productName": "阿尔山秋景 4 日游", "contactName": "王五", "invoiceType": null, "invoiceTypeText": null, "titleType": null, "titleName": null, "taxNo": null, "amount": null, "email": null, "status": "NONE", "statusText": "未申请", "requestedBy": null, "requestedAt": null, "teamNo": "T20261005-001", "createTime": "2026-09-01 09:12:00", "orderStatus": "COMPLETED", "orderStatusText": "已完成", "flowStatus": "COMPLETED", "flowStatusText": "已完成", "orderAmount": 6800.00, "paidAmount": 6800.00, "refundedAmount": 800.00, "settlementAmount": null } ``` 要点:`id`/发票侧字段为 null,`status` 固定 `NONE`,但**订单侧新字段全部照常回填**(未核单的订单 `settlementAmount` 可能为 null)。 ### 8.3 业务失败(参数校验失败) 请求: ``` GET /v3/admin/order/invoice/page?page=0&pageSize=200 ``` 响应: ```json { "code": 400, "msg": "页码最小为1", "data": null } ``` --- ## 九、业务边界 - **适用**:发票管理列表所有 tab(REQUESTED / ISSUED / PUSHED / NONE / ALL)均回填订单侧新字段;三种取数分支(发票 join 订单 / ALL 混合 / NONE 纯订单)共用同一填充逻辑,口径一致 - **不适用**:本接口不含订单侧更细字段(如出行人明细、支付流水),需要请走订单详情接口 - **容错口径(重要)**:`orderStatus` / `flowStatus` 码值为 null 或脏数据(无法映射枚举)时,对应 `orderStatusText` / `flowStatusText` 返回 **null**(后端降级不抛错,避免整页查询失败)。**前端对 null 文案需兜底**:显示码值原文或占位符(如「-」) - **金额口径**:`orderAmount` / `paidAmount` / `refundedAmount` / `settlementAmount` 单位均为元;净实收可由前端按 `paidAmount − refundedAmount` 计算 - `createTime` 为**订单创建时间**(不是发票申请时间 `requestedAt`),格式 yyyy-MM-dd HH:mm:ss --- ## 十、修改前后对比 ### 10.1 字段级对比(records[] 行元素) | 字段 | 修改前 | 修改后 | |------|--------|--------| | `teamNo` | 无 | 新增,String,团号 | | `createTime` | 无 | 新增,String,订单创建时间 | | `orderStatus` | 无 | 新增,String,订单状态码 | | `orderStatusText` | 无 | 新增,String,订单状态文案(可为 null) | | `flowStatus` | 无 | 新增,String,流程状态码 | | `flowStatusText` | 无 | 新增,String,流程状态文案(可为 null) | | `orderAmount` | 无 | 新增,Number,应收总额(元) | | `paidAmount` | 无 | 新增,Number,累计已收(元) | | `refundedAmount` | 无 | 新增,Number,累计已退(元) | | `settlementAmount` | 无 | 新增,Number,核单金额(元) | | 其余 26 个既有字段 | 不变 | 不变 | | 入参 7 个参数 | 不变 | 不变 | | `stats` / `tabCounts` / 分页元数据 | 不变 | 不变 | ### 10.2 行为级对比 | 场景 | 修改前 | 修改后 | |------|--------|--------| | 各 tab 列表行 | 仅发票侧字段 + 订单号/产品名等基础字段 | 追加团号、订单状态/流程状态(码+文案)、4 项金额、订单创建时间 | | NONE tab 行 | 只有订单号/产品名/客户名等基础字段 | 同样追加全部订单侧字段(三种取数分支共用同一填充逻辑) | --- ## 十一、影响评估 / 回滚 - **破坏兼容**:否。纯新增字段,JSON 响应多 10 个 key,不消费即无感 - **前端同步上线**:不要求。前端可独立排期接入新字段展示,先后端上前端不上无任何副作用 - **回滚方案**:后端回滚 PR #7899 对应 commit 即可;前端若已接入新字段,回滚后对应列取不到值需有兜底 --- ## 十二、注意事项 1. 既有 `id` / `orderId` 沿用字符串约定(Long + ToStringSerializer);**新增的 10 个字段无 Long 类型,不涉及 JS 精度问题** 2. `orderStatusText` / `flowStatusText` 可能为 null(脏数据容错),前端展示必须兜底 3. `settlementAmount` 未核单订单可能为 null,勿按必有值处理 4. 文案值由后端枚举 label 直出,前端**不要**自己维护码值→文案映射表(避免与后端枚举漂移) 5. 金额字段为 BigDecimal 序列化的 Number,展示格式化(千分位/两位小数)由前端处理 --- ## 十三、关联 / 联系人 - **Issue**: https://git.1814.love:8443/wx/HL/issues/7897 - **PR**: https://git.1814.love:8443/wx/HL/pulls/7899 - **Commit**: https://git.1814.love:8443/wx/HL/commit/e910acbfe965 - **后端负责人**: 腰苏图(yst)