Files
huang c665f32976
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m54s
feat: 套餐系统升级 - Worker 重构、流量重置、文档与规范更新
- 重构 Worker 启动流程,引入 bootstrap 模块统一管理依赖注入
- 实现套餐流量重置服务(日/月/年周期重置)
- 新增套餐激活排队、加油包绑定、囤货待实名激活逻辑
- 新增订单创建幂等性防重(Redis 业务键 + 分布式锁)
- 更新 AGENTS.md/CLAUDE.md:新增注释规范、幂等性规范,移除测试要求
- 添加套餐系统升级完整文档(API文档、使用指南、功能总结、运维指南)
- 归档 OpenSpec package-system-upgrade 变更,同步 specs 到主目录
- 新增 queue types 抽象和 Redis 常量定义
2026-02-12 14:24:15 +08:00

5.8 KiB
Raw Permalink Blame History

套餐系统升级 - 功能总结

概述

本次升级实现了完整的套餐生命周期管理,支持主套餐排队激活、加油包绑定主套餐、囤货待实名激活、流量按优先级扣减等核心功能。

核心功能

1. 套餐有效期计算

类型 计算方式 示例
自然月 激活月份 + N 个月,月末 23:59:59 2月15日激活3个月 → 5月31日 23:59:59 过期
按天 激活日期 + N 天,当天 23:59:59 2月15日激活30天 → 3月16日 23:59:59 过期

2. 主套餐排队机制

卡/设备 购买主套餐 A → 立即激活status=1, priority=1
       购买主套餐 B → 排队等待status=0, priority=2
       主套餐 A 过期 → 自动激活主套餐 B
  • 同一卡/设备同时只能有一个生效中的主套餐
  • 新购买的主套餐自动进入排队状态
  • 过期检查每 10 秒执行一次

3. 加油包绑定主套餐

加油包必须绑定到当前生效的主套餐master_usage_id
├── 加油包与主套餐同时生效
├── 主套餐过期时加油包自动失效status=4
└── 流量扣减时,先扣加油包,再扣主套餐
  • 购买加油包时必须有生效中或待生效的主套餐
  • 加油包可设置独立有效期(has_independent_expiry=true

4. 囤货待实名激活

后台为未实名卡/设备购买套餐
├── 套餐 status=0, pending_realname_activation=true
├── 用户完成实名
├── 轮询系统检测到实名状态变更
└── 自动激活套餐status=1
  • 仅当套餐 enable_realname_activation=true 时触发此机制
  • H5 端未实名用户无法直接购买套餐

5. 流量扣减优先级

扣减顺序:加油包(按 priority ASC→ 主套餐

// 示例:卡有 3 个生效套餐
主套餐1000MB已用 500MB
加油包1100MB已用 0MBpriority=2
加油包2200MB已用 50MBpriority=3

// 本次使用 180MB
扣减顺序
1. 加油包1  100MB用完status=2
2. 加油包2  80MB
3. 主套餐不变

6. 流量重置周期

周期 套餐类型 重置时间 说明
日重置 所有 每天 00:00:00 data_reset_cycle=daily
月重置 自然月 每月1号 00:00:00 calendar_type=natural_month
月重置 按天 从激活日期起每30天 calendar_type=by_day
年重置 所有 每年1月1日 00:00:00 data_reset_cycle=yearly

7. 停复机机制

  • 停机条件:所有生效套餐流量用完(主套餐 + 所有加油包 status=2
  • 复机条件:购买新套餐或套餐激活后自动复机

数据库变更

新增表

表名 说明
tb_package_usage_daily_record 套餐流量日记录
tb_card_daily_usage 卡每日流量使用汇总

扩展字段

tb_package 表

  • calendar_type: 有效期类型natural_month/by_day
  • data_reset_cycle: 流量重置周期daily/monthly/yearly/none
  • enable_realname_activation: 是否需要实名后激活
  • duration_days: 按天套餐的有效天数

tb_package_usage 表

  • priority: 套餐优先级
  • master_usage_id: 主套餐 ID加油包使用
  • has_independent_expiry: 加油包是否有独立有效期
  • pending_realname_activation: 是否待实名激活
  • data_reset_cycle: 流量重置周期
  • last_reset_at: 上次重置时间
  • next_reset_at: 下次重置时间

tb_iot_card 表

  • stopped_at: 停机时间
  • resumed_at: 复机时间
  • stop_reason: 停机原因

tb_carrier 表

  • billing_day: 运营商计费日(用于流量查询接口的计费周期计算,联通=27其他=1

API 端点

新增端点

端点 方法 说明
/api/h5/packages/my-usage GET 客户端查询我的流量使用情况
/api/admin/package-usage/:id/daily-records GET 查询套餐流量详单

扩展端点

套餐管理 API 支持新字段:

  • calendar_type: 有效期类型
  • duration_days: 有效天数
  • data_reset_cycle: 重置周期
  • enable_realname_activation: 实名激活开关

轮询任务

任务 调度频率 说明
套餐激活检查 每 10 秒 检查过期主套餐,激活排队套餐
流量重置调度 每 10 秒 执行日/月/年流量重置
实名状态检查 配置化 检测首次实名,触发套餐激活

Asynq 任务

任务类型 说明
task:package:first_activation 首次实名激活套餐
task:package:queue_activation 排队主套餐激活

错误码

错误码 说明
CodePackageActivationConflict 套餐正在激活中
CodeNoMainPackage 必须有主套餐才能购买加油包
CodeRealnameRequired 必须先完成实名认证才能购买套餐
CodeMixedOrderForbidden 同订单不能同时购买正式套餐和加油包

技术实现

Service 层

服务 文件 职责
ActivationService activation_service.go 套餐激活(实名激活、排队激活)
UsageService usage_service.go 流量扣减、停机检查
ResetService reset_service.go 流量重置(日/月/年)
CustomerViewService customer_view_service.go 客户端流量查询
DailyRecordService daily_record_service.go 套餐流量详单
StopResumeService stop_resume_service.go 停复机操作

工具函数

函数 说明
CalculateExpiryTime() 计算套餐过期时间
CalculateNextResetTime() 计算下次重置时间

性能优化

  • 流量重置分批处理:每批最多 10000 条
  • 使用 Redis 分布式锁避免套餐激活并发问题
  • Asynq 任务重试策略MaxRetry(3), Timeout(30s)