修复积分榜入库 UniqueViolationError

根因:ingest_bzzoiro_standings 批量处理时逐行 SELECT 检查 Standing 是否存在,
但新 INSERT 的 Standing 未 flush 就被同批下一条 SELECT 判定为不存在,
导致同一 (league_id, season, team_id) 插入两次 → UniqueViolation。

修复:
- 预加载该 league+season 的所有已有 standings team_id 到内存 set
- 同批内按 team.id 去重,同一 team 仅处理一次
- 命中已有 standing 时 UPDATE,否则 INSERT 并立即加入 set 防止同批重复

Co-Authored-By: new-provider/LongCat-2.0 <<EMAIL>>
This commit is contained in:
shangfangjian
2026-09-20 20:35:53 +08:00
co-authored by new-provider/LongCat-2.0 <
parent 19f17145cc
commit acac856b61
+22 -6
View File
@@ -417,7 +417,16 @@ async def ingest_bzzoiro_standings(db, *, leagues: Iterable[str], season: str |
for t in (await db.execute(stmt)).scalars(): for t in (await db.execute(stmt)).scalars():
team_map[t.name] = t team_map[t.name] = t
# 预加载该 league+season 已有的 standings(避免同批内重复 INSERT 导致 UniqueViolation)
existing_standings: set[int] = set()
stmt = select(Standing.team_id).where(
Standing.league_id == league.id, Standing.season == season_label,
)
for (tid,) in (await db.execute(stmt)).all():
existing_standings.add(tid)
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
seen_teams: set[int] = set() # 同批内去重:同一 team 只处理一次
for r in rows: for r in rows:
team_name = normalize_name(str(r.get("team_name", ""))) team_name = normalize_name(str(r.get("team_name", "")))
if not team_name: if not team_name:
@@ -430,6 +439,11 @@ async def ingest_bzzoiro_standings(db, *, leagues: Iterable[str], season: str |
team_map[team_name] = team team_map[team_name] = team
league_r["teams_created"] += 1 league_r["teams_created"] += 1
# 同批内同一 team 仅处理第一次
if team.id in seen_teams:
continue
seen_teams.add(team.id)
zone = r.get("zone") or {} zone = r.get("zone") or {}
values = dict( values = dict(
position=_to_int_or_none(r.get("position")) or 0, position=_to_int_or_none(r.get("position")) or 0,
@@ -449,20 +463,22 @@ async def ingest_bzzoiro_standings(db, *, leagues: Iterable[str], season: str |
retrieved_at=now, retrieved_at=now,
) )
if team.id in existing_standings:
# 已有 → 仍需 UPDATE:回查对象(少量,可接受)
stmt = select(Standing).where( stmt = select(Standing).where(
Standing.league_id == league.id, Standing.league_id == league.id,
Standing.season == season_label, Standing.season == season_label,
Standing.team_id == team.id, Standing.team_id == team.id,
) )
standing = (await db.execute(stmt)).scalar_one_or_none() standing = (await db.execute(stmt)).scalar_one_or_none()
if standing is None: if standing:
standing = Standing(
league_id=league.id, season=season_label, team_id=team.id, **values
)
db.add(standing)
else:
for k, v in values.items(): for k, v in values.items():
setattr(standing, k, v) setattr(standing, k, v)
else:
db.add(Standing(
league_id=league.id, season=season_label, team_id=team.id, **values,
))
existing_standings.add(team.id) # 防止同批内重复
league_r["upserted"] += 1 league_r["upserted"] += 1
league_r["rows"] = len(rows) league_r["rows"] = len(rows)