Files
junhong_cmp_fiber/internal/service/recharge/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

1489 lines
47 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package recharge
import (
"context"
"fmt"
"testing"
"time"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"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"
"gorm.io/gorm"
)
// setupTestService 创建测试用的 Service 实例
func setupTestService(t *testing.T) (*Service, *gorm.DB) {
t.Helper()
tx := testutils.NewTestTransaction(t)
rdb := testutils.GetTestRedis(t)
testutils.CleanTestRedisKeys(t, rdb)
logger, _ := zap.NewDevelopment()
// 创建各个 Store
rechargeStore := postgres.NewRechargeStore(tx, rdb)
walletStore := postgres.NewWalletStore(tx, rdb)
walletTransactionStore := postgres.NewWalletTransactionStore(tx, rdb)
iotCardStore := postgres.NewIotCardStore(tx, rdb)
deviceStore := postgres.NewDeviceStore(tx, rdb)
shopSeriesAllocationStore := postgres.NewShopSeriesAllocationStore(tx)
commissionRecordStore := postgres.NewCommissionRecordStore(tx, rdb)
service := New(
tx,
rechargeStore,
walletStore,
walletTransactionStore,
iotCardStore,
deviceStore,
shopSeriesAllocationStore,
commissionRecordStore,
logger,
)
return service, tx
}
// createTestIotCard 创建测试用 IoT 卡
func createTestIotCard(t *testing.T, tx *gorm.DB, shopID *uint, seriesAllocationID *uint) *model.IotCard {
t.Helper()
timestamp := time.Now().UnixNano()
card := &model.IotCard{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
ICCID: fmt.Sprintf("89860%014d", timestamp%100000000000000),
CardType: "流量卡",
CardCategory: "normal",
CarrierID: 1,
CarrierType: "CMCC",
CarrierName: "中国移动",
Status: 1,
ShopID: shopID,
SeriesID: seriesAllocationID,
}
require.NoError(t, tx.Create(card).Error)
return card
}
// createTestDevice 创建测试用设备
func createTestDevice(t *testing.T, tx *gorm.DB, shopID *uint, seriesAllocationID *uint) *model.Device {
t.Helper()
timestamp := time.Now().UnixNano()
device := &model.Device{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
DeviceNo: fmt.Sprintf("DEV%014d", timestamp%100000000000000),
DeviceName: "测试设备",
DeviceType: "GPS",
Status: 1,
ShopID: shopID,
SeriesID: seriesAllocationID,
}
require.NoError(t, tx.Create(device).Error)
return device
}
// createTestWallet 创建测试用钱包
func createTestWallet(t *testing.T, tx *gorm.DB, resourceType string, resourceID uint, walletType string) *model.Wallet {
t.Helper()
wallet := &model.Wallet{
ResourceType: resourceType,
ResourceID: resourceID,
WalletType: walletType,
Balance: 0,
Currency: "CNY",
Status: 1,
Version: 0,
}
require.NoError(t, tx.Create(wallet).Error)
return wallet
}
// createTestShop 创建测试用店铺
func createTestShop(t *testing.T, tx *gorm.DB) *model.Shop {
t.Helper()
timestamp := time.Now().UnixNano()
shop := &model.Shop{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
ShopName: fmt.Sprintf("测试店铺%d", timestamp%10000),
ShopCode: fmt.Sprintf("SHOP%d", timestamp%1000000),
Level: 1,
ContactName: "测试联系人",
ContactPhone: fmt.Sprintf("138%08d", timestamp%100000000),
Status: 1,
}
require.NoError(t, tx.Create(shop).Error)
return shop
}
// createTestSeriesAllocation 创建测试用系列分配
func createTestSeriesAllocation(t *testing.T, tx *gorm.DB, shopID uint, enableOneTime bool, trigger string, threshold int64, enableForceRecharge bool, forceAmount int64) *model.ShopSeriesAllocation {
t.Helper()
allocation := &model.ShopSeriesAllocation{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
ShopID: shopID,
SeriesID: 1,
AllocatorShopID: 0,
BaseCommissionMode: "percent",
BaseCommissionValue: 100,
EnableOneTimeCommission: enableOneTime,
OneTimeCommissionType: "fixed",
OneTimeCommissionTrigger: trigger,
OneTimeCommissionThreshold: threshold,
OneTimeCommissionMode: "fixed",
OneTimeCommissionValue: 5000, // 50元佣金
EnableForceRecharge: enableForceRecharge,
ForceRechargeAmount: forceAmount,
Status: 1,
}
require.NoError(t, tx.Create(allocation).Error)
return allocation
}
// TestService_Create 测试创建充值订单
func TestService_Create(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("成功创建充值订单_iot_card", func(t *testing.T) {
// 准备测试数据
card := createTestIotCard(t, tx, nil, nil)
createTestWallet(t, tx, "iot_card", card.ID, "main")
// 创建充值订单
req := &dto.CreateRechargeRequest{
ResourceType: "iot_card",
ResourceID: card.ID,
Amount: 10000, // 100元
PaymentMethod: "wechat",
}
resp, err := service.Create(ctx, req, 1)
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, int64(10000), resp.Amount)
assert.Equal(t, "wechat", resp.PaymentMethod)
assert.Equal(t, constants.RechargeStatusPending, resp.Status)
assert.True(t, len(resp.RechargeNo) > 0)
assert.Contains(t, resp.RechargeNo, "RCH")
})
t.Run("成功创建充值订单_device", func(t *testing.T) {
// 准备测试数据
device := createTestDevice(t, tx, nil, nil)
createTestWallet(t, tx, "device", device.ID, "main")
// 创建充值订单
req := &dto.CreateRechargeRequest{
ResourceType: "device",
ResourceID: device.ID,
Amount: 5000, // 50元
PaymentMethod: "alipay",
}
resp, err := service.Create(ctx, req, 1)
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, int64(5000), resp.Amount)
assert.Equal(t, "alipay", resp.PaymentMethod)
})
t.Run("金额低于最小值", func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
createTestWallet(t, tx, "iot_card", card.ID, "main")
req := &dto.CreateRechargeRequest{
ResourceType: "iot_card",
ResourceID: card.ID,
Amount: 50, // 0.5元低于1元
PaymentMethod: "wechat",
}
resp, err := service.Create(ctx, req, 1)
assert.Error(t, err)
assert.Nil(t, resp)
assert.Contains(t, err.Error(), "充值金额不能低于1元")
})
t.Run("金额超过最大值", func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
createTestWallet(t, tx, "iot_card", card.ID, "main")
req := &dto.CreateRechargeRequest{
ResourceType: "iot_card",
ResourceID: card.ID,
Amount: 20000000, // 200000元超过100000元
PaymentMethod: "wechat",
}
resp, err := service.Create(ctx, req, 1)
assert.Error(t, err)
assert.Nil(t, resp)
assert.Contains(t, err.Error(), "充值金额不能超过100000元")
})
t.Run("无效的资源类型", func(t *testing.T) {
req := &dto.CreateRechargeRequest{
ResourceType: "invalid",
ResourceID: 1,
Amount: 10000,
PaymentMethod: "wechat",
}
resp, err := service.Create(ctx, req, 1)
assert.Error(t, err)
assert.Nil(t, resp)
assert.Contains(t, err.Error(), "无效的资源类型")
})
t.Run("钱包不存在", func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
// 不创建钱包
req := &dto.CreateRechargeRequest{
ResourceType: "iot_card",
ResourceID: card.ID,
Amount: 10000,
PaymentMethod: "wechat",
}
resp, err := service.Create(ctx, req, 1)
assert.Error(t, err)
assert.Nil(t, resp)
assert.Contains(t, err.Error(), "钱包不存在")
})
t.Run("强充金额不匹配_单次充值", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 10000, false, 0)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
createTestWallet(t, tx, "iot_card", card.ID, "main")
req := &dto.CreateRechargeRequest{
ResourceType: "iot_card",
ResourceID: card.ID,
Amount: 5000, // 50元但需要100元
PaymentMethod: "wechat",
}
resp, err := service.Create(ctx, req, 1)
assert.Error(t, err)
assert.Nil(t, resp)
assert.Contains(t, err.Error(), "强充要求")
})
t.Run("强充金额匹配成功_单次充值", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 10000, false, 0)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
createTestWallet(t, tx, "iot_card", card.ID, "main")
req := &dto.CreateRechargeRequest{
ResourceType: "iot_card",
ResourceID: card.ID,
Amount: 10000, // 100元符合要求
PaymentMethod: "wechat",
}
resp, err := service.Create(ctx, req, 1)
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, int64(10000), resp.Amount)
})
}
// TestService_GetRechargeCheck 测试充值预检
func TestService_GetRechargeCheck(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("无强充要求_无系列分配", func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
result, err := service.GetRechargeCheck(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
assert.Equal(t, int64(constants.RechargeMinAmount), result.MinAmount)
assert.Equal(t, int64(constants.RechargeMaxAmount), result.MaxAmount)
})
t.Run("需要强充_单次充值触发", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 10000, false, 0)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
result, err := service.GetRechargeCheck(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.True(t, result.NeedForceRecharge)
assert.Equal(t, int64(10000), result.ForceRechargeAmount)
assert.Equal(t, "single_recharge", result.TriggerType)
})
t.Run("需要强充_累计充值启用强充", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "accumulated_recharge", 50000, true, 10000)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
result, err := service.GetRechargeCheck(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.True(t, result.NeedForceRecharge)
assert.Equal(t, int64(10000), result.ForceRechargeAmount) // 使用配置的强充金额
assert.Equal(t, "accumulated_recharge", result.TriggerType)
})
t.Run("无强充要求_累计充值未启用强充", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "accumulated_recharge", 50000, false, 0)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
result, err := service.GetRechargeCheck(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
assert.Contains(t, result.Message, "可自由充值")
})
t.Run("无效的资源类型", func(t *testing.T) {
result, err := service.GetRechargeCheck(ctx, "invalid", 1)
assert.Error(t, err)
assert.Nil(t, result)
assert.Contains(t, err.Error(), "无效的资源类型")
})
t.Run("资源不存在", func(t *testing.T) {
result, err := service.GetRechargeCheck(ctx, "iot_card", 999999)
assert.Error(t, err)
assert.Nil(t, result)
})
}
// TestService_GetByID 测试根据ID查询充值订单
func TestService_GetByID(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("成功查询", func(t *testing.T) {
// 准备测试数据
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
// 创建充值订单
timestamp := time.Now().UnixNano()
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: fmt.Sprintf("RCH%d", timestamp),
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 查询
resp, err := service.GetByID(ctx, recharge.ID, 1)
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, recharge.ID, resp.ID)
assert.Equal(t, int64(10000), resp.Amount)
})
t.Run("订单不存在", func(t *testing.T) {
resp, err := service.GetByID(ctx, 999999, 1)
assert.Error(t, err)
assert.Nil(t, resp)
assert.Contains(t, err.Error(), "充值订单不存在")
})
t.Run("无权查看他人订单", func(t *testing.T) {
// 准备测试数据
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
// 创建用户1的订单
timestamp := time.Now().UnixNano()
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: fmt.Sprintf("RCH%d", timestamp),
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 用户2尝试查询
resp, err := service.GetByID(ctx, recharge.ID, 2)
assert.Error(t, err)
assert.Nil(t, resp)
assert.Contains(t, err.Error(), "无权查看")
})
}
// TestService_List 测试查询充值订单列表
func TestService_List(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("查询用户订单列表", func(t *testing.T) {
// 准备测试数据
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
// 创建多个订单
for i := 0; i < 3; i++ {
timestamp := time.Now().UnixNano()
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 100,
Updater: 100,
},
UserID: 100,
WalletID: wallet.ID,
RechargeNo: fmt.Sprintf("RCH%d%d", timestamp, i),
Amount: int64((i + 1) * 1000),
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
}
// 查询
req := &dto.RechargeListRequest{
Page: 1,
PageSize: 10,
}
resp, err := service.List(ctx, req, 100)
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, int64(3), resp.Total)
assert.Len(t, resp.List, 3)
})
t.Run("状态筛选", func(t *testing.T) {
// 准备测试数据
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
// 创建不同状态的订单
for i, status := range []int{constants.RechargeStatusPending, constants.RechargeStatusPaid, constants.RechargeStatusCompleted} {
timestamp := time.Now().UnixNano()
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 101,
Updater: 101,
},
UserID: 101,
WalletID: wallet.ID,
RechargeNo: fmt.Sprintf("RCH%d%d", timestamp, i),
Amount: 10000,
PaymentMethod: "wechat",
Status: status,
}
require.NoError(t, tx.Create(recharge).Error)
}
// 筛选待支付
pendingStatus := constants.RechargeStatusPending
req := &dto.RechargeListRequest{
Page: 1,
PageSize: 10,
Status: &pendingStatus,
}
resp, err := service.List(ctx, req, 101)
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, int64(1), resp.Total)
})
}
// TestService_HandlePaymentCallback 测试支付回调处理
func TestService_HandlePaymentCallback(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("成功处理支付回调_无佣金", func(t *testing.T) {
// 准备测试数据
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
// 创建待支付订单
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 处理回调
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
require.NoError(t, err)
// 验证订单状态
var updatedRecharge model.RechargeRecord
err = tx.First(&updatedRecharge, recharge.ID).Error
require.NoError(t, err)
assert.Equal(t, constants.RechargeStatusCompleted, updatedRecharge.Status)
// 验证钱包余额
var updatedWallet model.Wallet
err = tx.First(&updatedWallet, wallet.ID).Error
require.NoError(t, err)
assert.Equal(t, int64(10000), updatedWallet.Balance)
})
t.Run("幂等性_已支付订单重复回调", func(t *testing.T) {
// 准备测试数据
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
wallet.Balance = 10000
tx.Save(wallet)
// 创建已完成订单
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusCompleted,
}
require.NoError(t, tx.Create(recharge).Error)
// 重复处理回调
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
require.NoError(t, err) // 应该成功(幂等)
// 验证钱包余额没有变化
var updatedWallet model.Wallet
err = tx.First(&updatedWallet, wallet.ID).Error
require.NoError(t, err)
assert.Equal(t, int64(10000), updatedWallet.Balance) // 余额不变
})
t.Run("订单不存在", func(t *testing.T) {
err := service.HandlePaymentCallback(ctx, "RCH_NOT_EXISTS", "wechat", "TX123456")
assert.Error(t, err)
assert.Contains(t, err.Error(), "充值订单不存在")
})
t.Run("订单状态不允许支付", func(t *testing.T) {
// 准备测试数据
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
// 创建已关闭订单
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusClosed,
}
require.NoError(t, tx.Create(recharge).Error)
// 处理回调
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
assert.Error(t, err)
assert.Contains(t, err.Error(), "订单状态不允许支付")
})
t.Run("成功处理支付回调_触发一次性佣金", func(t *testing.T) {
// 准备测试数据
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 10000, false, 0)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
createTestWallet(t, tx, "shop", shop.ID, "commission") // 店铺佣金钱包
// 创建待支付订单
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000, // 符合阈值
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 处理回调
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
require.NoError(t, err)
// 验证卡的 FirstCommissionPaid 已更新
var updatedCard model.IotCard
err = tx.First(&updatedCard, card.ID).Error
require.NoError(t, err)
assert.True(t, updatedCard.FirstCommissionPaid)
// 验证累计充值已更新
assert.Equal(t, int64(10000), updatedCard.AccumulatedRecharge)
// 验证佣金记录已创建
var commissionRecords []model.CommissionRecord
err = tx.Where("iot_card_id = ?", card.ID).Find(&commissionRecords).Error
require.NoError(t, err)
assert.Len(t, commissionRecords, 1)
assert.Equal(t, int64(5000), commissionRecords[0].Amount) // 50元佣金
// 验证店铺佣金钱包余额
var shopWallet model.Wallet
err = tx.Where("resource_type = ? AND resource_id = ? AND wallet_type = ?", "shop", shop.ID, "commission").First(&shopWallet).Error
require.NoError(t, err)
assert.Equal(t, int64(5000), shopWallet.Balance)
})
t.Run("累计充值触发佣金", func(t *testing.T) {
// 准备测试数据
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "accumulated_recharge", 15000, false, 0)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
// 设置初始累计充值
card.AccumulatedRecharge = 10000
tx.Save(card)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
createTestWallet(t, tx, "shop", shop.ID, "commission")
// 创建待支付订单
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 5000, // 再充50元累计150元达到阈值
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 处理回调
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
require.NoError(t, err)
// 验证累计充值已更新
var updatedCard model.IotCard
err = tx.First(&updatedCard, card.ID).Error
require.NoError(t, err)
assert.Equal(t, int64(15000), updatedCard.AccumulatedRecharge)
assert.True(t, updatedCard.FirstCommissionPaid)
})
}
// TestService_generateRechargeNo 测试生成充值订单号
func TestService_generateRechargeNo(t *testing.T) {
service, _ := setupTestService(t)
t.Run("订单号格式正确", func(t *testing.T) {
rechargeNo := service.generateRechargeNo()
assert.True(t, len(rechargeNo) > 0)
assert.Contains(t, rechargeNo, "RCH")
// RCH + 14位时间戳 + 6位随机数 = 23位
assert.Equal(t, 23, len(rechargeNo))
})
t.Run("订单号唯一性", func(t *testing.T) {
nos := make(map[string]bool)
for i := 0; i < 100; i++ {
no := service.generateRechargeNo()
assert.False(t, nos[no], "订单号应唯一: %s", no)
nos[no] = true
}
})
}
// TestService_checkForceRechargeRequirement 测试强充验证逻辑
func TestService_checkForceRechargeRequirement(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("一次性佣金已发放_无需强充", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 10000, false, 0)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
// 标记佣金已发放
card.FirstCommissionPaid = true
tx.Save(card)
result, err := service.checkForceRechargeRequirement(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
assert.True(t, result.FirstCommissionPaid)
})
t.Run("一次性佣金未启用_无需强充", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, false, "", 0, false, 0)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
result, err := service.checkForceRechargeRequirement(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
})
t.Run("设备资源类型", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 10000, false, 0)
device := createTestDevice(t, tx, &shop.ID, &allocation.ID)
result, err := service.checkForceRechargeRequirement(ctx, "device", device.ID)
require.NoError(t, err)
assert.True(t, result.NeedForceRecharge)
assert.Equal(t, int64(10000), result.ForceRechargeAmount)
})
}
// TestService_calculateOneTimeCommission 测试计算一次性佣金
func TestService_calculateOneTimeCommission(t *testing.T) {
service, _ := setupTestService(t)
t.Run("固定金额佣金", func(t *testing.T) {
allocation := &model.ShopSeriesAllocation{
OneTimeCommissionType: "fixed",
OneTimeCommissionMode: "fixed",
OneTimeCommissionValue: 5000, // 50元
}
amount := service.calculateOneTimeCommission(allocation, 10000)
assert.Equal(t, int64(5000), amount)
})
t.Run("百分比佣金", func(t *testing.T) {
allocation := &model.ShopSeriesAllocation{
OneTimeCommissionType: "fixed",
OneTimeCommissionMode: "percent",
OneTimeCommissionValue: 100, // 10%
}
amount := service.calculateOneTimeCommission(allocation, 10000)
assert.Equal(t, int64(1000), amount) // 10000 * 100 / 1000 = 1000
})
t.Run("梯度佣金不处理", func(t *testing.T) {
allocation := &model.ShopSeriesAllocation{
OneTimeCommissionType: "tiered",
OneTimeCommissionMode: "fixed",
OneTimeCommissionValue: 5000,
}
amount := service.calculateOneTimeCommission(allocation, 10000)
assert.Equal(t, int64(0), amount) // 梯度佣金返回0由其他服务处理
})
}
// TestService_GetRechargeCheck_Device 测试设备类型的充值预检
func TestService_GetRechargeCheck_Device(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("设备资源_无强充要求", func(t *testing.T) {
device := createTestDevice(t, tx, nil, nil)
result, err := service.GetRechargeCheck(ctx, "device", device.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
assert.Equal(t, int64(constants.RechargeMinAmount), result.MinAmount)
assert.Equal(t, int64(constants.RechargeMaxAmount), result.MaxAmount)
})
t.Run("设备资源_需要强充", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 20000, false, 0)
device := createTestDevice(t, tx, &shop.ID, &allocation.ID)
result, err := service.GetRechargeCheck(ctx, "device", device.ID)
require.NoError(t, err)
assert.True(t, result.NeedForceRecharge)
assert.Equal(t, int64(20000), result.ForceRechargeAmount)
})
t.Run("设备资源不存在", func(t *testing.T) {
result, err := service.GetRechargeCheck(ctx, "device", 999999)
assert.Error(t, err)
assert.Nil(t, result)
assert.Contains(t, err.Error(), "设备不存在")
})
}
// TestService_List_MoreFilters 测试更多列表筛选条件
func TestService_List_MoreFilters(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("钱包ID筛选", func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
// 创建订单
timestamp := time.Now().UnixNano()
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 200,
Updater: 200,
},
UserID: 200,
WalletID: wallet.ID,
RechargeNo: fmt.Sprintf("RCH%d", timestamp),
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 按钱包ID筛选
req := &dto.RechargeListRequest{
Page: 1,
PageSize: 10,
WalletID: &wallet.ID,
}
resp, err := service.List(ctx, req, 200)
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, int64(1), resp.Total)
})
t.Run("时间范围筛选", func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
// 创建订单
timestamp := time.Now().UnixNano()
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 201,
Updater: 201,
},
UserID: 201,
WalletID: wallet.ID,
RechargeNo: fmt.Sprintf("RCH%d", timestamp),
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 按时间范围筛选
startTime := time.Now().Add(-1 * time.Hour)
endTime := time.Now().Add(1 * time.Hour)
req := &dto.RechargeListRequest{
Page: 1,
PageSize: 10,
StartTime: &startTime,
EndTime: &endTime,
}
resp, err := service.List(ctx, req, 201)
require.NoError(t, err)
assert.NotNil(t, resp)
assert.GreaterOrEqual(t, resp.Total, int64(1))
})
t.Run("默认分页参数", func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
// 创建订单
timestamp := time.Now().UnixNano()
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 202,
Updater: 202,
},
UserID: 202,
WalletID: wallet.ID,
RechargeNo: fmt.Sprintf("RCH%d", timestamp),
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 不传分页参数
req := &dto.RechargeListRequest{}
resp, err := service.List(ctx, req, 202)
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 1, resp.Page)
assert.Equal(t, constants.DefaultPageSize, resp.PageSize)
})
}
// TestService_HandlePaymentCallback_Device 测试设备类型的支付回调
func TestService_HandlePaymentCallback_Device(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("设备充值_触发佣金", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 10000, false, 0)
device := createTestDevice(t, tx, &shop.ID, &allocation.ID)
wallet := createTestWallet(t, tx, "device", device.ID, "main")
createTestWallet(t, tx, "shop", shop.ID, "commission")
// 创建待支付订单
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 处理回调
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
require.NoError(t, err)
// 验证设备的 FirstCommissionPaid 已更新
var updatedDevice model.Device
err = tx.First(&updatedDevice, device.ID).Error
require.NoError(t, err)
assert.True(t, updatedDevice.FirstCommissionPaid)
assert.Equal(t, int64(10000), updatedDevice.AccumulatedRecharge)
})
t.Run("设备充值_无店铺归属_跳过佣金", func(t *testing.T) {
// 创建无店铺归属的设备
allocation := createTestSeriesAllocation(t, tx, 1, true, "single_recharge", 10000, false, 0)
device := createTestDevice(t, tx, nil, &allocation.ID) // 无店铺
wallet := createTestWallet(t, tx, "device", device.ID, "main")
// 创建待支付订单
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 处理回调 - 应该成功但不发放佣金
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
require.NoError(t, err)
// 验证钱包余额已更新
var updatedWallet model.Wallet
err = tx.First(&updatedWallet, wallet.ID).Error
require.NoError(t, err)
assert.Equal(t, int64(10000), updatedWallet.Balance)
})
}
// TestService_buildRechargeResponse_AllStatus 测试所有状态的响应构建
func TestService_buildRechargeResponse_AllStatus(t *testing.T) {
service, tx := setupTestService(t)
testCases := []struct {
status int
statusText string
}{
{constants.RechargeStatusPending, "待支付"},
{constants.RechargeStatusPaid, "已支付"},
{constants.RechargeStatusCompleted, "已完成"},
{constants.RechargeStatusClosed, "已关闭"},
{constants.RechargeStatusRefunded, "已退款"},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("状态_%s", tc.statusText), func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
now := time.Now()
paymentChannel := "jsapi"
paymentTxID := "TX123"
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: fmt.Sprintf("RCH%d%d", time.Now().UnixNano(), tc.status),
Amount: 10000,
PaymentMethod: "wechat",
PaymentChannel: &paymentChannel,
PaymentTransactionID: &paymentTxID,
Status: tc.status,
PaidAt: &now,
CompletedAt: &now,
}
require.NoError(t, tx.Create(recharge).Error)
resp := service.buildRechargeResponse(recharge)
assert.Equal(t, tc.status, resp.Status)
assert.Equal(t, tc.statusText, resp.StatusText)
assert.Equal(t, "wechat", resp.PaymentMethod)
assert.NotNil(t, resp.PaymentChannel)
assert.Equal(t, "jsapi", *resp.PaymentChannel)
assert.NotNil(t, resp.PaymentTransactionID)
assert.Equal(t, "TX123", *resp.PaymentTransactionID)
assert.NotNil(t, resp.PaidAt)
assert.NotNil(t, resp.CompletedAt)
})
}
}
// TestService_Create_Device 测试设备类型的创建
func TestService_Create_Device(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("设备钱包不存在", func(t *testing.T) {
device := createTestDevice(t, tx, nil, nil)
req := &dto.CreateRechargeRequest{
ResourceType: "device",
ResourceID: device.ID,
Amount: 10000,
PaymentMethod: "wechat",
}
resp, err := service.Create(ctx, req, 1)
assert.Error(t, err)
assert.Nil(t, resp)
assert.Contains(t, err.Error(), "钱包不存在")
})
t.Run("设备强充金额不匹配", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 20000, false, 0)
device := createTestDevice(t, tx, &shop.ID, &allocation.ID)
createTestWallet(t, tx, "device", device.ID, "main")
req := &dto.CreateRechargeRequest{
ResourceType: "device",
ResourceID: device.ID,
Amount: 10000,
PaymentMethod: "wechat",
}
resp, err := service.Create(ctx, req, 1)
assert.Error(t, err)
assert.Nil(t, resp)
assert.Contains(t, err.Error(), "强充要求")
})
t.Run("设备成功创建充值订单", func(t *testing.T) {
device := createTestDevice(t, tx, nil, nil)
createTestWallet(t, tx, "device", device.ID, "main")
req := &dto.CreateRechargeRequest{
ResourceType: "device",
ResourceID: device.ID,
Amount: 10000,
PaymentMethod: "alipay",
}
resp, err := service.Create(ctx, req, 1)
require.NoError(t, err)
assert.NotNil(t, resp)
assert.Equal(t, int64(10000), resp.Amount)
assert.Equal(t, "alipay", resp.PaymentMethod)
})
}
// TestService_checkForceRechargeRequirement_MoreCases 测试更多强充验证场景
func TestService_checkForceRechargeRequirement_MoreCases(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("无系列分配_无需强充", func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
result, err := service.checkForceRechargeRequirement(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
})
t.Run("系列分配不存在_无需强充", func(t *testing.T) {
nonExistentID := uint(999999)
card := createTestIotCard(t, tx, nil, &nonExistentID)
result, err := service.checkForceRechargeRequirement(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
})
t.Run("累计充值触发_启用强充", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "accumulated_recharge", 50000, true, 15000)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
result, err := service.checkForceRechargeRequirement(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.True(t, result.NeedForceRecharge)
assert.Equal(t, int64(15000), result.ForceRechargeAmount)
assert.Equal(t, "accumulated_recharge", result.TriggerType)
})
t.Run("未知触发类型_无需强充", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := &model.ShopSeriesAllocation{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
ShopID: shop.ID,
SeriesID: 1,
AllocatorShopID: 0,
BaseCommissionMode: "percent",
BaseCommissionValue: 100,
EnableOneTimeCommission: true,
OneTimeCommissionType: "fixed",
OneTimeCommissionTrigger: "unknown_trigger",
OneTimeCommissionThreshold: 10000,
OneTimeCommissionMode: "fixed",
OneTimeCommissionValue: 5000,
EnableForceRecharge: false,
ForceRechargeAmount: 0,
Status: 1,
}
require.NoError(t, tx.Create(allocation).Error)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
result, err := service.checkForceRechargeRequirement(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
})
t.Run("设备_累计充值触发_启用强充", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "accumulated_recharge", 50000, true, 15000)
device := createTestDevice(t, tx, &shop.ID, &allocation.ID)
result, err := service.checkForceRechargeRequirement(ctx, "device", device.ID)
require.NoError(t, err)
assert.True(t, result.NeedForceRecharge)
assert.Equal(t, int64(15000), result.ForceRechargeAmount)
})
t.Run("设备_佣金已发放_无需强充", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 10000, false, 0)
device := createTestDevice(t, tx, &shop.ID, &allocation.ID)
device.FirstCommissionPaid = true
tx.Save(device)
result, err := service.checkForceRechargeRequirement(ctx, "device", device.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
assert.True(t, result.FirstCommissionPaid)
})
t.Run("设备_系列分配不存在_无需强充", func(t *testing.T) {
nonExistentID := uint(999999)
device := createTestDevice(t, tx, nil, &nonExistentID)
result, err := service.checkForceRechargeRequirement(ctx, "device", device.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
})
t.Run("设备_无系列分配_无需强充", func(t *testing.T) {
device := createTestDevice(t, tx, nil, nil)
result, err := service.checkForceRechargeRequirement(ctx, "device", device.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
})
t.Run("设备_一次性佣金未启用_无需强充", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, false, "", 0, false, 0)
device := createTestDevice(t, tx, &shop.ID, &allocation.ID)
result, err := service.checkForceRechargeRequirement(ctx, "device", device.ID)
require.NoError(t, err)
assert.False(t, result.NeedForceRecharge)
})
t.Run("累计充值触发_启用强充_无配置金额_使用阈值", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := &model.ShopSeriesAllocation{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
ShopID: shop.ID,
SeriesID: 1,
AllocatorShopID: 0,
BaseCommissionMode: "percent",
BaseCommissionValue: 100,
EnableOneTimeCommission: true,
OneTimeCommissionType: "fixed",
OneTimeCommissionTrigger: "accumulated_recharge",
OneTimeCommissionThreshold: 50000,
OneTimeCommissionMode: "fixed",
OneTimeCommissionValue: 5000,
EnableForceRecharge: true,
ForceRechargeAmount: 0,
Status: 1,
}
require.NoError(t, tx.Create(allocation).Error)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
result, err := service.checkForceRechargeRequirement(ctx, "iot_card", card.ID)
require.NoError(t, err)
assert.True(t, result.NeedForceRecharge)
assert.Equal(t, int64(50000), result.ForceRechargeAmount)
})
}
// TestService_HandlePaymentCallback_MoreCases 测试更多支付回调场景
func TestService_HandlePaymentCallback_MoreCases(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("已支付状态_幂等返回成功", func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPaid,
}
require.NoError(t, tx.Create(recharge).Error)
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
require.NoError(t, err)
})
t.Run("已退款状态_不允许支付", func(t *testing.T) {
card := createTestIotCard(t, tx, nil, nil)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusRefunded,
}
require.NoError(t, tx.Create(recharge).Error)
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
assert.Error(t, err)
assert.Contains(t, err.Error(), "订单状态不允许支付")
})
}
// TestService_triggerOneTimeCommission_EdgeCases 测试一次性佣金触发的边界情况
func TestService_triggerOneTimeCommission_EdgeCases(t *testing.T) {
service, tx := setupTestService(t)
ctx := context.Background()
t.Run("佣金金额为0_不发放", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := &model.ShopSeriesAllocation{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
ShopID: shop.ID,
SeriesID: 1,
AllocatorShopID: 0,
BaseCommissionMode: "percent",
BaseCommissionValue: 100,
EnableOneTimeCommission: true,
OneTimeCommissionType: "fixed",
OneTimeCommissionTrigger: "single_recharge",
OneTimeCommissionThreshold: 10000,
OneTimeCommissionMode: "fixed",
OneTimeCommissionValue: 0, // 佣金金额为0
EnableForceRecharge: false,
ForceRechargeAmount: 0,
Status: 1,
}
require.NoError(t, tx.Create(allocation).Error)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
createTestWallet(t, tx, "shop", shop.ID, "commission")
// 创建待支付订单
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 处理回调
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
require.NoError(t, err)
// 验证没有创建佣金记录
var commissionRecords []model.CommissionRecord
err = tx.Where("iot_card_id = ?", card.ID).Find(&commissionRecords).Error
require.NoError(t, err)
assert.Len(t, commissionRecords, 0)
})
t.Run("未达到阈值_不触发佣金", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 20000, false, 0) // 阈值200元
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
createTestWallet(t, tx, "shop", shop.ID, "commission")
// 创建待支付订单(金额低于阈值)
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000, // 100元低于200元阈值
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 处理回调
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
require.NoError(t, err)
// 验证没有创建佣金记录
var commissionRecords []model.CommissionRecord
err = tx.Where("iot_card_id = ?", card.ID).Find(&commissionRecords).Error
require.NoError(t, err)
assert.Len(t, commissionRecords, 0)
// 验证 FirstCommissionPaid 未更新
var updatedCard model.IotCard
err = tx.First(&updatedCard, card.ID).Error
require.NoError(t, err)
assert.False(t, updatedCard.FirstCommissionPaid)
})
t.Run("店铺佣金钱包不存在_跳过佣金", func(t *testing.T) {
shop := createTestShop(t, tx)
allocation := createTestSeriesAllocation(t, tx, shop.ID, true, "single_recharge", 10000, false, 0)
card := createTestIotCard(t, tx, &shop.ID, &allocation.ID)
wallet := createTestWallet(t, tx, "iot_card", card.ID, "main")
// 不创建店铺佣金钱包
// 创建待支付订单
rechargeNo := fmt.Sprintf("RCH%d", time.Now().UnixNano())
recharge := &model.RechargeRecord{
BaseModel: model.BaseModel{
Creator: 1,
Updater: 1,
},
UserID: 1,
WalletID: wallet.ID,
RechargeNo: rechargeNo,
Amount: 10000,
PaymentMethod: "wechat",
Status: constants.RechargeStatusPending,
}
require.NoError(t, tx.Create(recharge).Error)
// 处理回调 - 应该成功但不发放佣金
err := service.HandlePaymentCallback(ctx, rechargeNo, "wechat", "TX123456")
require.NoError(t, err)
// 验证钱包余额已更新
var updatedWallet model.Wallet
err = tx.First(&updatedWallet, wallet.ID).Error
require.NoError(t, err)
assert.Equal(t, int64(10000), updatedWallet.Balance)
})
}