no message

This commit is contained in:
hajimi
2026-06-11 12:15:29 +08:00
parent 10ebe39c30
commit 96efa1d905
5859 changed files with 815501 additions and 5 deletions
@@ -0,0 +1,501 @@
<?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,
]);
}
}