changelog-filename-gate / validate (push) Failing after 2s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
260 行
11 KiB
Markdown
260 行
11 KiB
Markdown
---
|
||
schema: "hl-changelog/v2"
|
||
ticket: "8755"
|
||
title: "发票短信短码免登录下载:新增公开端点 GET /v3/open/invoice/{code},客户凭短信里的 8 位短码 302 到现签 1 小时的发票文件"
|
||
consumer: "multiple"
|
||
author: "jw(GIT)"
|
||
change_type: "新增接口"
|
||
backend_status: "deployed"
|
||
gateway_status: "verified"
|
||
frontend_status: "not_required"
|
||
frontend_owner: ""
|
||
frontend_ref: ""
|
||
target_release: ""
|
||
verified_at: ""
|
||
updated_at: "2026-10-04"
|
||
base: "dev-v3"
|
||
status_note: "新增公开端点 GET /v3/open/invoice/{code}(网关新路由 invoice-open-v3 + SKIP_URLS,无登录态,凭码即鉴权)。财务推送发票选短信时,order-v3 为本次推送生成 8 位 base62 短码(列 utf8mb4_bin 区分大小写、7 天有效),客户点短信链接经本端点 302 到现签 1 小时的 OSS 发票文件;按客户端 IP 30 次/分钟限流。端点只给短信链接用,不给管理后台 / 小程序前端调用,Swagger 不展示,前端零适配。已合并 dev-v3(9cd23903a),部署 TEST(gateway、order-v3、user-service),经网关匿名实测 302 + PDF 字节一致、篡改 / 过期 / 限流均按约定拒绝,工单 #8755 已验收关单。短信模板尚在申请(#8790,wx),模板到位前客户收不到短信,本端点可用但无人拿到码。"
|
||
---
|
||
|
||
# 发票短信短码:新增免登录下载端点(公开)
|
||
|
||
> **服务**: hl-order-service-v3(端口 8086/8186)+ hl-gateway(新路由与免鉴权白名单)
|
||
> **PR**: #8776
|
||
> **Issue**: #8755
|
||
> **日期**: 2026-10-03
|
||
> **影响范围**: 新增一个对外公开端点,只给发票推送短信里的链接用;管理后台、小程序前端零适配
|
||
|
||
---
|
||
|
||
## ⚠️ 关键变化
|
||
|
||
1. **新增公开端点** `GET /v3/open/invoice/{code}`:不带任何登录态,凭 8 位短码访问,成功 **302** 到现签 1 小时的发票文件(OSS 私有桶)。
|
||
2. **网关新前缀** `/v3/open/invoice/**`:新路由 `invoice-open-v3` → order-service-v3,并加入 `JwtAuthFilter.SKIP_URLS`;客户端伪造的 `X-User-Id` / `X-Admin-Id` 会被网关剥掉。
|
||
3. **短码来源**:财务在发票管理页推送并勾选「短信」时,每推一次生成一枚新码(`invoice_download_code`),7 天有效;码绑定发票,财务重传文件后旧码下载到的是新文件,发票作废后码失效。
|
||
4. **短信模板未到位**:通知配置 `INVOICE_PUSHED` 仍是哨兵 `TODO_PLACEHOLDER`(申请见 #8790),客户暂时收不到短信;端点已上线可用。
|
||
|
||
---
|
||
|
||
## 一、背景
|
||
|
||
发票推送原先是假推送(#4230 定「本期不真发」)。#8755 把短信渠道接到通知中心、按订单下单手机号直发;团期子订单绝大多数没有 userId,小程序发票下载要求本人登录(`order.userId == 当前用户`),这些客户在小程序里拿不到发票,所以短信里放一个免登录的下载链接。阿里云短信「链接参数」变量最多 8 位,放不下签名令牌,于是落库存 8 位随机短码。
|
||
|
||
---
|
||
|
||
## 二、变更接口清单
|
||
|
||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||
|---|------|------|------|----------|------|
|
||
| 1 | 发票短码下载 | GET | `/v3/open/invoice/{code}` | 新增接口 | 免登录;成功 302 到现签 1 小时的发票文件;按 IP 30 次/分钟限流 |
|
||
|
||
网关:新增路由 `invoice-open-v3`(`Path=/v3/open/invoice/**` → `lb://hl-order-service-v3`),`/v3/open/invoice/**` 进 `SKIP_URLS`。
|
||
|
||
---
|
||
|
||
## 三、接口详情
|
||
|
||
### 1. 发票短码下载 `GET /v3/open/invoice/{code}`
|
||
|
||
**VO**: `ResponseEntity<Void>`(成功为 302 无响应体,失败为通用 `Result<Void>`)
|
||
|
||
#### 使用场景
|
||
|
||
客户收到「发票已开具」短信,点短信里的链接(模板正文写死对外域名 + 路径,变量只放 `code`),浏览器经本端点跳到发票 PDF 直接查看 / 下载。不给任何前端页面调用。
|
||
|
||
#### 入参
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|---|---|---|---|---|---|
|
||
| code | path | string | 是 | 恰好 8 位字母数字 `^[0-9A-Za-z]{8}$`,**区分大小写** | 发票推送短信里的下载短码 |
|
||
|
||
无请求头要求:不需要 `Authorization`,带了也不参与鉴权(网关对本前缀剥掉身份头)。
|
||
|
||
#### 出参
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---|---|---|
|
||
| HTTP 状态 | int | 成功固定 `302 Found` |
|
||
| Location | header string | 现签的 OSS 下载 URL,有效 1 小时(user-service `oss.signed-url-expire`),PDF 以 inline 方式打开 |
|
||
| Cache-Control | header string | 固定 `no-store`,签名 URL 不进任何缓存 |
|
||
| body | - | 成功无响应体 |
|
||
|
||
#### 请求示例
|
||
|
||
```http
|
||
GET /v3/open/invoice/K7mQ2xRb HTTP/1.1
|
||
Host: api.test.1814.love
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
成功(302,无响应体;以下为响应头的结构化描述):
|
||
|
||
```json
|
||
{
|
||
"httpStatus": 302,
|
||
"headers": {
|
||
"Location": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/test/invoice/2026/10/03/64674f6aa6086d1e42d7abcc2b1b50e0.pdf?Expires=1791036463&OSSAccessKeyId=***&Signature=***",
|
||
"Cache-Control": "no-store"
|
||
},
|
||
"body": null
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
没有空数据形态。文件服务(user-service 签名接口)不可用时不跳转,返回业务码 581519:
|
||
|
||
```json
|
||
{
|
||
"code": 581519,
|
||
"message": "发票下载服务暂时不可用,请稍后重试",
|
||
"data": null,
|
||
"traceId": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
失败一律 HTTP 200 + 业务码,不带任何发票 / 订单字段:
|
||
|
||
| 业务码 | 场景 |
|
||
|---|---|
|
||
| 581527 | 码格式不符(非 8 位字母数字)、查无此码、大小写或任一字符被改动 |
|
||
| 581528 | 码已过期(默认生成后 7 天) |
|
||
| 581517 | 码对应的发票已不可下载(已作废 / 不存在 / 文件地址异常),文案「发票尚未开具,暂时无法下载」沿用既有码 |
|
||
| 581519 | 现签下载链接失败(文件服务不可用) |
|
||
| 100501 | 同一来源 60 秒内超过 30 次 |
|
||
|
||
```json
|
||
{
|
||
"code": 581527,
|
||
"message": "发票下载链接无效",
|
||
"data": null,
|
||
"traceId": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 581528,
|
||
"message": "发票下载链接已过期,请联系客服重新推送",
|
||
"data": null,
|
||
"traceId": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 100501,
|
||
"message": "访问过于频繁,请稍后再试",
|
||
"data": null,
|
||
"traceId": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 每次「推送 + 勾选短信」且下单手机号非空时才生成码;邮件 / 微信推送、手机号为空都不生成。
|
||
- 同一张发票多次推送会有多枚码,各自 7 天有效、互不作废。
|
||
- 码绑定发票而不是文件:财务「重新上传」后,旧码下载到的是新文件;发票作废(重开)后旧码返回 581517。
|
||
- 302 的 Location 每次点击现签,有效 1 小时;客户隔天再点同一短信链接会拿到新的签名 URL(码 7 天内有效)。
|
||
- 限流按客户端 IP(取 `X-Forwarded-For` 第一段),30 次 / 60 秒。
|
||
|
||
---
|
||
|
||
## 四、契约约束与正确调用方式
|
||
|
||
### ✅ 正确 / ❌ 错误调用顺序
|
||
|
||
- ✅ 只在短信模板正文里写死「对外域名 + `/v3/open/invoice/` + `${code}`」(或经 nginx 短路径转发到本路径,见 #8790),变量只放 8 位码。
|
||
- ✅ 浏览器直接打开即可,跟随 302。
|
||
- ❌ 不要在管理后台 / 小程序里拼这个链接给用户:后台有发票详情与下载能力,小程序有登录态下载接口。
|
||
- ❌ 不要把 Location 里的签名 URL 存下来复用,它 1 小时过期。
|
||
- ❌ 不要对码做大小写归一化,码区分大小写。
|
||
|
||
---
|
||
|
||
## 五、数据库行为
|
||
|
||
- **order-v3**:Flyway `V20261003_8755__create_invoice_download_code.sql` 新建表 `invoice_download_code`(`code_id` 雪花主键、`short_code VARCHAR(16) utf8mb4_bin` 唯一键、`invoice_id`、`order_id`、`push_log_id`、`expire_at` + BaseDO 五列)。码在发票短信推送时写入(每次推送一行),本端点**只读不写**。
|
||
- 有效期配置项 `hl.order-v3.invoice.download-code-ttl-days`,缺省 7(未写入 nacos,取默认值)。
|
||
|
||
---
|
||
|
||
## 六、边界行为
|
||
|
||
| 场景 | 行为 |
|
||
|---|---|
|
||
| 正常码、发票 ISSUED / PUSHED | 302 到现签 1 小时的发票文件 |
|
||
| 翻转码里一个字母的大小写 | 581527(列 `utf8mb4_bin` + 服务内逐字节比对,双保险) |
|
||
| 改动任一字符 / 位数不对 | 581527 |
|
||
| 码过期 | 581528 |
|
||
| 发票已作废 | 581517 |
|
||
| 文件服务不可用 | 581519 |
|
||
| 同一 IP 60 秒内第 31 次起 | 100501 |
|
||
| 带伪造的 `X-User-Id` / `X-Admin-Id` | 网关剥掉,按匿名处理 |
|
||
| 访问 `/v3/open/` 下其他路径 | 网关仍按需登录拦截(白名单只放行发票前缀) |
|
||
|
||
---
|
||
|
||
## 七、不影响范围
|
||
|
||
- **仅影响**: 新增一个公开端点、一张表、一条网关路由与白名单项。
|
||
- **零影响**:
|
||
- 小程序发票下载 `GET /v3/internal/mp/order/invoice/{id}/download`(登录态 + 本人校验,行为不变;内部改为复用抽出的 ossKey 解析,逐字等价)
|
||
- 管理后台发票列表、详情、开票、重传接口
|
||
- Swagger 文档(本端点方法级 hidden,不进任何分组)
|
||
- 零权限种子变更、零前端适配。
|
||
|
||
---
|
||
|
||
## 八、测试环境已验证
|
||
|
||
部署: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` order-v3 `20261003.8755` success=1,`invoice_download_code.short_code` 排序规则实测 `utf8mb4_bin`;构建身份探针:匿名请求不存在的码连打 6 次全部 581527。经网关 `https://api.test.1814.love` 匿名实测(2026-10-03 21:05–21:11),工单 #8755 已验收关单。
|
||
|
||
| # | 场景 | 结果 |
|
||
|---|---|---|
|
||
| 1 | 团期子订单(user_id 为空)发票推送短信后取码访问 | 302;`Cache-Control: no-store`;响应体 0 字节;Location 签名有效期 3600 秒 |
|
||
| 2 | 跟随 302 下载 | `%PDF-1.4`,SHA-256 与上传原件逐字节一致 |
|
||
| 3 | 翻转一个字母大小写 / 改最后一位 / 多一位 | 均 581527 |
|
||
| 4 | 把该码 `expire_at` 临时改到过去 | 581528;验后已还原,还原后恢复 302 |
|
||
| 5 | 同一来源 1.2 秒内连发 35 次 | 前 30 次 581527,第 31 次起 100501 |
|
||
| 6 | 日志脱敏 | TEST order-v3 日志里完整短码出现 0 次,只打前 2 位(如 `oK******`) |
|
||
|
||
---
|
||
|
||
## 九、相关历史 PR
|
||
|
||
| PR | Issue | 说明 | 是否仍有效 |
|
||
|----|-------|------|------------|
|
||
| — | #4230 | 发票推送「本期不真发」定案 | ⚠️ 被 #8755 取代(短信渠道已接通知中心) |
|
||
| — | #3780 | 车务 H5 录入链接短链化(短码进短信链接先例) | ✅ |
|
||
| **本 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)
|
||
- 同单配套变更(推送日志新增 SKIP): `changelogs-v2/2026-10/04_8755_发票推送接短信推送日志新增SKIP-修改接口-管理后台.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
|
||
- **短信模板**: @wx(#8790)
|