refactor: 一次性佣金配置从套餐级别提升到系列级别
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m29s

主要变更:
- 新增 tb_shop_series_allocation 表,存储系列级别的一次性佣金配置
- ShopPackageAllocation 移除 one_time_commission_amount 字段
- PackageSeries 新增 enable_one_time_commission 字段控制是否启用一次性佣金
- 新增 /api/admin/shop-series-allocations CRUD 接口
- 佣金计算逻辑改为从 ShopSeriesAllocation 获取一次性佣金金额
- 删除废弃的 ShopSeriesOneTimeCommissionTier 模型
- OpenAPI Tag '系列分配' 和 '单套餐分配' 合并为 '套餐分配'

迁移脚本:
- 000042: 重构佣金套餐模型
- 000043: 简化佣金分配
- 000044: 一次性佣金分配重构
- 000045: PackageSeries 添加 enable_one_time_commission 字段

测试:
- 新增验收测试 (shop_series_allocation, commission_calculation)
- 新增流程测试 (one_time_commission_chain)
- 删除过时的单元测试(已被验收测试覆盖)
This commit is contained in:
2026-02-04 14:28:44 +08:00
parent fba8e9e76b
commit b18ecfeb55
106 changed files with 9899 additions and 6608 deletions

View File

@@ -2,18 +2,16 @@ package dto
// CreatePackageRequest 创建套餐请求
type CreatePackageRequest struct {
PackageCode string `json:"package_code" validate:"required,min=1,max=100" required:"true" minLength:"1" maxLength:"100" description:"套餐编码"`
PackageName string `json:"package_name" validate:"required,min=1,max=255" required:"true" minLength:"1" maxLength:"255" description:"套餐名称"`
SeriesID *uint `json:"series_id" validate:"omitempty" description:"套餐系列ID"`
PackageType string `json:"package_type" validate:"required,oneof=formal addon" required:"true" description:"套餐类型 (formal:正式套餐, addon:附加套餐)"`
DurationMonths int `json:"duration_months" validate:"required,min=1,max=120" required:"true" minimum:"1" maximum:"120" description:"套餐时长(月数)"`
DataType *string `json:"data_type" validate:"omitempty,oneof=real virtual" description:"流量类型 (real:真流量, virtual:虚流量)"`
RealDataMB *int64 `json:"real_data_mb" validate:"omitempty,min=0" minimum:"0" description:"流量额度(MB)"`
VirtualDataMB *int64 `json:"virtual_data_mb" validate:"omitempty,min=0" minimum:"0" description:"虚流量额度(MB)"`
DataAmountMB *int64 `json:"data_amount_mb" validate:"omitempty,min=0" minimum:"0" description:"总流量额度(MB)"`
Price int64 `json:"price" validate:"required,min=0" required:"true" minimum:"0" description:"套餐价格(分)"`
SuggestedCostPrice *int64 `json:"suggested_cost_price" validate:"omitempty,min=0" minimum:"0" description:"建议成本价(分)"`
SuggestedRetailPrice *int64 `json:"suggested_retail_price" validate:"omitempty,min=0" minimum:"0" description:"建议售价(分)"`
PackageCode string `json:"package_code" validate:"required,min=1,max=100" required:"true" minLength:"1" maxLength:"100" description:"套餐编码"`
PackageName string `json:"package_name" validate:"required,min=1,max=255" required:"true" minLength:"1" maxLength:"255" description:"套餐名称"`
SeriesID *uint `json:"series_id" validate:"omitempty" description:"套餐系列ID"`
PackageType string `json:"package_type" validate:"required,oneof=formal addon" required:"true" description:"套餐类型 (formal:正式套餐, addon:附加套餐)"`
DurationMonths int `json:"duration_months" validate:"required,min=1,max=120" required:"true" minimum:"1" maximum:"120" description:"套餐时长(月数)"`
RealDataMB *int64 `json:"real_data_mb" validate:"omitempty,min=0" minimum:"0" description:"真流量额度(MB)"`
VirtualDataMB *int64 `json:"virtual_data_mb" validate:"omitempty,min=0" minimum:"0" description:"流量额度(MB)"`
EnableVirtualData bool `json:"enable_virtual_data" description:"是否启用虚流量"`
SuggestedRetailPrice *int64 `json:"suggested_retail_price" validate:"omitempty,min=0" minimum:"0" description:"建议售价(分)"`
CostPrice int64 `json:"cost_price" validate:"required,min=0" required:"true" minimum:"0" description:"成本价(分)"`
}
// UpdatePackageRequest 更新套餐请求
@@ -22,13 +20,11 @@ type UpdatePackageRequest struct {
SeriesID *uint `json:"series_id" validate:"omitempty" description:"套餐系列ID"`
PackageType *string `json:"package_type" validate:"omitempty,oneof=formal addon" description:"套餐类型 (formal:正式套餐, addon:附加套餐)"`
DurationMonths *int `json:"duration_months" validate:"omitempty,min=1,max=120" minimum:"1" maximum:"120" description:"套餐时长(月数)"`
DataType *string `json:"data_type" validate:"omitempty,oneof=real virtual" description:"流量类型 (real:真流量, virtual:虚流量)"`
RealDataMB *int64 `json:"real_data_mb" validate:"omitempty,min=0" minimum:"0" description:"真流量额度(MB)"`
VirtualDataMB *int64 `json:"virtual_data_mb" validate:"omitempty,min=0" minimum:"0" description:"虚流量额度(MB)"`
DataAmountMB *int64 `json:"data_amount_mb" validate:"omitempty,min=0" minimum:"0" description:"总流量额度(MB)"`
Price *int64 `json:"price" validate:"omitempty,min=0" minimum:"0" description:"套餐价格(分)"`
SuggestedCostPrice *int64 `json:"suggested_cost_price" validate:"omitempty,min=0" minimum:"0" description:"建议成本价(分)"`
EnableVirtualData *bool `json:"enable_virtual_data" description:"是否启用虚流量"`
SuggestedRetailPrice *int64 `json:"suggested_retail_price" validate:"omitempty,min=0" minimum:"0" description:"建议售价(分)"`
CostPrice *int64 `json:"cost_price" validate:"omitempty,min=0" minimum:"0" description:"成本价(分)"`
}
// PackageListRequest 套餐列表请求
@@ -61,28 +57,26 @@ type CommissionTierInfo struct {
// PackageResponse 套餐响应
type PackageResponse struct {
ID uint `json:"id" description:"套餐ID"`
PackageCode string `json:"package_code" description:"套餐编码"`
PackageName string `json:"package_name" description:"套餐名称"`
SeriesID *uint `json:"series_id" description:"套餐系列ID"`
SeriesName *string `json:"series_name" description:"套餐系列名称"`
PackageType string `json:"package_type" description:"套餐类型 (formal:正式套餐, addon:附加套餐)"`
DurationMonths int `json:"duration_months" description:"套餐时长(月数)"`
DataType string `json:"data_type" description:"流量类型 (real:真流量, virtual:虚流量)"`
RealDataMB int64 `json:"real_data_mb" description:"流量额度(MB)"`
VirtualDataMB int64 `json:"virtual_data_mb" description:"虚流量额度(MB)"`
DataAmountMB int64 `json:"data_amount_mb" description:"总流量额度(MB)"`
Price int64 `json:"price" description:"套餐价格(分)"`
SuggestedCostPrice int64 `json:"suggested_cost_price" description:"建议成本价(分)"`
SuggestedRetailPrice int64 `json:"suggested_retail_price" description:"建议售价(分)"`
Status int `json:"status" description:"状态 (1:启用, 2:禁用)"`
ShelfStatus int `json:"shelf_status" description:"上架状态 (1:上架, 2:下架)"`
CreatedAt string `json:"created_at" description:"创建时间"`
UpdatedAt string `json:"updated_at" description:"更新时间"`
CostPrice *int64 `json:"cost_price,omitempty" description:"成本价(分,仅代理用户可见)"`
ProfitMargin *int64 `json:"profit_margin,omitempty" description:"利润空间(分,仅代理用户可见)"`
CurrentCommissionRate string `json:"current_commission_rate,omitempty" description:"当前返佣比例(仅代理用户可见)"`
TierInfo *CommissionTierInfo `json:"tier_info,omitempty" description:"梯度返佣信息(仅代理用户可见)"`
ID uint `json:"id" description:"套餐ID"`
PackageCode string `json:"package_code" description:"套餐编码"`
PackageName string `json:"package_name" description:"套餐名称"`
SeriesID *uint `json:"series_id" description:"套餐系列ID"`
SeriesName *string `json:"series_name" description:"套餐系列名称"`
PackageType string `json:"package_type" description:"套餐类型 (formal:正式套餐, addon:附加套餐)"`
DurationMonths int `json:"duration_months" description:"套餐时长(月数)"`
RealDataMB int64 `json:"real_data_mb" description:"流量额度(MB)"`
VirtualDataMB int64 `json:"virtual_data_mb" description:"流量额度(MB)"`
EnableVirtualData bool `json:"enable_virtual_data" description:"是否启用虚流量"`
SuggestedRetailPrice int64 `json:"suggested_retail_price" description:"建议售价(分)"`
CostPrice int64 `json:"cost_price" description:"成本价(分)"`
OneTimeCommissionAmount *int64 `json:"one_time_commission_amount,omitempty" description:"一次性佣金金额(分,代理视角)"`
Status int `json:"status" description:"状态 (1:启用, 2:禁用)"`
ShelfStatus int `json:"shelf_status" description:"上架状态 (1:上架, 2:下架)"`
CreatedAt string `json:"created_at" description:"创建时间"`
UpdatedAt string `json:"updated_at" description:"更新时间"`
ProfitMargin *int64 `json:"profit_margin,omitempty" description:"利润空间(分,仅代理用户可见)"`
CurrentCommissionRate string `json:"current_commission_rate,omitempty" description:"当前返佣比例(仅代理用户可见)"`
TierInfo *CommissionTierInfo `json:"tier_info,omitempty" description:"梯度返佣信息(仅代理用户可见)"`
}
// UpdatePackageParams 更新套餐聚合参数