Files
junhong_cmp_fiber/internal/service/commission_calculation/service_test.go
huang 37f43d2e2d 重构: 将卡/设备的套餐系列绑定从分配ID改为系列ID
- 数据库: 重命名 series_allocation_id → series_id
- Model: IotCard 和 Device 字段重命名
- DTO: 所有请求/响应字段统一为 series_id
- Store: 方法重命名,新增 GetByShopAndSeries 查询
- Service: 业务逻辑优化,系列验证和权限验证分离
- 测试: 更新所有测试用例,新增 shop_series_allocation_store_test.go
- 文档: 更新 API 文档说明参数变更

BREAKING CHANGE: API 参数从 series_allocation_id 改为 series_id
2026-02-02 12:09:53 +08:00

370 lines
12 KiB
Go

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")
}
})
}
}