- 为后端Admin新增migration_compat工具,支持离线迁移时的基线资源检查,重构全部alembic迁移脚本适配离线模式,避免重复创建已存在的数据库表和字段 - 完善初始数据库迁移脚本,添加默认部门种子数据,新增ConciergeAdvisor详情字段从JSON到JSONB的类型转换迁移 - 小程序端新增图片资源解析工具resolveNetworkImage,实现本地静态资源到CDN的自动映射,添加图片加载容错降级机制 - 完善HeroSlide类型定义,新增actionLabel字段,优化首页轮播组件的文本渲染与错误处理逻辑 - 新增对应测试用例验证迁移路径正确性与前端数据处理逻辑 - 新增临时 brainstorm UI对比草稿与开发临时状态文件
160 lines
8.9 KiB
Python
160 lines
8.9 KiB
Python
"""Create the wanfa category and route domain.
|
|
|
|
Revision ID: 0015_wanfa
|
|
Revises: 0001_initial_schema
|
|
"""
|
|
|
|
from datetime import datetime
|
|
|
|
from alembic import context, op
|
|
import sqlalchemy as sa
|
|
from sqlalchemy import inspect
|
|
|
|
from app.migration_compat import baseline_table_names
|
|
|
|
|
|
revision = "0015_wanfa"
|
|
down_revision = "0001_initial_schema"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
SEED_CATEGORIES = (
|
|
("family-route", "亲子路线", 0),
|
|
("photo-route", "旅拍路线", 1),
|
|
("healing-route", "疗愈路线", 2),
|
|
("team-building", "团建", 3),
|
|
("guizhou-panorama", "贵州全景", 4),
|
|
("private-custom", "私人定制", 5),
|
|
)
|
|
|
|
SEED_ROUTES = (
|
|
("family-water", "family-route", "亲子玩水", "贵州·轻松节奏与自然课堂", "https://p0.51img.ca/i/669740a019b94.jpeg", 4, "亲子玩水", 0),
|
|
("family-village", "family-route", "亲子村寨漫游", "贵州·苗寨、古城与在地体验", "https://p6.itc.cn/q_70/images03/20200918/df728d2b79d943da869333e2ea2c92c8.jpeg", 3, "亲子村寨漫游", 1),
|
|
("family-grassland", "family-route", "高原亲子假期", "贵州·草甸、花海与轻徒步", "https://p2.cri.cn/M00/89/21/rBABC2aHnWeACxj5AAAAAAAAAAA159.1000x566.jpg", 2, "高原亲子假期", 2),
|
|
("miao-photo", "photo-route", "苗寨人文旅拍", "黔东南·村寨与非遗影像", "https://i.pinimg.com/736x/d5/86/5d/d5865d0b2765dfb6713787aede09f086.jpg", 3, "苗寨旅拍", 0),
|
|
("peak-photo", "photo-route", "峰林风光旅拍", "黔西南·峰林与峡谷取景", "https://wanfenglin-guanwang.oss-cn-chengdu.aliyuncs.com/uploads/20240718/23ea4291a9570b94a1f8df9a012c18ec.jpg", 2, "万峰林旅拍", 1),
|
|
("terrace-photo", "photo-route", "梯田日落旅拍", "黔东南·侗寨与山野光影", "https://news.cgtn.com/news/2023-08-28/Spectacular-rice-terraces-in-Guizhou-1mDHGG4vtHq/img/9ff1dc49c6304b35bdb83bc1f7f8df93/9ff1dc49c6304b35bdb83bc1f7f8df93.jpeg", 2, "梯田旅拍", 2),
|
|
("mountain-healing", "healing-route", "山野慢游", "贵州·森林、溪谷与松弛时光", "https://images.unsplash.com/photo-1501785888041-af3ef285b470?auto=format&fit=crop&fm=jpg&q=80&w=1200&h=800", 3, "山野慢游", 0),
|
|
("hot-spring-healing", "healing-route", "温泉与村寨", "黔东南·泡汤、慢行与在地风味", "https://www.gz007.net/images/upload/jianhewenquan2.jpg", 2, "温泉与村寨", 1),
|
|
("river-healing", "healing-route", "清凉河谷", "荔波·水线轻徒步与自然疗愈", "https://p0.51img.ca/i/669740a019b94.jpeg", 3, "清凉河谷", 2),
|
|
("team-challenge", "team-building", "山野挑战", "贵州·峡谷协作与户外任务", "https://q9.itc.cn/q_70/images03/20250810/b62f5afc191a4947a66e6b32721b0235.jpeg", 3, "山野挑战", 0),
|
|
("team-stream", "team-building", "溪谷共创", "贵州·溯溪、协作与团队复盘", "https://dimg04.c-ctrip.com/images/0EQ5712000ca7t504EC0E_W_640_10000.jpg?proc=autoorient", 2, "溪谷共创", 1),
|
|
("team-culture", "team-building", "苗寨共聚", "黔东南·人文体验与团队共创", "https://news.cgtn.com/news/2023-07-13/Miao-people-celebrate-Canoe-Dragon-Boat-Festival-1lpanidzxu0/img/61b7100caa8a441aacb3780cfa3eeb69/61b7100caa8a441aacb3780cfa3eeb69.jpeg", 2, "苗寨共聚", 2),
|
|
("classic-panorama", "guizhou-panorama", "经典贵州全景", "贵州·瀑布、苗寨、古城与梵净山", "https://news.cgtn.com/news/2023-07-02/Picturesque-landscapes-draw-tourists-to-Guizhou--1l6RJMNVStq/img/f0d32df3426e4b70bf7a8b00a3404ef9/f0d32df3426e4b70bf7a8b00a3404ef9.jpeg", 8, "经典人文打卡线路", 0),
|
|
("mountain-panorama", "guizhou-panorama", "山水人文全景", "贵州·小七孔、侗寨与山地风光", "https://p6.itc.cn/q_70/images03/20200918/df728d2b79d943da869333e2ea2c92c8.jpeg", 6, "贵州全景", 1),
|
|
("wild-panorama", "guizhou-panorama", "山野户外全景", "贵州·溶洞、峰林与野咖路线", "https://wanfenglin-guanwang.oss-cn-chengdu.aliyuncs.com/uploads/20240718/23ea4291a9570b94a1f8df9a012c18ec.jpg", 7, "山野户外全景", 2),
|
|
("private-family", "private-custom", "家庭专属定制", "贵州·按同行人和节奏安排", "https://news.cgtn.com/news/2023-06-23/Zhenyuan-ancient-town-a-must-see-in-Guizhou-1kRXUSXOP1m/img/6aead33b391640589740a7672c66a685/6aead33b391640589740a7672c66a685.jpeg", 1, "家庭专属定制", 0),
|
|
("private-business", "private-custom", "企业专属定制", "贵州·团建、会议与奖励旅行", "https://wanfenglin-guanwang.oss-cn-chengdu.aliyuncs.com/uploads/20240718/23ea4291a9570b94a1f8df9a012c18ec.jpg", 1, "企业专属定制", 1),
|
|
("private-wild", "private-custom", "山野深度定制", "贵州·溶洞、瀑降与小众目的地", "https://www.zurnal24.si/media/img/5e/d5/9526a56dba168aa136f3.jpeg", 1, "山野深度定制", 2),
|
|
)
|
|
|
|
|
|
def _seed_wanfa() -> None:
|
|
category_table = sa.table(
|
|
"WanfaCategory",
|
|
sa.column("id", sa.String()),
|
|
sa.column("label", sa.String()),
|
|
sa.column("sortOrder", sa.Integer()),
|
|
sa.column("createdAt", sa.DateTime()),
|
|
sa.column("updatedAt", sa.DateTime()),
|
|
)
|
|
route_table = sa.table(
|
|
"WanfaRoute",
|
|
sa.column("id", sa.String()),
|
|
sa.column("categoryId", sa.String()),
|
|
sa.column("title", sa.String()),
|
|
sa.column("subtitle", sa.Text()),
|
|
sa.column("image", sa.String()),
|
|
sa.column("routeCount", sa.Integer()),
|
|
sa.column("demandKeyword", sa.String()),
|
|
sa.column("sortOrder", sa.Integer()),
|
|
sa.column("createdAt", sa.DateTime()),
|
|
sa.column("updatedAt", sa.DateTime()),
|
|
)
|
|
timestamp = datetime(2026, 1, 1)
|
|
op.bulk_insert(
|
|
category_table,
|
|
[
|
|
{"id": item_id, "label": label, "sortOrder": sort_order, "createdAt": timestamp, "updatedAt": timestamp}
|
|
for item_id, label, sort_order in SEED_CATEGORIES
|
|
],
|
|
)
|
|
op.bulk_insert(
|
|
route_table,
|
|
[
|
|
{
|
|
"id": item_id,
|
|
"categoryId": category_id,
|
|
"title": title,
|
|
"subtitle": subtitle,
|
|
"image": image,
|
|
"routeCount": route_count,
|
|
"demandKeyword": demand_keyword,
|
|
"sortOrder": sort_order,
|
|
"createdAt": timestamp,
|
|
"updatedAt": timestamp,
|
|
}
|
|
for item_id, category_id, title, subtitle, image, route_count, demand_keyword, sort_order in SEED_ROUTES
|
|
],
|
|
)
|
|
|
|
|
|
def upgrade() -> None:
|
|
if context.is_offline_mode():
|
|
existing_tables = baseline_table_names()
|
|
else:
|
|
bind = op.get_bind()
|
|
existing_tables = set(inspect(bind).get_table_names())
|
|
|
|
if "WanfaCategory" not in existing_tables:
|
|
op.create_table(
|
|
"WanfaCategory",
|
|
sa.Column("id", sa.String(), nullable=False),
|
|
sa.Column("label", sa.String(), nullable=False),
|
|
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"),
|
|
)
|
|
op.create_index("ix_WanfaCategory_sortOrder", "WanfaCategory", ["sortOrder"])
|
|
|
|
if "WanfaRoute" not in existing_tables:
|
|
op.create_table(
|
|
"WanfaRoute",
|
|
sa.Column("id", sa.String(), nullable=False),
|
|
sa.Column("categoryId", sa.String(), nullable=False),
|
|
sa.Column("title", sa.String(), nullable=False),
|
|
sa.Column("subtitle", sa.Text(), nullable=False),
|
|
sa.Column("image", sa.String(), nullable=False),
|
|
sa.Column("routeCount", sa.Integer(), nullable=False),
|
|
sa.Column("demandKeyword", sa.String(), nullable=False),
|
|
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.ForeignKeyConstraint(["categoryId"], ["WanfaCategory.id"], ondelete="RESTRICT"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index("ix_WanfaRoute_categoryId", "WanfaRoute", ["categoryId"])
|
|
op.create_index("ix_WanfaRoute_sortOrder", "WanfaRoute", ["sortOrder"])
|
|
|
|
if context.is_offline_mode():
|
|
_seed_wanfa()
|
|
return
|
|
|
|
category_table = sa.table("WanfaCategory", sa.column("id", sa.String()))
|
|
route_table = sa.table("WanfaRoute", sa.column("id", sa.String()))
|
|
category_exists = bind.execute(sa.select(category_table.c.id).limit(1)).first() is not None
|
|
route_exists = bind.execute(sa.select(route_table.c.id).limit(1)).first() is not None
|
|
if not category_exists and not route_exists:
|
|
_seed_wanfa()
|
|
|
|
|
|
def downgrade() -> None:
|
|
bind = op.get_bind()
|
|
existing_tables = set(inspect(bind).get_table_names())
|
|
if "WanfaRoute" in existing_tables:
|
|
op.drop_table("WanfaRoute")
|
|
if "WanfaCategory" in existing_tables:
|
|
op.drop_table("WanfaCategory")
|