归档: add-polling-card-status-with-verbose-log,同步主规范
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m24s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m24s
change 目录归档至 openspec/changes/archive/2026-04-15-add-polling-card-status-with-verbose-log/ 新建主规范 openspec/specs/polling-card-status-task/spec.md(6个需求) 追加 verbose log 需求至 openspec/specs/polling-task-handlers/spec.md(4个新需求) protect handler 规范更新字段名:network_status → network_status_at_check + action_taken Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -1,57 +1,57 @@
|
||||
## 1. 数据库迁移
|
||||
|
||||
- [ ] 1.1 创建迁移文件 `000120_add_polling_card_status.up.sql`:`tb_polling_config` 新增 `card_status_check_interval INT NULL`,`tb_iot_card` 新增 `last_card_status_check_at TIMESTAMPTZ NULL`
|
||||
- [ ] 1.2 创建对应回滚文件 `000120_add_polling_card_status.down.sql`:DROP COLUMN 两列
|
||||
- [ ] 1.3 执行 `make migrate-up` 并验证:`make migrate-version` 确认 dirty=false,使用 PostgreSQL MCP 验证两列已创建
|
||||
- [x] 1.1 创建迁移文件 `000120_add_polling_card_status.up.sql`:`tb_polling_config` 新增 `card_status_check_interval INT NULL`,`tb_iot_card` 新增 `last_card_status_check_at TIMESTAMPTZ NULL`
|
||||
- [x] 1.2 创建对应回滚文件 `000120_add_polling_card_status.down.sql`:DROP COLUMN 两列
|
||||
- [x] 1.3 执行 `make migrate-up` 并验证:`make migrate-version` 确认 dirty=false,使用 PostgreSQL MCP 验证两列已创建
|
||||
|
||||
## 2. 常量与配置
|
||||
|
||||
- [ ] 2.1 `pkg/constants/constants.go`:轮询任务类型常量块新增 `TaskTypePollingCardStatus = "polling:card_status"`
|
||||
- [ ] 2.2 `pkg/config/config.go`:新增 `PollingConfig struct { VerboseLog bool }`,在 `Config` 中加入 `Polling PollingConfig` 字段
|
||||
- [ ] 2.3 `pkg/config/defaults/config.yaml`:在 polling 配置区新增 `verbose_log: false`
|
||||
- [ ] 2.4 验证:`LspDiagnostics` 检查 `pkg/constants/constants.go` 和 `pkg/config/config.go` 无报错
|
||||
- [x] 2.1 `pkg/constants/constants.go`:轮询任务类型常量块新增 `TaskTypePollingCardStatus = "polling:card_status"`
|
||||
- [x] 2.2 `pkg/config/config.go`:新增 `PollingConfig struct { VerboseLog bool }`,在 `Config` 中加入 `Polling PollingConfig` 字段
|
||||
- [x] 2.3 `pkg/config/defaults/config.yaml`:在 polling 配置区新增 `verbose_log: false`
|
||||
- [x] 2.4 验证:`LspDiagnostics` 检查 `pkg/constants/constants.go` 和 `pkg/config/config.go` 无报错
|
||||
|
||||
## 3. 数据模型更新
|
||||
|
||||
- [ ] 3.1 `internal/model/polling.go`:`PollingConfig` 结构体新增 `CardStatusCheckInterval *int`(GORM column: `card_status_check_interval`)
|
||||
- [ ] 3.2 `internal/model/iot_card.go`:`IotCard` 结构体新增 `LastCardStatusCheckAt *time.Time`(GORM column: `last_card_status_check_at`)
|
||||
- [ ] 3.3 验证:`LspDiagnostics` 检查两个 model 文件无报错
|
||||
- [x] 3.1 `internal/model/polling.go`:`PollingConfig` 结构体新增 `CardStatusCheckInterval *int`(GORM column: `card_status_check_interval`)
|
||||
- [x] 3.2 `internal/model/iot_card.go`:`IotCard` 结构体新增 `LastCardStatusCheckAt *time.Time`(GORM column: `last_card_status_check_at`)
|
||||
- [x] 3.3 验证:`LspDiagnostics` 检查两个 model 文件无报错
|
||||
|
||||
## 4. PollingBase 新增 verboseLog 支持
|
||||
|
||||
- [ ] 4.1 `internal/task/polling_base.go`:`PollingBase` 结构体新增 `verboseLog bool` 字段;`NewPollingBase` 函数签名新增 `verboseLog bool` 参数并赋值
|
||||
- [ ] 4.2 `cmd/worker/main.go`:`task.NewPollingBase(...)` 调用新增 `cfg.Polling.VerboseLog` 参数
|
||||
- [ ] 4.3 验证:`LspDiagnostics` 检查 `polling_base.go` 和 `main.go` 无报错
|
||||
- [x] 4.1 `internal/task/polling_base.go`:`PollingBase` 结构体新增 `verboseLog bool` 字段;`NewPollingBase` 函数签名新增 `verboseLog bool` 参数并赋值
|
||||
- [x] 4.2 `cmd/worker/main.go`:`task.NewPollingBase(...)` 调用新增 `cfg.Polling.VerboseLog` 参数
|
||||
- [x] 4.3 验证:`LspDiagnostics` 检查 `polling_base.go` 和 `main.go` 无报错
|
||||
|
||||
## 5. 现有 Handler 添加 verbose 日志
|
||||
|
||||
- [ ] 5.1 `internal/task/polling_realname_handler.go`:在 `QueryRealnameStatus` 成功返回后,判断 `h.base.verboseLog` 为 true 时输出 Info 日志(card_id、iccid、real_status、new_status、changed)
|
||||
- [ ] 5.2 `internal/task/polling_carddata_handler.go`:在 `QueryFlow` 成功返回后,判断 `h.base.verboseLog` 为 true 时输出 Info 日志(card_id、iccid、gateway_flow_mb、increment_mb、is_cross_month)
|
||||
- [ ] 5.3 `internal/task/polling_package_handler.go`:在 `freshCard` 加载成功后、调用 `EvaluateAndAct` 之前,判断 `h.base.verboseLog` 为 true 时输出 Info 日志(card_id、iccid、network_status、stop_reason)
|
||||
- [ ] 5.4 `internal/task/polling_protect_handler.go`:在 switch 块执行完毕、`UpdateFields(last_protect_check_at)` 之前,判断 `h.base.verboseLog` 为 true 时输出 Info 日志(card_id、iccid、stop_protect、start_protect、network_status)
|
||||
- [ ] 5.5 验证:`LspDiagnostics` 检查上述 4 个 handler 文件无报错
|
||||
- [x] 5.1 `internal/task/polling_realname_handler.go`:在 `QueryRealnameStatus` 成功返回后,判断 `h.base.verboseLog` 为 true 时输出 Info 日志(card_id、iccid、real_status、new_status、changed)
|
||||
- [x] 5.2 `internal/task/polling_carddata_handler.go`:在 `QueryFlow` 成功返回后,判断 `h.base.verboseLog` 为 true 时输出 Info 日志(card_id、iccid、gateway_flow_mb、increment_mb、is_cross_month)
|
||||
- [x] 5.3 `internal/task/polling_package_handler.go`:在 `freshCard` 加载成功后、调用 `EvaluateAndAct` 之前,判断 `h.base.verboseLog` 为 true 时输出 Info 日志(card_id、iccid、network_status、stop_reason)
|
||||
- [x] 5.4 `internal/task/polling_protect_handler.go`:在 switch 块执行完毕、`UpdateFields(last_protect_check_at)` 之前,判断 `h.base.verboseLog` 为 true 时输出 Info 日志(card_id、iccid、stop_protect、start_protect、network_status)
|
||||
- [x] 5.5 验证:`LspDiagnostics` 检查上述 4 个 handler 文件无报错
|
||||
|
||||
## 6. 卡状态轮询 Handler 实现
|
||||
|
||||
- [ ] 6.1 新建 `internal/task/polling_cardstatus_handler.go`:实现 `PollingCardStatusHandler`(字段:base、gateway、iotCardStore、stopResumeSvc),完整实现 `Handle` 方法
|
||||
- [ ] 6.2 `Handle` 方法逻辑:获取并发信号量 → 获取卡信息(带缓存)→ 调用 `gateway.QueryCardStatus` → 映射状态("停机"→0,其他→1)→ verbose log 判断 → 比较状态是否变化 → 写 DB(`last_card_status_check_at` + 可选 `network_status`)→ 状态变化时更新缓存并调用 `EvaluateAndAct` → 更新统计 → 重入队
|
||||
- [ ] 6.3 验证:`LspDiagnostics` 检查新文件无报错
|
||||
- [x] 6.1 新建 `internal/task/polling_cardstatus_handler.go`:实现 `PollingCardStatusHandler`(字段:base、gateway、iotCardStore、stopResumeSvc),完整实现 `Handle` 方法
|
||||
- [x] 6.2 `Handle` 方法逻辑:获取并发信号量 → 获取卡信息(带缓存)→ 调用 `gateway.QueryCardStatus` → 映射状态("停机"→0,其他→1)→ verbose log 判断 → 比较状态是否变化 → 写 DB(`last_card_status_check_at` + 可选 `network_status`)→ 状态变化时更新缓存并调用 `EvaluateAndAct` → 更新统计 → 重入队
|
||||
- [x] 6.3 验证:`LspDiagnostics` 检查新文件无报错
|
||||
|
||||
## 7. 轮询系统基础设施集成
|
||||
|
||||
- [ ] 7.1 `internal/task/polling_utils.go`:`getIntervalByTaskType` 新增 `case constants.TaskTypePollingCardStatus`,返回 `cfg.CardStatusCheckInterval`
|
||||
- [ ] 7.2 `internal/polling/queue_manager.go`:`allTaskTypes` slice 追加 `constants.TaskTypePollingCardStatus`
|
||||
- [ ] 7.3 `internal/polling/lifecycle_service.go`:`getEnabledTaskTypes` 新增 card_status 条件(`CardStatusCheckInterval != nil && *CardStatusCheckInterval > 0`);`calcInitialDelay` 新增对应 case
|
||||
- [ ] 7.4 `internal/polling/initializer.go`:在 `initBatch()` 的 if-block 链中新增一段,当 `cfg.CardStatusCheckInterval != nil && *cfg.CardStatusCheckInterval > 0` 时,以 `card.LastCardStatusCheckAt`(新增字段)为基准调用 `calculateNextCheckTime`,向 `TaskTypePollingCardStatus` 分片队列写入 ZAdd 条目;位置紧跟 ProtectCheckInterval 块之后,格式与其他 4 种类型一致
|
||||
- [ ] 7.5 验证:`LspDiagnostics` 检查上述 4 个文件无报错
|
||||
- [x] 7.1 `internal/task/polling_utils.go`:`getIntervalByTaskType` 新增 `case constants.TaskTypePollingCardStatus`,返回 `cfg.CardStatusCheckInterval`
|
||||
- [x] 7.2 `internal/polling/queue_manager.go`:`allTaskTypes` slice 追加 `constants.TaskTypePollingCardStatus`
|
||||
- [x] 7.3 `internal/polling/lifecycle_service.go`:`getEnabledTaskTypes` 新增 card_status 条件(`CardStatusCheckInterval != nil && *CardStatusCheckInterval > 0`);`calcInitialDelay` 新增对应 case
|
||||
- [x] 7.4 `internal/polling/initializer.go`:在 `initBatch()` 的 if-block 链中新增一段,当 `cfg.CardStatusCheckInterval != nil && *cfg.CardStatusCheckInterval > 0` 时,以 `card.LastCardStatusCheckAt`(新增字段)为基准调用 `calculateNextCheckTime`,向 `TaskTypePollingCardStatus` 分片队列写入 ZAdd 条目;位置紧跟 ProtectCheckInterval 块之后,格式与其他 4 种类型一致
|
||||
- [x] 7.5 验证:`LspDiagnostics` 检查上述 4 个文件无报错
|
||||
|
||||
## 8. Worker Handler 注册
|
||||
|
||||
- [ ] 8.1 `pkg/queue/handler.go`:在 `RegisterHandlers` 中创建 `cardStatusHandler := task.NewPollingCardStatusHandler(...)` 并注册 `h.mux.HandleFunc(constants.TaskTypePollingCardStatus, cardStatusHandler.Handle)`
|
||||
- [ ] 8.2 验证:`LspDiagnostics` 检查 `handler.go` 无报错;`go build ./cmd/worker/...` 成功编译
|
||||
- [x] 8.1 `pkg/queue/handler.go`:在 `RegisterHandlers` 中创建 `cardStatusHandler := task.NewPollingCardStatusHandler(...)` 并注册 `h.mux.HandleFunc(constants.TaskTypePollingCardStatus, cardStatusHandler.Handle)`
|
||||
- [x] 8.2 验证:`LspDiagnostics` 检查 `handler.go` 无报错;`go build ./cmd/worker/...` 成功编译
|
||||
|
||||
## 9. 整体验证
|
||||
|
||||
- [ ] 9.1 执行 `go build ./...` 确认整个项目编译通过
|
||||
- [ ] 9.2 使用 PostgreSQL MCP 验证迁移列已正确创建(类型、NULL 约束)
|
||||
- [ ] 9.3 检查 `make migrate-down && make migrate-up` 回滚和重新迁移均成功
|
||||
- [x] 9.1 执行 `go build ./...` 确认整个项目编译通过
|
||||
- [x] 9.2 使用 PostgreSQL MCP 验证迁移列已正确创建(类型、NULL 约束)
|
||||
- [x] 9.3 检查 `make migrate-down && make migrate-up` 回滚和重新迁移均成功
|
||||
Reference in New Issue
Block a user