## 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-01),Gateway 返回 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-05,Gateway 返回 total_usage_mb=50(新月重置后) - **THEN** 系统检测到跨月,保存 last_month_total_mb=400(上月结束值),重置 current_month_start_date=2024-02-01,current_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 Hash(polling: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