# 发票推送客户 - 新增接口（管理后台）

> Issue: [#4230](https://git.1814.love:8443/wx/HL/issues/4230)
> PR: [#4238](https://git.1814.love:8443/wx/HL/pulls/4238)
> 日期: 2026-06-22
> 服务: hl-order-service-v3（invoice 域）
> 端类型: 管理后台

---

## 1. 接口背景

invoice 域开票完成后，财务需要将发票推送给客户。本次新增 PUT /v3/admin/order/invoice/{id}/push 接口，财务指定推送渠道（邮件 / 微信 / 短信）后调用，发票状态置为 PUSHED 并记录推送时间与渠道。

**注意**：本期推送仅记录状态与渠道，后端**暂不真正发送**邮件 / 微信 / 短信通知（通知网关后续单独接入）。前端可正常调用并据返回展示「已推送」，但客户实际不会收到通知，功能上线后再由后端对接通知网关。

---

## 2. 变更清单

| # | 接口 | 变更类型 | 说明 |
|---|------|----------|------|
| 1 | PUT /v3/admin/order/invoice/{id}/push | 新增接口 | 推送发票给客户（记录状态与渠道，暂不真发通知） |
---

## 3. 接口详情

**接口**：PUT /v3/admin/order/invoice/{id}/push
**功能描述**：推送已开票发票给客户，指定推送渠道。
**认证**：管理后台 JWT，Header 携带 Authorization: Bearer <token>。
**请求方式**：PUT，请求体 Content-Type: application/json。
**幂等性**：不保证幂等，多次调用=多次推送记录（PUSHED 状态再次调用视为「再次推送」）。
**限流**：网关全局限流，无接口级特殊限流。

---

## 4. 接口入参

### 4.1 路径参数 / Query 参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | Long | 是 | 发票 ID（路径参数） |

### 4.2 请求体字段

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| channels | Array<String> | 是 | 推送渠道列表，至少一个，合法值仅 email / wechat / sms，见 6 节枚举 |
---

## 5. 出参字段

**响应类型**：Result<Void>（data 为 null）

| 字段 | 类型 | 说明 |
|------|------|------|
| code | Integer | 200 表示推送成功 |
| data | null | 固定为 null |
| msg | String | 成功时为 null 或 success；失败时为错误描述 |

---

## 6. 枚举 / 数据字典

**推送渠道（channels 数组合法值）**

| 值 | 说明 |
|----|------|
| email | 电子邮件 |
| wechat | 微信通知 |
| sms | 短信 |

**InvoiceStatus 发票状态（推送前后）**

| 枚举值 | 中文名 | 说明 |
|--------|--------|------|
| REQUESTED | 待开票 | 不允许推送 |
| ISSUED | 已开票 | 允许推送，推送后变 PUSHED |
| PUSHED | 已推送 | 允许再次推送，状态维持 PUSHED |
| VOIDED | 已作废 | 不允许推送 |

---

## 7. 错误码

| 错误码 | 触发场景 |
|--------|----------|
| 581520 | 发票当前状态不允许推送（发票处于 REQUESTED 或 VOIDED 状态时调用） |
| 581521 | 推送渠道不能为空（channels 为 null 或空数组） |
| 581522 | 推送渠道非法（channels 中含 email / wechat / sms 以外的值） |
---

## 8. 示例

### 8.1 典型成功：ISSUED 状态首次推送（邮件 + 微信）

请求：



请求体：



响应：



调用后，发票状态由 ISSUED 变为 PUSHED，记录推送时间与渠道 [email, wechat]。

### 8.2 边界情况：PUSHED 状态再次推送（新增 sms 渠道）

发票已处于 PUSHED 状态，再次调用视为「再次推送」，状态维持 PUSHED，渠道记录更新为 [sms]。

请求：



请求体：



响应：



### 8.3 业务失败：发票处于 REQUESTED 状态（未开票）调用推送

请求：



请求体：



发票当前状态为 REQUESTED（待开票），响应：



追加：空 channels 触发 581521：



响应：


---

## 9. 业务边界

**适用：**
- 发票状态为 ISSUED（已开票）时可调用，推送后状态变为 PUSHED
- 发票状态为 PUSHED（已推送）时可再次调用，状态维持 PUSHED，记录最新推送渠道

**不适用：**
- 发票状态为 REQUESTED（待开票）时禁止推送，须先财务开票
- 发票状态为 VOIDED（已作废）时禁止推送

**特殊边界：**
- 本期推送为「记录意图」：调用成功仅更新状态 / 记录渠道，后端不真正发送邮件 / 微信 / 短信。通知网关对接为后续迭代。
- channels 可同时传多个渠道（如 [email, wechat, sms]），后端均记录，但通知未接通时均不发送。
- channels 中只要有一个非法值（如 [email, phone]），整个请求返回 581522 错误，不部分推送。

---

## 10. 修改前后对比

（本文件为新增接口，无修改前对比。）

---

## 11. 影响评估 / 回滚

（本文件为新增接口，无兼容性破坏。）

---

## 12. 注意事项

1. **推送不真发**：本期调用成功后客户实际收不到邮件 / 微信 / 短信。前端展示「已推送」状态，但需在 UI 说明或等通知网关接入后再对外宣传此功能。
2. channels 字段必须是字符串数组（即使只有一个渠道也要用数组形式：[email]），不能传字符串 email。
3. PUSHED 状态再次调用成功，可用于「重新推送」场景（如客户反馈未收到，财务再次操作）。
4. id 为发票 ID（Long 序列化字符串），非订单 ID；调用前需先通过发票列表接口获取发票 id。

---

## 13. 关联 / 联系人

- Issue（发票推送客户）: https://git.1814.love:8443/wx/HL/issues/4230
- PR（发票推送 #4238）: https://git.1814.love:8443/wx/HL/pulls/4238
- 后端负责人: yaosutu