All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m54s
- 重构 Worker 启动流程,引入 bootstrap 模块统一管理依赖注入 - 实现套餐流量重置服务(日/月/年周期重置) - 新增套餐激活排队、加油包绑定、囤货待实名激活逻辑 - 新增订单创建幂等性防重(Redis 业务键 + 分布式锁) - 更新 AGENTS.md/CLAUDE.md:新增注释规范、幂等性规范,移除测试要求 - 添加套餐系统升级完整文档(API文档、使用指南、功能总结、运维指南) - 归档 OpenSpec package-system-upgrade 变更,同步 specs 到主目录 - 新增 queue types 抽象和 Redis 常量定义
279 lines
5.8 KiB
Markdown
279 lines
5.8 KiB
Markdown
# 套餐系统升级 - 使用指南
|
||
|
||
## 场景一:囤货待实名激活
|
||
|
||
### 业务场景
|
||
|
||
代理商后台为未实名的卡/设备预先购买套餐,用户实名后自动激活。
|
||
|
||
### 操作流程
|
||
|
||
```
|
||
1. 代理商登录后台
|
||
2. 选择未实名的卡/设备
|
||
3. 购买套餐(选择支持实名激活的套餐)
|
||
4. 套餐状态:待激活(status=0, pending_realname_activation=true)
|
||
5. 用户完成实名认证
|
||
6. 系统自动激活套餐
|
||
```
|
||
|
||
### 前置条件
|
||
|
||
- 套餐必须启用 `enable_realname_activation=true`
|
||
- 卡/设备当前未实名
|
||
|
||
### 注意事项
|
||
|
||
- 囤货套餐在实名前不会计算有效期
|
||
- 实名后,有效期从激活日期开始计算
|
||
- 如果卡/设备已实名,套餐会立即激活
|
||
|
||
---
|
||
|
||
## 场景二:主套餐排队
|
||
|
||
### 业务场景
|
||
|
||
用户当前有生效中的主套餐,想提前购买下一个套餐。
|
||
|
||
### 操作流程
|
||
|
||
```
|
||
1. 用户购买新主套餐
|
||
2. 系统检测到已有生效中主套餐
|
||
3. 新套餐进入排队状态(status=0, priority=N+1)
|
||
4. 当前主套餐过期
|
||
5. 系统自动激活排队中的下一个套餐
|
||
```
|
||
|
||
### 排队规则
|
||
|
||
| 情况 | 新套餐状态 |
|
||
|------|-----------|
|
||
| 无生效中主套餐 | 立即激活(status=1, priority=1) |
|
||
| 有生效中主套餐 | 排队等待(status=0, priority=MAX+1) |
|
||
|
||
### 查看排队情况
|
||
|
||
```http
|
||
GET /api/h5/packages/my-usage
|
||
|
||
// 响应
|
||
{
|
||
"main_package": {
|
||
"package_name": "月度套餐",
|
||
"status": 1, // 生效中
|
||
"expires_at": "2025-03-31T23:59:59Z"
|
||
},
|
||
"queued_packages": [
|
||
{
|
||
"package_name": "季度套餐",
|
||
"status": 0, // 排队中
|
||
"priority": 2
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 场景三:加油包购买
|
||
|
||
### 业务场景
|
||
|
||
用户主套餐流量不够用,需要购买加油包补充流量。
|
||
|
||
### 操作流程
|
||
|
||
```
|
||
1. 确认用户有生效中或待生效的主套餐
|
||
2. 用户选择加油包
|
||
3. 系统自动绑定到当前主套餐
|
||
4. 加油包立即生效
|
||
5. 流量扣减时优先使用加油包
|
||
```
|
||
|
||
### 购买限制
|
||
|
||
| 限制项 | 说明 |
|
||
|-------|------|
|
||
| 必须有主套餐 | 无主套餐无法购买加油包 |
|
||
| 混买禁止 | 同一订单不能同时购买主套餐和加油包 |
|
||
|
||
### 加油包生命周期
|
||
|
||
```
|
||
主套餐过期 → 加油包自动失效(status=4)
|
||
```
|
||
|
||
### 加油包有效期
|
||
|
||
| 类型 | 有效期计算 |
|
||
|------|-----------|
|
||
| 随主套餐 | 与主套餐同时过期(has_independent_expiry=false) |
|
||
| 独立有效期 | 从购买日期开始计算(has_independent_expiry=true) |
|
||
|
||
---
|
||
|
||
## 场景四:流量查询
|
||
|
||
### 客户端查询我的流量
|
||
|
||
```http
|
||
GET /api/h5/packages/my-usage
|
||
Authorization: Bearer {token}
|
||
```
|
||
|
||
响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"main_package": {
|
||
"package_usage_id": 101,
|
||
"package_name": "月度套餐 30G",
|
||
"data_limit_mb": 30720,
|
||
"data_usage_mb": 15360,
|
||
"status": 1,
|
||
"activated_at": "2025-02-01T00:00:00Z",
|
||
"expires_at": "2025-02-28T23:59:59Z"
|
||
},
|
||
"addon_packages": [
|
||
{
|
||
"package_usage_id": 102,
|
||
"package_name": "加油包 5G",
|
||
"data_limit_mb": 5120,
|
||
"data_usage_mb": 2048,
|
||
"status": 1,
|
||
"priority": 2
|
||
}
|
||
],
|
||
"total": {
|
||
"total_mb": 35840,
|
||
"used_mb": 17408,
|
||
"remaining_mb": 18432
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 后台查询套餐流量详单
|
||
|
||
```http
|
||
GET /api/admin/package-usage/101/daily-records?start_date=2025-02-01&end_date=2025-02-15
|
||
Authorization: Bearer {token}
|
||
```
|
||
|
||
响应示例:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"package_usage_id": 101,
|
||
"package_name": "月度套餐 30G",
|
||
"records": [
|
||
{
|
||
"date": "2025-02-01",
|
||
"daily_usage_mb": 1024,
|
||
"cumulative_usage_mb": 1024
|
||
},
|
||
{
|
||
"date": "2025-02-02",
|
||
"daily_usage_mb": 512,
|
||
"cumulative_usage_mb": 1536
|
||
}
|
||
],
|
||
"total_usage_mb": 15360
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 套餐状态说明
|
||
|
||
| 状态码 | 名称 | 说明 |
|
||
|-------|------|------|
|
||
| 0 | 待生效 | 排队中或待实名激活 |
|
||
| 1 | 生效中 | 正在使用 |
|
||
| 2 | 已用完 | 流量已耗尽 |
|
||
| 3 | 已过期 | 超过有效期 |
|
||
| 4 | 已失效 | 主套餐过期导致加油包失效 |
|
||
|
||
---
|
||
|
||
## 流量重置说明
|
||
|
||
### 重置类型
|
||
|
||
| 类型 | 套餐类型 | 重置时间 | 适用场景 |
|
||
|------|---------|---------|---------|
|
||
| 日重置 | 所有 | 每天 00:00:00 | 日租卡 |
|
||
| 月重置 | 自然月 | 每月1号 00:00:00 | 自然月套餐 |
|
||
| 月重置 | 按天 | 从激活日起每30天 | 按天套餐 |
|
||
| 年重置 | 所有 | 每年1月1日 | 年度套餐 |
|
||
| 不重置 | 所有 | 不重置 | 一次性流量包 |
|
||
|
||
### 重置行为
|
||
|
||
```
|
||
重置前:data_usage_mb = 25600
|
||
重置后:data_usage_mb = 0
|
||
```
|
||
|
||
- 重置只清空已用流量,不影响有效期
|
||
- 流量用完的套餐(status=2)重置后恢复为生效中(status=1)
|
||
|
||
---
|
||
|
||
## 停复机说明
|
||
|
||
### 停机条件
|
||
|
||
所有生效套餐流量用完:
|
||
- 主套餐 status=2
|
||
- 所有加油包 status=2
|
||
|
||
### 复机条件
|
||
|
||
- 购买新套餐
|
||
- 套餐流量重置
|
||
- 排队套餐激活
|
||
|
||
### 停机记录
|
||
|
||
```sql
|
||
-- 停机记录在 tb_iot_card 表
|
||
stopped_at: 停机时间
|
||
stop_reason: 停机原因(如 "流量耗尽")
|
||
|
||
-- 复机后
|
||
resumed_at: 复机时间
|
||
```
|
||
|
||
---
|
||
|
||
## 常见问题
|
||
|
||
### Q: 为什么购买加油包提示"必须有主套餐"?
|
||
|
||
A: 加油包必须绑定到主套餐,请先购买主套餐再购买加油包。
|
||
|
||
### Q: 主套餐过期后加油包还能用吗?
|
||
|
||
A: 不能。主套餐过期后,绑定的加油包会自动失效(status=4)。
|
||
|
||
### Q: 套餐排队后可以取消吗?
|
||
|
||
A: 目前不支持取消排队中的套餐,请联系客服处理。
|
||
|
||
### Q: 流量重置后为什么还是停机状态?
|
||
|
||
A: 流量重置后系统会自动触发复机,如果仍是停机状态,请检查运营商接口是否正常。
|
||
|
||
### Q: H5 端未实名用户如何购买套餐?
|
||
|
||
A: H5 端必须先完成实名认证才能购买套餐。代理商可在后台为未实名用户囤货。
|