优化杀分逻辑
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"t5Weight": "T5池权重(%)",
|
||||
"weightsSumHint": "五个池权重总和:",
|
||||
"weightsSumUnit": "% / 100%(必须为100%)",
|
||||
"weightsSumUnitCurrent": "% / 100%(须为100%)",
|
||||
"currentPoolTitle": "当前彩金池",
|
||||
"loading": "加载中...",
|
||||
"poolName": "池子名称",
|
||||
|
||||
@@ -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_weight~t5_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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -34,7 +34,7 @@ class DiceLotteryPoolConfigLogic extends BaseLogic
|
||||
* 获取当前彩金池(type=0)+ 杀分权重为 type=1 的只读展示
|
||||
* profit_amount 每次从 DB 实时读取;t1_weight~t5_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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
* 名称 搜索
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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。
|
||||
|
||||
Reference in New Issue
Block a user