Files
huang 931e140e8e
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m35s
feat: 实现 IoT 卡轮询系统(支持千万级卡规模)
实现功能:
- 实名状态检查轮询(可配置间隔)
- 卡流量检查轮询(支持跨月流量追踪)
- 套餐检查与超额自动停机
- 分布式并发控制(Redis 信号量)
- 手动触发轮询(单卡/批量/条件筛选)
- 数据清理配置与执行
- 告警规则与历史记录
- 实时监控统计(队列/性能/并发)

性能优化:
- Redis 缓存卡信息,减少 DB 查询
- Pipeline 批量写入 Redis
- 异步流量记录写入
- 渐进式初始化(10万卡/批)

压测工具(scripts/benchmark/):
- Mock Gateway 模拟上游服务
- 测试卡生成器
- 配置初始化脚本
- 实时监控脚本

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 17:32:44 +08:00

7.1 KiB
Raw Blame History

ADDED Requirements

Requirement: 卡流量查询

系统 SHALL 调用 Gateway API 查询卡的流量使用情况,并处理跨月流量计算。

Scenario: 查询卡流量

  • WHEN 流量检查任务执行卡A的 ICCID 为 "89860123456789012345"
  • THEN 系统调用 Gateway.QueryFlow(ICCID),获取流量响应(包含月总流量 MB

Scenario: Gateway 返回月总流量

  • WHEN Gateway 返回流量数据total_usage_mb=500本自然月累计
  • THEN 系统获取到月总流量数据,准备计算增量

Scenario: Gateway API 超时

  • WHEN 调用 Gateway API 超时(>30秒
  • THEN 系统记录错误日志,任务失败,不重试,卡重新入队(按原计划下次检查)

Scenario: Gateway API 返回错误

  • WHEN Gateway API 返回业务错误(如卡号不存在)
  • THEN 系统记录错误日志,不更新数据库,任务失败,卡重新入队

Requirement: 跨月流量计算

系统 SHALL 正确处理跨月流量计算,识别月份切换并重置月度统计。

Scenario: 首次查询流量(冷启动)

  • WHEN 卡A从未查询过流量current_month_start_date 为 NULL
  • THEN 系统初始化 current_month_start_date=当前月1日current_month_usage_mb=Gateway返回值last_month_total_mb=0

Scenario: 同月内流量增长

  • WHEN 卡A上次查询在本月current_month_start_date=2024-01-01Gateway 返回 total_usage_mb=500数据库当前 current_month_usage_mb=400
  • THEN 系统计算增量 100 MB更新 current_month_usage_mb=500

Scenario: 跨月流量重置

  • WHEN 卡A上次查询在上月current_month_start_date=2024-01-01当前时间为 2024-02-05Gateway 返回 total_usage_mb=50新月重置后
  • THEN 系统检测到跨月,保存 last_month_total_mb=400上月结束值重置 current_month_start_date=2024-02-01current_month_usage_mb=50

Scenario: Gateway 返回值回退(异常)

  • WHEN Gateway 返回 total_usage_mb=300小于数据库当前值 current_month_usage_mb=400同月内
  • THEN 系统记录警告日志,怀疑数据异常,但仍更新为 Gateway 值(信任 Gateway 数据源)

Requirement: 数据库更新

系统 SHALL 更新卡的流量字段到数据库。

Scenario: 更新流量字段

  • WHEN 计算出增量流量 100 MB
  • THEN 系统执行 UPDATE iot_cards SET current_month_usage_mb=500, current_month_start_date='2024-01-01', last_realname_check_at=NOW() WHERE id=?

Scenario: 数据库更新失败

  • WHEN 数据库更新失败(如连接断开)
  • THEN 系统记录错误日志,任务失败,卡重新入队

Requirement: Redis 缓存更新

系统 SHALL 同步更新 Redis 缓存中的卡流量信息。

Scenario: 更新缓存流量字段

  • WHEN 数据库更新成功
  • THEN 系统使用 HSET 更新 Redis 缓存polling:card:{card_id})的 current_month_usage_mb、current_month_start_date、last_month_total_mb 字段

Scenario: 缓存更新失败不影响主流程

  • WHEN Redis 更新失败
  • THEN 系统记录警告日志,但任务仍视为成功(缓存可以通过定时同步或懒加载恢复)

Requirement: 流量历史记录

系统 SHALL 记录流量变化历史到 data_usage_records 表。

Scenario: 记录流量增量

  • WHEN 计算出增量流量 100 MB
  • THEN 系统插入记录到 data_usage_records 表card_id、usage_mb=100、usage_type='data'、recorded_at=NOW()

Scenario: 跨月时记录上月总量

  • WHEN 检测到跨月,上月总流量为 400 MB
  • THEN 系统插入一条月度汇总记录card_id、usage_mb=400、usage_type='monthly_summary'、recorded_at=上月最后一天)

Scenario: 历史记录插入失败

  • WHEN 历史记录插入失败
  • THEN 系统记录警告日志,但任务仍视为成功(历史记录非关键路径)

Requirement: 触发套餐检查

系统 SHALL 在流量更新后触发关联套餐的流量检查。

Scenario: 卡有关联套餐

  • WHEN 卡A更新流量后card_package_id 不为空
  • THEN 系统将该套餐加入 polling:manual:package 手动触发队列,优先检查套餐流量

Scenario: 卡无关联套餐

  • WHEN 卡A更新流量后card_package_id 为空
  • THEN 系统不触发套餐检查

Scenario: 卡关联设备级套餐

  • WHEN 卡A属于设备D设备D有套餐
  • THEN 系统将设备的套餐加入手动触发队列

Requirement: 并发控制

系统 SHALL 使用 Redis 信号量控制流量检查的并发数。

Scenario: 获取并发信号量成功

  • WHEN 流量检查任务开始执行,当前并发数为 30配置的最大并发数为 50
  • THEN 系统使用 INCR 增加计数,获取信号量成功,执行任务

Scenario: 并发已满

  • WHEN 流量检查任务开始执行,当前并发数为 50配置的最大并发数为 50
  • THEN 系统 INCR 后发现超过限制DECR 归还,任务返回 SkipRetry不执行等待下次调度

Scenario: 任务完成释放信号量

  • WHEN 流量检查任务完成(成功或失败)
  • THEN 系统使用 DECR 释放信号量

Requirement: 重新入队

系统 SHALL 在检查完成后,将卡重新加入轮询队列。

Scenario: 计算下次检查时间

  • WHEN 流量检查完成,当前配置的检查间隔为 1800 秒
  • THEN 系统计算 next_check_time = NOW() + 1800秒

Scenario: 加回队列

  • WHEN 计算出下次检查时间
  • THEN 系统使用 ZADD 将卡ID和时间戳加入 polling:queue:carddata

Scenario: 任务失败也重新入队

  • WHEN 任务因 Gateway 超时失败
  • THEN 系统仍然将卡重新入队(按原计划下次检查),不阻塞后续检查

Requirement: 监控统计

系统 SHALL 记录流量检查的成功率和耗时。

Scenario: 记录成功统计

  • WHEN 流量检查成功完成,耗时 234 毫秒
  • THEN 系统更新 Redis Hashpolling:stats:carddata增加 success_count_1h累加 total_duration_1h

Scenario: 记录失败统计

  • WHEN 流量检查失败Gateway 超时)
  • THEN 系统更新 Redis Hash增加 failure_count_1h

Scenario: 每小时重置计数器

  • WHEN 每小时整点(如 10:00:00
  • THEN 系统重置计数器success_count_1h、failure_count_1h、total_duration_1h保持时间窗口滚动

Requirement: 日志记录

系统 SHALL 记录详细的流量检查日志,便于排查问题。

Scenario: 记录开始日志

  • WHEN 流量检查任务开始执行
  • THEN 系统记录 Info 日志,包含 card_id、iccid、config_id

Scenario: 记录成功日志

  • WHEN 流量检查成功完成
  • THEN 系统记录 Info 日志,包含 card_id、iccid、old_usage_mb、new_usage_mb、increment_mb、duration_ms

Scenario: 记录失败日志

  • WHEN 流量检查失败
  • THEN 系统记录 Error 日志,包含 card_id、iccid、error 详情

Scenario: 记录跨月日志

  • WHEN 检测到跨月
  • THEN 系统记录 Info 日志,包含 card_id、old_month、new_month、last_month_total_mb