包含全部10个接口的详细定义:请求参数、响应字段、校验规则、 调用示例、状态流转、关联字典、前端页面对接建议。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>(未开票时 data 为 null)
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,新发票状态为PENDING,originalInvoiceId指向原发票
调用示例:
{
"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_status):PENDING / 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=ISSUED且reissueCount < 3时显示「换开」按钮
管理端页面
发票列表页:
- 顶部筛选栏:状态下拉(全部/待开票/已开票/开票失败/已作废)+ 订单号搜索框
- 列表列:订单号、用户、金额、抬头、状态、申请时间、操作
- 操作按钮:
PENDING→ 「开票」「标记失败」FAILED→ 「重新开票」ISSUED→ 「作废」VOIDED→ 无操作
开票操作:
- 先调
POST /admin/invoice/upload上传发票文件 → 拿到fileUrl - 再调
PUT /admin/invoice/{id}/issue传入fileUrl+invoiceNo