"""V15 T+ 持久化任务、幂等和重试元数据。""" from collections.abc import Sequence from alembic import op import sqlalchemy as sa from sqlalchemy.dialects.mysql import BIGINT revision: str = "e5f607a8b9c0" down_revision: str | Sequence[str] | None = "d4e5f6a7b8c9" branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None def upgrade() -> None: """仅扩展管理元数据,不改原始暂存行或工资业务表。""" op.add_column( "tplus_sync_batches", sa.Column("entity_types_json", sa.JSON(), comment="请求读取的实体类型;历史批次可为空"), ) op.add_column( "tplus_sync_batches", sa.Column( "skipped_record_count", sa.Integer(), nullable=False, server_default="0", comment="重复快照跳过条数", ), ) op.add_column( "tplus_sync_batches", sa.Column("parent_batch_no", sa.String(32), comment="重试来源批次号,保留原批次记录"), ) op.add_column( "tplus_sync_batches", sa.Column("request_key", sa.String(36), comment="请求幂等键,空值用于历史和直接CLI任务"), ) op.add_column( "tplus_sync_batches", sa.Column( "requested_by", BIGINT(unsigned=True), comment="请求用户ID,CLI任务为空,无物理外键" ), ) op.add_column( "tplus_sync_batches", sa.Column("audit_result_json", sa.JSON(), comment="关联核验结果,只含系统字典和汇总"), ) op.alter_column( "tplus_sync_batches", "started_at", existing_type=sa.DateTime(), nullable=True, comment="开始执行时间(排队时为空)", ) op.alter_column( "tplus_sync_batches", "sync_mode", existing_type=sa.String(16), nullable=False, comment="任务方式:FULL全量 AUDIT关联核验 INCREMENTAL预留", ) op.alter_column( "tplus_sync_batches", "status", existing_type=sa.String(16), nullable=False, comment="状态:QUEUED/RUNNING/SUCCEEDED/PARTIAL/FAILED", ) op.create_unique_constraint( "uk_tplus_sync_batches_request_key", "tplus_sync_batches", ["request_key"] ) op.create_index( "idx_tplus_sync_batches_parent_status", "tplus_sync_batches", ["parent_batch_no", "status"] ) op.execute( "UPDATE tplus_sync_batches SET skipped_record_count=GREATEST(0, source_record_count-staged_record_count-failed_record_count) WHERE status='SUCCEEDED' AND sync_mode='FULL'" ) def downgrade() -> None: """回滚前关闭 worker;原始暂存数据和历史批次仍保留。""" op.execute( "UPDATE tplus_sync_batches SET started_at=create_time, status='FAILED', finished_at=CURRENT_TIMESTAMP, error_message='任务管理版本已回滚' WHERE status='QUEUED'" ) op.execute("UPDATE tplus_sync_batches SET started_at=create_time WHERE started_at IS NULL") op.drop_index("idx_tplus_sync_batches_parent_status", table_name="tplus_sync_batches") op.drop_constraint("uk_tplus_sync_batches_request_key", "tplus_sync_batches", type_="unique") for name in ( "audit_result_json", "requested_by", "request_key", "parent_batch_no", "skipped_record_count", "entity_types_json", ): op.drop_column("tplus_sync_batches", name) op.alter_column( "tplus_sync_batches", "started_at", existing_type=sa.DateTime(), nullable=False, comment="开始读取时间", ) op.alter_column( "tplus_sync_batches", "sync_mode", existing_type=sa.String(16), nullable=False, comment="同步模式:FULL全量 INCREMENTAL增量", ) op.alter_column( "tplus_sync_batches", "status", existing_type=sa.String(16), nullable=False, comment="状态:RUNNING/SUCCEEDED/PARTIAL/FAILED", )