All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m45s
- 归档 fix-one-time-commission-config-and-accumulation 到 archive/2026-01-29-* - 同步 delta specs 到主规范(one-time-commission-trigger、commission-calculation) - 新增累计触发逻辑文档和测试用例 - 修复一次性佣金配置落库和累计充值更新逻辑
411 lines
15 KiB
Go
411 lines
15 KiB
Go
package unit
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
|
"github.com/break/junhong_cmp_fiber/internal/service/commission_calculation"
|
|
"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/constants"
|
|
"github.com/break/junhong_cmp_fiber/tests/testutils"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestCommissionCalculation_AccumulatedRecharge(t *testing.T) {
|
|
tx := testutils.NewTestTransaction(t)
|
|
rdb := testutils.GetTestRedis(t)
|
|
testutils.CleanTestRedisKeys(t, rdb)
|
|
|
|
ctx := context.Background()
|
|
|
|
t.Run("累计充值触发-每次支付都写回累计金额", func(t *testing.T) {
|
|
shop := &model.Shop{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
ShopName: "测试店铺",
|
|
ShopCode: fmt.Sprintf("SHOP_%d", time.Now().UnixNano()),
|
|
Level: 1,
|
|
Status: constants.StatusEnabled,
|
|
}
|
|
require.NoError(t, tx.Create(shop).Error)
|
|
|
|
series := &model.PackageSeries{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
SeriesCode: fmt.Sprintf("SERIES_%d", time.Now().UnixNano()),
|
|
SeriesName: "测试系列",
|
|
Status: constants.StatusEnabled,
|
|
}
|
|
require.NoError(t, tx.Create(series).Error)
|
|
|
|
allocation := &model.ShopSeriesAllocation{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
ShopID: shop.ID,
|
|
SeriesID: series.ID,
|
|
AllocatorShopID: 0,
|
|
BaseCommissionMode: model.CommissionModeFixed,
|
|
BaseCommissionValue: 1000,
|
|
EnableOneTimeCommission: true,
|
|
OneTimeCommissionType: model.OneTimeCommissionTypeFixed,
|
|
OneTimeCommissionTrigger: model.OneTimeCommissionTriggerAccumulatedRecharge,
|
|
OneTimeCommissionThreshold: 10000,
|
|
OneTimeCommissionMode: model.CommissionModeFixed,
|
|
OneTimeCommissionValue: 500,
|
|
Status: constants.StatusEnabled,
|
|
}
|
|
require.NoError(t, tx.Create(allocation).Error)
|
|
|
|
card := &model.IotCard{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
ICCID: fmt.Sprintf("898600%013d", time.Now().Unix()%10000000000000),
|
|
CardType: "4G",
|
|
CardCategory: "normal",
|
|
CarrierID: 1,
|
|
CarrierType: "CMCC",
|
|
CarrierName: "中国移动",
|
|
Status: 3,
|
|
ShopID: &shop.ID,
|
|
SeriesAllocationID: &allocation.ID,
|
|
FirstCommissionPaid: false,
|
|
AccumulatedRecharge: 0,
|
|
}
|
|
require.NoError(t, tx.Create(card).Error)
|
|
|
|
wallet := &model.Wallet{
|
|
ResourceType: "shop",
|
|
ResourceID: shop.ID,
|
|
WalletType: "commission",
|
|
Balance: 0,
|
|
Version: 0,
|
|
Status: 1,
|
|
}
|
|
require.NoError(t, tx.Create(wallet).Error)
|
|
|
|
iotCardStore := postgres.NewIotCardStore(tx, rdb)
|
|
shopSeriesAllocationStore := postgres.NewShopSeriesAllocationStore(tx)
|
|
|
|
t.Run("第一次支付-累计金额更新为3000", func(t *testing.T) {
|
|
order1 := &model.Order{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
OrderNo: fmt.Sprintf("ORDER_%d_1", time.Now().UnixNano()),
|
|
OrderType: model.OrderTypeSingleCard,
|
|
BuyerType: model.BuyerTypeAgent,
|
|
BuyerID: shop.ID,
|
|
IotCardID: &card.ID,
|
|
SellerShopID: &shop.ID,
|
|
SeriesID: &series.ID,
|
|
TotalAmount: 3000,
|
|
SellerCostPrice: 2000,
|
|
PaymentStatus: model.PaymentStatusPaid,
|
|
CommissionStatus: model.CommissionStatusPending,
|
|
}
|
|
require.NoError(t, tx.Create(order1).Error)
|
|
|
|
cardBefore, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(0), cardBefore.AccumulatedRecharge)
|
|
|
|
alloc, err := shopSeriesAllocationStore.GetByID(ctx, *card.SeriesAllocationID)
|
|
require.NoError(t, err)
|
|
|
|
if alloc.OneTimeCommissionTrigger == model.OneTimeCommissionTriggerAccumulatedRecharge {
|
|
newAccumulated := cardBefore.AccumulatedRecharge + order1.TotalAmount
|
|
err := tx.Model(&model.IotCard{}).Where("id = ?", card.ID).
|
|
Update("accumulated_recharge", newAccumulated).Error
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
cardAfter, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(3000), cardAfter.AccumulatedRecharge, "第一次支付后累计金额应为3000")
|
|
assert.False(t, cardAfter.FirstCommissionPaid, "未达阈值不应标记为已发放")
|
|
})
|
|
|
|
t.Run("第二次支付-累计金额更新为7000", func(t *testing.T) {
|
|
order2 := &model.Order{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
OrderNo: fmt.Sprintf("ORDER_%d_2", time.Now().UnixNano()),
|
|
OrderType: model.OrderTypeSingleCard,
|
|
BuyerType: model.BuyerTypeAgent,
|
|
BuyerID: shop.ID,
|
|
IotCardID: &card.ID,
|
|
SellerShopID: &shop.ID,
|
|
SeriesID: &series.ID,
|
|
TotalAmount: 4000,
|
|
SellerCostPrice: 3000,
|
|
PaymentStatus: model.PaymentStatusPaid,
|
|
CommissionStatus: model.CommissionStatusPending,
|
|
}
|
|
require.NoError(t, tx.Create(order2).Error)
|
|
|
|
cardBefore, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(3000), cardBefore.AccumulatedRecharge)
|
|
|
|
alloc, err := shopSeriesAllocationStore.GetByID(ctx, *card.SeriesAllocationID)
|
|
require.NoError(t, err)
|
|
|
|
if alloc.OneTimeCommissionTrigger == model.OneTimeCommissionTriggerAccumulatedRecharge {
|
|
newAccumulated := cardBefore.AccumulatedRecharge + order2.TotalAmount
|
|
err := tx.Model(&model.IotCard{}).Where("id = ?", card.ID).
|
|
Update("accumulated_recharge", newAccumulated).Error
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
cardAfter, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(7000), cardAfter.AccumulatedRecharge, "第二次支付后累计金额应为7000")
|
|
assert.False(t, cardAfter.FirstCommissionPaid, "仍未达阈值不应标记为已发放")
|
|
})
|
|
|
|
t.Run("第三次支付-累计金额更新为11000且达到阈值", func(t *testing.T) {
|
|
order3 := &model.Order{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
OrderNo: fmt.Sprintf("ORDER_%d_3", time.Now().UnixNano()),
|
|
OrderType: model.OrderTypeSingleCard,
|
|
BuyerType: model.BuyerTypeAgent,
|
|
BuyerID: shop.ID,
|
|
IotCardID: &card.ID,
|
|
SellerShopID: &shop.ID,
|
|
SeriesID: &series.ID,
|
|
TotalAmount: 4000,
|
|
SellerCostPrice: 3000,
|
|
PaymentStatus: model.PaymentStatusPaid,
|
|
CommissionStatus: model.CommissionStatusPending,
|
|
}
|
|
require.NoError(t, tx.Create(order3).Error)
|
|
|
|
cardBefore, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(7000), cardBefore.AccumulatedRecharge)
|
|
|
|
alloc, err := shopSeriesAllocationStore.GetByID(ctx, *card.SeriesAllocationID)
|
|
require.NoError(t, err)
|
|
|
|
if alloc.OneTimeCommissionTrigger == model.OneTimeCommissionTriggerAccumulatedRecharge {
|
|
newAccumulated := cardBefore.AccumulatedRecharge + order3.TotalAmount
|
|
err := tx.Model(&model.IotCard{}).Where("id = ?", card.ID).
|
|
Update("accumulated_recharge", newAccumulated).Error
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
cardAfter, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(11000), cardAfter.AccumulatedRecharge, "第三次支付后累计金额应为11000")
|
|
|
|
if cardAfter.AccumulatedRecharge >= alloc.OneTimeCommissionThreshold && !cardAfter.FirstCommissionPaid {
|
|
assert.GreaterOrEqual(t, cardAfter.AccumulatedRecharge, alloc.OneTimeCommissionThreshold, "累计金额已达阈值")
|
|
|
|
err := tx.Model(&model.IotCard{}).Where("id = ?", card.ID).
|
|
Update("first_commission_paid", true).Error
|
|
require.NoError(t, err)
|
|
|
|
cardFinal, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.True(t, cardFinal.FirstCommissionPaid, "达到阈值应标记为已发放")
|
|
}
|
|
})
|
|
|
|
t.Run("第四次支付-累计金额继续更新但不重复发放", func(t *testing.T) {
|
|
order4 := &model.Order{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
OrderNo: fmt.Sprintf("ORDER_%d_4", time.Now().UnixNano()),
|
|
OrderType: model.OrderTypeSingleCard,
|
|
BuyerType: model.BuyerTypeAgent,
|
|
BuyerID: shop.ID,
|
|
IotCardID: &card.ID,
|
|
SellerShopID: &shop.ID,
|
|
SeriesID: &series.ID,
|
|
TotalAmount: 3000,
|
|
SellerCostPrice: 2000,
|
|
PaymentStatus: model.PaymentStatusPaid,
|
|
CommissionStatus: model.CommissionStatusPending,
|
|
}
|
|
require.NoError(t, tx.Create(order4).Error)
|
|
|
|
cardBefore, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(11000), cardBefore.AccumulatedRecharge)
|
|
assert.True(t, cardBefore.FirstCommissionPaid, "标记应保持为true")
|
|
|
|
alloc, err := shopSeriesAllocationStore.GetByID(ctx, *card.SeriesAllocationID)
|
|
require.NoError(t, err)
|
|
|
|
if alloc.OneTimeCommissionTrigger == model.OneTimeCommissionTriggerAccumulatedRecharge {
|
|
newAccumulated := cardBefore.AccumulatedRecharge + order4.TotalAmount
|
|
err := tx.Model(&model.IotCard{}).Where("id = ?", card.ID).
|
|
Update("accumulated_recharge", newAccumulated).Error
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
cardAfter, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int64(14000), cardAfter.AccumulatedRecharge, "第四次支付后累计金额应为14000")
|
|
assert.True(t, cardAfter.FirstCommissionPaid, "已发放标记不应改变")
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestCommissionCalculation_OneTimeCommissionLogic(t *testing.T) {
|
|
tx := testutils.NewTestTransaction(t)
|
|
rdb := testutils.GetTestRedis(t)
|
|
testutils.CleanTestRedisKeys(t, rdb)
|
|
|
|
ctx := context.Background()
|
|
|
|
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)
|
|
shopSeriesCommissionStatsStore := postgres.NewShopSeriesCommissionStatsStore(tx)
|
|
commissionStatsService := commission_stats.New(shopSeriesCommissionStatsStore)
|
|
logger, _ := zap.NewDevelopment()
|
|
|
|
commCalcService := commission_calculation.New(
|
|
tx,
|
|
commissionRecordStore,
|
|
shopStore,
|
|
shopSeriesAllocationStore,
|
|
shopSeriesOneTimeCommissionTierStore,
|
|
iotCardStore,
|
|
deviceStore,
|
|
walletStore,
|
|
walletTransactionStore,
|
|
orderStore,
|
|
orderItemStore,
|
|
packageStore,
|
|
commissionStatsService,
|
|
logger,
|
|
)
|
|
|
|
t.Run("单次充值触发-达到阈值时发放佣金", func(t *testing.T) {
|
|
shop := &model.Shop{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
ShopName: "单次触发店铺",
|
|
ShopCode: fmt.Sprintf("SHOP_%d", time.Now().UnixNano()),
|
|
Level: 1,
|
|
Status: constants.StatusEnabled,
|
|
}
|
|
require.NoError(t, tx.Create(shop).Error)
|
|
|
|
series := &model.PackageSeries{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
SeriesCode: fmt.Sprintf("SERIES_%d", time.Now().UnixNano()),
|
|
SeriesName: "单次触发系列",
|
|
Status: constants.StatusEnabled,
|
|
}
|
|
require.NoError(t, tx.Create(series).Error)
|
|
|
|
allocation := &model.ShopSeriesAllocation{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
ShopID: shop.ID,
|
|
SeriesID: series.ID,
|
|
AllocatorShopID: 0,
|
|
BaseCommissionMode: model.CommissionModeFixed,
|
|
BaseCommissionValue: 500,
|
|
EnableOneTimeCommission: true,
|
|
OneTimeCommissionType: model.OneTimeCommissionTypeFixed,
|
|
OneTimeCommissionTrigger: model.OneTimeCommissionTriggerSingleRecharge,
|
|
OneTimeCommissionThreshold: 5000,
|
|
OneTimeCommissionMode: model.CommissionModeFixed,
|
|
OneTimeCommissionValue: 300,
|
|
Status: constants.StatusEnabled,
|
|
}
|
|
require.NoError(t, tx.Create(allocation).Error)
|
|
|
|
card := &model.IotCard{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
ICCID: fmt.Sprintf("898600%013d", time.Now().Unix()%10000000000000+1),
|
|
CardType: "4G",
|
|
CardCategory: "normal",
|
|
CarrierID: 1,
|
|
CarrierType: "CMCC",
|
|
CarrierName: "中国移动",
|
|
Status: 3,
|
|
ShopID: &shop.ID,
|
|
SeriesAllocationID: &allocation.ID,
|
|
FirstCommissionPaid: false,
|
|
AccumulatedRecharge: 0,
|
|
}
|
|
require.NoError(t, tx.Create(card).Error)
|
|
|
|
wallet := &model.Wallet{
|
|
ResourceType: "shop",
|
|
ResourceID: shop.ID,
|
|
WalletType: "commission",
|
|
Balance: 0,
|
|
Version: 0,
|
|
Status: 1,
|
|
}
|
|
require.NoError(t, tx.Create(wallet).Error)
|
|
|
|
t.Run("单次充值未达阈值-不触发", func(t *testing.T) {
|
|
order := &model.Order{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
OrderNo: fmt.Sprintf("ORDER_%d", time.Now().UnixNano()),
|
|
OrderType: model.OrderTypeSingleCard,
|
|
BuyerType: model.BuyerTypeAgent,
|
|
BuyerID: shop.ID,
|
|
IotCardID: &card.ID,
|
|
SellerShopID: &shop.ID,
|
|
SeriesID: &series.ID,
|
|
TotalAmount: 3000,
|
|
SellerCostPrice: 2500,
|
|
PaymentStatus: model.PaymentStatusPaid,
|
|
CommissionStatus: model.CommissionStatusPending,
|
|
}
|
|
require.NoError(t, tx.Create(order).Error)
|
|
|
|
err := commCalcService.CalculateCommission(ctx, order.ID)
|
|
require.NoError(t, err)
|
|
|
|
cardAfter, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.False(t, cardAfter.FirstCommissionPaid, "单次充值未达阈值不应发放")
|
|
})
|
|
|
|
t.Run("单次充值达到阈值-触发", func(t *testing.T) {
|
|
order := &model.Order{
|
|
BaseModel: model.BaseModel{Creator: 1, Updater: 1},
|
|
OrderNo: fmt.Sprintf("ORDER_%d", time.Now().UnixNano()+1),
|
|
OrderType: model.OrderTypeSingleCard,
|
|
BuyerType: model.BuyerTypeAgent,
|
|
BuyerID: shop.ID,
|
|
IotCardID: &card.ID,
|
|
SellerShopID: &shop.ID,
|
|
SeriesID: &series.ID,
|
|
TotalAmount: 6000,
|
|
SellerCostPrice: 5500,
|
|
PaymentStatus: model.PaymentStatusPaid,
|
|
CommissionStatus: model.CommissionStatusPending,
|
|
}
|
|
require.NoError(t, tx.Create(order).Error)
|
|
|
|
err := commCalcService.CalculateCommission(ctx, order.ID)
|
|
require.NoError(t, err)
|
|
|
|
cardAfter, err := iotCardStore.GetByID(ctx, card.ID)
|
|
require.NoError(t, err)
|
|
assert.True(t, cardAfter.FirstCommissionPaid, "单次充值达到阈值应发放")
|
|
|
|
var commRecords []model.CommissionRecord
|
|
err = tx.Where("order_id = ? AND commission_source = ?", order.ID, model.CommissionSourceOneTime).
|
|
Find(&commRecords).Error
|
|
require.NoError(t, err)
|
|
assert.Len(t, commRecords, 1, "应有一条一次性佣金记录")
|
|
assert.Equal(t, int64(300), commRecords[0].Amount, "佣金金额应为300")
|
|
})
|
|
})
|
|
}
|