2026-06-18 17:13:56 +08:00

107 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}