From 93e5cd0218991b8196c6ab98427b8ba85c9886cc Mon Sep 17 00:00:00 2001 From: zhx Date: Fri, 12 Jun 2026 23:28:04 +0800 Subject: [PATCH] fix: generate lucky gift tier ids in admin --- .../internal/modules/luckygift/handler.go | 37 +++++++++++++++++-- .../modules/luckygift/handler_test.go | 26 +++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/server/admin/internal/modules/luckygift/handler.go b/server/admin/internal/modules/luckygift/handler.go index 32c53011..041c15bc 100644 --- a/server/admin/internal/modules/luckygift/handler.go +++ b/server/admin/internal/modules/luckygift/handler.go @@ -2,6 +2,7 @@ package luckygift import ( "context" + "fmt" "math" "strings" "time" @@ -227,12 +228,17 @@ func (h *Handler) activityContext(c *gin.Context) (context.Context, context.Canc func configToProto(req configRequest) *activityv1.LuckyGiftRuleConfig { stages := make([]*activityv1.LuckyGiftRuleStage, 0, len(req.Stages)) for _, stage := range req.Stages { + stageName := strings.TrimSpace(stage.Stage) + tierIDCounts := make(map[string]int, len(stage.Tiers)) tiers := make([]*activityv1.LuckyGiftRuleTier, 0, len(stage.Tiers)) for _, tier := range stage.Tiers { + multiplierPPM := multiplierToPPM(tier.Multiplier) tiers = append(tiers, &activityv1.LuckyGiftRuleTier{ - Stage: strings.TrimSpace(stage.Stage), - TierId: strings.TrimSpace(tier.TierID), - MultiplierPpm: multiplierToPPM(tier.Multiplier), + Stage: stageName, + // 奖档 ID 是 activity-service 持久化主键的一部分,不能让运营手工维护;admin-server 按阶段和倍率生成稳定 ID, + // 同一阶段同倍率出现多行时追加序号,保证进入 activity-service 前已经满足唯一性。 + TierId: generatedLuckyGiftTierID(stageName, multiplierPPM, tierIDCounts), + MultiplierPpm: multiplierPPM, BaseWeightPpm: percentToPPM(tier.ProbabilityPercent), RewardSource: strings.TrimSpace(tier.RewardSource), HighWaterOnly: tier.HighWaterOnly, @@ -241,7 +247,7 @@ func configToProto(req configRequest) *activityv1.LuckyGiftRuleConfig { }) } stages = append(stages, &activityv1.LuckyGiftRuleStage{ - Stage: strings.TrimSpace(stage.Stage), + Stage: stageName, Tiers: tiers, }) } @@ -317,6 +323,29 @@ func configFromProto(config *activityv1.LuckyGiftRuleConfig) configDTO { } } +func generatedLuckyGiftTierID(stage string, multiplierPPM int64, counts map[string]int) string { + baseID := luckyGiftTierIDBase(stage, multiplierPPM) + counts[baseID]++ + if counts[baseID] == 1 { + return baseID + } + return fmt.Sprintf("%s_%d", baseID, counts[baseID]) +} + +func luckyGiftTierIDBase(stage string, multiplierPPM int64) string { + stage = strings.TrimSpace(stage) + if multiplierPPM <= 0 { + return stage + "_none" + } + whole := multiplierPPM / 1_000_000 + fraction := multiplierPPM % 1_000_000 + if fraction == 0 { + return fmt.Sprintf("%s_%dx", stage, whole) + } + fractionText := strings.TrimRight(fmt.Sprintf("%06d", fraction), "0") + return fmt.Sprintf("%s_%d_%sx", stage, whole, fractionText) +} + func percentToPPM(value float64) int64 { return int64(math.Round(value * 10_000)) } diff --git a/server/admin/internal/modules/luckygift/handler_test.go b/server/admin/internal/modules/luckygift/handler_test.go index d7fdedc5..d30688e1 100644 --- a/server/admin/internal/modules/luckygift/handler_test.go +++ b/server/admin/internal/modules/luckygift/handler_test.go @@ -64,6 +64,32 @@ func TestConfigToProtoConvertsBusinessUnitsToPPM(t *testing.T) { } } +func TestConfigToProtoGeneratesUniqueTierIDs(t *testing.T) { + config := configToProto(configRequest{ + PoolID: "lucky", + Stages: []stageDTO{ + { + Stage: "advanced", + Tiers: []tierDTO{ + {TierID: "manual_one", Multiplier: 0, ProbabilityPercent: 50, RewardSource: "base_rtp", Enabled: true}, + {TierID: "manual_two", Multiplier: 5, ProbabilityPercent: 25, RewardSource: "base_rtp", Enabled: true}, + {TierID: "manual_two", Multiplier: 5, ProbabilityPercent: 25, RewardSource: "base_rtp", Enabled: true}, + {TierID: "manual_decimal", Multiplier: 0.5, ProbabilityPercent: 0, RewardSource: "base_rtp"}, + }, + }, + }, + }) + + tiers := config.GetStages()[0].GetTiers() + got := []string{tiers[0].GetTierId(), tiers[1].GetTierId(), tiers[2].GetTierId(), tiers[3].GetTierId()} + want := []string{"advanced_none", "advanced_5x", "advanced_5x_2", "advanced_0_5x"} + for index := range want { + if got[index] != want[index] { + t.Fatalf("tier id[%d] = %q, want %q; all=%v", index, got[index], want[index], got) + } + } +} + func TestConfigFromProtoConvertsPPMToBusinessUnits(t *testing.T) { config := configFromProto(&activityv1.LuckyGiftRuleConfig{ AppCode: "hyapp",