fix: generate lucky gift tier ids in admin

This commit is contained in:
zhx 2026-06-12 23:28:04 +08:00
parent 982ac46415
commit 93e5cd0218
2 changed files with 59 additions and 4 deletions

View File

@ -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))
}

View File

@ -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",