67 lines
2.2 KiB
Python
67 lines
2.2 KiB
Python
"""공개 읽기 API — 경기 목록 / 경기 상세 (AI예측 + crowd 포함)."""
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.orm import selectinload
|
|
|
|
from ..database import get_db
|
|
from ..domain import match_out
|
|
from ..models import Match
|
|
from ..schemas import MatchOut
|
|
from ..services.baseball_details import fetch_live, get_extras
|
|
|
|
router = APIRouter(prefix="/api/matches", tags=["matches"])
|
|
|
|
|
|
@router.get("", response_model=list[MatchOut])
|
|
async def list_matches(
|
|
lang: str = Query("ko"),
|
|
league: str = Query("", description="wc | kbo | mlb — 빈값이면 전체"),
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> list[MatchOut]:
|
|
q = (
|
|
select(Match)
|
|
.options(selectinload(Match.predictions), selectinload(Match.crowd))
|
|
.order_by(Match.kickoff_at)
|
|
)
|
|
if league:
|
|
q = q.where(Match.league == league)
|
|
rows = (await db.execute(q)).scalars().all()
|
|
extras = await get_extras(db, rows)
|
|
return [match_out(m, lang, extras=extras.get(m.match_id)) for m in rows]
|
|
|
|
|
|
@router.get("/{match_id}", response_model=MatchOut)
|
|
async def get_match(
|
|
match_id: str,
|
|
lang: str = Query("ko"),
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> MatchOut:
|
|
m = (
|
|
await db.execute(
|
|
select(Match)
|
|
.where(Match.match_id == match_id)
|
|
.options(selectinload(Match.predictions), selectinload(Match.crowd))
|
|
)
|
|
).scalars().first()
|
|
if not m:
|
|
raise HTTPException(status_code=404, detail="MATCH_NOT_FOUND")
|
|
extras = await get_extras(db, [m])
|
|
return match_out(m, lang, extras=extras.get(m.match_id))
|
|
|
|
|
|
@router.get("/{match_id}/live")
|
|
async def get_live(
|
|
match_id: str,
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> dict:
|
|
"""야구 라이브 필드 뷰 (kbo=네이버 relay, mlb=공식 feed/live). 15초 TTL 캐시."""
|
|
m = await db.get(Match, match_id)
|
|
if not m:
|
|
raise HTTPException(status_code=404, detail="MATCH_NOT_FOUND")
|
|
if m.league not in ("kbo", "mlb"):
|
|
return {"available": False}
|
|
return await fetch_live(m)
|