Files
junhong_cmp_fiber/openspec/changes/add-card-device-series-bindng/design.md
huang 79c061b6fa
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m24s
feat: 实现套餐管理模块,包含套餐系列、双状态管理、废弃模型清理
- 新增套餐系列管理 (CRUD + 状态切换)
- 新增套餐管理 (CRUD + 启用/禁用 + 上架/下架双状态)
- 清理 8 个废弃分佣模型及对应数据库表
- Package 模型新增建议成本价、建议售价、上架状态字段
- 完整的 Store/Service/Handler 三层实现
- 包含单元测试和集成测试
- 归档 add-package-module change
- 新增多个 OpenSpec changes (订单支付、店铺套餐分配、一次性分佣、卡设备系列绑定)
2026-01-27 19:55:47 +08:00

124 lines
4.2 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.
## Context
Phase 2 完成了代理套餐分配机制,但卡和设备还没有关联到具体的套餐系列。本期在 IotCard 和 Device 模型上新增字段,记录其所属的套餐系列分配,为后续的套餐购买和佣金计算做准备。
**关键业务规则**
- 卡/设备关联后才能购买该系列下的套餐
- 设备关联后,其绑定的所有卡共享该套餐系列(设备级套餐)
- 每张卡/设备只能触发一次一次性佣金
## Goals / Non-Goals
**Goals:**
- 在 IotCard 模型新增套餐系列关联和佣金状态字段
- 在 Device 模型新增相同字段
- 提供批量设置卡/设备套餐系列的 API
- 验证关联的系列必须是当前店铺被分配的
**Non-Goals:**
- 不实现订单支付Phase 4
- 不实现佣金计算Phase 5
- 不自动同步设备和卡的关联(手动设置)
## Decisions
### 1. 新增字段设计
**决策**:在 IotCard 和 Device 模型各新增 3 个字段
```go
// IotCard 新增字段
SeriesAllocationID uint `gorm:"column:series_allocation_id;index;comment:套餐系列分配ID" json:"series_allocation_id"`
FirstCommissionPaid bool `gorm:"column:first_commission_paid;default:false;comment:一次性佣金是否已发放" json:"first_commission_paid"`
AccumulatedRecharge int64 `gorm:"column:accumulated_recharge;type:bigint;default:0;comment:累计充值金额(分)" json:"accumulated_recharge"`
// Device 新增字段(相同)
SeriesAllocationID uint `gorm:"column:series_allocation_id;index;comment:套餐系列分配ID" json:"series_allocation_id"`
FirstCommissionPaid bool `gorm:"column:first_commission_paid;default:false;comment:一次性佣金是否已发放" json:"first_commission_paid"`
AccumulatedRecharge int64 `gorm:"column:accumulated_recharge;type:bigint;default:0;comment:累计充值金额(分)" json:"accumulated_recharge"`
```
**理由**
- `series_allocation_id`:关联到 ShopSeriesAllocation决定可购买的套餐
- `first_commission_paid`:标记一次性佣金状态,防止重复发放
- `accumulated_recharge`:累计充值金额,用于累计充值触发条件
### 2. 设备与卡的关系
**决策**:设备和卡独立设置套餐系列
```
场景 1单卡销售
- IotCard.series_allocation_id 有值
- 购买套餐时使用卡的 series_allocation_id
场景 2设备销售整机出货
- Device.series_allocation_id 有值
- 设备下的卡可以不设置 series_allocation_id
- 购买套餐时优先使用 Device.series_allocation_id
```
**理由**
- 单卡和设备是两种不同的销售模式
- 设备级套餐购买时,所有卡共享流量
- 佣金按设备计算,不按卡数倍增
### 3. 批量设置 API 设计
**决策**:使用 PATCH 方法批量更新
```
PATCH /api/admin/iot-cards/series-bindng
Body: { "iccids": ["xxx", "yyy"], "series_allocation_id": 123 }
PATCH /api/admin/devices/series-bindng
Body: { "device_ids": [1, 2, 3], "series_allocation_id": 123 }
```
**理由**
- PATCH 语义合适(部分更新)
- 支持批量操作提高效率
- 通过 ICCID/设备 ID 定位资源
### 4. 权限验证
**决策**:只能关联当前店铺被分配的套餐系列
验证逻辑:
1. 获取卡/设备的 shop_id
2. 检查 series_allocation_id 对应的分配是否属于该店铺
3. 检查分配状态是否启用
**理由**
- 防止关联未被分配的系列
- 确保数据一致性
## Risks / Trade-offs
### 风险 1批量操作性能
**风险**:大批量设置时可能超时
**缓解**
- 限制单次批量数量(如最多 500 条)
- 使用批量更新 SQL 而非循环单条更新
### 风险 2设备和卡关联不一致
**风险**:设备设置了系列但卡没设置,或反过来
**缓解**
- 购买套餐时明确优先级:设备级 > 卡级
- 查询接口明确返回实际使用的系列来源
## Open Questions
1. **是否需要清除关联功能?**
- 当前设计:可以将 series_allocation_id 设为 0 清除关联
- 待确认:清除后是否影响已购买的套餐?
2. **设备和卡的 accumulated_recharge 如何同步?**
- 当前设计:设备级购买时更新 Device.accumulated_recharge
- 单卡购买时更新 IotCard.accumulated_recharge
- 待确认:是否需要双向同步?