优化游玩记录DicePlayRecord
This commit is contained in:
@@ -117,6 +117,8 @@
|
|||||||
is_win: undefined,
|
is_win: undefined,
|
||||||
win_coin_min: undefined,
|
win_coin_min: undefined,
|
||||||
win_coin_max: undefined,
|
win_coin_max: undefined,
|
||||||
|
roll_number_min: undefined,
|
||||||
|
roll_number_max: undefined,
|
||||||
reward_ui_text: undefined,
|
reward_ui_text: undefined,
|
||||||
reward_tier: undefined,
|
reward_tier: undefined,
|
||||||
direction: undefined
|
direction: undefined
|
||||||
@@ -190,6 +192,7 @@
|
|||||||
{ prop: 'start_index', label: '起始索引', width: 90 },
|
{ prop: 'start_index', label: '起始索引', width: 90 },
|
||||||
{ prop: 'target_index', label: '终点索引', width: 90 },
|
{ prop: 'target_index', label: '终点索引', width: 90 },
|
||||||
{ prop: 'roll_array', label: '摇取点数', width: 140, useSlot: true },
|
{ prop: 'roll_array', label: '摇取点数', width: 140, useSlot: true },
|
||||||
|
{ prop: 'roll_number', label: '摇取点数和', width: 110, sortable: true },
|
||||||
{
|
{
|
||||||
prop: 'reward_config_id',
|
prop: 'reward_config_id',
|
||||||
label: '奖励配置',
|
label: '奖励配置',
|
||||||
|
|||||||
@@ -142,6 +142,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="roll-array-hint">固定 5 个数,每个 1~6</div>
|
<div class="roll-array-hint">固定 5 个数,每个 1~6</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="摇取点数和" prop="roll_number">
|
||||||
|
<el-input-number
|
||||||
|
v-model="formData.roll_number"
|
||||||
|
placeholder="5 个色子点数之和(5~30)"
|
||||||
|
:min="5"
|
||||||
|
:max="30"
|
||||||
|
:precision="0"
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="dialogType === 'edit'"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="奖励配置" prop="reward_config_id">
|
<el-form-item label="奖励配置" prop="reward_config_id">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="formData.reward_config_id"
|
v-model="formData.reward_config_id"
|
||||||
@@ -245,6 +256,7 @@
|
|||||||
start_index: null as number | null,
|
start_index: null as number | null,
|
||||||
target_index: null as number | null,
|
target_index: null as number | null,
|
||||||
roll_array: null as string | number[] | null,
|
roll_array: null as string | number[] | null,
|
||||||
|
roll_number: null as number | null,
|
||||||
reward_config_id: null as number | null
|
reward_config_id: null as number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,6 +318,7 @@
|
|||||||
'start_index',
|
'start_index',
|
||||||
'target_index',
|
'target_index',
|
||||||
'roll_array',
|
'roll_array',
|
||||||
|
'roll_number',
|
||||||
'reward_config_id'
|
'reward_config_id'
|
||||||
]
|
]
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
@@ -319,6 +332,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// 若后端未返回 roll_number,根据摇取点数计算
|
||||||
|
if (formData.roll_number == null && formData.rollArrayItems.length === 5) {
|
||||||
|
formData.roll_number = formData.rollArrayItems.reduce((s, n) => s + (n ?? 0), 0) || null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 将接口的 roll_array 转为固定 5 项数组,不足补 null */
|
/** 将接口的 roll_array 转为固定 5 项数组,不足补 null */
|
||||||
@@ -355,10 +372,12 @@
|
|||||||
const payload = { ...formData } as Record<string, unknown>
|
const payload = { ...formData } as Record<string, unknown>
|
||||||
// 将 5 个输入值拼成 [1,2,3,4,5] 格式,确保每项为 1~6 的整数
|
// 将 5 个输入值拼成 [1,2,3,4,5] 格式,确保每项为 1~6 的整数
|
||||||
const items = formData.rollArrayItems
|
const items = formData.rollArrayItems
|
||||||
payload.roll_array = items.map((n) => {
|
const rollArray = items.map((n) => {
|
||||||
const v = n != null ? Number(n) : 1
|
const v = n != null ? Number(n) : 1
|
||||||
return Math.min(6, Math.max(1, Number.isNaN(v) ? 1 : Math.floor(v)))
|
return Math.min(6, Math.max(1, Number.isNaN(v) ? 1 : Math.floor(v)))
|
||||||
})
|
})
|
||||||
|
payload.roll_array = rollArray
|
||||||
|
payload.roll_number = formData.roll_number ?? rollArray.reduce((s, n) => s + n, 0)
|
||||||
delete payload.rollArrayItems
|
delete payload.rollArrayItems
|
||||||
if (props.dialogType === 'add') {
|
if (props.dialogType === 'add') {
|
||||||
delete payload.id
|
delete payload.id
|
||||||
|
|||||||
@@ -63,6 +63,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col v-bind="setSpan(6)">
|
||||||
|
<el-form-item label="摇取点数和" prop="roll_number_min">
|
||||||
|
<div class="range-wrap">
|
||||||
|
<el-input-number
|
||||||
|
v-model="formData.roll_number_min"
|
||||||
|
placeholder="最小"
|
||||||
|
:min="5"
|
||||||
|
:max="30"
|
||||||
|
:precision="0"
|
||||||
|
controls-position="right"
|
||||||
|
class="range-input"
|
||||||
|
/>
|
||||||
|
<span class="range-sep">至</span>
|
||||||
|
<el-input-number
|
||||||
|
v-model="formData.roll_number_max"
|
||||||
|
placeholder="最大"
|
||||||
|
:min="5"
|
||||||
|
:max="30"
|
||||||
|
:precision="0"
|
||||||
|
controls-position="right"
|
||||||
|
class="range-input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col v-bind="setSpan(6)">
|
<el-col v-bind="setSpan(6)">
|
||||||
<el-form-item label="奖励配置" prop="reward_ui_text">
|
<el-form-item label="奖励配置" prop="reward_ui_text">
|
||||||
<el-input v-model="formData.reward_ui_text" placeholder="前端显示文本模糊" clearable />
|
<el-input v-model="formData.reward_ui_text" placeholder="前端显示文本模糊" clearable />
|
||||||
|
|||||||
@@ -122,6 +122,7 @@
|
|||||||
{ prop: 'ui_text', label: '前端显示文本', align: 'center' },
|
{ prop: 'ui_text', label: '前端显示文本', align: 'center' },
|
||||||
{ prop: 'real_ev', label: '真实资金结算', align: 'center' },
|
{ prop: 'real_ev', label: '真实资金结算', align: 'center' },
|
||||||
{ prop: 'tier', label: '所属档位', sortable: true, align: 'center' },
|
{ prop: 'tier', label: '所属档位', sortable: true, align: 'center' },
|
||||||
|
{ prop: 'weight', label: '权重(%)', width: 100, align: 'center' },
|
||||||
// { prop: 'create_time', label: '创建时间', sortable: true, align: 'center' },
|
// { prop: 'create_time', label: '创建时间', sortable: true, align: 'center' },
|
||||||
{
|
{
|
||||||
prop: 'operation',
|
prop: 'operation',
|
||||||
|
|||||||
@@ -34,8 +34,12 @@
|
|||||||
<el-option label="T3" value="T3" />
|
<el-option label="T3" value="T3" />
|
||||||
<el-option label="T4" value="T4" />
|
<el-option label="T4" value="T4" />
|
||||||
<el-option label="T5" value="T5" />
|
<el-option label="T5" value="T5" />
|
||||||
|
<el-option label="BIGWIN(超级大奖)" value="BIGWIN" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item v-if="formData.tier === 'BIGWIN'" label="权重(%)" prop="weight">
|
||||||
|
<el-slider v-model="formData.weight" :min="0" :max="100" :step="0.01" show-input />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注" prop="remark">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.remark"
|
v-model="formData.remark"
|
||||||
@@ -95,7 +99,24 @@
|
|||||||
grid_number: [{ required: true, message: '色子点数必需填写', trigger: 'blur' }],
|
grid_number: [{ required: true, message: '色子点数必需填写', trigger: 'blur' }],
|
||||||
ui_text: [{ required: true, message: '前端显示文本必需填写', trigger: 'blur' }],
|
ui_text: [{ required: true, message: '前端显示文本必需填写', trigger: 'blur' }],
|
||||||
real_ev: [{ required: true, message: '真实资金结算必需填写', trigger: 'blur' }],
|
real_ev: [{ required: true, message: '真实资金结算必需填写', trigger: 'blur' }],
|
||||||
tier: [{ required: true, message: '所属档位必需填写', trigger: 'blur' }]
|
tier: [{ required: true, message: '所属档位必需填写', trigger: 'blur' }],
|
||||||
|
weight: [
|
||||||
|
{
|
||||||
|
validator: (_rule: unknown, value: number | null, callback: (e?: Error) => void) => {
|
||||||
|
if (formData.tier !== 'BIGWIN') {
|
||||||
|
callback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const n = value != null ? Number(value) : NaN
|
||||||
|
if (Number.isNaN(n) || n < 0 || n > 100) {
|
||||||
|
callback(new Error('权重仅 BIGWIN 可设定,且必须为 0-100%'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,6 +128,7 @@
|
|||||||
ui_text: '',
|
ui_text: '',
|
||||||
real_ev: '',
|
real_ev: '',
|
||||||
tier: '',
|
tier: '',
|
||||||
|
weight: 0 as number,
|
||||||
remark: ''
|
remark: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,14 +163,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化表单数据
|
* 初始化表单数据(数值字段转为 number,便于滑块/输入框正确回显)
|
||||||
*/
|
*/
|
||||||
const initForm = () => {
|
const initForm = () => {
|
||||||
if (props.data) {
|
if (!props.data) return
|
||||||
for (const key in formData) {
|
const numKeys = ['id', 'grid_number', 'real_ev', 'weight']
|
||||||
if (props.data[key] != null && props.data[key] != undefined) {
|
for (const key of Object.keys(formData)) {
|
||||||
;(formData as any)[key] = props.data[key]
|
if (!(key in props.data)) continue
|
||||||
}
|
const val = props.data[key]
|
||||||
|
if (val == null || val === undefined) continue
|
||||||
|
if (numKeys.includes(key)) {
|
||||||
|
const numVal = Number(val)
|
||||||
|
;(formData as Record<string, unknown>)[key] =
|
||||||
|
key === 'id' ? numVal || null : Number.isNaN(numVal) ? 0 : numVal
|
||||||
|
} else {
|
||||||
|
;(formData as Record<string, unknown>)[key] = val ?? ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,11 +197,18 @@
|
|||||||
if (!formRef.value) return
|
if (!formRef.value) return
|
||||||
try {
|
try {
|
||||||
await formRef.value.validate()
|
await formRef.value.validate()
|
||||||
|
const payload = { ...formData }
|
||||||
|
if (payload.tier !== 'BIGWIN') {
|
||||||
|
payload.weight = 0
|
||||||
|
} else {
|
||||||
|
const w = Number(payload.weight)
|
||||||
|
payload.weight = Number.isNaN(w) ? 0 : Math.max(0, Math.min(100, w))
|
||||||
|
}
|
||||||
if (props.dialogType === 'add') {
|
if (props.dialogType === 'add') {
|
||||||
await api.save(formData)
|
await api.save(payload)
|
||||||
ElMessage.success('新增成功')
|
ElMessage.success('新增成功')
|
||||||
} else {
|
} else {
|
||||||
await api.update(formData)
|
await api.update(payload)
|
||||||
ElMessage.success('修改成功')
|
ElMessage.success('修改成功')
|
||||||
}
|
}
|
||||||
emit('success')
|
emit('success')
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ class GameController extends OpenController
|
|||||||
'start_index' => 0,
|
'start_index' => 0,
|
||||||
'target_index' => 0,
|
'target_index' => 0,
|
||||||
'roll_array' => '[]',
|
'roll_array' => '[]',
|
||||||
|
'roll_number' => 0,
|
||||||
'status' => PlayStartLogic::RECORD_STATUS_TIMEOUT,
|
'status' => PlayStartLogic::RECORD_STATUS_TIMEOUT,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $inner) {
|
} catch (\Exception $inner) {
|
||||||
|
|||||||
@@ -34,8 +34,10 @@ class PlayStartLogic
|
|||||||
|
|
||||||
/** 开启对局最低余额 = |DiceRewardConfig 最小 real_ev + 100| */
|
/** 开启对局最低余额 = |DiceRewardConfig 最小 real_ev + 100| */
|
||||||
private const MIN_COIN_EXTRA = 100;
|
private const MIN_COIN_EXTRA = 100;
|
||||||
/** 豹子号中大奖额外平台币(可从 dice_config 等配置读取) */
|
/** 豹子号中大奖额外平台币(无 BIGWIN 配置时兜底) */
|
||||||
private const SUPER_WIN_BONUS = 500;
|
private const SUPER_WIN_BONUS = 500;
|
||||||
|
/** 可触发超级大奖的 grid_number(5=全1 10=全2 15=全3 20=全4 25=全5) */
|
||||||
|
private const SUPER_WIN_GRID_NUMBERS = [5, 10, 15, 20, 25];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行一局游戏
|
* 执行一局游戏
|
||||||
@@ -112,7 +114,30 @@ class PlayStartLogic
|
|||||||
? (int) ($startRecord['s_end_index'] ?? 0)
|
? (int) ($startRecord['s_end_index'] ?? 0)
|
||||||
: (int) ($startRecord['n_end_index'] ?? 0);
|
: (int) ($startRecord['n_end_index'] ?? 0);
|
||||||
$rollNumber = (int) ($startRecord['grid_number'] ?? 0);
|
$rollNumber = (int) ($startRecord['grid_number'] ?? 0);
|
||||||
|
$realEv = (float) ($chosen['real_ev'] ?? 0);
|
||||||
|
$rewardWinCoin = 100 + $realEv; // 摇色子中奖平台币 = 100 + DiceRewardConfig.real_ev
|
||||||
|
|
||||||
|
// 当抽到的 grid_number 为 5/10/15/20/25 时,从缓存查 tier=BIGWIN 同 grid_number 的配置,按 weight 决定是否生成豹子组合
|
||||||
|
$superWinCoin = 0;
|
||||||
|
$isWin = 0;
|
||||||
|
if (in_array($rollNumber, self::SUPER_WIN_GRID_NUMBERS, true)) {
|
||||||
|
$bigWinConfig = DiceRewardConfig::getCachedByTierAndGridNumber('BIGWIN', $rollNumber);
|
||||||
|
$weight = $bigWinConfig !== null
|
||||||
|
? max(0.0, min(100.0, (float) ($bigWinConfig['weight'] ?? 0)))
|
||||||
|
: 100.0;
|
||||||
|
$roll = mt_rand(1, 10000) / 10000;
|
||||||
|
if ($roll <= $weight / 100) {
|
||||||
|
$rollArray = $this->getSuperWinRollArray($rollNumber);
|
||||||
|
$isWin = 1;
|
||||||
|
$superWinCoin = $bigWinConfig !== null
|
||||||
|
? 100 + (float) ($bigWinConfig['real_ev'] ?? 0)
|
||||||
|
: self::SUPER_WIN_BONUS;
|
||||||
|
} else {
|
||||||
|
$rollArray = $this->generateNonSuperWinRollArrayWithSum($rollNumber);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
$rollArray = $this->generateRollArrayFromSum($rollNumber);
|
$rollArray = $this->generateRollArrayFromSum($rollNumber);
|
||||||
|
}
|
||||||
|
|
||||||
Log::info(sprintf(
|
Log::info(sprintf(
|
||||||
'摇取点数 roll_number=%d, 方向=%d, start_index=%d, target_index=%d',
|
'摇取点数 roll_number=%d, 方向=%d, start_index=%d, target_index=%d',
|
||||||
@@ -121,17 +146,6 @@ class PlayStartLogic
|
|||||||
$startIndex,
|
$startIndex,
|
||||||
$targetIndex
|
$targetIndex
|
||||||
));
|
));
|
||||||
$realEv = (float) ($chosen['real_ev'] ?? 0);
|
|
||||||
$rewardWinCoin = 100 + $realEv; // 摇色子中奖平台币 = 100 + DiceRewardConfig.real_ev
|
|
||||||
$isSuperWin = DicePlayRecord::isSuperWin($rollArray);
|
|
||||||
// 豹子中大奖时从缓存查 tier=BIGWIN 且 grid_number=roll_number 的奖励配置,取 real_ev 计算中大奖平台币
|
|
||||||
$superWinCoin = 0;
|
|
||||||
if ($isSuperWin) {
|
|
||||||
$bigWinConfig = DiceRewardConfig::getCachedByTierAndGridNumber('BIGWIN', $rollNumber);
|
|
||||||
$superWinCoin = $bigWinConfig !== null
|
|
||||||
? 100 + (float) ($bigWinConfig['real_ev'] ?? 0)
|
|
||||||
: self::SUPER_WIN_BONUS;
|
|
||||||
}
|
|
||||||
$winCoin = $superWinCoin + $rewardWinCoin; // 赢取平台币 = 中大奖 + 摇色子中奖
|
$winCoin = $superWinCoin + $rewardWinCoin; // 赢取平台币 = 中大奖 + 摇色子中奖
|
||||||
|
|
||||||
$record = null;
|
$record = null;
|
||||||
@@ -139,7 +153,6 @@ class PlayStartLogic
|
|||||||
$rewardId = $chosenId;
|
$rewardId = $chosenId;
|
||||||
$configName = (string) ($config->name ?? '');
|
$configName = (string) ($config->name ?? '');
|
||||||
$isTierT5 = (string) ($chosen['tier'] ?? '') === 'T5';
|
$isTierT5 = (string) ($chosen['tier'] ?? '') === 'T5';
|
||||||
$isWin = $isSuperWin ? 1 : 0;
|
|
||||||
try {
|
try {
|
||||||
Db::transaction(function () use (
|
Db::transaction(function () use (
|
||||||
$playerId,
|
$playerId,
|
||||||
@@ -173,6 +186,7 @@ class PlayStartLogic
|
|||||||
'start_index' => $startIndex,
|
'start_index' => $startIndex,
|
||||||
'target_index' => $targetIndex,
|
'target_index' => $targetIndex,
|
||||||
'roll_array' => is_array($rollArray) ? json_encode($rollArray) : $rollArray,
|
'roll_array' => is_array($rollArray) ? json_encode($rollArray) : $rollArray,
|
||||||
|
'roll_number' => is_array($rollArray) ? array_sum($rollArray) : 0,
|
||||||
'lottery_name' => $configName,
|
'lottery_name' => $configName,
|
||||||
'status' => self::RECORD_STATUS_SUCCESS,
|
'status' => self::RECORD_STATUS_SUCCESS,
|
||||||
]);
|
]);
|
||||||
@@ -239,6 +253,7 @@ class PlayStartLogic
|
|||||||
'start_index' => $startIndex,
|
'start_index' => $startIndex,
|
||||||
'target_index' => 0,
|
'target_index' => 0,
|
||||||
'roll_array' => '[]',
|
'roll_array' => '[]',
|
||||||
|
'roll_number' => 0,
|
||||||
'status' => self::RECORD_STATUS_TIMEOUT,
|
'status' => self::RECORD_STATUS_TIMEOUT,
|
||||||
]);
|
]);
|
||||||
} catch (\Throwable $_) {
|
} catch (\Throwable $_) {
|
||||||
@@ -290,4 +305,51 @@ class PlayStartLogic
|
|||||||
shuffle($arr);
|
shuffle($arr);
|
||||||
return array_values($arr);
|
return array_values($arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 豹子组合:grid_number 5->[1,1,1,1,1],10->[2,2,2,2,2],15->[3,3,3,3,3],20->[4,4,4,4,4],25->[5,5,5,5,5]
|
||||||
|
* @return int[]
|
||||||
|
*/
|
||||||
|
private function getSuperWinRollArray(int $gridNumber): array
|
||||||
|
{
|
||||||
|
$n = (int) ($gridNumber / 5);
|
||||||
|
$n = max(1, min(5, $n));
|
||||||
|
return array_fill(0, 5, $n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成总和为 $sum 且非豹子的 5 个色子(1-6);sum=5 时仅 [1,1,1,1,1] 可能,仍返回该组合
|
||||||
|
* @return int[]
|
||||||
|
*/
|
||||||
|
private function generateNonSuperWinRollArrayWithSum(int $sum): array
|
||||||
|
{
|
||||||
|
$sum = max(5, min(30, $sum));
|
||||||
|
$super = $this->getSuperWinRollArray($sum);
|
||||||
|
if ($sum === 5) {
|
||||||
|
return $super;
|
||||||
|
}
|
||||||
|
$arr = $super;
|
||||||
|
$maxAttempts = 20;
|
||||||
|
for ($a = 0; $a < $maxAttempts; $a++) {
|
||||||
|
$idx = array_rand($arr);
|
||||||
|
$j = array_rand($arr);
|
||||||
|
if ($idx === $j) {
|
||||||
|
$j = ($j + 1) % 5;
|
||||||
|
}
|
||||||
|
$i = $idx;
|
||||||
|
if ($arr[$i] >= 2 && $arr[$j] <= 5) {
|
||||||
|
$arr[$i]--;
|
||||||
|
$arr[$j]++;
|
||||||
|
shuffle($arr);
|
||||||
|
return array_values($arr);
|
||||||
|
}
|
||||||
|
if ($arr[$i] <= 5 && $arr[$j] >= 2) {
|
||||||
|
$arr[$i]++;
|
||||||
|
$arr[$j]--;
|
||||||
|
shuffle($arr);
|
||||||
|
return array_values($arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->generateRollArrayFromSum($sum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ class DicePlayRecordController extends BaseController
|
|||||||
['is_win', ''],
|
['is_win', ''],
|
||||||
['win_coin_min', ''],
|
['win_coin_min', ''],
|
||||||
['win_coin_max', ''],
|
['win_coin_max', ''],
|
||||||
|
['roll_number_min', ''],
|
||||||
|
['roll_number_max', ''],
|
||||||
['reward_ui_text', ''],
|
['reward_ui_text', ''],
|
||||||
['reward_tier', ''],
|
['reward_tier', ''],
|
||||||
['direction', ''],
|
['direction', ''],
|
||||||
|
|||||||
@@ -43,16 +43,18 @@ class DicePlayRecordLogic extends BaseLogic
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将 roll_array 从数组转为 JSON 字符串
|
* 将 roll_array 转为 JSON 字符串,并确保 roll_number 与摇取点数一致
|
||||||
*/
|
*/
|
||||||
private function normalizeRollArray(array $data): array
|
private function normalizeRollArray(array $data): array
|
||||||
{
|
{
|
||||||
if (!array_key_exists('roll_array', $data)) {
|
if (array_key_exists('roll_array', $data)) {
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
$val = $data['roll_array'];
|
$val = $data['roll_array'];
|
||||||
if (is_array($val)) {
|
if (is_array($val)) {
|
||||||
$data['roll_array'] = json_encode($val, JSON_UNESCAPED_UNICODE);
|
$data['roll_array'] = json_encode($val, JSON_UNESCAPED_UNICODE);
|
||||||
|
if (!isset($data['roll_number'])) {
|
||||||
|
$data['roll_number'] = array_sum($val);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use app\dice\model\reward_config\DiceRewardConfig;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 奖励配置逻辑层
|
* 奖励配置逻辑层
|
||||||
|
* weight 仅 tier=BIGWIN 时可设定,保存时非 BIGWIN 强制 weight=0
|
||||||
*/
|
*/
|
||||||
class DiceRewardConfigLogic extends BaseLogic
|
class DiceRewardConfigLogic extends BaseLogic
|
||||||
{
|
{
|
||||||
@@ -24,4 +25,36 @@ class DiceRewardConfigLogic extends BaseLogic
|
|||||||
$this->model = new DiceRewardConfig();
|
$this->model = new DiceRewardConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增前:非 BIGWIN 时强制 weight=0
|
||||||
|
*/
|
||||||
|
public function add(array $data): mixed
|
||||||
|
{
|
||||||
|
$data = $this->normalizeWeightByTier($data);
|
||||||
|
return parent::add($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改前:非 BIGWIN 时强制 weight=0
|
||||||
|
*/
|
||||||
|
public function edit($id, array $data): mixed
|
||||||
|
{
|
||||||
|
$data = $this->normalizeWeightByTier($data);
|
||||||
|
return parent::edit($id, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅 tier=BIGWIN 时保留 weight(且限制 0-100),否则强制为 0
|
||||||
|
*/
|
||||||
|
private function normalizeWeightByTier(array $data): array
|
||||||
|
{
|
||||||
|
$tier = isset($data['tier']) ? (string) $data['tier'] : '';
|
||||||
|
if ($tier !== 'BIGWIN') {
|
||||||
|
$data['weight'] = 0;
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
$w = isset($data['weight']) ? (float) $data['weight'] : 0;
|
||||||
|
$data['weight'] = max(0, min(100, $w));
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ use think\model\relation\BelongsTo;
|
|||||||
* @property $start_index 起始索引
|
* @property $start_index 起始索引
|
||||||
* @property $target_index 结束索引
|
* @property $target_index 结束索引
|
||||||
* @property $roll_array 摇取点数,格式:[1,2,3,4,5](5个点数)
|
* @property $roll_array 摇取点数,格式:[1,2,3,4,5](5个点数)
|
||||||
|
* @property $roll_number 摇取点数和(5个色子点数之和,5-30)
|
||||||
* @property $lottery_name 奖池名
|
* @property $lottery_name 奖池名
|
||||||
* @property $status 状态:0=超时/失败 1=成功
|
* @property $status 状态:0=超时/失败 1=成功
|
||||||
* @property $create_time 创建时间
|
* @property $create_time 创建时间
|
||||||
@@ -222,4 +223,20 @@ class DicePlayRecord extends BaseModel
|
|||||||
$query->where('direction', '=', $value);
|
$query->where('direction', '=', $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 摇取点数和下限 */
|
||||||
|
public function searchRollNumberMinAttr($query, $value)
|
||||||
|
{
|
||||||
|
if ($value !== '' && $value !== null) {
|
||||||
|
$query->where('roll_number', '>=', $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 摇取点数和上限 */
|
||||||
|
public function searchRollNumberMaxAttr($query, $value)
|
||||||
|
{
|
||||||
|
if ($value !== '' && $value !== null) {
|
||||||
|
$query->where('roll_number', '<=', $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use support\think\Cache;
|
|||||||
* @property $ui_text 前端显示文本
|
* @property $ui_text 前端显示文本
|
||||||
* @property $real_ev 真实资金结算
|
* @property $real_ev 真实资金结算
|
||||||
* @property $tier 所属档位
|
* @property $tier 所属档位
|
||||||
|
* @property $weight 权重%(仅 tier=BIGWIN 时可设定,0-100)
|
||||||
* @property $s_end_index 顺时针结束索引
|
* @property $s_end_index 顺时针结束索引
|
||||||
* @property $n_end_index 逆时针结束索引
|
* @property $n_end_index 逆时针结束索引
|
||||||
* @property $remark 备注
|
* @property $remark 备注
|
||||||
@@ -80,7 +81,8 @@ class DiceRewardConfig extends BaseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新从数据库加载并写入缓存(保存时调用),构建列表与索引
|
* 重新从数据库加载并写入缓存(DiceRewardConfig 新增/修改/删除后调用),构建列表与索引
|
||||||
|
* 实例化结果含完整行(含 weight),供 playStart 从缓存中查找 BIGWIN 的 weight 按概率抽奖
|
||||||
*/
|
*/
|
||||||
public static function refreshCache(): void
|
public static function refreshCache(): void
|
||||||
{
|
{
|
||||||
@@ -148,10 +150,11 @@ class DiceRewardConfig extends BaseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从缓存按档位 + 色子点数取一条奖励配置(用于超级大奖 tier=BIGWIN + grid_number=roll_number)
|
* 从缓存实例按档位 + 色子点数取一条奖励配置(用于超级大奖 tier=BIGWIN + grid_number=roll_number)
|
||||||
|
* 返回行含 weight(0-100):playStart 据此概率抽奖,weight=100 表示摇到该 roll_number 时 100% 中超级大奖
|
||||||
* @param string $tier 档位,如 BIGWIN
|
* @param string $tier 档位,如 BIGWIN
|
||||||
* @param int $gridNumber 色子点数(如摇出总和)
|
* @param int $gridNumber 色子点数(摇出总和 roll_number)
|
||||||
* @return array|null 配置行或 null
|
* @return array|null 配置行(含 weight、real_ev 等)或 null
|
||||||
*/
|
*/
|
||||||
public static function getCachedByTierAndGridNumber(string $tier, int $gridNumber): ?array
|
public static function getCachedByTierAndGridNumber(string $tier, int $gridNumber): ?array
|
||||||
{
|
{
|
||||||
@@ -277,4 +280,20 @@ class DiceRewardConfig extends BaseModel
|
|||||||
$query->where('tier', '=', $value);
|
$query->where('tier', '=', $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 权重下限(仅 tier=BIGWIN 时有意义) */
|
||||||
|
public function searchWeightMinAttr($query, $value)
|
||||||
|
{
|
||||||
|
if ($value !== '' && $value !== null) {
|
||||||
|
$query->where('weight', '>=', $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 权重上限 */
|
||||||
|
public function searchWeightMaxAttr($query, $value)
|
||||||
|
{
|
||||||
|
if ($value !== '' && $value !== null) {
|
||||||
|
$query->where('weight', '<=', $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use plugin\saiadmin\basic\BaseValidate;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 奖励配置验证器
|
* 奖励配置验证器
|
||||||
|
* weight 仅当 tier=BIGWIN 时可设定,且严格限制 0-100(%)
|
||||||
*/
|
*/
|
||||||
class DiceRewardConfigValidate extends BaseValidate
|
class DiceRewardConfigValidate extends BaseValidate
|
||||||
{
|
{
|
||||||
@@ -21,6 +22,7 @@ class DiceRewardConfigValidate extends BaseValidate
|
|||||||
'ui_text' => 'require',
|
'ui_text' => 'require',
|
||||||
'real_ev' => 'require',
|
'real_ev' => 'require',
|
||||||
'tier' => 'require',
|
'tier' => 'require',
|
||||||
|
'weight' => 'checkWeight',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,24 +33,33 @@ class DiceRewardConfigValidate extends BaseValidate
|
|||||||
'ui_text' => '前端显示文本必须填写',
|
'ui_text' => '前端显示文本必须填写',
|
||||||
'real_ev' => '真实资金结算必须填写',
|
'real_ev' => '真实资金结算必须填写',
|
||||||
'tier' => '所属档位必须填写',
|
'tier' => '所属档位必须填写',
|
||||||
|
'weight' => '权重仅 tier=BIGWIN 时可设定,且必须为 0-100',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义场景
|
* 定义场景
|
||||||
*/
|
*/
|
||||||
protected $scene = [
|
protected $scene = [
|
||||||
'save' => [
|
'save' => ['grid_number', 'ui_text', 'real_ev', 'tier', 'weight'],
|
||||||
'grid_number',
|
'update' => ['grid_number', 'ui_text', 'real_ev', 'tier', 'weight'],
|
||||||
'ui_text',
|
|
||||||
'real_ev',
|
|
||||||
'tier',
|
|
||||||
],
|
|
||||||
'update' => [
|
|
||||||
'grid_number',
|
|
||||||
'ui_text',
|
|
||||||
'real_ev',
|
|
||||||
'tier',
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* weight:仅 tier=BIGWIN 时可设定,严格限制 0-100(%)
|
||||||
|
*/
|
||||||
|
protected function checkWeight($value, $rule = '', $data = []): bool
|
||||||
|
{
|
||||||
|
$tier = isset($data['tier']) ? (string) $data['tier'] : '';
|
||||||
|
if ($tier !== 'BIGWIN') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$num = is_numeric($value) ? (float) $value : null;
|
||||||
|
if ($num === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($num < 0 || $num > 100) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user