"""Create independent detail presentation records for frontend pages. Revision ID: 0021_detail_records Revises: 0020_home_team_building_details """ from datetime import datetime from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql revision = "0021_detail_records" down_revision = "0020_home_team_building_details" branch_labels = None depends_on = None def upgrade() -> None: op.create_table( "DetailRecord", sa.Column("id", sa.String(), nullable=False), sa.Column("key", sa.String(), nullable=False), sa.Column("eyebrow", sa.String(), nullable=False), sa.Column("duration", sa.String(), nullable=False), sa.Column("title", sa.String(), nullable=False), sa.Column("subtitle", sa.Text(), nullable=False), sa.Column("intro", sa.Text(), nullable=False), sa.Column("highlights", postgresql.JSONB(astext_type=sa.Text()), nullable=False, server_default=sa.text("'[]'::jsonb")), sa.Column("included", postgresql.JSONB(astext_type=sa.Text()), nullable=False, server_default=sa.text("'[]'::jsonb")), sa.Column("excluded", postgresql.JSONB(astext_type=sa.Text()), nullable=False, server_default=sa.text("'[]'::jsonb")), sa.Column("notes", postgresql.JSONB(astext_type=sa.Text()), nullable=False, server_default=sa.text("'[]'::jsonb")), sa.Column("gallery", postgresql.JSONB(astext_type=sa.Text()), nullable=False, server_default=sa.text("'[]'::jsonb")), sa.Column("isActive", sa.Boolean(), nullable=False, server_default=sa.text("true")), sa.Column("sortOrder", sa.Integer(), nullable=False, server_default="0"), sa.Column("createdAt", sa.DateTime(), nullable=False), sa.Column("updatedAt", sa.DateTime(), nullable=False), sa.PrimaryKeyConstraint("id"), sa.UniqueConstraint("key"), ) op.create_index("ix_DetailRecord_isActive", "DetailRecord", ["isActive"]) op.create_index("ix_DetailRecord_sortOrder", "DetailRecord", ["sortOrder"]) detail_table = sa.table( "DetailRecord", sa.column("id", sa.String()), sa.column("key", sa.String()), sa.column("eyebrow", sa.String()), sa.column("duration", sa.String()), sa.column("title", sa.String()), sa.column("subtitle", sa.Text()), sa.column("intro", sa.Text()), sa.column("highlights", postgresql.JSONB()), sa.column("included", postgresql.JSONB()), sa.column("excluded", postgresql.JSONB()), sa.column("notes", postgresql.JSONB()), sa.column("gallery", postgresql.JSONB()), sa.column("isActive", sa.Boolean()), sa.column("sortOrder", sa.Integer()), sa.column("createdAt", sa.DateTime()), sa.column("updatedAt", sa.DateTime()), ) routes = op.get_bind().execute( sa.text('SELECT "id", "title", "subtitle", "image", "sortOrder" FROM "WanfaRoute" ORDER BY "sortOrder" ASC') ).mappings().all() timestamp = datetime(2026, 1, 1) op.bulk_insert( detail_table, [ { "id": f"detail-{route['id']}", "key": route["id"], "eyebrow": "玩法推荐", "duration": "5天4晚", "title": route["title"], "subtitle": route["subtitle"], "intro": route["subtitle"], "highlights": ["核心景观串联", "小团出行,按同行人的节奏灵活调整"], "included": ["行程内用车与接送服务"], "excluded": ["往返大交通及个人消费"], "notes": ["最终方案以出行日期、人数和资源确认结果为准。"], "gallery": [route["image"]], "isActive": True, "sortOrder": route["sortOrder"], "createdAt": timestamp, "updatedAt": timestamp, } for route in routes ], ) def downgrade() -> None: op.drop_index("ix_DetailRecord_sortOrder", table_name="DetailRecord") op.drop_index("ix_DetailRecord_isActive", table_name="DetailRecord") op.drop_table("DetailRecord")