# PRD:Novalon 轻量 Headless CMS 建设与内容全量迁移 > 本 PRD 基于 grilling 会话(docs/cms-evaluation.md)与 ADR-0007 整理而成。 ## Problem Statement Novalon 官网目前存在两层脱节: 1. **CMS 数据层已建,但页面未切换**:`src/lib/cms/` 已定义 `ContentModel`、`ContentItem`、`ContentZone`、`MediaAsset`、`AuditLog` 等类型,`/api/cms/*` 与 `/api/admin/*` 路由也已存在,但所有营销页面仍从 `src/lib/constants/*.ts` 读取内容。内容更新必须改代码、走构建、再部署,市场/运营人员无法自主维护。 2. **部署模式口径不一致**:`README.md` 仍称项目为“纯静态网站”,但 `next.config.mjs` 已移除 `output: 'export'` 并存在 `/api/cms/revalidate`、`/api/cms/draft/*` 等假设服务端运行的路由。团队对是否采用混合渲染(SSR/ISR)缺乏统一决策,导致 CMS 的实时预览、一键发布、版本回滚等能力无法落地。 3. **权限与工作流未实现**:类型中虽有 `ContentStatus` 和 `AuditLog`,但缺少 RBAC 中间件、角色模型和“提交 → 审核 → 发布”的状态流转,`review` 状态实际是死状态。 4. **媒体管理停留在类型层面**:`MediaAsset` 预留了 `local | oss | s3` 枚举,但无上传接口、无缩略图/格式转换、无 OSS 对接,媒体文件仍散落在 `public/`。 结果是:官网内容运营效率低、协作流程不规范、CMS 投资无法产生实际价值。 ## Solution 在现有 `src/lib/cms/` + Prisma + Next.js API 基础上,建设一个**字段可配置、RBAC 模型+操作级、多语言预留、支持本地+OSS 双写、具备最简工作流**的轻量 Headless CMS。配合已确认的**混合渲染(SSR/ISR)**部署模式,实现内容实时发布、草稿预览、版本回滚。 内容按页面类型**全量迁移**到 CMS:legal → news → team → cases → services → solutions → products → standalone-products → homepage zones,逐步废弃对应 `src/lib/constants/*.ts` 数据。 ## User Stories ### 系统管理员 1. 作为系统管理员,我希望为不同用户分配角色(super_admin / content_admin / content_editor / reviewer / readonly),以便控制谁能操作哪些内容。 2. 作为系统管理员,我希望按“角色 × 内容模型 × 操作(create/read/update/delete/publish)”配置权限矩阵,以便实现“编辑能写新闻但不能发布产品”这类需求。 3. 作为系统管理员,我希望在管理后台查看操作日志(AuditLog),以便追踪内容变更与状态流转。 4. 作为系统管理员,我希望为内容模型启用/禁用版本控制,以便灵活控制哪些模型需要保留历史版本。 ### 内容编辑 5. 作为内容编辑,我希望在管理后台创建和编辑新闻、案例、团队、产品、方案、服务、法律页等内容,而不需要修改代码。 6. 作为内容编辑,我希望将内容保存为草稿,以便在正式发布前反复修改。 7. 作为内容编辑,我希望将完成的内容提交审核,以便进入发布流程。 8. 作为内容编辑,我希望收到审核结果通知(站内消息),以便知晓内容是否通过或需要修改。 9. 作为内容编辑,我希望在内容被驳回后能看到驳回原因,并重新编辑提交。 10. 作为内容编辑,我希望在媒体库上传图片并自动获得缩略图/WebP/AVIF 派生格式链接,以便在内容中引用优化后的图片。 11. 作为内容编辑,我希望为同一个内容条目预留多语言字段(当前默认 zh-CN),以便未来扩展其他语言时无需重构。 ### 内容审核员 12. 作为审核员,我希望在管理后台看到所有待审核内容的列表和 badge,以便快速定位需要处理的内容。 13. 作为审核员,我希望查看内容的完整版本差异(before/after),以便做出准确的审核判断。 14. 作为审核员,我希望通过或驳回待审核内容,并填写审核意见,以便内容进入发布或回退到编辑状态。 15. 作为审核员,我希望只有具备 publish 权限的人才能将内容发布到线上,以便保证内容质量。 ### 网站访客 16. 作为网站访客,我希望在内容发布后即刻看到最新内容(通过 ISR),而无需等待全站构建。 17. 作为网站访客,我希望访问的法律条款、隐私政策等页面始终是最新版本,以便获取准确信息。 18. 作为网站访客,我希望在移动端和桌面端都能正常浏览 CMS 驱动的内容,以便获得一致的响应式体验。 ### 开发者/运维 19. 作为开发者,我希望 CMS API 返回类型安全的数据结构,以便前端组件可靠渲染。 20. 作为开发者,我希望内容发布后通过 `/api/cms/revalidate` 刷新 ISR 缓存,以便前端页面自动更新。 21. 作为开发者,我希望草稿预览通过 Draft Mode 实现,以便编辑和审核人员在正式发布前验证页面效果。 22. 作为运维,我希望媒体文件在开发/测试环境存储在本地,在生产环境写入 OSS/S3,以便降低本地开发成本并保证生产性能。 23. 作为运维,我希望 Nginx 配置正确代理 SSR/ISR/API 请求到 Next.js 服务,以便混合渲染架构正常运行。 24. 作为开发者,我希望为权限中间件、数据访问层、API 路由编写单元和集成测试,以便在持续迭代中防止回归。 ## Implementation Decisions ### 1. 部署模式:混合渲染(SSR/ISR) - 营销展示页面默认使用 SSG + ISR,CMS 内容发布后调用 `/api/cms/revalidate` 刷新缓存。 - 管理后台、认证、CMS API、联系表单等使用 SSR/API Routes。 - 草稿预览通过 Next.js Draft Mode 实现,由 `/api/cms/draft/enable` 与 `/api/cms/draft/disable` 控制。 - 该决策已记录于 ADR-0007。 ### 2. CMS 路线:坚持自研轻量 Headless CMS - 基于现有 `src/lib/cms/` + Prisma + Next.js API 继续建设。 - 不引入 Strapi、Payload、Directus 等开源方案,也不采购 SaaS CMS。 - 核心范围限定为:数据模型扩展、RBAC、媒体管理、最简工作流、内容迁移、管理后台界面。 ### 3. 数据模型扩展 #### 3.1 `ContentItem` 增加 `locale` 字段 - 默认值为 `'zh-CN'`。 - 为存在 `slug` 的模型增加唯一索引 `(slug, modelCode, locale)`,保证同一模型同语言下 slug 唯一。 - 多语言内容通过新增 `locale` 记录实现,不改动现有字段结构。 #### 3.2 RBAC 数据模型 - `Role`:super_admin、content_admin、content_editor、reviewer、readonly。 - `Permission`:角色 × 内容模型 × 操作(create / read / update / delete / publish)。 - 用户与角色多对多关联。 - 在 `/api/admin/*` 路由增加权限中间件,拒绝未授权请求。 权限矩阵示例(来自 grilling 决策): | 角色 | news | product | legal | |------|------|---------|-------| | content_editor | create/read/update | read | read | | reviewer | read/publish | read/publish | read/publish | | readonly | read | read | read | #### 3.3 `review` 状态语义 - `review` = 已提交待审核。 - 仅拥有对应模型 `publish` 权限的角色可以审核通过(进入 `published`)或驳回(回到 `draft`)。 - 状态流转:draft → review → published → archived;draft → review → draft(驳回)。 ### 4. 媒体管理模块 - 完成 `/api/admin/media` 上传接口。 - 本地环境/测试落盘到 `public/uploads/`;生产环境写入 OSS/S3。 - 上传后生成缩略图、WebP、AVIF 派生格式;`MediaAsset` 记录原图与所有派生格式 URL。 - 存储类型枚举沿用现有 `local | oss | s3`。 ### 5. 工作流引擎(最简版) - 状态机仅支持 `draft → review → published / archived`,驳回回 `draft`。 - 站内消息通知:审核人收到待审核 badge,提交人收到审核通过/驳回通知。 - `AuditLog` 记录每次状态流转的 before/after、操作人、时间戳。 - 不实现邮件、企业微信、钉钉等外部通知。 ### 6. 内容迁移策略 - 按页面类型全量迁移,顺序为:legal → news → team → cases → services → solutions → products → standalone-products → homepage zones。 - 每个页面类型迁移时,同步废弃 `src/lib/constants/*.ts` 中对应数据,改为从 CMS API 获取。 - 页面侧使用 ISR,发布/回滚后调用 `/api/cms/revalidate`。 - 独立产品(standalone-products)需要单独设计 `standalone-product` 内容模型,支持技术参数、合规认证等字段分组。 ### 7. 管理后台界面 - 模型管理:CRUD 内容模型与字段定义。 - 内容编辑:按模型展示列表与表单,支持草稿、提交审核、发布。 - 媒体库:上传、预览、复制链接、删除。 - 角色权限:角色列表、权限矩阵配置。 - 工作流审核:待审核列表、版本对比、通过/驳回。 - 通知中心:站内消息列表与 badge。 ### 8. API 契约 - 保持现有 `/api/admin/*` 与 `/api/cms/*` 路由结构。 - 新增 `/api/admin/media` 用于媒体上传与管理。 - 新增 `/api/admin/roles` 与 `/api/admin/permissions` 用于 RBAC 配置。 - 新增 `/api/admin/notifications` 用于站内消息。 - `/api/cms/revalidate` 接收 `path` 或 `tag` 参数,刷新 ISR 缓存。 - `/api/cms/draft/*` 继续用于 Draft Mode 启停。 ## Testing Decisions ### 测试原则 - 只测外部行为,不测实现细节。例如:测试“未授权用户不能发布新闻”,而不是测试权限中间件的内部判断逻辑。 - 优先复用现有测试基础设施:Jest 用于单元/集成测试,Playwright 用于 E2E。 ### 测试接缝 1. **数据访问层**:`src/lib/cms/data-server.ts` 中的增删改查函数。 2. **权限中间件**:在 `/api/admin/*` 路由上的授权行为。 3. **API 路由**:`/api/admin/content/*`、`/api/admin/media`、`/api/cms/revalidate` 的响应状态与数据。 4. **管理后台界面**:Playwright 覆盖登录 → 创建草稿 → 提交审核 → 审核通过 → 页面可见的完整流程。 ### 具体测试覆盖 - **单元测试**:RBAC 权限计算、状态机流转、媒体元数据生成、locale 唯一索引校验。 - **集成测试**:带权限中间件的 API 路由、Prisma 事务与回滚、ISR revalidate 调用。 - **E2E 测试**: - 管理员创建角色并分配权限; - 编辑登录后创建新闻草稿并提交审核; - 审核人登录后通过新闻; - 前台新闻页面在 revalidate 后显示新内容; - 未授权用户尝试发布内容被 403。 ### 现有参考 - `src/lib/cms/data-server.test.ts` 已存在 CMS 数据层测试,可扩展覆盖 locale 与 RBAC 相关场景。 - `e2e/` 目录已有 Playwright 配置与测试,可新增 CMS 管理后台流程测试。 ## Out of Scope 1. **字段级权限**:本期只实现模型级 + 操作级 RBAC,字段级权限作为未来扩展预留。 2. **外部通知**:不实现邮件、企业微信、钉钉等工作流通知,仅保留站内消息。 3. **内容日历与任务分配**:不实现排期、指派、协作看板等高级 CMS 功能。 4. **表单/线索统一管理**:联系表单继续由独立 `/api/contact/*` 处理,本期不纳入 CMS。 5. **多语言前端切换**:虽然预留 `locale` 字段,但本期不实现语言切换 UI 与多语言内容录入,仅保证数据模型可扩展。 6. **替换开源/SaaS CMS**:本期坚持自研,不引入 Strapi、Payload、Sanity 等第三方 CMS。 7. **字段级版本历史**:版本控制到内容条目级别,不细化到单个字段。 ## Further Notes - 风险:全量迁移工作量较大,可能挤压 7 月其他任务。缓解措施是按 P0 → P1 → P2 分阶段交付,内容迁移可逐页面进行。 - 风险:混合渲染对 Nginx 配置提出新要求。缓解措施是更新 `nginx-static-production.conf`,确保 `/api/*`、`/admin/*` 及动态路由正确代理到 Next.js 服务。 - 风险:SQLite 高并发写入可能成为瓶颈。当前后台管理并发低,可接受;未来可平滑迁移至 PostgreSQL,数据模型无需改动。 - 参考文档: - [docs/cms-evaluation.md](../../cms-evaluation.md) - [docs/adr/0007-hybrid-rendering-for-cms.md](../../adr/0007-hybrid-rendering-for-cms.md) - [CONTEXT.md](../../../../CONTEXT.md) - [CLAUDE.md](../../../../CLAUDE.md)