hl-api-changelog/changelogs/2026-03/2026-03-25_invoice_full_api.md
API Changelog Bot 18ff6a008b docs: 发票模块完整API文档(管理端+小程序端)
包含全部10个接口的详细定义:请求参数、响应字段、校验规则、
调用示例、状态流转、关联字典、前端页面对接建议。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:50:01 +08:00

11 KiB

发票模块完整 API 文档(管理端 + 小程序端)

发票功能:用户对已支付订单申请开票 → 管理员审核开票 → 用户下载电子发票。支持换开最多3次


一、状态流转

用户申请 → PENDING待开票
              ├─ 管理员开票完成 → ISSUED已开票
              └─ 管理员标记失败 → FAILED开票失败→ 管理员重新开票 → ISSUED
ISSUED → 用户换开 → 原发票 VOIDED已作废+ 新发票 PENDING
ISSUED → 管理员作废 → VOIDED

二、关联字典

字典 dict_type 说明 枚举值
invoice_status 发票状态 PENDING=待开票, ISSUED=已开票, FAILED=开票失败, VOIDED=已作废
invoice_title_type 抬头类型 PERSONAL=个人, COMPANY=企业

三、通用响应结构 InvoiceVO

所有发票接口返回的数据结构(管理端直接返回 InvoiceVO,小程序端经 Feign 透传为 Map<String, Object>,字段相同):

字段 类型 说明
id String 发票ID
orderId String 订单ID
orderNo String 订单编号
amount String 发票金额(元)
titleType String 抬头类型:PERSONAL=个人 / COMPANY=企业(字典 invoice_title_type
invoiceTitle String 发票抬头(个人姓名或企业名称)
taxNumber String 纳税人识别号(企业发票有值)
email String 接收邮箱
invoiceType String 发票类型,固定为"电子普通发票"
invoiceContent String 发票内容,固定为"明细"
status String 发票状态(字典 invoice_status
fileUrl String 发票文件URL已开票后有值
failReason String 开票失败原因(失败时有值)
contactPhone String 联系电话
remark String 备注信息
bankName String 开户行(企业发票)
bankAccount String 银行账号(企业发票)
companyAddress String 企业地址(企业发票)
companyPhone String 企业电话(企业发票)
invoiceNo String 发票号码(已开票后有值)
issuedAt String 开票时间,格式 yyyy-MM-dd HH:mm:ss
issuedBy String 开票操作人管理员ID
userId String 申请用户ID
originalInvoiceId String 原发票ID换开时有值,指向被作废的原发票
reissueCount Integer 换开次数0~3
createdAt String 申请时间
updatedAt String 更新时间

四、小程序端接口C端用户

网关路径前缀:/mp/invoice 需要用户登录(携带 Authorization: Bearer token

4.1 申请开票

POST /mp/invoice/apply

使用场景:用户在订单详情页点击「申请发票」,填写开票信息后提交

请求参数 (JSON Body)

字段 类型 必填 说明
orderId String 订单ID
invoiceTitle String 发票抬头(个人姓名/企业名称,最长100字
titleType String 抬头类型,默认 PERSONAL。可选:PERSONAL=个人, COMPANY=企业
taxNumber String 企业必填 纳税人识别号,最长30位。titleType=COMPANY 时必填
email String 接收邮箱邮箱格式校验,最长100字
contactPhone String 联系电话,最长20位
remark String 备注信息,最长500字
bankName String 开户行企业发票选填,最长100字
bankAccount String 银行账号企业发票选填,最长30位
companyAddress String 企业地址企业发票选填,最长200字
companyPhone String 企业电话企业发票选填,最长20位

响应Result<InvoiceVO>(见第三节)

校验规则

  • 订单必须属于当前用户
  • 订单状态须为:已支付 / 已确认 / 待出发 / 旅行中 / 已完成 / 已付定金
  • 同一订单不可重复申请(已有待开票或已开票的发票时拒绝)
  • 开票金额自动取订单实付金额(paidAmount),如无则取总价(totalPrice
  • 发票类型固定「电子普通发票」,内容固定「明细」

调用示例

// 个人发票
{
  "orderId": "1902000000000001",
  "invoiceTitle": "张三",
  "titleType": "PERSONAL",
  "email": "zhangsan@example.com",
  "contactPhone": "13800138000"
}

// 企业发票
{
  "orderId": "1902000000000001",
  "invoiceTitle": "呼伦贝尔XX旅行社有限公司",
  "titleType": "COMPANY",
  "taxNumber": "91150000MA00000X00",
  "email": "finance@company.com",
  "bankName": "中国银行海拉尔支行",
  "bankAccount": "1234567890123456789",
  "companyAddress": "呼伦贝尔市海拉尔区XX路XX号",
  "companyPhone": "0470-1234567"
}

4.2 发票详情按发票ID

GET /mp/invoice/{id}

使用场景:从发票列表点进详情页

路径参数

参数 类型 说明
id Long 发票ID

响应Result<InvoiceVO>

校验规则:只能查看自己的发票


4.3 通过订单ID查询发票

GET /mp/invoice/order/{orderId}

使用场景:订单详情页判断是否已开票。返回 data=null 表示未开票,可显示「申请发票」按钮

路径参数

参数 类型 说明
orderId Long 订单ID

响应Result<InvoiceVO>(未开票时 datanull


4.4 发票换开

POST /mp/invoice/{invoiceId}/reissue

使用场景:已开票后发现抬头/税号填错,点击「换开发票」修改信息重新开具

路径参数

参数 类型 说明
invoiceId Long 原发票ID

请求参数 (JSON Body)

字段 类型 必填 说明
invoiceTitle String 新发票抬头,2~100字
titleType String 新抬头类型:PERSONAL / COMPANY
taxNumber String 企业必填 新纳税人识别号
email String 新接收邮箱

响应Result<InvoiceVO>(返回新创建的发票记录,原发票自动作废)

校验规则

  • 只能对状态为 ISSUED(已开票)的发票换开
  • 换开次数上限 3次reissueCount >= 3 时拒绝)
  • 企业抬头必须填税号
  • 换开后:原发票状态变为 VOIDED,新发票状态为 PENDINGoriginalInvoiceId 指向原发票

调用示例

{
  "invoiceTitle": "呼伦贝尔XX旅行社有限公司",
  "titleType": "COMPANY",
  "taxNumber": "91150000MA00000X00",
  "email": "finance@company.com"
}

五、管理端接口(后台管理员)

网关路径前缀:/admin/invoice 需要管理员登录(携带 Admin-Token

5.1 上传发票文件

POST /admin/invoice/upload

使用场景管理员在开票前先上传发票PDF/图片文件

请求参数 (form-data)

字段 类型 必填 说明
file File 发票文件,支持 PDF / JPG / JPEG / PNG,最大 10MB

响应

{
  "code": 200,
  "message": "success",
  "data": {
    "fileUrl": "https://oss.xxx.com/invoice/20260325/abc123.pdf"
  }
}

5.2 发票分页列表

GET /admin/invoice/page

使用场景:发票管理列表页,支持筛选

请求参数 (Query)

参数 类型 必填 默认值 说明
status String - 按状态筛选(字典 invoice_statusPENDING / ISSUED / FAILED / VOIDED
orderNo String - 按订单编号模糊搜索
userId Long - 按用户ID筛选
page int 1 页码最小1
pageSize int 20 每页条数1~100

响应

{
  "code": 200,
  "message": "success",
  "data": {
    "records": [ InvoiceVO, ... ],
    "total": 100,
    "current": 1,
    "size": 20
  }
}

5.3 发票详情

GET /admin/invoice/{id}

路径参数id — 发票ID

响应Result<InvoiceVO>


5.4 开票完成

PUT /admin/invoice/{id}/issue

使用场景:管理员线下开票完成后,上传发票文件并填写发票号码,标记开票完成

路径参数id — 发票ID

请求参数 (JSON Body)

字段 类型 必填 说明
fileUrl String 发票文件URL通过 5.1 上传接口获取)
invoiceNo String 发票号码12位数字

响应Result<InvoiceVO>

校验规则:只有 PENDING(待开票)或 FAILED(开票失败)状态的发票可操作

调用示例

{
  "fileUrl": "https://oss.xxx.com/invoice/20260325/abc123.pdf",
  "invoiceNo": "012345678901"
}

5.5 开票失败

PUT /admin/invoice/{id}/fail

使用场景:发票信息有误,管理员标记失败并填写原因,用户可修正后重新申请

路径参数id — 发票ID

请求参数 (JSON Body)

字段 类型 必填 说明
failReason String 失败原因

响应Result<InvoiceVO>

校验规则:只有 PENDING(待开票)状态的发票可操作

调用示例

{
  "failReason": "发票抬头与纳税人识别号不匹配,请核实后重新申请"
}

5.6 作废发票

PUT /admin/invoice/{id}/void

使用场景:管理员主动作废已开票的发票

路径参数id — 发票ID

请求参数:无

响应Result<InvoiceVO>

校验规则:已作废的发票不可重复作废


六、前端页面对接建议

小程序端页面

订单详情页

  • 调用 GET /mp/invoice/order/{orderId} 判断是否已开票
  • data=null → 显示「申请发票」按钮(订单状态需满足条件)
  • data.status=PENDING → 显示「开票中」
  • data.status=ISSUED → 显示「查看发票」+「换开发票」按钮
  • data.status=FAILED → 显示失败原因 + 「重新申请」按钮
  • data.status=VOIDED → 显示「已作废」

申请开票页

  • 默认「个人」抬头,切换「企业」后展开额外字段(税号必填 + 开户行/银行账号/地址/电话选填)
  • 金额自动显示(不可编辑,由后端计算)

发票详情页

  • 显示全部字段信息
  • status=ISSUED 时显示发票文件下载链接(fileUrl)和发票号码(invoiceNo
  • status=ISSUEDreissueCount < 3 时显示「换开」按钮

管理端页面

发票列表页

  • 顶部筛选栏:状态下拉(全部/待开票/已开票/开票失败/已作废)+ 订单号搜索框
  • 列表列:订单号、用户、金额、抬头、状态、申请时间、操作
  • 操作按钮:
    • PENDING → 「开票」「标记失败」
    • FAILED → 「重新开票」
    • ISSUED → 「作废」
    • VOIDED → 无操作

开票操作

  1. 先调 POST /admin/invoice/upload 上传发票文件 → 拿到 fileUrl
  2. 再调 PUT /admin/invoice/{id}/issue 传入 fileUrl + invoiceNo