feat: 网关只读资产改为数据表,只读资产只允许查询类调用
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m36s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m36s
- 新增 tb_gateway_readonly_card(卡 ICCID / 设备 IMEI)与开关 JUNHONG_GATEWAY_READONLY_ENABLED, 替换环境变量名单 JUNHONG_GATEWAY_READONLY_ICCIDS - 只读资产只放行查卡状态、查实名、查流量、查设备信息、查设备槽位、同步设备状态; 停机、复机、限速、实名认证链接与设备写操作在网关出口本地拦截;查表失败按拦截;不缓存 - 测试环境部署开启只读模式;新增技术方案与清理顺序文档 Change: add-gateway-readonly-prod-import Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
150
docs/测试环境导入生产数据验证流量计量技术方案.md
Normal file
150
docs/测试环境导入生产数据验证流量计量技术方案.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# 测试环境导入生产数据验证流量计量技术方案
|
||||
|
||||
| 项目 | 内容 |
|
||||
|---|---|
|
||||
| 日期 | 2026-10-29 |
|
||||
| 状态 | 方案已确认(2026-10-29),代码未修改 |
|
||||
| 范围 | 测试环境 |
|
||||
| 关联 | `docs/卡流量采集写入与扣减技术方案.md`、Change `add-card-traffic-queue-and-observed-deduction`、`docs/测试环境生产卡只读模式清理顺序.md` |
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
生产(迁移版本 240,仍是卡表基线算法)大量卡"卡住":卡表基线 `last_gateway_reading_mb` 高于运营商读数,读数被当成下降拒绝,套餐长期扣 0。2026-10-29 核对:
|
||||
|
||||
- 生产有生效套餐、开启轮询、本月用量为 0、基线大于 0 的卡:1632 张(独立卡 1233 张)。
|
||||
- 其中 916 张在 flowcollector 10-26 起的读数里持续上涨、一直未扣,合计约 1635 GB。
|
||||
- 单卡导入测试环境已验证新算法可解冻并按增量扣费(测试卡 12244、12245),但单卡无法说明全量差距。
|
||||
|
||||
目标:把生产的卡、设备与套餐数据导入测试环境,用新计量逻辑(采集队列、日快照、建起点、按采集时刻扣减)跑真实读数,算出全量差距;**全过程不得对真实卡、设备发起任何写操作(停复机、限速、实名认证、设备指令)**。
|
||||
|
||||
不做:订单、钱包、分佣、退款、审批、个人客户、通知等业务流程的验证;生产发布与回滚。
|
||||
|
||||
## 2. 数据规模(2026-10-29)
|
||||
|
||||
| 项 | 生产 | 测试 |
|
||||
|---|---|---|
|
||||
| 卡 | 98,567(开启轮询 90,073,独立卡 59,900) | 33 |
|
||||
| 设备 | 20,144(有效绑定 38,667) | 6 |
|
||||
| 生效中套餐 | 22,426(其中设备套餐 10,799) | — |
|
||||
| 待生效 / 生效中 / 已用完套餐 | 29,175 | 128(全部状态) |
|
||||
| 使用中的运营商通道 | 34 | 13 |
|
||||
| 迁移版本 | 240 | 268 |
|
||||
|
||||
## 3. 方案总览
|
||||
|
||||
```
|
||||
① 建只读表 + 开关上线(测试环境)
|
||||
② 生产数据 → 只读表(全部 ICCID 与设备 IMEI)
|
||||
③ 确认部署生效、只读表已写入
|
||||
④ 导入卡、设备、绑定、套餐(测试库),全部卡打"待建起点"
|
||||
⑤ 轮询按新逻辑采集 → 采集记录、日快照、套餐扣减
|
||||
⑥ 差距报表:新逻辑应扣 vs 生产已扣
|
||||
⑦ 验证结束后按清理文档撤除
|
||||
```
|
||||
|
||||
必须先 ①②③ 再 ④:导入的卡在测试环境一开始没有套餐或状态不一致,会立即触发停复机判断,此时只读保护必须已生效。
|
||||
|
||||
## 4. 只读表与开关
|
||||
|
||||
### 4.1 表 `tb_gateway_readonly_card`
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `id` | 主键 |
|
||||
| `identifier` | 网关请求里的卡号 `cardNo`:卡为 ICCID,设备指令为设备 IMEI;大写;唯一索引 |
|
||||
| `identifier_type` | `iccid` / `imei` |
|
||||
| `source` | 来源,如 `prod_import_20261029` |
|
||||
| `remark` | 备注 |
|
||||
| `created_at` | 写入时间 |
|
||||
|
||||
- 新的成对迁移建表;不写入数据(数据是测试环境专用,由导入脚本写入)。
|
||||
- 不使用数据库外键。
|
||||
|
||||
### 4.2 开关 `JUNHONG_GATEWAY_READONLY_ENABLED`
|
||||
|
||||
只读表里的卡与设备**只允许查询类调用**,其余一律不允许:
|
||||
|
||||
| 允许 | 接口 |
|
||||
|---|---|
|
||||
| 查卡状态 | `/flow-card/status` |
|
||||
| 查实名 | `/flow-card/realName` |
|
||||
| 查流量 | `/flow-card/flow` |
|
||||
| 查设备信息 | `/device/info` |
|
||||
| 查设备槽位 | `/device/slot-info` |
|
||||
| 同步设备状态 | `/device/sync-info` |
|
||||
|
||||
- `true`:网关客户端每次调用前,接口不在上表时,按请求卡号(`cardNo`)查表,命中则本地拦截、不发请求,返回明确失败(不是结果未知),记日志 `gateway.readonly_card.blocked`。上表接口不查表、直接放行。
|
||||
- 被拦截的包括但不限于:停机 `/flow-card/cardStop`、复机 `/flow-card/cardStart`、限速 `/flow-card/speedLimit`、实名认证链接 `/flow-card/RealNameVerification`,以及设备切卡、恢复出厂、重启、WiFi 配置、模式切换。
|
||||
- 查表失败(含超时、表不存在)按拦截处理:宁可调用失败,不放过真卡。
|
||||
- 不做内存缓存:非查询类调用本身不频繁,按唯一索引查表约 1 ms;表一写入立即生效,没有刷新空窗。
|
||||
- 未配置或为 `false`:不查表。生产不配置。
|
||||
- 替换现有 `JUNHONG_GATEWAY_READONLY_ICCIDS`;名单内现有两张卡(`89861590172420410612`、`89861590172420420169`)迁入表中。
|
||||
|
||||
### 4.3 拦截覆盖面
|
||||
|
||||
所有网关调用都经过 `internal/gateway/client.go` 的 `doRequest` / `doRequestWithoutPayloadLog`,在这里判断即可覆盖所有入口:自动停复机(轮询、流量扣减、通道阈值、套餐激活与到期、订单、退款)、人工停复机按钮、限速档位、实名认证链接、设备各类接口等。业务代码不做任何改动。
|
||||
|
||||
## 5. 数据导入
|
||||
|
||||
### 5.1 范围(2026-10-29 已确认:全量)
|
||||
|
||||
| 数据 | 导入内容 |
|
||||
|---|---|
|
||||
| 运营商通道 | 不导入;按 `carrier_code` 映射到测试库已有通道,测试库缺的通道按生产配置补建(含重置日) |
|
||||
| 卡 | 生产全部卡(98,567):ICCID、ICCID19/20、MSISDN、虚拟号、通道、卡类别、实名状态、网络状态、实名策略、轮询开关;店铺统一挂测试代理 9「测试采购部」 |
|
||||
| 资产标识 | 每张卡的 ICCID、虚拟号写入 `tb_asset_identifier` |
|
||||
| 设备与绑定 | 生产全部设备(20,144):设备号、IMEI、SN、槽位与有效绑定关系;店铺统一挂测试代理 9「测试采购部」 |
|
||||
| 套餐使用记录 | 卡套餐与设备套餐中待生效、生效中、已用完的记录(约 29,175 条),保留生产的开始(生效)时间、结束(到期)时间、真总量、虚总量、重置周期与下次重置时间、优先级与主套餐 / 加油包关系;**只保存真实已用量**(`data_usage_mb`,导入时刻的生产当前值;待生效套餐本来为 0)。虚流量已用量不存储,由真实已用量 × 展示倍率 `display_gain_ratio_snapshot` 现算(上限为真总量);虚总量、展示倍率、是否启用虚流量是套餐条款快照(停机线 = 启用虚流量时的虚总量,否则真总量),照原样复制;订单号用 `PROD-<原订单号>` 标记,订单 ID 用不存在的占位值;`package_id` 指向一个测试占位套餐 |
|
||||
| 不导入 | 订单、钱包、分佣、退款、个人客户、审批、历史日用量与审计 |
|
||||
|
||||
### 5.2 方式
|
||||
|
||||
用导入脚本直接写测试库(后台导卡接口一次只能导卡,不能导设备与套餐,也不适合 10 万级数据):
|
||||
|
||||
1. 从生产只读导出(dbhub / 只读连接),生成 CSV。
|
||||
2. 写只读表:全部 ICCID 与设备 IMEI。
|
||||
3. 确认开关已部署(以 Gitea Actions 部署成功为准)且只读表行数与导出一致。
|
||||
4. 按通道映射写卡、资产标识、设备、绑定、套餐;同 ICCID / IMEI 已存在则跳过并记录。
|
||||
5. 全部导入的卡置"待建起点"(`traffic_baseline_pending = true`)。
|
||||
6. 触发轮询重新加载(重启 Worker 或调用轮询初始化),使导入的卡进入调度。
|
||||
|
||||
导入脚本放在 `cmd/zz_tmp_*`,不提交;只允许连接 `junhong_cmp_test`。
|
||||
|
||||
### 5.3 建起点
|
||||
|
||||
每张卡在测试环境第一次通过队列写入时只建起点:把当时运营商读数对应的本月累计全部记为已结算,不扣费。之后按增量扣到导入的套餐上。因此测试环境算出的是"导入之后"的应扣,导入之前的差距由第 7 节用 flowcollector 读数补充。
|
||||
|
||||
## 6. 轮询
|
||||
|
||||
- **完全按测试环境现有的轮询配置与代码逻辑运行**,不为导入的卡、设备单独配置,也不改业务流程:查卡状态、查实名、查流量、查套餐照常执行;流量照常进采集队列、写日快照、建起点、按增量扣套餐;套餐照常激活、到期、重置;停复机判断照常执行并记录。
|
||||
- 唯一不同:导入资产的网关调用只放行查询类(查卡状态、查实名、查流量、查设备信息、查设备槽位、同步设备状态),停机、复机等其他调用在网关出口拦截(第 4 节)。被拦截的停复机记为失败并按退避重试,本地卡状态不变;这些失败记录是预期的,也正好说明"按新逻辑该停/该复"的卡有哪些。
|
||||
- 查卡状态、查实名放行后,测试环境的网络状态、实名状态跟随真实情况。
|
||||
- 轮询频率不作为本方案的约束。
|
||||
|
||||
## 7. 差距报表
|
||||
|
||||
按套餐(设备套餐按设备合并)输出:
|
||||
|
||||
| 列 | 来源 |
|
||||
|---|---|
|
||||
| 卡 / 设备、套餐、生效与到期时间、真总量、虚总量 | 导入数据 |
|
||||
| 导入时已用量 | 导入时刻生产套餐已用量(两边共同起点) |
|
||||
| 测试环境新增扣减 | 测试环境套餐已用量 − 导入时已用量(建起点之后按新逻辑扣) |
|
||||
| 导入前差距上限 | 本月运营商读数(重置日按通道配置换算)对生效后的上限,来自 flowcollector |
|
||||
| 生产同期新增扣减 | 生产套餐已用量(同一时间点只读查询)− 导入时已用量 |
|
||||
| 差额、是否已到停机线 | 计算 |
|
||||
|
||||
汇总:卡住卡数、总差额(GB)、按新逻辑已到停机线但生产未停的套餐数。
|
||||
|
||||
## 8. 清理
|
||||
|
||||
按 `docs/测试环境生产卡只读模式清理顺序.md` 执行:先处理测试环境里的生产卡 → 关开关并重新部署 → 新迁移删表 → 可选清理代码。导入的卡、设备、套餐与采集数据在第一步删除。
|
||||
|
||||
## 9. 已确认事项
|
||||
|
||||
已确认(2026-10-29):
|
||||
|
||||
1. 导入全量:卡、设备、绑定,以及卡套餐与设备套餐(含开始、结束时间)。
|
||||
2. 套餐只保存真实已用量(导入时刻生产当前值),虚流量已用量不存储、由真实已用量 × 展示倍率现算。差距报表因此按"导入后测试环境新增扣减 vs 生产同期新增扣减"对比,两边起点一致。
|
||||
3. 卡和设备全部挂测试代理 9「测试采购部」。之前单独导入的两张卡(测试卡 12244、12245,目前未挂店铺)一并挂过去。
|
||||
4. 生产卡的 MSISDN、虚拟号等按原值导入,不脱敏。
|
||||
47
docs/测试环境生产卡只读模式清理顺序.md
Normal file
47
docs/测试环境生产卡只读模式清理顺序.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 测试环境生产卡只读模式清理顺序
|
||||
|
||||
| 项目 | 内容 |
|
||||
|---|---|
|
||||
| 日期 | 2026-10-29 |
|
||||
| 范围 | 测试环境 |
|
||||
| 对应机制 | 网关只读卡表 `tb_gateway_readonly_card` + 环境变量 `JUNHONG_GATEWAY_READONLY_ENABLED` |
|
||||
|
||||
## 背景
|
||||
|
||||
为验证卡流量采集与扣减,把生产上"卡住"的卡导入测试环境。这些是真实用户的卡,测试环境只能查询,不能停复机。
|
||||
|
||||
- 只读卡表 `tb_gateway_readonly_card`:存放导入卡的 ICCID 与导入设备的 IMEI(`identifier` 唯一索引)。
|
||||
- 环境变量 `JUNHONG_GATEWAY_READONLY_ENABLED=true` 时,网关每次写操作(停机、复机、限速、实名认证、设备指令)前按请求卡号查表:在表里就本地拦截、不发请求;查流量、卡状态、实名这三类查询不查表、直接放行。
|
||||
- 查表失败按拦截处理(宁可停复机失败,不放过真卡)。
|
||||
- 生产不配置该环境变量,代码不查表。
|
||||
|
||||
## 清理顺序(必须按顺序执行)
|
||||
|
||||
### 1. 先处理测试环境里的生产卡(保护仍在,安全)
|
||||
|
||||
- 把表里这些卡在测试环境的 `enable_polling` 设为 `false`,或删除卡及其测试套餐、采集记录、日快照。
|
||||
- 确认这些卡已不在轮询队列中,也没有待执行的停复机指令。
|
||||
|
||||
> 顺序错误的后果:保护撤掉后这些卡仍在测试环境,一旦触发无套餐、套餐用完、套餐到期等停复机,会**真实停掉用户的卡**。
|
||||
|
||||
### 2. 关闭开关并重新部署
|
||||
|
||||
- 部署脚本去掉 `JUNHONG_GATEWAY_READONLY_ENABLED`(或设为 `false`),重新部署 API 与 Worker。
|
||||
- 以 Gitea Actions 部署成功为准确认生效(不要用数据库连接建立时间判断,连接池每 5 分钟会重建连接)。
|
||||
|
||||
> 顺序错误的后果:开关开着就删表,查表报"表不存在"按失败处理,测试环境**所有卡**的写操作都会被拦截,直到关闭开关。
|
||||
|
||||
### 3. 删表
|
||||
|
||||
- 新建一对迁移:up 执行 `DROP TABLE tb_gateway_readonly_card`,down 重建该表。
|
||||
- 不修改原建表迁移。
|
||||
|
||||
### 4. 清理代码(可选)
|
||||
|
||||
- 删除写操作前的查表逻辑与开关配置。开关关闭时这段代码不生效,不删也不影响。
|
||||
|
||||
## 其他说明
|
||||
|
||||
- 生产:不配置开关,不查表。若该版本发布到生产,建表迁移会建出空表,不影响功能,后续删表迁移随之执行即可。
|
||||
- 数据量:几万行(ICCID + 备注),删表瞬间完成,无锁表风险。
|
||||
- 留痕:拦截记录在日志、停复机记录与审计中,与该表无关,删表不影响已有记录。
|
||||
Reference in New Issue
Block a user