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

148 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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、移动端菜单 |
前端通过 `SiteConfigProvider``src/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.ts``src/lib/constants/navigation.ts` 中的静态数据。该 fallback 仅用于:
- 开发环境未初始化数据库时的本地启动体验
- 极端异常(数据库不可用)下的降级展示
正常生产环境下,CMS 数据为唯一真相源;不应长期依赖 fallback 作为主要内容来源。
### 4. 路由定义保留在代码层
URL 路由模式(如 `/products/{slug}``/solutions/{slug}``/contact`)由 Next.js 的目录结构和 `generateStaticParams` 决定,不由 CMS 控制。CMS 只控制“显示哪些链接”以及“链接的文案、顺序、分组”。
### 5. 当前阶段采用 JSON Blob 模型,未来可演进为引用关系
`navigation` 模型当前将 `mainNav``megaDropdown` 存储为 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` + `navigation`Contentful 的 Site Configuration、Sanity 的 siteConfig、Strapi 的 Global single type 等)
2. **业务匹配度高**Novalon 作为 B2B 企业,导航菜单、联系方式、备案信息需要灵活调整,CMS 化能显著降低运营成本
3. **工程效率平衡**:复用现有 `ContentModel`/`ContentItem` 机制,无需新建数据库表或 API,一周内可落地
4. **与现有架构一致**`site-config``navigation` 模型已存在于 `prisma/seed.ts`,只需明确其职责边界和 fallback 策略
5. **可演进**:JSON blob 方案不排斥未来拆分为更细粒度的 `nav-item` 模型,改动范围可控
## 后果
### 正面
- 运营团队可在不发版的情况下修改导航、页脚联系信息、备案号等全局内容
- 全局布局与内容页面统一使用 CMS 作为真相源,架构一致
- 新增页面时,只需在 CMS 中添加导航链接即可上线,无需改代码
- 开发环境未初始化数据库时仍可正常启动(依赖 fallback)
### 负面
- CMS 成为全局布局的强依赖:CMS 数据错误会直接影响全站所有页面
- `navigation` 以 JSON blob 存储,后台编辑体验不如可视化菜单编辑器友好
- 需要为 CMS 内容变更设计缓存失效/重新生成策略(ISR/revalidate
### 风险
- **风险 1**CMS 中误删 `site-config``navigation` 条目导致全站 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`: 全局设计系统、品牌色与导航层级定义
## 实施状态
- [x] 创建 `site-config` 内容模型并填充种子数据
- [x] 创建 `navigation` 内容模型并填充种子数据
- [x] `src/app/layout.tsx` 从 CMS 读取并注入 `SiteConfigProvider`
- [x] `Header``Footer``MobileMenu` 通过 `useSiteConfig()` 消费
- [x] 保留静态 fallback 用于开发环境和异常降级
- [ ] 后续演进:将 `megaDropdown` JSON blob 拆分为可引用 `product`/`solution`/`service` 的独立模型(可选)