Files
zhixing-system/zhixing-server/migrations/versions/0003_market_integrity_checks.py
T

91 lines
3.3 KiB
Python

"""Create independent market-data integrity check and issue tables."""
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
revision: str = "0003_market_integrity_checks"
down_revision: str | None = "0002_selection_results"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
"""Create only check metadata; market facts remain untouched."""
op.create_table(
"market_integrity_check",
sa.Column("id", sa.String(36), primary_key=True),
sa.Column("status", sa.String(24), nullable=False),
sa.Column("window_start", sa.Date()),
sa.Column("window_end", sa.Date()),
sa.Column("target_count", sa.Integer(), nullable=False, server_default="0"),
sa.Column("checked_count", sa.Integer(), nullable=False, server_default="0"),
sa.Column("issue_count", sa.Integer(), nullable=False, server_default="0"),
sa.Column("error_type", sa.String(64)),
sa.Column("error_message", sa.Text()),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
nullable=False,
server_default=sa.text("now()"),
),
sa.Column(
"updated_at",
sa.DateTime(timezone=True),
nullable=False,
server_default=sa.text("now()"),
),
sa.Column("finished_at", sa.DateTime(timezone=True)),
)
op.create_index(
"ix_market_integrity_check_status_created_at",
"market_integrity_check",
["status", "created_at"],
)
# A partial unique index makes the running claim atomic under concurrent
# POST requests; stale recovery first moves an abandoned row to failed.
op.create_index(
"uq_market_integrity_check_running",
"market_integrity_check",
["status"],
unique=True,
postgresql_where=sa.text("status = 'running'"),
)
op.create_table(
"market_integrity_issue",
sa.Column(
"check_id",
sa.String(36),
sa.ForeignKey("market_integrity_check.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("issue_key", sa.String(64), nullable=False),
sa.Column("item_kind", sa.String(24), nullable=False),
sa.Column("item_key", sa.String(128), nullable=False),
sa.Column("issue_type", sa.String(64), nullable=False),
sa.Column("message", sa.Text(), nullable=False),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
nullable=False,
server_default=sa.text("now()"),
),
sa.PrimaryKeyConstraint("check_id", "issue_key"),
)
op.create_index("ix_market_integrity_issue_check_id", "market_integrity_issue", ["check_id"])
def downgrade() -> None:
"""Drop only the integrity report tables in dependency-safe order."""
op.drop_index("ix_market_integrity_issue_check_id", table_name="market_integrity_issue")
op.drop_table("market_integrity_issue")
op.drop_index("uq_market_integrity_check_running", table_name="market_integrity_check")
op.drop_index(
"ix_market_integrity_check_status_created_at",
table_name="market_integrity_check",
)
op.drop_table("market_integrity_check")