All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m18s
- 移除 IoT 卡和号卡的 card_type 字段(数据库迁移) - 优化账号列表查询,支持按店铺和企业筛选 - 账号响应增加店铺名称和企业名称字段 - 实现批量加载店铺和企业名称,避免 N+1 查询 - 更新权限检查中间件,完善权限验证逻辑 - 更新相关测试用例,确保功能正确性
1488 lines
46 KiB
Go
1488 lines
46 KiB
Go
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),
|
||
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)
|
||
})
|
||
}
|