From 0a6af0657a95909ec6057e141dd1ad4b84742632 Mon Sep 17 00:00:00 2001 From: shangfangjian Date: Mon, 21 Sep 2026 01:54:30 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=84=E6=B5=8B=E5=8E=86=E5=8F=B2:=E6=AF=94?= =?UTF-8?q?=E8=B5=9B=E4=BF=A1=E6=81=AF=E5=86=85=E5=B5=8C=E5=88=B0=E9=A2=84?= =?UTF-8?q?=E6=B5=8BAPI=20+=20=E5=AE=9E=E9=99=85=E6=AF=94=E5=88=86?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=A1=AB=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端: - PredictionOut 新增 match 字段(内嵌比赛信息) - list_predictions 返回完整比赛数据(日期/队名/赛果/主客徽标) - 新增 _match_dict() 辅助函数序列化比赛对象 前端: - 移除独立的 fetchMatches 调用,直接使用 p.match - 实际比分自动从比赛赛果填充 - 显示:比赛日(M/D) + 主客队徽标 + 队伍中文名 - 赛后一键结算(使用比赛实际赛果) Co-Authored-By: new-provider/LongCat-2.0 <> ) --- .../src/admin/pages/PredictionHistory.tsx | 20 ++++++------------- src/api/routes/predict.py | 20 +++++++++++++++++++ src/api/schemas.py | 2 ++ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/frontend/src/admin/pages/PredictionHistory.tsx b/frontend/src/admin/pages/PredictionHistory.tsx index fd64a89..b38b9be 100644 --- a/frontend/src/admin/pages/PredictionHistory.tsx +++ b/frontend/src/admin/pages/PredictionHistory.tsx @@ -8,8 +8,8 @@ */ import { useCallback, useEffect, useState } from 'react' -import { fetchPredictions, settlePrediction, fetchMatches } from '../dal' -import type { Prediction, Match } from '../types' +import { fetchPredictions, settlePrediction } from '../dal' +import type { Prediction } from '../types' import { Card, CardBody, CardHeader, Badge, SectionHeader, Alert, Spinner } from '../components' import TeamSideTag from '../../components/TeamSideTag' @@ -23,7 +23,6 @@ function fmtDate(s?: string | null): string { export default function PredictionHistoryPage() { const [predictions, setPredictions] = useState([]) - const [matches, setMatches] = useState>(new Map()) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [filter, setFilter] = useState<'all' | 'settled' | 'unsettled'>('all') @@ -34,15 +33,8 @@ export default function PredictionHistoryPage() { setLoading(true) setError(null) try { - const list = await fetchPredictions(100) + const list = await fetchPredictions(200) setPredictions(list) - const matchIds = [...new Set(list.map(p => p.match_id))] - if (matchIds.length > 0) { - const matchesData = await fetchMatches({ limit: 500 }) - const matchMap = new Map() - matchesData.items.forEach(m => matchMap.set(m.id, m)) - setMatches(matchMap) - } } catch (err) { setError(err instanceof Error ? err.message : '加载失败') } finally { @@ -53,7 +45,7 @@ export default function PredictionHistoryPage() { useEffect(() => { load() }, [load]) const handleSettle = async (p: Prediction) => { - const match = matches.get(p.match_id) + const match = (p as any).match if (!match || match.home_goals == null || match.away_goals == null) return setSettlingId(p.id) try { @@ -162,7 +154,7 @@ export default function PredictionHistoryPage() { {filtered.map(p => { - const match = matches.get(p.match_id) + const match = (p as any).match const matchDate = match?.match_date const homeName = match?.home_team_zh || match?.home_team || '?' const awayName = match?.away_team_zh || match?.away_team || '?' @@ -254,7 +246,7 @@ export default function PredictionHistoryPage() { {expandedId && (() => { const p = predictions.find(pr => pr.id === expandedId) if (!p) return null - const match = matches.get(p.match_id) + const match = (p as any).match const reports = p.agent_outputs ?? [] return ( diff --git a/src/api/routes/predict.py b/src/api/routes/predict.py index 79dd32e..2e19ef8 100644 --- a/src/api/routes/predict.py +++ b/src/api/routes/predict.py @@ -167,11 +167,31 @@ async def list_predictions( actual_home_goals=p.actual_home_goals, actual_away_goals=p.actual_away_goals, settled=p.settled, + match=_match_dict(p.match) if p.match else None, ) for p in rows ] +def _match_dict(m) -> dict | None: + if m is None: + return None + return { + "id": m.id, + "league_code": m.league.code if m.league else None, + "season": m.season, + "home_team": m.home_team.name if m.home_team else "?", + "away_team": m.away_team.name if m.away_team else "?", + "home_team_zh": m.home_team.name_zh if m.home_team else None, + "away_team_zh": m.away_team.name_zh if m.away_team else None, + "match_date": m.match_date.isoformat() if m.match_date else None, + "match_status": m.match_status, + "home_goals": m.home_goals, + "away_goals": m.away_goals, + "match_stage": m.match_stage, + } + + @router.get("/predictions/{prediction_id}", response_model=PredictionOut, dependencies=[Depends(require_admin)]) async def get_prediction(prediction_id: int, db: AsyncSession = Depends(get_db_read)): p = await db.get(Prediction, prediction_id) diff --git a/src/api/schemas.py b/src/api/schemas.py index 9fc8993..06e6693 100644 --- a/src/api/schemas.py +++ b/src/api/schemas.py @@ -102,6 +102,8 @@ class PredictionOut(BaseModel): actual_home_goals: int | None actual_away_goals: int | None settled: bool + # 比赛信息(可选,列表接口不返回以减少 payload) + match: dict | None = None class IngestBzzoiroRequest(BaseModel):