'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 */ 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 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; } }