This commit is contained in:
2025-07-31 15:41:00 +08:00
parent f3a3bc84c7
commit 934dce2776
36 changed files with 1614 additions and 264 deletions

View File

@@ -7,12 +7,31 @@ import (
"github.com/shopspring/decimal"
"go.uber.org/zap"
"tyapi-server/internal/config"
"tyapi-server/internal/domains/finance/entities"
"tyapi-server/internal/domains/finance/repositories"
"tyapi-server/internal/shared/database"
"tyapi-server/internal/shared/interfaces"
)
// calculateAlipayRechargeBonus 计算支付宝充值赠送金额
func calculateAlipayRechargeBonus(rechargeAmount decimal.Decimal, walletConfig *config.WalletConfig) decimal.Decimal {
if walletConfig == nil || len(walletConfig.AliPayRechargeBonus) == 0 {
return decimal.Zero
}
// 按充值金额从高到低排序,找到第一个匹配的赠送规则
// 由于配置中规则是按充值金额从低到高排列的,我们需要反向遍历
for i := len(walletConfig.AliPayRechargeBonus) - 1; i >= 0; i-- {
rule := walletConfig.AliPayRechargeBonus[i]
if rechargeAmount.GreaterThanOrEqual(decimal.NewFromFloat(rule.RechargeAmount)) {
return decimal.NewFromFloat(rule.BonusAmount)
}
}
return decimal.Zero
}
// RechargeRecordService 充值记录服务接口
type RechargeRecordService interface {
// 对公转账充值
@@ -47,6 +66,7 @@ type RechargeRecordServiceImpl struct {
walletService WalletAggregateService
txManager *database.TransactionManager
logger *zap.Logger
cfg *config.Config
}
func NewRechargeRecordService(
@@ -56,6 +76,7 @@ func NewRechargeRecordService(
walletService WalletAggregateService,
txManager *database.TransactionManager,
logger *zap.Logger,
cfg *config.Config,
) RechargeRecordService {
return &RechargeRecordServiceImpl{
rechargeRecordRepo: rechargeRecordRepo,
@@ -64,6 +85,7 @@ func NewRechargeRecordService(
walletService: walletService,
txManager: txManager,
logger: logger,
cfg: cfg,
}
}
@@ -273,6 +295,10 @@ func (s *RechargeRecordServiceImpl) HandleAlipayPaymentSuccess(ctx context.Conte
return nil
}
// 计算充值赠送金额
bonusAmount := calculateAlipayRechargeBonus(amount, &s.cfg.Wallet)
totalAmount := amount.Add(bonusAmount)
// 在事务中执行所有更新操作
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
// 更新支付宝订单状态为成功
@@ -291,8 +317,22 @@ func (s *RechargeRecordServiceImpl) HandleAlipayPaymentSuccess(ctx context.Conte
return err
}
// 使用钱包聚合服务更新钱包余额
err = s.walletService.Recharge(txCtx, rechargeRecord.UserID, amount)
// 如果有赠送金额,创建赠送充值记录
if bonusAmount.GreaterThan(decimal.Zero) {
giftRechargeRecord := entities.NewGiftRechargeRecord(rechargeRecord.UserID, bonusAmount, "充值活动赠送")
_, err = s.rechargeRecordRepo.Create(txCtx, *giftRechargeRecord)
if err != nil {
s.logger.Error("创建赠送充值记录失败", zap.Error(err))
return err
}
s.logger.Info("创建赠送充值记录成功",
zap.String("user_id", rechargeRecord.UserID),
zap.String("bonus_amount", bonusAmount.String()),
zap.String("gift_recharge_id", giftRechargeRecord.ID))
}
// 使用钱包聚合服务更新钱包余额(包含赠送金额)
err = s.walletService.Recharge(txCtx, rechargeRecord.UserID, totalAmount)
if err != nil {
s.logger.Error("更新钱包余额失败", zap.String("user_id", rechargeRecord.UserID), zap.Error(err))
return err
@@ -307,7 +347,9 @@ func (s *RechargeRecordServiceImpl) HandleAlipayPaymentSuccess(ctx context.Conte
s.logger.Info("支付宝支付成功回调处理成功",
zap.String("user_id", rechargeRecord.UserID),
zap.String("amount", amount.String()),
zap.String("recharge_amount", amount.String()),
zap.String("bonus_amount", bonusAmount.String()),
zap.String("total_amount", totalAmount.String()),
zap.String("out_trade_no", outTradeNo),
zap.String("trade_no", tradeNo),
zap.String("recharge_id", rechargeRecord.ID),

View File

@@ -0,0 +1,88 @@
package services
import (
"testing"
"github.com/shopspring/decimal"
"github.com/stretchr/testify/assert"
"tyapi-server/internal/config"
)
func TestCalculateAlipayRechargeBonus(t *testing.T) {
// 创建测试配置
walletConfig := &config.WalletConfig{
AliPayRechargeBonus: []config.AliPayRechargeBonusRule{
{RechargeAmount: 1000.00, BonusAmount: 50.00}, // 充1000送50
{RechargeAmount: 5000.00, BonusAmount: 300.00}, // 充5000送300
{RechargeAmount: 10000.00, BonusAmount: 800.00}, // 充10000送800
},
}
tests := []struct {
name string
rechargeAmount decimal.Decimal
expectedBonus decimal.Decimal
}{
{
name: "充值500元无赠送",
rechargeAmount: decimal.NewFromFloat(500.00),
expectedBonus: decimal.Zero,
},
{
name: "充值1000元赠送50元",
rechargeAmount: decimal.NewFromFloat(1000.00),
expectedBonus: decimal.NewFromFloat(50.00),
},
{
name: "充值2000元赠送50元",
rechargeAmount: decimal.NewFromFloat(2000.00),
expectedBonus: decimal.NewFromFloat(50.00),
},
{
name: "充值5000元赠送300元",
rechargeAmount: decimal.NewFromFloat(5000.00),
expectedBonus: decimal.NewFromFloat(300.00),
},
{
name: "充值8000元赠送300元",
rechargeAmount: decimal.NewFromFloat(8000.00),
expectedBonus: decimal.NewFromFloat(300.00),
},
{
name: "充值10000元赠送800元",
rechargeAmount: decimal.NewFromFloat(10000.00),
expectedBonus: decimal.NewFromFloat(800.00),
},
{
name: "充值15000元赠送800元",
rechargeAmount: decimal.NewFromFloat(15000.00),
expectedBonus: decimal.NewFromFloat(800.00),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bonus := calculateAlipayRechargeBonus(tt.rechargeAmount, walletConfig)
assert.True(t, bonus.Equal(tt.expectedBonus),
"充值金额: %s, 期望赠送: %s, 实际赠送: %s",
tt.rechargeAmount.String(), tt.expectedBonus.String(), bonus.String())
})
}
}
func TestCalculateAlipayRechargeBonus_EmptyConfig(t *testing.T) {
// 测试空配置
walletConfig := &config.WalletConfig{
AliPayRechargeBonus: []config.AliPayRechargeBonusRule{},
}
bonus := calculateAlipayRechargeBonus(decimal.NewFromFloat(1000.00), walletConfig)
assert.True(t, bonus.Equal(decimal.Zero), "空配置应该返回零赠送金额")
// 测试nil配置
bonus = calculateAlipayRechargeBonus(decimal.NewFromFloat(1000.00), nil)
assert.True(t, bonus.Equal(decimal.Zero), "nil配置应该返回零赠送金额")
}