feat(market-data): 优化同步并增加完整性检查
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""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")
|
||||
Reference in New Issue
Block a user