Files
WonderQ-Project/WonderQ-Admin/alembic/versions/0021_detail_records.py
T

140 lines
5.4 KiB
Python

"""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 context, op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy import inspect
from sqlalchemy.dialects import postgresql
revision = "0021_detail_records"
down_revision = "0020_home_team_building_details"
branch_labels = None
depends_on = None
DETAIL_RECORD_INDEXES = {
"ix_DetailRecord_isActive": ["isActive"],
"ix_DetailRecord_sortOrder": ["sortOrder"],
}
def missing_detail_record_indexes(existing_indexes: set[str]) -> set[str]:
return set(DETAIL_RECORD_INDEXES).difference(existing_indexes)
def build_detail_seed_rows(routes, existing_keys: set[str], timestamp: datetime) -> list[dict]:
return [
{
"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
if route["id"] not in existing_keys
]
def upgrade() -> None:
if context.is_offline_mode():
table_exists = False
existing_index_names = set()
else:
bind = op.get_bind()
inspector = inspect(bind)
table_exists = "DetailRecord" in set(inspector.get_table_names())
existing_index_names = {
index["name"]
for index in inspector.get_indexes("DetailRecord")
} if table_exists else set()
if not table_exists:
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"),
)
existing_index_names = set()
for index_name in missing_detail_record_indexes(existing_index_names):
op.create_index(index_name, "DetailRecord", DETAIL_RECORD_INDEXES[index_name])
def upgrade() -> None:
bind = op.get_bind()
if "DetailRecord" not in set(inspect(bind).get_table_names()):
_create_detail_table()
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()),
)
timestamp = datetime(2026, 1, 1)
if context.is_offline_mode():
return
conn = op.get_bind()
routes = conn.execute(
sa.text('SELECT "id", "title", "subtitle", "image", "sortOrder" FROM "WanfaRoute" ORDER BY "sortOrder" ASC')
).mappings().all()
existing_keys = set(conn.execute(sa.text('SELECT "key" FROM "DetailRecord"')).scalars())
seed_rows = build_detail_seed_rows(routes, existing_keys, timestamp)
if seed_rows:
op.bulk_insert(detail_table, seed_rows)
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")