"""Add source, document, review and audit management.""" import sqlalchemy as sa from alembic import op revision = "0002_catalog" down_revision = "0001_baseline" branch_labels = None depends_on = None def version_columns(): return [ sa.Column("version", sa.Integer(), server_default="1", nullable=False), sa.Column( "created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False ), sa.Column( "updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False ), sa.Column("is_deleted", sa.Boolean(), server_default=sa.false(), nullable=False), ] def upgrade(): op.create_table( "sources", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("name", sa.String(250), nullable=False), sa.Column("source_url", sa.String(1000)), sa.Column("notes", sa.Text(), nullable=False), *version_columns(), ) op.create_table( "documents", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("source_id", sa.Integer(), sa.ForeignKey("sources.id"), nullable=False), sa.Column("title", sa.String(250), nullable=False), sa.Column("category", sa.String(80), nullable=False), sa.Column("content", sa.Text(), nullable=False), sa.Column("collected_at", sa.DateTime(timezone=True), nullable=False), sa.Column("notes", sa.Text(), nullable=False), *version_columns(), ) op.create_index("ix_documents_source_id", "documents", ["source_id"]) op.create_table( "audit_events", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("entity_type", sa.String(30), nullable=False), sa.Column("entity_id", sa.Integer(), nullable=False), sa.Column("action", sa.String(30), nullable=False), sa.Column("actor", sa.String(100)), sa.Column("notes", sa.Text(), nullable=False), sa.Column("snapshot", sa.JSON(), nullable=False), sa.Column( "created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False ), ) op.create_index("ix_audit_events_entity_type", "audit_events", ["entity_type"]) op.create_index("ix_audit_events_entity_id", "audit_events", ["entity_id"]) for column in version_columns(): if column.name != "created_at": op.add_column("knowledge_items", column) for name, kind in [ ("source_id", sa.Integer()), ("document_id", sa.Integer()), ("collected_at", sa.DateTime(timezone=True)), ("reviewer", sa.String(100)), ("reviewed_at", sa.DateTime(timezone=True)), ("verification_notes", sa.Text()), ]: op.add_column("knowledge_items", sa.Column(name, kind)) for name, parent in [("source_id", "sources"), ("document_id", "documents")]: op.create_foreign_key(f"fk_knowledge_{name}", "knowledge_items", parent, [name], ["id"]) op.create_index(f"ix_knowledge_items_{name}", "knowledge_items", [name]) op.create_check_constraint( "ck_knowledge_verification_status", "knowledge_items", "verification_status IN ('pending', 'verified', 'rejected')", ) op.execute("""INSERT INTO audit_events (entity_type, entity_id, action, notes, snapshot) SELECT 'knowledge_items', id, 'import', '保留基础版本数据及原状态', json_build_object('item', row_to_json(k)) FROM knowledge_items k""") op.execute("""UPDATE knowledge_items SET verification_status = 'pending', version = version + 1 WHERE verification_status = 'verified'""") op.execute("""INSERT INTO audit_events (entity_type, entity_id, action, notes, snapshot) SELECT 'knowledge_items', id, 'invalidate', '旧版本没有完整审核记录,需要重新核验', json_build_object('item', row_to_json(k)) FROM knowledge_items k WHERE version = 2""") def downgrade(): op.drop_constraint("ck_knowledge_verification_status", "knowledge_items", type_="check") for name in ["source_id", "document_id"]: op.drop_index(f"ix_knowledge_items_{name}", table_name="knowledge_items") op.drop_constraint(f"fk_knowledge_{name}", "knowledge_items", type_="foreignkey") for name in [ "source_id", "document_id", "collected_at", "reviewer", "reviewed_at", "verification_notes", "version", "updated_at", "is_deleted", ]: op.drop_column("knowledge_items", name) op.drop_table("audit_events") op.drop_table("documents") op.drop_table("sources")