fix(commission): 代购订单跳过一次性佣金和累计充值更新
This commit is contained in:
@@ -91,6 +91,8 @@ func (s *Service) CalculateCommission(ctx context.Context, orderID uint) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 代购订单不触发一次性佣金和累计充值更新
|
||||||
|
if !order.IsPurchaseOnBehalf {
|
||||||
if order.OrderType == model.OrderTypeSingleCard && order.IotCardID != nil {
|
if order.OrderType == model.OrderTypeSingleCard && order.IotCardID != nil {
|
||||||
if err := s.triggerOneTimeCommissionForCardInTx(ctx, tx, order, *order.IotCardID); err != nil {
|
if err := s.triggerOneTimeCommissionForCardInTx(ctx, tx, order, *order.IotCardID); err != nil {
|
||||||
return errors.Wrap(errors.CodeInternalError, err, "触发单卡一次性佣金失败")
|
return errors.Wrap(errors.CodeInternalError, err, "触发单卡一次性佣金失败")
|
||||||
@@ -100,6 +102,7 @@ func (s *Service) CalculateCommission(ctx context.Context, orderID uint) error {
|
|||||||
return errors.Wrap(errors.CodeInternalError, err, "触发设备一次性佣金失败")
|
return errors.Wrap(errors.CodeInternalError, err, "触发设备一次性佣金失败")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := tx.Model(&model.Order{}).Where("id = ?", orderID).Update("commission_status", model.CommissionStatusCalculated).Error; err != nil {
|
if err := tx.Model(&model.Order{}).Where("id = ?", orderID).Update("commission_status", model.CommissionStatusCalculated).Error; err != nil {
|
||||||
return errors.Wrap(errors.CodeDatabaseError, err, "更新订单佣金状态失败")
|
return errors.Wrap(errors.CodeDatabaseError, 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 {
|
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)
|
card, err := s.iotCardStore.GetByID(ctx, cardID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(errors.CodeDatabaseError, err, "获取卡信息失败")
|
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 {
|
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)
|
device, err := s.deviceStore.GetByID(ctx, deviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(errors.CodeDatabaseError, err, "获取设备信息失败")
|
return errors.Wrap(errors.CodeDatabaseError, err, "获取设备信息失败")
|
||||||
|
|||||||
369
internal/service/commission_calculation/service_test.go
Normal file
369
internal/service/commission_calculation/service_test.go
Normal 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")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user