0, 'Not Started' => 0, 'First Half' => 1, 'First Half, Kick Off' => 1, 'Halftime' => 1, 'Second Half' => 1, 'Second Half, 2nd Half Started' => 1, 'Extra Time' => 1, 'Break Time' => 1, 'Penalty In Progress' => 1, 'Match Suspended' => 1, 'Match Interrupted' => 1, 'Match Finished' => 2, 'Match Finished' => 2, 'Match Finished' => 2, 'Match Postponed' => 3, 'Match Cancelled' => 4, 'Match Abandoned' => 4, 'Technical Loss' => 4, 'WalkOver' => 4, 'In Progress' => 1, ]; protected $fixture_status = [ 'First Half' => 1, 'First Half, Kick Off' => 1, 'Halftime' => 1, 'Second Half' => 1, 'Second Half, 2nd Half Started' => 1, 'Extra Time' => 1, 'Break Time' => 1, 'Penalty In Progress' => 1, ]; /** * 命令描述 * * @var string */ protected $description = '赔率(直播赔率5秒更新一次,塞前赔率3小时更新一次)'; /** * 执行命令 * * @return int */ public function handle() { $this->is_live = $this->argument('is_live'); $this->sportOddsData($this->is_live); } public function sportOddsData($is_live) { if ($is_live == 1) { // 直播赔率通常是一次获取全量,但也需要对整体更新逻辑做保护 $data = SportClientService::oddsLive([]); if ($data) { // file_put_contents("oddsLive.json",json_encode($data)); $this->updateOddsLive($data); } } else { $limit = 3000; //普通球赛是开赛前购买,更新数据 $where['state'] = 0; //比赛状态:0未开始1进行中2已完场3延期4取消 $list = SportModel::where($where)->where('odds',null)->limit($limit)->get()->toArray(); foreach ($list as $item) { // --- 关键优化:在循环内部嵌套 try-catch --- try { $data = SportClientService::odds([ 'fixture' => $item['data_id'], ]); $this->updateOdds($item['data_id'], $data); } catch (Throwable $e) { // 记录具体哪条赛事出错了,但不抛出,让循环继续 Log::error($item['data_id'].",赛前赔率更新失败: " . $e->getMessage()); continue; } } } return true; } public function updateOddsLive($data) { if (empty($data['response'])) return; //体育赛事结束前几(分钟)锁盘,90分钟结束 $sport_locked = Config::where('field', 'sport_locked')->first()->val ?? 1; $responses = $data['response']; foreach ($responses as $item) { // --- 关键优化:直播赔率逐条更新也要包裹,防止单条数据格式问题挂掉全盘 --- try { $data_id = $item['fixture']['id']; $sport_info = SportModel::where('data_id',$data_id)->select('odds','odd_ids_locked')->first(); if (!$sport_info) continue; $old_odd_ids_locked = $sport_info['odd_ids_locked']; $odds_data = $this->doOdds($item['odds'], $sport_info['odds']); $odds = $odds_data['odds']; $odds = !empty($odds) ? json_encode($odds) : null; $odd_ids_locked = array_merge($old_odd_ids_locked, $odds_data['odd_ids_locked']); $update_data = [ 'is_send' => 0, 'is_roll' => 1, 'is_locked' => 0, 'odd_ids_locked' => array_unique($odd_ids_locked), 'odds' => $odds, 'error' => 0, //异常类型:0正常;1直播赔率的赛事时异常 ]; $odd_fixture_status = null;//直播赔率中的赛事时间和状态(与直播赛事信息中的赛事时间和状态存在较大差异) if (isset($item['fixture']['status']['long']) ) { // $long = $item['fixture']['status']['long']; // if (isset($this->fixture_status[$long])) { $sport_info = SportModel::where('data_id',$data_id)->first(); $fixture_status = $sport_info['fixture_status'] ? json_decode($sport_info['fixture_status'],true) : []; $odd_fixture_status = $item['fixture']['status']; $update_data['odd_fixture_status'] = json_encode($odd_fixture_status); //如果时间差距超过3分钟,则锁盘 if (isset($fixture_status['elapsed']) && isset($odd_fixture_status['elapsed'])) { if ( $fixture_status['elapsed'] - $odd_fixture_status['elapsed'] >= 3) { $update_data['error'] = 1; //异常 $update_data['is_locked'] = 1; //锁盘 unset($update_data['odds']);//不更新赔率 } else { $update_data['error'] = 0; } } // } } //锁盘 if (isset($item['fixture']['status']['blocked']) && $item['fixture']['status']['blocked']) { $update_data['is_locked'] = 1; } //提前锁盘(比赛进行时长,分钟) if (isset($item['fixture']['status']['elapsed'])) { $elapsed = $item['fixture']['status']['elapsed']; if ((int)$elapsed >= 90 - $sport_locked ) { $update_data['is_locked'] = 1; } } //已结束 if (isset($item['fixture']['status']['finished']) && $item['fixture']['status']['finished']) { $update_data['state'] = 2; } //如果赛事取消、延期等,标记需要退款 if (isset($update_data['state']) && $update_data['state'] > 2) { $update_data['refund_status'] = 1; } SportModel::where('data_id', $data_id)->update($update_data); } catch (Throwable $e) { Log::error("直播赔率单条更新失败 [ID: {$data_id}]: " . $e->getMessage()); continue; } } } public function updateOdds($data_id, $data) { if (!empty($data['response'][0]['bookmakers'][0]['bets'])) { $odds = $data['response'][0]['bookmakers'][0]['bets']; SportModel::where('data_id',$data_id)->update(['odds' => json_encode($odds), 'is_send' => 0]); } } //去掉无效的赔率 private function doOdds($odds, $old_odds) { // 1. 获取赔率,缓存数据 $sport_odds = cache('sport_odds'); if (!$sport_odds) { $sport_odds = SportOddsModel::where('function_name', '<>', null)->get()->toArray(); Cache::set('sport_odds', $sport_odds, 300); //有效期5分钟 } $old_odds = $old_odds ? json_decode($old_odds, true) : []; $odds_ids = []; //新的玩法id集合 $sport_odds = array_column($sport_odds, null,'odd_name_en'); $new_odds = []; foreach($odds as $item) { if (!isset($sport_odds[$item['name']])) { continue; } $new_odds[] = $item; $odds_ids[] = $item['id']; } $odd_ids_locked = []; foreach($old_odds as $item) { if (!in_array($item['id'], $odds_ids)) { $new_odds[] = $item; //历史玩法如果没有了,数据保存,加锁 $odd_ids_locked[] = $item['id']; } } return [ 'odds' => $new_odds, 'odd_ids_locked' => $odd_ids_locked, ]; } }