from __future__ import annotations import json import os from sqlalchemy.orm import Session from app.config import settings from app.models.brand import Brand, BrandTrustStat, BrandDifferentiator from app.models.product import ProductConfig, ProductVersion, ProductVersionHighlight, SummerCamp, SelectionGuide from app.models.faq import FaqCategory, FaqQuestion from app.models.review import ReviewSummary, Review from app.models.about import ( AboutStory, AboutSubsidiary, AboutCertification, AboutTrademark, AboutCopyright, AboutGuarantee, AboutXiaohongshu, AboutTeam, AboutCulture, AboutFounderDetail, AboutFounder, AboutMilestone, AboutServicePrinciple, AboutDifferentiation, AboutServiceJourney, AboutStats, ) from app.models.customize_config import CustomizeConfig from app.models.contact import ContactChannel, ContactConfig from app.models.seo import SeoPage from app.models.navigation import NavHeader, NavFooterGroup from app.models.version import VersionConfig, VersionUpgrade, VersionHighlight, VersionCompare, VersionTimeline, VersionPhilosophy from app.models.guide import GuideConfig, GuideSection from app.models.site_image import SiteImage from app.models.seasonal_product import SeasonalProductConfig, SeasonalProductVersion, SeasonalProductHighlight, SeasonalProductTimeline from app.models.winter_camp import WinterCampConfig, WinterCampHotel, WinterCampItinerary, WinterCampFaq from app.models.destination import DestinationConfig, DestinationItem, DestinationDimension, DestinationHonestItem from app.models.blog import Blog from app.models.story import Story from app.models.news import News from app.models.pricing import PricingItem from app.models.qualification import Qualification from app.models.partner import Partner from app.models.gallery import GalleryItem from app.models.destination_detail import DestinationDetail from app.models.selector import SelectorConfig from app.models.courses import CoursesConfig def _write_json(filename: str, data): path = os.path.join(settings.NUXT_DATA_PATH, filename) with open(path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) def export_brand(db: Session): brand = db.query(Brand).first() if not brand: return data = { "name": brand.name, "fullName": brand.full_name, "domain": brand.domain, "url": brand.url, "slogan": { "emotional": brand.slogan_emotional, "functional": brand.slogan_functional, }, "trustStats": [ {"value": s.value, "unit": s.unit, "label": s.label} for s in brand.trust_stats ], "differentiators": [ {"title": d.title, "description": d.description} for d in brand.differentiators ], "icpEntity": brand.icp_entity, "icp": brand.icp, "icpUrl": brand.icp_url, "eContract": brand.e_contract, } if brand.cta_buttons: data["ctaButtons"] = brand.cta_buttons if brand.conversion_path: data["conversionPath"] = brand.conversion_path _write_json("brand.json", data) def export_products(db: Session): config = db.query(ProductConfig).first() if not config: return versions = db.query(ProductVersion).filter_by(config_id=config.id).order_by(ProductVersion.sort_order).all() data = { "narrative": config.narrative, "versions": [], } for v in versions: data["versions"].append({ "id": v.version_id, "name": v.name, "days": v.days, "nights": v.nights, "audience": v.audience, "description": v.description, "highlights": [h.text for h in (v.highlights or [])], "tag": v.tag, }) # Summer camp camp = db.query(SummerCamp).filter_by(config_id=config.id).first() if camp: data["summerCamp"] = { "name": camp.name, "positioning": camp.positioning, "days": camp.days, "nights": camp.nights, "sessions": camp.sessions_json or {}, "principles": [p.text for p in (camp.principles or [])], "coreActivities": [a.text for a in (camp.activities or [])], "itinerary": [it.text for it in (camp.itinerary or [])], "faq": [{"question": f.question, "answer": f.answer} for f in (camp.faq or [])], "differenceFromV9": camp.difference_from_v9, } # Selection guide guide = db.query(SelectionGuide).filter_by(config_id=config.id).first() if guide: data["selectionGuide"] = { "byVacationLength": guide.by_vacation_length or [], "byChildAge": guide.by_child_age or [], "byPreference": guide.by_preference or [], } if config.pricing_philosophy: data["pricingPhilosophy"] = config.pricing_philosophy _write_json("products.json", data) def export_faq(db: Session): categories = db.query(FaqCategory).order_by(FaqCategory.sort_order).all() data = { "categories": [ { "id": cat.category_id, "name": cat.name, "questions": [ { "id": q.question_id, "question": q.question, "answer": q.answer, "relatedLinks": q.related_links or [], } for q in cat.questions ], } for cat in categories ] } _write_json("faq.json", data) def export_reviews(db: Session): summary = db.query(ReviewSummary).first() reviews = db.query(Review).filter(Review.is_visible == True).order_by(Review.sort_order, Review.id).all() data = { "summary": { "totalCount": summary.total_count if summary else 0, "approvalRate": summary.approval_rate if summary else "98%", "keywords": summary.keywords if summary else [], }, "items": [ { "id": r.id, "nickname": r.nickname, "travelDate": r.travel_date, "productVersion": r.product_version, "screenshot": r.screenshot, "content": r.content, "scenes": r.scenes or [], "concerns": r.concerns or [], } for r in reviews ], } _write_json("reviews.json", data) def export_about(db: Session): story = db.query(AboutStory).first() subs = db.query(AboutSubsidiary).order_by(AboutSubsidiary.sort_order).all() certs = db.query(AboutCertification).order_by(AboutCertification.sort_order).all() tm = db.query(AboutTrademark).first() copyrights = db.query(AboutCopyright).order_by(AboutCopyright.sort_order).all() guarantees = db.query(AboutGuarantee).order_by(AboutGuarantee.sort_order).all() xhs = db.query(AboutXiaohongshu).first() team = db.query(AboutTeam).first() culture = db.query(AboutCulture).first() founder_detail = db.query(AboutFounderDetail).first() founders = db.query(AboutFounder).order_by(AboutFounder.sort_order).all() milestones = db.query(AboutMilestone).order_by(AboutMilestone.sort_order).all() principles = db.query(AboutServicePrinciple).order_by(AboutServicePrinciple.sort_order).all() differentiation = db.query(AboutDifferentiation).order_by(AboutDifferentiation.sort_order).all() sj = db.query(AboutServiceJourney).first() stats_row = db.query(AboutStats).first() data = {} if story: paragraphs = [p.strip() for p in story.content.split("\n\n") if p.strip()] if story.content else [] story_data = {"title": story.title, "paragraphs": paragraphs} if story.totem_description or story.totem_tagline: story_data["totem"] = {"description": story.totem_description, "tagline": story.totem_tagline} data["story"] = story_data data["subsidiaries"] = [{"name": s.name, "role": s.role, "established": s.established} for s in subs] data["certifications"] = [{"title": c.title, "detail": c.detail} for c in certs] if tm: data["trademark"] = {"name": tm.name, "scope": tm.scope, "holder": tm.holder, "description": tm.description} data["copyrights"] = [ {"name": c.name, "regNo": c.reg_no, "category": c.category, "holder": c.holder, "date": c.date, "description": c.description} for c in copyrights ] data["guarantees"] = [{"title": g.title, "detail": g.detail} for g in guarantees] if xhs: data["xiaohongshu"] = { "account": xhs.account, "verified": xhs.verified, "verifiedType": xhs.verified_type, "followers": xhs.followers, "likes": xhs.likes, "awards": xhs.awards or [], "tagline": xhs.tagline, "tags": xhs.tags or [], "description": xhs.description, } if team: data["team"] = {"summary": team.summary} if culture: culture_data = { "transparency": culture.transparency, "values": [{"name": v.name, "expression": v.expression} for v in (culture.values or [])], } if culture.core: culture_data["core"] = culture.core if culture.trust: culture_data["trust"] = culture.trust if culture.grief_award_name: culture_data["griefAward"] = {"name": culture.grief_award_name, "description": culture.grief_award_description} data["culture"] = culture_data if founder_detail: data["founder"] = { "name": founder_detail.name, "title": founder_detail.title, "brandFounded": founder_detail.brand_founded, "yearsInHulunbuir": founder_detail.years_in_hulunbuir, "background": founder_detail.background, "expertise": founder_detail.expertise or [], "mediaPresence": founder_detail.media_presence or [], } if founders: data["founders"] = [{"name": f.name, "title": f.title, "story": f.story} for f in founders] if milestones: data["milestones"] = [{"year": m.year, "event": m.event} for m in milestones] if principles: data["servicePrinciples"] = [p.text for p in principles] if differentiation: data["differentiation"] = [{"name": d.name, "detail": d.detail} for d in differentiation] if sj: data["serviceJourney"] = { "title": sj.title, "subtitle": sj.subtitle, "moments": [{"step": m.step, "name": m.name, "detail": m.detail} for m in (sj.moments or [])], } if stats_row and stats_row.data: data["stats"] = stats_row.data _write_json("about.json", data) def export_customize(db: Session): cfg = db.query(CustomizeConfig).first() if not cfg: return data = { "trustStats": cfg.trust_stats or [], "durations": cfg.durations or [], "activities": cfg.activities or [], "budgets": cfg.budgets or [], "process": cfg.process or [], "contact": cfg.contact or {}, } _write_json("customize.json", data) def export_contact(db: Session): channels = db.query(ContactChannel).order_by(ContactChannel.sort_order).all() config = db.query(ContactConfig).first() data = { "channels": [ { "type": c.type, "label": c.label, "value": c.value, **({"qrImage": c.qr_image} if c.qr_image else {}), **({"primary": True} if c.is_primary else {}), "description": c.description, } for c in channels ], "securityNotice": config.security_notice if config else "", } _write_json("contact.json", data) def export_seo(db: Session): pages = db.query(SeoPage).all() data = {"pages": {}} for p in pages: page_data = { "title": p.title, "description": p.description, "h1": p.h1, "ogImage": p.og_image, } if p.keywords: page_data["keywords"] = p.keywords data["pages"][p.page_key] = page_data _write_json("seo.json", data) def export_navigation(db: Session): headers = db.query(NavHeader).order_by(NavHeader.sort_order).all() groups = db.query(NavFooterGroup).order_by(NavFooterGroup.sort_order).all() header_list = [] for h in headers: item = {"text": h.text, "to": h.to_path} if h.children: item["children"] = h.children header_list.append(item) data = { "header": header_list, "footer": [ {"title": g.title, "links": [{"text": l.text, "to": l.to_path} for l in (g.links or [])]} for g in groups ], } _write_json("navigation.json", data) def export_versions(db: Session): config = db.query(VersionConfig).first() if not config: return upgrades = db.query(VersionUpgrade).filter_by(config_id=config.id).order_by(VersionUpgrade.sort_order).all() highlights = db.query(VersionHighlight).filter_by(config_id=config.id).order_by(VersionHighlight.sort_order).all() compare = db.query(VersionCompare).filter_by(config_id=config.id).first() timeline = db.query(VersionTimeline).filter_by(config_id=config.id).order_by(VersionTimeline.sort_order).all() philosophy = db.query(VersionPhilosophy).filter_by(config_id=config.id).order_by(VersionPhilosophy.sort_order).all() data = { "stats": { "iterations": config.stats_iterations, "years": config.stats_years, "guests": config.stats_guests, }, "upgrades2026": [ {"tag": u.tag, "name": u.name, "description": u.description, "reason": u.reason} for u in upgrades ], "highlights": [{"label": h.label, "text": h.text} for h in highlights], } if compare: data["compareV8V9"] = {"headers": compare.headers or [], "rows": compare.rows or []} data["timeline"] = [ { "version": t.version, "date": t.date, "title": t.title, "changes": [{"type": ch.type, "text": ch.text} for ch in (t.changes or [])], "reason": t.reason, } for t in timeline ] if philosophy: data["philosophy"] = [{"label": p.label, "text": p.text} for p in philosophy] if config.quote_text: data["quote"] = {"text": config.quote_text, "author": config.quote_author} _write_json("versions.json", data) def export_guides(db: Session): config = db.query(GuideConfig).first() if not config: return sections = db.query(GuideSection).filter_by(config_id=config.id).order_by(GuideSection.sort_order).all() data = { "pageIntro": config.page_intro, "sections": [], } for s in sections: section_data = { "id": s.section_id, "title": s.title, "subtitle": s.subtitle, "icon": s.icon, "content": s.content, } # Merge the JSON data fields back into the section if s.data and isinstance(s.data, dict): section_data.update(s.data) data["sections"].append(section_data) _write_json("guides.json", data) def export_images(db: Session): images = db.query(SiteImage).order_by(SiteImage.group_name, SiteImage.image_key).all() data = {} for img in images: if img.group_name not in data: data[img.group_name] = {} data[img.group_name][img.image_key] = img.image_path _write_json("images.json", data) def _export_seasonal(db: Session, season: str, filename: str): config = db.query(SeasonalProductConfig).filter_by(season=season).first() versions = db.query(SeasonalProductVersion).filter_by(season=season).order_by(SeasonalProductVersion.sort_order).all() highlights = db.query(SeasonalProductHighlight).filter_by(season=season).order_by(SeasonalProductHighlight.sort_order).all() timeline = db.query(SeasonalProductTimeline).filter_by(season=season).order_by(SeasonalProductTimeline.sort_order).all() data = {} if config: if config.brand_name: data["brand"] = config.brand_name if config.version_label: data["version"] = config.version_label if config.season_label: data["season"] = config.season_label data["narrative"] = config.narrative data["style"] = config.style data["versions"] = [ { "id": v.version_id, "name": v.name, "days": v.days, "nights": v.nights, "tag": v.tag, "line": v.line, "route": v.route, "audience": v.audience, "description": v.description, "highlights": v.highlights or [], "itinerary": v.itinerary or [], } for v in versions ] shared = [h for h in highlights if h.category == "shared"] south = [h for h in highlights if h.category == "south"] north = [h for h in highlights if h.category == "north"] data["highlights"] = [{"title": h.title, "description": h.description} for h in shared] data["southHighlights"] = [{"title": h.title, "description": h.description} for h in south] data["northHighlights"] = [{"title": h.title, "description": h.description} for h in north] data["timeline"] = [ { "version": t.version, "date": t.date, "title": t.title, "changes": t.changes or [], "reason": t.reason, } for t in timeline ] if config and config.selection_guide: data["selectionGuide"] = config.selection_guide _write_json(filename, data) def export_autumn_products(db: Session): _export_seasonal(db, "autumn", "autumn-products.json") def export_winter_products(db: Session): _export_seasonal(db, "winter", "winter-products.json") def export_winter_camp(db: Session): config = db.query(WinterCampConfig).first() if not config: return hotels = db.query(WinterCampHotel).order_by(WinterCampHotel.sort_order).all() itinerary = db.query(WinterCampItinerary).order_by(WinterCampItinerary.sort_order).all() faq = db.query(WinterCampFaq).order_by(WinterCampFaq.sort_order).all() data = { "name": config.name, "positioning": config.positioning, "days": config.days, "nights": config.nights, "maxFamilies": config.max_families, "totalSessions": config.total_sessions, "ageRange": config.age_range, "deposit": config.deposit, "season": config.season, "route": config.route, "whyHulunbuir": config.why_hulunbuir, "closingNote": config.closing_note, "photographer": config.photographer or {}, "winterClothing": config.winter_clothing or {}, "campAdvantages": config.camp_advantages or [], "serviceConfig": config.service_config or [], "campEssentials": config.camp_essentials or [], "hotels": [{"name": h.name, "star": h.star, "nights": h.nights, "description": h.description} for h in hotels], "itinerary": [ {"day": d.day, "title": d.title, "summary": d.summary, "highlights": d.highlights or [], "hotel": d.hotel} for d in itinerary ], "faq": [{"q": f.question, "a": f.answer} for f in faq], } _write_json("winter-camp.json", data) def export_destinations(db: Session): config = db.query(DestinationConfig).first() if not config: return items = db.query(DestinationItem).order_by(DestinationItem.sort_order).all() dims = db.query(DestinationDimension).order_by(DestinationDimension.sort_order).all() honest = db.query(DestinationHonestItem).order_by(DestinationHonestItem.sort_order).all() data = { "title": config.title, "subtitle": config.subtitle, "intro": config.intro, "destinations": [ {**({"highlight": True} if d.highlight else {}), "id": d.dest_id, "name": d.name, "tag": d.tag} for d in items ], "dimensions": [{"label": d.label, "icon": d.icon, "values": d.values or []} for d in dims], "honestNote": { "title": config.honest_title, "subtitle": config.honest_subtitle, "items": [h.text for h in honest], }, "closing": {"title": config.closing_title, "text": config.closing_text}, "dataSources": config.data_sources, } _write_json("destinations.json", data) def export_blog(db: Session): posts = db.query(Blog).filter(Blog.is_visible == True).order_by(Blog.sort_order, Blog.id).all() data = { "articles": [ { "id": p.slug, "title": p.title, "subtitle": p.summary, "author": p.author, "date": p.published_at, "category": p.category, "summary": p.summary, "coverImage": p.cover_image, "tags": p.tags or [], "content": p.content or "", } for p in posts ] } _write_json("blog.json", data) def export_stories(db: Session): stories = db.query(Story).filter(Story.is_visible == True).order_by(Story.sort_order, Story.id).all() data = { "stories": [ { "id": f"story-{s.id}", "title": s.title, "subtitle": s.summary, "coverImage": s.cover_image, "familyType": s.customer_name, "travelDate": s.travel_date, "product": s.product_name, "tags": s.scenes or [], "summary": s.summary, "content": s.content or "", } for s in stories ] } _write_json("stories.json", data) def export_news(db: Session): articles = db.query(News).filter(News.is_visible == True).order_by(News.sort_order, News.id).all() data = { "articles": [ { "id": f"news-{n.id}", "title": n.title, "date": n.published_at, "category": n.source, "summary": n.summary, "content": n.content or "", } for n in articles ] } _write_json("news.json", data) def export_pricing(db: Session): items = db.query(PricingItem).filter(PricingItem.is_visible == True).order_by(PricingItem.sort_order).all() data = { "products": [ { "id": p.product_slug or f"product-{p.id}", "name": p.product_name, "priceLabel": p.price_label, "priceNote": p.description, "highlights": p.features or [], "url": f"/products/{p.product_slug}" if p.product_slug else None, } for p in items ] } _write_json("pricing.json", data) def export_qualifications(db: Session): items = db.query(Qualification).order_by(Qualification.sort_order).all() licenses = [ {"title": q.title, "issuer": q.issuer, "description": q.description} for q in items if q.category == "license" ] insurance_items = [ {"title": q.title, "issuer": q.issuer, "description": q.description} for q in items if q.category == "insurance" ] awards = [ {"title": q.title, "issuer": q.issuer, "year": int(q.year) if q.year and q.year.isdigit() else q.year, "description": q.description} for q in items if q.category == "award" ] heritage_items = [ {"title": q.title, "description": q.description} for q in items if q.category == "heritage" ] data = { "licenses": licenses, "insurance": insurance_items[0] if insurance_items else {}, "awards": awards, "heritage": heritage_items[0] if heritage_items else {}, } _write_json("qualifications.json", data) def export_partners(db: Session): partners = db.query(Partner).order_by(Partner.sort_order).all() cat_map = { "政府合作": "govPartners", "行业协会": "associations", "校企合作": "academicCoops", "景区合作": "scenicPartners", "酒店合作": "hotelPartners", "平台认证": "platformEndorsements", "媒体报道": "mediaReports", } data = {v: [] for v in cat_map.values()} for p in partners: key = cat_map.get(p.category, "govPartners") data[key].append({ "name": p.name, "description": p.description, "website": p.website, "verified": True, }) _write_json("partners.json", data) def export_gallery(db: Session): items = db.query(GalleryItem).filter(GalleryItem.is_visible == True).order_by(GalleryItem.sort_order, GalleryItem.id).all() data = { "works": [ { "id": f"gallery-{str(g.id).zfill(3)}", "title": g.title, "category": (g.tags or [None])[0] if g.tags else None, "location": g.location, "description": g.description, "image": g.image, } for g in items ] } _write_json("gallery.json", data) def export_destinations_detail(db: Session): items = db.query(DestinationDetail).filter(DestinationDetail.is_visible == True).order_by(DestinationDetail.sort_order).all() data = { "destinations": [ { "id": d.slug, "name": d.name, "subtitle": d.subtitle, "tag": (d.tags or [None])[0] if d.tags else None, "description": d.description, "highlights": [ h.get("text", h) if isinstance(h, dict) else h for h in (d.highlights or []) ], "bestSeason": {"primary": d.best_season} if d.best_season else {}, "heroImage": d.cover_image, } for d in items ] } _write_json("destinations-detail.json", data) def export_selector(db: Session): cfg = db.query(SelectorConfig).first() if not cfg: return rules = cfg.rules or {} data = { "questions": cfg.questions or [], "scoring": rules.get("scoring", {}), "matchReasons": rules.get("matchReasons", {}), } _write_json("selector.json", data) def export_courses(db: Session): cfg = db.query(CoursesConfig).first() if not cfg: return data = { "modules": cfg.modules or [], "ageGroups": cfg.age_groups or [], "faq": cfg.faqs or [], } _write_json("courses.json", data) # Module name -> export function mapping EXPORT_FUNCTIONS = { "brand": export_brand, "products": export_products, "faq": export_faq, "reviews": export_reviews, "about": export_about, "contact": export_contact, "seo": export_seo, "navigation": export_navigation, "versions": export_versions, "guides": export_guides, "images": export_images, "autumn-products": export_autumn_products, "winter-products": export_winter_products, "winter-camp": export_winter_camp, "destinations": export_destinations, "customize": export_customize, "blog": export_blog, "stories": export_stories, "news": export_news, "pricing": export_pricing, "qualifications": export_qualifications, "partners": export_partners, "gallery": export_gallery, "destinations-detail": export_destinations_detail, "selector": export_selector, "courses": export_courses, } def export_modules(db: Session, modules: list[str]) -> dict: if "all" in modules: modules = list(EXPORT_FUNCTIONS.keys()) results = {} for module in modules: fn = EXPORT_FUNCTIONS.get(module) if fn: try: fn(db) results[module] = "success" except Exception as e: results[module] = f"failed: {str(e)}" else: results[module] = "unknown module" return results