feat: 实现套餐管理模块,包含套餐系列、双状态管理、废弃模型清理
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m24s

- 新增套餐系列管理 (CRUD + 状态切换)
- 新增套餐管理 (CRUD + 启用/禁用 + 上架/下架双状态)
- 清理 8 个废弃分佣模型及对应数据库表
- Package 模型新增建议成本价、建议售价、上架状态字段
- 完整的 Store/Service/Handler 三层实现
- 包含单元测试和集成测试
- 归档 add-package-module change
- 新增多个 OpenSpec changes (订单支付、店铺套餐分配、一次性分佣、卡设备系列绑定)
This commit is contained in:
2026-01-27 19:55:47 +08:00
parent 30a0717316
commit 79c061b6fa
70 changed files with 7554 additions and 244 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-01-27

View File

@@ -0,0 +1,217 @@
## Context
Phase 1 完成了套餐系列和套餐的基础管理,但代理商还不能分销套餐。本期实现代理套餐分配机制,使上级代理能够:
1. 为下级店铺分配可销售的套餐系列
2. 通过加价模式设置下级的成本价
3. 配置梯度佣金(基于销量/销售额的阶梯奖励)
**当前代理层级结构**
- 店铺通过 `Shop.parent_id` 维护层级关系
- 最多 7 级代理
- 数据权限通过 `GetSubordinateShopIDs()` 递归查询
## Goals / Non-Goals
**Goals:**
- 实现套餐系列级别的分配机制
- 支持固定金额和百分比两种加价模式
- 支持梯度佣金配置(月度/季度/年度/自定义时间范围)
- 代理能查看自己被分配的套餐及成本价
- 可选的单套餐级别成本价覆盖
**Non-Goals:**
- 不实现卡/设备的套餐系列关联Phase 3
- 不实现订单支付流程Phase 4
- 不实现佣金计算逻辑Phase 5
- 不支持跨级分配(只能分配给直属下级)
## Decisions
### 1. 分配模型设计
**决策**:三个独立模型
```go
// ShopSeriesAllocation 店铺套餐系列分配
type ShopSeriesAllocation struct {
gorm.Model
BaseModel
ShopID uint // 被分配的店铺 ID
SeriesID uint // 套餐系列 ID
AllocatorShopID uint // 分配者店铺 ID上级
PricingMode string // 加价模式: fixed-固定金额 percent-百分比
PricingValue int64 // 加价值(分或千分比)
OneTimeCommissionTrigger string // 一次性佣金触发类型: one_time_recharge-单次充值 accumulated_recharge-累计充值
OneTimeCommissionThreshold int64 // 一次性佣金触发阈值(分)
OneTimeCommissionAmount int64 // 一次性佣金金额(分)
Status int // 状态 1-启用 2-禁用
}
// ShopSeriesCommissionTier 梯度佣金配置
type ShopSeriesCommissionTier struct {
gorm.Model
BaseModel
AllocationID uint // 关联的分配 ID
TierType string // 梯度类型: sales_count-销量 sales_amount-销售额
PeriodType string // 周期类型: monthly-月度 quarterly-季度 yearly-年度 custom-自定义
PeriodStartDate *time.Time // 自定义周期开始日期
PeriodEndDate *time.Time // 自定义周期结束日期
ThresholdValue int64 // 阈值(销量或金额)
CommissionAmount int64 // 佣金金额(分)
}
// ShopPackageAllocation 店铺单套餐分配(可选覆盖)
type ShopPackageAllocation struct {
gorm.Model
BaseModel
ShopID uint // 被分配的店铺 ID
PackageID uint // 套餐 ID
AllocationID uint // 关联的系列分配 ID
CostPrice int64 // 覆盖的成本价(分)
Status int // 状态 1-启用 2-禁用
}
```
**理由**
- 系列级别分配是主要方式,减少配置工作量
- 单套餐分配用于特殊场景(如某个套餐给特定代理优惠价)
- 梯度佣金独立模型,支持多档配置
### 2. 加价模式与成本价计算
**决策**:成本价 = 上级成本价 + 加价值
```
# 固定金额加价
下级成本价 = 上级成本价 + pricing_value
# 百分比加价pricing_value 为千分比,如 100 = 10%
下级成本价 = 上级成本价 × (1 + pricing_value / 1000)
```
**理由**
- 基于上级成本价加价,确保每级都有利润空间
- 千分比精度满足业务需求0.1% 精度)
- 平台作为顶级,其成本价 = Package.suggested_cost_price
**约束**
- 下级成本价 ≥ 上级成本价(禁止负加价)
- 验证时需递归获取上级成本价
### 3. 成本价获取逻辑
**决策**:递归查询 + 缓存
```go
func GetCostPrice(shopID, packageID uint) int64 {
// 1. 检查是否有单套餐覆盖
if override := GetPackageAllocation(shopID, packageID); override != nil {
return override.CostPrice
}
// 2. 获取系列分配
allocation := GetSeriesAllocation(shopID, package.SeriesID)
if allocation == nil {
return 0 // 未分配,不可购买
}
// 3. 获取上级成本价
parentCostPrice := GetParentCostPrice(allocation.AllocatorShopID, packageID)
// 4. 计算当前成本价
return CalculatePrice(parentCostPrice, allocation.PricingMode, allocation.PricingValue)
}
```
**理由**
- 单套餐覆盖优先级最高
- 递归到平台级别时,使用 Package.suggested_cost_price
- 可考虑缓存热点套餐的成本价(后续优化)
### 4. 梯度佣金周期计算
**决策**:支持固定周期和自定义周期
| PeriodType | 计算方式 |
|------------|----------|
| monthly | 当月 1 日 00:00 至月末 23:59:59 |
| quarterly | 当季度第一天至最后一天 |
| yearly | 当年 1 月 1 日至 12 月 31 日 |
| custom | PeriodStartDate 至 PeriodEndDate |
**理由**
- 固定周期覆盖常见场景
- 自定义周期支持促销活动等特殊需求
### 5. API 设计
**决策**RESTful + 嵌套资源
```
# 套餐系列分配
POST /api/admin/shop-series-allocations 为下级分配系列
GET /api/admin/shop-series-allocations 查询分配列表
GET /api/admin/shop-series-allocations/:id 分配详情
PUT /api/admin/shop-series-allocations/:id 更新分配
DELETE /api/admin/shop-series-allocations/:id 删除分配
PATCH /api/admin/shop-series-allocations/:id/status 启用/禁用
# 梯度佣金(嵌套在分配下)
POST /api/admin/shop-series-allocations/:id/tiers 添加梯度
GET /api/admin/shop-series-allocations/:id/tiers 梯度列表
PUT /api/admin/shop-series-allocations/:id/tiers/:tierId 更新梯度
DELETE /api/admin/shop-series-allocations/:id/tiers/:tierId 删除梯度
# 单套餐分配
POST /api/admin/shop-package-allocations 分配单套餐
GET /api/admin/shop-package-allocations 查询列表
PUT /api/admin/shop-package-allocations/:id 更新
DELETE /api/admin/shop-package-allocations/:id 删除
# 代理可售套餐
GET /api/admin/my-packages 查询我的可售套餐
GET /api/admin/my-packages/:id 套餐详情(含成本价)
```
## Risks / Trade-offs
### 风险 1递归成本价计算性能
**风险**:多级代理场景下,递归查询成本价可能较慢
**缓解**
- 首期不做缓存,观察实际性能
- 如有问题,后续增加 Redis 缓存(按 shop_id + package_id 缓存)
- 缓存失效策略:分配变更时清除相关缓存
### 风险 2分配一致性
**风险**:上级删除分配后,下级的分配关系如何处理
**缓解**
- 删除分配时检查是否有下级依赖
- 如有下级依赖,禁止删除或级联禁用
- 本期采用禁止删除策略,要求先清理下级分配
### 风险 3梯度佣金统计复杂度
**风险**:统计周期内的销量/销售额可能涉及大量数据
**缓解**
- 佣金计算在 Phase 5 实现
- 可考虑定时任务预计算周期统计数据
- 本期只做配置,不做实际统计
## Open Questions
1. **是否支持批量分配?**
- 当前设计:单个分配
- 待确认:是否需要批量为多个下级分配同一系列?
2. **分配删除策略?**
- 当前设计:有下级依赖时禁止删除
- 待确认:是否需要级联删除或级联禁用?
3. **梯度佣金是否可叠加?**
- 当前设计:达到最高档位只拿最高档佣金
- 待确认:是否需要累加所有达标档位的佣金?

View File

@@ -0,0 +1,61 @@
## Why
Phase 1 完成了套餐基础模块,但代理商还不能分销套餐。需要实现代理套餐分配机制:上级代理为下级分配套餐系列,设置成本价(通过加价模式计算),并支持梯度佣金配置。代理只能看到和销售被分配的套餐。
## What Changes
**新增模型:**
- `ShopSeriesAllocation`:店铺套餐系列分配,记录哪个店铺被分配了哪个套餐系列、成本价加价模式、**一次性佣金触发配置**
- `ShopSeriesCommissionTier`:梯度佣金配置,基于销量/销售额设置不同的阶梯奖励金额
- `ShopPackageAllocation`:店铺单套餐分配(可选),用于覆盖系列级别的成本价设置
**新增 API**
- 为下级店铺分配套餐系列(设置加价模式)
- 查询店铺的套餐系列分配列表
- 更新/删除套餐系列分配
- 配置梯度佣金(按系列)
- 为下级店铺分配单个套餐(覆盖成本价)
- 代理查看自己可销售的套餐列表(含成本价)
**业务规则:**
- 加价模式:固定金额加价 或 百分比加价(基于上级成本价)
- 代理给下级设置的成本价 ≥ 自己的成本价(不可亏本)
- 梯度佣金支持时间范围配置(月度/季度/年度/自定义)
- 套餐系列分配是主要方式,单套餐分配用于特殊覆盖
## Capabilities
### New Capabilities
- `shop-series-allocation`: 店铺套餐系列分配 - 为下级店铺分配套餐系列,设置加价模式计算成本价
- `shop-commission-tier`: 梯度佣金配置 - 基于销量/销售额配置不同档位的一次性佣金
- `shop-package-allocation`: 店铺单套餐分配 - 可选的单套餐级别成本价覆盖
- `agent-available-packages`: 代理可售套餐查询 - 代理查看自己被分配的套餐及成本价
### Modified Capabilities
<!-- 无 -->
## Impact
**代码影响:**
- `internal/model/` - 新增 3 个模型文件
- `migrations/` - 创建 3 个新表
- `internal/handler/admin/` - 新增分配管理 Handler
- `internal/service/` - 新增分配管理 Service
- `internal/store/postgres/` - 新增 3 个 Store
- `internal/model/dto/` - 新增请求/响应 DTO
- `internal/bootstrap/` - 注册新组件
- `internal/router/` - 注册新路由
**API 影响:**
- 新增 `/api/admin/shop-series-allocations/*` 路由组
- 新增 `/api/admin/shop-package-allocations/*` 路由组
- 新增 `/api/admin/my-packages` 代理可售套餐查询
**数据库影响:**
- 新增表:`tb_shop_series_allocation`, `tb_shop_series_commission_tier`, `tb_shop_package_allocation`
**依赖关系:**
- 依赖 Phase 1add-package-module完成
- Phase 3卡/设备关联)依赖本期

View File

@@ -0,0 +1,65 @@
## ADDED Requirements
### Requirement: 查询代理可售套餐列表
系统 SHALL 允许代理查询自己被分配的所有套餐。结果 MUST 包含套餐信息和代理的成本价。支持按套餐系列筛选、按套餐类型筛选。
#### Scenario: 查询所有可售套餐
- **WHEN** 代理查询可售套餐列表
- **THEN** 系统返回该代理被分配的所有套餐系列下的启用且上架的套餐
#### Scenario: 响应包含成本价
- **WHEN** 代理查询可售套餐
- **THEN** 每个套餐包含:套餐信息、建议售价、代理成本价、利润空间
#### Scenario: 按系列筛选
- **WHEN** 代理指定套餐系列 ID 筛选
- **THEN** 系统只返回该系列下的套餐
#### Scenario: 只返回可售套餐
- **WHEN** 代理查询可售套餐
- **THEN** 系统只返回状态为启用(1)且上架状态为上架(1)的套餐
---
### Requirement: 查询代理可售套餐详情
系统 SHALL 允许代理查询单个套餐的详细信息,包含完整的价格信息。
#### Scenario: 查询可售套餐详情
- **WHEN** 代理查询指定套餐的详情
- **THEN** 系统返回套餐完整信息,包含:成本价、建议售价、价格来源(系列加价/单套餐覆盖)
#### Scenario: 查询未分配的套餐
- **WHEN** 代理查询一个未被分配的套餐详情
- **THEN** 系统返回 "您没有该套餐的销售权限" 错误
---
### Requirement: 成本价计算优先级
系统计算代理成本价时 MUST 遵循以下优先级:
1. 单套餐覆盖价(如果存在且启用)
2. 系列级别加价计算
#### Scenario: 存在单套餐覆盖
- **WHEN** 代理查询一个有覆盖价的套餐
- **THEN** 成本价使用覆盖价,价格来源标记为 "单套餐覆盖"
#### Scenario: 使用系列加价
- **WHEN** 代理查询一个无覆盖价的套餐
- **THEN** 成本价 = 上级成本价 + 加价值,价格来源标记为 "系列加价"
---
### Requirement: 查询代理被分配的套餐系列
系统 SHALL 允许代理查询自己被分配的套餐系列列表。
#### Scenario: 查询被分配的系列
- **WHEN** 代理查询自己的套餐系列分配
- **THEN** 系统返回所有分配给该代理的套餐系列(启用状态的)
#### Scenario: 响应包含系列下套餐数量
- **WHEN** 代理查询被分配的系列
- **THEN** 每个系列包含:系列信息、可售套餐数量、加价模式信息

View File

@@ -0,0 +1,77 @@
## ADDED Requirements
### Requirement: 配置梯度佣金
系统 SHALL 允许代理为套餐系列分配配置梯度佣金。每个梯度包含:梯度类型(销量/销售额)、周期类型、阈值、佣金金额。
#### Scenario: 添加销量梯度佣金
- **WHEN** 代理为分配添加梯度:类型=销量,周期=月度,阈值=100佣金=5000分
- **THEN** 系统创建梯度配置,当下级月销量达到 100 时可获得 50 元佣金
#### Scenario: 添加销售额梯度佣金
- **WHEN** 代理添加梯度:类型=销售额,周期=季度,阈值=100000分佣金=10000分
- **THEN** 系统创建梯度配置,当下级季度销售额达到 1000 元时可获得 100 元佣金
#### Scenario: 配置自定义周期
- **WHEN** 代理添加梯度,周期类型=自定义,指定开始和结束日期
- **THEN** 系统创建梯度配置,统计指定日期范围内的数据
#### Scenario: 添加多个梯度档位
- **WHEN** 代理为同一分配添加多个梯度100件=50元200件=120元500件=350元
- **THEN** 系统创建多个梯度记录,支持阶梯奖励
---
### Requirement: 查询梯度佣金配置
系统 SHALL 提供梯度佣金配置的查询功能,按分配 ID 查询。
#### Scenario: 查询分配的梯度配置
- **WHEN** 代理查询指定分配的梯度配置
- **THEN** 系统返回该分配下的所有梯度配置,按阈值升序排列
#### Scenario: 分配无梯度配置
- **WHEN** 代理查询一个没有配置梯度的分配
- **THEN** 系统返回空列表
---
### Requirement: 更新梯度佣金配置
系统 SHALL 允许代理更新梯度配置的阈值和佣金金额。
#### Scenario: 更新梯度阈值
- **WHEN** 代理将梯度阈值从 100 改为 150
- **THEN** 系统更新梯度记录
#### Scenario: 更新梯度佣金金额
- **WHEN** 代理将佣金金额从 5000 改为 6000
- **THEN** 系统更新梯度记录
---
### Requirement: 删除梯度佣金配置
系统 SHALL 允许代理删除梯度配置。
#### Scenario: 删除梯度配置
- **WHEN** 代理删除指定的梯度配置
- **THEN** 系统软删除该梯度记录
---
### Requirement: 梯度佣金周期类型
系统 MUST 支持以下周期类型:
- monthly月度当月 1 日至月末)
- quarterly季度当季第一天至最后一天
- yearly年度1 月 1 日至 12 月 31 日)
- custom自定义指定开始和结束日期
#### Scenario: 月度周期
- **WHEN** 配置月度周期的梯度
- **THEN** 统计范围为当月 1 日 00:00:00 至月末 23:59:59
#### Scenario: 自定义周期必填日期
- **WHEN** 代理选择自定义周期但未提供开始或结束日期
- **THEN** 系统返回参数验证错误

View File

@@ -0,0 +1,65 @@
## ADDED Requirements
### Requirement: 为下级店铺分配单个套餐
系统 SHALL 允许代理为下级店铺的特定套餐设置覆盖成本价。此功能用于对单个套餐给予特殊定价,优先级高于系列级别的加价计算。
#### Scenario: 成功分配单套餐覆盖价
- **WHEN** 代理为下级的某个套餐设置覆盖成本价 8000 分
- **THEN** 系统创建单套餐分配记录,该下级购买此套餐时成本价为 8000 分(不再使用系列加价计算)
#### Scenario: 覆盖价低于上级成本价
- **WHEN** 代理尝试设置的覆盖价低于自己的成本价
- **THEN** 系统返回错误 "覆盖价不能低于您的成本价"
#### Scenario: 套餐未在系列分配中
- **WHEN** 代理尝试为一个未分配系列下的套餐设置覆盖价
- **THEN** 系统返回错误 "该套餐的系列未分配给此店铺"
---
### Requirement: 查询单套餐分配列表
系统 SHALL 提供单套餐分配的查询功能,支持按店铺、套餐、状态筛选。
#### Scenario: 查询店铺的单套餐分配
- **WHEN** 代理查询指定店铺的单套餐分配列表
- **THEN** 系统返回该店铺的所有单套餐覆盖配置
#### Scenario: 查询结果包含套餐信息
- **WHEN** 代理查询单套餐分配列表
- **THEN** 响应包含套餐名称、套餐编码、原计算成本价、覆盖成本价
---
### Requirement: 更新单套餐分配
系统 SHALL 允许代理更新单套餐分配的覆盖成本价。
#### Scenario: 更新覆盖成本价
- **WHEN** 代理将覆盖成本价从 8000 改为 7500
- **THEN** 系统更新记录,下级的该套餐成本价变为 7500
---
### Requirement: 删除单套餐分配
系统 SHALL 允许代理删除单套餐分配。删除后恢复使用系列级别的加价计算。
#### Scenario: 删除单套餐覆盖
- **WHEN** 代理删除单套餐分配记录
- **THEN** 系统软删除记录,下级的该套餐成本价恢复为系列加价计算值
---
### Requirement: 单套餐分配状态管理
系统 SHALL 允许代理启用/禁用单套餐分配。禁用后恢复使用系列级别价格。
#### Scenario: 禁用单套餐覆盖
- **WHEN** 代理禁用单套餐分配
- **THEN** 该套餐暂时使用系列级别的加价计算
#### Scenario: 启用单套餐覆盖
- **WHEN** 代理启用已禁用的单套餐分配
- **THEN** 该套餐恢复使用覆盖成本价

View File

@@ -0,0 +1,103 @@
## ADDED Requirements
### Requirement: 为下级店铺分配套餐系列
系统 SHALL 允许代理为其直属下级店铺分配套餐系列。分配时 MUST 指定加价模式(固定金额或百分比)和加价值。可选配置一次性佣金触发条件(触发类型、阈值、金额)。分配者只能分配自己已被分配的套餐系列。
#### Scenario: 成功分配套餐系列
- **WHEN** 代理为直属下级店铺分配一个自己拥有的套餐系列,设置固定金额加价 1000 分
- **THEN** 系统创建分配记录,下级成本价 = 上级成本价 + 1000
#### Scenario: 百分比加价分配
- **WHEN** 代理设置百分比加价模式,加价值为 10010%
- **THEN** 系统创建分配记录,下级成本价 = 上级成本价 × 1.1
#### Scenario: 尝试分配未拥有的系列
- **WHEN** 代理尝试分配自己未被分配的套餐系列
- **THEN** 系统返回错误 "您没有该套餐系列的分配权限"
#### Scenario: 尝试分配给非直属下级
- **WHEN** 代理尝试分配给非直属下级店铺
- **THEN** 系统返回错误 "只能为直属下级分配套餐"
#### Scenario: 重复分配同一系列
- **WHEN** 代理尝试为同一下级店铺重复分配同一套餐系列
- **THEN** 系统返回错误 "该店铺已分配此套餐系列"
#### Scenario: 配置一次性佣金触发条件
- **WHEN** 代理分配时设置一次性佣金触发类型为"单次充值",阈值 30000 分,金额 5000 分
- **THEN** 系统创建分配记录,下级的卡/设备在单次充值 ≥ 300 元时可获得 50 元一次性佣金
#### Scenario: 配置累计充值触发条件
- **WHEN** 代理分配时设置一次性佣金触发类型为"累计充值",阈值 50000 分,金额 8000 分
- **THEN** 系统创建分配记录,下级的卡/设备在累计充值 ≥ 500 元时可获得 80 元一次性佣金
---
### Requirement: 查询套餐系列分配列表
系统 SHALL 提供分配列表查询,支持按下级店铺筛选、按套餐系列筛选、按状态筛选。结果 MUST 包含计算后的成本价。
#### Scenario: 查询所有分配
- **WHEN** 代理查询分配列表,不带筛选条件
- **THEN** 系统返回该代理创建的所有分配记录
#### Scenario: 按店铺筛选
- **WHEN** 代理指定下级店铺 ID 筛选
- **THEN** 系统只返回该店铺的分配记录
#### Scenario: 响应包含成本价
- **WHEN** 代理查询分配列表
- **THEN** 每条记录包含计算后的下级成本价
---
### Requirement: 更新套餐系列分配
系统 SHALL 允许代理更新分配的加价模式和加价值。更新后下级的成本价 MUST 同步变化。
#### Scenario: 更新加价值
- **WHEN** 代理将加价值从 1000 改为 2000
- **THEN** 系统更新分配记录,下级成本价相应增加
#### Scenario: 更新不存在的分配
- **WHEN** 代理更新不存在的分配 ID
- **THEN** 系统返回 "分配记录不存在" 错误
---
### Requirement: 删除套餐系列分配
系统 SHALL 允许代理删除分配记录。如果有下级依赖此分配MUST 禁止删除。
#### Scenario: 成功删除无依赖的分配
- **WHEN** 代理删除一个没有下级依赖的分配记录
- **THEN** 系统软删除该记录
#### Scenario: 尝试删除有下级依赖的分配
- **WHEN** 代理尝试删除一个已被下级使用的分配(下级基于此分配又分配给了更下级)
- **THEN** 系统返回错误 "存在下级依赖,无法删除"
---
### Requirement: 启用/禁用套餐系列分配
系统 SHALL 允许代理切换分配的启用状态。禁用后下级 MUST NOT 能使用该分配购买套餐。
#### Scenario: 禁用分配
- **WHEN** 代理将分配状态设为禁用
- **THEN** 系统更新状态,下级无法基于此分配购买套餐
#### Scenario: 启用分配
- **WHEN** 代理将禁用的分配设为启用
- **THEN** 系统更新状态,下级可以继续使用
---
### Requirement: 平台分配套餐系列
平台管理员 SHALL 能够为一级代理分配套餐系列。平台的成本价基准为 Package.suggested_cost_price。
#### Scenario: 平台为一级代理分配
- **WHEN** 平台管理员为一级代理分配套餐系列
- **THEN** 系统创建分配记录,一级代理成本价 = suggested_cost_price + 加价值

View File

@@ -0,0 +1,167 @@
## 1. 新增模型
- [ ] 1.1 创建 `internal/model/shop_series_allocation.go`,定义 ShopSeriesAllocation 模型shop_id, series_id, allocator_shop_id, pricing_mode, pricing_value, one_time_commission_trigger, one_time_commission_threshold, one_time_commission_amount, status
- [ ] 1.2 创建 `internal/model/shop_series_commission_tier.go`,定义 ShopSeriesCommissionTier 模型allocation_id, tier_type, period_type, period_start_date, period_end_date, threshold_value, commission_amount
- [ ] 1.3 创建 `internal/model/shop_package_allocation.go`,定义 ShopPackageAllocation 模型shop_id, package_id, allocation_id, cost_price, status
## 2. 数据库迁移
- [ ] 2.1 创建迁移文件,创建 tb_shop_series_allocation 表
- [ ] 2.2 创建 tb_shop_series_commission_tier 表
- [ ] 2.3 创建 tb_shop_package_allocation 表
- [ ] 2.4 添加必要的索引shop_id, series_id, allocation_id
- [ ] 2.5 本地执行迁移验证
## 3. 套餐系列分配 DTO
- [ ] 3.1 创建 `internal/model/dto/shop_series_allocation.go`,定义 CreateShopSeriesAllocationRequest含 one_time_commission_trigger, one_time_commission_threshold, one_time_commission_amount 可选字段)
- [ ] 3.2 定义 UpdateShopSeriesAllocationRequest
- [ ] 3.3 定义 ShopSeriesAllocationListRequest支持 shop_id, series_id, status 筛选)
- [ ] 3.4 定义 UpdateStatusRequest
- [ ] 3.5 定义 ShopSeriesAllocationResponse包含计算后的成本价
## 4. 梯度佣金 DTO
- [ ] 4.1 定义 CreateCommissionTierRequesttier_type, period_type, period_start_date, period_end_date, threshold_value, commission_amount
- [ ] 4.2 定义 UpdateCommissionTierRequest
- [ ] 4.3 定义 CommissionTierResponse
## 5. 单套餐分配 DTO
- [ ] 5.1 创建 `internal/model/dto/shop_package_allocation.go`,定义 CreateShopPackageAllocationRequest
- [ ] 5.2 定义 UpdateShopPackageAllocationRequest
- [ ] 5.3 定义 ShopPackageAllocationListRequest
- [ ] 5.4 定义 ShopPackageAllocationResponse
## 6. 代理可售套餐 DTO
- [ ] 6.1 定义 MyPackageListRequestseries_id, package_type 筛选)
- [ ] 6.2 定义 MyPackageResponse包含成本价、建议售价、价格来源
- [ ] 6.3 定义 MySeriesAllocationResponse
## 7. 套餐系列分配 Store
- [ ] 7.1 创建 `internal/store/postgres/shop_series_allocation_store.go`,实现 Create 方法
- [ ] 7.2 实现 GetByID 方法
- [ ] 7.3 实现 GetByShopAndSeries 方法(检查重复分配)
- [ ] 7.4 实现 Update 方法
- [ ] 7.5 实现 Delete 方法
- [ ] 7.6 实现 List 方法(支持分页和筛选)
- [ ] 7.7 实现 UpdateStatus 方法
- [ ] 7.8 实现 HasDependentAllocations 方法(检查下级依赖)
- [ ] 7.9 实现 GetByShopID 方法(获取店铺的所有分配)
## 8. 梯度佣金 Store
- [ ] 8.1 创建 `internal/store/postgres/shop_series_commission_tier_store.go`,实现 Create 方法
- [ ] 8.2 实现 GetByID 方法
- [ ] 8.3 实现 Update 方法
- [ ] 8.4 实现 Delete 方法
- [ ] 8.5 实现 ListByAllocationID 方法
## 9. 单套餐分配 Store
- [ ] 9.1 创建 `internal/store/postgres/shop_package_allocation_store.go`,实现 Create 方法
- [ ] 9.2 实现 GetByID 方法
- [ ] 9.3 实现 GetByShopAndPackage 方法
- [ ] 9.4 实现 Update 方法
- [ ] 9.5 实现 Delete 方法
- [ ] 9.6 实现 List 方法
- [ ] 9.7 实现 UpdateStatus 方法
## 10. 套餐系列分配 Service
- [ ] 10.1 创建 `internal/service/shop_series_allocation/service.go`,实现 Create 方法(验证权限、检查重复、计算成本价)
- [ ] 10.2 实现 Get 方法
- [ ] 10.3 实现 Update 方法
- [ ] 10.4 实现 Delete 方法(检查下级依赖)
- [ ] 10.5 实现 List 方法
- [ ] 10.6 实现 UpdateStatus 方法
- [ ] 10.7 实现 GetParentCostPrice 辅助方法(递归获取上级成本价)
- [ ] 10.8 实现 CalculateCostPrice 辅助方法(根据加价模式计算)
## 11. 梯度佣金 Service
- [ ] 11.1 在 shop_series_allocation service 中实现 AddTier 方法
- [ ] 11.2 实现 UpdateTier 方法
- [ ] 11.3 实现 DeleteTier 方法
- [ ] 11.4 实现 ListTiers 方法
## 12. 单套餐分配 Service
- [ ] 12.1 创建 `internal/service/shop_package_allocation/service.go`,实现 Create 方法(验证系列已分配、验证成本价)
- [ ] 12.2 实现 Get 方法
- [ ] 12.3 实现 Update 方法
- [ ] 12.4 实现 Delete 方法
- [ ] 12.5 实现 List 方法
- [ ] 12.6 实现 UpdateStatus 方法
## 13. 代理可售套餐 Service
- [ ] 13.1 创建 `internal/service/my_package/service.go`,实现 ListMyPackages 方法(获取可售套餐列表)
- [ ] 13.2 实现 GetMyPackage 方法(获取单个套餐详情含成本价)
- [ ] 13.3 实现 ListMySeriesAllocations 方法(获取被分配的系列)
- [ ] 13.4 实现 GetCostPrice 核心方法(成本价计算,考虑优先级)
## 14. 套餐系列分配 Handler
- [ ] 14.1 创建 `internal/handler/admin/shop_series_allocation.go`,实现 Create 接口
- [ ] 14.2 实现 Get 接口
- [ ] 14.3 实现 Update 接口
- [ ] 14.4 实现 Delete 接口
- [ ] 14.5 实现 List 接口
- [ ] 14.6 实现 UpdateStatus 接口
- [ ] 14.7 实现 AddTier 接口
- [ ] 14.8 实现 UpdateTier 接口
- [ ] 14.9 实现 DeleteTier 接口
- [ ] 14.10 实现 ListTiers 接口
## 15. 单套餐分配 Handler
- [ ] 15.1 创建 `internal/handler/admin/shop_package_allocation.go`,实现 Create 接口
- [ ] 15.2 实现 Get 接口
- [ ] 15.3 实现 Update 接口
- [ ] 15.4 实现 Delete 接口
- [ ] 15.5 实现 List 接口
- [ ] 15.6 实现 UpdateStatus 接口
## 16. 代理可售套餐 Handler
- [ ] 16.1 创建 `internal/handler/admin/my_package.go`,实现 ListMyPackages 接口
- [ ] 16.2 实现 GetMyPackage 接口
- [ ] 16.3 实现 ListMySeriesAllocations 接口
## 17. Bootstrap 注册
- [ ] 17.1 在 stores.go 中注册 ShopSeriesAllocationStore, ShopSeriesCommissionTierStore, ShopPackageAllocationStore
- [ ] 17.2 在 services.go 中注册 ShopSeriesAllocationService, ShopPackageAllocationService, MyPackageService
- [ ] 17.3 在 handlers.go 中注册 ShopSeriesAllocationHandler, ShopPackageAllocationHandler, MyPackageHandler
## 18. 路由注册
- [ ] 18.1 注册 `/api/admin/shop-series-allocations` 路由组
- [ ] 18.2 注册 `/api/admin/shop-series-allocations/:id/tiers` 嵌套路由
- [ ] 18.3 注册 `/api/admin/shop-package-allocations` 路由组
- [ ] 18.4 注册 `/api/admin/my-packages` 路由
- [ ] 18.5 注册 `/api/admin/my-series-allocations` 路由
## 19. 文档生成器更新
- [ ] 19.1 在 docs.go 和 gendocs/main.go 中添加新 Handler
- [ ] 19.2 执行文档生成验证
## 20. 测试
- [ ] 20.1 ShopSeriesAllocationStore 单元测试
- [ ] 20.2 ShopPackageAllocationStore 单元测试
- [ ] 20.3 ShopSeriesAllocationService 单元测试(覆盖权限验证、成本价计算)
- [ ] 20.4 MyPackageService 单元测试(覆盖成本价优先级)
- [ ] 20.5 套餐系列分配 API 集成测试
- [ ] 20.6 代理可售套餐 API 集成测试
- [ ] 20.7 执行 `go test ./...` 确认通过
## 21. 最终验证
- [ ] 21.1 执行 `go build ./...` 确认编译通过
- [ ] 21.2 启动服务,手动测试分配流程
- [ ] 21.3 验证成本价计算逻辑正确