2026-05-15 09:58:01 +08:00

124 lines
3.4 KiB
Go

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
}