refactor: 一次性佣金配置从套餐级别提升到系列级别
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m29s
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:
@@ -1,6 +1,8 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -11,26 +13,126 @@ import (
|
||||
// 通过 shop_id 区分所有权:NULL=平台库存,有值=店铺所有
|
||||
type Device struct {
|
||||
gorm.Model
|
||||
BaseModel `gorm:"embedded"`
|
||||
DeviceNo string `gorm:"column:device_no;type:varchar(100);uniqueIndex:idx_device_no,where:deleted_at IS NULL;not null;comment:设备编号(唯一标识)" json:"device_no"`
|
||||
DeviceName string `gorm:"column:device_name;type:varchar(255);comment:设备名称" json:"device_name"`
|
||||
DeviceModel string `gorm:"column:device_model;type:varchar(100);comment:设备型号" json:"device_model"`
|
||||
DeviceType string `gorm:"column:device_type;type:varchar(50);comment:设备类型" json:"device_type"`
|
||||
MaxSimSlots int `gorm:"column:max_sim_slots;type:int;default:4;comment:最大插槽数量(默认4)" json:"max_sim_slots"`
|
||||
Manufacturer string `gorm:"column:manufacturer;type:varchar(255);comment:制造商" json:"manufacturer"`
|
||||
BatchNo string `gorm:"column:batch_no;type:varchar(100);comment:批次号" json:"batch_no"`
|
||||
ShopID *uint `gorm:"column:shop_id;index;comment:店铺ID(NULL=平台库存,有值=店铺所有)" json:"shop_id,omitempty"`
|
||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-在库 2-已分销 3-已激活 4-已停用" json:"status"`
|
||||
ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at"`
|
||||
DeviceUsername string `gorm:"column:device_username;type:varchar(100);comment:设备登录用户名" json:"device_username"`
|
||||
DevicePasswordEncrypted string `gorm:"column:device_password_encrypted;type:varchar(255);comment:设备登录密码(加密)" json:"device_password_encrypted"`
|
||||
DeviceAPIEndpoint string `gorm:"column:device_api_endpoint;type:varchar(500);comment:设备API端点" json:"device_api_endpoint"`
|
||||
SeriesID *uint `gorm:"column:series_id;index;comment:套餐系列ID(关联PackageSeries)" json:"series_id,omitempty"`
|
||||
FirstCommissionPaid bool `gorm:"column:first_commission_paid;type:boolean;default:false;comment:一次性佣金是否已发放" json:"first_commission_paid"`
|
||||
AccumulatedRecharge int64 `gorm:"column:accumulated_recharge;type:bigint;default:0;comment:累计充值金额(分)" json:"accumulated_recharge"`
|
||||
BaseModel `gorm:"embedded"`
|
||||
DeviceNo string `gorm:"column:device_no;type:varchar(100);uniqueIndex:idx_device_no,where:deleted_at IS NULL;not null;comment:设备编号(唯一标识)" json:"device_no"`
|
||||
DeviceName string `gorm:"column:device_name;type:varchar(255);comment:设备名称" json:"device_name"`
|
||||
DeviceModel string `gorm:"column:device_model;type:varchar(100);comment:设备型号" json:"device_model"`
|
||||
DeviceType string `gorm:"column:device_type;type:varchar(50);comment:设备类型" json:"device_type"`
|
||||
MaxSimSlots int `gorm:"column:max_sim_slots;type:int;default:4;comment:最大插槽数量(默认4)" json:"max_sim_slots"`
|
||||
Manufacturer string `gorm:"column:manufacturer;type:varchar(255);comment:制造商" json:"manufacturer"`
|
||||
BatchNo string `gorm:"column:batch_no;type:varchar(100);comment:批次号" json:"batch_no"`
|
||||
ShopID *uint `gorm:"column:shop_id;index;comment:店铺ID(NULL=平台库存,有值=店铺所有)" json:"shop_id,omitempty"`
|
||||
Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-在库 2-已分销 3-已激活 4-已停用" json:"status"`
|
||||
ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at"`
|
||||
DeviceUsername string `gorm:"column:device_username;type:varchar(100);comment:设备登录用户名" json:"device_username"`
|
||||
DevicePasswordEncrypted string `gorm:"column:device_password_encrypted;type:varchar(255);comment:设备登录密码(加密)" json:"device_password_encrypted"`
|
||||
DeviceAPIEndpoint string `gorm:"column:device_api_endpoint;type:varchar(500);comment:设备API端点" json:"device_api_endpoint"`
|
||||
SeriesID *uint `gorm:"column:series_id;index;comment:套餐系列ID(关联PackageSeries)" json:"series_id,omitempty"`
|
||||
FirstCommissionPaid bool `gorm:"column:first_commission_paid;type:boolean;default:false;comment:一次性佣金是否已发放(废弃,使用按系列追踪)" json:"first_commission_paid"`
|
||||
AccumulatedRecharge int64 `gorm:"column:accumulated_recharge;type:bigint;default:0;comment:累计充值金额(分,废弃,使用按系列追踪)" json:"accumulated_recharge"`
|
||||
AccumulatedRechargeBySeriesJSON string `gorm:"column:accumulated_recharge_by_series;type:jsonb;default:'{}';comment:按套餐系列追踪的累计充值金额" json:"-"`
|
||||
FirstRechargeTriggeredBySeriesJSON string `gorm:"column:first_recharge_triggered_by_series;type:jsonb;default:'{}';comment:按套餐系列追踪的首充触发状态" json:"-"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (Device) TableName() string {
|
||||
return "tb_device"
|
||||
}
|
||||
|
||||
func (d *Device) GetAccumulatedRechargeBySeriesMap() (map[uint]int64, error) {
|
||||
result := make(map[uint]int64)
|
||||
if d.AccumulatedRechargeBySeriesJSON == "" || d.AccumulatedRechargeBySeriesJSON == "{}" {
|
||||
return result, nil
|
||||
}
|
||||
var raw map[string]int64
|
||||
if err := json.Unmarshal([]byte(d.AccumulatedRechargeBySeriesJSON), &raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range raw {
|
||||
id, err := strconv.ParseUint(k, 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
result[uint(id)] = v
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *Device) SetAccumulatedRechargeBySeriesMap(m map[uint]int64) error {
|
||||
raw := make(map[string]int64)
|
||||
for k, v := range m {
|
||||
raw[strconv.FormatUint(uint64(k), 10)] = v
|
||||
}
|
||||
data, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.AccumulatedRechargeBySeriesJSON = string(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) GetAccumulatedRechargeBySeries(seriesID uint) int64 {
|
||||
m, err := d.GetAccumulatedRechargeBySeriesMap()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return m[seriesID]
|
||||
}
|
||||
|
||||
func (d *Device) AddAccumulatedRechargeBySeries(seriesID uint, amount int64) error {
|
||||
m, err := d.GetAccumulatedRechargeBySeriesMap()
|
||||
if err != nil {
|
||||
m = make(map[uint]int64)
|
||||
}
|
||||
m[seriesID] += amount
|
||||
return d.SetAccumulatedRechargeBySeriesMap(m)
|
||||
}
|
||||
|
||||
func (d *Device) GetFirstRechargeTriggeredBySeriesMap() (map[uint]bool, error) {
|
||||
result := make(map[uint]bool)
|
||||
if d.FirstRechargeTriggeredBySeriesJSON == "" || d.FirstRechargeTriggeredBySeriesJSON == "{}" {
|
||||
return result, nil
|
||||
}
|
||||
var raw map[string]bool
|
||||
if err := json.Unmarshal([]byte(d.FirstRechargeTriggeredBySeriesJSON), &raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range raw {
|
||||
id, err := strconv.ParseUint(k, 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
result[uint(id)] = v
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *Device) SetFirstRechargeTriggeredBySeriesMap(m map[uint]bool) error {
|
||||
raw := make(map[string]bool)
|
||||
for k, v := range m {
|
||||
raw[strconv.FormatUint(uint64(k), 10)] = v
|
||||
}
|
||||
data, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.FirstRechargeTriggeredBySeriesJSON = string(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) IsFirstRechargeTriggeredBySeries(seriesID uint) bool {
|
||||
m, err := d.GetFirstRechargeTriggeredBySeriesMap()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return m[seriesID]
|
||||
}
|
||||
|
||||
func (d *Device) SetFirstRechargeTriggeredBySeries(seriesID uint, triggered bool) error {
|
||||
m, err := d.GetFirstRechargeTriggeredBySeriesMap()
|
||||
if err != nil {
|
||||
m = make(map[uint]bool)
|
||||
}
|
||||
m[seriesID] = triggered
|
||||
return d.SetFirstRechargeTriggeredBySeriesMap(m)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user