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:
@@ -9,34 +9,36 @@ import (
|
||||
"github.com/break/junhong_cmp_fiber/internal/service/commission_stats"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/utils"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
db *gorm.DB
|
||||
commissionRecordStore *postgres.CommissionRecordStore
|
||||
shopStore *postgres.ShopStore
|
||||
shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore
|
||||
shopSeriesOneTimeCommissionTierStore *postgres.ShopSeriesOneTimeCommissionTierStore
|
||||
iotCardStore *postgres.IotCardStore
|
||||
deviceStore *postgres.DeviceStore
|
||||
walletStore *postgres.WalletStore
|
||||
walletTransactionStore *postgres.WalletTransactionStore
|
||||
orderStore *postgres.OrderStore
|
||||
orderItemStore *postgres.OrderItemStore
|
||||
packageStore *postgres.PackageStore
|
||||
commissionStatsService *commission_stats.Service
|
||||
logger *zap.Logger
|
||||
db *gorm.DB
|
||||
commissionRecordStore *postgres.CommissionRecordStore
|
||||
shopStore *postgres.ShopStore
|
||||
shopPackageAllocationStore *postgres.ShopPackageAllocationStore
|
||||
shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore
|
||||
packageSeriesStore *postgres.PackageSeriesStore
|
||||
iotCardStore *postgres.IotCardStore
|
||||
deviceStore *postgres.DeviceStore
|
||||
walletStore *postgres.WalletStore
|
||||
walletTransactionStore *postgres.WalletTransactionStore
|
||||
orderStore *postgres.OrderStore
|
||||
orderItemStore *postgres.OrderItemStore
|
||||
packageStore *postgres.PackageStore
|
||||
commissionStatsStore *postgres.ShopSeriesCommissionStatsStore
|
||||
commissionStatsService *commission_stats.Service
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func New(
|
||||
db *gorm.DB,
|
||||
commissionRecordStore *postgres.CommissionRecordStore,
|
||||
shopStore *postgres.ShopStore,
|
||||
shopPackageAllocationStore *postgres.ShopPackageAllocationStore,
|
||||
shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore,
|
||||
shopSeriesOneTimeCommissionTierStore *postgres.ShopSeriesOneTimeCommissionTierStore,
|
||||
packageSeriesStore *postgres.PackageSeriesStore,
|
||||
iotCardStore *postgres.IotCardStore,
|
||||
deviceStore *postgres.DeviceStore,
|
||||
walletStore *postgres.WalletStore,
|
||||
@@ -44,24 +46,27 @@ func New(
|
||||
orderStore *postgres.OrderStore,
|
||||
orderItemStore *postgres.OrderItemStore,
|
||||
packageStore *postgres.PackageStore,
|
||||
commissionStatsStore *postgres.ShopSeriesCommissionStatsStore,
|
||||
commissionStatsService *commission_stats.Service,
|
||||
logger *zap.Logger,
|
||||
) *Service {
|
||||
return &Service{
|
||||
db: db,
|
||||
commissionRecordStore: commissionRecordStore,
|
||||
shopStore: shopStore,
|
||||
shopSeriesAllocationStore: shopSeriesAllocationStore,
|
||||
shopSeriesOneTimeCommissionTierStore: shopSeriesOneTimeCommissionTierStore,
|
||||
iotCardStore: iotCardStore,
|
||||
deviceStore: deviceStore,
|
||||
walletStore: walletStore,
|
||||
walletTransactionStore: walletTransactionStore,
|
||||
orderStore: orderStore,
|
||||
orderItemStore: orderItemStore,
|
||||
packageStore: packageStore,
|
||||
commissionStatsService: commissionStatsService,
|
||||
logger: logger,
|
||||
db: db,
|
||||
commissionRecordStore: commissionRecordStore,
|
||||
shopStore: shopStore,
|
||||
shopPackageAllocationStore: shopPackageAllocationStore,
|
||||
shopSeriesAllocationStore: shopSeriesAllocationStore,
|
||||
packageSeriesStore: packageSeriesStore,
|
||||
iotCardStore: iotCardStore,
|
||||
deviceStore: deviceStore,
|
||||
walletStore: walletStore,
|
||||
walletTransactionStore: walletTransactionStore,
|
||||
orderStore: orderStore,
|
||||
orderItemStore: orderItemStore,
|
||||
packageStore: packageStore,
|
||||
commissionStatsStore: commissionStatsStore,
|
||||
commissionStatsService: commissionStatsService,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +151,14 @@ func (s *Service) CalculateCostDiffCommission(ctx context.Context, order *model.
|
||||
})
|
||||
}
|
||||
|
||||
// 获取订单明细以获取套餐ID(用于成本价查询)
|
||||
orderItems, err := s.orderItemStore.ListByOrderID(ctx, order.ID)
|
||||
if err != nil || len(orderItems) == 0 {
|
||||
s.logger.Warn("获取订单明细失败或订单无明细,跳过成本价差佣金计算", zap.Uint("order_id", order.ID), zap.Error(err))
|
||||
return records, nil
|
||||
}
|
||||
packageID := orderItems[0].PackageID
|
||||
|
||||
childCostPrice := order.SellerCostPrice
|
||||
currentShopID := sellerShop.ParentID
|
||||
|
||||
@@ -156,13 +169,13 @@ func (s *Service) CalculateCostDiffCommission(ctx context.Context, order *model.
|
||||
break
|
||||
}
|
||||
|
||||
allocation, err := s.shopSeriesAllocationStore.GetByShopAndSeries(ctx, currentShop.ID, *order.SeriesID)
|
||||
allocation, err := s.shopPackageAllocationStore.GetByShopAndPackage(ctx, currentShop.ID, packageID)
|
||||
if err != nil {
|
||||
s.logger.Warn("上级店铺未分配该系列,跳过", zap.Uint("shop_id", currentShop.ID), zap.Uint("series_id", *order.SeriesID))
|
||||
s.logger.Warn("上级店铺未分配该套餐,跳过", zap.Uint("shop_id", currentShop.ID), zap.Uint("package_id", packageID))
|
||||
break
|
||||
}
|
||||
|
||||
myCostPrice := s.calculateCostPrice(allocation, order.TotalAmount)
|
||||
myCostPrice := allocation.CostPrice
|
||||
profit := childCostPrice - myCostPrice
|
||||
if profit > 0 {
|
||||
records = append(records, &model.CommissionRecord{
|
||||
@@ -187,12 +200,7 @@ func (s *Service) CalculateCostDiffCommission(ctx context.Context, order *model.
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (s *Service) calculateCostPrice(allocation *model.ShopSeriesAllocation, orderAmount int64) int64 {
|
||||
return utils.CalculateCostPrice(allocation, orderAmount)
|
||||
}
|
||||
|
||||
func (s *Service) triggerOneTimeCommissionForCardInTx(ctx context.Context, tx *gorm.DB, order *model.Order, cardID uint) error {
|
||||
// 代购订单不触发一次性佣金和累计充值更新
|
||||
if order.IsPurchaseOnBehalf {
|
||||
return nil
|
||||
}
|
||||
@@ -206,79 +214,64 @@ func (s *Service) triggerOneTimeCommissionForCardInTx(ctx context.Context, tx *g
|
||||
return nil
|
||||
}
|
||||
|
||||
allocation, err := s.shopSeriesAllocationStore.GetByShopAndSeries(ctx, *card.ShopID, *card.SeriesID)
|
||||
seriesID := *card.SeriesID
|
||||
series, err := s.packageSeriesStore.GetByID(ctx, seriesID)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "获取系列分配失败")
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "获取套餐系列失败")
|
||||
}
|
||||
|
||||
if !allocation.EnableOneTimeCommission {
|
||||
config, err := series.GetOneTimeCommissionConfig()
|
||||
if err != nil || config == nil || !config.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
if allocation.OneTimeCommissionTrigger == model.OneTimeCommissionTriggerAccumulatedRecharge {
|
||||
newAccumulated := card.AccumulatedRecharge + order.TotalAmount
|
||||
if s.isOneTimeCommissionExpired(config, card.ActivatedAt) {
|
||||
s.logger.Info("一次性佣金规则已过期,跳过",
|
||||
zap.Uint("card_id", cardID),
|
||||
zap.Uint("series_id", seriesID),
|
||||
zap.String("validity_type", config.ValidityType))
|
||||
return nil
|
||||
}
|
||||
|
||||
if config.TriggerType == model.OneTimeCommissionTriggerFirstRecharge {
|
||||
accumulatedBySeries := card.GetAccumulatedRechargeBySeries(seriesID)
|
||||
newAccumulated := accumulatedBySeries + order.TotalAmount
|
||||
card.AddAccumulatedRechargeBySeries(seriesID, order.TotalAmount)
|
||||
if err := tx.Model(&model.IotCard{}).Where("id = ?", cardID).
|
||||
Update("accumulated_recharge", newAccumulated).Error; err != nil {
|
||||
Update("accumulated_recharge_by_series", card.AccumulatedRechargeBySeriesJSON).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "更新卡累计充值金额失败")
|
||||
}
|
||||
card.AccumulatedRecharge = newAccumulated
|
||||
|
||||
if card.IsFirstRechargeTriggeredBySeries(seriesID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if newAccumulated < config.Threshold {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if card.FirstCommissionPaid {
|
||||
if card.IsFirstRechargeTriggeredBySeries(seriesID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var rechargeAmount int64
|
||||
switch allocation.OneTimeCommissionTrigger {
|
||||
case model.OneTimeCommissionTriggerSingleRecharge:
|
||||
rechargeAmount = order.TotalAmount
|
||||
case model.OneTimeCommissionTriggerAccumulatedRecharge:
|
||||
rechargeAmount = card.AccumulatedRecharge
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if rechargeAmount < allocation.OneTimeCommissionThreshold {
|
||||
return nil
|
||||
}
|
||||
|
||||
commissionAmount, err := s.calculateOneTimeCommission(ctx, allocation, order.TotalAmount)
|
||||
records, err := s.calculateChainOneTimeCommission(ctx, *card.ShopID, seriesID, order, &cardID, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "计算一次性佣金失败")
|
||||
return err
|
||||
}
|
||||
|
||||
if commissionAmount <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if card.ShopID == nil {
|
||||
return errors.New(errors.CodeInvalidParam, "卡未归属任何店铺,无法发放佣金")
|
||||
}
|
||||
|
||||
record := &model.CommissionRecord{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: order.Creator,
|
||||
Updater: order.Updater,
|
||||
},
|
||||
ShopID: *card.ShopID,
|
||||
OrderID: order.ID,
|
||||
IotCardID: &cardID,
|
||||
CommissionSource: model.CommissionSourceOneTime,
|
||||
Amount: commissionAmount,
|
||||
Status: model.CommissionStatusReleased,
|
||||
Remark: "一次性佣金",
|
||||
}
|
||||
|
||||
if err := tx.Create(record).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建一次性佣金记录失败")
|
||||
}
|
||||
|
||||
if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "一次性佣金入账失败")
|
||||
for _, record := range records {
|
||||
if err := tx.Create(record).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建一次性佣金记录失败")
|
||||
}
|
||||
if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "一次性佣金入账失败")
|
||||
}
|
||||
}
|
||||
|
||||
card.SetFirstRechargeTriggeredBySeries(seriesID, true)
|
||||
if err := tx.Model(&model.IotCard{}).Where("id = ?", cardID).
|
||||
Update("first_commission_paid", true).Error; err != nil {
|
||||
Update("first_recharge_triggered_by_series", card.FirstRechargeTriggeredBySeriesJSON).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "更新卡佣金发放状态失败")
|
||||
}
|
||||
|
||||
@@ -292,7 +285,6 @@ func (s *Service) TriggerOneTimeCommissionForCard(ctx context.Context, order *mo
|
||||
}
|
||||
|
||||
func (s *Service) triggerOneTimeCommissionForDeviceInTx(ctx context.Context, tx *gorm.DB, order *model.Order, deviceID uint) error {
|
||||
// 代购订单不触发一次性佣金和累计充值更新
|
||||
if order.IsPurchaseOnBehalf {
|
||||
return nil
|
||||
}
|
||||
@@ -306,79 +298,64 @@ func (s *Service) triggerOneTimeCommissionForDeviceInTx(ctx context.Context, tx
|
||||
return nil
|
||||
}
|
||||
|
||||
allocation, err := s.shopSeriesAllocationStore.GetByShopAndSeries(ctx, *device.ShopID, *device.SeriesID)
|
||||
seriesID := *device.SeriesID
|
||||
series, err := s.packageSeriesStore.GetByID(ctx, seriesID)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "获取系列分配失败")
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "获取套餐系列失败")
|
||||
}
|
||||
|
||||
if !allocation.EnableOneTimeCommission {
|
||||
config, err := series.GetOneTimeCommissionConfig()
|
||||
if err != nil || config == nil || !config.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
if allocation.OneTimeCommissionTrigger == model.OneTimeCommissionTriggerAccumulatedRecharge {
|
||||
newAccumulated := device.AccumulatedRecharge + order.TotalAmount
|
||||
if s.isOneTimeCommissionExpired(config, device.ActivatedAt) {
|
||||
s.logger.Info("一次性佣金规则已过期,跳过",
|
||||
zap.Uint("device_id", deviceID),
|
||||
zap.Uint("series_id", seriesID),
|
||||
zap.String("validity_type", config.ValidityType))
|
||||
return nil
|
||||
}
|
||||
|
||||
if config.TriggerType == model.OneTimeCommissionTriggerFirstRecharge {
|
||||
accumulatedBySeries := device.GetAccumulatedRechargeBySeries(seriesID)
|
||||
newAccumulated := accumulatedBySeries + order.TotalAmount
|
||||
device.AddAccumulatedRechargeBySeries(seriesID, order.TotalAmount)
|
||||
if err := tx.Model(&model.Device{}).Where("id = ?", deviceID).
|
||||
Update("accumulated_recharge", newAccumulated).Error; err != nil {
|
||||
Update("accumulated_recharge_by_series", device.AccumulatedRechargeBySeriesJSON).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "更新设备累计充值金额失败")
|
||||
}
|
||||
device.AccumulatedRecharge = newAccumulated
|
||||
|
||||
if device.IsFirstRechargeTriggeredBySeries(seriesID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if newAccumulated < config.Threshold {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if device.FirstCommissionPaid {
|
||||
if device.IsFirstRechargeTriggeredBySeries(seriesID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var rechargeAmount int64
|
||||
switch allocation.OneTimeCommissionTrigger {
|
||||
case model.OneTimeCommissionTriggerSingleRecharge:
|
||||
rechargeAmount = order.TotalAmount
|
||||
case model.OneTimeCommissionTriggerAccumulatedRecharge:
|
||||
rechargeAmount = device.AccumulatedRecharge
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if rechargeAmount < allocation.OneTimeCommissionThreshold {
|
||||
return nil
|
||||
}
|
||||
|
||||
commissionAmount, err := s.calculateOneTimeCommission(ctx, allocation, order.TotalAmount)
|
||||
records, err := s.calculateChainOneTimeCommission(ctx, *device.ShopID, seriesID, order, nil, &deviceID)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "计算一次性佣金失败")
|
||||
return err
|
||||
}
|
||||
|
||||
if commissionAmount <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if device.ShopID == nil {
|
||||
return errors.New(errors.CodeInvalidParam, "设备未归属任何店铺,无法发放佣金")
|
||||
}
|
||||
|
||||
record := &model.CommissionRecord{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: order.Creator,
|
||||
Updater: order.Updater,
|
||||
},
|
||||
ShopID: *device.ShopID,
|
||||
OrderID: order.ID,
|
||||
DeviceID: &deviceID,
|
||||
CommissionSource: model.CommissionSourceOneTime,
|
||||
Amount: commissionAmount,
|
||||
Status: model.CommissionStatusReleased,
|
||||
Remark: "一次性佣金(设备)",
|
||||
}
|
||||
|
||||
if err := tx.Create(record).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建一次性佣金记录失败")
|
||||
}
|
||||
|
||||
if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "一次性佣金入账失败")
|
||||
for _, record := range records {
|
||||
if err := tx.Create(record).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "创建一次性佣金记录失败")
|
||||
}
|
||||
if err := s.creditCommissionInTx(ctx, tx, record); err != nil {
|
||||
return errors.Wrap(errors.CodeInternalError, err, "一次性佣金入账失败")
|
||||
}
|
||||
}
|
||||
|
||||
device.SetFirstRechargeTriggeredBySeries(seriesID, true)
|
||||
if err := tx.Model(&model.Device{}).Where("id = ?", deviceID).
|
||||
Update("first_commission_paid", true).Error; err != nil {
|
||||
Update("first_recharge_triggered_by_series", device.FirstRechargeTriggeredBySeriesJSON).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "更新设备佣金发放状态失败")
|
||||
}
|
||||
|
||||
@@ -391,74 +368,197 @@ func (s *Service) TriggerOneTimeCommissionForDevice(ctx context.Context, order *
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) calculateOneTimeCommission(ctx context.Context, allocation *model.ShopSeriesAllocation, orderAmount int64) (int64, error) {
|
||||
switch allocation.OneTimeCommissionType {
|
||||
case model.OneTimeCommissionTypeFixed:
|
||||
return s.calculateFixedCommission(allocation.OneTimeCommissionMode, allocation.OneTimeCommissionValue, orderAmount), nil
|
||||
case model.OneTimeCommissionTypeTiered:
|
||||
return s.calculateTieredCommission(ctx, allocation.ID, orderAmount)
|
||||
func (s *Service) isOneTimeCommissionExpired(config *model.OneTimeCommissionConfig, activatedAt *time.Time) bool {
|
||||
if config == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
switch config.ValidityType {
|
||||
case model.OneTimeCommissionValidityPermanent:
|
||||
return false
|
||||
|
||||
case model.OneTimeCommissionValidityFixedDate:
|
||||
if config.ValidityValue == "" {
|
||||
return false
|
||||
}
|
||||
expiryDate, err := time.Parse("2006-01-02", config.ValidityValue)
|
||||
if err != nil {
|
||||
s.logger.Warn("解析一次性佣金到期日期失败",
|
||||
zap.String("validity_value", config.ValidityValue),
|
||||
zap.Error(err))
|
||||
return false
|
||||
}
|
||||
expiryDate = expiryDate.Add(24*time.Hour - time.Second)
|
||||
return now.After(expiryDate)
|
||||
|
||||
case model.OneTimeCommissionValidityRelative:
|
||||
if activatedAt == nil {
|
||||
return false
|
||||
}
|
||||
if config.ValidityValue == "" {
|
||||
return false
|
||||
}
|
||||
months := 0
|
||||
if _, err := fmt.Sscanf(config.ValidityValue, "%d", &months); err != nil || months <= 0 {
|
||||
s.logger.Warn("解析一次性佣金相对时长失败",
|
||||
zap.String("validity_value", config.ValidityValue),
|
||||
zap.Error(err))
|
||||
return false
|
||||
}
|
||||
expiryTime := activatedAt.AddDate(0, months, 0)
|
||||
return now.After(expiryTime)
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (s *Service) calculateFixedCommission(mode string, value int64, orderAmount int64) int64 {
|
||||
if mode == model.CommissionModeFixed {
|
||||
return value
|
||||
} else if mode == model.CommissionModePercent {
|
||||
return orderAmount * value / 1000
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func (s *Service) calculateChainOneTimeCommission(ctx context.Context, bottomShopID uint, seriesID uint, order *model.Order, cardID *uint, deviceID *uint) ([]*model.CommissionRecord, error) {
|
||||
var records []*model.CommissionRecord
|
||||
|
||||
func (s *Service) calculateTieredCommission(ctx context.Context, allocationID uint, orderAmount int64) (int64, error) {
|
||||
tiers, err := s.shopSeriesOneTimeCommissionTierStore.ListByAllocationID(ctx, allocationID)
|
||||
series, err := s.packageSeriesStore.GetByID(ctx, seriesID)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(errors.CodeDatabaseError, err, "获取梯度配置失败")
|
||||
s.logger.Warn("获取套餐系列失败,跳过一次性佣金", zap.Uint("series_id", seriesID), zap.Error(err))
|
||||
return records, nil
|
||||
}
|
||||
|
||||
config, err := series.GetOneTimeCommissionConfig()
|
||||
if err != nil || config == nil || !config.Enable {
|
||||
return records, nil
|
||||
}
|
||||
|
||||
bottomSeriesAllocation, err := s.shopSeriesAllocationStore.GetByShopAndSeries(ctx, bottomShopID, seriesID)
|
||||
if err != nil {
|
||||
s.logger.Warn("底层店铺未分配该系列,跳过一次性佣金", zap.Uint("shop_id", bottomShopID), zap.Uint("series_id", seriesID))
|
||||
return records, nil
|
||||
}
|
||||
|
||||
bottomShop, err := s.shopStore.GetByID(ctx, bottomShopID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "获取店铺信息失败")
|
||||
}
|
||||
|
||||
childAmountGiven := int64(0)
|
||||
currentShopID := bottomShopID
|
||||
currentShop := bottomShop
|
||||
currentSeriesAllocation := bottomSeriesAllocation
|
||||
|
||||
for {
|
||||
var myAmount int64
|
||||
|
||||
if config.CommissionType == "tiered" && len(config.Tiers) > 0 {
|
||||
tieredAmount, tierErr := s.matchOneTimeCommissionTier(ctx, currentShopID, seriesID, currentSeriesAllocation.ID, config.Tiers)
|
||||
if tierErr != nil {
|
||||
s.logger.Warn("匹配梯度佣金失败,使用固定金额", zap.Uint("shop_id", currentShopID), zap.Error(tierErr))
|
||||
myAmount = currentSeriesAllocation.OneTimeCommissionAmount
|
||||
} else {
|
||||
myAmount = tieredAmount
|
||||
}
|
||||
} else {
|
||||
myAmount = currentSeriesAllocation.OneTimeCommissionAmount
|
||||
}
|
||||
|
||||
actualProfit := myAmount - childAmountGiven
|
||||
|
||||
if actualProfit > 0 {
|
||||
remark := "一次性佣金"
|
||||
if deviceID != nil {
|
||||
remark = "一次性佣金(设备)"
|
||||
}
|
||||
|
||||
records = append(records, &model.CommissionRecord{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: order.Creator,
|
||||
Updater: order.Updater,
|
||||
},
|
||||
ShopID: currentShopID,
|
||||
OrderID: order.ID,
|
||||
IotCardID: cardID,
|
||||
DeviceID: deviceID,
|
||||
CommissionSource: model.CommissionSourceOneTime,
|
||||
Amount: actualProfit,
|
||||
Status: model.CommissionStatusReleased,
|
||||
Remark: remark,
|
||||
})
|
||||
}
|
||||
|
||||
if currentShop.ParentID == nil || *currentShop.ParentID == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
parentShopID := *currentShop.ParentID
|
||||
parentSeriesAllocation, err := s.shopSeriesAllocationStore.GetByShopAndSeries(ctx, parentShopID, seriesID)
|
||||
if err != nil {
|
||||
s.logger.Warn("上级店铺未分配该系列,停止链式计算",
|
||||
zap.Uint("parent_shop_id", parentShopID),
|
||||
zap.Uint("series_id", seriesID))
|
||||
break
|
||||
}
|
||||
|
||||
parentShop, err := s.shopStore.GetByID(ctx, parentShopID)
|
||||
if err != nil {
|
||||
s.logger.Error("获取上级店铺失败", zap.Uint("shop_id", parentShopID), zap.Error(err))
|
||||
break
|
||||
}
|
||||
|
||||
childAmountGiven = myAmount
|
||||
currentShopID = parentShopID
|
||||
currentShop = parentShop
|
||||
currentSeriesAllocation = parentSeriesAllocation
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (s *Service) matchOneTimeCommissionTier(ctx context.Context, shopID uint, seriesID uint, allocationID uint, tiers []model.OneTimeCommissionTier) (int64, error) {
|
||||
if len(tiers) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
stats, err := s.commissionStatsService.GetCurrentStats(ctx, allocationID, "all_time")
|
||||
if err != nil {
|
||||
s.logger.Error("获取销售业绩统计失败", zap.Uint("allocation_id", allocationID), zap.Error(err))
|
||||
return 0, nil
|
||||
}
|
||||
now := time.Now()
|
||||
var matchedAmount int64 = 0
|
||||
|
||||
if stats == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var matchedTier *model.ShopSeriesOneTimeCommissionTier
|
||||
for _, tier := range tiers {
|
||||
var salesValue int64
|
||||
if tier.TierType == model.TierTypeSalesCount {
|
||||
salesValue = stats.TotalSalesCount
|
||||
} else if tier.TierType == model.TierTypeSalesAmount {
|
||||
salesValue = stats.TotalSalesAmount
|
||||
var salesCount, salesAmount int64
|
||||
var err error
|
||||
|
||||
if tier.StatScope == model.OneTimeCommissionStatScopeSelfAndSub {
|
||||
subordinateIDs, subErr := s.shopStore.GetSubordinateShopIDs(ctx, shopID)
|
||||
if subErr != nil {
|
||||
s.logger.Warn("获取下级店铺失败", zap.Uint("shop_id", shopID), zap.Error(subErr))
|
||||
subordinateIDs = []uint{shopID}
|
||||
}
|
||||
|
||||
allocationIDs, allocErr := s.shopSeriesAllocationStore.GetIDsByShopIDsAndSeries(ctx, subordinateIDs, seriesID)
|
||||
if allocErr != nil {
|
||||
return 0, errors.Wrap(errors.CodeDatabaseError, allocErr, "获取下级分配ID失败")
|
||||
}
|
||||
|
||||
salesCount, salesAmount, err = s.commissionStatsStore.GetAggregatedStats(ctx, allocationIDs, "monthly", now)
|
||||
} else {
|
||||
salesCount, salesAmount, err = s.commissionStatsStore.GetAggregatedStats(ctx, []uint{allocationID}, "monthly", now)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.logger.Warn("获取销售统计失败", zap.Uint("allocation_id", allocationID), zap.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
if salesValue >= tier.ThresholdValue {
|
||||
if matchedTier == nil || tier.ThresholdValue > matchedTier.ThresholdValue {
|
||||
matchedTier = tier
|
||||
}
|
||||
var currentValue int64
|
||||
if tier.Dimension == model.TierTypeSalesCount {
|
||||
currentValue = salesCount
|
||||
} else {
|
||||
currentValue = salesAmount
|
||||
}
|
||||
|
||||
if currentValue >= tier.Threshold && tier.Amount > matchedAmount {
|
||||
matchedAmount = tier.Amount
|
||||
}
|
||||
}
|
||||
|
||||
if matchedTier == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if matchedTier.CommissionMode == model.CommissionModeFixed {
|
||||
return matchedTier.CommissionValue, nil
|
||||
} else if matchedTier.CommissionMode == model.CommissionModePercent {
|
||||
return orderAmount * matchedTier.CommissionValue / 1000, nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
return matchedAmount, nil
|
||||
}
|
||||
|
||||
func (s *Service) creditCommissionInTx(ctx context.Context, tx *gorm.DB, record *model.CommissionRecord) error {
|
||||
|
||||
@@ -1,369 +0,0 @@
|
||||
package commission_calculation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/service/commission_stats"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/tests/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func TestCalculateCommission_PurchaseOnBehalf(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
commissionRecordStore := postgres.NewCommissionRecordStore(tx, rdb)
|
||||
shopStore := postgres.NewShopStore(tx, rdb)
|
||||
shopSeriesAllocationStore := postgres.NewShopSeriesAllocationStore(tx)
|
||||
shopSeriesOneTimeCommissionTierStore := postgres.NewShopSeriesOneTimeCommissionTierStore(tx)
|
||||
iotCardStore := postgres.NewIotCardStore(tx, rdb)
|
||||
deviceStore := postgres.NewDeviceStore(tx, rdb)
|
||||
walletStore := postgres.NewWalletStore(tx, rdb)
|
||||
walletTransactionStore := postgres.NewWalletTransactionStore(tx, rdb)
|
||||
orderStore := postgres.NewOrderStore(tx, rdb)
|
||||
orderItemStore := postgres.NewOrderItemStore(tx, rdb)
|
||||
packageStore := postgres.NewPackageStore(tx)
|
||||
statsStore := postgres.NewShopSeriesCommissionStatsStore(tx)
|
||||
commissionStatsService := commission_stats.New(statsStore)
|
||||
|
||||
service := New(
|
||||
tx,
|
||||
commissionRecordStore,
|
||||
shopStore,
|
||||
shopSeriesAllocationStore,
|
||||
shopSeriesOneTimeCommissionTierStore,
|
||||
iotCardStore,
|
||||
deviceStore,
|
||||
walletStore,
|
||||
walletTransactionStore,
|
||||
orderStore,
|
||||
orderItemStore,
|
||||
packageStore,
|
||||
commissionStatsService,
|
||||
zap.NewNop(),
|
||||
)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
shop := &model.Shop{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
ShopName: "测试店铺",
|
||||
ShopCode: "TEST001",
|
||||
ContactName: "测试联系人",
|
||||
ContactPhone: "13800000001",
|
||||
}
|
||||
require.NoError(t, tx.Create(shop).Error)
|
||||
|
||||
wallet := &model.Wallet{
|
||||
ResourceType: "shop",
|
||||
ResourceID: shop.ID,
|
||||
WalletType: "commission",
|
||||
Balance: 0,
|
||||
Version: 1,
|
||||
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
||||
}
|
||||
require.NoError(t, tx.Create(wallet).Error)
|
||||
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
ShopID: shop.ID,
|
||||
SeriesID: 1,
|
||||
AllocatorShopID: 1,
|
||||
BaseCommissionMode: model.CommissionModeFixed,
|
||||
BaseCommissionValue: 5000,
|
||||
EnableOneTimeCommission: true,
|
||||
OneTimeCommissionTrigger: model.OneTimeCommissionTriggerAccumulatedRecharge,
|
||||
OneTimeCommissionThreshold: 10000,
|
||||
OneTimeCommissionType: model.OneTimeCommissionTypeFixed,
|
||||
OneTimeCommissionMode: model.CommissionModeFixed,
|
||||
OneTimeCommissionValue: 1000,
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(allocation).Error)
|
||||
|
||||
card := &model.IotCard{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
ICCID: "89860000000000000001",
|
||||
ShopID: &shop.ID,
|
||||
SeriesID: &allocation.SeriesID,
|
||||
AccumulatedRecharge: 0,
|
||||
FirstCommissionPaid: false,
|
||||
}
|
||||
require.NoError(t, tx.Create(card).Error)
|
||||
|
||||
seriesID := allocation.SeriesID
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
isPurchaseOnBehalf bool
|
||||
expectedAccumulatedRecharge int64
|
||||
expectedCommissionRecords int
|
||||
expectedOneTimeCommission bool
|
||||
}{
|
||||
{
|
||||
name: "普通订单_触发累计充值和一次性佣金",
|
||||
isPurchaseOnBehalf: false,
|
||||
expectedAccumulatedRecharge: 15000,
|
||||
expectedCommissionRecords: 2,
|
||||
expectedOneTimeCommission: true,
|
||||
},
|
||||
{
|
||||
name: "代购订单_不触发累计充值和一次性佣金",
|
||||
isPurchaseOnBehalf: true,
|
||||
expectedAccumulatedRecharge: 0,
|
||||
expectedCommissionRecords: 1,
|
||||
expectedOneTimeCommission: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.NoError(t, tx.Model(&model.IotCard{}).Where("id = ?", card.ID).Updates(map[string]interface{}{
|
||||
"accumulated_recharge": 0,
|
||||
"first_commission_paid": false,
|
||||
}).Error)
|
||||
|
||||
require.NoError(t, tx.Where("1=1").Delete(&model.CommissionRecord{}).Error)
|
||||
require.NoError(t, tx.Where("1=1").Delete(&model.Order{}).Error)
|
||||
|
||||
order := &model.Order{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
OrderNo: "ORD" + time.Now().Format("20060102150405"),
|
||||
OrderType: model.OrderTypeSingleCard,
|
||||
IotCardID: &card.ID,
|
||||
BuyerType: model.BuyerTypeAgent,
|
||||
BuyerID: shop.ID,
|
||||
SellerShopID: &shop.ID,
|
||||
SeriesID: &seriesID,
|
||||
TotalAmount: 15000,
|
||||
SellerCostPrice: 5000,
|
||||
IsPurchaseOnBehalf: tt.isPurchaseOnBehalf,
|
||||
CommissionStatus: model.CommissionStatusPending,
|
||||
PaymentStatus: model.PaymentStatusPaid,
|
||||
}
|
||||
require.NoError(t, tx.Create(order).Error)
|
||||
|
||||
err := service.CalculateCommission(ctx, order.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
var updatedCard model.IotCard
|
||||
require.NoError(t, tx.First(&updatedCard, card.ID).Error)
|
||||
assert.Equal(t, tt.expectedAccumulatedRecharge, updatedCard.AccumulatedRecharge, "累计充值金额不符合预期")
|
||||
|
||||
var records []model.CommissionRecord
|
||||
require.NoError(t, tx.Where("order_id = ?", order.ID).Find(&records).Error)
|
||||
assert.Equal(t, tt.expectedCommissionRecords, len(records), "佣金记录数量不符合预期")
|
||||
|
||||
hasOneTimeCommission := false
|
||||
for _, record := range records {
|
||||
if record.CommissionSource == model.CommissionSourceOneTime {
|
||||
hasOneTimeCommission = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Equal(t, tt.expectedOneTimeCommission, hasOneTimeCommission, "一次性佣金触发状态不符合预期")
|
||||
|
||||
if tt.expectedOneTimeCommission {
|
||||
assert.True(t, updatedCard.FirstCommissionPaid, "首次佣金发放标记应为true")
|
||||
} else {
|
||||
assert.False(t, updatedCard.FirstCommissionPaid, "首次佣金发放标记应为false")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateCommission_Device_PurchaseOnBehalf(t *testing.T) {
|
||||
tx := testutils.NewTestTransaction(t)
|
||||
rdb := testutils.GetTestRedis(t)
|
||||
testutils.CleanTestRedisKeys(t, rdb)
|
||||
|
||||
commissionRecordStore := postgres.NewCommissionRecordStore(tx, rdb)
|
||||
shopStore := postgres.NewShopStore(tx, rdb)
|
||||
shopSeriesAllocationStore := postgres.NewShopSeriesAllocationStore(tx)
|
||||
shopSeriesOneTimeCommissionTierStore := postgres.NewShopSeriesOneTimeCommissionTierStore(tx)
|
||||
iotCardStore := postgres.NewIotCardStore(tx, rdb)
|
||||
deviceStore := postgres.NewDeviceStore(tx, rdb)
|
||||
walletStore := postgres.NewWalletStore(tx, rdb)
|
||||
walletTransactionStore := postgres.NewWalletTransactionStore(tx, rdb)
|
||||
orderStore := postgres.NewOrderStore(tx, rdb)
|
||||
orderItemStore := postgres.NewOrderItemStore(tx, rdb)
|
||||
packageStore := postgres.NewPackageStore(tx)
|
||||
statsStore := postgres.NewShopSeriesCommissionStatsStore(tx)
|
||||
commissionStatsService := commission_stats.New(statsStore)
|
||||
|
||||
service := New(
|
||||
tx,
|
||||
commissionRecordStore,
|
||||
shopStore,
|
||||
shopSeriesAllocationStore,
|
||||
shopSeriesOneTimeCommissionTierStore,
|
||||
iotCardStore,
|
||||
deviceStore,
|
||||
walletStore,
|
||||
walletTransactionStore,
|
||||
orderStore,
|
||||
orderItemStore,
|
||||
packageStore,
|
||||
commissionStatsService,
|
||||
zap.NewNop(),
|
||||
)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
shop := &model.Shop{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
ShopName: "测试店铺",
|
||||
ShopCode: "TEST002",
|
||||
ContactName: "测试联系人",
|
||||
ContactPhone: "13800000002",
|
||||
}
|
||||
require.NoError(t, tx.Create(shop).Error)
|
||||
|
||||
wallet := &model.Wallet{
|
||||
ResourceType: "shop",
|
||||
ResourceID: shop.ID,
|
||||
WalletType: "commission",
|
||||
Balance: 0,
|
||||
Version: 1,
|
||||
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
||||
}
|
||||
require.NoError(t, tx.Create(wallet).Error)
|
||||
|
||||
allocation := &model.ShopSeriesAllocation{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
ShopID: shop.ID,
|
||||
SeriesID: 1,
|
||||
AllocatorShopID: 1,
|
||||
BaseCommissionMode: model.CommissionModeFixed,
|
||||
BaseCommissionValue: 5000,
|
||||
EnableOneTimeCommission: true,
|
||||
OneTimeCommissionTrigger: model.OneTimeCommissionTriggerAccumulatedRecharge,
|
||||
OneTimeCommissionThreshold: 10000,
|
||||
OneTimeCommissionType: model.OneTimeCommissionTypeFixed,
|
||||
OneTimeCommissionMode: model.CommissionModeFixed,
|
||||
OneTimeCommissionValue: 1000,
|
||||
Status: 1,
|
||||
}
|
||||
require.NoError(t, tx.Create(allocation).Error)
|
||||
|
||||
device := &model.Device{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
DeviceNo: "DEV001",
|
||||
ShopID: &shop.ID,
|
||||
SeriesID: &allocation.SeriesID,
|
||||
AccumulatedRecharge: 0,
|
||||
FirstCommissionPaid: false,
|
||||
}
|
||||
require.NoError(t, tx.Create(device).Error)
|
||||
|
||||
seriesID := allocation.SeriesID
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
isPurchaseOnBehalf bool
|
||||
expectedAccumulatedRecharge int64
|
||||
expectedCommissionRecords int
|
||||
expectedOneTimeCommission bool
|
||||
}{
|
||||
{
|
||||
name: "普通订单_触发累计充值和一次性佣金",
|
||||
isPurchaseOnBehalf: false,
|
||||
expectedAccumulatedRecharge: 15000,
|
||||
expectedCommissionRecords: 2,
|
||||
expectedOneTimeCommission: true,
|
||||
},
|
||||
{
|
||||
name: "代购订单_不触发累计充值和一次性佣金",
|
||||
isPurchaseOnBehalf: true,
|
||||
expectedAccumulatedRecharge: 0,
|
||||
expectedCommissionRecords: 1,
|
||||
expectedOneTimeCommission: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.NoError(t, tx.Model(&model.Device{}).Where("id = ?", device.ID).Updates(map[string]interface{}{
|
||||
"accumulated_recharge": 0,
|
||||
"first_commission_paid": false,
|
||||
}).Error)
|
||||
|
||||
require.NoError(t, tx.Where("1=1").Delete(&model.CommissionRecord{}).Error)
|
||||
require.NoError(t, tx.Where("1=1").Delete(&model.Order{}).Error)
|
||||
|
||||
order := &model.Order{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
OrderNo: "ORD" + time.Now().Format("20060102150405"),
|
||||
OrderType: model.OrderTypeDevice,
|
||||
DeviceID: &device.ID,
|
||||
BuyerType: model.BuyerTypeAgent,
|
||||
BuyerID: shop.ID,
|
||||
SellerShopID: &shop.ID,
|
||||
SeriesID: &seriesID,
|
||||
TotalAmount: 15000,
|
||||
SellerCostPrice: 5000,
|
||||
IsPurchaseOnBehalf: tt.isPurchaseOnBehalf,
|
||||
CommissionStatus: model.CommissionStatusPending,
|
||||
PaymentStatus: model.PaymentStatusPaid,
|
||||
}
|
||||
require.NoError(t, tx.Create(order).Error)
|
||||
|
||||
err := service.CalculateCommission(ctx, order.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
var updatedDevice model.Device
|
||||
require.NoError(t, tx.First(&updatedDevice, device.ID).Error)
|
||||
assert.Equal(t, tt.expectedAccumulatedRecharge, updatedDevice.AccumulatedRecharge, "累计充值金额不符合预期")
|
||||
|
||||
var records []model.CommissionRecord
|
||||
require.NoError(t, tx.Where("order_id = ?", order.ID).Find(&records).Error)
|
||||
assert.Equal(t, tt.expectedCommissionRecords, len(records), "佣金记录数量不符合预期")
|
||||
|
||||
hasOneTimeCommission := false
|
||||
for _, record := range records {
|
||||
if record.CommissionSource == model.CommissionSourceOneTime {
|
||||
hasOneTimeCommission = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Equal(t, tt.expectedOneTimeCommission, hasOneTimeCommission, "一次性佣金触发状态不符合预期")
|
||||
|
||||
if tt.expectedOneTimeCommission {
|
||||
assert.True(t, updatedDevice.FirstCommissionPaid, "首次佣金发放标记应为true")
|
||||
} else {
|
||||
assert.False(t, updatedDevice.FirstCommissionPaid, "首次佣金发放标记应为false")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user