feat(订单): 实现推荐奖励系统支持佣金和赠送天数两种模式
Build docker and publish / build (20.15.1) (push) Failing after 9m13s

重构推荐奖励处理逻辑,新增支持根据配置选择佣金奖励或赠送天数奖励
修改Discount相关字段类型为float64以支持小数折扣
添加GiftDays配置项控制赠送天数
新增FindActiveSubscribe方法查询用户有效订阅
This commit is contained in:
2025-10-17 06:01:29 -07:00
parent 7da63ade5c
commit bfbc675e1a
12 changed files with 139 additions and 27 deletions
+90 -12
View File
@@ -179,8 +179,8 @@ func (l *ActivateOrderLogic) NewPurchase(ctx context.Context, orderInfo *order.O
return err
}
// Handle commission in separate goroutine to avoid blocking
go l.handleCommission(context.Background(), userInfo, orderInfo)
// Handle referral reward in separate goroutine to avoid blocking
go l.handleReferralReward(context.Background(), userInfo, orderInfo)
// Clear cache
l.clearServerCache(ctx, sub)
@@ -349,10 +349,12 @@ func (l *ActivateOrderLogic) createUserSubscription(ctx context.Context, orderIn
return userSub, nil
}
// handleCommission processes referral commission for the referrer if applicable.
// handleReferralReward processes referral rewards for the referrer if applicable.
// This runs asynchronously to avoid blocking the main order processing flow.
func (l *ActivateOrderLogic) handleCommission(ctx context.Context, userInfo *user.User, orderInfo *order.Order) {
if !l.shouldProcessCommission(userInfo, orderInfo.IsNew) {
// 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) {
return
}
@@ -372,13 +374,25 @@ func (l *ActivateOrderLogic) handleCommission(ctx context.Context, userInfo *use
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, referralPercentage)
amount := l.calculateCommission(orderInfo.Amount-orderInfo.FeeAmount, percentage)
// 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
}
@@ -420,9 +434,73 @@ func (l *ActivateOrderLogic) handleCommission(ctx context.Context, userInfo *use
}
}
// shouldProcessCommission determines if commission should be processed based on
// 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
// referrer existence, commission settings, and order type
func (l *ActivateOrderLogic) shouldProcessCommission(userInfo *user.User, isFirstPurchase bool) bool {
func (l *ActivateOrderLogic) shouldProcessReferralReward(userInfo *user.User, isFirstPurchase bool) bool {
if userInfo == nil || userInfo.RefererId == 0 {
return false
}
@@ -504,8 +582,8 @@ func (l *ActivateOrderLogic) Renewal(ctx context.Context, orderInfo *order.Order
// Clear cache
l.clearServerCache(ctx, sub)
// Handle commission
go l.handleCommission(context.Background(), userInfo, orderInfo)
// Handle referral reward
go l.handleReferralReward(context.Background(), userInfo, orderInfo)
// Send notifications
l.sendNotifications(ctx, orderInfo, userInfo, sub, userSub, telegram.RenewalNotify)