1.优化抽奖券的抽奖逻辑
This commit is contained in:
@@ -54,14 +54,16 @@
|
||||
"weightTest": {
|
||||
"title": "One-Click Weight Test",
|
||||
"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).",
|
||||
"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.",
|
||||
"labelKillModeEnabled": "Enable test kill mode",
|
||||
"labelTestSafetyLine": "default safety line",
|
||||
"killEnabledOn": "kill on",
|
||||
"killEnabledOff": "kill off",
|
||||
"poolProfitPrefix": "pool profit ",
|
||||
"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.",
|
||||
"killModePanelTitle": "Test kill mode",
|
||||
"killModeSwitchOn": "On",
|
||||
"killModeSwitchOff": "Off",
|
||||
"labelTestSafetyLine": "Test safety line",
|
||||
"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",
|
||||
"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).",
|
||||
|
||||
@@ -54,14 +54,16 @@
|
||||
"weightTest": {
|
||||
"title": "一键测试权重",
|
||||
"alertTitle": "彩金池逻辑说明",
|
||||
"alertBody": "测试模式默认不启用杀分切换;开启「测试内杀分」后,判定规则与线上一致:以 default 彩金池累计盈利(profit_amount)对比安全线,且需 kill_enabled=开启、存在 killScore 配置。",
|
||||
"alertBody": "测试模式默认不启用杀分切换;开启下方「测试内杀分」后,可单独设置测试安全线(与彩金池配置中的安全线无关),用于模拟杀分触发。",
|
||||
"chainModeHint": "模拟方式:只配置付费抽奖次数(顺/逆时针)。付费抽到「再来一次」或 T5 时,下一局自动为免费抽奖,底注与触发局相同,抽奖类型记为免费、付费金额记为 0。免费抽奖的档位概率由下方「免费抽奖」配置决定(含通过再来一次触发的后续免费局)。",
|
||||
"killModeHint": "杀分开关开启后:从 default 奖池当前 profit_amount 起步,测试内逐局累加(付费=win_coin-paid_amount,免费=win_coin);当累计盈利 ≥ 安全线且 kill_enabled 开启时,后续付费抽奖切 killScore;免费抽奖仍走 name=free 奖池。",
|
||||
"labelKillModeEnabled": "开启测试内杀分",
|
||||
"labelTestSafetyLine": "default 安全线",
|
||||
"killEnabledOn": "杀分已开启",
|
||||
"killEnabledOff": "杀分已关闭",
|
||||
"poolProfitPrefix": "当前池盈利 ",
|
||||
"killModeHint": "杀分开关开启后:从 default 奖池当前 profit_amount 起步逐局累加(付费=win_coin-paid_amount,免费=win_coin);当累计盈利 ≥ 下方「测试安全线」时,后续付费抽奖切 killScore;免费抽奖仍走 name=free 奖池。",
|
||||
"killModePanelTitle": "测试内杀分",
|
||||
"killModeSwitchOn": "已开启",
|
||||
"killModeSwitchOff": "已关闭",
|
||||
"labelTestSafetyLine": "测试安全线",
|
||||
"testSafetyLineHint": "仅用于本次权重测试,与彩金池配置页的安全线独立;可设较小值以便快速观察杀分切换效果。",
|
||||
"poolProfitRef": "参考:当前 default 池盈利 {profit},彩金池配置安全线 {line}",
|
||||
"killModeOffHint": "关闭时全程按付费/免费配置抽档,不模拟杀分切换。",
|
||||
"sectionPaid": "付费抽奖",
|
||||
"sectionFreeAfterPlayAgain": "免费抽奖(再来一次后的档位概率)",
|
||||
"tierProbHintFreeChain": "当使用自定义档位时:以下为「免费抽奖」时 T1~T5 的档位概率(仅在有免费局时参与摇档,与 dice_reward 格子权重共同决定结果)。",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<ElForm :model="form" label-width="108px" class="weight-test-form">
|
||||
<ElRow :gutter="16">
|
||||
<ElCol :span="8">
|
||||
<ElCol :span="12">
|
||||
<ElFormItem :label="$t('page.weightTest.labelAnte')" prop="ante_config_id" required>
|
||||
<ElSelect
|
||||
v-model="form.ante_config_id"
|
||||
@@ -38,21 +38,43 @@
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
<ElFormItem :label="$t('page.weightTest.labelKillModeEnabled')" prop="kill_mode_enabled">
|
||||
<ElSwitch v-model="form.kill_mode_enabled" />
|
||||
</ElFormItem>
|
||||
</ElCol>
|
||||
<ElCol :span="8">
|
||||
<ElFormItem :label="$t('page.weightTest.labelTestSafetyLine')">
|
||||
<ElInput
|
||||
:model-value="defaultPoolSafetyLineText"
|
||||
readonly
|
||||
:disabled="!form.kill_mode_enabled"
|
||||
</ElRow>
|
||||
|
||||
<div class="kill-mode-panel">
|
||||
<div class="kill-mode-head">
|
||||
<div class="kill-mode-title">{{ $t('page.weightTest.killModePanelTitle') }}</div>
|
||||
<ElSwitch
|
||||
v-model="form.kill_mode_enabled"
|
||||
:active-text="$t('page.weightTest.killModeSwitchOn')"
|
||||
:inactive-text="$t('page.weightTest.killModeSwitchOff')"
|
||||
inline-prompt
|
||||
/>
|
||||
</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>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
<div class="kill-mode-hint">{{ $t('page.weightTest.testSafetyLineHint') }}</div>
|
||||
<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">
|
||||
<ElCol :span="12">
|
||||
@@ -218,17 +240,8 @@
|
||||
free_tier_weights: { T1: 20, T2: 20, T3: 20, T4: 20, T5: 20 } as Record<string, number>,
|
||||
paid_s_count: 100,
|
||||
paid_n_count: 100,
|
||||
kill_mode_enabled: false
|
||||
})
|
||||
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})`
|
||||
kill_mode_enabled: false,
|
||||
test_safety_line: 5000
|
||||
})
|
||||
const lotteryOptions = ref<Array<{ id: number; name: string }>>([])
|
||||
const paidLotteryOptions = computed(() =>
|
||||
@@ -373,7 +386,7 @@
|
||||
free_n_count: 0,
|
||||
chain_free_mode: true,
|
||||
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) {
|
||||
payload.paid_lottery_config_id = form.paid_lottery_config_id
|
||||
@@ -402,6 +415,10 @@
|
||||
ElMessage.warning(t('page.weightTest.warnPaidSpins'))
|
||||
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 needFreeTier = form.free_lottery_config_id == null
|
||||
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 {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ class DiceRewardController extends BaseController
|
||||
* 参数:lottery_config_id 可选;paid_tier_weights / free_tier_weights 自定义档位;
|
||||
* paid_s_count, paid_n_count
|
||||
* 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')]
|
||||
public function startWeightTest(Request $request): Response
|
||||
|
||||
@@ -17,7 +17,7 @@ use support\think\Db;
|
||||
* 一键测试权重:单进程后台执行模拟摇色子,写入 dice_play_record_test 并更新 dice_reward_config_record 进度
|
||||
* 抽奖规则与 PlayStartLogic 一致:
|
||||
* - 付费未杀分:按模拟玩家档位权重抽档,lottery_config_id 记 default
|
||||
* - 付费杀分:default.profit_amount + safety_line + kill_enabled 满足后切 killScore
|
||||
* - 付费杀分:测试内杀分开启且模拟池盈利 >= test_safety_line 后切 killScore
|
||||
* - 免费券:name=free 奖池(无则 default),排除 5/30 豹子
|
||||
*/
|
||||
class WeightTestRunner
|
||||
@@ -115,10 +115,12 @@ class WeightTestRunner
|
||||
DiceReward::clearRequestInstance();
|
||||
|
||||
$killModeEnabled = (int) ($record->kill_mode_enabled ?? 0) === 1;
|
||||
$safetyLine = (int) ($configType0->safety_line ?? 0);
|
||||
$dbKillEnabled = ((int) ($configType0->kill_enabled ?? 1)) === 1;
|
||||
$testSafetyLine = (int) ($record->test_safety_line ?? 5000);
|
||||
if ($testSafetyLine < 0) {
|
||||
$testSafetyLine = 0;
|
||||
}
|
||||
|
||||
// 彩金池累计盈利:与线上一致,从 default.profit_amount 起步并在测试内逐局累加
|
||||
// 彩金池累计盈利:从 default.profit_amount 起步并在测试内逐局累加,与杀分判定值一致
|
||||
$poolProfitTotal = (float) ($configType0->profit_amount ?? 0);
|
||||
|
||||
// 付费未杀分时的模拟玩家档位权重(自定义 > 快照 > 兜底奖池)
|
||||
@@ -145,8 +147,7 @@ class WeightTestRunner
|
||||
$paidPlayerWeights,
|
||||
$freeTierWeightsCustom,
|
||||
$killModeEnabled,
|
||||
$safetyLine,
|
||||
$dbKillEnabled,
|
||||
$testSafetyLine,
|
||||
$poolProfitTotal,
|
||||
$resultCounts,
|
||||
$tierCounts,
|
||||
@@ -186,8 +187,7 @@ class WeightTestRunner
|
||||
?array $paidPlayerWeights,
|
||||
?array $freeTierWeightsCustom,
|
||||
bool $killModeEnabled,
|
||||
int $safetyLine,
|
||||
bool $dbKillEnabled,
|
||||
int $testSafetyLine,
|
||||
float &$poolProfitTotal,
|
||||
array &$resultCounts,
|
||||
array &$tierCounts,
|
||||
@@ -211,8 +211,7 @@ class WeightTestRunner
|
||||
|
||||
if ($isPaid) {
|
||||
$useKillForPaid = $killModeEnabled
|
||||
&& $dbKillEnabled
|
||||
&& $poolProfitTotal >= $safetyLine
|
||||
&& $poolProfitTotal >= $testSafetyLine
|
||||
&& $killPoolConfig !== null;
|
||||
if ($useKillForPaid) {
|
||||
$cfg = $killPoolConfig;
|
||||
|
||||
Reference in New Issue
Block a user