All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m35s
实现功能: - 实名状态检查轮询(可配置间隔) - 卡流量检查轮询(支持跨月流量追踪) - 套餐检查与超额自动停机 - 分布式并发控制(Redis 信号量) - 手动触发轮询(单卡/批量/条件筛选) - 数据清理配置与执行 - 告警规则与历史记录 - 实时监控统计(队列/性能/并发) 性能优化: - Redis 缓存卡信息,减少 DB 查询 - Pipeline 批量写入 Redis - 异步流量记录写入 - 渐进式初始化(10万卡/批) 压测工具(scripts/benchmark/): - Mock Gateway 模拟上游服务 - 测试卡生成器 - 配置初始化脚本 - 实时监控脚本 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
6.1 KiB
6.1 KiB
ADDED Requirements
Requirement: 实名状态查询
系统 SHALL 调用 Gateway API 查询卡的实名状态,并更新数据库和缓存。
Scenario: 查询未实名卡
- WHEN 实名检查任务执行,卡A的 ICCID 为 "89860123456789012345",real_name_status=0
- THEN 系统调用 Gateway.QueryRealnameStatus(ICCID),获取实名状态响应
Scenario: 实名状态未变化
- WHEN Gateway 返回实名状态为"未实名"(status=0),与数据库当前值相同
- THEN 系统更新 last_realname_check_at 字段,不触发配置重新匹配
Scenario: 实名状态变化为已实名
- WHEN Gateway 返回实名状态为"已实名"(status=1),数据库当前值为 0
- THEN 系统更新 real_name_status=1 和 last_realname_check_at,触发 OnCardStatusChanged() 重新匹配配置
Scenario: Gateway API 超时
- WHEN 调用 Gateway API 超时(>30秒)
- THEN 系统记录错误日志,任务失败,不重试,卡重新入队(按原计划下次检查)
Scenario: Gateway API 返回错误
- WHEN Gateway API 返回业务错误(如卡号不存在)
- THEN 系统记录错误日志,不更新数据库,任务失败,卡重新入队
Requirement: 并发控制
系统 SHALL 使用 Redis 信号量控制实名检查的并发数,避免打爆 Gateway。
Scenario: 获取并发信号量成功
- WHEN 实名检查任务开始执行,当前并发数为 30,配置的最大并发数为 50
- THEN 系统使用 INCR 增加计数,获取信号量成功,执行任务
Scenario: 并发已满
- WHEN 实名检查任务开始执行,当前并发数为 50,配置的最大并发数为 50
- THEN 系统 INCR 后发现超过限制,DECR 归还,任务返回 SkipRetry(不执行,等待下次调度)
Scenario: 任务完成释放信号量
- WHEN 实名检查任务完成(成功或失败)
- THEN 系统使用 DECR 释放信号量
Requirement: 数据库更新
系统 SHALL 更新卡的实名状态和最后检查时间到数据库。
Scenario: 更新实名状态
- WHEN Gateway 返回实名状态为 1
- THEN 系统执行
UPDATE iot_cards SET real_name_status=1, last_realname_check_at=NOW() WHERE id=?
Scenario: 数据库更新失败
- WHEN 数据库更新失败(如连接断开)
- THEN 系统记录错误日志,任务失败,卡重新入队
Requirement: Redis 缓存更新
系统 SHALL 同步更新 Redis 缓存中的卡信息。
Scenario: 更新缓存实名状态
- WHEN 数据库更新成功
- THEN 系统使用 HSET 更新 Redis 缓存(polling:card:{card_id})的 real_name_status 和 last_realname_check_at 字段
Scenario: 缓存更新失败不影响主流程
- WHEN Redis 更新失败
- THEN 系统记录警告日志,但任务仍视为成功(缓存可以通过定时同步或懒加载恢复)
Requirement: 配置重新匹配
系统 SHALL 在实名状态变化时重新匹配轮询配置。
Scenario: 从未实名变为已实名
- WHEN 卡A从 real_name_status=0 变为 1
- THEN 系统调用 OnCardStatusChanged(),重新匹配配置(从"未实名卡配置"切换到"已实名卡配置")
Scenario: 切换到低频检查
- WHEN 卡A匹配的配置从"未实名-30秒"切换到"已实名-3600秒"
- THEN 系统更新 Redis 缓存的 matched_config_id,更新队列中的 next_check_time(下次检查时间推迟)
Scenario: 不再匹配任何配置
- WHEN 卡A状态变化后,不再匹配任何启用的配置
- THEN 系统从所有队列移除该卡
Requirement: 重新入队
系统 SHALL 在检查完成后,将卡重新加入轮询队列。
Scenario: 计算下次检查时间
- WHEN 实名检查完成,当前配置的检查间隔为 60 秒
- THEN 系统计算 next_check_time = NOW() + 60秒
Scenario: 加回队列
- WHEN 计算出下次检查时间
- THEN 系统使用 ZADD 将卡ID和时间戳加入 polling:queue:realname
Scenario: 任务失败也重新入队
- WHEN 任务因 Gateway 超时失败
- THEN 系统仍然将卡重新入队(按原计划下次检查),不阻塞后续检查
Requirement: 行业卡特殊处理
系统 SHALL 识别行业卡,行业卡无需实名检查。
Scenario: 行业卡不参与实名检查
- WHEN 轮询配置匹配卡时,卡A的 card_category="industry"
- THEN 如果配置启用实名检查,系统跳过该卡或使用不启用实名检查的配置
Scenario: 行业卡配置示例
- WHEN 管理员创建配置,card_category="industry",real_name_check_enabled=false,card_data_check_enabled=true
- THEN 行业卡只参与流量检查,不参与实名检查
Requirement: 监控统计
系统 SHALL 记录实名检查的成功率和耗时。
Scenario: 记录成功统计
- WHEN 实名检查成功完成,耗时 123 毫秒
- THEN 系统更新 Redis Hash(polling:stats:realname),增加 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_status、new_status、duration_ms
Scenario: 记录失败日志
- WHEN 实名检查失败
- THEN 系统记录 Error 日志,包含 card_id、iccid、error 详情
Scenario: 记录状态变化日志
- WHEN 实名状态发生变化
- THEN 系统记录 Info 日志,包含 card_id、old_status、new_status、old_config、new_config