Files
junhong_cmp_fiber/docs/package-system-upgrade/功能总结.md
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

184 lines
5.8 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.
# 套餐系统升级 - 功能总结
## 概述
本次升级实现了完整的套餐生命周期管理,支持主套餐排队激活、加油包绑定主套餐、囤货待实名激活、流量按优先级扣减等核心功能。
## 核心功能
### 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→ 主套餐**
```go
// 示例:卡有 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)