Files
huang 8980e6d999
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m11s
fix: 补全三处轮询覆盖缺口
问题一:protect 轮询任务从未被调度器初始化入队
PollingConfig 新增 protect_check_interval 字段(NULL=不参与),
调度器 initCardsBatch/initCardPolling/requeueCard 补全 protect 队列
初始化逻辑,IotCard 新增 last_protect_check_at 记录上次检查时间。
迁移文件:000102_add_polling_protect_fields

问题二:设备套餐流量耗尽时绑定卡未被停机
UsageService.checkAndTriggerSuspension 新增 carrier_type=device 分支,
通过 DeviceSimBindingStore.ListByDeviceID 查询绑定卡,对每张卡异步
触发 CheckAndStopCard,同步注入 Bootstrap 依赖。

问题三:主套餐过期后不立即停机,依赖下次轮询兜底
PackageActivationHandler.processExpiredPackage 在 updateCarrierSuspended
Status 后,若载体无后续生效套餐,立即异步调用 CheckAndStopCard(iot_card
类型直接触发,device 类型遍历绑定卡),消除停机延迟窗口。

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-02 14:36:24 +08:00

2.9 KiB
Raw Permalink Blame History

Spec: 设备级套餐耗尽触发绑定卡停机

业务背景

为什么需要设备级套餐耗尽停机

现状问题

  • 设备套餐(carrier_type = "device")流量耗尽时,checkAndTriggerSuspension 缺少对绑定卡的停机逻辑
  • 仅有 iot_card 类型载体的停机分支,设备类型无对应处理
  • 导致设备套餐流量耗尽后,绑定卡仍保持在线状态,无法及时停机

业务目标

  • 设备套餐流量耗尽时,自动对所有绑定卡触发停机检查
  • 与 iot_card 类型保持行为一致性
  • 幂等安全,重复触发不产生副作用

ADDED Requirements

Requirement: 设备级套餐耗尽时触发绑定卡停机

当套餐载体为设备(carrier_type = "device")且所有生效套餐流量耗尽时,系统 SHALL 查询该设备绑定的所有 IoT 卡,并对每张卡异步触发停机检查(CheckAndStopCard)。

触发位置UsageService.checkAndTriggerSuspension,在确认无生效套餐后执行。

幂等保护CheckAndStopCard 内部检查卡是否已为停机状态(network_status = 0),重复调用不会重复调网关。

仅处理绑定卡:通过 DeviceSimBindingStore.ListByDeviceID 获取当前绑定关系(bind_status = 1),未绑定或已解绑的卡不受影响。

Scenario: 设备套餐流量耗尽,绑定卡在线

  • WHEN DeductDataUsagecarrier_type = "device" 场景下扣减流量后,checkAndTriggerSuspension 发现该设备无生效套餐(status IN (0,1)
  • THEN 系统查询该设备的绑定卡列表,对每张在线(network_status = 1)且已实名(real_name_status = 1)的卡异步调用 CheckAndStopCard,将卡停机(network_status = 0stop_reason = "traffic_exhausted"

Scenario: 设备套餐流量耗尽,绑定卡已停机

  • WHEN checkAndTriggerSuspension 触发设备级停机检查,但绑定卡已经是停机状态(network_status = 0
  • THEN CheckAndStopCard 检查到卡已停机,跳过网关调用,不产生重复操作

Scenario: 设备无绑定卡

  • WHEN checkAndTriggerSuspension 触发设备级停机检查,但该设备当前无有效绑定记录
  • THEN 系统静默跳过,不报错,记录 Debug 日志

Scenario: 套餐载体为 iot_card不受影响

  • WHEN DeductDataUsagecarrier_type = "iot_card" 场景下触发停机检查
  • THEN 仅对该卡本身执行 CheckAndStopCard,不查询设备绑定关系(与现有行为一致)

实现说明

依赖注入变更

UsageService 新增 deviceSimBindingStore *postgres.DeviceSimBindingStore 字段,通过构造函数注入,bootstrap 层负责传入。

异步触发模式

对每张绑定卡启动独立 goroutine 异步执行 CheckAndStopCard,不阻塞当前流量扣减流程。若 stopResumeCallback == nil,仅记录 Warn 日志,不报错。