- 数据库: 重命名 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
370 lines
12 KiB
Go
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")
|
|
}
|
|
})
|
|
}
|
|
}
|