Files
junhong_cmp_fiber/openspec/changes/add-polling-card-status-with-verbose-log/design.md
huang 4a1e44f9e1
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
提案
2026-04-15 11:17:23 +08:00

64 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
轮询系统采用分片 Redis Sorted Set + Asynq 任务队列的架构,支持 4 种轮询类型realname / carddata / package / protect。每种类型对应一个 Asynq handler所有 handler 通过 `PollingBase` 共享并发控制、缓存和重入队逻辑。
Gateway 已提供 `QueryCardStatus` 接口(返回 `CardStatus: "准备" | "正常" | "停机"`IotCard model 已有 `NetworkStatus int`0=停机1=开机),但轮询系统从未主动调用该接口同步状态。
## Goals / Non-Goals
**Goals:**
- 新增第 5 种轮询类型 `polling:card_status`,与现有 4 种类型完全对等集成
- `PollingBase` 新增 `verboseLog bool` 字段,所有 handler 在成功查询到上游数据后可选输出详细日志
- 保持向后兼容:现有轮询配置不受影响,`card_status_check_interval` 为 NULL 时该类型不参与轮询
**Non-Goals:**
- 不修改停复机决策逻辑(`EvaluateAndAct` 已处理)
- 不新增 HTTP API纯后台轮询任务
- 不对现有 4 种 handler 做任何功能性改动,只追加 verbose log 调用
## Decisions
**决策 1Gateway 状态映射规则**
- `"准备"``NetworkStatusOnline (1)`:卡已就绪,视为开机
- `"正常"``NetworkStatusOnline (1)`:正常使用
- `"停机"``NetworkStatusOffline (0)`:停机
- 理由:与现有 `realname` handler 的布尔映射风格一致,映射逻辑内联在 handler 内,不引入额外函数
**决策 2状态变更后触发 EvaluateAndAct**
- 上游报告状态变化时,先更新 DB + 缓存,再调用 `stopResumeSvc.EvaluateAndAct(freshCard)`
- 理由:与 realname/carddata handler 行为完全一致,停复机决策集中在 `EvaluateAndAct`
- 替代方案:仅写 DB 不触发评估 —— 拒绝,因为状态变化后不及时评估可能导致卡停复机延迟
**决策 3verboseLog 通过配置文件控制**
-`pkg/config/config.go` 新增 `PollingConfig.VerboseLog bool``NewPollingBase` 接受该参数
- `verboseLog` 开启时,每次成功从 Gateway 获取数据后以 `Info` 级别输出日志
- 理由:配置文件方式符合项目 Viper 规范,比 Redis key 实现简单,重启代价可接受(轮询系统重启本身就有初始化流程)
- 替代方案Redis key 动态开关 —— 额外的 Redis 查询开销,且 key 管理增加运维复杂度
**决策 4allTaskTypes 驱动初始化和出队**
- `queue_manager.go` 中的 `allTaskTypes` slice 控制:初始化时写入哪些队列、`RemoveFromAllQueues` 清理哪些队列、Scheduler 出队哪些类型
- 新类型追加到该 slice 即可自动接入所有相关流程,无需修改调度器核心逻辑
**决策 5数据库迁移**
- `tb_polling_config` 新增 `card_status_check_interval INT NULL`NULL 表示不启用
- `tb_iot_card` 新增 `last_card_status_check_at TIMESTAMPTZ NULL`,与其他 `last_*_check_at` 字段保持一致
- 两列均 `DEFAULT NULL`,完全向后兼容,无需数据迁移
## Risks / Trade-offs
- **[风险] card_status 轮询调用量增加约 25%(原 4 种 → 5 种)** → 缓解:`card_status_check_interval` 可配置,初期设置较长间隔(如 300 秒),观察 Gateway 负载后调整
- **[风险] verbose_log 开启后日志量大幅增加** → 缓解:默认关闭(`false`),生产环境仅在排查问题时临时开启;日志文件已配置 Lumberjack 轮转
- **[权衡] 状态以上游为准** → 若上游短暂故障返回异常状态(如误报"停机"handler 查询失败时会 warn 并重新入队,不更新状态;只有成功响应才触发写 DB
## Migration Plan
1. 执行数据库迁移(`make migrate-up`
2. 更新 Worker 二进制并重启(无需 API 服务重启)
3. 在轮询配置后台为需要卡状态轮询的配置项设置 `card_status_check_interval`
4. 初始化器将在 Worker 启动时自动将全量卡写入新的 `polling:card_status` 队列
5. 如需回滚:将 `card_status_check_interval` 设置为 NULL 即可停止该类型轮询,无需重启
## Open Questions
无。