优化杀分逻辑
This commit is contained in:
@@ -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