## Why 轮询系统审计发现 3 个覆盖盲点:`polling:protect` 队列从未被初始化导致保护期一致性检查完全失效;设备级套餐耗尽时停机逻辑分支为空导致绑定卡继续在线消耗流量;套餐过期后卡的网络状态不主动停机,依赖下一轮 carddata 轮询兜底存在最长 60s 的流量泄露窗口。这三个问题均直接影响计费准确性和资源管控。 ## What Changes - **补全 `polling:protect` 调度器初始化**:在 `PollingConfig` 添加 `protect_check_interval` 字段,在 `tb_iot_card` 添加 `last_protect_check_at` 字段,在 `initCardsBatch`/`initCardPolling`/`requeueCard` 补全 protect 队列写入逻辑,使保护期一致性检查能够定时执行 - **设备级套餐耗尽触发停机**:在 `UsageService.checkAndTriggerSuspension` 补全 `carrierType == "device"` 分支,查询设备绑定的所有卡并异步触发停机 - **套餐过期时主动停机**:在 `PackageActivationHandler.processExpiredPackage` 过期主套餐且无后续套餐时,直接异步触发 `CheckAndStopCard`,消除依赖轮询兜底的延迟窗口 - **修正 `getCardCondition` 死代码**:已实名+离线的卡永远返回 `"real_name"` 而非 `"suspended"`,移除永不可达的分支 ## Capabilities ### New Capabilities - `device-package-exhausted-stop`:设备级套餐(`device_id` 绑定)流量耗尽时,自动触发设备绑定卡的停机操作 ### Modified Capabilities - `polling-protect-consistency`:补全调度器初始化逻辑,使保护期一致性检查能够按配置间隔定时调度(新增 DB 字段 + 初始化代码) - `auto-stop-resume`:新增套餐过期主动停机场景,不再完全依赖下一次 carddata 轮询触发停机 ## Impact - **数据库迁移**:`tb_polling_config` 新增 `protect_check_interval` 字段;`tb_iot_card` 新增 `last_protect_check_at` 字段 - **受影响代码**: - `internal/polling/scheduler.go` — `initCardsBatch`、`initCardPolling`、`requeueCard` - `internal/service/package/usage_service.go` — `checkAndTriggerSuspension` - `internal/polling/package_activation_handler.go` — `processExpiredPackage`、`updateIotCardSuspendedStatus` - `internal/model/polling.go` — `PollingConfig` struct - `internal/model/iot_card.go` — `IotCard` struct - `internal/task/polling_handler.go` — `getCardCondition` 死代码清理 - **无 API 变更**:所有修复为内部调度逻辑,不影响对外接口 - **需要数据库迁移**:两张表各增一列,向下兼容(nullable)