Files
junhong_cmp_fiber/openspec/changes/archive/2026-03-30-refactor-traffic-system/proposal.md
huang cebcada950
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m16s
refactor: 流量系统重构 — 增量累加算法 + 日粒度缓冲 + 旧详单清理
核心改造:
- 增量算法:流量计算从覆盖式改为增量累加(gateway - lastReading),支持上游运营商重置检测
- 日流量缓冲:insertDataUsageRecord 改为 Redis INCRBYFLOAT,每日凌晨落盘到 tb_card_daily_usage
- 运营商:新增 data_reset_day 字段(联通=27,其余=1)
- IoT卡:新增 last_gateway_reading_mb 字段存储上次网关读数
- 查询层:新建 TrafficQueryService 合并 Redis(今日)+ DB(历史)数据源
- 清理:删除 DataUsageRecord model/store,移除 polling_handler 旧引用

迁移:000094-000097(carrier字段、iot_card字段、数据初始化、日流量表)
2026-03-30 09:59:30 +08:00

4.8 KiB
Raw Blame History

Why

当前流量数据处理存在两个架构性问题:

  1. 流量详单无差别写 DBP2-26每次轮询无条件调用 insertDataUsageRecord(),即使增量为 0 也写记录,大量卡每日产生大量无意义记录,数据库膨胀。
  2. 月流量直接覆盖P2-27calculateFlowUpdates() 直接用网关返回值覆盖 current_month_usage_mb。上游运营商按自然月重置(联通 27 号,移动/电信/广电 1 号),重置后网关返回 0我们的套餐月用量也归零——但套餐周期未必与自然月一致导致用量数据错误。

这是架构改造,涉及 DB 迁移 + Redis 引入 + 轮询核心路径修改,建议低峰期发布

What Changes

  • E-2 月流量改增量累加(先做):tb_iot_card 新增 last_gateway_reading_mb 字段记录上次网关读数,tb_carrier 新增 data_reset_day 字段记录运营商月重置日。合并 calculateFlowUpdates()calculateFlowIncrement() 为一个函数,改为增量算法:increment = 当前读数 - 上次读数,检测到上游重置时特殊处理。current_month_usage_mb 从直接覆盖改为 += increment。跨自然月时 current_month_usage_mb 重置为 0不再 = gatewayFlowMB)。⚠️ 上线前需运行数据初始化脚本,将所有卡的 last_gateway_reading_mb 设为当前 current_month_usage_mb
  • E-1 流量详单改日粒度缓冲:新建 tb_card_daily_usage 表(每卡每天一条)。insertDataUsageRecord() 改为只有增量 > 0 时写 RedisINCRBYFLOAT直接替换旧写入路径,不再写 tb_data_usage_record。新增每日落盘 Asynq 定时任务,凌晨 2 点分批 SCAN 昨日 Redis key → 覆盖 UPSERT 到 tb_card_daily_usage → 删 key。
  • E-3 流量查询层适配:新建 TrafficQueryService,查询时合并"今日 Redis + 历史 DB"两段数据。更新所有受影响的查询入口。
  • 清理:删除 tb_data_usage_record 相关代码model、store、bootstrap 引用)。

Capabilities

New Capabilities

  • traffic-daily-buffer: 流量日粒度 Redis 缓冲 + 每日落盘机制
  • traffic-query-service: 统一流量查询服务Redis + DB 合并)

Modified Capabilities

  • package-usage-daily-record: 流量详单从无差别写 DB 改为 Redis 缓冲 + 日落盘(直接替换旧路径)
  • carrier: 运营商新增 data_reset_day 字段(上游重置日)
  • iot-card: IoT 卡新增 last_gateway_reading_mb 字段,月流量改为增量累加,跨自然月重置为 0

Impact

  • DB 迁移3 个迁移文件(tb_carrier 加字段、tb_iot_card 加字段、新建 tb_card_daily_usage 表)
  • 数据初始化:上线前需运行脚本初始化 last_gateway_reading_mb
  • 修改文件internal/task/polling_handler.go(核心改动,合并两个增量计算函数)、internal/model/iot_card.gointernal/model/carrier.go、相关 DTO、运营商管理接口
  • 新增文件internal/model/card_daily_usage.gointernal/store/postgres/card_daily_usage_store.go(落盘+查询共用)、internal/service/traffic/query_service.go、日落盘任务 handler
  • 删除文件internal/model/data_usage.gointernal/store/postgres/data_usage_record_store.go(旧流量详单代码)
  • 新增常量Redis keytraffic:daily:{card_id}:{date})、任务类型(TaskTypeDailyTrafficFlush
  • Redis 新增用途流量增量缓存INCRBYFLOAT + 48h TTL
  • 轮询核心路径变更:高风险,需低峰期发布

受影响的查询接口E-3 需逐一适配)

# 接口路径 文件 影响方式
1 GET /api/admin/assets/:type/:id/realtime-status service/asset/service.go CurrentMonthUsageMB 语义变化
2 GET /api/admin/assets/resolve/:identifier handler/admin/asset.go resolve 返回流量概况
3 GET /api/admin/assets/device/:id/realtime-status service/asset/service.go 设备级聚合绑定卡用量
4 GET /api/c/v1/asset/info handler/app/client_asset.go C端首页流量数据
5 GET /api/c/v1/asset/packages handler/app/client_asset.go C端套餐用量展示
6 GET /api/c/v1/asset/refresh handler/app/client_asset.go C端手动刷新
7 GET /api/admin/assets/:type/:id/packages service/asset/service.go 管理端套餐列表

不受影响的模块(确认独立)

  • 套餐级流量重置ResetService):操作 PackageUsage.DataUsageMB,与卡级字段无关
  • 套餐日记录tb_package_usage_daily_record):套餐级日记录,由 UsageService.updateDailyRecord() 写入,与卡级 tb_card_daily_usage 是两个不同维度
  • 套餐创建/管理data_reset_cycle 配置不受影响