"""文章已读记录(per-user) - article_reads.user_id BIGINT FK users.id ON DELETE CASCADE - article_reads.article_id BIGINT FK articles.id ON DELETE CASCADE - article_reads.read_at TIMESTAMPTZ DEFAULT now() - 复合主键 (user_id, article_id) — 天然幂等 Revision ID: 0007 Revises: 0006 Create Date: 2026-06-13 """ from __future__ import annotations from typing import Sequence, Union import sqlalchemy as sa from alembic import op revision: str = "0007" down_revision: Union[str, None] = "0006" branch_labels = None depends_on = None def upgrade() -> None: op.create_table( "article_reads", sa.Column("user_id", sa.Integer, nullable=False), # users.id 是 Integer sa.Column("article_id", sa.BigInteger, nullable=False), # articles.id 是 BigInteger sa.Column("read_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")), sa.PrimaryKeyConstraint("user_id", "article_id", name="pk_article_reads"), sa.ForeignKeyConstraint( "user_id", ["users.id"], ondelete="CASCADE", ), sa.ForeignKeyConstraint( "article_id", ["articles.id"], ondelete="CASCADE", ), ) op.create_index( "ix_article_reads_user_read_at", "article_reads", ["user_id", "read_at"], ) op.create_index( "ix_article_reads_article", "article_reads", ["article_id"], ) def downgrade() -> None: op.drop_index("ix_article_reads_article", table_name="article_reads") op.drop_index("ix_article_reads_user_read_at", table_name="article_reads") op.drop_table("article_reads")