3.5 KiB
3.5 KiB
Delta Spec:本文件仅描述对
openspec/specs/polling-task-handlers/spec.md的变更部分。未提及的需求保持不变。
变更:polling_realname_handler——实名逆转防抖
原行为:检测到实名状态 1→0 逆转时,立即更新 DB 并调用 EvaluateAndAct 触发停机。
新行为:检测到逆转时,通过 Redis 计数器累计,仅在连续 3 次检测到逆转后才触发 EvaluateAndAct;检测到 true 时重置计数器。DB 写入行为(real_name_status 同步)保持不变——只延迟停机决策,不延迟状态记录。
Redis Key:polling:card:realname:reversal:{card_id}(RedisPollingRealnameReversalCountKey)
TTL:10 分钟(超时自动归零)
阈值常量:PollingRealnameReversalThreshold = 3
Scenario: 单次逆转不触发停机
- GIVEN card_id=4,
real_name_status=1(DB),Gateway 返回false - WHEN
polling_realname_handler处理该卡 - THEN DB 更新
real_name_status=0;Redis 计数器reversal:4变为 1(TTL=10min);不调用 EvaluateAndAct;卡保持在线
Scenario: 连续 3 次逆转才触发停机
- GIVEN card_id=4,前 2 次轮询 Gateway 均返回 false(计数器为 2)
- WHEN 第 3 次轮询 Gateway 仍返回 false
- THEN Redis 计数器变为 3(达到阈值);删除计数器;调用
EvaluateAndAct(freshCard);触发停机,stop_reason='not_realname'
Scenario: 中间出现 true 则重置计数
- GIVEN card_id=4,已记录 2 次逆转(计数器=2)
- WHEN 下次轮询 Gateway 返回 true(
real_name_status重置为 1) - THEN 删除 Redis 计数器(归零);若状态从 0→1 则触发复机评估(逻辑不变)
Scenario: 10 分钟未触发则计数自动归零
- GIVEN card_id=4 记录了 1 次逆转(计数器=1),之后 10 分钟内 Gateway 均返回 true
- WHEN 10 分钟 TTL 到期
- THEN Redis key 自动过期,计数归零;后续逆转需重新从 1 开始累计
变更:polling_cardstatus_handler——网关侧停机补写 stop_reason
原行为:检测到 Gateway 报告卡为"停机"(在线→停机状态变化)时,仅更新 network_status=0,不设置 stop_reason。
新行为:在写入 network_status=0 的同时,若当前 stop_reason 为空,补写 stop_reason='carrier_stopped'。
说明:只在状态变化时补写,若
stop_reason已有值(如已被本系统停机)则不覆盖。
Scenario: 网关侧停机事件补写 carrier_stopped
- GIVEN 卡在线(
network_status=1,stop_reason=''),Gateway 本次返回"停机" - WHEN
polling_cardstatus_handler处理,检测到statusChanged=true,newNetworkStatus=0 - THEN DB 更新
network_status=0, stop_reason='carrier_stopped';isPollingStopReason('carrier_stopped')=false;EvaluateAndAct不触发自动复机
Scenario: 不覆盖已有 stop_reason
- GIVEN 卡已停机(
network_status=0,stop_reason='not_realname'),Gateway 本次仍返回"停机" - WHEN
polling_cardstatus_handler处理,statusChanged=false(状态未变) - THEN 不写入任何字段;
stop_reason保持not_realname不变
Scenario: carrier_stopped 的卡通过 EvaluateAndAct 调用时正确跳过复机
- GIVEN 卡停机,
stop_reason='carrier_stopped',套餐有效,已实名 - WHEN 其他轮询任务调用
EvaluateAndAct(card) - THEN
isPollingStopReason('carrier_stopped')=false;直接返回 nil,不执行复机