fix(commission): 代购订单跳过一次性佣金和累计充值更新

This commit is contained in:
2026-01-31 11:46:50 +08:00
parent 1036b5979e
commit c7bf43f306
2 changed files with 389 additions and 7 deletions

View File

@@ -91,13 +91,16 @@ func (s *Service) CalculateCommission(ctx context.Context, orderID uint) error {
}
}
if order.OrderType == model.OrderTypeSingleCard && order.IotCardID != nil {
if err := s.triggerOneTimeCommissionForCardInTx(ctx, tx, order, *order.IotCardID); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "触发单卡一次性佣金失败")
}
} else if order.OrderType == model.OrderTypeDevice && order.DeviceID != nil {
if err := s.triggerOneTimeCommissionForDeviceInTx(ctx, tx, order, *order.DeviceID); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "触发设备一次性佣金失败")
// 代购订单不触发一次性佣金和累计充值更新
if !order.IsPurchaseOnBehalf {
if order.OrderType == model.OrderTypeSingleCard && order.IotCardID != nil {
if err := s.triggerOneTimeCommissionForCardInTx(ctx, tx, order, *order.IotCardID); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "触发单卡一次性佣金失败")
}
} else if order.OrderType == model.OrderTypeDevice && order.DeviceID != nil {
if err := s.triggerOneTimeCommissionForDeviceInTx(ctx, tx, order, *order.DeviceID); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "触发设备一次性佣金失败")
}
}
}
@@ -189,6 +192,11 @@ func (s *Service) calculateCostPrice(allocation *model.ShopSeriesAllocation, ord
}
func (s *Service) triggerOneTimeCommissionForCardInTx(ctx context.Context, tx *gorm.DB, order *model.Order, cardID uint) error {
// 代购订单不触发一次性佣金和累计充值更新
if order.IsPurchaseOnBehalf {
return nil
}
card, err := s.iotCardStore.GetByID(ctx, cardID)
if err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "获取卡信息失败")
@@ -284,6 +292,11 @@ 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
}
device, err := s.deviceStore.GetByID(ctx, deviceID)
if err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "获取设备信息失败")

View File

@@ -0,0 +1,369 @@
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,
SeriesAllocationID: &allocation.ID,
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,
SeriesAllocationID: &allocation.ID,
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")
}
})
}
}