# 区块链发票 → 航信电子发票 迁移文档

## 一、背景

深圳税局已停止微信区块链电子发票接口服务（即系统原有的 `new-tax-control-fapiao` 业务），现有发票功能将全部切换至航信（票慧通）乐企版 v5.1 电子发票平台。

**替换范围**：所有发票开具、查询、下载、状态跟踪功能。

**不受影响**：微信支付、退款、订单管理、充电业务等。

---

## 二、新旧流程对比

### 2.1 开票流程

```
旧流程（微信区块链发票）：
  用户选订单 → 获取微信抬头填写URL → 用户在微信小程序填抬头
  → 微信回调通知 → 管理员审核开票 → 微信开具 → 微信回调 → 下载

新流程（航信电子发票）：
  用户选订单 + 填写抬头信息 → 管理员审核 → 航信开具
  → 系统轮询结果 → 自动发邮件 / 下载
```

### 2.2 关键差异

| 事项 | 旧（微信） | 新（航信） |
|------|-----------|-----------|
| 抬头收集 | 用户跳转微信小程序填写 → 异步回调 | 用户申请时在 App 内直接填写 |
| 开票触发 | 调用微信 `fapiao-applications` API | 调用航信 `openBlueInvoice` API |
| 结果获取 | 微信主动回调 `/invoice/notify` | 系统每 5 分钟轮询航信查询 |
| 发票文件 | 微信临时链接（30 秒有效） | 永久 PDF / OFD / XML 链接 |
| 邮件发送 | 不支持 | 开票成功后自动发送到用户邮箱 |
| 发票红冲 | 微信 API | 航信 `openRedInvoice` API |

### 2.3 API 端点变化

| 端点 | 状态 | 说明 |
|------|------|------|
| `POST /invoice/applyInvoice` | 变更 | 返回值从 TitleUrl 改为 Invoice 对象 |
| `POST /invoice/notify` | 移除 | 航信为轮询模式，不再需要回调 |
| `POST /invoice/titleWriteNotice` | 移除 | 抬头信息在申请时直接提交 |
| `GET /invoice/downloadInvoice/{id}` | 保留 | 改为返回航信 PDF/OFD/XML URL |
| `GET /invoice/list` | 不变 | 用户发票列表 |
| `GET /finance/handleInvoice/{id}` | 变更 | 后端改为调用航信开票 |
| `GET /finance/downloadInvoice/{id}` | 变更 | 改为返回航信下载链接 |
| `GET /finance/getUserTitle/{applyId}` | 移除 | 航信无此概念 |
| `GET /finance/getInvoice/{applyId}` | 变更 | 改为查询本地 InvoiceDetail 表 |
| `GET /finance/listInvoice` | 不变 | 管理端发票列表 |
| `GET /finance/export/invoiceList` | 不变 | 发票导出 |
| `GET /finance/confirmManualInvoice/{id}` | 不变 | 手动确认开票 |

---

## 三、配置说明

### 3.1 新增配置

在 `application.yml` 中添加（admin 和 miniapp 模块均需配置）：

```yaml
huapiaoer:
  app-secret: 票慧通提供的appsecret
  nsrsbh: 开票方税号（商家税号）
  b2cshopid: 商家税号（同 nsrsbh）
```

### 3.2 保留的旧配置

`wechat.fapiao.*` 配置暂保留但不再使用，后续可清理。

---

## 四、数据库变更

执行 `database/7.sql`：

```sql
-- 发票详情增加下载链接字段
ALTER TABLE charge_admin.t_invoice_detail
  ADD COLUMN IF NOT EXISTS invoice_url VARCHAR(512) COMMENT '发票PDF下载地址',
  ADD COLUMN IF NOT EXISTS ofd_url VARCHAR(512) COMMENT '发票OFD下载地址',
  ADD COLUMN IF NOT EXISTS xml_url VARCHAR(512) COMMENT '发票XML下载地址';

-- 发票抬头管理：增加默认抬头字段
ALTER TABLE charge_app.t_invoice_title
  ADD COLUMN IF NOT EXISTS is_default TINYINT(1) DEFAULT 0 COMMENT '是否默认抬头';
```

- `t_invoice` 表结构不变，数据兼容
- 历史发票记录的 `t_invoice_detail` 数据不受影响

---

## 五、文件变更清单

### 5.1 新增文件

| 文件 | 说明 |
|------|------|
| `common/.../config/HuapiaoerProperties.java` | 航信配置属性类 |
| `service/.../miniapp/HuapiaoerInvoiceService.java` | 航信开票业务封装 |

### 5.2 修改文件

| 文件 | 变更摘要 |
|------|----------|
| `service/pom.xml` | 新增 `huapiaoer-sdk:5.1.0` 依赖 |
| `admin/.../application-dev.yml` | 新增 `huapiaoer` 配置块 |
| `admin/.../application-prod.yml` | 新增 `huapiaoer` 配置块 |
| `miniapp/.../application-dev.yml` | 新增 `huapiaoer` 配置块 |
| `miniapp/.../application-prod.yml` | 新增 `huapiaoer` 配置块 |
| `entity/.../InvoiceDetail.java` | 新增 `invoiceUrl/ofdUrl/xmlUrl` 字段 |
| `service/.../InvoiceService.java` | `applyInvoice()` 返回值改为 `Invoice` |
| `service/.../InvoiceServiceImpl.java` | 申请时直接保存抬头信息 |
| `service/.../InvoiceDetailService.java` | 移除微信 `updateInvoiceDetail` 方法 |
| `service/.../InvoiceDetailServiceImpl.java` | 移除微信发票详情更新逻辑 |
| `service/.../WxPayService.java` | 移除全部 10 个发票方法声明 |
| `service/.../WxPayServiceImpl.java` | 移除全部发票实现及依赖注入 |
| `miniapp/.../InvoiceController.java` | 简化接口，移除回调端点 |
| `admin/.../FinanceController.java` | 改为调用航信服务 |
| `admin/.../InvoiceStatusJob.java` | 轮询航信替代微信，频率 5 分钟 |
| `miniapp/.../SaTokenConfigure.java` | 移除 `/invoice/notify` 等免认证路径 |

---

## 六、部署步骤

### 6.1 部署前准备

1. 向票慧通申请 `appSecret`、确认商家税号（`nsrsbh` / `b2cshopid`）
2. 执行数据库 DDL：
   ```sql
   ALTER TABLE charge_admin.t_invoice_detail
     ADD COLUMN invoice_url VARCHAR(512) COMMENT '发票PDF下载地址',
     ADD COLUMN ofd_url VARCHAR(512) COMMENT '发票OFD下载地址',
     ADD COLUMN xml_url VARCHAR(512) COMMENT '发票XML下载地址';
   ```

### 6.2 配置文件

1. 在 4 个 `application-*.yml` 中填写 `huapiaoer.app-secret`、`huapiaoer.nsrsbh`、`huapiaoer.b2cshopid`
2. 生产环境使用真实的航信正式环境 `https://erp.huapiaoer.com`（默认值，无需额外配置）

### 6.3 构建与发布

```bash
# 1. 安装 huapiaoer-sdk 到本地
mvn install -pl huapiaoer-sdk -DskipTests

# 2. 编译全部模块
mvn compile -pl huapiaoer-sdk,entity,common,service,admin,miniapp

# 3. 打包部署
mvn package -DskipTests
```

### 6.4 部署后验证

1. **用户申请开票**：在 App 中选择订单申请开票，确认可以直接提交抬头信息
2. **管理端开票**：在后台审核并点击"开具发票"，确认调用航信成功
3. **状态轮询**：等待 5 分钟（或手动触发定时任务），确认发票状态更新为"已开票"
4. **下载发票**：确认可以获取 PDF/OFD/XML 下载链接
5. **邮件发送**：确认填写了邮箱的发票自动发送邮件

---

## 七、回滚方案

如需回退到旧系统（如微信接口恢复）：

1. 恢复部署旧版本代码
2. 数据库无需回滚（新字段不影响旧逻辑）

---

## 八、发票抬头管理（新增功能）

替换微信的抬头管理能力，改为充电桩系统自行管理。

### API 端点

| 端点 | 方法 | 说明 |
|------|------|------|
| `GET /invoice/title/list` | GET | 当前用户的抬头列表（默认抬头排前） |
| `GET /invoice/title/{id}` | GET | 查看单个抬头详情 |
| `POST /invoice/title` | POST | 新增抬头 |
| `PUT /invoice/title/{id}` | PUT | 修改抬头 |
| `DELETE /invoice/title/{id}` | DELETE | 删除抬头 |
| `PUT /invoice/title/{id}/default` | PUT | 设为默认抬头 |

### 自动化

- 用户申请开票时，填写的抬头信息自动保存到抬头表（去重）
- 用户下次申请开票时可直接从保存的抬头中选择

---

## 九、定时任务

| 任务 | 旧频率 | 新频率 | 说明 |
|------|--------|--------|------|
| `InvoiceStatusJob` | 每天 18:00 | 每 5 分钟 | 轮询航信查询开票结果，更新状态并自动发送邮件 |

---

## 九、异常处理

1. **航信 API 返回失败**（`code != "200"`）：抛出 `HuapiaoerException`，前端提示错误信息
2. **网络异常**：抛出 `HuapiaoerException`（code=500），定时任务会在下一轮重新处理
3. **定时任务轮询失败**：记录错误日志，下一轮（5 分钟后）继续重试，直到成功
4. **邮件发送失败**：仅记录日志，不影响开票结果
