feat: 实现卡和设备的套餐系列绑定功能
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m37s

- 添加 Device 和 IotCard 模型的 SeriesID 字段
- 实现 DeviceService 和 IotCardService 的套餐系列绑定逻辑
- 添加 DeviceStore 和 IotCardStore 的数据库操作方法
- 更新 API 接口和路由支持套餐系列绑定
- 创建数据库迁移脚本(000027_add_series_binding_fields)
- 添加完整的单元测试和集成测试
- 更新 OpenAPI 文档
- 归档 OpenSpec 变更文档

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-01-28 19:49:45 +08:00
parent 1da680a790
commit a945a4f554
38 changed files with 2906 additions and 318 deletions

View File

@@ -89,7 +89,7 @@ type OrderItem struct {
**理由**
- 简化首期实现,所有终端用户统一售价
- 代理的利润 = suggested_retail_price - 成本价
- 代理的利润来自返佣(基础返佣 + 一次性佣金)
- 后续如需支持代理自定义售价,可扩展 ShopPackageAllocation 增加 retail_price 字段
**非首期功能**
@@ -98,6 +98,38 @@ type OrderItem struct {
---
### 3.1 佣金配置版本快照
**决策**:订单创建时快照当时的佣金配置版本
**新增字段**
```go
type Order struct {
// ... 现有字段
// 🆕 佣金配置版本快照
CommissionConfigVersion int `gorm:"column:commission_config_version;comment:佣金配置版本"`
}
```
**理由**
- 佣金配置可能随时调整(基础返佣、一次性佣金等)
- 订单创建时锁定配置版本,确保历史订单的佣金计算依据可追溯
- 使用 ShopSeriesAllocationConfig 表查询特定版本的配置
**查询示例**
```go
// 订单创建时
config := allocationConfigStore.GetEffective(allocationID, time.Now())
order.CommissionConfigVersion = config.Version
// 佣金计算时
config := allocationConfigStore.GetByVersion(allocationID, order.CommissionConfigVersion)
// 使用 config 中的返佣配置计算佣金
```
---
### 4. 购买权限验证
**决策**:多层验证
@@ -151,13 +183,14 @@ func ValidatePurchase(card/device, packageID) error {
### 6. 套餐生效逻辑
**决策**:创建 PackageUsage 记录
**决策**:创建 PackageUsage 记录 + 更新销售统计
```go
func ActivatePackage(order *Order) {
for _, item := range order.Items {
pkg := GetPackage(item.PackageID)
// 1. 创建套餐使用记录
usage := &PackageUsage{
OrderID: order.ID,
PackageID: item.PackageID,
@@ -170,10 +203,22 @@ func ActivatePackage(order *Order) {
Status: 1, // 生效中
}
CreatePackageUsage(usage)
// 2. 🆕 更新销售统计(用于一次性佣金的梯度判断)
allocationID := GetAllocationIDByPackage(order, item.PackageID)
if allocationID > 0 {
commissionStatsService.UpdateStats(ctx, allocationID, "all_time", 1, item.Amount)
}
}
}
```
**关键说明**
- 套餐生效后,更新 ShopSeriesCommissionStats 表
- 统计维度allocationID该系列分配的累计销量和销售额
- 统计类型:"all_time" 表示永久累计(不按周期重置)
- 一次性佣金的梯度判断依赖此统计数据
### 7. API 设计
```