文件
hl-api-changelog/changelogs-v2/2026-10/04_8755_发票推送接短信推送日志新增SKIP-修改接口-管理后台.md
T
Mimingguang b4f2e3da14
changelog-filename-gate / validate (push) Failing after 1s
docs(changelog): #8755 回写前端 implemented(mmg,ref bfe3c4537)
2026-10-04 16:07:12 +08:00

450 行
17 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
schema: "hl-changelog/v2"
ticket: "8755"
title: "发票推送接短信(按下单手机号直发):推送日志按真实结果落库,pushStatus 新增 SKIP「未发送」,邮件 / 微信不再假写成功"
consumer: "admin"
author: "jw(GIT)"
change_type: "修改接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "mmg"
frontend_ref: "bfe3c4537a718769af4f8f99dbe0fa24e0f763f0"
target_release: "v2.1"
verified_at: "2026-10-04"
status_note: "PUT /v3/admin/order/invoice/{id}/push 入参 / 出参 / 错误码不变,行为变:选 sms 时经通知中心按订单下单手机号直发「发票已开具」短信(带 8 位下载短码),推送日志按通知中心真实结果写 SUCCESS / FAILED / SKIP / PENDING;email / wechat 未接入,一律写 SKIP。GET /{id}/push-logs 与 GET /{id} 的 pushLogs[].pushStatus 新增取值 SKIP(statusText「未发送」),failReason 如实写原因。短信模板尚在申请(#8790,wx),模板到位前短信渠道写 SKIP「短信模板未配置,未发送」。已合并 dev-v3(9cd23903a)并部署 TEST,经网关实测,工单 #8755 已关。前端待做两处:推送弹窗 PushModal.vue:15 文案「本期仅记录推送状态,客户暂不会实际收到通知」改为如实说明(短信按下单手机号发送、邮件 / 微信暂不发送);推送日志抽屉 SKIP 行的原因前缀「失败原因:」改为「原因:」,可选给 SKIP 配一个标签色。statusText 已能自动显示「未发送」。前端已交付(2026-10-04):PushModal 文案改如实说明(短信按下单手机号发送/模板生效后,邮件微信暂不发送);PushLogsDrawer 前缀改「原因:」且仅 FAILED 染红(SKIP 走灰 tag+次级色原因行);invoice.js JSDoc 补 SKIP 口径,提交 bfe3c4537。"
updated_at: "2026-10-04"
base: "dev-v3"
---
# 发票推送接短信:推送日志按真实结果落库、新增 SKIP(管理后台)
> **服务**: hl-order-service-v3(端口 8086/8186)+ hl-user-service(通知事件配置迁移)
> **PR**: #8776
> **Issue**: #8755
> **日期**: 2026-10-03
> **影响范围**: 管理后台财务「发票管理」页的推送弹窗、推送记录抽屉、发票详情抽屉;接口签名零变化
---
## ⚠️ 关键变化
1. **推送不再是假推送**:选「短信」时经通知中心按**订单下单手机号**直发(不依赖小程序 userId,团期子订单也能收到),短信里带 8 位下载短码,客户免登录下载发票(配套新增接口见同单另一份 changelog)。
2. **推送日志如实落库**:`pushStatus` 新增 **`SKIP`**(`statusText`「未发送」),`failReason` 写清原因;原先三个渠道一律写 `SUCCESS` 的口径作废。
3. **邮件 / 微信未接入**:选了照样能推(状态机不变),但日志写 `SKIP`「邮件渠道未接入,未发送」/「微信渠道未接入,未发送」。
4. **短信模板未到位**(#8790):模板过审填入前,短信渠道日志写 `SKIP`「短信模板未配置,未发送」,客户收不到短信。
5. **短信结果是异步回写的**:推送接口返回时短信那行先是 `PENDING`,通常 1 秒内回写为终态;推送记录抽屉打开时再拉一次即可。
---
## 一、背景
#4230 曾定「本期不真发」:三个推送渠道监听器只登记一条 `SUCCESS`,前端弹窗也提示「客户暂不会实际收到通知」。jw 2026-10-03 定客户触达一律短信、按下单手机号直发、模板后补,于是本单接通短信并把推送日志改为如实落库。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 推送发票给客户 | PUT | `/v3/admin/order/invoice/{id}/push` | 修改接口 | 入参出参不变;sms 真发短信,各渠道日志按真实结果落库 |
| 2 | 发票推送明细日志 | GET | `/v3/admin/order/invoice/{id}/push-logs` | 修改接口 | `pushStatus` 新增 `SKIP`,`failReason` 如实写原因 |
| 3 | 发票详情 | GET | `/v3/admin/order/invoice/{id}` | 修改接口 | 仅 `pushLogs[]` 同上变化,其余字段不变 |
网关:本组接口无改动(在既有 `/v3/admin/**` 通配下)。
---
## 三、接口详情
### 1. 推送发票给客户 `PUT /v3/admin/order/invoice/{id}/push`
**VO**: `InvoicePushReqVO`(入参)/ `Result<Void>`(出参)
#### 使用场景
财务在「发票管理 · 待推送 / 已推送」点「推送」,勾选渠道后提交。发票状态 ISSUED → PUSHED(PUSHED 可再次推送);事务提交后各渠道异步发送并写推送日志。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| id | path | string(Long) | 是 | 发票 ID | 发票主键,字符串透传 |
| channels | body | string[] | 是 | 非空;元素 ∈ 字典 `invoice_push_channel`(email / wechat / sms) | 推送渠道,可多选 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| code | int | 200 成功 |
| message | string | 「成功」 |
| data | null | 无数据;各渠道发送结果看推送日志 |
#### 请求示例
```json
{
"channels": ["sms", "email"]
}
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": null,
"traceId": null,
"success": true
}
```
#### 空数据 / 降级响应
接口本身无空数据形态。通知中心不可用、短信发送失败都**不影响推送接口返回成功**(发送在事务提交后异步进行),失败结果体现在推送日志 `FAILED` / `PENDING`:
```json
{
"channel": "sms",
"pushStatus": "FAILED",
"statusText": "失败",
"pushedBy": "王会计",
"pushedAt": "2026-10-03 21:05:45",
"failReason": "通知中心调用失败"
}
```
#### 错误响应
错误码与改前完全一致:
| 业务码 | 场景 |
|---|---|
| 581500 | 发票不存在 |
| 581520 | 发票当前状态不允许推送(须 ISSUED / PUSHED) |
| 581521 | 推送渠道为空 |
| 581522 | 推送渠道非法 |
```json
{
"code": 581520,
"message": "发票当前状态不允许推送(须已开票)",
"data": null,
"traceId": null,
"success": false
}
```
#### 业务边界
- 状态机不变:ISSUED / PUSHED → PUSHED,`pushedAt` / `pushedChannels` 每次推送覆盖。
- 每个勾选渠道每次推送写一条日志;sms 先写 `PENDING`,发完回写终态。
- 短信收件人 = 订单 `customer_phone`(下单手机号);为空时写 `SKIP`「下单手机号为空,未发送短信」,不生成下载码。
- 短信只在 sms 渠道发;email / wechat 不发、写 `SKIP`。
- 同一发票连推两次会发两条短信(每次推送各自生成下载码)。
### 2. 发票推送明细日志 `GET /v3/admin/order/invoice/{id}/push-logs`
**VO**: `InvoicePushLogRespVO`
#### 使用场景
推送记录抽屉按推送时间倒序展示每次、每个渠道的推送结果。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| id | path | string(Long) | 是 | 发票 ID | 发票主键 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| channel | string | email / wechat / sms |
| pushStatus | string | SUCCESS / FAILED / PENDING / **SKIP(新增)** |
| statusText | string | 成功 / 失败 / 待发 / **未发送(新增)** |
| pushedBy | string | 操作人真实姓名 |
| pushedAt | string | 推送时间 `yyyy-MM-dd HH:mm:ss` |
| failReason | string \| null | 原因:FAILED / SKIP / PENDING 时有值,SUCCESS 为 null(原注释「预留」,现已启用) |
#### 请求示例
```http
GET /v3/admin/order/invoice/2106369928091283458/push-logs
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": [
{
"channel": "email",
"pushStatus": "SKIP",
"statusText": "未发送",
"pushedBy": "admin",
"pushedAt": "2026-10-03 21:11:53",
"failReason": "邮件渠道未接入,未发送"
},
{
"channel": "sms",
"pushStatus": "SKIP",
"statusText": "未发送",
"pushedBy": "admin",
"pushedAt": "2026-10-03 21:05:45",
"failReason": "短信模板未配置,未发送"
}
],
"traceId": null,
"success": true
}
```
#### 空数据 / 降级响应
从未推送过:
```json
{
"code": 200,
"message": "成功",
"data": [],
"traceId": null,
"success": true
}
```
#### 错误响应
```json
{
"code": 581500,
"message": "发票不存在",
"data": null,
"traceId": null,
"success": false
}
```
#### 业务边界
- 存量日志不订正:本单上线前的历史行仍是 `SUCCESS`(当时并未真发)。
- `pushStatus` 未知值时 `statusText` 回落为原始值(既有口径)。
- 短信行在推送后约 1 秒内由 `PENDING` 回写为终态;结果不确定时保留 `PENDING` 并写「以通知中心发送日志为准」。
### 3. 发票详情 `GET /v3/admin/order/invoice/{id}`
**VO**: `AdminInvoiceDetailRespVO`
#### 使用场景
发票详情 / 上传抽屉。本单只影响其中的 `pushLogs[]`(与接口 2 同源同 VO),其余字段零变化。
#### 入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| id | path | string(Long) | 是 | 发票 ID | 发票主键 |
#### 出参
| 字段 | 类型 | 说明 |
|---|---|---|
| pushLogs | InvoicePushLogRespVO[] | 字段与接口 2 完全一致:`pushStatus` 新增 SKIP、`statusText` 新增「未发送」、`failReason` 如实写原因 |
| 其余字段 | - | 不变 |
#### 请求示例
```http
GET /v3/admin/order/invoice/2106369928091283458
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"data": {
"id": "2106369928091283458",
"orderNo": "HL20260930170915373",
"status": "PUSHED",
"statusText": "已推送",
"invoiceNo": "26152000000087551236",
"pushedChannels": "email",
"pushLogs": [
{
"channel": "email",
"pushStatus": "SKIP",
"statusText": "未发送",
"pushedBy": "admin",
"pushedAt": "2026-10-03 21:11:53",
"failReason": "邮件渠道未接入,未发送"
},
{
"channel": "sms",
"pushStatus": "SKIP",
"statusText": "未发送",
"pushedBy": "admin",
"pushedAt": "2026-10-03 21:05:45",
"failReason": "短信模板未配置,未发送"
}
]
},
"traceId": null,
"success": true
}
```
#### 空数据 / 降级响应
未推送过的发票 `pushLogs` 为空数组:
```json
{
"pushLogs": []
}
```
#### 错误响应
```json
{
"code": 581500,
"message": "发票不存在",
"data": null,
"traceId": null,
"success": false
}
```
#### 业务边界
- 判权、其余字段、`status` / `statusText` 口径均不变。
---
## 四、契约约束与正确调用方式
### ✅ 正确 / ❌ 错误调用顺序
- ✅ 推送记录展示用 `statusText`;`failReason` 非空时都展示(SKIP、PENDING 也有原因),前缀建议用「原因:」而不是「失败原因:」。
- ✅ 推送成功后若立刻打开推送记录,短信行可能还是 `PENDING`,可在抽屉打开时重新拉取。
- ❌ 不要把 `SKIP` 渲染成失败(红色),它表示「没发」而不是「发失败」。
- ❌ 不要继续展示「本期仅记录推送状态,客户暂不会实际收到通知」。
---
## 五、数据库行为
- **order-v3**:零 DDL。`invoice_push_log.push_status`(VARCHAR(16)、无 CHECK)新增写入值 `SKIP`;`fail_reason` 由「预留」改为如实写原因。短信推送每次写一条 `invoice_download_code`(建表见同单新增接口 changelog)。
- **user-service**:Flyway `V20261003_8755__invoice_pushed_notification.sql` 往 `notification_event_config` 插(或按 `uk_event_code` 收敛)`INVOICE_PUSHED`:只开短信、模板哨兵 `TODO_PLACEHOLDER`、`sms_field_mapping` 为 orderNo / invoiceNo / amount / code;重放不覆盖运营已填的模板编码。通知中心每次发送写一行 `notification_send_log`(`biz_type=INVOICE_PUSH`、`biz_id`=推送日志 ID,收件人脱敏)。
---
## 六、边界行为
| 场景 | 推送日志 |
|---|---|
| 选 sms,模板未配置(当前) | SKIP「短信模板未配置,未发送」 |
| 选 sms,下单手机号为空 | SKIP「下单手机号为空,未发送短信」 |
| 选 sms,阿里云受理(带回执号) | SUCCESS |
| 选 sms,短信通道为模拟档(无回执号) | SKIP「短信通道为模拟档(未配置短信网关),未真实发送」 |
| 选 sms,阿里云拒发 | FAILED「短信发送失败:<原因>」 |
| 选 sms,通知中心不可用 | FAILED「通知中心调用失败」 |
| 选 sms,投递结果不确定 / 查不到发送记录 | PENDING(写明以通知中心发送日志为准) |
| 选 email / wechat | SKIP「邮件 / 微信渠道未接入,未发送」 |
| 发票状态非 ISSUED / PUSHED | 581520,不写日志(同改前) |
---
## 六.6、修改前后对比
| 项 | 改前 | 改后 |
|----|------|------|
| 选 sms | 不发,日志写 SUCCESS | 经通知中心按下单手机号直发,日志按真实结果写 |
| 选 email / wechat | 不发,日志写 SUCCESS | 不发,日志写 SKIP + 原因 |
| `pushStatus` 取值 | SUCCESS(实际恒定)/ FAILED / PENDING(预留) | SUCCESS / FAILED / PENDING / **SKIP** |
| `statusText` 取值 | 成功 / 失败 / 待发 | 成功 / 失败 / 待发 / **未发送** |
| `failReason` | 恒 null | FAILED / SKIP / PENDING 时写原因 |
| 短信行写入时机 | 推送后异步一次写入 | 推送后异步先写 PENDING,发完回写终态 |
| 入参 / 出参 / 错误码 / 状态机 | — | 不变 |
---
## 六.7、影响评估
- **前端(hl-ui v2.1 实查)**:
- `src/views/order/invoice/components/PushLogsDrawer.vue:30` 显示 `log.statusText || log.pushStatus`,「未发送」**自动生效**;`:27` 标签色 `STATUS_TYPE[s] || 'default'`,SKIP 落灰色,可选补一个色。
- `PushLogsDrawer.vue:41`把 `failReason` 冠以「失败原因:」前缀展示——SKIP 行也会显示,**前缀建议改「原因:」**。
- `src/views/order/invoice/components/PushModal.vue:15`「本期仅记录推送状态,客户暂不会实际收到通知。」**需改**为如实说明:短信按订单下单手机号发送(模板生效后),邮件、微信暂不发送。
- `src/api/invoice.js:191` 注释中的取值说明可同步补 SKIP。
- **历史数据**:上线前的推送日志仍是 SUCCESS(当时未真发),不订正。
- **其他调用方**:无。小程序端、通知中心其他事件、团期 / 订单流程零影响。
---
## 七、不影响范围
- **仅影响**: 推送接口的发送行为与推送日志取值(3 个管理端接口的 `pushLogs` 相关字段)。
- **零影响**:
- 发票申请、开票、重传、重开、列表、统计接口
- 小程序发票查询 / 下载 / 重开
- 通知中心其他事件
- 零权限种子变更、零网关变更(本组接口)。
---
## 八、测试环境已验证
部署:hl-user-service、hl-order-service-v3、hl-gateway = dev-v3 @ 9cd23903a(2026-10-03 20:56 / 20:59 / 21:00);TEST `flyway_schema_history` user `20261003.8755` success=1,`INVOICE_PUSHED` 配置行读回只开短信、模板 `TODO_PLACEHOLDER`;构建身份探针 6/6 命中新代码。经网关 `https://api.test.1814.love` 真实鉴权实测(2026-10-03 21:05–21:11),工单 #8755 已验收关单。
| # | 场景 | 结果 |
|---|---|---|
| 1 | 团期子订单(user_id 为空)发票 ISSUED,推 `["sms"]` | 200;发票 → PUSHED;推送日志 SKIP「短信模板未配置,未发送」;通知中心发送日志一行 `INVOICE_PUSHED / SMS / status=3`,收件人 `138****2356`,`params_json` 无手机号;生成下载码 1 枚(7 天) |
| 2 | 另一张 ISSUED 发票推 `["email","wechat"]` | 200;发票 → PUSHED;两行 SKIP,原因分别为微信 / 邮件渠道未接入;不生成下载码 |
| 3 | 已 PUSHED 发票再推 `["email"]` | 200;仍 PUSHED,`pushedAt` / `pushedChannels` 已更新 |
| 4 | `GET /{id}/push-logs`、`GET /{id}` | `pushStatus=SKIP`、`statusText=未发送`、`failReason` 如实 |
| 5 | 日志脱敏 | TEST order-v3 日志 11 位手机号出现 0 次 |
| 6 | TEST 短信网关形态 | `aliyun.sms.access-key-id` 为真实 AK(非模拟档);哨兵期在模板检查处跳过,未触达阿里云、未发出短信 |
---
## 九、相关历史 PR
| PR | Issue | 说明 | 是否仍有效 |
|----|-------|------|------------|
| — | #4230 | 发票推送「本期不真发」 | ⚠️ 被本单取代 |
| — | #3713 | 通知中心 directPhone 收件通道 | ✅ |
| — | #8754 | 成团通知客户短信(同样按下单手机号直发) | ✅ |
| **本 PR #8776** | **#8755** | 发票推送接短信 + 凭短码免登录下载 | ✅ 最新 |
---
## 十、相关文档
- 关联 Issue: [wx/HL#8755](https://git.1814.love/wx/HL/issues/8755)
- 关联 PR: [wx/HL#8776](https://git.1814.love/wx/HL/pulls/8776)
- 短信模板申请跟进: [wx/HL#8790](https://git.1814.love/wx/HL/issues/8790)
- 同单配套新增接口: `changelogs-v2/2026-10/04_8755_发票短信短码免登录下载-新增接口-管理后台.md`
## 关联 / 联系人
### 链接
- **Issue**: [#8755](https://git.1814.love/wx/HL/issues/8755)
- **PR**: [#8776](https://git.1814.love/wx/HL/pulls/8776)
- **Merge commit**: [9cd23903a](https://git.1814.love/wx/HL/commit/9cd23903abe8edc75e13ca2e7c4d5c65bd9e4453)
### 联系人
- **后端负责人**: @jw
- **前端负责人**: @mmg
- **短信模板**: @wx(#8790)