From 18ff6a008b0a75a5a81dcc5e11f09c58f587a5c2 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Wed, 25 Mar 2026 09:50:01 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=8F=91=E7=A5=A8=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=AE=8C=E6=95=B4API=E6=96=87=E6=A1=A3=EF=BC=88=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=AB=AF+=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=AB=AF?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 包含全部10个接口的详细定义:请求参数、响应字段、校验规则、 调用示例、状态流转、关联字典、前端页面对接建议。 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../2026-03/2026-03-25_invoice_full_api.md | 396 ++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 changelogs/2026-03/2026-03-25_invoice_full_api.md diff --git a/changelogs/2026-03/2026-03-25_invoice_full_api.md b/changelogs/2026-03/2026-03-25_invoice_full_api.md new file mode 100644 index 0000000..7dae05c --- /dev/null +++ b/changelogs/2026-03/2026-03-25_invoice_full_api.md @@ -0,0 +1,396 @@ +# 发票模块完整 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`,字段相同): + +| 字段 | 类型 | 说明 | +|---|---|---| +| `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`(见第三节) + +**校验规则**: +- 订单必须属于当前用户 +- 订单状态须为:已支付 / 已确认 / 待出发 / 旅行中 / 已完成 / 已付定金 +- 同一订单不可重复申请(已有待开票或已开票的发票时拒绝) +- 开票金额自动取订单实付金额(`paidAmount`),如无则取总价(`totalPrice`) +- 发票类型固定「电子普通发票」,内容固定「明细」 + +**调用示例**: +```json +// 个人发票 +{ + "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` + +**校验规则**:只能查看自己的发票 + +--- + +### 4.3 通过订单ID查询发票 + +``` +GET /mp/invoice/order/{orderId} +``` + +**使用场景**:订单详情页判断是否已开票。返回 `data=null` 表示未开票,可显示「申请发票」按钮 + +**路径参数**: + +| 参数 | 类型 | 说明 | +|---|---|---| +| `orderId` | Long | 订单ID | + +**响应**:`Result`(未开票时 `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`(返回新创建的发票记录,原发票自动作废) + +**校验规则**: +- 只能对状态为 `ISSUED`(已开票)的发票换开 +- 换开次数上限 **3次**(reissueCount >= 3 时拒绝) +- 企业抬头必须填税号 +- 换开后:原发票状态变为 `VOIDED`,新发票状态为 `PENDING`,`originalInvoiceId` 指向原发票 + +**调用示例**: +```json +{ + "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 | + +**响应**: +```json +{ + "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) | + +**响应**: +```json +{ + "code": 200, + "message": "success", + "data": { + "records": [ InvoiceVO, ... ], + "total": 100, + "current": 1, + "size": 20 + } +} +``` + +--- + +### 5.3 发票详情 + +``` +GET /admin/invoice/{id} +``` + +**路径参数**:`id` — 发票ID + +**响应**:`Result` + +--- + +### 5.4 开票完成 + +``` +PUT /admin/invoice/{id}/issue +``` + +**使用场景**:管理员线下开票完成后,上传发票文件并填写发票号码,标记开票完成 + +**路径参数**:`id` — 发票ID + +**请求参数** (JSON Body): + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `fileUrl` | String | ✅ | 发票文件URL(通过 5.1 上传接口获取) | +| `invoiceNo` | String | ✅ | 发票号码(12位数字) | + +**响应**:`Result` + +**校验规则**:只有 `PENDING`(待开票)或 `FAILED`(开票失败)状态的发票可操作 + +**调用示例**: +```json +{ + "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` + +**校验规则**:只有 `PENDING`(待开票)状态的发票可操作 + +**调用示例**: +```json +{ + "failReason": "发票抬头与纳税人识别号不匹配,请核实后重新申请" +} +``` + +--- + +### 5.6 作废发票 + +``` +PUT /admin/invoice/{id}/void +``` + +**使用场景**:管理员主动作废已开票的发票 + +**路径参数**:`id` — 发票ID + +**请求参数**:无 + +**响应**:`Result` + +**校验规则**:已作废的发票不可重复作废 + +--- + +## 六、前端页面对接建议 + +### 小程序端页面 + +**订单详情页**: +- 调用 `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` → 无操作 + +**开票操作**: +1. 先调 `POST /admin/invoice/upload` 上传发票文件 → 拿到 `fileUrl` +2. 再调 `PUT /admin/invoice/{id}/issue` 传入 `fileUrl` + `invoiceNo`