优化彩金池累加计算方式
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
<span class="realtime-badge">实时</span>
|
||||
</div>
|
||||
<div class="profit-calc-hint">
|
||||
计算方式:每局抽奖累加 (100 − 该局中奖档位 real_ev),弹窗打开期间每 2 秒自动刷新
|
||||
计算方式:每局抽奖扣除本局发放成本(普通档位 real_ev + 中大奖时 BIGWIN.real_ev),弹窗打开期间每 2 秒自动刷新
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip-block">
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="art-full-height">
|
||||
<!-- ???? -->
|
||||
<!-- 搜索条件 -->
|
||||
<TableSearch v-model="searchForm" @search="handleSearch" @reset="resetSearchParams" />
|
||||
|
||||
<ElCard class="art-table-card" shadow="never">
|
||||
<!-- ???? -->
|
||||
<!-- 表格操作 -->
|
||||
<ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
|
||||
<template #left>
|
||||
<ElSpace wrap>
|
||||
@@ -12,7 +12,7 @@
|
||||
<template #icon>
|
||||
<ArtSvgIcon icon="ri:add-fill" />
|
||||
</template>
|
||||
??
|
||||
新增
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-permission="'dice:player:index:destroy'"
|
||||
@@ -23,13 +23,13 @@
|
||||
<template #icon>
|
||||
<ArtSvgIcon icon="ri:delete-bin-5-line" />
|
||||
</template>
|
||||
??
|
||||
删除
|
||||
</ElButton>
|
||||
</ElSpace>
|
||||
</template>
|
||||
</ArtTableHeader>
|
||||
|
||||
<!-- ?? -->
|
||||
<!-- 表格 -->
|
||||
<ArtTable
|
||||
ref="tableRef"
|
||||
rowKey="id"
|
||||
@@ -42,7 +42,7 @@
|
||||
@pagination:size-change="handleSizeChange"
|
||||
@pagination:current-change="handleCurrentChange"
|
||||
>
|
||||
<!-- ????????? -->
|
||||
<!-- 状态列 -->
|
||||
<template #status="{ row }">
|
||||
<ElSwitch
|
||||
v-permission="'dice:player:index:update'"
|
||||
@@ -51,7 +51,7 @@
|
||||
@change="(v: string | number | boolean) => handleStatusChange(row, v ? 1 : 0)"
|
||||
/>
|
||||
</template>
|
||||
<!-- ????tag ??????????? -->
|
||||
<!-- 平台币,点击可操作 -->
|
||||
<template #coin="{ row }">
|
||||
<ElTag
|
||||
type="info"
|
||||
@@ -62,7 +62,7 @@
|
||||
{{ row.coin ?? 0 }}
|
||||
</ElTag>
|
||||
</template>
|
||||
<!-- ??? -->
|
||||
<!-- 操作 -->
|
||||
<template #operation="{ row }">
|
||||
<div class="flex gap-2">
|
||||
<SaButton
|
||||
@@ -80,7 +80,7 @@
|
||||
</ArtTable>
|
||||
</ElCard>
|
||||
|
||||
<!-- ???? -->
|
||||
<!-- 编辑弹窗 -->
|
||||
<EditDialog
|
||||
v-model="dialogVisible"
|
||||
:dialog-type="dialogType"
|
||||
@@ -88,7 +88,7 @@
|
||||
@success="refreshData"
|
||||
/>
|
||||
|
||||
<!-- ?????????/??? -->
|
||||
<!-- 钱包操作弹窗 -->
|
||||
<WalletOperateDialog
|
||||
v-model="walletDialogVisible"
|
||||
:player="walletOperatePlayer"
|
||||
@@ -105,7 +105,7 @@
|
||||
import EditDialog from './modules/edit-dialog.vue'
|
||||
import WalletOperateDialog from './modules/WalletOperateDialog.vue'
|
||||
|
||||
// ????
|
||||
// 搜索表单
|
||||
const searchForm = ref({
|
||||
username: undefined,
|
||||
name: undefined,
|
||||
@@ -115,23 +115,23 @@
|
||||
lottery_config_id: undefined
|
||||
})
|
||||
|
||||
// ????
|
||||
// 搜索
|
||||
const handleSearch = (params: Record<string, any>) => {
|
||||
Object.assign(searchParams, params)
|
||||
getData()
|
||||
}
|
||||
|
||||
// ???? % ? formatter?ColumnOption.formatter ??? row?
|
||||
// 权重列显示为百分比
|
||||
const weightFormatter = (prop: string) => (row: any) => {
|
||||
const cellValue = row[prop]
|
||||
return cellValue != null && cellValue !== '' ? `${cellValue}%` : '-'
|
||||
}
|
||||
|
||||
// ???????lottery_config_id ?? DiceLotteryPoolConfig??? name
|
||||
// 根据 lottery_config_id 显示彩金池配置名称
|
||||
const lotteryConfigNameFormatter = (row: any) =>
|
||||
row?.diceLotteryPoolConfig?.name ?? (row?.lottery_config_id ? `#${row.lottery_config_id}` : '???')
|
||||
row?.diceLotteryPoolConfig?.name ?? (row?.lottery_config_id ? `#${row.lottery_config_id}` : '未知')
|
||||
|
||||
// ????
|
||||
// 表格
|
||||
const {
|
||||
columns,
|
||||
columnChecks,
|
||||
@@ -150,73 +150,73 @@
|
||||
apiFn: api.list,
|
||||
columnsFactory: () => [
|
||||
{ type: 'selection' },
|
||||
{ prop: 'username', label: '???', align: 'center' },
|
||||
{ prop: 'phone', label: '???', align: 'center' },
|
||||
{ prop: 'name', label: '??', align: 'center' },
|
||||
{ prop: 'username', label: '用户名', align: 'center' },
|
||||
{ prop: 'phone', label: '手机号', align: 'center' },
|
||||
{ prop: 'name', label: '昵称', align: 'center' },
|
||||
{
|
||||
prop: 'status',
|
||||
label: '??',
|
||||
label: '状态',
|
||||
width: 88,
|
||||
align: 'center',
|
||||
useSlot: true
|
||||
},
|
||||
{
|
||||
prop: 'coin',
|
||||
label: '???',
|
||||
label: '平台币',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
useSlot: true
|
||||
},
|
||||
{
|
||||
prop: 'lottery_config_id',
|
||||
label: '?????',
|
||||
label: '彩金池配置',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: (row: any) => lotteryConfigNameFormatter(row)
|
||||
},
|
||||
{
|
||||
prop: 't1_weight',
|
||||
label: 'T1???',
|
||||
label: 'T1权重',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
formatter: weightFormatter('t1_weight')
|
||||
},
|
||||
{
|
||||
prop: 't2_weight',
|
||||
label: 'T2???',
|
||||
label: 'T2权重',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
formatter: weightFormatter('t2_weight')
|
||||
},
|
||||
{
|
||||
prop: 't3_weight',
|
||||
label: 'T3???',
|
||||
label: 'T3权重',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
formatter: weightFormatter('t3_weight')
|
||||
},
|
||||
{
|
||||
prop: 't4_weight',
|
||||
label: 'T4???',
|
||||
label: 'T4权重',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
formatter: weightFormatter('t4_weight')
|
||||
},
|
||||
{
|
||||
prop: 't5_weight',
|
||||
label: 'T5???',
|
||||
label: 'T5权重',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
formatter: weightFormatter('t5_weight')
|
||||
},
|
||||
{ prop: 'total_ticket_count', label: '?????', align: 'center' },
|
||||
{ prop: 'paid_ticket_count', label: '??????', align: 'center' },
|
||||
{ prop: 'free_ticket_count', label: '??????', align: 'center' },
|
||||
{ prop: 'create_time', label: '????', align: 'center' },
|
||||
{ prop: 'update_time', label: '????', align: 'center' },
|
||||
{ prop: 'total_ticket_count', label: '总抽奖次数', align: 'center' },
|
||||
{ prop: 'paid_ticket_count', label: '购买抽奖次数', align: 'center' },
|
||||
{ prop: 'free_ticket_count', label: '赠送抽奖次数', align: 'center' },
|
||||
{ prop: 'create_time', label: '创建时间', align: 'center' },
|
||||
{ prop: 'update_time', label: '更新时间', align: 'center' },
|
||||
{
|
||||
prop: 'operation',
|
||||
label: '??',
|
||||
label: '操作',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
@@ -226,7 +226,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
// ???????????????
|
||||
// 状态切换
|
||||
const handleStatusChange = async (row: Record<string, any>, status: number) => {
|
||||
row._statusLoading = true
|
||||
try {
|
||||
@@ -239,7 +239,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ????
|
||||
// 弹窗与删除
|
||||
const {
|
||||
dialogType,
|
||||
dialogVisible,
|
||||
@@ -251,7 +251,7 @@
|
||||
selectedRows
|
||||
} = useSaiAdmin()
|
||||
|
||||
// ??????????? tag ?????
|
||||
// 钱包操作弹窗
|
||||
const walletDialogVisible = ref(false)
|
||||
type WalletPlayer = { id: number; username?: string; coin?: number }
|
||||
const walletOperatePlayer = ref<WalletPlayer | null>(null)
|
||||
|
||||
@@ -102,8 +102,8 @@
|
||||
set: (value) => emit('update:modelValue', value)
|
||||
})
|
||||
|
||||
/** BIGWIN 且 grid_number 为 5 或 30 时豹子概率不可修改 */
|
||||
const isWeightDisabled = computed(
|
||||
/** BIGWIN 且 grid_number 为 5 或 30 时豹子概率固定为 100%(禁止手动调整) */
|
||||
const isWeightFixed100 = computed(
|
||||
() =>
|
||||
formData.tier === 'BIGWIN' &&
|
||||
(formData.grid_number === 5 || formData.grid_number === 30)
|
||||
|
||||
@@ -101,7 +101,7 @@ class PlayStartLogic
|
||||
}
|
||||
$maxRewardRetry = count($tierRewards);
|
||||
for ($attempt = 0; $attempt < $maxRewardRetry; $attempt++) {
|
||||
$chosen = $tierRewards[array_rand($tierRewards)];
|
||||
$chosen = self::drawRewardByWeight($tierRewards);
|
||||
$chosenId = (int) ($chosen['id'] ?? 0);
|
||||
if ($direction === 0) {
|
||||
$startCandidates = DiceRewardConfig::getCachedBySEndIndex($chosenId);
|
||||
@@ -246,10 +246,10 @@ class PlayStartLogic
|
||||
|
||||
$p->save();
|
||||
|
||||
// 彩金池盈利累加:每局累加 (100 - 本局总 real_ev),其中本局总 real_ev = 普通档位 real_ev + BIGWIN.real_ev(如触发)
|
||||
// 彩金池盈利:每局扣除本局发放的真实成本(普通档位 real_ev + BIGWIN.real_ev 如触发),不额外加 100
|
||||
// 需确保表有 profit_amount 字段(见 db/dice_lottery_config_add_profit_amount.sql)
|
||||
$totalRealEv = $realEv + $bigWinRealEv;
|
||||
$addProfit = 100 - $totalRealEv;
|
||||
$addProfit = -$totalRealEv;
|
||||
try {
|
||||
DiceLotteryPoolConfig::where('id', $configId)->update([
|
||||
'profit_amount' => Db::raw('IFNULL(profit_amount,0) + ' . (float) $addProfit),
|
||||
@@ -322,6 +322,36 @@ class PlayStartLogic
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* T1-T5 档位内按 DiceRewardConfig.weight 抽取一条配置;权重均为 0 或未设时等权随机
|
||||
* @param array<int, array> $rewards 该档位配置列表(每条含 weight、id、real_ev 等)
|
||||
* @return array 抽中的一条配置
|
||||
*/
|
||||
private static function drawRewardByWeight(array $rewards): array
|
||||
{
|
||||
if (empty($rewards)) {
|
||||
throw new \InvalidArgumentException('rewards 不能为空');
|
||||
}
|
||||
$weights = [];
|
||||
foreach ($rewards as $i => $row) {
|
||||
$w = isset($row['weight']) ? max(0, (float) $row['weight']) : 0;
|
||||
$weights[$i] = $w;
|
||||
}
|
||||
$total = array_sum($weights);
|
||||
if ($total <= 0) {
|
||||
return $rewards[array_rand($rewards)];
|
||||
}
|
||||
$r = mt_rand(1, (int) $total);
|
||||
$acc = 0;
|
||||
foreach ($weights as $i => $w) {
|
||||
$acc += $w;
|
||||
if ($r <= $acc) {
|
||||
return $rewards[$i];
|
||||
}
|
||||
}
|
||||
return $rewards[array_key_last($rewards)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据摇取点数(5-30)生成 5 个色子数组,每个 1-6,总和为 $sum
|
||||
* @return int[] 如 [1,2,3,4,5]
|
||||
|
||||
Reference in New Issue
Block a user