1.优化抽奖券的抽奖逻辑
This commit is contained in:
@@ -54,14 +54,16 @@
|
|||||||
"weightTest": {
|
"weightTest": {
|
||||||
"title": "One-Click Weight Test",
|
"title": "One-Click Weight Test",
|
||||||
"alertTitle": "Bonus pool logic",
|
"alertTitle": "Bonus pool logic",
|
||||||
"alertBody": "Test mode is non-kill by default. When test kill mode is enabled, rules match production: compare default pool profit_amount to the safety line, with kill_enabled on and a killScore config present.",
|
"alertBody": "Kill switching is off by default. Enable “Test kill mode” below and set a test safety line (independent from the lottery pool config) to simulate kill triggers.",
|
||||||
"chainModeHint": "Simulation: set paid spin counts only (CW/CCW). If a paid draw hits “play again” (or T5), the next draw is free with the same ante, lottery type free, paid amount 0. Free-draw tier odds are configured below (including chained free plays).",
|
"chainModeHint": "Simulation: set paid spin counts only (CW/CCW). If a paid draw hits “play again” (or T5), the next draw is free with the same ante, lottery type free, paid amount 0. Free-draw tier odds are configured below (including chained free plays).",
|
||||||
"killModeHint": "When test kill mode is on: start from default pool profit_amount and accumulate each spin (paid: win_coin - paid_amount; free: win_coin). Once profit >= safety line and kill_enabled is on, subsequent paid draws use killScore; free draws still use the name=free pool.",
|
"killModeHint": "When test kill mode is on: start from default pool profit_amount and accumulate each spin (paid: win_coin - paid_amount; free: win_coin). Once profit >= the test safety line below, subsequent paid draws use killScore; free draws still use the name=free pool.",
|
||||||
"labelKillModeEnabled": "Enable test kill mode",
|
"killModePanelTitle": "Test kill mode",
|
||||||
"labelTestSafetyLine": "default safety line",
|
"killModeSwitchOn": "On",
|
||||||
"killEnabledOn": "kill on",
|
"killModeSwitchOff": "Off",
|
||||||
"killEnabledOff": "kill off",
|
"labelTestSafetyLine": "Test safety line",
|
||||||
"poolProfitPrefix": "pool profit ",
|
"testSafetyLineHint": "Used for this weight test only, independent from the pool config safety line. Use a lower value to observe kill switching quickly.",
|
||||||
|
"poolProfitRef": "Reference: default pool profit {profit}, pool config safety line {line}",
|
||||||
|
"killModeOffHint": "When off, all draws follow paid/free settings without kill switching.",
|
||||||
"sectionPaid": "Paid draws",
|
"sectionPaid": "Paid draws",
|
||||||
"sectionFreeAfterPlayAgain": "Free draw tier odds (after play-again)",
|
"sectionFreeAfterPlayAgain": "Free draw tier odds (after play-again)",
|
||||||
"tierProbHintFreeChain": "When using custom tier odds: T1–T5 below apply when a free draw runs (tier roll; combined with dice_reward row weights).",
|
"tierProbHintFreeChain": "When using custom tier odds: T1–T5 below apply when a free draw runs (tier roll; combined with dice_reward row weights).",
|
||||||
|
|||||||
@@ -54,14 +54,16 @@
|
|||||||
"weightTest": {
|
"weightTest": {
|
||||||
"title": "一键测试权重",
|
"title": "一键测试权重",
|
||||||
"alertTitle": "彩金池逻辑说明",
|
"alertTitle": "彩金池逻辑说明",
|
||||||
"alertBody": "测试模式默认不启用杀分切换;开启「测试内杀分」后,判定规则与线上一致:以 default 彩金池累计盈利(profit_amount)对比安全线,且需 kill_enabled=开启、存在 killScore 配置。",
|
"alertBody": "测试模式默认不启用杀分切换;开启下方「测试内杀分」后,可单独设置测试安全线(与彩金池配置中的安全线无关),用于模拟杀分触发。",
|
||||||
"chainModeHint": "模拟方式:只配置付费抽奖次数(顺/逆时针)。付费抽到「再来一次」或 T5 时,下一局自动为免费抽奖,底注与触发局相同,抽奖类型记为免费、付费金额记为 0。免费抽奖的档位概率由下方「免费抽奖」配置决定(含通过再来一次触发的后续免费局)。",
|
"chainModeHint": "模拟方式:只配置付费抽奖次数(顺/逆时针)。付费抽到「再来一次」或 T5 时,下一局自动为免费抽奖,底注与触发局相同,抽奖类型记为免费、付费金额记为 0。免费抽奖的档位概率由下方「免费抽奖」配置决定(含通过再来一次触发的后续免费局)。",
|
||||||
"killModeHint": "杀分开关开启后:从 default 奖池当前 profit_amount 起步,测试内逐局累加(付费=win_coin-paid_amount,免费=win_coin);当累计盈利 ≥ 安全线且 kill_enabled 开启时,后续付费抽奖切 killScore;免费抽奖仍走 name=free 奖池。",
|
"killModeHint": "杀分开关开启后:从 default 奖池当前 profit_amount 起步逐局累加(付费=win_coin-paid_amount,免费=win_coin);当累计盈利 ≥ 下方「测试安全线」时,后续付费抽奖切 killScore;免费抽奖仍走 name=free 奖池。",
|
||||||
"labelKillModeEnabled": "开启测试内杀分",
|
"killModePanelTitle": "测试内杀分",
|
||||||
"labelTestSafetyLine": "default 安全线",
|
"killModeSwitchOn": "已开启",
|
||||||
"killEnabledOn": "杀分已开启",
|
"killModeSwitchOff": "已关闭",
|
||||||
"killEnabledOff": "杀分已关闭",
|
"labelTestSafetyLine": "测试安全线",
|
||||||
"poolProfitPrefix": "当前池盈利 ",
|
"testSafetyLineHint": "仅用于本次权重测试,与彩金池配置页的安全线独立;可设较小值以便快速观察杀分切换效果。",
|
||||||
|
"poolProfitRef": "参考:当前 default 池盈利 {profit},彩金池配置安全线 {line}",
|
||||||
|
"killModeOffHint": "关闭时全程按付费/免费配置抽档,不模拟杀分切换。",
|
||||||
"sectionPaid": "付费抽奖",
|
"sectionPaid": "付费抽奖",
|
||||||
"sectionFreeAfterPlayAgain": "免费抽奖(再来一次后的档位概率)",
|
"sectionFreeAfterPlayAgain": "免费抽奖(再来一次后的档位概率)",
|
||||||
"tierProbHintFreeChain": "当使用自定义档位时:以下为「免费抽奖」时 T1~T5 的档位概率(仅在有免费局时参与摇档,与 dice_reward 格子权重共同决定结果)。",
|
"tierProbHintFreeChain": "当使用自定义档位时:以下为「免费抽奖」时 T1~T5 的档位概率(仅在有免费局时参与摇档,与 dice_reward 格子权重共同决定结果)。",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<ElForm :model="form" label-width="108px" class="weight-test-form">
|
<ElForm :model="form" label-width="108px" class="weight-test-form">
|
||||||
<ElRow :gutter="16">
|
<ElRow :gutter="16">
|
||||||
<ElCol :span="8">
|
<ElCol :span="12">
|
||||||
<ElFormItem :label="$t('page.weightTest.labelAnte')" prop="ante_config_id" required>
|
<ElFormItem :label="$t('page.weightTest.labelAnte')" prop="ante_config_id" required>
|
||||||
<ElSelect
|
<ElSelect
|
||||||
v-model="form.ante_config_id"
|
v-model="form.ante_config_id"
|
||||||
@@ -38,21 +38,43 @@
|
|||||||
</ElSelect>
|
</ElSelect>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</ElCol>
|
</ElCol>
|
||||||
<ElCol :span="8">
|
</ElRow>
|
||||||
<ElFormItem :label="$t('page.weightTest.labelKillModeEnabled')" prop="kill_mode_enabled">
|
|
||||||
<ElSwitch v-model="form.kill_mode_enabled" />
|
<div class="kill-mode-panel">
|
||||||
</ElFormItem>
|
<div class="kill-mode-head">
|
||||||
</ElCol>
|
<div class="kill-mode-title">{{ $t('page.weightTest.killModePanelTitle') }}</div>
|
||||||
<ElCol :span="8">
|
<ElSwitch
|
||||||
<ElFormItem :label="$t('page.weightTest.labelTestSafetyLine')">
|
v-model="form.kill_mode_enabled"
|
||||||
<ElInput
|
:active-text="$t('page.weightTest.killModeSwitchOn')"
|
||||||
:model-value="defaultPoolSafetyLineText"
|
:inactive-text="$t('page.weightTest.killModeSwitchOff')"
|
||||||
readonly
|
inline-prompt
|
||||||
:disabled="!form.kill_mode_enabled"
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="form.kill_mode_enabled" class="kill-mode-body">
|
||||||
|
<ElFormItem
|
||||||
|
:label="$t('page.weightTest.labelTestSafetyLine')"
|
||||||
|
prop="test_safety_line"
|
||||||
|
class="kill-safety-item"
|
||||||
|
label-width="96px"
|
||||||
|
>
|
||||||
|
<ElInputNumber
|
||||||
|
v-model="form.test_safety_line"
|
||||||
|
:min="0"
|
||||||
|
:step="100"
|
||||||
|
controls-position="right"
|
||||||
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</ElCol>
|
<div class="kill-mode-hint">{{ $t('page.weightTest.testSafetyLineHint') }}</div>
|
||||||
</ElRow>
|
<div v-if="defaultPoolInfo" class="kill-mode-ref">
|
||||||
|
{{ $t('page.weightTest.poolProfitRef', {
|
||||||
|
profit: defaultPoolInfo.profit_amount,
|
||||||
|
line: defaultPoolInfo.safety_line
|
||||||
|
}) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="kill-mode-off-hint">{{ $t('page.weightTest.killModeOffHint') }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ElRow :gutter="20" class="section-row">
|
<ElRow :gutter="20" class="section-row">
|
||||||
<ElCol :span="12">
|
<ElCol :span="12">
|
||||||
@@ -218,17 +240,8 @@
|
|||||||
free_tier_weights: { T1: 20, T2: 20, T3: 20, T4: 20, T5: 20 } as Record<string, number>,
|
free_tier_weights: { T1: 20, T2: 20, T3: 20, T4: 20, T5: 20 } as Record<string, number>,
|
||||||
paid_s_count: 100,
|
paid_s_count: 100,
|
||||||
paid_n_count: 100,
|
paid_n_count: 100,
|
||||||
kill_mode_enabled: false
|
kill_mode_enabled: false,
|
||||||
})
|
test_safety_line: 5000
|
||||||
const defaultPoolSafetyLineText = computed(() => {
|
|
||||||
const info = defaultPoolInfo.value
|
|
||||||
if (!info) {
|
|
||||||
return '-'
|
|
||||||
}
|
|
||||||
const killText = info.kill_enabled === 1
|
|
||||||
? t('page.weightTest.killEnabledOn')
|
|
||||||
: t('page.weightTest.killEnabledOff')
|
|
||||||
return `${info.safety_line}(${killText},${t('page.weightTest.poolProfitPrefix')}${info.profit_amount})`
|
|
||||||
})
|
})
|
||||||
const lotteryOptions = ref<Array<{ id: number; name: string }>>([])
|
const lotteryOptions = ref<Array<{ id: number; name: string }>>([])
|
||||||
const paidLotteryOptions = computed(() =>
|
const paidLotteryOptions = computed(() =>
|
||||||
@@ -373,7 +386,7 @@
|
|||||||
free_n_count: 0,
|
free_n_count: 0,
|
||||||
chain_free_mode: true,
|
chain_free_mode: true,
|
||||||
kill_mode_enabled: form.kill_mode_enabled,
|
kill_mode_enabled: form.kill_mode_enabled,
|
||||||
test_safety_line: defaultPoolInfo.value?.safety_line ?? 0
|
test_safety_line: form.test_safety_line
|
||||||
}
|
}
|
||||||
if (form.paid_lottery_config_id != null) {
|
if (form.paid_lottery_config_id != null) {
|
||||||
payload.paid_lottery_config_id = form.paid_lottery_config_id
|
payload.paid_lottery_config_id = form.paid_lottery_config_id
|
||||||
@@ -402,6 +415,10 @@
|
|||||||
ElMessage.warning(t('page.weightTest.warnPaidSpins'))
|
ElMessage.warning(t('page.weightTest.warnPaidSpins'))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if (form.kill_mode_enabled && (form.test_safety_line == null || form.test_safety_line < 0)) {
|
||||||
|
ElMessage.warning(t('page.weightTest.warnTestSafetyLine'))
|
||||||
|
return false
|
||||||
|
}
|
||||||
const needPaidTier = form.paid_lottery_config_id == null
|
const needPaidTier = form.paid_lottery_config_id == null
|
||||||
const needFreeTier = form.free_lottery_config_id == null
|
const needFreeTier = form.free_lottery_config_id == null
|
||||||
if (needPaidTier) {
|
if (needPaidTier) {
|
||||||
@@ -498,6 +515,54 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kill-mode-panel {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
|
border-radius: var(--el-border-radius-base);
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kill-mode-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kill-mode-title {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kill-mode-body {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px dashed var(--el-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kill-safety-item {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
:deep(.el-form-item__label) {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.kill-mode-hint,
|
||||||
|
.kill-mode-off-hint,
|
||||||
|
.kill-mode-ref {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kill-mode-ref {
|
||||||
|
margin-top: 4px;
|
||||||
|
color: var(--el-text-color-placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
.section-row {
|
.section-row {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class DiceRewardController extends BaseController
|
|||||||
* 参数:lottery_config_id 可选;paid_tier_weights / free_tier_weights 自定义档位;
|
* 参数:lottery_config_id 可选;paid_tier_weights / free_tier_weights 自定义档位;
|
||||||
* paid_s_count, paid_n_count
|
* paid_s_count, paid_n_count
|
||||||
* chain_free_mode=1:仅按付费次数模拟;付费抽到再来一次/T5 则在队列中插入免费局(同底注、lottery_type=免费、paid_amount=0)
|
* chain_free_mode=1:仅按付费次数模拟;付费抽到再来一次/T5 则在队列中插入免费局(同底注、lottery_type=免费、paid_amount=0)
|
||||||
* kill_mode_enabled=1:测试内启用杀分;规则与线上一致(default.profit_amount + safety_line + kill_enabled)
|
* kill_mode_enabled=1:测试内启用杀分;当模拟池盈利达到 test_safety_line 后,付费抽奖切到 killScore
|
||||||
*/
|
*/
|
||||||
#[Permission('一键测试权重', 'dice:reward:index:startWeightTest')]
|
#[Permission('一键测试权重', 'dice:reward:index:startWeightTest')]
|
||||||
public function startWeightTest(Request $request): Response
|
public function startWeightTest(Request $request): Response
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use support\think\Db;
|
|||||||
* 一键测试权重:单进程后台执行模拟摇色子,写入 dice_play_record_test 并更新 dice_reward_config_record 进度
|
* 一键测试权重:单进程后台执行模拟摇色子,写入 dice_play_record_test 并更新 dice_reward_config_record 进度
|
||||||
* 抽奖规则与 PlayStartLogic 一致:
|
* 抽奖规则与 PlayStartLogic 一致:
|
||||||
* - 付费未杀分:按模拟玩家档位权重抽档,lottery_config_id 记 default
|
* - 付费未杀分:按模拟玩家档位权重抽档,lottery_config_id 记 default
|
||||||
* - 付费杀分:default.profit_amount + safety_line + kill_enabled 满足后切 killScore
|
* - 付费杀分:测试内杀分开启且模拟池盈利 >= test_safety_line 后切 killScore
|
||||||
* - 免费券:name=free 奖池(无则 default),排除 5/30 豹子
|
* - 免费券:name=free 奖池(无则 default),排除 5/30 豹子
|
||||||
*/
|
*/
|
||||||
class WeightTestRunner
|
class WeightTestRunner
|
||||||
@@ -115,10 +115,12 @@ class WeightTestRunner
|
|||||||
DiceReward::clearRequestInstance();
|
DiceReward::clearRequestInstance();
|
||||||
|
|
||||||
$killModeEnabled = (int) ($record->kill_mode_enabled ?? 0) === 1;
|
$killModeEnabled = (int) ($record->kill_mode_enabled ?? 0) === 1;
|
||||||
$safetyLine = (int) ($configType0->safety_line ?? 0);
|
$testSafetyLine = (int) ($record->test_safety_line ?? 5000);
|
||||||
$dbKillEnabled = ((int) ($configType0->kill_enabled ?? 1)) === 1;
|
if ($testSafetyLine < 0) {
|
||||||
|
$testSafetyLine = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// 彩金池累计盈利:与线上一致,从 default.profit_amount 起步并在测试内逐局累加
|
// 彩金池累计盈利:从 default.profit_amount 起步并在测试内逐局累加,与杀分判定值一致
|
||||||
$poolProfitTotal = (float) ($configType0->profit_amount ?? 0);
|
$poolProfitTotal = (float) ($configType0->profit_amount ?? 0);
|
||||||
|
|
||||||
// 付费未杀分时的模拟玩家档位权重(自定义 > 快照 > 兜底奖池)
|
// 付费未杀分时的模拟玩家档位权重(自定义 > 快照 > 兜底奖池)
|
||||||
@@ -145,8 +147,7 @@ class WeightTestRunner
|
|||||||
$paidPlayerWeights,
|
$paidPlayerWeights,
|
||||||
$freeTierWeightsCustom,
|
$freeTierWeightsCustom,
|
||||||
$killModeEnabled,
|
$killModeEnabled,
|
||||||
$safetyLine,
|
$testSafetyLine,
|
||||||
$dbKillEnabled,
|
|
||||||
$poolProfitTotal,
|
$poolProfitTotal,
|
||||||
$resultCounts,
|
$resultCounts,
|
||||||
$tierCounts,
|
$tierCounts,
|
||||||
@@ -186,8 +187,7 @@ class WeightTestRunner
|
|||||||
?array $paidPlayerWeights,
|
?array $paidPlayerWeights,
|
||||||
?array $freeTierWeightsCustom,
|
?array $freeTierWeightsCustom,
|
||||||
bool $killModeEnabled,
|
bool $killModeEnabled,
|
||||||
int $safetyLine,
|
int $testSafetyLine,
|
||||||
bool $dbKillEnabled,
|
|
||||||
float &$poolProfitTotal,
|
float &$poolProfitTotal,
|
||||||
array &$resultCounts,
|
array &$resultCounts,
|
||||||
array &$tierCounts,
|
array &$tierCounts,
|
||||||
@@ -211,8 +211,7 @@ class WeightTestRunner
|
|||||||
|
|
||||||
if ($isPaid) {
|
if ($isPaid) {
|
||||||
$useKillForPaid = $killModeEnabled
|
$useKillForPaid = $killModeEnabled
|
||||||
&& $dbKillEnabled
|
&& $poolProfitTotal >= $testSafetyLine
|
||||||
&& $poolProfitTotal >= $safetyLine
|
|
||||||
&& $killPoolConfig !== null;
|
&& $killPoolConfig !== null;
|
||||||
if ($useKillForPaid) {
|
if ($useKillForPaid) {
|
||||||
$cfg = $killPoolConfig;
|
$cfg = $killPoolConfig;
|
||||||
|
|||||||
Reference in New Issue
Block a user