"""后台管理:球队别名管理(只读列表 + 添加别名)。 归一名(teams.name)是球队唯一键;别名(team_aliases)是同一球队的不同写法 (大小写/译名/缩写)到归一后 teams.id 的映射。入库时 normalize(name) 依次查 teams.name 与 team_aliases,命中即复用,避免重复 Team。 不自动合并历史重复队;需显式添加别名(或先 SQL/再经由此接口)。 """ from __future__ import annotations import logging from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import desc, select from src.api.deps import require_admin from src.api.schemas import TeamAliasIn, TeamAliasOut from src.db.base import AsyncSession, get_db_read from src.db.models import Team, TeamAlias from src.db.repositories import TeamRepository logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/v1/admin", tags=["admin"], dependencies=[Depends(require_admin)]) @router.get("/teams/aliases", response_model=list[TeamAliasOut]) async def list_team_aliases(db: AsyncSession = Depends(get_db_read)): """列出所有球队别名(最新在前)。""" rows = (await db.execute(select(TeamAlias).order_by(desc(TeamAlias.created_at)).limit(200))).scalars().all() return [ TeamAliasOut( alias_normalized=r.alias_normalized, team_id=r.team_id, original_alias=r.original_alias, ) for r in rows ] @router.post("/teams/aliases", response_model=TeamAliasOut, status_code=201) async def add_team_alias(req: TeamAliasIn, db: AsyncSession = Depends(get_db_read)): """为已有 Team 添加别名(幂等:重复添加会更新指向)。 不自动合并历史重复队。若需合并 A→B:先为 A 的归一名添加别名指向 B, 再人工确认 A 是否仍有独立引用。 """ # 校验目标 Team 存在 team = await db.get(Team, req.team_id) if team is None: raise HTTPException(404, f"目标 Team 不存在: id={req.team_id}") repo = TeamRepository(db) row = await repo.add_alias(req.alias, req.team_id) logger.info("添加 Team 别名: %s -> team_id=%s", req.alias, req.team_id) return TeamAliasOut( alias_normalized=row.alias_normalized, team_id=row.team_id, original_alias=row.original_alias, )