### Requirement: 注册卡状态轮询任务类型常量
系统 SHALL 在 `pkg/constants/constants.go` 中定义常量 `TaskTypePollingCardStatus = "polling:card_status"`，与其他轮询类型常量在同一常量块内。

#### Scenario: 常量可被引用
- **WHEN** 其他包引用 `constants.TaskTypePollingCardStatus`
- **THEN** 编译通过，值为 `"polling:card_status"`

---

### Requirement: 轮询配置支持卡状态检查间隔
`PollingConfig` model（`tb_polling_config`）SHALL 包含 `CardStatusCheckInterval *int` 字段（GORM column: `card_status_check_interval`），NULL 表示该配置不启用卡状态轮询。

#### Scenario: 配置间隔为正整数时卡状态轮询启用
- **WHEN** `PollingConfig.CardStatusCheckInterval` 不为 NULL 且值 > 0
- **THEN** `getEnabledTaskTypes` 返回的列表中包含 `TaskTypePollingCardStatus`

#### Scenario: 配置间隔为 NULL 时卡状态轮询不启用
- **WHEN** `PollingConfig.CardStatusCheckInterval` 为 NULL
- **THEN** `getEnabledTaskTypes` 返回的列表中不包含 `TaskTypePollingCardStatus`

---

### Requirement: IotCard 记录最后一次卡状态检查时间
`IotCard` model（`tb_iot_card`）SHALL 包含 `LastCardStatusCheckAt *time.Time` 字段（GORM column: `last_card_status_check_at`），每次卡状态轮询成功执行后更新。

#### Scenario: 每次执行卡状态轮询后更新时间戳
- **WHEN** `PollingCardStatusHandler.Handle` 成功执行（无论状态是否变化）
- **THEN** `tb_iot_card.last_card_status_check_at` 更新为当前时间

---

### Requirement: PollingCardStatusHandler 查询 Gateway 卡状态并同步 DB
系统 SHALL 实现 `PollingCardStatusHandler`，遵循与 `PollingRealnameHandler` 相同的结构：获取并发信号量 → 从缓存/DB 获取卡信息 → 调用 `gateway.QueryCardStatus` → 解析状态 → 更新 DB → 触发停复机评估 → 更新统计 → 重入队。

#### Scenario: Gateway 返回"正常"时卡 network_status 为 1
- **WHEN** `gateway.QueryCardStatus` 返回 `CardStatus = "正常"` 或 `"准备"`
- **THEN** `newNetworkStatus = 1`（`NetworkStatusOnline`）

#### Scenario: Gateway 返回"停机"时卡 network_status 为 0
- **WHEN** `gateway.QueryCardStatus` 返回 `CardStatus = "停机"`
- **THEN** `newNetworkStatus = 0`（`NetworkStatusOffline`）

#### Scenario: 状态未变化时不触发停复机评估
- **WHEN** Gateway 返回的 `newNetworkStatus` 与 `card.NetworkStatus` 相同
- **THEN** 不调用 `stopResumeSvc.EvaluateAndAct`

#### Scenario: 状态发生变化时更新 DB + 缓存 + 触发评估
- **WHEN** Gateway 返回的 `newNetworkStatus` 与 `card.NetworkStatus` 不同
- **THEN** `tb_iot_card.network_status` 更新为新值，Redis 缓存同步更新，调用 `stopResumeSvc.EvaluateAndAct`

#### Scenario: Gateway 查询失败时重入队不更新状态
- **WHEN** `gateway.QueryCardStatus` 返回 error
- **THEN** 记录 Warn 日志，更新失败统计，重入队后返回，不修改 DB

#### Scenario: 并发已满时重新入队
- **WHEN** `acquireConcurrency` 返回 false
- **THEN** 调用 `requeueCard` 后返回，不执行 Gateway 查询

---

### Requirement: 卡状态轮询类型纳入队列管理
`allTaskTypes`（`queue_manager.go`）SHALL 包含 `TaskTypePollingCardStatus`，使得 `RemoveFromAllQueues`、调度器出队、初始化器均覆盖卡状态队列。

#### Scenario: 卡删除时清理卡状态队列
- **WHEN** 调用 `RemoveFromAllQueues(ctx, cardID)`
- **THEN** 卡 ID 从 `polling:card_status` 的所有分片队列中移除

---

### Requirement: 初始化器写入卡状态队列
`PollingInitializer` SHALL 在批量初始化时，为每张卡的 `polling:card_status` 分片 Sorted Set 写入初始条目（与 realname/carddata/package/protect 保持一致）。

#### Scenario: Worker 启动时卡状态队列被初始化
- **WHEN** `PollingInitializer.StartBackground` 完成批量加载
- **THEN** 每张启用轮询且匹配配置的卡在 `polling:card_status` 对应分片队列中有条目
