package voiceroomrocket import ( "encoding/json" "math" "strings" ) type energyRuleConfig struct { GoldRatio float64 `json:"goldRatio"` AllowedGiftTypes []string `json:"allowedGiftTypes"` AllowedGiftTabs []string `json:"allowedGiftTabs"` BlockedGiftTypes []string `json:"blockedGiftTypes"` BlockedGiftTabs []string `json:"blockedGiftTabs"` GiftRules []giftEnergyRule `json:"giftRules"` GiftTabRules []giftTabEnergyRule `json:"giftTabRules"` } type giftEnergyRule struct { GiftID flexibleInt64 `json:"giftId"` GiftIDs []flexibleInt64 `json:"giftIds"` Enabled *bool `json:"enabled"` Allow *bool `json:"allow"` Energy int64 `json:"energy"` EnergyPerUnit int64 `json:"energyPerUnit"` Ratio float64 `json:"ratio"` } type giftTabEnergyRule struct { GiftTab string `json:"giftTab"` GiftTabs []string `json:"giftTabs"` Enabled *bool `json:"enabled"` Allow *bool `json:"allow"` Energy int64 `json:"energy"` EnergyPerUnit int64 `json:"energyPerUnit"` Ratio float64 `json:"ratio"` } func resolveGiftEnergy(event giftEvent, snapshot configSnapshot) (int64, string) { rule, err := parseEnergyRule(snapshot.EnergyRuleJSON) if err != nil { return 0, "invalid_energy_rule" } base := calculateBaseGiftEnergy(event) if base <= 0 { return 0, "zero_energy" } if custom, ok, reason := applyGiftIDRule(event, base, rule); ok || reason != "" { return custom, reason } if !isAllowedByGiftTypeAndTab(event, rule) { return 0, "unsupported_gift" } if custom, ok, reason := applyGiftTabRule(event, base, rule); ok || reason != "" { return custom, reason } ratio := rule.GoldRatio if ratio <= 0 { ratio = 1 } energy := int64(math.Floor(float64(base) * ratio)) if energy <= 0 { return 0, "zero_energy" } return energy, "" } func parseEnergyRule(raw string) (energyRuleConfig, error) { raw = strings.TrimSpace(raw) if raw == "" || raw == "null" || raw == "{}" { return energyRuleConfig{}, nil } var rule energyRuleConfig if err := json.Unmarshal([]byte(raw), &rule); err != nil { return energyRuleConfig{}, err } return rule, nil } func applyGiftIDRule(event giftEvent, base int64, rule energyRuleConfig) (int64, bool, string) { giftID := event.GiftConfig.ID.Int64() if giftID <= 0 || len(rule.GiftRules) == 0 { return 0, false, "" } for _, item := range rule.GiftRules { if !giftRuleMatches(item, giftID) { continue } if item.Enabled != nil && !*item.Enabled { return 0, true, "gift_rule_disabled" } if item.Allow != nil && !*item.Allow { return 0, true, "gift_rule_blocked" } quantity := normalizedGiftQuantity(event) acceptUserSize := normalizedAcceptUserSize(event) if item.Energy > 0 { return item.Energy * quantity * acceptUserSize, true, "" } if item.EnergyPerUnit > 0 { return item.EnergyPerUnit * quantity * acceptUserSize, true, "" } if item.Ratio > 0 { energy := int64(math.Floor(float64(base) * item.Ratio)) if energy <= 0 { return 0, true, "zero_energy" } return energy, true, "" } return base, true, "" } return 0, false, "" } func applyGiftTabRule(event giftEvent, base int64, rule energyRuleConfig) (int64, bool, string) { giftTab := normalizeGiftToken(event.GiftConfig.GiftTab) if giftTab == "" || len(rule.GiftTabRules) == 0 { return 0, false, "" } for _, item := range rule.GiftTabRules { if !giftTabRuleMatches(item, giftTab) { continue } if item.Enabled != nil && !*item.Enabled { return 0, true, "gift_tab_rule_disabled" } if item.Allow != nil && !*item.Allow { return 0, true, "gift_tab_rule_blocked" } quantity := normalizedGiftQuantity(event) acceptUserSize := normalizedAcceptUserSize(event) if item.Energy > 0 { return item.Energy * quantity * acceptUserSize, true, "" } if item.EnergyPerUnit > 0 { return item.EnergyPerUnit * quantity * acceptUserSize, true, "" } if item.Ratio > 0 { energy := int64(math.Floor(float64(base) * item.Ratio)) if energy <= 0 { return 0, true, "zero_energy" } return energy, true, "" } return base, true, "" } return 0, false, "" } func giftRuleMatches(rule giftEnergyRule, giftID int64) bool { if rule.GiftID.Int64() == giftID { return true } for _, id := range rule.GiftIDs { if id.Int64() == giftID { return true } } return false } func giftTabRuleMatches(rule giftTabEnergyRule, giftTab string) bool { if normalizeGiftToken(rule.GiftTab) == giftTab { return true } for _, tab := range rule.GiftTabs { if normalizeGiftToken(tab) == giftTab { return true } } return false } func isAllowedByGiftTypeAndTab(event giftEvent, rule energyRuleConfig) bool { giftType := normalizeGiftToken(event.GiftConfig.Type) giftTab := normalizeGiftToken(event.GiftConfig.GiftTab) if containsGiftToken(rule.BlockedGiftTypes, giftType) || containsGiftToken(rule.BlockedGiftTabs, giftTab) { return false } if len(rule.AllowedGiftTypes) > 0 && !containsGiftToken(rule.AllowedGiftTypes, giftType) { return false } if len(rule.AllowedGiftTabs) > 0 && !containsGiftToken(rule.AllowedGiftTabs, giftTab) { return false } if isDefaultBlockedGiftToken(giftType) || isDefaultBlockedGiftToken(giftTab) { return false } if giftType == "" { return false } return giftType == rewardTypeGold } func containsGiftToken(list []string, token string) bool { token = normalizeGiftToken(token) if token == "" { return false } for _, item := range list { if normalizeGiftToken(item) == token { return true } } return false } func isDefaultBlockedGiftToken(token string) bool { switch normalizeGiftToken(token) { case "DIAMOND", "LUCKY", "ACTIVITY", "CP", "MAGIC", "FREE": return true default: return false } } func normalizeGiftToken(value string) string { return strings.ToUpper(strings.TrimSpace(value)) } func calculateBaseGiftEnergy(event giftEvent) int64 { if actual := event.GiftValue.ActualAmount.Int64(); actual > 0 { return actual } quantity := normalizedGiftQuantity(event) giftCandy := event.GiftConfig.GiftCandy.Int64() if giftCandy <= 0 { return 0 } return giftCandy * quantity * normalizedAcceptUserSize(event) } func calculateRawEnergy(event giftEvent) int64 { return calculateBaseGiftEnergy(event) } func normalizedGiftQuantity(event giftEvent) int64 { quantity := event.Quantity.Int64() if quantity <= 0 { return 1 } return quantity } func normalizedAcceptUserSize(event giftEvent) int64 { acceptUserSize := int64(countDistinctAcceptUsers(event.Accepts)) if acceptUserSize <= 0 { return 1 } return acceptUserSize }