feat: 新增客片案例与团队共创模块,重构玩法与配置

- 新增客片案例全流程功能,包含前台页面、管理端配置、后端API、数据库迁移与文档
- 新增团队共创详情页面与对应接口
- 重构玩法模块:将静态playData替换为动态API获取,拆分类型定义到playTypes.ts
- 优化vite构建配置与环境变量处理,调整详情页操作栏文案
- 更新全量相关文档与配图,新增客片案例、团队共创API文档
- 新增测试用例,完善数据归一化逻辑
- 调整路由配置与环境变量示例文件
This commit is contained in:
duanshuwen committed 2026-08-19 20:23:04 +08:00
1 parent cda8069630
commit 7ba92f3c5d
76 files changed
+7970 -137

No files matched your search

@@ -6,6 +6,7 @@ Revises: 0013_remove_map_theme_modules
from alembic import op
from sqlalchemy import inspect
import sqlalchemy as sa
revision = "0014_remove_destination_regions_module"
@@ -15,6 +16,13 @@ depends_on = None
def upgrade() -> None:
op.alter_column(
"alembic_version",
"version_num",
existing_type=sa.String(length=32),
type_=sa.String(length=128),
existing_nullable=False,
)
existing_tables = set(inspect(op.get_bind()).get_table_names())
if "DestinationRegion" in existing_tables:
op.drop_table("DestinationRegion")
@@ -0,0 +1,154 @@
"""Create the wanfa category and route domain.
Revision ID: 0015_wanfa
Revises: 0014_remove_destination_regions_module
"""
from datetime import datetime
from alembic import context, op
import sqlalchemy as sa
from sqlalchemy import inspect
revision = "0015_wanfa"
down_revision = "0014_remove_destination_regions_module"
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:
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")
@@ -0,0 +1,118 @@
"""Create the concierge advisor domain.
Revision ID: 0016_concierge_advisors
Revises: 0015_wanfa
"""
from datetime import datetime
from alembic import context, op
import sqlalchemy as sa
from sqlalchemy import inspect
revision = "0016_concierge_advisors"
down_revision = "0015_wanfa"
branch_labels = None
depends_on = None
SEED_ADVISORS = (
{
"id": "advisor-amanda",
"avatar": "https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&fm=jpg&q=80&w=240&h=240",
"name": "Amanda",
"role": "SENIOR TRAVEL ADVISOR",
"details": [
{"icon": "calendar", "label": "服务经验:8年"},
{"icon": "navigate", "label": "擅长领域:瀑降 / 探洞"},
],
"qrImage": "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=WonderQ%20Concierge",
"isActive": True,
"sortOrder": 0,
},
{
"id": "advisor-mia",
"avatar": "https://images.unsplash.com/photo-1531123897727-8f129e1688ce?auto=format&fit=crop&fm=jpg&q=80&w=240&h=240",
"name": "Mia",
"role": "LIFESTYLE TRAVEL ADVISOR",
"details": [
{"icon": "calendar", "label": "服务经验:6年"},
{"icon": "navigate", "label": "擅长领域:溯溪 / 人文探索"},
],
"qrImage": "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=WonderQ%20Concierge",
"isActive": True,
"sortOrder": 1,
},
{
"id": "advisor-leo",
"avatar": "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&fm=jpg&q=80&w=240&h=240",
"name": "Leo",
"role": "WILD TRAVEL ADVISOR",
"details": [
{"icon": "calendar", "label": "服务经验:9年"},
{"icon": "navigate", "label": "擅长领域:洞穴探险 / 山地徒步"},
],
"qrImage": "https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=WonderQ%20Concierge",
"isActive": True,
"sortOrder": 2,
},
)
def _seed_advisors() -> None:
table = sa.table(
"ConciergeAdvisor",
sa.column("id", sa.String()),
sa.column("avatar", sa.String()),
sa.column("name", sa.String()),
sa.column("role", sa.String()),
sa.column("details", sa.JSON()),
sa.column("qrImage", sa.String()),
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)
op.bulk_insert(
table,
[{**advisor, "createdAt": timestamp, "updatedAt": timestamp} for advisor in SEED_ADVISORS],
)
def upgrade() -> None:
bind = op.get_bind()
existing_tables = set(inspect(bind).get_table_names())
if "ConciergeAdvisor" not in existing_tables:
op.create_table(
"ConciergeAdvisor",
sa.Column("id", sa.String(), nullable=False),
sa.Column("avatar", sa.String(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("role", sa.String(), nullable=False),
sa.Column("details", sa.JSON(), nullable=False),
sa.Column("qrImage", sa.String(), nullable=False),
sa.Column("isActive", sa.Boolean(), nullable=False, server_default=sa.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"),
)
op.create_index("ix_ConciergeAdvisor_isActive", "ConciergeAdvisor", ["isActive"])
op.create_index("ix_ConciergeAdvisor_sortOrder", "ConciergeAdvisor", ["sortOrder"])
if context.is_offline_mode():
_seed_advisors()
return
advisor_table = sa.table("ConciergeAdvisor", sa.column("id", sa.String()))
if bind.execute(sa.select(advisor_table.c.id).limit(1)).first() is None:
_seed_advisors()
def downgrade() -> None:
bind = op.get_bind()
existing_tables = set(inspect(bind).get_table_names())
if "ConciergeAdvisor" in existing_tables:
op.drop_table("ConciergeAdvisor")
@@ -0,0 +1,195 @@
"""Create the homepage content domains.
Revision ID: 0017_home_content
Revises: 0016_concierge_advisors
"""
from datetime import datetime
from alembic import context, op
import sqlalchemy as sa
from sqlalchemy import inspect
revision = "0017_home_content"
down_revision = "0016_concierge_advisors"
branch_labels = None
depends_on = None
SEED_EXPERIENCES = (
{
"id": "waterfall-descent",
"badge": "玩过推荐",
"category": "瀑降体验",
"title": "悬崖瀑降",
"englishTitle": "WATERFALL DESCENT",
"image": "https://genk.mediacdn.vn/139269124445442048/2024/4/27/10-23-sinkhole-1714189653945948438879.jpg",
"demandKeyword": "悬崖瀑降",
"isActive": True,
"sortOrder": 0,
},
{
"id": "cave-exploration",
"badge": "玩过推荐",
"category": "地心探险",
"title": "森林&探洞",
"englishTitle": "CAVE EXPLORATION",
"image": "https://www.zurnal24.si/media/img/5e/d5/9526a56dba168aa136f3.jpeg",
"demandKeyword": "地心探险",
"isActive": True,
"sortOrder": 1,
},
)
SEED_TEAM_BUILDINGS = (
{
"id": "wild-challenge",
"tag": "户外挑战",
"title": "山野挑战,共创极境",
"description": "洞穴、瀑降与协作,适合 10-30 人。",
"image": "https://dimg04.c-ctrip.com/images/1mh0412000njfr1ot9453_W_640_10000.jpg?proc=autoorient",
"demandKeyword": "户外团建",
"isActive": True,
"sortOrder": 0,
},
{
"id": "canyon-teamwork",
"tag": "团队协作",
"title": "峡谷溯溪,默契同行",
"description": "溯溪与分组协作,兼顾参与感与安全。",
"image": "https://dimg04.c-ctrip.com/images/0EQ5712000ca7t504EC0E_W_640_10000.jpg?proc=autoorient",
"demandKeyword": "峡谷团建",
"isActive": True,
"sortOrder": 1,
},
{
"id": "village-gathering",
"tag": "人文聚会",
"title": "苗寨共聚,认识彼此",
"description": "夜游、长桌宴与文化体验,适合团建收尾。",
"image": "https://p6.itc.cn/q_70/images03/20200918/df728d2b79d943da869333e2ea2c92c8.jpeg",
"demandKeyword": "贵州团建",
"isActive": True,
"sortOrder": 2,
},
)
SEED_WILD_ARCHIVES = (
{
"id": "hundred-meter-descent",
"title": "百米自降",
"image": "https://genk.mediacdn.vn/139269124445442048/2024/4/27/10-23-sinkhole-1714189653945948438879.jpg",
"demandKeyword": "悬崖瀑降",
"isActive": True,
"sortOrder": 0,
},
{
"id": "shilong-cave",
"title": "石龙洞",
"image": "https://www.zurnal24.si/media/img/5e/d5/9526a56dba168aa136f3.jpeg",
"demandKeyword": "地心探险",
"isActive": True,
"sortOrder": 1,
},
{
"id": "cliff-current",
"title": "绝壁迎流",
"image": "https://q9.itc.cn/q_70/images03/20250810/b62f5afc191a4947a66e6b32721b0235.jpeg",
"demandKeyword": "峡谷探险",
"isActive": True,
"sortOrder": 2,
},
{
"id": "canyon-streaming",
"title": "峡谷溯溪",
"image": "https://dimg04.c-ctrip.com/images/0EQ5712000ca7t504EC0E_W_640_10000.jpg?proc=autoorient",
"demandKeyword": "峡谷溯溪",
"isActive": True,
"sortOrder": 3,
},
)
def _create_table(table_name: str, columns: list[sa.Column]) -> None:
bind = op.get_bind()
if table_name in set(inspect(bind).get_table_names()):
return
op.create_table(table_name, *columns, sa.PrimaryKeyConstraint("id"))
op.create_index(f"ix_{table_name}_isActive", table_name, ["isActive"])
op.create_index(f"ix_{table_name}_sortOrder", table_name, ["sortOrder"])
def _seed_table(table_name: str, columns: list[sa.Column], rows: tuple[dict, ...]) -> None:
table = sa.table(table_name, *[sa.column(column.name, column.type) for column in columns])
timestamp = datetime(2026, 1, 1)
op.bulk_insert(
table,
[{**row, "createdAt": timestamp, "updatedAt": timestamp} for row in rows],
)
def upgrade() -> None:
experience_columns = [
sa.Column("id", sa.String(), nullable=False),
sa.Column("badge", sa.String(), nullable=False),
sa.Column("category", sa.String(), nullable=False),
sa.Column("title", sa.String(), nullable=False),
sa.Column("englishTitle", sa.String(), nullable=False),
sa.Column("image", sa.String(), nullable=False),
sa.Column("demandKeyword", sa.String(), nullable=False),
sa.Column("isActive", sa.Boolean(), nullable=False, server_default=sa.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),
]
team_columns = [
sa.Column("id", sa.String(), nullable=False),
sa.Column("tag", sa.String(), nullable=False),
sa.Column("title", sa.String(), nullable=False),
sa.Column("description", sa.Text(), nullable=False),
sa.Column("image", sa.String(), nullable=False),
sa.Column("demandKeyword", sa.String(), nullable=False),
sa.Column("isActive", sa.Boolean(), nullable=False, server_default=sa.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),
]
archive_columns = [
sa.Column("id", sa.String(), nullable=False),
sa.Column("title", sa.String(), nullable=False),
sa.Column("image", sa.String(), nullable=False),
sa.Column("demandKeyword", sa.String(), nullable=False),
sa.Column("isActive", sa.Boolean(), nullable=False, server_default=sa.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),
]
_create_table("HomeExperience", experience_columns)
_create_table("HomeTeamBuilding", team_columns)
_create_table("HomeWildArchive", archive_columns)
if context.is_offline_mode():
_seed_table("HomeExperience", experience_columns, SEED_EXPERIENCES)
_seed_table("HomeTeamBuilding", team_columns, SEED_TEAM_BUILDINGS)
_seed_table("HomeWildArchive", archive_columns, SEED_WILD_ARCHIVES)
return
bind = op.get_bind()
for table_name, columns, rows in (
("HomeExperience", experience_columns, SEED_EXPERIENCES),
("HomeTeamBuilding", team_columns, SEED_TEAM_BUILDINGS),
("HomeWildArchive", archive_columns, SEED_WILD_ARCHIVES),
):
table = sa.table(table_name, sa.column("id", sa.String()))
if bind.execute(sa.select(table.c.id).limit(1)).first() is None:
_seed_table(table_name, columns, rows)
def downgrade() -> None:
bind = op.get_bind()
existing_tables = set(inspect(bind).get_table_names())
for table_name in ("HomeWildArchive", "HomeTeamBuilding", "HomeExperience"):
if table_name in existing_tables:
op.drop_table(table_name)
@@ -0,0 +1,43 @@
"""Add homepage wanfa category recommendations.
Revision ID: 0018_home_wanfa_recommendations
Revises: 0017_home_content
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
revision = "0018_home_wanfa_recommendations"
down_revision = "0017_home_content"
branch_labels = None
depends_on = None
def upgrade() -> None:
bind = op.get_bind()
if "HomeWanfaRecommendation" in set(inspect(bind).get_table_names()):
return
op.create_table(
"HomeWanfaRecommendation",
sa.Column("id", sa.String(), nullable=False),
sa.Column("categoryId", sa.String(), nullable=False),
sa.Column("isActive", sa.Boolean(), nullable=False, server_default=sa.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.ForeignKeyConstraint(["categoryId"], ["WanfaCategory.id"], ondelete="RESTRICT"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("categoryId", name="uq_HomeWanfaRecommendation_categoryId"),
)
op.create_index("ix_HomeWanfaRecommendation_categoryId", "HomeWanfaRecommendation", ["categoryId"])
op.create_index("ix_HomeWanfaRecommendation_isActive", "HomeWanfaRecommendation", ["isActive"])
op.create_index("ix_HomeWanfaRecommendation_sortOrder", "HomeWanfaRecommendation", ["sortOrder"])
def downgrade() -> None:
bind = op.get_bind()
if "HomeWanfaRecommendation" in set(inspect(bind).get_table_names()):
op.drop_table("HomeWanfaRecommendation")
@@ -0,0 +1,37 @@
"""Add detail images to homepage wild archive cases.
Revision ID: 0019_home_wild_archive_images
Revises: 0018_home_wanfa_recommendations
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision = "0019_home_wild_archive_images"
down_revision = "0018_home_wanfa_recommendations"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"HomeWildArchive",
sa.Column(
"images",
postgresql.JSONB(astext_type=sa.Text()),
nullable=False,
server_default=sa.text("'[]'::jsonb"),
),
)
op.execute(
sa.text(
'UPDATE "HomeWildArchive" SET "images" = jsonb_build_array("image") WHERE jsonb_array_length("images") = 0'
)
)
op.alter_column("HomeWildArchive", "images", server_default=None)
def downgrade() -> None:
op.drop_column("HomeWildArchive", "images")
@@ -0,0 +1,51 @@
"""Add long-form detail content to homepage team buildings.
Revision ID: 0020_home_team_building_details
Revises: 0019_home_wild_archive_images
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision = "0020_home_team_building_details"
down_revision = "0019_home_wild_archive_images"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"HomeTeamBuilding",
sa.Column("detailSubtitle", sa.String(), nullable=False, server_default=""),
)
op.add_column(
"HomeTeamBuilding",
sa.Column(
"detailParagraphs",
postgresql.JSONB(astext_type=sa.Text()),
nullable=False,
server_default=sa.text("'[]'::jsonb"),
),
)
op.execute(
sa.text(
'UPDATE "HomeTeamBuilding" '
'SET "detailSubtitle" = CASE '
'WHEN "detailSubtitle" = \'\' THEN "description" '
'ELSE "detailSubtitle" END, '
'"detailParagraphs" = CASE '
'WHEN jsonb_array_length("detailParagraphs") = 0 '
'THEN jsonb_build_array("description") '
'ELSE "detailParagraphs" END '
'WHERE "detailSubtitle" = \'\' OR jsonb_array_length("detailParagraphs") = 0'
)
)
op.alter_column("HomeTeamBuilding", "detailSubtitle", server_default=None)
op.alter_column("HomeTeamBuilding", "detailParagraphs", server_default=None)
def downgrade() -> None:
op.drop_column("HomeTeamBuilding", "detailParagraphs")
op.drop_column("HomeTeamBuilding", "detailSubtitle")