feat: 轮询系统重构(分片队列 + 停复机统一 + Handler 拆分)
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m46s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m46s
【核心变更】
1. 停复机逻辑统一(StopResumeService)
- 新增 EvaluateAndAct 统一入口,封装三条件停复机判断
- 停机条件:无套餐(no_package) / 流量耗尽(traffic_exhausted) / 未实名(not_realname)
- 复机条件:stop_reason 合规 + 有套餐且未耗尽 + 已实名或行业卡
- 修复设备套餐 Bug:hasValidPackage 按 device_id 查套餐,而非仅 iot_card_id
- 设备维度停复机加幂等锁(Redis SetNX,TTL 30s),防止多卡并发重复调 Gateway
2. Redis 分片队列(PollingQueueManager)
- 新建 queue_manager.go,封装所有轮询 Redis 操作
- 16 分片 Sorted Set,Key 格式:polling:shard:{shardID}:queue:{taskType}
- Lua 脚本原子出队(ZRANGEBYSCORE + 分批 ZREM),消除竞态窗口
- 新增背压检测:队列深度超 50 万时 Scheduler 跳过该分片
- RemoveFromAllQueues 覆盖 4 种任务类型(含 protect)
3. Handler 拆分(polling_handler.go 1360行 → 5个专注文件)
- polling_base.go:共享基类(并发控制/卡缓存/重入队)
- polling_realname_handler.go:实名采集,实名 0→1 时立即触发复机
- polling_carddata_handler.go:流量采集,保留跨月边界检测逻辑
- polling_package_handler.go:套餐采集,委托 EvaluateAndAct 决策
- polling_protect_handler.go:保护期一致性检查,保护期内强制修正
4. 配置管理(PollingConfigManager)
- 新建 config_manager.go,从 scheduler.go 提取配置职责
- 内存缓存 + 5 分钟定时刷新,刷新失败保留原缓存
- 修复 getCardCondition:停机卡返回 suspended,不再错配 activated 配置
5. 渐进式初始化(CardInitializer)
- 新建 initializer.go,分批加载(每批 10 万),批次间 sleep 500ms
- 过滤 enable_polling=false 的卡,初始化完成前 Scheduler 不出队
6. 卡生命周期服务(PollingLifecycleService)
- 新建 lifecycle_service.go,替代已删除的 callbacks.go 和 api_callback.go
- OnCardCreated/OnCardEnabled/OnCardStatusChanged 入队前检查 enable_polling
7. Scheduler 精简(1000+行 → 227行)
- 保留纯调度循环:scheduleLoop + processShardSchedule + enqueueBatch
- 保留每 10 秒触发套餐过期检测和流量重置
- 移除所有 DB 操作、配置加载、卡初始化逻辑
8. 轮询管控 API(enable_polling)
- 新增 PUT /api/admin/assets/:id/polling-status 接口
- 支持对设备/卡维度开关轮询,关闭后从所有分片队列移除
9. 数据库迁移
- 000103:tb_device 新增 enable_polling 字段(boolean, NOT NULL, DEFAULT true)
- 000104:新增 suspended 轮询配置,为 activated 配置补全 protect_check_interval
【文件统计】
- 新增:19 个文件(handler × 5、polling 组件 × 4、迁移 × 3 等)
- 修改:20 个文件(bootstrap 注入、store 接口、monitoring 适配分片等)
- 删除:3 个文件(polling_handler.go、callbacks.go、api_callback.go)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
97
.opencode/skills/openspec-api-contract/SKILL.md
Normal file
97
.opencode/skills/openspec-api-contract/SKILL.md
Normal file
@@ -0,0 +1,97 @@
|
||||
---
|
||||
name: openspec-api-contract
|
||||
description: OpenSpec API 契约规范。创建涉及接口的 OpenSpec 提案时使用。探索阶段提供业务与契约引导清单,提案文档要求 API 契约设计、错误码与完成标准等必填章节。
|
||||
---
|
||||
|
||||
# OpenSpec API 契约规范
|
||||
|
||||
**适用场景**:创建涉及 API/接口的 OpenSpec 提案时,探索和提案两个阶段均须遵守本规范。
|
||||
|
||||
---
|
||||
|
||||
## 一、探索阶段引导清单
|
||||
|
||||
在 `openspec-explore` 阶段,当内容涉及接口时,讨论必须覆盖以下所有维度。
|
||||
|
||||
### 业务与契约确认
|
||||
|
||||
**输入与输出**
|
||||
- 请求方是谁?用户类型(SuperAdmin/Platform/Agent/Enterprise/Personal)?
|
||||
- 输入参数:必填/选填字段、格式约束、参数来源(路径/查询/Body)?
|
||||
- 输出结构:哪些字段必须返回?是否需要分页?
|
||||
|
||||
**业务规则**
|
||||
- 核心业务规则与边界条件?
|
||||
- 是否涉及状态流转?状态机的完整定义?
|
||||
- 依赖外部服务时,外部异常的降级行为?
|
||||
|
||||
**权限与资源所有权**
|
||||
- 哪些用户类型可以访问?
|
||||
- 是否涉及跨用户/跨店铺/跨企业的资源访问?(需三层越权防护)
|
||||
- 资源所有权校验方式:`CanManageShop` / `CanManageEnterprise` / 自有资源?
|
||||
|
||||
**幂等性**
|
||||
- 操作类型:查询(天然幂等)/ 创建 / 更新 / 删除?
|
||||
- 写操作幂等策略:状态条件更新 / Redis 业务键防重 + 分布式锁 / 乐观锁(version)?
|
||||
- 异步任务是否需要任务锁?
|
||||
|
||||
**数据模型变更**
|
||||
- 是否需要新建表、修改现有表或数据回填?
|
||||
- 迁移策略:上线顺序、兼容旧数据的方式?
|
||||
- 是否影响 GORM Callback 自动数据权限过滤?
|
||||
|
||||
**错误码与异常语义**
|
||||
- 预期错误场景及对应错误码?
|
||||
- 错误响应是否泄露敏感信息?(参数校验失败统一返回 `CodeInvalidParam`)
|
||||
|
||||
---
|
||||
|
||||
## 二、提案文档必填章节
|
||||
|
||||
在 `openspec-propose` 生成的提案(`proposal.md` / `design.md`)中,涉及接口时以下内容**不可缺失**。
|
||||
|
||||
### API 契约设计
|
||||
|
||||
**接口定义**
|
||||
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
| Endpoint | `METHOD /api/{scope}/{resource}[/:id]` |
|
||||
| 请求参数 | 字段名、类型、必填/选填、说明 |
|
||||
| 响应结构 | `data` 字段的完整结构定义 |
|
||||
| 鉴权要求 | 允许的用户类型 |
|
||||
| 资源所有权 | 所有权校验方式 |
|
||||
|
||||
**列表接口额外要求**
|
||||
- 分页:`page` + `page_size`(默认 20,最大 100)
|
||||
- 排序:默认排序字段与方向
|
||||
- 过滤:支持的过滤条件列表
|
||||
|
||||
**错误码清单**
|
||||
|
||||
| 场景 | 错误码 | 说明 |
|
||||
|---|---|---|
|
||||
| 参数校验失败 | `CodeInvalidParam` | 统一返回,不泄露细节 |
|
||||
| 资源不存在/越权 | `CodeForbidden` | 不区分两者,防止信息泄露 |
|
||||
| (业务错误场景...) | (对应错误码) | (说明) |
|
||||
|
||||
### 完成标准
|
||||
|
||||
**最小验证步骤**(按顺序列出可操作的验证步骤)
|
||||
|
||||
1. (例:调用创建接口,验证返回 `code=0`)
|
||||
2. (例:查询接口确认数据存在且字段正确)
|
||||
3. (例:PostgreSQL MCP 查询确认数据库记录符合预期)
|
||||
|
||||
**影响范围说明**
|
||||
- 新增/修改的表:
|
||||
- 影响的现有接口:
|
||||
- 影响的权限与数据过滤范围:
|
||||
|
||||
---
|
||||
|
||||
## 约束(必须遵守)
|
||||
|
||||
- **优先复用现有架构与库**:不引入新依赖,错误码优先复用已有定义
|
||||
- **不做顺手重构**:提案范围严格限定在目标功能;发现可优化点,记录到 backlog 但不执行
|
||||
- **数据库设计**:禁止外键约束,禁止 GORM 关联标签,关联通过 ID 字段手动维护
|
||||
Reference in New Issue
Block a user