107 lines
3.8 KiB
Go
107 lines
3.8 KiB
Go
package wheel
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
|
||
"hyapp/pkg/xerr"
|
||
lotteryengine "hyapp/services/activity-service/internal/domain/lotteryengine"
|
||
domain "hyapp/services/activity-service/internal/domain/wheel"
|
||
)
|
||
|
||
const defaultWheelID = "default"
|
||
|
||
func NormalizeWheelID(wheelID string) string {
|
||
wheelID = strings.TrimSpace(wheelID)
|
||
if wheelID == "" {
|
||
return defaultWheelID
|
||
}
|
||
return wheelID
|
||
}
|
||
|
||
// NormalizeRuleConfig 是转盘配置发布前的唯一收口。
|
||
// 这里强制道具/装扮的 RTP 价值为 0,避免后台展示价值、运营估值或资源原价污染真实返奖率。
|
||
func NormalizeRuleConfig(config domain.RuleConfig) domain.RuleConfig {
|
||
config.AppCode = strings.TrimSpace(config.AppCode)
|
||
config.WheelID = NormalizeWheelID(config.WheelID)
|
||
normalized := make([]domain.Tier, 0, len(config.Tiers))
|
||
for _, tier := range config.Tiers {
|
||
tier.TierID = strings.TrimSpace(tier.TierID)
|
||
tier.DisplayName = strings.TrimSpace(tier.DisplayName)
|
||
tier.RewardType = lotteryengine.NormalizePrizeKind(tier.RewardType)
|
||
tier.RewardID = strings.TrimSpace(tier.RewardID)
|
||
tier.MetadataJSON = strings.TrimSpace(tier.MetadataJSON)
|
||
if tier.MetadataJSON == "" {
|
||
tier.MetadataJSON = "{}"
|
||
}
|
||
if tier.RewardCount <= 0 {
|
||
tier.RewardCount = 1
|
||
}
|
||
tier.RTPValueCoins = lotteryengine.NormalizeRTPValueCoins(tier.RewardType, tier.RTPValueCoins)
|
||
normalized = append(normalized, tier)
|
||
}
|
||
config.Tiers = normalized
|
||
return config
|
||
}
|
||
|
||
// ValidateRuleConfig 只验证运行所需的硬边界;概率是否满足产品预期由后台配置页负责展示和二次确认。
|
||
func ValidateRuleConfig(config domain.RuleConfig) error {
|
||
config = NormalizeRuleConfig(config)
|
||
if config.WheelID == "" {
|
||
return xerr.New(xerr.InvalidArgument, "wheel_id is required")
|
||
}
|
||
if config.DrawPriceCoins <= 0 {
|
||
return xerr.New(xerr.InvalidArgument, "draw_price_coins must be positive")
|
||
}
|
||
if config.TargetRTPPPM < 0 {
|
||
return xerr.New(xerr.InvalidArgument, "target_rtp_ppm must be non-negative")
|
||
}
|
||
if config.PoolRatePPM < 0 {
|
||
return xerr.New(xerr.InvalidArgument, "pool_rate_ppm must be non-negative")
|
||
}
|
||
if config.SettlementWindowDraws <= 0 {
|
||
return xerr.New(xerr.InvalidArgument, "settlement_window_draws must be positive")
|
||
}
|
||
if len(config.Tiers) == 0 {
|
||
return xerr.New(xerr.InvalidArgument, "wheel tiers are required")
|
||
}
|
||
seen := map[string]bool{}
|
||
var enabled bool
|
||
for _, tier := range config.Tiers {
|
||
if tier.TierID == "" {
|
||
return xerr.New(xerr.InvalidArgument, "wheel tier_id is required")
|
||
}
|
||
if seen[tier.TierID] {
|
||
return xerr.New(xerr.InvalidArgument, fmt.Sprintf("duplicate wheel tier_id: %s", tier.TierID))
|
||
}
|
||
seen[tier.TierID] = true
|
||
if tier.WeightPPM < 0 {
|
||
return xerr.New(xerr.InvalidArgument, fmt.Sprintf("wheel tier %s weight_ppm must be non-negative", tier.TierID))
|
||
}
|
||
switch tier.RewardType {
|
||
case lotteryengine.PrizeKindCoin:
|
||
if tier.RewardCoins < 0 {
|
||
return xerr.New(xerr.InvalidArgument, fmt.Sprintf("wheel tier %s reward_coins must be non-negative", tier.TierID))
|
||
}
|
||
case lotteryengine.PrizeKindGift:
|
||
if tier.RewardID == "" {
|
||
return xerr.New(xerr.InvalidArgument, fmt.Sprintf("wheel tier %s gift reward_id is required", tier.TierID))
|
||
}
|
||
case lotteryengine.PrizeKindProp, lotteryengine.PrizeKindDress:
|
||
if tier.RewardID == "" {
|
||
return xerr.New(xerr.InvalidArgument, fmt.Sprintf("wheel tier %s prop reward_id is required", tier.TierID))
|
||
}
|
||
if tier.RTPValueCoins != 0 {
|
||
return xerr.New(xerr.InvalidArgument, fmt.Sprintf("wheel tier %s prop rtp_value_coins must be zero", tier.TierID))
|
||
}
|
||
default:
|
||
return xerr.New(xerr.InvalidArgument, fmt.Sprintf("unsupported wheel reward_type: %s", tier.RewardType))
|
||
}
|
||
enabled = enabled || tier.Enabled
|
||
}
|
||
if !enabled {
|
||
return xerr.New(xerr.InvalidArgument, "wheel enabled tiers are required")
|
||
}
|
||
return nil
|
||
}
|