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) {
|
if (stage.stage !== stageKey) {
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
const index = stage.tiers.length + 1;
|
const nextMultiplier = nextSuggestedTierMultiplier(stage.tiers);
|
||||||
return {
|
return {
|
||||||
...stage,
|
...stage,
|
||||||
tiers: [
|
tiers: [
|
||||||
...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 })) }));
|
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) {
|
function normalizeStages(stages) {
|
||||||
// server 返回缺阶段时仍用默认三阶段补齐表单,保证运营看到的是完整发布形态;
|
// server 返回缺阶段时仍用默认三阶段补齐表单,保证运营看到的是完整发布形态;
|
||||||
// 保存时 schema 会再次校验概率合计和 0x 档,不让半成品配置进入发布请求。
|
// 保存时 schema 会再次校验概率合计和 0x 档,不让半成品配置进入发布请求。
|
||||||
|
|||||||
@ -56,7 +56,13 @@ describe("lucky gift config payload", () => {
|
|||||||
const advanced = nextStages.find((stage) => stage.stage === "advanced");
|
const advanced = nextStages.find((stage) => stage.stage === "advanced");
|
||||||
|
|
||||||
expect(advanced.tiers).toHaveLength(5);
|
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", () => {
|
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%");
|
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"],
|
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) => {
|
const expectedRTPPPM = stage.tiers.reduce((sum, tier) => {
|
||||||
if (!tier.enabled || tier.rewardSource !== "base_rtp") {
|
if (!tier.enabled || tier.rewardSource !== "base_rtp") {
|
||||||
return sum;
|
return sum;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user