fix(P0-02): 积分榜改为追加快照(append-only) + available_at cutoff
去掉 uq_standings_league_season_team,改为 (league, season, team, available_at) 唯一; 每次采集 INSERT 新行(available_at=now),ON CONFLICT DO NOTHING,不覆盖旧行。 standings_slice(before):DISTINCT ON (team_id) WHERE available_at<=cutoff ORDER available_at DESC;before=None → cutoff=now()。 公开 list_standings 取每队最新可用快照(子查询 max available_at)。 迁移 0023 + 切片/路由/docs 同步;测试 test_p0_standings_cutoff(5/5)。 284 测试全绿。
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
"""P0-02: 积分榜改为追加快照(append-only) + available_at
|
||||
|
||||
去掉 uq_standings_league_season_team(league,season,team 唯一),
|
||||
改为 (league, season, team, available_at) 唯一;
|
||||
每次采集 INSERT 新行(available_at=now),支持回测还原历史榜单。
|
||||
|
||||
Revision ID: 0023_standings_append_only
|
||||
Revises: 0022_match_score_status
|
||||
Create Date: 2026-09-22
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision: str = '0023_standings_append_only'
|
||||
down_revision: Union[str, None] = '0022_match_score_status'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
from datetime import datetime, timezone
|
||||
|
||||
# 1) 加 available_at 列(非空,默认 now;存量回填 retrieved_at 或 now)
|
||||
op.add_column(
|
||||
'standings',
|
||||
sa.Column('available_at', sa.DateTime(timezone=True), nullable=False,
|
||||
server_default=sa.func.now()),
|
||||
)
|
||||
# 存量行: available_at 取 retrieved_at(若存在)否则 now
|
||||
op.execute("UPDATE standings SET available_at = COALESCE(retrieved_at, NOW())")
|
||||
|
||||
# 2) 去旧唯一约束,加新唯一约束(league, season, team, available_at)
|
||||
op.drop_constraint('uq_standings_league_season_team', 'standings', type_='unique')
|
||||
op.drop_index('ix_standings_leason_season_pos', table_name='standings')
|
||||
op.create_index('ix_standings_league_season_pos', 'standings', ['league_id', 'season', 'position'])
|
||||
op.create_unique_constraint(
|
||||
'uq_standings_league_season_team_available', 'standings',
|
||||
['league_id', 'season', 'team_id', 'available_at'],
|
||||
)
|
||||
op.create_index(
|
||||
'ix_standings_league_season_team_available', 'standings',
|
||||
['league_id', 'season', 'team_id', 'available_at'],
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index('ix_standings_league_season_team_available', table_name='standings')
|
||||
op.drop_constraint('uq_standings_league_season_team_available', 'standings', type_='unique')
|
||||
op.drop_index('ix_standings_league_season_pos', table_name='standings')
|
||||
op.create_index('ix_standings_leason_season_pos', 'standings', ['league_id', 'season', 'position'])
|
||||
op.create_unique_constraint(
|
||||
'uq_standings_league_season_team', 'standings',
|
||||
['league_id', 'season', 'team_id'],
|
||||
)
|
||||
op.drop_column('standings', 'available_at')
|
||||
Reference in New Issue
Block a user