- 移除聊天室右下角浮动游戏图标(占卜、百家乐、赛马、老虎机) - 用户名片按钮区:修复已婚/已好友时按钮换行问题,统一单行显示 - 婚礼红包弹窗:重设计为喜庆鲜红背景,领取按钮改为圆形米黄样式 - 新增婚礼红包恢复接口(/wedding/pending-envelopes),刷新后自动恢复领取按钮 - 修复 Alpine :style 字符串覆盖静态 style 导致圆形按钮失效的问题 - 撤职后用户等级改为根据经验值重新计算,不再无条件重置为1 - 管理员修改用户经验值后自动重算等级,有职务用户等级锁定 - 娱乐大厅钓鱼游戏按钮直接调用 startFishing() 简化操作流程 - 新增赛马、占卜、百家乐游戏及相关后端逻辑
150 lines
4.1 KiB
PHP
150 lines
4.1 KiB
PHP
<?php
|
|
|
|
/**
|
|
* 文件功能:赛马竞猜局次模型
|
|
*
|
|
* 代表一场赛马比赛,包含参赛马匹信息、场次状态、
|
|
* 注池统计以及赛果信息。提供当前场次查询和赔率计算方法。
|
|
*
|
|
* @author ChatRoom Laravel
|
|
*
|
|
* @version 1.0.0
|
|
*/
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
|
|
class HorseRace extends Model
|
|
{
|
|
protected $fillable = [
|
|
'status',
|
|
'bet_opens_at',
|
|
'bet_closes_at',
|
|
'race_starts_at',
|
|
'race_ends_at',
|
|
'horses',
|
|
'winner_horse_id',
|
|
'total_bets',
|
|
'total_pool',
|
|
'settled_at',
|
|
];
|
|
|
|
/**
|
|
* 属性类型转换。
|
|
*/
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'bet_opens_at' => 'datetime',
|
|
'bet_closes_at' => 'datetime',
|
|
'race_starts_at' => 'datetime',
|
|
'race_ends_at' => 'datetime',
|
|
'settled_at' => 'datetime',
|
|
'horses' => 'array',
|
|
'winner_horse_id' => 'integer',
|
|
'total_bets' => 'integer',
|
|
'total_pool' => 'integer',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 本场所有下注记录。
|
|
*/
|
|
public function bets(): HasMany
|
|
{
|
|
return $this->hasMany(HorseBet::class, 'race_id');
|
|
}
|
|
|
|
/**
|
|
* 判断当前是否在押注时间窗口内。
|
|
*/
|
|
public function isBettingOpen(): bool
|
|
{
|
|
return $this->status === 'betting'
|
|
&& now()->between($this->bet_opens_at, $this->bet_closes_at);
|
|
}
|
|
|
|
/**
|
|
* 查询当前正在进行的场次(状态为 betting 且押注未截止)。
|
|
*/
|
|
public static function currentRace(): ?static
|
|
{
|
|
return static::query()
|
|
->whereIn('status', ['betting', 'running'])
|
|
->latest()
|
|
->first();
|
|
}
|
|
|
|
/**
|
|
* 生成参赛马匹列表(根据马匹数量随机选名)。
|
|
*
|
|
* @param int $count 马匹数量
|
|
* @return array<int, array{id: int, name: string, emoji: string}>
|
|
*/
|
|
public static function generateHorses(int $count): array
|
|
{
|
|
// 可用马匹名池(原版竞技风格)
|
|
$namePool = [
|
|
['name' => '赤兔', 'emoji' => '🐎'],
|
|
['name' => '乌骓', 'emoji' => '🐴'],
|
|
['name' => '的卢', 'emoji' => '🎠'],
|
|
['name' => '绝影', 'emoji' => '🦄'],
|
|
['name' => '紫骍', 'emoji' => '🐎'],
|
|
['name' => '爪黄', 'emoji' => '🐴'],
|
|
['name' => '汗血', 'emoji' => '🎠'],
|
|
['name' => '飞电', 'emoji' => '⚡'],
|
|
];
|
|
|
|
// 随机打乱并取前 N 个
|
|
shuffle($namePool);
|
|
$selected = array_slice($namePool, 0, $count);
|
|
|
|
$horses = [];
|
|
foreach ($selected as $i => $horse) {
|
|
$horses[] = [
|
|
'id' => $i + 1,
|
|
'name' => $horse['name'],
|
|
'emoji' => $horse['emoji'],
|
|
];
|
|
}
|
|
|
|
return $horses;
|
|
}
|
|
|
|
/**
|
|
* 根据注池计算各马匹实时赔率(彩池制,扣除庄家抽水后按比例分配)。
|
|
*
|
|
* @param int $horseBetAmounts 各马匹的注额数组 [horse_id => amount]
|
|
* @param int $housePercent 庄家抽水百分比
|
|
* @return array<int, float> horse_id => 赔率(含本金)
|
|
*/
|
|
public static function calcOdds(array $horseBetAmounts, int $housePercent): array
|
|
{
|
|
$totalPool = array_sum($horseBetAmounts);
|
|
|
|
if ($totalPool <= 0) {
|
|
// 尚无下注,返回等额赔率
|
|
$count = count($horseBetAmounts);
|
|
|
|
return array_map(fn () => 1.0, $horseBetAmounts);
|
|
}
|
|
|
|
$netPool = $totalPool * (1 - $housePercent / 100);
|
|
$odds = [];
|
|
|
|
foreach ($horseBetAmounts as $horseId => $amount) {
|
|
if ($amount <= 0) {
|
|
// 无人押注的马,赔率设为理论最大值
|
|
$odds[$horseId] = round($netPool, 2);
|
|
} else {
|
|
// 赔率 = 净注池 / 该马注额(含本金返还)
|
|
$odds[$horseId] = round($netPool / $amount, 2);
|
|
}
|
|
}
|
|
|
|
return $odds;
|
|
}
|
|
}
|