Files
junhong_cmp_fiber/openspec/changes/fix-polling-stop-logic/specs/polling-task-handlers/spec.md
huang 868f5a8ef3
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m37s
fix: 修复轮询停机连坐、实名逆转防抖及carrier_stopped原因写入缺失三个逻辑缺陷
2026-04-20 11:24:56 +08:00

3.5 KiB
Raw Blame History

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 Keypolling:card:realname:reversal:{card_id}RedisPollingRealnameReversalCountKey
TTL10 分钟(超时自动归零)
阈值常量PollingRealnameReversalThreshold = 3

Scenario: 单次逆转不触发停机

  • GIVEN card_id=4real_name_status=1DBGateway 返回 false
  • WHEN polling_realname_handler 处理该卡
  • THEN DB 更新 real_name_status=0Redis 计数器 reversal:4 变为 1TTL=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 返回 truereal_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=1stop_reason=''Gateway 本次返回"停机"
  • WHEN polling_cardstatus_handler 处理,检测到 statusChanged=truenewNetworkStatus=0
  • THEN DB 更新 network_status=0, stop_reason='carrier_stopped'isPollingStopReason('carrier_stopped')=falseEvaluateAndAct 不触发自动复机

Scenario: 不覆盖已有 stop_reason

  • GIVEN 卡已停机(network_status=0stop_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不执行复机