refactor: 重构用户模型和密码验证逻辑 feat(epay): 添加支付类型支持 docs: 添加安装和配置指南文档 fix: 修复优惠券过期检查逻辑 perf: 优化设备解绑缓存清理流程 test: 添加密码验证测试用例 chore: 更新依赖版本
This commit is contained in:
@@ -179,8 +179,8 @@ func (l *ActivateOrderLogic) NewPurchase(ctx context.Context, orderInfo *order.O
|
||||
return err
|
||||
}
|
||||
|
||||
// Handle referral reward in separate goroutine to avoid blocking
|
||||
go l.handleReferralReward(context.Background(), userInfo, orderInfo)
|
||||
// Handle commission in separate goroutine to avoid blocking
|
||||
go l.handleCommission(context.Background(), userInfo, orderInfo)
|
||||
|
||||
// Clear cache
|
||||
l.clearServerCache(ctx, sub)
|
||||
@@ -192,12 +192,12 @@ func (l *ActivateOrderLogic) NewPurchase(ctx context.Context, orderInfo *order.O
|
||||
return nil
|
||||
}
|
||||
|
||||
// getUserOrCreate retrieves an existing user or creates a new user based on order details
|
||||
// getUserOrCreate retrieves an existing user or creates a new guest user based on order details
|
||||
func (l *ActivateOrderLogic) getUserOrCreate(ctx context.Context, orderInfo *order.Order) (*user.User, error) {
|
||||
if orderInfo.UserId != 0 {
|
||||
return l.getExistingUser(ctx, orderInfo.UserId)
|
||||
}
|
||||
return l.createUserFromTempOrder(ctx, orderInfo)
|
||||
return l.createGuestUser(ctx, orderInfo)
|
||||
}
|
||||
|
||||
// getExistingUser retrieves user information by user ID
|
||||
@@ -213,9 +213,9 @@ func (l *ActivateOrderLogic) getExistingUser(ctx context.Context, userId int64)
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
// createUserFromTempOrder creates a new user account using temporary order information
|
||||
// stored in Redis cache. All users created this way are formal users, not guests.
|
||||
func (l *ActivateOrderLogic) createUserFromTempOrder(ctx context.Context, orderInfo *order.Order) (*user.User, error) {
|
||||
// createGuestUser creates a new user account for guest orders using temporary order information
|
||||
// stored in Redis cache
|
||||
func (l *ActivateOrderLogic) createGuestUser(ctx context.Context, orderInfo *order.Order) (*user.User, error) {
|
||||
tempOrder, err := l.getTempOrderInfo(ctx, orderInfo.OrderNo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -223,6 +223,7 @@ func (l *ActivateOrderLogic) createUserFromTempOrder(ctx context.Context, orderI
|
||||
|
||||
userInfo := &user.User{
|
||||
Password: tool.EncodePassWord(tempOrder.Password),
|
||||
Algo: "default",
|
||||
AuthMethods: []user.AuthMethods{
|
||||
{
|
||||
AuthType: tempOrder.AuthType,
|
||||
@@ -253,7 +254,7 @@ func (l *ActivateOrderLogic) createUserFromTempOrder(ctx context.Context, orderI
|
||||
// Handle referrer relationship
|
||||
l.handleReferrer(ctx, userInfo, tempOrder.InviteCode)
|
||||
|
||||
logger.WithContext(ctx).Info("Create user success",
|
||||
logger.WithContext(ctx).Info("Create guest user success",
|
||||
logger.Field("user_id", userInfo.Id),
|
||||
logger.Field("identifier", tempOrder.Identifier),
|
||||
logger.Field("auth_type", tempOrder.AuthType),
|
||||
@@ -349,12 +350,10 @@ func (l *ActivateOrderLogic) createUserSubscription(ctx context.Context, orderIn
|
||||
return userSub, nil
|
||||
}
|
||||
|
||||
// handleReferralReward processes referral rewards for the referrer if applicable.
|
||||
// handleCommission processes referral commission for the referrer if applicable.
|
||||
// This runs asynchronously to avoid blocking the main order processing flow.
|
||||
// If referral percentage > 0: commission reward
|
||||
// If referral percentage = 0: gift days to both parties
|
||||
func (l *ActivateOrderLogic) handleReferralReward(ctx context.Context, userInfo *user.User, orderInfo *order.Order) {
|
||||
if !l.shouldProcessReferralReward(userInfo, orderInfo.IsNew) {
|
||||
func (l *ActivateOrderLogic) handleCommission(ctx context.Context, userInfo *user.User, orderInfo *order.Order) {
|
||||
if !l.shouldProcessCommission(userInfo, orderInfo.IsNew) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -374,25 +373,13 @@ func (l *ActivateOrderLogic) handleReferralReward(ctx context.Context, userInfo
|
||||
referralPercentage = uint8(l.svc.Config.Invite.ReferralPercentage)
|
||||
}
|
||||
|
||||
// Check if this is commission reward or gift days reward
|
||||
if referralPercentage > 0 {
|
||||
// Commission reward mode
|
||||
l.processCommissionReward(ctx, referer, orderInfo, referralPercentage)
|
||||
} else {
|
||||
// Gift days reward mode
|
||||
l.processGiftDaysReward(ctx, referer, userInfo, orderInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// processCommissionReward handles commission-based rewards
|
||||
func (l *ActivateOrderLogic) processCommissionReward(ctx context.Context, referer *user.User, orderInfo *order.Order, percentage uint8) {
|
||||
// Order commission calculation: (Order Amount - Order Fee) * Referral Percentage
|
||||
amount := l.calculateCommission(orderInfo.Amount-orderInfo.FeeAmount, percentage)
|
||||
amount := l.calculateCommission(orderInfo.Amount-orderInfo.FeeAmount, referralPercentage)
|
||||
|
||||
// Use transaction for commission updates
|
||||
err := l.svc.DB.Transaction(func(tx *gorm.DB) error {
|
||||
err = l.svc.DB.Transaction(func(tx *gorm.DB) error {
|
||||
referer.Commission += amount
|
||||
if err := l.svc.UserModel.Update(ctx, referer, tx); err != nil {
|
||||
if err = l.svc.UserModel.Update(ctx, referer, tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -434,73 +421,9 @@ func (l *ActivateOrderLogic) processCommissionReward(ctx context.Context, refere
|
||||
}
|
||||
}
|
||||
|
||||
// processGiftDaysReward handles gift days rewards for both parties
|
||||
func (l *ActivateOrderLogic) processGiftDaysReward(ctx context.Context, referer *user.User, referee *user.User, orderInfo *order.Order) {
|
||||
giftDays := l.svc.Config.Invite.GiftDays
|
||||
if giftDays <= 0 {
|
||||
giftDays = 3 // Default to 3 days
|
||||
}
|
||||
|
||||
// Get the subscription info to determine the unit time
|
||||
sub, err := l.getSubscribeInfo(ctx, orderInfo.SubscribeId)
|
||||
if err != nil {
|
||||
logger.WithContext(ctx).Error("Get subscribe info failed for gift days",
|
||||
logger.Field("error", err.Error()),
|
||||
logger.Field("subscribe_id", orderInfo.SubscribeId),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Grant gift days to both referer and referee
|
||||
l.grantGiftDays(ctx, referer, giftDays, sub.UnitTime, "referer")
|
||||
l.grantGiftDays(ctx, referee, giftDays, sub.UnitTime, "referee")
|
||||
}
|
||||
|
||||
// grantGiftDays grants gift days to a user by extending their subscription
|
||||
func (l *ActivateOrderLogic) grantGiftDays(ctx context.Context, user *user.User, days int64, unitTime string, role string) {
|
||||
// Find user's active subscription
|
||||
userSub, err := l.svc.UserModel.FindActiveSubscribe(ctx, user.Id)
|
||||
if err != nil {
|
||||
logger.WithContext(ctx).Error("Find user active subscription failed for gift days",
|
||||
logger.Field("error", err.Error()),
|
||||
logger.Field("user_id", user.Id),
|
||||
logger.Field("role", role),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if userSub == nil {
|
||||
logger.WithContext(ctx).Info("User has no active subscription for gift days",
|
||||
logger.Field("user_id", user.Id),
|
||||
logger.Field("role", role),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Extend subscription by gift days
|
||||
userSub.ExpireTime = tool.AddTime("day", days, userSub.ExpireTime)
|
||||
|
||||
err = l.svc.UserModel.UpdateSubscribe(ctx, userSub)
|
||||
if err != nil {
|
||||
logger.WithContext(ctx).Error("Update user subscription for gift days failed",
|
||||
logger.Field("error", err.Error()),
|
||||
logger.Field("user_id", user.Id),
|
||||
logger.Field("role", role),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
logger.WithContext(ctx).Info("Gift days granted successfully",
|
||||
logger.Field("user_id", user.Id),
|
||||
logger.Field("role", role),
|
||||
logger.Field("days", days),
|
||||
logger.Field("new_expire_time", userSub.ExpireTime),
|
||||
)
|
||||
}
|
||||
|
||||
// shouldProcessReferralReward determines if referral reward should be processed based on
|
||||
// shouldProcessCommission determines if commission should be processed based on
|
||||
// referrer existence, commission settings, and order type
|
||||
func (l *ActivateOrderLogic) shouldProcessReferralReward(userInfo *user.User, isFirstPurchase bool) bool {
|
||||
func (l *ActivateOrderLogic) shouldProcessCommission(userInfo *user.User, isFirstPurchase bool) bool {
|
||||
if userInfo == nil || userInfo.RefererId == 0 {
|
||||
return false
|
||||
}
|
||||
@@ -582,8 +505,8 @@ func (l *ActivateOrderLogic) Renewal(ctx context.Context, orderInfo *order.Order
|
||||
// Clear cache
|
||||
l.clearServerCache(ctx, sub)
|
||||
|
||||
// Handle referral reward
|
||||
go l.handleReferralReward(context.Background(), userInfo, orderInfo)
|
||||
// Handle commission
|
||||
go l.handleCommission(context.Background(), userInfo, orderInfo)
|
||||
|
||||
// Send notifications
|
||||
l.sendNotifications(ctx, orderInfo, userInfo, sub, userSub, telegram.RenewalNotify)
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/pkg/exchangeRate"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
)
|
||||
|
||||
type RateLogic struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewRateLogic(svcCtx *svc.ServiceContext) *RateLogic {
|
||||
return &RateLogic{
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *RateLogic) ProcessTask(ctx context.Context, _ *asynq.Task) error {
|
||||
// Retrieve system currency configuration
|
||||
currency, err := l.svcCtx.SystemModel.GetCurrencyConfig(ctx)
|
||||
if err != nil {
|
||||
logger.Errorw("[PurchaseCheckout] GetCurrencyConfig error", logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
// Parse currency configuration
|
||||
configs := struct {
|
||||
CurrencyUnit string
|
||||
CurrencySymbol string
|
||||
AccessKey string
|
||||
}{}
|
||||
tool.SystemConfigSliceReflectToStruct(currency, &configs)
|
||||
|
||||
// Skip conversion if no exchange rate API key configured
|
||||
if configs.AccessKey == "" {
|
||||
logger.Debugf("[RateLogic] skip exchange rate, no access key configured")
|
||||
return nil
|
||||
}
|
||||
// Update exchange rates
|
||||
result, err := exchangeRate.GetExchangeRete(configs.CurrencyUnit, "CNY", configs.AccessKey, 1)
|
||||
if err != nil {
|
||||
logger.Errorw("[RateLogic] GetExchangeRete error", logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
l.svcCtx.ExchangeRate = result
|
||||
logger.WithContext(ctx).Infof("[RateLogic] GetExchangeRete success, result: %+v", result)
|
||||
return nil
|
||||
}
|
||||
@@ -167,7 +167,7 @@ func (l *StatLogic) ProcessTask(ctx context.Context, _ *asynq.Task) error {
|
||||
|
||||
// Delete old traffic logs
|
||||
if l.svc.Config.Log.AutoClear {
|
||||
err = tx.WithContext(ctx).Model(&traffic.TrafficLog{}).Where("created_at <= ?", end.AddDate(0, 0, int(-l.svc.Config.Log.ClearDays))).Delete(&traffic.TrafficLog{}).Error
|
||||
err = tx.WithContext(ctx).Model(&traffic.TrafficLog{}).Where("timestamp <= ?", end.AddDate(0, 0, int(-l.svc.Config.Log.ClearDays))).Delete(&traffic.TrafficLog{}).Error
|
||||
if err != nil {
|
||||
logger.Errorf("[Traffic Stat Queue] Delete server traffic log failed: %v", err.Error())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user