# 发票申请入参票种修正(最终契约) > 变更类型:修改接口(破坏性) > 端类型:管理后台 > 日期:2026-06-23 | Issue:#4290 | PR:#4292 / #4302 | 服务:hl-order-service-v3 > ⚠️ **破坏性变更**:`invoiceType` 删除 `ELECTRONIC`、`email` 改为始终必填 + 格式校验、新增 `taxNo`/银行/注册信息专票五项校验、删除 `mailAddress` 字段、专票限 `COMPANY` 抬头。前端申请表单需同步改动,上线时前后端须同步发布。 > **勘误(截至 PR #4311 收口)**:抬头字段统一 `titleName`、金额 `amount` 改 JSON number(单位元,非分/×100)、票种仅 2 值(`VAT_NORMAL`/`VAT_SPECIAL`)、专票限公司抬头、入参和出参均无 `mailAddress`。关联 Issue [#4290](https://git.1814.love:8443/wx/HL/issues/4290) [#4296](https://git.1814.love:8443/wx/HL/issues/4296) [#4310](https://git.1814.love:8443/wx/HL/issues/4310) / PR [#4292](https://git.1814.love:8443/wx/HL/pulls/4292) [#4302](https://git.1814.love:8443/wx/HL/pulls/4302) [#4311](https://git.1814.love:8443/wx/HL/pulls/4311)。 > **勘误(截至 PR #4311 收口)**:抬头字段统一 `titleName`、金额 `amount` 改 JSON number(单位元,非分/x100)、票种仅 2 值(`VAT_NORMAL`/`VAT_SPECIAL`)、专票限公司抬头、入参和出参均无 `mailAddress`。关联 Issue [#4290](https://git.1814.love:8443/wx/HL/issues/4290) [#4296](https://git.1814.love:8443/wx/HL/issues/4296) [#4310](https://git.1814.love:8443/wx/HL/issues/4310) / PR [#4292](https://git.1814.love:8443/wx/HL/pulls/4292) [#4302](https://git.1814.love:8443/wx/HL/pulls/4302) [#4311](https://git.1814.love:8443/wx/HL/pulls/4311)。 --- ## 接口背景 发票模型经 PR #4292 + #4302 多轮修正后定稿:删除原设计中的「电子发票」票种(业务决策统一走 VAT_NORMAL 普票),对专票收严字段校验(专票限公司抬头 + 必填五项),email 改为始终必填(全系统电子交付),删除纸质邮寄字段 `mailAddress`。 本文档为最终契约,与本月早前 `23_4172_...` 系列文件中的历史设计存在差异,以本文档为准。 --- ## 变更清单 | # | 变更类型 | 说明 | |---|---------|------| | 1 | ⚠️ 删枚举值 | `invoiceType` 删除 `ELECTRONIC`,只保留 `VAT_NORMAL` / `VAT_SPECIAL` | | 2 | ⚠️ 删字段 | 申请入参 + 详情出参均删除 `mailAddress`(纸质邮寄地址) | | 3 | ⚠️ 校验收严 | `email` 改为始终必填(原为条件必填),并加邮箱格式校验 | | 4 | ⚠️ 新增校验 | `taxNo`:单位抬头或专票时必填(原仅"公司抬头"须填) | | 5 | ⚠️ 新增校验 | `bankName` / `bankAccount` / `registAddress` / `registPhone`:专票必填(原无此四项限制) | | 6 | ⚠️ 新增错误码 | `581523` 专票只能开给单位(`titleType` 须 `COMPANY`) | | 7 | ✨ 行为明确 | `VAT_SPECIAL` 自动限 `COMPANY` 抬头,个人选专票被拒(581523) | --- ## 接口详情 ### 管理后台申请接口 | 项 | 说明 | |---|------| | **方法 + 路径** | `POST /v3/admin/order/{orderId}/invoice/apply` | | **接口名** | 管理后台代客申请发票 | | **描述** | 财务或定制师代客提交开票申请,支持普票/专票,全电子交付 | | **认证** | 管理后台 JWT(Bearer Token) | | **幂等性** | 非幂等,重复提交触发 581511(一单一票) | | **限流** | 无特殊限流 | --- ## 接口入参 ### 路径参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | `orderId` | string(Long 雪花 ID) | 是 | 订单 ID,字符串传输防精度丢失 | ### 请求体字段 | 字段名 | 类型 | 必填条件 | 说明 | |--------|------|---------|------| | `invoiceType` | string | 始终必填 | 发票类型,只有 `VAT_NORMAL` / `VAT_SPECIAL` 两值,**不含 ELECTRONIC** | | `titleType` | string | 始终必填 | 抬头类型:`COMPANY`(单位)/ `PERSONAL`(个人);**VAT_SPECIAL 只能 COMPANY** | | `titleName` | string | 始终必填 | 发票抬头(企业全称或个人姓名) | | `taxNo` | string | `titleType=COMPANY` 或 `invoiceType=VAT_SPECIAL` 时必填 | 纳税人识别号 | | `bankName` | string | `invoiceType=VAT_SPECIAL` 时必填 | 开户银行名称 | | `bankAccount` | string | `invoiceType=VAT_SPECIAL` 时必填 | 银行账号 | | `registAddress` | string | `invoiceType=VAT_SPECIAL` 时必填 | 注册地址 | | `registPhone` | string | `invoiceType=VAT_SPECIAL` 时必填 | 注册电话 | | `amount` | number | 始终必填 | 开票金额,JSON number,单位**元**(如 `986.00`),须 > 0 且 ≤ 订单总金额 | | `email` | string | 始终必填 | 收件邮箱,全电子交付,须通过邮箱格式校验 | | `remark` | string | 选填 | 申请备注 | | `applyReason` | string | 选填 | 申请原因(与 remark 二选一或并用) | > ⚠️ `mailAddress` 字段**已删除**,后端不接收,填了也忽略。 --- ## 出参字段 返回结构:`Result` — 返回新建发票 ID(字符串,雪花 ID)。 | 字段名 | 类型 | 说明 | |--------|------|------| | `data` | string | 新建发票 ID,如 `"1934567890123456789"` | --- ## 枚举 / 数据字典 ### 发票类型(invoiceType) | 枚举值 | 中文名 | 校验规则 | |--------|--------|---------| | `VAT_NORMAL` | 增值税普通发票 | bankName / bankAccount / registAddress / registPhone 均不需要 | | `VAT_SPECIAL` | 增值税专用发票 | taxNo + 银行四项均必填,且 titleType 必须 COMPANY | > ~~`ELECTRONIC`~~ 已删除,禁止传入(传入返回 581513)。 ### 抬头类型(titleType) | 枚举值 | 中文名 | 限制 | |--------|--------|------| | `COMPANY` | 单位 | 普票/专票均可 | | `PERSONAL` | 个人 | 只能选 `VAT_NORMAL`,选 `VAT_SPECIAL` 返回 581523 | --- ## 错误码 | 错误码 | 含义 | 触发场景 | |--------|------|---------| | `581510` | 订单未完成,不可开票 | 订单状态不是 `COMPLETED` | | `581511` | 一单一票,已有有效发票 | 该订单已存在 REQUESTED / ISSUED / PUSHED 状态发票 | | `581512` | 开票金额超订单总额 | `amount` > 订单 `orderAmount` | | `581513` | 发票类型非法 | `invoiceType` 不是 `VAT_NORMAL` 或 `VAT_SPECIAL` | | `581514` | 公司抬头或专票须填税号 | `titleType=COMPANY` 或 `invoiceType=VAT_SPECIAL` 时 `taxNo` 为空 | | `581515` | 专票须填银行及注册信息 | `invoiceType=VAT_SPECIAL` 时 taxNo / bankName / bankAccount / registAddress / registPhone 任一为空 | | `581516` | 收件邮箱不能为空 | 入参层 `@NotBlank`,返回 HTTP 400「收件邮箱不能为空」;格式错误返回 400「邮箱格式不正确」 | | `581523` | 专票只能开给单位 | `invoiceType=VAT_SPECIAL` 且 `titleType=PERSONAL` | | `401` | 未授权 | 未携带有效 JWT | | `403` | 无权限 | 当前账号无发票申请权限 | > 注意:581516 在入参校验层(`@NotBlank` / `@Email`)返回 HTTP 400,不走业务错误码;581513~581523 走 `BusinessException` 返回 200 包体。 --- ## 示例 ### 典型成功(增值税普通发票,个人抬头) 请求: ``` POST /v3/admin/order/1920000000000000001/invoice/apply Authorization: Bearer Content-Type: application/json ``` ```json { "invoiceType": "VAT_NORMAL", "titleType": "PERSONAL", "titleName": "李四", "taxNo": null, "amount": 986.00, "email": "lisi@example.com", "remark": "报销使用" } ``` 响应: ```json { "code": 200, "msg": "success", "data": "1934567890123456789" } ``` ### 典型成功(增值税专用发票,公司抬头,全字段) 请求: ``` POST /v3/admin/order/1920000000000000002/invoice/apply Authorization: Bearer Content-Type: application/json ``` ```json { "invoiceType": "VAT_SPECIAL", "titleType": "COMPANY", "titleName": "某某贸易有限公司", "taxNo": "91310000YYYYYYYYYY", "bankName": "工商银行上海支行", "bankAccount": "6222000000000001", "registAddress": "上海市浦东新区XX路XX号", "registPhone": "021-12345678", "amount": 1500.00, "email": "finance@company.com", "remark": null } ``` 响应: ```json { "code": 200, "msg": "success", "data": "1934567890123456790" } ``` ### 业务失败(专票个人抬头被拒,错误码 581523) 请求: ``` POST /v3/admin/order/1920000000000000003/invoice/apply Authorization: Bearer Content-Type: application/json ``` ```json { "invoiceType": "VAT_SPECIAL", "titleType": "PERSONAL", "titleName": "张三", "taxNo": null, "amount": 500.00, "email": "zhangsan@example.com" } ``` 响应: ```json { "code": 581523, "msg": "增值税专用发票只能开给单位,个人抬头不可选专票", "data": null } ``` --- ## 业务边界 **适用场景** - 财务在「发票管理 - 未申请 tab」找到已完成订单,代客提交开票申请 - 定制师在订单详情页为客户发起申请 **不适用场景** - 重新开票(走 `POST /v3/admin/order/invoice/{id}/reissue` 重开接口,入参相同) - 小程序客户自主申请(走小程序端专属接口 `POST /v3/internal/mp/order/{orderId}/invoice/apply`) **特殊边界** - 一单一票:同一订单只允许一张有效发票(REQUESTED / ISSUED / PUSHED 状态),若已有则返回 581511,须先作废旧发票再申请 - 订单状态:仅 `COMPLETED`(已完成)状态订单可申请,其他状态返回 581510 - 金额单位:`amount` 为 JSON number,单位**元**(如 `986.00`),>0,≤ 订单总金额;前端输入框以元为单位直接提交,无需 ×100 --- ## 修改前后对比 ### 发票类型(invoiceType 枚举) | 旧值(历史设计) | 新值(最终实现) | |--------|--------| | `VAT_NORMAL` 增值税普通发票 | `VAT_NORMAL` 增值税普通发票(保留) | | `VAT_SPECIAL` 增值税专用发票 | `VAT_SPECIAL` 增值税专用发票(保留) | | `ELECTRONIC` 电子发票 | **已删除,禁止传入** | ### email 字段 | 旧行为 | 新行为 | |--------|--------| | 条件必填(电子发票时必填,普票/专票选填) | **始终必填** + 邮箱格式校验(全电子交付) | ### mailAddress 字段 | 旧行为 | 新行为 | |--------|--------| | 纸质发票邮寄地址(选填) | **已删除**(无纸质邮寄,全电子交付) | ### 专票校验 | 旧行为 | 新行为 | |--------|--------| | 无 titleType 限制 | **专票只能 COMPANY**(个人返回 581523) | | bankName 等四项无强制 | **专票 bankName / bankAccount / registAddress / registPhone 全部必填**(返回 581515) | --- ## 影响评估 / 回滚 ### 破坏兼容性评估(前端申请表单必须改动) | 模块 | 必须改动 | |------|---------| | 发票类型下拉 | 去掉「电子发票」选项,只保留「增值税普通发票」/「增值税专用发票」 | | 专票表单区块 | 新增开户行、银行账号、注册地址、注册电话 4 个必填输入框,仅在选专票时显示 | | 抬头类型联动 | 选专票时,抬头类型只能选「单位」,隐藏或禁用「个人」选项 | | email 字段 | 改为始终必填,加邮箱格式校验(正则或 HTML input type=email) | | mailAddress 字段 | 移除,不再向后端传此字段 | ### 回滚方案 后端回滚至 #4292 前版本后,`ELECTRONIC` 枚举值重新有效,`email` 恢复条件必填,`mailAddress` 重新可用。前端需同步回滚或做前向兼容处理。 --- ## 注意事项 1. `ELECTRONIC` 枚举值已删除,前端下拉不可出现此选项;老订单历史数据如有该枚举值,出参展示时可显示「未知类型」或联系后端处理 2. `email` 无论票种均必填,前端不可根据票种做条件隐藏 3. 专票表单区块(税号 + 开户行 + 账号 + 注册地址 + 注册电话)共 5 个字段,在选专票时全部必填,不可缺任何一项 4. `amount` 为 JSON number,单位**元**(如 `986.00`),前端输入框直接传元值,无需 ×100;后端已按元存储和校验 --- ## 关联 / 联系人 | 项 | 内容 | |----|------| | **Issue(功能)** | [#4290 发票申请票种修正](https://git.1814.love:8443/wx/HL/issues/4290) | | **Issue(小程序)** | [#4296 小程序发票申请同步修正](https://git.1814.love:8443/wx/HL/issues/4296) | | **PR(票种删 ELECTRONIC)** | [#4292](https://git.1814.love:8443/wx/HL/pulls/4292) | | **PR(专票限公司/email 必填/删 mailAddress)** | [#4302](https://git.1814.love:8443/wx/HL/pulls/4302) | | **后端负责人** | yst |