优化杀分逻辑

This commit is contained in:
2026-03-17 15:36:14 +08:00
parent 1892c7bcb7
commit 150d31eac5
9 changed files with 38 additions and 37 deletions

View File

@@ -18,7 +18,6 @@
"t5Weight": "T5 Pool Weight (%)",
"weightsSumHint": "Total pool weights: ",
"weightsSumUnit": "% / 100% (must equal 100%)",
"weightsSumUnitCurrent": "% / 100%",
"currentPoolTitle": "Current Lottery Pool",
"loading": "Loading...",
"poolName": "Pool Name",

View File

@@ -18,7 +18,6 @@
"t5Weight": "T5池权重(%)",
"weightsSumHint": "五个池权重总和:",
"weightsSumUnit": "% / 100%必须为100%",
"weightsSumUnitCurrent": "% / 100%须为100%",
"currentPoolTitle": "当前彩金池",
"loading": "加载中...",
"poolName": "池子名称",

View File

@@ -104,6 +104,7 @@ export default {
id: number
name: string
safety_line: number
kill_enabled: number
t1_weight: number
t2_weight: number
t3_weight: number
@@ -118,14 +119,7 @@ export default {
/**
* 更新当前彩金池:仅 safety_line、t1_weightt5_weight不可改 profit_amount
*/
updateCurrentPool(params: {
safety_line?: number
t1_weight?: number
t2_weight?: number
t3_weight?: number
t4_weight?: number
t5_weight?: number
}) {
updateCurrentPool(params: { safety_line?: number; kill_enabled?: number }) {
return request.post<any>({
url: '/core/dice/lottery_pool_config/DiceLotteryPoolConfig/updateCurrentPool',
data: params

View File

@@ -42,6 +42,9 @@
style="width: 100%"
/>
</el-form-item>
<el-form-item label="开启杀分">
<el-switch v-model="formData.kill_enabled" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item :label="$t('page.form.killScoreWeights')">
<div class="text-gray-500 text-sm">
T1: {{ pool.t1_weight }}% / T2: {{ pool.t2_weight }}% / T3: {{ pool.t3_weight }}% / T4: {{ pool.t4_weight }}% / T5: {{ pool.t5_weight }}%
@@ -49,10 +52,7 @@
</el-form-item>
<el-form-item>
<div class="text-gray-500 text-sm">
{{ $t('page.form.weightsSumHint') }}<span :class="weightsSum !== 100 ? 'text-red-500' : ''">{{
weightsSum
}}</span
>{{ $t('page.form.weightsSumUnitCurrent') }} {{ $t('page.form.killWeightNote') }}
{{ $t('page.form.killWeightNote') }}
</div>
</el-form-item>
</el-form>
@@ -81,6 +81,7 @@
id: number
name: string
safety_line: number
kill_enabled: number
t1_weight: number
t2_weight: number
t3_weight: number
@@ -104,7 +105,8 @@
const formRef = ref<FormInstance>()
const formData = reactive({
safety_line: 0
safety_line: 0,
kill_enabled: 1
})
const rules = computed<FormRules>(() => ({
@@ -143,6 +145,7 @@
if (data && typeof data === 'object') {
pool.value = data
formData.safety_line = data.safety_line ?? 0
formData.kill_enabled = (data.kill_enabled ?? 1) === 1 ? 1 : 0
}
} catch (e: any) {
ElMessage.error(e?.message ?? t('page.form.msgGetPoolFailed'))
@@ -180,7 +183,8 @@
await formRef.value?.validate?.()
saving.value = true
await api.updateCurrentPool({
safety_line: formData.safety_line
safety_line: formData.safety_line,
kill_enabled: formData.kill_enabled
})
ElMessage.success(t('page.form.msgSaveSuccess'))
await loadPool()

View File

@@ -75,10 +75,6 @@ class PlayStartLogic
if (!$configType0) {
throw new ApiException('奖池配置不存在(需 type=0');
}
// 杀分时使用 type=1 配置的权重;未杀分时付费用 type=0、免费用 type=1无 type=1 时回退 type=0
$configForWeights = $ticketType === self::LOTTERY_TYPE_PAID
? $configType0
: ($configType1 ?? $configType0);
// 玩家累计盈利:仅统计 lottery_config_id=type=0 的成功对局(中奖金额-100*局数)
$playerQuery = DicePlayRecord::where('player_id', $playerId)
@@ -88,13 +84,11 @@ class PlayStartLogic
$playerPlayCount = (int) $playerQuery->count();
$playerProfitTotal = $playerWinSum - 100.0 * $playerPlayCount;
$safetyLine = (int) ($configType0->safety_line ?? 0);
// 玩家累计盈利>=安全线时杀分:用 type=1 的 T*_weight并记录 lottery_config_id=type=1 的 id否则用玩家权重记录对应配置 id
$usePoolWeights = $playerProfitTotal >= $safetyLine && $configType1 !== null;
if ($usePoolWeights) {
$config = $configType1;
} else {
$config = $configForWeights;
}
$killEnabled = ((int) ($configType0->kill_enabled ?? 1)) === 1;
// 玩家累计盈利>=安全线时杀分:无论付费/免费,都用 type=1 的 T*_weight未达到时一律按玩家权重
// 记录 lottery_config_id杀分记 type=1未杀分统一记当前池 type=0
$usePoolWeights = $killEnabled && $playerProfitTotal >= $safetyLine && $configType1 !== null;
$config = $usePoolWeights ? $configType1 : $configType0;
// 按档位 T1-T5 抽取后,从 DiceReward 表按当前方向取该档位数据,再按 weight 抽取一条得到 grid_number
$rewardInstance = DiceReward::getCachedInstance();

View File

@@ -34,7 +34,7 @@ class DiceLotteryPoolConfigLogic extends BaseLogic
* 获取当前彩金池type=0+ 杀分权重为 type=1 的只读展示
* profit_amount 每次从 DB 实时读取t1_weightt5_weight 来自 type=1杀分权重不可在弹窗内修改
*
* @return array{id:int,name:string,safety_line:int,t1_weight:int,...,t5_weight:int,profit_amount:float}
* @return array{id:int,name:string,safety_line:int,kill_enabled:int,t1_weight:int,...,t5_weight:int,profit_amount:float}
*/
public function getCurrentPool(): array
{
@@ -49,6 +49,7 @@ class DiceLotteryPoolConfigLogic extends BaseLogic
'id' => (int) $row0['id'],
'name' => (string) ($row0['name'] ?? ''),
'safety_line' => (int) ($row0['safety_line'] ?? 0),
'kill_enabled' => (int) ($row0['kill_enabled'] ?? 1),
'profit_amount' => $profitAmount,
];
$row1 = $configType1 ? $configType1->toArray() : [];
@@ -61,19 +62,28 @@ class DiceLotteryPoolConfigLogic extends BaseLogic
}
/**
* 更新当前彩金池:仅允许修改 type=0 的 safety_line杀分权重来自 type=1不可在此接口修改
* 更新当前彩金池:仅允许修改 type=0 的 safety_line、kill_enabled(杀分权重来自 type=1不可在此接口修改
*
* @param array{safety_line?:int} $data
* @param array{safety_line?:int,kill_enabled?:int} $data
*/
public function updateCurrentPool(array $data): void
{
$pool = $this->getCurrentPool();
$id = (int) $pool['id'];
if (!array_key_exists('safety_line', $data)) {
if (!array_key_exists('safety_line', $data) && !array_key_exists('kill_enabled', $data)) {
return;
}
$safetyLine = (int) $data['safety_line'];
DiceLotteryPoolConfig::where('id', $id)->update(['safety_line' => $safetyLine]);
$update = [];
if (array_key_exists('safety_line', $data)) {
$update['safety_line'] = (int) $data['safety_line'];
}
if (array_key_exists('kill_enabled', $data)) {
$update['kill_enabled'] = ((int) $data['kill_enabled']) === 1 ? 1 : 0;
}
if ($update === []) {
return;
}
DiceLotteryPoolConfig::where('id', $id)->update($update);
}
/**

View File

@@ -11,13 +11,14 @@ use plugin\saiadmin\basic\think\BaseModel;
/**
* 色子奖池配置模型
*
* dice_lottery_config 色子奖池配置
* dice_lottery_pool_config 色子奖池配置
*
* @property $id ID
* @property $name 名称
* @property $remark 备注
* @property $type 奖池类型
* @property $safety_line 安全线
* @property $kill_enabled 是否启用杀分0=关闭 1=开启
* @property $create_time 创建时间
* @property $update_time 修改时间
* @property $t1_weight T1池权重
@@ -39,7 +40,7 @@ class DiceLotteryPoolConfig extends BaseModel
* 数据库表名称
* @var string
*/
protected $table = 'dice_lottery_config';
protected $table = 'dice_lottery_pool_config';
/**
* 名称 搜索

View File

@@ -115,7 +115,7 @@ Goal: hot paths should hit Redis most of the time, and DB should primarily be fo
- In `playStart`, ensure player is loaded once and reused (do not call `DicePlayer::find` multiple times per request).
3. EV update strategy:
- Repeated `UPDATE dice_lottery_config SET ev = ev - ?` on a hot row causes lock contention.
- Repeated `UPDATE dice_lottery_pool_config SET ev = ev - ?` on a hot row causes lock contention.
- Better:
- Accumulate EV deltas in Redis (per pool or per shard).
- Periodic cron job to aggregate Redis deltas back into MySQL in batches.

View File

@@ -113,7 +113,7 @@
-`playStart` 中,玩家信息应只查询一次:`$player = DicePlayer::find($playerId)`,后续逻辑统一使用 `$player`,避免重复 `find`
3. **EV 更新策略**
- 频繁在在线请求中执行 `UPDATE dice_lottery_config SET ev = ev - ?`,会造成该行热点锁竞争;
- 频繁在在线请求中执行 `UPDATE dice_lottery_pool_config SET ev = ev - ?`,会造成该行热点锁竞争;
- 建议:
- 在线请求仅将 EV 变动累加到 Redis 计数器;
- 通过定时任务批量同步 Redis 中的统计数据回 MySQL。