fix: prevent duplicate lucky gift tier ids
This commit is contained in:
parent
3983e355fb
commit
282c9f1734
@ -130,12 +130,16 @@ export function addTierToStage(stages, stageKey) {
|
||||
if (stage.stage !== stageKey) {
|
||||
return stage;
|
||||
}
|
||||
const index = stage.tiers.length + 1;
|
||||
const nextMultiplier = nextSuggestedTierMultiplier(stage.tiers);
|
||||
return {
|
||||
...stage,
|
||||
tiers: [
|
||||
...stage.tiers,
|
||||
tier(`${stage.stage}_${index}x`, "", "", { broadcastLevel: "none", rewardSource: "base_rtp" }),
|
||||
tier(tierIDFromMultiplier(stage.stage, nextMultiplier), nextMultiplier, "", {
|
||||
broadcastLevel: "none",
|
||||
highWaterOnly: nextMultiplier >= 5,
|
||||
rewardSource: "base_rtp",
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
@ -231,6 +235,28 @@ function defaultStages() {
|
||||
return stageOptions.map(([stage]) => ({ stage, tiers: defaultStageTiers[stage].map((item) => ({ ...item })) }));
|
||||
}
|
||||
|
||||
function nextSuggestedTierMultiplier(tiers) {
|
||||
const usedMultipliers = (tiers || [])
|
||||
.map((item) => decimal(item.multiplier))
|
||||
.filter((value) => Number.isFinite(value) && value > 0);
|
||||
if (usedMultipliers.length === 0) {
|
||||
return 1;
|
||||
}
|
||||
const maxMultiplier = Math.max(...usedMultipliers);
|
||||
if (maxMultiplier >= 5) {
|
||||
return maxMultiplier * 2;
|
||||
}
|
||||
if (maxMultiplier >= 2) {
|
||||
return 5;
|
||||
}
|
||||
return maxMultiplier + 1;
|
||||
}
|
||||
|
||||
function tierIDFromMultiplier(stage, multiplier) {
|
||||
const multiplierText = String(multiplier).replace(".", "_");
|
||||
return `${stage}_${multiplierText}x`;
|
||||
}
|
||||
|
||||
function normalizeStages(stages) {
|
||||
// server 返回缺阶段时仍用默认三阶段补齐表单,保证运营看到的是完整发布形态;
|
||||
// 保存时 schema 会再次校验概率合计和 0x 档,不让半成品配置进入发布请求。
|
||||
|
||||
@ -56,7 +56,13 @@ describe("lucky gift config payload", () => {
|
||||
const advanced = nextStages.find((stage) => stage.stage === "advanced");
|
||||
|
||||
expect(advanced.tiers).toHaveLength(5);
|
||||
expect(advanced.tiers[4]).toMatchObject({ broadcastLevel: "none", rewardSource: "base_rtp" });
|
||||
expect(advanced.tiers[4]).toMatchObject({
|
||||
broadcastLevel: "none",
|
||||
highWaterOnly: true,
|
||||
multiplier: 10,
|
||||
rewardSource: "base_rtp",
|
||||
tierId: "advanced_10x",
|
||||
});
|
||||
});
|
||||
|
||||
test("corrects a stage probability to requested RTP without zeroing enabled tiers", () => {
|
||||
|
||||
@ -18,4 +18,28 @@ describe("lucky gift config schema", () => {
|
||||
expect(result.error.issues.map((issue) => issue.message)).toContain("启用奖档概率合计必须等于 100%");
|
||||
}
|
||||
});
|
||||
|
||||
test("rejects duplicated tier id inside the same stage before submit", () => {
|
||||
const form = emptyLuckyGiftForm();
|
||||
const advanced = form.stages.find((stage: { stage: string }) => stage.stage === "advanced");
|
||||
if (!advanced) {
|
||||
throw new Error("advanced stage missing");
|
||||
}
|
||||
advanced.tiers = [
|
||||
...advanced.tiers,
|
||||
{
|
||||
...advanced.tiers[advanced.tiers.length - 1],
|
||||
multiplier: 10,
|
||||
probabilityPercent: 0,
|
||||
tierId: "advanced_5x",
|
||||
},
|
||||
];
|
||||
|
||||
const result = luckyGiftConfigFormSchema.safeParse(form);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.map((issue) => issue.message)).toContain("同一阶段内奖档 ID 不能重复");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -93,6 +93,18 @@ export const luckyGiftConfigFormSchema = z
|
||||
path: ["stages", stageIndex, "tiers"],
|
||||
});
|
||||
}
|
||||
const tierIDSet = new Set<string>();
|
||||
stage.tiers.forEach((tier, tierIndex) => {
|
||||
const tierID = String(tier.tierId || "").trim();
|
||||
if (tierIDSet.has(tierID)) {
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
message: "同一阶段内奖档 ID 不能重复",
|
||||
path: ["stages", stageIndex, "tiers", tierIndex, "tierId"],
|
||||
});
|
||||
}
|
||||
tierIDSet.add(tierID);
|
||||
});
|
||||
const expectedRTPPPM = stage.tiers.reduce((sum, tier) => {
|
||||
if (!tier.enabled || tier.rewardSource !== "base_rtp") {
|
||||
return sum;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user