Files
junhong_cmp_fiber/.opencode/skills/comment-standards/SKILL.md
huang 434a8b0349
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m46s
feat: 轮询系统重构(分片队列 + 停复机统一 + Handler 拆分)
【核心变更】

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>
2026-04-07 12:27:04 +08:00

4.2 KiB
Raw Blame History

name, description
name description
comment-standards Go 注释规范。编写 Go 代码注释、文档注释时使用。包含包注释、结构体注释、接口注释、函数注释、内联注释的完整规范与示例。

Go 注释规范

基本原则

  • 所有注释使用中文
  • 导出符号必须有文档注释(包、函数、方法、类型、接口、常量、变量)
  • 复杂逻辑必须有实现注释(解释"为什么",而不是"做了什么"
  • 禁止废话注释(不要用注释复述代码本身)
  • 修改代码时必须同步更新注释

包注释

每个包的入口文件(通常是主文件或 doc.go)必须有包注释:

// Package account 提供账号管理的业务逻辑服务
// 包含账号创建、修改、删除、权限分配等功能
package account

结构体注释

所有导出结构体必须有文档注释,说明该结构体代表什么:

// Service 账号业务服务
// 负责账号的 CRUD、角色分配、密码管理等业务逻辑
type Service struct {
    store        *Store
    auditService AuditServiceInterface
}

接口注释

导出接口必须注释接口用途,每个方法必须说明契约:

// PermissionChecker 权限检查器接口
// 用于查询用户的权限列表
type PermissionChecker interface {
    // CheckPermission 检查用户是否拥有指定权限
    // userID: 用户ID
    // permCode: 权限编码(格式: module:action
    // platform: 端口类型 (all/web/h5)
    CheckPermission(ctx context.Context, userID uint, permCode string, platform string) (bool, error)
}

函数和方法注释

导出函数/方法必须以函数名开头,说明功能:

// Create 创建账号
// POST /api/admin/accounts
func (h *AccountHandler) Create(c *fiber.Ctx) error {

复杂方法(超过 30 行或包含复杂业务逻辑)必须额外说明实现思路:

// ActivateByRealname 首次实名激活套餐
// 当用户完成实名认证后,自动激活处于"囤货待实名"状态的套餐:
// 1. 查找该卡所有 status=3待实名激活的套餐
// 2. 按创建时间排序第一个主套餐立即激活status=1
// 3. 其余主套餐进入排队状态status=4
// 4. 加油包如果绑定了已激活的主套餐则一并激活
func (s *UsageService) ActivateByRealname(ctx context.Context, cardID uint) error {

未导出函数/方法

  • 简单逻辑(< 15 行):可以不加注释
  • 复杂逻辑(≥ 15 行)或非显而易见的算法:必须加注释
// buildPermissionTree 递归构建权限树
// 采用 map 索引 + 单次遍历算法,时间复杂度 O(n)
func (s *Service) buildPermissionTree(permissions []*model.Permission) []*dto.PermissionTreeNode {

常量和枚举注释

分组常量必须有组注释,每个值必须有行内注释:

// 用户类型常量
const (
    UserTypeSuperAdmin = 1 // 超级管理员
    UserTypePlatform   = 2 // 平台用户
    UserTypeAgent      = 3 // 代理账号
    UserTypeEnterprise = 4 // 企业账号
)

内联注释规范

必须添加内联注释的场景

场景 要求
复杂条件判断 解释判断的业务含义
多步骤业务流程 用编号注释标明每一步
非显而易见的设计决策 解释"为什么这样做"而不是"做了什么"
缓存/事务/并发处理 说明策略和原因
临时方案/兼容逻辑 标注 TODO 或说明背景

好的内联注释(解释为什么)

// 使用 Redis 分布式锁防止并发重复创建,锁超时 10 秒
if !s.acquireLock(ctx, lockKey, 10*time.Second) {
    return errors.New(errors.CodeTooManyRequests, "操作过于频繁,请稍后重试")
}

// 先冻结佣金再扣款,保证资金安全(失败时佣金自动解冻)
if err := s.freezeCommission(ctx, tx, orderID); err != nil {
    return err
}

废话注释(禁止)

// 获取用户ID          ← 禁止:代码本身已经很清楚
userID := middleware.GetUserIDFromContext(ctx)

// 创建账号            ← 禁止:变量名已说明意图
account := &model.Account{}

// 返回错误            ← 禁止return err 不需要注释
return err