diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 00000000..add2a628 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,12 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "admin-platform", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["--dir", "/Users/hy/Documents/hy/hyapp-admin-platform", "dev", "--port", "7002"], + "port": 7002, + "autoPort": false + } + ] +} diff --git a/services/lucky-gift-service/internal/storage/mysql/lucky_gift_rule_config_repository.go b/services/lucky-gift-service/internal/storage/mysql/lucky_gift_rule_config_repository.go index 4c190daf..bf7cf8a6 100644 --- a/services/lucky-gift-service/internal/storage/mysql/lucky_gift_rule_config_repository.go +++ b/services/lucky-gift-service/internal/storage/mysql/lucky_gift_rule_config_repository.go @@ -65,7 +65,7 @@ func (r *Repository) PublishLuckyGiftRuleConfig(ctx context.Context, config doma settlement_window_wager, control_band_ppm, gift_price_reference, novice_max_equivalent_draws, normal_max_equivalent_draws, effective_from_ms, created_by_admin_id, created_at_ms - ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, + ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, config.AppCode, config.PoolID, config.RuleVersion, config.Enabled, config.TargetRTPPPM, config.PoolRatePPM, config.SettlementWindowWager, config.ControlBandPPM, config.GiftPriceReference, config.NoviceMaxEquivalentDraws, config.NormalMaxEquivalentDraws, diff --git a/services/lucky-gift-service/internal/storage/mysql/lucky_gift_rule_config_repository_test.go b/services/lucky-gift-service/internal/storage/mysql/lucky_gift_rule_config_repository_test.go new file mode 100644 index 00000000..1d0bfff2 --- /dev/null +++ b/services/lucky-gift-service/internal/storage/mysql/lucky_gift_rule_config_repository_test.go @@ -0,0 +1,72 @@ +package mysql + +import ( + "context" + "testing" + + "hyapp/internal/testutil/mysqlschema" + "hyapp/pkg/appcode" + domain "hyapp/services/lucky-gift-service/internal/domain/luckygift" +) + +// 回归:INSERT 列清单和占位符数量必须一致。拆分 lucky-gift-service 时列从 20 裁到 14, +// 占位符只裁到 15,导致所有规则发布在 MySQL 层直接失败并以 opaque internal error 暴露给运营后台。 +func TestPublishLuckyGiftRuleConfigPersistsVersionsAndTiers(t *testing.T) { + caller := mysqlschema.CallerFile(t, 1) + schema := mysqlschema.New(t, mysqlschema.Config{ + EnvVar: "LUCKY_GIFT_SERVICE_MYSQL_TEST_DSN", + InitDBPath: mysqlschema.InitDBPath(t, caller, "..", "..", "..", "deploy", "mysql", "initdb", "001_lucky_gift_service.sql"), + DatabasePrefix: "hy_lucky_gift", + }) + + repo := &Repository{db: schema.DB} + ctx := appcode.WithContext(context.Background(), "lalu") + config := domain.RuleConfig{ + PoolID: "lucky", + Enabled: true, + TargetRTPPPM: 950_000, + PoolRatePPM: 960_000, + SettlementWindowWager: 1_000_000, + ControlBandPPM: 10_000, + GiftPriceReference: 100, + NoviceMaxEquivalentDraws: 2_000, + NormalMaxEquivalentDraws: 20_000, + Stages: []domain.RuleStage{ + {Stage: domain.StageNovice, Tiers: []domain.RuleTier{ + {Stage: domain.StageNovice, TierID: "novice_1x", MultiplierPPM: 1_000_000, BaseWeightPPM: 1_000_000, Enabled: true}, + }}, + }, + } + + published, err := repo.PublishLuckyGiftRuleConfig(ctx, config, 1_700_000_000_000) + if err != nil { + t.Fatalf("publish first rule version: %v", err) + } + if published.RuleVersion != 1 { + t.Fatalf("first publish rule_version = %d, want 1", published.RuleVersion) + } + + // 版本必须只增不改:同奖池再次发布要拿到 v2,且两版规则和奖档都能按最新版本读回。 + again, err := repo.PublishLuckyGiftRuleConfig(ctx, config, 1_700_000_100_000) + if err != nil { + t.Fatalf("publish second rule version: %v", err) + } + if again.RuleVersion != 2 { + t.Fatalf("second publish rule_version = %d, want 2", again.RuleVersion) + } + + configs, err := repo.ListLuckyGiftRuleConfigs(ctx, "lalu") + if err != nil { + t.Fatalf("list rule configs: %v", err) + } + if len(configs) != 1 { + t.Fatalf("list should return the latest version per pool, got %d rows", len(configs)) + } + latest := configs[0] + if latest.PoolID != "lucky" || latest.RuleVersion != 2 || !latest.Enabled { + t.Fatalf("latest config = %+v, want lucky v2 enabled", latest) + } + if len(latest.Stages) != 1 || len(latest.Stages[0].Tiers) != 1 || latest.Stages[0].Tiers[0].TierID != "novice_1x" { + t.Fatalf("latest stages = %+v, want single novice tier novice_1x", latest.Stages) + } +}