502 lines
18 KiB
PHP
502 lines
18 KiB
PHP
<?php
|
|
|
|
namespace app\api\controller;
|
|
|
|
use app\api\lists\match\MatchLists;
|
|
use app\common\model\match\MatchEvent;
|
|
use app\common\model\match\MatchHistory;
|
|
use app\common\model\match\MatchEventLog;
|
|
use app\common\model\match\MatchData;
|
|
use app\common\model\match\MatchRecent;
|
|
use app\common\model\match\MatchLiveText;
|
|
use app\common\model\match\MatchLineup;
|
|
use app\common\model\match\WorldCupPersonRanking;
|
|
use app\common\model\match\WorldCupStanding;
|
|
use app\common\model\league\League;
|
|
|
|
class MatchController extends BaseApiController
|
|
{
|
|
public array $notNeedLogin = ['lists', 'detail', 'leagues', 'sportTypes', 'liveText', 'lineup', 'worldCupStandings', 'worldCupRankings', 'worldCupSchedule'];
|
|
|
|
protected int $worldCupSeasonId = 26123;
|
|
protected string $worldCupLeagueName = '世界杯';
|
|
protected string $worldCupDefaultLiveUrl = 'https://sports.cctv.com/?spm=C28340.P9mj1V5B06xk.E2XVQsMhlk44.7';
|
|
|
|
public function sportTypes()
|
|
{
|
|
$types = League::where('is_show', 1)
|
|
->where('type', 'sport')
|
|
->field('sport_type, label, icon')
|
|
->order('sort asc')
|
|
->select()
|
|
->toArray();
|
|
|
|
$result = [];
|
|
foreach ($types as $item) {
|
|
$result[] = [
|
|
'value' => $item['sport_type'],
|
|
'label' => $item['label'],
|
|
'icon' => $item['icon'],
|
|
];
|
|
}
|
|
return $this->data($result);
|
|
}
|
|
|
|
public function leagues()
|
|
{
|
|
$sportType = $this->request->get('sport_type/d', 0);
|
|
$query = League::where('is_show', 1)
|
|
->where('type', 'league');
|
|
if ($sportType > 0) {
|
|
$query->where('sport_type', $sportType);
|
|
}
|
|
$leagues = $query->field('id, label, sport_type, icon, sort')
|
|
->order('sort asc')
|
|
->select()
|
|
->toArray();
|
|
|
|
$leagueNames = array_column($leagues, 'label');
|
|
$counts = [];
|
|
if (!empty($leagueNames)) {
|
|
$rows = MatchEvent::where('is_show', 1)
|
|
->where('status', 1)
|
|
->whereIn('league_name', $leagueNames)
|
|
->field('league_name, COUNT(*) as cnt')
|
|
->group('league_name')
|
|
->select()
|
|
->toArray();
|
|
foreach ($rows as $row) {
|
|
$counts[$row['league_name']] = (int) $row['cnt'];
|
|
}
|
|
}
|
|
|
|
$grouped = [];
|
|
foreach ($leagues as $item) {
|
|
$st = $item['sport_type'];
|
|
if (!isset($grouped[$st])) {
|
|
$grouped[$st] = [];
|
|
}
|
|
$grouped[$st][] = [
|
|
'id' => $item['id'],
|
|
'league_name' => $item['label'],
|
|
'icon' => $item['icon'],
|
|
'count' => $counts[$item['label']] ?? 0,
|
|
];
|
|
}
|
|
return $this->data($grouped);
|
|
}
|
|
|
|
public function lists()
|
|
{
|
|
$sportType = $this->request->get('sport_type/d', 0);
|
|
$leagueName = $this->request->get('league_name/s', '');
|
|
$endedPage = $this->request->get('ended_page/d', 1);
|
|
$endedPageSize = $this->request->get('ended_page_size/d', 20);
|
|
$endedOnly = $this->request->get('ended_only/d', 0);
|
|
|
|
$field = 'id,league_name,league_icon,home_team,home_icon,home_score,away_team,away_icon,away_score,sport_type,status,match_time,current_minute,half_score,home_odds,draw_odds,away_odds,home_corner,away_corner,home_yellow,away_yellow,home_red,away_red,is_hot,round_name';
|
|
|
|
// 基础筛选
|
|
$baseWhere = [['is_show', '=', 1]];
|
|
if ($sportType > 0) {
|
|
$baseWhere[] = ['sport_type', '=', $sportType];
|
|
}
|
|
if (!empty($leagueName)) {
|
|
$baseWhere[] = ['league_name', '=', $leagueName];
|
|
} elseif ($sportType <= 0) {
|
|
$validLeagues = League::where('is_show', 1)->where('type', 'league')->column('label');
|
|
if (!empty($validLeagues)) {
|
|
$baseWhere[] = ['league_name', 'in', $validLeagues];
|
|
}
|
|
}
|
|
|
|
// 已结束:分页,按时间倒序
|
|
$endedOffset = ($endedPage - 1) * $endedPageSize;
|
|
$ended = MatchEvent::where($baseWhere)->where('status', 2)
|
|
->field($field)->order('match_time desc')
|
|
->limit($endedOffset, $endedPageSize)->select()->toArray();
|
|
$endedCount = MatchEvent::where($baseWhere)->where('status', 2)->count();
|
|
|
|
// 加载更多时只返回已结束数据
|
|
if ($endedOnly) {
|
|
return $this->data([
|
|
'ended' => [
|
|
'lists' => $ended,
|
|
'count' => $endedCount,
|
|
'page_no' => $endedPage,
|
|
'page_size' => $endedPageSize,
|
|
],
|
|
]);
|
|
}
|
|
|
|
// 进行中:全部返回
|
|
$live = MatchEvent::where($baseWhere)->where('status', 1)
|
|
->field($field)->order('match_time asc')->select()->toArray();
|
|
|
|
// 未开始:只取前20条,按时间正序
|
|
$upcoming = MatchEvent::where($baseWhere)->where('status', 0)
|
|
->field($field)->order('match_time asc')->limit(20)->select()->toArray();
|
|
|
|
return $this->data([
|
|
'live' => $live,
|
|
'upcoming' => $upcoming,
|
|
'ended' => [
|
|
'lists' => $ended,
|
|
'count' => $endedCount,
|
|
'page_no' => $endedPage,
|
|
'page_size' => $endedPageSize,
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function detail()
|
|
{
|
|
$id = $this->request->get('id/d');
|
|
$match = MatchEvent::where('id', $id)->where('is_show', 1)->findOrEmpty();
|
|
if ($match->isEmpty()) {
|
|
return $this->fail('赛事不存在');
|
|
}
|
|
$data = $match->toArray();
|
|
if (empty($data['live_url']) && $this->isWorldCupMatch($data)) {
|
|
$data['live_url'] = $this->worldCupDefaultLiveUrl;
|
|
}
|
|
$data['living_tv'] = '';
|
|
|
|
$matchDataRaw = MatchData::where('match_id', $data['match_id'])
|
|
->order('id desc')
|
|
->value('raw_data');
|
|
if (!empty($matchDataRaw)) {
|
|
$rawData = json_decode($matchDataRaw, true);
|
|
if (json_last_error() === JSON_ERROR_NONE && is_array($rawData)) {
|
|
$data['living_tv'] = $this->resolveLiveSourceText($rawData);
|
|
}
|
|
}
|
|
|
|
$homeTeam = $data['home_team'];
|
|
$awayTeam = $data['away_team'];
|
|
|
|
// 对赛往绩
|
|
$data['history'] = MatchHistory::where(function ($query) use ($homeTeam, $awayTeam) {
|
|
$query->where([['home_team', '=', $homeTeam], ['away_team', '=', $awayTeam]])
|
|
->whereOr([['home_team', '=', $awayTeam], ['away_team', '=', $homeTeam]]);
|
|
})->order('match_time desc')->limit(5)->select()->toArray();
|
|
|
|
// 精彩瞬间(按业务字段去重)
|
|
$data['events'] = MatchEventLog::where('match_id', $data['match_id'])
|
|
->group('minute,event_type,player_name,team_side')
|
|
->order('minute asc')
|
|
->select()->toArray();
|
|
|
|
// 主队近期战绩
|
|
$data['home_recent'] = MatchRecent::where('team_name', $homeTeam)
|
|
->order('match_time desc')->limit(5)->select()->toArray();
|
|
|
|
// 客队近期战绩
|
|
$data['away_recent'] = MatchRecent::where('team_name', $awayTeam)
|
|
->order('match_time desc')->limit(5)->select()->toArray();
|
|
|
|
// 文字直播(最新50条)
|
|
$data['live_text'] = MatchLiveText::where('match_id', $data['match_id'])
|
|
->field('id, msg_id, event_type, username, avatar, message, image, timestamp, create_time')
|
|
->order('msg_id desc')
|
|
->limit(50)
|
|
->select()->toArray();
|
|
$data['live_text'] = array_reverse($data['live_text']);
|
|
|
|
return $this->data($data);
|
|
}
|
|
|
|
private function resolveLiveSourceText(array $rawData): string
|
|
{
|
|
foreach (['livingTv', 'live_source', 'live_no_source', 'tv_live_info'] as $key) {
|
|
if (!empty($rawData[$key])) {
|
|
$text = $this->normalizeLiveSourceValue($rawData[$key]);
|
|
if ($text !== '') {
|
|
return $text;
|
|
}
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
private function normalizeLiveSourceValue($value): string
|
|
{
|
|
if (is_string($value) || is_numeric($value)) {
|
|
return trim((string) $value);
|
|
}
|
|
|
|
if (!is_array($value)) {
|
|
return '';
|
|
}
|
|
|
|
$items = [];
|
|
foreach ($value as $item) {
|
|
if (is_string($item) || is_numeric($item)) {
|
|
$text = trim((string) $item);
|
|
if ($text !== '') {
|
|
$items[] = $text;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!is_array($item)) {
|
|
continue;
|
|
}
|
|
|
|
foreach (['live_tag', 'name', 'title', 'source_name', 'label'] as $field) {
|
|
if (!empty($item[$field])) {
|
|
$text = trim((string) $item[$field]);
|
|
if ($text !== '') {
|
|
$items[] = $text;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$items = array_values(array_unique(array_filter($items)));
|
|
return implode('、', $items);
|
|
}
|
|
|
|
protected function isWorldCupMatch(array $match): bool
|
|
{
|
|
return ($match['league_name'] ?? '') === $this->worldCupLeagueName
|
|
|| (int)($match['competition_id'] ?? 0) === 61;
|
|
}
|
|
|
|
public function liveText()
|
|
{
|
|
$matchId = $this->request->get('match_id/d');
|
|
if (!$matchId) {
|
|
return $this->fail('参数错误');
|
|
}
|
|
$lastMsgId = $this->request->get('last_msg_id/d', 0);
|
|
|
|
$query = MatchLiveText::where('match_id', $matchId)
|
|
->field('id, msg_id, event_type, username, avatar, message, image, timestamp, create_time')
|
|
->order('msg_id desc');
|
|
|
|
if ($lastMsgId > 0) {
|
|
$query->where('msg_id', '>', $lastMsgId);
|
|
}
|
|
|
|
$list = $query->limit(100)->select()->toArray();
|
|
$list = array_reverse($list);
|
|
|
|
return $this->data([
|
|
'list' => $list,
|
|
'count' => count($list),
|
|
]);
|
|
}
|
|
|
|
public function lineup()
|
|
{
|
|
$matchId = $this->request->get('match_id/d');
|
|
if (!$matchId) {
|
|
return $this->fail('参数错误');
|
|
}
|
|
|
|
$list = MatchLineup::where('match_id', $matchId)
|
|
->field('id, match_id, team_side, is_starter, person_id, person_name, person_logo, shirt_number, captain, position')
|
|
->order('team_side asc, is_starter desc, formation_place asc')
|
|
->select()->toArray();
|
|
|
|
return $this->data([
|
|
'list' => $list,
|
|
]);
|
|
}
|
|
|
|
public function worldCupStandings()
|
|
{
|
|
$rows = WorldCupStanding::where('season_id', $this->worldCupSeasonId)
|
|
->order('stage_name asc')
|
|
->order('group_name asc')
|
|
->order('row_order asc')
|
|
->order('rank asc')
|
|
->select()
|
|
->toArray();
|
|
|
|
$stageName = '';
|
|
$groups = [];
|
|
$header = ['球队', '赛', '胜', '平', '负', '进/失', '积分'];
|
|
|
|
foreach ($rows as $row) {
|
|
if ($stageName === '' && !empty($row['stage_name'])) {
|
|
$stageName = $row['stage_name'];
|
|
}
|
|
$groupName = $row['group_name'] ?: '未分组';
|
|
if (!isset($groups[$groupName])) {
|
|
$groups[$groupName] = [
|
|
'group_name' => $groupName,
|
|
'header' => $header,
|
|
'teams' => [],
|
|
];
|
|
}
|
|
$groups[$groupName]['teams'][] = [
|
|
'team_id' => (int) $row['team_id'],
|
|
'team_name' => $row['team_name'],
|
|
'team_logo' => $row['team_logo'],
|
|
'rank' => (int) $row['rank'],
|
|
'points' => (int) $row['points'],
|
|
'played' => (int) $row['played'],
|
|
'won' => (int) $row['won'],
|
|
'drawn' => (int) $row['drawn'],
|
|
'lost' => (int) $row['lost'],
|
|
'goals_for' => (int) $row['goals_for'],
|
|
'goals_against' => (int) $row['goals_against'],
|
|
'goal_diff' => (int) $row['goal_diff'],
|
|
'row_order' => (int) $row['row_order'],
|
|
];
|
|
}
|
|
|
|
return $this->data([
|
|
'season_id' => $this->worldCupSeasonId,
|
|
'stage_name' => $stageName,
|
|
'groups' => array_values($groups),
|
|
]);
|
|
}
|
|
|
|
public function worldCupRankings()
|
|
{
|
|
$type = $this->request->get('type/s', 'goals');
|
|
if (!in_array($type, ['goals', 'assists'], true)) {
|
|
return $this->fail('排行榜类型错误');
|
|
}
|
|
|
|
$rows = WorldCupPersonRanking::where('season_id', $this->worldCupSeasonId)
|
|
->where('rank_type', $type)
|
|
->order('rank asc')
|
|
->order('count desc')
|
|
->order('id asc')
|
|
->select()
|
|
->toArray();
|
|
|
|
$header = $type === 'goals' ? ['球员', '球队', '进球'] : ['球员', '球队', '助攻'];
|
|
$list = [];
|
|
foreach ($rows as $row) {
|
|
$list[] = [
|
|
'person_id' => (int) $row['person_id'],
|
|
'person_name' => $row['person_name'],
|
|
'person_logo' => $row['person_logo'],
|
|
'team_id' => (int) $row['team_id'],
|
|
'team_name' => $row['team_name'],
|
|
'team_logo' => $row['team_logo'],
|
|
'rank' => (int) $row['rank'],
|
|
'count' => (int) $row['count'],
|
|
];
|
|
}
|
|
|
|
return $this->data([
|
|
'season_id' => $this->worldCupSeasonId,
|
|
'type' => $type,
|
|
'header' => $header,
|
|
'list' => $list,
|
|
]);
|
|
}
|
|
|
|
public function worldCupSchedule()
|
|
{
|
|
$roundOrder = [
|
|
'小组赛 第1轮',
|
|
'小组赛 第2轮',
|
|
'小组赛 第3轮',
|
|
'1/16决赛',
|
|
'1/8决赛',
|
|
'1/4决赛',
|
|
'半决赛',
|
|
'三四名决赛',
|
|
'决赛',
|
|
];
|
|
$bracketRounds = ['1/16决赛', '1/8决赛', '1/4决赛', '半决赛', '三四名决赛', '决赛'];
|
|
$roundWeightMap = [];
|
|
foreach ($roundOrder as $index => $roundName) {
|
|
$roundWeightMap[$roundName] = $index;
|
|
}
|
|
|
|
$rows = MatchEvent::where('league_name', $this->worldCupLeagueName)
|
|
->where('sport_type', 1)
|
|
->where('is_show', 1)
|
|
->field('id,match_id,competition_id,league_name,round_name,stage,home_team,home_icon,home_score,away_team,away_icon,away_score,status,match_time,current_minute,half_score')
|
|
->order('sort asc')
|
|
->order('match_time asc')
|
|
->order('id asc')
|
|
->select()
|
|
->toArray();
|
|
|
|
$scheduleMap = [];
|
|
foreach ($rows as $row) {
|
|
$roundName = $row['round_name'] ?: '未分轮次';
|
|
if (!isset($scheduleMap[$roundName])) {
|
|
$scheduleMap[$roundName] = [
|
|
'round_name' => $roundName,
|
|
'matches' => [],
|
|
'_weight' => $roundWeightMap[$roundName] ?? 999,
|
|
];
|
|
}
|
|
$scheduleMap[$roundName]['matches'][] = [
|
|
'id' => (int) $row['id'],
|
|
'match_id' => (int) $row['match_id'],
|
|
'competition_id' => (int) $row['competition_id'],
|
|
'league_name' => $row['league_name'],
|
|
'round_name' => $roundName,
|
|
'stage' => $row['stage'],
|
|
'home_team' => $row['home_team'],
|
|
'home_icon' => $row['home_icon'],
|
|
'home_score' => (int) $row['home_score'],
|
|
'away_team' => $row['away_team'],
|
|
'away_icon' => $row['away_icon'],
|
|
'away_score' => (int) $row['away_score'],
|
|
'status' => (int) $row['status'],
|
|
'match_time' => (int) $row['match_time'],
|
|
'current_minute' => $row['current_minute'],
|
|
'half_score' => $row['half_score'],
|
|
];
|
|
}
|
|
|
|
$scheduleRounds = array_values($scheduleMap);
|
|
usort($scheduleRounds, function ($left, $right) {
|
|
return $left['_weight'] <=> $right['_weight'];
|
|
});
|
|
|
|
$currentRoundName = '';
|
|
foreach ($scheduleRounds as $round) {
|
|
foreach ($round['matches'] as $match) {
|
|
if ((int) $match['status'] === 1) {
|
|
$currentRoundName = $round['round_name'];
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
if ($currentRoundName === '') {
|
|
foreach ($scheduleRounds as $round) {
|
|
foreach ($round['matches'] as $match) {
|
|
if ((int) $match['status'] === 0) {
|
|
$currentRoundName = $round['round_name'];
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ($currentRoundName === '' && !empty($scheduleRounds)) {
|
|
$currentRoundName = $scheduleRounds[count($scheduleRounds) - 1]['round_name'];
|
|
}
|
|
|
|
$bracketRoundList = [];
|
|
foreach ($scheduleRounds as &$round) {
|
|
unset($round['_weight']);
|
|
if (in_array($round['round_name'], $bracketRounds, true)) {
|
|
$bracketRoundList[] = $round;
|
|
}
|
|
}
|
|
unset($round);
|
|
|
|
return $this->data([
|
|
'season_id' => $this->worldCupSeasonId,
|
|
'current_round_name' => $currentRoundName,
|
|
'bracket_rounds' => $bracketRoundList,
|
|
'schedule_rounds' => $scheduleRounds,
|
|
]);
|
|
}
|
|
}
|