"""E - 历史交锋切片: 交手史与胜负规律(h2h)。""" from __future__ import annotations from typing import TYPE_CHECKING from sqlalchemy import select from sqlalchemy.orm import selectinload from src.db.base import AsyncSessionLocal from src.db.models import Match from src.llm.slices.common import MatchHeader, SliceResult if TYPE_CHECKING: from sqlalchemy.ext.asyncio import AsyncSession async def h2h_slice(header: MatchHeader, *, limit: int = 8, before=None, db: AsyncSession | None = None) -> SliceResult: """E - 历史交锋切片: 过去数年 + 近期交手数据,提取交手规律。before=match_date 用于回测。 db: 可选共享 session,避免每个切片独立建连(见 context_builder 模块 docstring)。 """ if db is not None: h2h = await _get_h2h(db, header.home_team_id, header.away_team_id, before=before, limit=limit) else: async with AsyncSessionLocal() as new_db: h2h = await _get_h2h(new_db, header.home_team_id, header.away_team_id, before=before, limit=limit) lines = [f"── 历史交锋(近 {limit} 次) ──"] n_with_score = 0 if h2h: # 从当前主队视角统计:判断当前主队在每场交锋中是主是客 current_home_wins = current_home_draws = current_home_losses = 0 for hm in h2h: d = hm.match_date.strftime("%Y-%m") if hm.match_date else "?" if hm.home_goals is not None: n_with_score += 1 # 判断当前主队当时是主队还是客队 if hm.home_team_id == header.home_team_id: # 当前主队当时是主队 if hm.home_goals > hm.away_goals: current_home_wins += 1 elif hm.home_goals == hm.away_goals: current_home_draws += 1 else: current_home_losses += 1 else: # 当前主队当时是客队(从客队视角看赛果) if hm.away_goals > hm.home_goals: current_home_wins += 1 elif hm.away_goals == hm.home_goals: current_home_draws += 1 else: current_home_losses += 1 lines.append(f" {d}: {hm.home_team.name} {hm.home_goals}-{hm.away_goals} {hm.away_team.name}") else: lines.append(f" {d}: {hm.home_team.name} vs {hm.away_team.name} (无比分)") total = current_home_wins + current_home_draws + current_home_losses if total: lines.append( f" 总计 {total} 场(从当前主队 {header.home_name} 视角): " f"{current_home_wins}胜 {current_home_draws}平 {current_home_losses}负" ) else: lines.append(" 无数据") # has_data 以「有比分的交锋」为准:仅有对阵无比分时不足以支撑分析 return SliceResult(text="\n".join(lines), has_data=n_with_score > 0, n_records=n_with_score) async def _get_h2h(db, home_id: int, away_id: int, before, *, limit: int = 5) -> list[Match]: """两队交锋史。需预加载 home_team / away_team(切片输出队名)。""" stmt = ( select(Match) .options( selectinload(Match.home_team), selectinload(Match.away_team), ) .where(Match.match_status == "finished") .where(Match.home_goals.is_not(None)) .where( ((Match.home_team_id == home_id) & (Match.away_team_id == away_id)) | ((Match.home_team_id == away_id) & (Match.away_team_id == home_id)) ) .order_by(Match.match_date.desc()) .limit(limit) ) if before is not None: stmt = stmt.where(Match.match_date < before) result = await db.execute(stmt) return list(result.scalars().all())