package smashegg import ( "context" "errors" "math/big" "strings" "time" "chatapp3-golang/internal/integration" "chatapp3-golang/internal/model" "gorm.io/gorm" ) type newbieEligibility struct { Eligible bool Remaining int } func (item newbieEligibility) remainingAfterUse(poolType string) int { if normalizePoolType(poolType) != poolTypeNewbie { return item.Remaining } if item.Remaining <= 0 { return 0 } return item.Remaining - 1 } func (s *Service) resolveRewardPool(ctx context.Context, bundle *configBundle, user AuthUser) (string, []model.SmashEggRewardConfig, newbieEligibility, error) { eligibility := newbieEligibility{} if bundle == nil || bundle.Config == nil { return poolTypeGeneral, nil, eligibility, nil } generalRewards := rewardsForPool(bundle.Rewards, poolTypeGeneral) if !bundle.Config.NewbiePoolEnabled || user.UserID <= 0 { return poolTypeGeneral, generalRewards, eligibility, nil } newbieRewards := rewardsForPool(bundle.Rewards, poolTypeNewbie) if len(newbieRewards) == 0 { return poolTypeGeneral, generalRewards, eligibility, nil } resolved, err := s.resolveNewbieEligibility(ctx, bundle.Config, user) if err != nil { return "", nil, eligibility, err } if !resolved.Eligible { return poolTypeGeneral, generalRewards, resolved, nil } return poolTypeNewbie, newbieRewards, resolved, nil } func (s *Service) resolveNewbieEligibility(ctx context.Context, configRow *model.SmashEggConfig, user AuthUser) (newbieEligibility, error) { result := newbieEligibility{} if configRow == nil || !configRow.NewbiePoolEnabled || user.UserID <= 0 { return result, nil } windowDays := effectiveNewbieWindowDays(configRow.NewbieWindowDays) maxDrawCount := effectiveNewbieMaxDrawCount(configRow.NewbieMaxDrawCount, true) if maxDrawCount <= 0 { return result, nil } var userInfo model.UserBaseInfo err := s.db.WithContext(ctx).Where("id = ?", user.UserID).First(&userInfo).Error if errors.Is(err, gorm.ErrRecordNotFound) { return result, nil } if err != nil { return result, err } if userInfo.CreateTime.IsZero() { return result, nil } if time.Since(userInfo.CreateTime) > time.Duration(windowDays)*24*time.Hour { return result, nil } var used int64 if err := s.db.WithContext(ctx).Model(&model.SmashEggDrawRecord{}). Where("sys_origin = ? AND user_id = ? AND pool_type = ? AND status IN ?", normalizeSysOrigin(user.SysOrigin), user.UserID, poolTypeNewbie, []string{drawStatusPending, drawStatusSuccess}). Distinct("draw_no"). Count(&used).Error; err != nil { return result, err } remaining := maxDrawCount - int(used) if remaining <= 0 { return result, nil } if configRow.NewbieMinRechargeAmount > 0 { if s.java == nil { return result, nil } rechargeMap, err := s.java.MapUserTotalRecharge(ctx, []int64{user.UserID}) if err != nil { return result, nil } if !totalRechargeAtLeast(rechargeMap[user.UserID], configRow.NewbieMinRechargeAmount) { return result, nil } } result.Eligible = true result.Remaining = remaining return result, nil } func totalRechargeAtLeast(items []integration.UserTotalRecharge, threshold int64) bool { if threshold <= 0 { return true } total := new(big.Rat) for _, item := range items { if strings.EqualFold(strings.TrimSpace(item.Type), "REFUND") { continue } total.Add(total, parseDecimalRat(item.Amount)) } return total.Cmp(new(big.Rat).SetInt64(threshold)) >= 0 }