Files
junhong_cmp_fiber/internal/model/package.go
huang b18ecfeb55
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m29s
refactor: 一次性佣金配置从套餐级别提升到系列级别
主要变更:
- 新增 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)
- 删除过时的单元测试(已被验收测试覆盖)
2026-02-04 14:28:44 +08:00

141 lines
7.3 KiB
Go
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.
package model
import (
"encoding/json"
"time"
"gorm.io/gorm"
)
// PackageSeries 套餐系列模型
// 套餐的分组,用于一次性分佣规则配置
type PackageSeries struct {
gorm.Model
BaseModel `gorm:"embedded"`
SeriesCode string `gorm:"column:series_code;type:varchar(100);uniqueIndex:idx_package_series_code,where:deleted_at IS NULL;not null;comment:系列编码" json:"series_code"`
SeriesName string `gorm:"column:series_name;type:varchar(255);not null;comment:系列名称" json:"series_name"`
Description string `gorm:"column:description;type:text;comment:描述" json:"description"`
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
OneTimeCommissionConfigJSON string `gorm:"column:one_time_commission_config;type:jsonb;default:'{}';comment:一次性佣金规则配置" json:"-"`
EnableOneTimeCommission bool `gorm:"column:enable_one_time_commission;default:false;comment:是否启用一次性佣金顶层字段支持SQL索引" json:"enable_one_time_commission"`
}
// TableName 指定表名
func (PackageSeries) TableName() string {
return "tb_package_series"
}
// Package 套餐模型
// 只适用于 IoT 卡,支持真流量/虚流量共存机制
type Package struct {
gorm.Model
BaseModel `gorm:"embedded"`
PackageCode string `gorm:"column:package_code;type:varchar(100);uniqueIndex:idx_package_code,where:deleted_at IS NULL;not null;comment:套餐编码" json:"package_code"`
PackageName string `gorm:"column:package_name;type:varchar(255);not null;comment:套餐名称" json:"package_name"`
SeriesID uint `gorm:"column:series_id;index;comment:套餐系列ID" json:"series_id"`
PackageType string `gorm:"column:package_type;type:varchar(50);not null;comment:套餐类型 formal-正式套餐 addon-附加套餐" json:"package_type"`
DurationMonths int `gorm:"column:duration_months;type:int;not null;comment:套餐时长(月数) 1-月套餐 12-年套餐" json:"duration_months"`
RealDataMB int64 `gorm:"column:real_data_mb;type:bigint;default:0;comment:真流量额度(MB)" json:"real_data_mb"`
VirtualDataMB int64 `gorm:"column:virtual_data_mb;type:bigint;default:0;comment:虚流量额度(MB,用于停机判断)" json:"virtual_data_mb"`
EnableVirtualData bool `gorm:"column:enable_virtual_data;type:boolean;default:false;not null;comment:是否启用虚流量" json:"enable_virtual_data"`
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-启用 2-禁用" json:"status"`
CostPrice int64 `gorm:"column:cost_price;type:bigint;default:0;comment:成本价(分为单位)" json:"cost_price"`
SuggestedRetailPrice int64 `gorm:"column:suggested_retail_price;type:bigint;default:0;comment:建议售价(分为单位)" json:"suggested_retail_price"`
ShelfStatus int `gorm:"column:shelf_status;type:int;default:2;not null;comment:上架状态 1-上架 2-下架" json:"shelf_status"`
}
// TableName 指定表名
func (Package) TableName() string {
return "tb_package"
}
// PackageUsage 套餐使用情况模型
// 跟踪单卡套餐和设备级套餐的流量使用
type PackageUsage struct {
gorm.Model
BaseModel `gorm:"embedded"`
OrderID uint `gorm:"column:order_id;index;not null;comment:订单ID" json:"order_id"`
PackageID uint `gorm:"column:package_id;index;not null;comment:套餐ID" json:"package_id"`
UsageType string `gorm:"column:usage_type;type:varchar(20);not null;comment:使用类型 single_card-单卡套餐 device-设备级套餐" json:"usage_type"`
IotCardID uint `gorm:"column:iot_card_id;index;comment:IoT卡ID(单卡套餐时有值)" json:"iot_card_id"`
DeviceID uint `gorm:"column:device_id;index;comment:设备ID(设备级套餐时有值)" json:"device_id"`
DataLimitMB int64 `gorm:"column:data_limit_mb;type:bigint;not null;comment:流量限额(MB)" json:"data_limit_mb"`
DataUsageMB int64 `gorm:"column:data_usage_mb;type:bigint;default:0;comment:已使用流量(MB)" json:"data_usage_mb"`
RealDataUsageMB int64 `gorm:"column:real_data_usage_mb;type:bigint;default:0;comment:真流量使用(MB)" json:"real_data_usage_mb"`
VirtualDataUsageMB int64 `gorm:"column:virtual_data_usage_mb;type:bigint;default:0;comment:虚流量使用(MB)" json:"virtual_data_usage_mb"`
ActivatedAt time.Time `gorm:"column:activated_at;not null;comment:套餐生效时间" json:"activated_at"`
ExpiresAt time.Time `gorm:"column:expires_at;not null;comment:套餐过期时间" json:"expires_at"`
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-生效中 2-已用完 3-已过期" json:"status"`
LastPackageCheckAt *time.Time `gorm:"column:last_package_check_at;comment:最后一次套餐流量检查时间" json:"last_package_check_at"`
}
// TableName 指定表名
func (PackageUsage) TableName() string {
return "tb_package_usage"
}
// OneTimeCommissionConfig 一次性佣金规则配置
type OneTimeCommissionConfig struct {
Enable bool `json:"enable"`
TriggerType string `json:"trigger_type"`
Threshold int64 `json:"threshold"`
CommissionType string `json:"commission_type"`
CommissionAmount int64 `json:"commission_amount"`
Tiers []OneTimeCommissionTier `json:"tiers,omitempty"`
ValidityType string `json:"validity_type"`
ValidityValue string `json:"validity_value"`
EnableForceRecharge bool `json:"enable_force_recharge"`
ForceCalcType string `json:"force_calc_type"`
ForceAmount int64 `json:"force_amount"`
}
// OneTimeCommissionTier 一次性佣金梯度配置
type OneTimeCommissionTier struct {
Dimension string `json:"dimension"`
StatScope string `json:"stat_scope"`
Threshold int64 `json:"threshold"`
Amount int64 `json:"amount"`
}
const (
OneTimeCommissionTriggerFirstRecharge = "first_recharge"
OneTimeCommissionTriggerAccumulatedRecharge = "accumulated_recharge"
OneTimeCommissionValidityPermanent = "permanent"
OneTimeCommissionValidityFixedDate = "fixed_date"
OneTimeCommissionValidityRelative = "relative"
OneTimeCommissionForceCalcFixed = "fixed"
OneTimeCommissionForceCalcDynamic = "dynamic"
OneTimeCommissionStatScopeSelf = "self"
OneTimeCommissionStatScopeSelfAndSub = "self_and_sub"
TierTypeSalesCount = "sales_count"
TierTypeSalesAmount = "sales_amount"
)
func (ps *PackageSeries) GetOneTimeCommissionConfig() (*OneTimeCommissionConfig, error) {
if ps.OneTimeCommissionConfigJSON == "" {
return nil, nil
}
var config OneTimeCommissionConfig
if err := json.Unmarshal([]byte(ps.OneTimeCommissionConfigJSON), &config); err != nil {
return nil, err
}
return &config, nil
}
func (ps *PackageSeries) SetOneTimeCommissionConfig(config *OneTimeCommissionConfig) error {
if config == nil {
ps.OneTimeCommissionConfigJSON = ""
return nil
}
data, err := json.Marshal(config)
if err != nil {
return err
}
ps.OneTimeCommissionConfigJSON = string(data)
return nil
}