Files
novalon-website/docs/adr/0005-global-layout-cms-managed.md
T
张翔 f4b98fb730 feat(cms): integrate global layout with CMS and document decision
- Move site-config and navigation to CMS-backed SiteConfigProvider
- Keep static constants as fallback for dev and degraded modes
- Add ADR-0005 recording the global layout CMS strategy

Refs ADR-0005
2026-07-08 14:46:14 +08:00

7.6 KiB
Raw Blame History

ADR 0005: 全局布局内容 CMS 化——导航、页脚与站点配置

状态

已接受

日期

2026-07-08

上下文

Novalon 网站已逐步实现全站 CMS 化:产品、解决方案、服务、案例、新闻、关于我们、联系我们、法律页面等均已从 CMS 数据库读取内容。然而,全局布局(Header 导航、Footer 页脚信息、站点元信息)的数据来源边界尚未明确记录,存在以下问题:

  1. 营销/运营团队是否能不经过发版就修改导航菜单、页脚联系方式或备案信息?
  2. 如果 CMS 读取失败,全局布局是否应该有兜底?兜底数据的优先级是什么?
  3. 导航下拉菜单中的产品/方案条目应该引用已有 CMS 内容,还是作为独立 JSON blob 维护?

需要决定全局布局中哪些内容由 CMS 管理、哪些由代码控制,以及数据模型如何设计。

决策

1. 全局内容由 CMS 管理

以下全局内容统一纳入 CMS,使用 Prisma + SQLite 中的内容模型存储:

内容 CMS 模型 关键字段 用途
站点基本信息 site-config name, shortName, displayName, slogan, description, founded, location, email, address, icp, police SEO、页脚公司信息、备案、联系方式
主导航与下拉菜单 navigation mainNav, megaDropdown Header 导航、Mega Dropdown、移动端菜单

前端通过 SiteConfigProvidersrc/app/layout.tsx 中从 CMS 读取并注入全局,Header、Footer、MobileMenu 均通过 useSiteConfig() 消费。

2. 内容页面继续由各自 CMS 模型驱动

产品、解决方案、服务、案例、新闻、关于我们、团队、联系我们、法律页面等继续由各自的内容模型(product, solution, service, case-study, news, about-page, team-page, contact-page, legal-page)驱动。导航中链接到这些页面的条目,其 href 必须与代码中定义的路由保持一致。

3. 保留代码层兜底,但 CMS 为唯一真相源

src/app/layout.tsx 在 CMS 读取失败或数据为空时,回退到 src/lib/constants/company.tssrc/lib/constants/navigation.ts 中的静态数据。该 fallback 仅用于:

  • 开发环境未初始化数据库时的本地启动体验
  • 极端异常(数据库不可用)下的降级展示

正常生产环境下,CMS 数据为唯一真相源;不应长期依赖 fallback 作为主要内容来源。

4. 路由定义保留在代码层

URL 路由模式(如 /products/{slug}/solutions/{slug}/contact)由 Next.js 的目录结构和 generateStaticParams 决定,不由 CMS 控制。CMS 只控制“显示哪些链接”以及“链接的文案、顺序、分组”。

5. 当前阶段采用 JSON Blob 模型,未来可演进为引用关系

navigation 模型当前将 mainNavmegaDropdown 存储为 JSON 数组。这是第一阶段的平衡方案,原因是:

  • 实现成本低,能立即满足非技术人员的修改需求
  • 与现有 ContentItem.data JSON 存储机制一致
  • 不阻塞全站 CMS 化的整体进度

未来如果运营团队需要频繁调整导航结构,或需要下拉项自动同步产品/方案的增删,可演进为:

  • nav-item 独立内容模型,支持排序、启用/禁用、权限
  • mega-dropdown-group 模型,支持引用 product/solution/service 条目
  • 在 CMS 管理后台提供可视化菜单编辑器

备选方案

方案 A:全局布局完全静态

Header、Footer、站点信息全部硬编码在 src/lib/constants/ 中。

  • 优点:实现最简单,无 CMS 依赖,无异常风险
  • 缺点
    • 每次修改导航、联系方式、备案信息都需要发版
    • 与“全站 CMS 化”目标冲突
    • 营销/运营团队无法自主更新
  • 否决原因:不满足官方网站长期运营需求,也违反项目已确定的“全站 CMS 化”方向

方案 B:导航条目拆分为独立可引用模型

每个导航项、每个下拉分组都是独立 ContentItem,并通过 reference 字段关联到 product/solution/service

  • 优点
    • 粒度最细,运营体验最好
    • 新增产品时可在 CMS 中直接勾选加入导航
    • 符合 Enterprise CMSContentful/Sanity/Strapi)的最佳实践
  • 缺点
    • 当前 CMS 管理后台尚未建成,reference 字段的编辑体验需要额外开发
    • 实现周期更长,会阻塞当前阶段目标
  • 否决原因:当前阶段过度设计;保留为后续演进方向

方案 C:仅站点信息 CMS 化,导航仍静态

site-config 进 CMS,但 navigation 继续用静态常量。

  • 优点:改动小,页脚信息可随时改
  • 缺点:导航菜单仍是发版瓶颈;导航作为官网最高频变更的全局元素之一,静态化不可接受
  • 否决原因:不满足“全局布局 CMS 化”的完整性要求

理由

选择“JSON Blob 模型的 CMS 化”作为当前方案的核心论据:

  1. 符合行业标准:企业官网普遍使用 Headless CMS 管理 site-config + navigationContentful 的 Site Configuration、Sanity 的 siteConfig、Strapi 的 Global single type 等)
  2. 业务匹配度高Novalon 作为 B2B 企业,导航菜单、联系方式、备案信息需要灵活调整,CMS 化能显著降低运营成本
  3. 工程效率平衡:复用现有 ContentModel/ContentItem 机制,无需新建数据库表或 API,一周内可落地
  4. 与现有架构一致site-confignavigation 模型已存在于 prisma/seed.ts,只需明确其职责边界和 fallback 策略
  5. 可演进:JSON blob 方案不排斥未来拆分为更细粒度的 nav-item 模型,改动范围可控

后果

正面

  • 运营团队可在不发版的情况下修改导航、页脚联系信息、备案号等全局内容
  • 全局布局与内容页面统一使用 CMS 作为真相源,架构一致
  • 新增页面时,只需在 CMS 中添加导航链接即可上线,无需改代码
  • 开发环境未初始化数据库时仍可正常启动(依赖 fallback)

负面

  • CMS 成为全局布局的强依赖:CMS 数据错误会直接影响全站所有页面
  • navigation 以 JSON blob 存储,后台编辑体验不如可视化菜单编辑器友好
  • 需要为 CMS 内容变更设计缓存失效/重新生成策略(ISR/revalidate

风险

  • 风险 1CMS 中误删 site-confignavigation 条目导致全站 fallback 到旧静态数据
    • 缓解措施:seed 脚本保证基础数据存在;生产环境限制删除单例配置
  • 风险 2:导航链接的 href 与代码路由不匹配,导致 404
    • 缓解措施:建立路由检查清单;E2E 测试覆盖所有导航链接可访问性
  • 风险 3:JSON blob 编辑出错导致导航解析失败
    • 缓解措施:在 CMS 管理后台增加 JSON Schema 校验;关键字段必填校验

相关决策

  • ADR-0002: HSI 信息架构与四层叙事模型——导航是 HSI 架构的关键入口
  • docs/superpowers/plans/2026-07-01-novalon-cms-system.md: Novalon CMS 自研系统实现计划
  • docs/cms/api-contract.md: CMS 后端 API 设计规范
  • CONTEXT.md: 全局设计系统、品牌色与导航层级定义

实施状态

  • 创建 site-config 内容模型并填充种子数据
  • 创建 navigation 内容模型并填充种子数据
  • src/app/layout.tsx 从 CMS 读取并注入 SiteConfigProvider
  • HeaderFooterMobileMenu 通过 useSiteConfig() 消费
  • 保留静态 fallback 用于开发环境和异常降级
  • 后续演进:将 megaDropdown JSON blob 拆分为可引用 product/solution/service 的独立模型(可选)