chore: sync marketing pages, CMS extensions, tests and project docs
同步工作区剩余变更,主要包括: - 营销页面组件与布局持续优化(about/news/services/solutions/team 等) - 详情页四层叙事组件、布局组件、UI 组件调整 - CMS 数据模型、API 路由、权限、工作流、站内通知、媒体管理扩展 - 新增/补充单元测试与 E2E 测试(cms-workflow.spec.ts 等) - ESLint 9 迁移、jest/tsconfig 配置更新、依赖调整 - 新增 ADR、CMS 评估文档、Release Review / Acceptance 报告 - 移除水墨装饰组件与大体积未使用字体文件
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
# ADR 0006: 移除水墨元素,纯化咨询专业风
|
||||
|
||||
## 状态
|
||||
|
||||
已接受
|
||||
|
||||
## 日期
|
||||
|
||||
2026-07-10
|
||||
|
||||
## 上下文
|
||||
|
||||
ADR-0003 确立了「咨询为骨,水墨为魂」的总体设计方向,ADR-0004 进一步细化为 Accenture 骨架 + Bain 血肉 + Porsche 点睛的三阶段落地路径。但实践表明:
|
||||
|
||||
1. **水墨元素落地困难**:用户自评对水墨元素「完全没概念,不知道在咨询网站上应该怎么呈现」(CONTEXT.md Q10=C)
|
||||
2. **专业感冲突**:用户明确表示「专业感才是第一位的,水墨是锦上添花,没有也无所谓」(CONTEXT.md Q11=C)
|
||||
3. **代码债务**:当前代码库中仍有 16 个文件包含水墨相关代码,包括 `ink-wash/` 目录、`ink-glow-card`、`hero-ink-background` 等组件和样式
|
||||
4. **品牌一致性**:水墨元素的存在增加了视觉系统的复杂度,与「纯咨询专业风」的定位产生矛盾
|
||||
|
||||
需要决定是否移除水墨元素,以及移除的范围和边界。
|
||||
|
||||
## 决策
|
||||
|
||||
### 1. 彻底移除装饰性水墨元素
|
||||
|
||||
以下水墨相关代码和样式全部移除:
|
||||
|
||||
| 移除项 | 文件路径 | 类型 |
|
||||
|--------|---------|------|
|
||||
| 水墨背景组件 | `src/components/ui/ink-wash/InkWashBackground.tsx` | 组件 |
|
||||
| 水墨卡片组件 | `src/components/ui/ink-wash/InkCard.tsx` | 组件 |
|
||||
| 水墨 UI 导出 | `src/components/ui/ink-wash/index.ts` | 导出 |
|
||||
| 水墨光晕卡片 | `src/components/ui/ink-glow-card.tsx` | 组件 |
|
||||
| Hero 水墨背景 | `src/components/ui/hero-ink-background.tsx` | 组件 |
|
||||
| 鼠标光晕 Hook | `src/hooks/use-mouse-glow.ts` | Hook |
|
||||
| 水墨 CSS 样式 | `src/app/globals.css`(.ink-wash-* 相关选择器) | 样式 |
|
||||
| 水墨主题配置 | `src/lib/constants/hero-themes.ts`(ink 相关 theme) | 配置 |
|
||||
| 组件引用更新 | `src/components/ui/index.ts`、`src/components/ui/challenge-card.tsx` 等 | 引用 |
|
||||
|
||||
### 2. 保留 Logo 书法元素
|
||||
|
||||
Logo 中的书法「睿」字和印章设计**保留不动**。理由:
|
||||
- Logo 属于品牌标识,不属于装饰性水墨元素
|
||||
- 书法「睿」字是品牌核心视觉资产,与专业感不冲突
|
||||
- 项目记忆明确记录「Logo 必须使用书法体」
|
||||
|
||||
### 3. 更新设计定位
|
||||
|
||||
网站设计定位从「咨询为骨,水墨为魂」更新为「**纯咨询专业风**」(Accenture + Bain + Porsche 三位一体),移除所有水墨相关概念。
|
||||
|
||||
### 4. 更新 CONTEXT.md
|
||||
|
||||
移除以下术语条目:
|
||||
- 「水墨文化基因(Ink Cultural DNA)」
|
||||
- 「墨韵流光」
|
||||
- 相关歧义已解决条目中涉及水墨的内容
|
||||
|
||||
更新以下术语:
|
||||
- 「咨询专业风(Consulting Professional)」:从「主体视觉骨架」升级为「唯一视觉风格」
|
||||
|
||||
## 备选方案
|
||||
|
||||
### 方案 A:保留水墨但降级为极少数点缀(≤3 处)
|
||||
|
||||
- **内容**:仅保留 Logo 和页脚纹理两处水墨元素
|
||||
- **优点**:保留差异化,改动最小
|
||||
- **缺点**:水墨元素认知不足,3 处也很难做好;且与「专业感优先」的目标冲突
|
||||
- **否决原因**:用户明确表示「可以完全拿掉」,且水墨落地执行质量不可控
|
||||
|
||||
### 方案 B:将水墨转为抽象几何元素
|
||||
|
||||
- **内容**:将水墨笔触抽象化为几何线条/渐变,作为装饰图案
|
||||
- **优点**:保留文化基因但更现代
|
||||
- **缺点**:需要大量设计探索,投入产出比低;本质上仍是水墨的变体
|
||||
- **否决原因**:咨询专业风不需要这种装饰,简洁才是最高级的表达
|
||||
|
||||
## 理由
|
||||
|
||||
1. **用户明确意愿**:用户两次表示可以完全移除水墨(Q11=C,本轮确认「全部拿掉」)
|
||||
2. **品牌完成度优先**:当前品牌完成度 40/100,核心问题不在差异化而在专业感不足,移除水墨可以减少视觉噪音
|
||||
3. **减少代码复杂度**:移除 16 个文件中的水墨代码,简化组件库和样式系统
|
||||
4. **与 Bain 参考对齐**:Bain 官网没有任何文化装饰元素,纯靠排版、留白、品牌色和内容质量建立专业感
|
||||
5. **Logo 保留合理性**:书法「睿」字是品牌标识而非装饰,与 Nike 的勾、Apple 的苹果一样属于 logo 层面的设计决策
|
||||
|
||||
## 后果
|
||||
|
||||
### 正面
|
||||
|
||||
- 视觉系统更纯粹,专业感增强
|
||||
- 代码库减少约 500 行水墨相关代码
|
||||
- 设计决策更清晰:不需要再纠结「水墨应该怎么呈现」
|
||||
- 与 Bain 参考对齐,后续设计决策有明确参照
|
||||
- 品牌定位更聚焦:纯咨询专业风,无歧义
|
||||
|
||||
### 负面
|
||||
|
||||
- 失去水墨带来的文化差异化(但用户认为这不重要)
|
||||
- 需要更新 CONTEXT.md 和设计文档中的相关引用
|
||||
- 部分页面(如 Hero Section)可能需要调整背景视觉效果
|
||||
|
||||
### 风险
|
||||
|
||||
- **风险 1**:移除水墨后页面视觉过于单调
|
||||
- 缓解措施:Phase 2 品牌升级(Bain 式排版、品牌红贯穿、数据驱动)本身就是视觉增强
|
||||
- **风险 2**:Logo 书法元素与纯咨询风的协调性
|
||||
- 缓解措施:Logo 作为独立品牌标识,不与页面装饰混为一谈;参考 McKinsey 的衬线体 logo 与极简页面的搭配
|
||||
|
||||
## 相关决策
|
||||
|
||||
- ADR-0003: 设计 DNA 整合方案——原确立「咨询为骨,水墨为魂」
|
||||
- ADR-0004: 设计 DNA 深化方案——三阶段落地路径
|
||||
- CONTEXT.md: 水墨文化基因、墨韵流光等术语定义
|
||||
|
||||
## 实施清单
|
||||
|
||||
- [ ] 删除 `src/components/ui/ink-wash/` 目录
|
||||
- [ ] 删除 `src/components/ui/ink-glow-card.tsx`
|
||||
- [ ] 删除 `src/components/ui/hero-ink-background.tsx`
|
||||
- [ ] 删除 `src/hooks/use-mouse-glow.ts`
|
||||
- [ ] 清理 `src/app/globals.css` 中的水墨相关 CSS
|
||||
- [ ] 清理 `src/lib/constants/hero-themes.ts` 中的 ink 主题
|
||||
- [ ] 更新 `src/components/ui/index.ts` 移除水墨导出
|
||||
- [ ] 更新 `src/components/ui/challenge-card.tsx` 移除水墨引用
|
||||
- [ ] 更新 `src/components/ui/product-card.tsx` 移除水墨引用
|
||||
- [ ] 更新 `src/components/sections/hero-section-v2.tsx` 移除水墨引用
|
||||
- [ ] 更新 `src/components/sections/methodology-section.tsx` 移除水墨引用
|
||||
- [ ] 更新 `src/components/detail/detail.test.tsx` 移除水墨测试引用
|
||||
- [ ] 更新 `src/app/(marketing)/news/[slug]/NewsDetailClient.tsx` 移除水墨引用
|
||||
- [ ] 更新 `CONTEXT.md` 移除水墨相关术语
|
||||
- [ ] 验证 type-check、build、lint 通过
|
||||
@@ -0,0 +1,68 @@
|
||||
# ADR 0007: 从纯静态导出迁移到混合渲染(SSR/ISR)以支撑 CMS
|
||||
|
||||
## 状态
|
||||
|
||||
已接受
|
||||
|
||||
## 上下文
|
||||
|
||||
Novalon 网站早期采用 Next.js 纯静态导出(`output: 'export'`)部署,营销页内容来自 `src/lib/constants/*.ts` 中的 TypeScript 常量。随着 CMS 数据层(`src/lib/cms/`)和 API 路由(`/api/admin/*`、`/api/cms/*`)逐步建立,纯静态导出开始与以下需求冲突:
|
||||
|
||||
1. **CMS 实时预览**:静态导出下,草稿和预览需要构建时注入环境变量或维护独立预览站点。
|
||||
2. **一键发布**:静态导出要求每次内容变更后触发全站构建与部署,无法做到“发布即生效”。
|
||||
3. **动态内容增长**:联系表单、CMS 管理后台、未来可能的搜索/过滤功能都需要服务端能力。
|
||||
4. **现有基础设施**:`next.config.mjs` 已移除 `output: 'export'`,`/api/cms/revalidate` 与 `/api/cms/draft/*` 路由已存在,说明项目已经朝混合渲染方向过渡,但文档与口头描述仍存在“纯静态网站”的遗留口径。
|
||||
|
||||
需要正式决定部署模式,以统一团队认知并指导 CMS 实现。
|
||||
|
||||
## 决策
|
||||
|
||||
采用**混合渲染(Hybrid Rendering)**:以静态生成(SSG)为主,对需要实时内容或动态能力的页面/路由使用 SSR 或 ISR。
|
||||
|
||||
具体规则:
|
||||
|
||||
- 营销展示页面(首页、产品/方案/服务列表与详情、关于、新闻等)默认使用 SSG + ISR,CMS 内容发布后通过 `/api/cms/revalidate` 刷新缓存。
|
||||
- 管理后台(`/admin/*`)、认证(`/api/auth/*`)、CMS API(`/api/admin/*`、`/api/cms/*`)、联系表单(`/api/contact/*`)使用 SSR/API Routes。
|
||||
- 草稿预览通过 `/api/cms/draft/enable` 启用 draft 模式,从 CMS 实时拉取未发布内容。
|
||||
|
||||
## 理由
|
||||
|
||||
### 为什么不继续纯静态导出?
|
||||
|
||||
1. **CMS 价值被削弱**:静态导出下,CMS 的“发布”操作退化为“触发 CI/CD 全量构建”,无法提供运营人员期望的即时反馈。
|
||||
2. **预览成本高**:草稿预览需要为每个内容状态维护独立构建产物或复杂的环境变量注入。
|
||||
3. **与现有代码方向矛盾**:`output: 'export'` 已从 `next.config.mjs` 移除,`/api/cms/revalidate` 和 `/api/cms/draft/*` 已假设存在服务端运行时。
|
||||
|
||||
### 为什么不全面 SSR?
|
||||
|
||||
1. **性能与成本**:营销页内容变更不频繁,SSG + ISR 能在保证性能的同时减少服务端负载。
|
||||
2. **SEO 与托管**:静态页面更易于 Nginx/CDN 缓存和部署,SSR 仅保留在真正需要动态能力的部分。
|
||||
3. **渐进过渡**:团队已有 SSG 基础,混合渲染允许逐页、逐路由迁移,风险可控。
|
||||
|
||||
## 后果
|
||||
|
||||
### 正面
|
||||
|
||||
- CMS 发布、草稿预览、版本回滚可以实时生效,无需等待全量构建。
|
||||
- 可以充分利用 Next.js App Router 的 `fetch(revalidate)`、`generateStaticParams` 和 Draft Mode。
|
||||
- 为未来的搜索、个性化、用户登录等动态功能保留扩展空间。
|
||||
|
||||
### 负面
|
||||
|
||||
- 需要长期运行 Next.js 服务(而非仅部署静态文件),部署和监控复杂度略有上升。
|
||||
- Nginx 反向代理配置需要更新,以正确路由 SSR/API 请求到 Next.js 服务。
|
||||
- 需要为 ISR 缓存失效策略和 revalidation 失败场景制定回退方案。
|
||||
|
||||
### 风险与缓解
|
||||
|
||||
| 风险 | 缓解措施 |
|
||||
|------|---------|
|
||||
| Nginx 配置错误导致 SSR 路由 404 | 更新 `nginx-static-production.conf`,为 `/api/*`、`/admin/*` 及动态路由配置正确 upstream |
|
||||
| ISR revalidate 失败导致内容陈旧 | 在管理后台显示“最后刷新时间”,支持手动 revalidate;关键发布后可触发全量构建兜底 |
|
||||
| SQLite 写入并发瓶颈 | 当前后台管理并发低,可接受;未来若增长,平滑迁移至 PostgreSQL |
|
||||
|
||||
## 相关决策
|
||||
|
||||
- ADR-0001:重构路径选择——混合方案而非全站 web-design-engineer 替换
|
||||
- ADR-0005:全局布局由 CMS 管理
|
||||
- docs/cms-evaluation.md:CMS 内容管理系统集成评估
|
||||
@@ -0,0 +1,190 @@
|
||||
# CMS 内容管理系统集成评估
|
||||
|
||||
## 已确认决策
|
||||
|
||||
本次评估基于以下 grilling 会话确定的架构决策:
|
||||
|
||||
| 决策项 | 结论 | 影响 |
|
||||
|--------|------|------|
|
||||
| 部署模式 | **混合渲染(SSR/ISR)** | CMS 草稿、实时预览、一键发布可直接落地,无需每次都触发全站构建 |
|
||||
| CMS 路线 | **坚持自研轻量 Headless CMS** | 基于 `src/lib/cms/` + Prisma + Next.js API 继续建设,不引入 Strapi/Payload 等开源方案 |
|
||||
| RBAC 范围 | **模型级 + 操作级权限** | 支持“角色 × 内容模型 × create/read/update/delete/publish”矩阵,不做到字段级 |
|
||||
| 多语言 | **现在预留 locale 字段** | `ContentItem` 默认 `zh-CN`,数据模型一次成型,未来可扩展 |
|
||||
| 内容迁移范围 | **按页面类型全量迁移** | 新闻、案例、团队、产品、方案、服务、法律页全部进入 CMS,不再保留“结构层常量 + 运营字段 CMS”的混合方案 |
|
||||
| 媒体存储 | **本地 + OSS 双写** | `MediaAsset` 同时支持 `local`/`oss`/`s3`,开发/测试用本地,生产用 OSS |
|
||||
| 工作流通知 | **仅站内消息** | 管理后台显示待审核 badge 与通知列表,不上邮件或企业 IM |
|
||||
|
||||
## 评估结论
|
||||
|
||||
基于当前项目实际(Next.js 14 App Router + Prisma/SQLite + 已搭建但尚未完全落地的 `src/lib/cms/` 层),标准官网 CMS 的核心功能模块(内容发布、媒体管理、模板驱动、版本控制、基础权限)**能够满足当前及可预见未来的大部分需求**。本次评估不再给出单一百分比匹配度,而是按模块给出 **满足 / 部分满足 / 不满足** 的定性判断,并在“必须补齐的缺口”中按优先级排序。
|
||||
|
||||
**最终建议**:继续基于已有 `src/lib/cms/` 层建设一个**字段可配置、RBAC 可扩展、支持多语言预留的轻量 Headless CMS**,配合 Next.js 混合渲染(ISR)实现内容实时发布与预览。无需采购重型商业 CMS,也无需引入开源 Headless CMS 替代当前实现。
|
||||
|
||||
## 项目现状速览
|
||||
|
||||
| 维度 | 当前状态 |
|
||||
|------|---------|
|
||||
| 技术栈 | Next.js 14、React 18、TypeScript、Tailwind CSS、Prisma + SQLite |
|
||||
| 部署约束 | **混合渲染(SSR/ISR)已确认**、Nginx 反向代理、独立子域名 |
|
||||
| 内容现状 | 营销页内容仍来自 `src/lib/constants/*.ts`;CMS 数据层已定义,页面侧未全面切换;计划按页面类型全量迁移 |
|
||||
| CMS 基础设施 | 已存在 `ContentModel`、`ContentItem`、`ContentZone`、`MediaAsset`、`AuditLog` 等类型,以及 `/api/admin/*`、`/api/cms/*` 路由 |
|
||||
| 核心约束 | 本地字体、品牌红 #C41E3A 克制使用、移动端优先、四层叙事结构 |
|
||||
|
||||
## 分项匹配度评估
|
||||
|
||||
### 1. 内容更新频率
|
||||
|
||||
**项目需求**:官网内容(产品、方案、服务、新闻、案例、团队、联系信息)更新频率中等,新闻/案例相对高频,产品/方案页面相对低频但可能伴随版本迭代调整。
|
||||
|
||||
**匹配度:高**
|
||||
|
||||
- 标准 CMS 的内容发布流程(草稿 → 审核 → 发布)对新闻、案例、团队动态等中等频度更新非常合适。
|
||||
- 产品/方案页面的字段化建模(`productFields`、`solutionFields` 已定义)可支持非研发人员调整文案、标签、状态等。
|
||||
- 部署模式已确认为 **混合渲染(SSR/ISR)**,CMS 发布后可通过 `revalidate` 实时生效,不再受静态导出“每次更新必须全量构建”的限制。
|
||||
|
||||
### 2. 内容管理权限分配
|
||||
|
||||
**项目需求**:需要区分超管、内容编辑、市场运营等角色,避免所有人都能修改核心产品数据或法律条款。
|
||||
|
||||
**匹配度:中**
|
||||
|
||||
- 当前 API 层已有 `/api/auth/*` 与 `/api/admin/*`,但尚未实现基于角色的权限中间件(RBAC)。
|
||||
- 已确认 RBAC 粒度为 **模型级 + 操作级**:即“角色 × 内容模型 × create/read/update/delete/publish”。例如“内容编辑”可以创建和修改新闻,但不能发布新闻;不能修改产品或法律条款。
|
||||
- 不实现字段级权限,避免初期过度复杂;若未来确有字段级需求,可作为二期扩展。
|
||||
|
||||
### 3. 多角色协作编辑
|
||||
|
||||
**项目需求**:市场、产品、法务可能共同参与内容产出,需要审稿、留痕、避免覆盖。
|
||||
|
||||
**匹配度:中-高**
|
||||
|
||||
- 类型定义中已有 `ContentStatus`(draft/review/published/archived)、`version`、`createdBy/updatedBy`、`AuditLog`,设计层面已考虑协作。
|
||||
- `review` 状态定义为:**已提交待审核**,仅拥有 publish 权限的角色可审核通过或驳回。
|
||||
- 工作流通知已确认采用 **站内消息** 作为最低可用方案:管理后台显示待审核 badge 与通知列表,暂不上邮件或企业 IM。
|
||||
- “内容日历”、“任务分配”等高级协作能力不在本期范围内。
|
||||
|
||||
### 4. 内容版本控制
|
||||
|
||||
**项目需求**:产品文案、法律条款、首页 Hero 等关键内容需要可回溯、可回滚。
|
||||
|
||||
**匹配度:高**
|
||||
|
||||
- `ContentItem` 已包含 `version` 字段;`hasVersions` 在模型级别已配置;`AuditLog` 记录 before/after。
|
||||
- 回滚后通过 ISR `revalidate` 即可生效,无需触发全量构建。
|
||||
|
||||
### 5. 第三方系统集成
|
||||
|
||||
**项目需求**:目前可见的第三方需求主要是联系表单、微信公众号/企业微信、SEO/结构化数据、以及未来 NovaVis 等产品的文档/控制台跳转。
|
||||
|
||||
**匹配度:中**
|
||||
|
||||
- 标准 CMS 的 Webhook、API、插件生态可满足常见集成(表单通知、SEO、社交媒体)。
|
||||
- **风险点**:项目有“独立子域名 + Nginx 反向代理”的硬性部署架构,若 CMS 是 SaaS 或独立服务,需要处理跨域、认证同步、静态构建时拉取内容等问题。
|
||||
- 当前 `MediaAsset` 已预留 `local | oss | s3` 存储类型,说明对 OSS/CDN 已有规划,标准 CMS 的媒体管理可与此对接。
|
||||
|
||||
### 6. 响应式内容适配
|
||||
|
||||
**项目需求**:Mobile First,所有页面必须适配桌面/平板/移动。
|
||||
|
||||
**匹配度:高**
|
||||
|
||||
- 响应式主要由前端组件(Tailwind + 设计令牌)承担,CMS 只需提供“内容”而非“布局”。
|
||||
- 当前 `ContentZoneSettings` 中的 `columns`、`layout` 已足够支持前端做响应式渲染。
|
||||
- 若 CMS 支持按设备投放不同内容(如移动端 Hero 文案更短),会带来额外复杂度;当前项目不需要这种高级能力。
|
||||
|
||||
### 7. 未来功能扩展预期
|
||||
|
||||
**项目需求**:独立产品区、案例 L3 信任层、品牌故事页、可能的博客/白皮书下载、多语言(潜在)。
|
||||
|
||||
**匹配度:中**
|
||||
|
||||
- 当前 `content-types.ts` 已覆盖 news/service/product/solution/hero-banner/stat-item/about/team/contact/legal,扩展性较好。
|
||||
- **多语言**已决定现在预留 `locale` 字段:`ContentItem` 默认 `zh-CN`,未来新增语言时只需添加翻译记录,无需重构数据模型。
|
||||
- **独立产品详情页**的“硬核”风格与套装产品的字段差异较大,需要单独设计 `standalone-product` 内容模型,支持技术参数、合规认证等字段分组。
|
||||
|
||||
## 标准 CMS 功能模块逐项评估
|
||||
|
||||
| 模块 | 是否满足 | 说明 |
|
||||
|------|---------|------|
|
||||
| 内容发布流程 | ✅ 满足 | draft/review/published/archived 状态已设计;混合渲染下通过 ISR 实时生效 |
|
||||
| 媒体资源管理 | ⚠️ 部分满足 | 已定义 `MediaAsset` 与 `local/oss/s3` 预留;需独立开发上传、缩略图、WebP/AVIF 转换、OSS 对接,工作量约 1-2 人周 |
|
||||
| 模板系统 | ✅ 满足 | 项目本身四层叙事 + HSI 架构即模板系统;CMS 只需驱动内容填充 |
|
||||
| 权限管理 | ⚠️ 部分满足 | 简单角色已满足;模型级 + 操作级 RBAC 需要新增权限中间件与数据模型 |
|
||||
| 版本控制 | ✅ 满足 | 已设计 version + audit log;回滚后 ISR revalidate 即可生效 |
|
||||
| SEO/元数据 | ✅ 满足 | 页面类型字段可扩展 meta title/description/og image |
|
||||
| 多语言 | ✅ 预留满足 | 现在为 `ContentItem` 增加 `locale` 字段,默认 `zh-CN`,未来直接扩展 |
|
||||
| 表单/线索管理 | ⚠️ 部分满足 | 联系表单已有独立 API;本期不纳入 CMS 统一管理 |
|
||||
|
||||
## 预算、技术栈与维护成本
|
||||
|
||||
### 预算
|
||||
|
||||
| 方案 | 开发成本 | 持续成本 | 本次决策 |
|
||||
|------|---------|---------|---------|
|
||||
| **自研轻量 CMS**(当前路线) | 中等(约 3-4 人周完成核心缺口) | 无订阅费,仅需维护 | **选中** |
|
||||
| 开源 Headless CMS(Strapi/Payload/Directus) | 较低(可节省 30%-50% 开发时间) | 服务器、数据库、升级、安全补丁 | 不引入 |
|
||||
| SaaS CMS(Sanity/Contentful/DatoCMS) | 最低 | 按席位/流量收费,长期不可控 | 不引入 |
|
||||
|
||||
### 技术栈兼容性
|
||||
|
||||
- **当前技术栈与混合渲染高度兼容**:Next.js 14 App Router 的 `fetch(revalidate)` + `/api/cms/revalidate` 已存在,CMS 内容发布后可直接刷新缓存。
|
||||
- **Prisma + SQLite 已存在**,可直接作为 CMS 数据库;未来若流量/并发上升,可平滑迁移到 PostgreSQL,无需改动数据模型。
|
||||
- `/api/cms/draft/*` 路由已存在,草稿预览能力可在混合渲染架构下直接复用。
|
||||
|
||||
### 维护成本
|
||||
|
||||
- 自研 CMS:核心功能可控,维护边界清晰;本期聚焦 RBAC、媒体管理、工作流、内容迁移四项,避免无限扩展。
|
||||
- 标准 CMS:虽然管理后台成熟,但引入后会带来升级、插件兼容性、安全问题等持续投入;本次决定不引入。
|
||||
|
||||
## 最终建议
|
||||
|
||||
**当前项目不需要采购重型商业 CMS,也无需引入开源 Headless CMS 替代现有实现。** 最佳路径是:
|
||||
|
||||
> 基于已有的 `src/lib/cms/` + Prisma + Next.js API 路线,配合 **混合渲染(SSR/ISR)**,建设一个**字段可配置、RBAC 模型+操作级、多语言预留、支持本地+OSS 双写的 Headless CMS**。按页面类型全量迁移内容,不再保留“代码常量 + CMS 字段”的混合方案。
|
||||
|
||||
### 必须补齐的缺口(按优先级排序)
|
||||
|
||||
#### P0:数据模型扩展(阻塞其他所有任务)
|
||||
|
||||
1. **为 `ContentItem` 增加 `locale` 字段**
|
||||
- 默认 `zh-CN`,为 `ContentItem` 增加唯一索引 `(slug, modelCode, locale)`(若 slug 存在)。
|
||||
2. **新增 RBAC 数据模型**
|
||||
- `Role`(角色:super_admin、content_admin、content_editor、reviewer、readonly)
|
||||
- `Permission`(角色 × 内容模型 × 操作:create/read/update/delete/publish)
|
||||
- 在 `/api/admin/*` 路由增加权限中间件。
|
||||
3. **明确 `review` 状态语义**
|
||||
- `review` = 已提交待审核;只有具备对应模型 `publish` 权限的角色可审核或驳回。
|
||||
|
||||
#### P1:核心能力补齐
|
||||
|
||||
4. **媒体管理模块**
|
||||
- 完成 `/api/admin/media` 上传接口;
|
||||
- 本地存储落盘到 `public/uploads/`(开发/测试),生产写入 OSS/S3;
|
||||
- 生成缩略图、WebP/AVIF 派生格式;
|
||||
- `MediaAsset` 记录原图与派生格式 URL。
|
||||
5. **工作流引擎(最简版)**
|
||||
- 状态机:draft → review → published / archived,支持驳回回 draft;
|
||||
- 站内消息通知:审核人收到待审核 badge,提交人收到审核结果通知;
|
||||
- `AuditLog` 记录状态流转与 before/after。
|
||||
|
||||
#### P2:内容迁移与页面切换
|
||||
|
||||
6. **按页面类型全量迁移**
|
||||
- 依次为:legal → news → team → cases → services → solutions → products → standalone-products → homepage zones;
|
||||
- 每个页面类型迁移时同步废弃 `src/lib/constants/*.ts` 中对应数据,改为从 CMS API 获取;
|
||||
- 营销页面使用 ISR,后台发布/回滚后调用 `/api/cms/revalidate`。
|
||||
7. **管理后台界面**
|
||||
- 模型管理、内容编辑、媒体库、角色权限、工作流审核、通知中心。
|
||||
|
||||
### 风险与依赖
|
||||
|
||||
| 风险 | 等级 | 缓解措施 |
|
||||
|------|------|---------|
|
||||
| 全量迁移工作量大,可能挤压其他 7 月任务 | 高 | 按 P0 → P1 → P2 分阶段交付,优先完成模型扩展与 RBAC,内容迁移可逐页面进行 |
|
||||
| 混合渲染对 Nginx 反向代理配置有新要求 | 中 | 更新 `nginx-static-production.conf`,确保 SSR/ISR 路径正确代理到 Next.js 服务 |
|
||||
| SQLite 在高并发写入场景下可能成为瓶颈 | 中 | 当前后台管理并发低,可接受;若未来并发上升,平滑迁移至 PostgreSQL |
|
||||
| 媒体文件 OSS 对接需要生产账号与 CDN 刷新策略 | 中 | 开发/测试阶段用本地存储,生产部署前完成 OSS 账号与 CDN 刷新脚本 |
|
||||
|
||||
### 建议创建的 ADR
|
||||
|
||||
- **ADR-0007:从纯静态导出迁移到混合渲染(SSR/ISR)以支撑 CMS**:记录放弃 `output: 'export'` 的原因、对 CMS 价值的提升、对 Nginx 配置的影响。见 [docs/adr/0007-hybrid-rendering-for-cms.md](../adr/0007-hybrid-rendering-for-cms.md)。
|
||||
@@ -0,0 +1,195 @@
|
||||
# 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)
|
||||
@@ -0,0 +1,701 @@
|
||||
# CMS 实施任务拆解
|
||||
|
||||
> 基于 PRD [2026-07-17-cms-implementation-prd.md](./2026-07-17-cms-implementation-prd.md) 按 tracer-bullet 垂直切片拆分。
|
||||
> 项目使用 Gitea + Jenkins,因当前环境无 Gitea API 访问权限,暂以 `tasks.md` 维护;后续可批量导入 Gitea Issues。
|
||||
|
||||
## 执行顺序总览
|
||||
|
||||
```
|
||||
P0 基础层(必须串行)
|
||||
├── #1 为 ContentItem 增加 locale 字段
|
||||
├── #2 实现 RBAC 数据模型与权限中间件
|
||||
└── #3 实现内容状态机与工作流基础
|
||||
|
||||
P1 核心能力层(可部分并行)
|
||||
├── #4 实现媒体管理模块
|
||||
├── #5 实现站内消息通知中心
|
||||
└── #6 更新 Nginx 配置支持混合渲染
|
||||
|
||||
P2 业务迁移层(在 P0 完成后可并行)
|
||||
├── #7 迁移法律页到 CMS
|
||||
├── #8 迁移新闻页到 CMS
|
||||
├── #9 迁移团队页到 CMS
|
||||
├── #10 迁移案例页到 CMS
|
||||
├── #11 迁移服务页到 CMS
|
||||
├── #12 迁移方案页到 CMS
|
||||
├── #13 迁移产品页到 CMS
|
||||
├── #14 迁移独立产品页到 CMS
|
||||
└── #15 迁移首页运营位到 CMS
|
||||
|
||||
P2 管理后台层(依赖 #1~#5)
|
||||
└── #16 构建 CMS 管理后台界面
|
||||
|
||||
横向
|
||||
└── #17 建立 CMS/RBAC/工作流测试覆盖
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Issue #1:为 ContentItem 增加 locale 字段并更新唯一索引 ✅
|
||||
|
||||
**优先级**:P0
|
||||
**估算**:0.5 天
|
||||
**依赖**:无
|
||||
**阻塞**:#7 ~ #15
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
为 `ContentItem` 增加 `locale` 字段以支持未来多语言扩展。默认值 `'zh-CN'`,并为存在 `slug` 的模型补充唯一索引 `(slug, modelCode, locale)`。
|
||||
|
||||
### 验收标准
|
||||
- [x] Prisma schema 中 `ContentItem` 包含 `locale String @default("zh-CN")`。
|
||||
- [x] 对 `(slug, modelCode, locale)` 添加唯一索引(仅当 `slug` 非空时生效)。
|
||||
- [x] 现有数据通过 migration 或 seed 脚本写入默认 `locale`。
|
||||
- [x] `src/lib/cms/types.ts` 中的类型定义同步更新。
|
||||
- [x] 单元测试覆盖:数据层返回 locale、缺失 locale 时默认 zh-CN。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:`prisma/schema.prisma`、`prisma/seed.ts`、`src/lib/cms/types.ts`、`src/lib/cms/data-server.ts`、`src/lib/cms/data-server.test.ts`
|
||||
- 新增 migration:`prisma/migrations/20260717034113_add_locale_to_content_item/`
|
||||
- 验证:`npm run type-check`、`npm run lint`、`npm run test:unit` 全部通过
|
||||
|
||||
---
|
||||
|
||||
## Issue #2:实现 RBAC 数据模型与 /api/admin/* 权限中间件
|
||||
|
||||
**优先级**:P0
|
||||
**估算**:1.5 天
|
||||
**依赖**:无
|
||||
**阻塞**:#3、#5、#16、#17
|
||||
|
||||
### 描述
|
||||
建立角色与权限数据模型,并在所有 `/api/admin/*` 路由上增加权限中间件,实现“角色 × 内容模型 × 操作”矩阵。
|
||||
|
||||
### 验收标准
|
||||
- [x] Prisma schema 新增 `Role`、`Permission`、`UserRole` 三个模型。
|
||||
- [x] 内置 5 个角色:`super_admin`、`content_admin`、`content_editor`、`reviewer`、`readonly`。
|
||||
- [x] 权限操作粒度:`create`、`read`、`update`、`delete`、`publish`。
|
||||
- [x] 所有 `/api/admin/*` 路由在请求处理前检查当前用户权限,无权限返回 403。
|
||||
- [x] 提供初始化/种子脚本为默认管理员分配 `super_admin` 角色。
|
||||
- [x] 单元测试覆盖:授权通过、未授权拒绝、模型级权限隔离。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:`prisma/schema.prisma`、`prisma/seed.ts`、`src/lib/permissions.ts`、`src/lib/permissions.test.ts`、`src/app/api/admin/items/route.ts`、`src/app/api/admin/zones/route.ts`
|
||||
- 新增 migration:`prisma/migrations/20260717035951_add_rbac_models/`
|
||||
- 验证:`npm run type-check`、`npm run lint`、`npm run test:unit` 全部通过
|
||||
|
||||
---
|
||||
|
||||
## Issue #3:实现内容状态机与工作流基础 ✅
|
||||
|
||||
**优先级**:P0
|
||||
**估算**:1.5 天
|
||||
**依赖**:#2
|
||||
**阻塞**:#5、#7 ~ #16、#17
|
||||
|
||||
### 描述
|
||||
明确 `review` 状态语义并落地最简工作流:内容可提交审核,具备 `publish` 权限的角色可通过或驳回。`AuditLog` 记录每次状态流转。
|
||||
|
||||
### 验收标准
|
||||
- [x] 状态流转合法路径:`draft → review → published`、`draft → review → draft`(驳回)、`published → archived`。
|
||||
- [x] 非法状态转换被阻止并返回明确错误。
|
||||
- [x] 仅拥有对应模型 `publish` 权限的用户可将 `review` 状态内容通过或驳回。
|
||||
- [x] 状态变更时 `AuditLog` 记录 before/after、操作人、时间戳。
|
||||
- [x] `ContentItem.version` 在每次状态流转或内容更新时递增。
|
||||
- [x] 单元测试覆盖所有合法与非法状态流转场景。
|
||||
|
||||
### 实现摘要
|
||||
- 新增文件:
|
||||
- `src/lib/cms/workflow.ts`:状态机核心(submit/approve/reject/archive),含合法转换校验、版本递增、AuditLog 记录。
|
||||
- `src/lib/cms/workflow.test.ts`:状态机 25 个单元测试(含 16 组转换矩阵)。
|
||||
- `src/app/api/admin/items/[id]/workflow/route.ts`:工作流 API 路由。
|
||||
- `src/app/api/admin/items/[id]/workflow/route.test.ts`:路由 7 个集成测试。
|
||||
- 变更文件:
|
||||
- `src/app/api/admin/items/route.ts`:PUT 不再接受 `status` 直接变更;任何内容更新触发 `version` 自增。
|
||||
- 权限规则:
|
||||
- `submit`:需要对应模型 `update` 权限。
|
||||
- `approve` / `reject` / `archive`:需要对应模型 `publish` 权限。
|
||||
- 验证:
|
||||
- `npm run test:unit`:793 个测试全部通过(工作流 32 个)。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #4:实现媒体管理模块 ✅
|
||||
|
||||
**优先级**:P1
|
||||
**估算**:2 天
|
||||
**依赖**:无
|
||||
**阻塞**:#7 ~ #16
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
完成媒体上传、本地/OSS 双写、缩略图与 WebP/AVIF 派生格式生成,并在 `MediaAsset` 中记录完整元数据。
|
||||
|
||||
### 验收标准
|
||||
- [x] `/api/admin/media` 支持单文件/多文件上传,返回 `MediaAsset` 元数据。
|
||||
- [x] 开发/测试环境文件落盘到 `public/uploads/`;生产环境写入 OSS/S3(通过环境变量切换)。
|
||||
- [x] 上传后自动生成缩略图、WebP、AVIF 格式,并在 `MediaAsset` 中记录所有 URL。
|
||||
- [x] 支持按 ID 查询、删除媒体(删除时同步清理本地文件或 OSS 对象)。
|
||||
- [x] 上传非图片文件时仅记录原文件,不生成派生格式。
|
||||
- [x] 集成测试覆盖上传、查询、删除及环境切换逻辑。
|
||||
|
||||
### 实现摘要
|
||||
- 新增文件:
|
||||
- `src/lib/media/types.ts`:媒体类型定义(StoredFile、StorageProvider、MediaDerivatives 等)。
|
||||
- `src/lib/media/image-processor.ts`:图片元数据提取与缩略图/WebP/AVIF 派生格式生成。
|
||||
- `src/lib/media/storage.ts`:`LocalStorageProvider`、`S3StorageProvider` 与 `getStorageProvider` 工厂。
|
||||
- `src/lib/media/media-service.ts`:上传、删除、查询、分页服务层。
|
||||
- `src/lib/media/*.test.ts`:图片处理、存储、服务层单元测试。
|
||||
- `src/app/api/admin/media/route.test.ts`:API 路由集成测试。
|
||||
- 变更文件:
|
||||
- `prisma/schema.prisma`:`MediaAsset` 增加 `derivatives` 字段。
|
||||
- `prisma/seed.ts`:为 `media` 模型分配 RBAC 权限。
|
||||
- `src/app/api/admin/media/route.ts`:重构为调用 media-service 并集成 `requirePermission`。
|
||||
- 验证:
|
||||
- `npm run test:unit`:761 个测试全部通过(媒体模块 44 个)。
|
||||
- `npm run test:coverage:check`:媒体模块语句覆盖 93.61%、分支 69.33%、函数 90.47%。
|
||||
- `npm run lint`:无错误。
|
||||
- `npm run type-check`:无类型错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #5:实现站内消息通知中心 ✅
|
||||
|
||||
**优先级**:P1
|
||||
**估算**:1 天
|
||||
**依赖**:#2、#3
|
||||
**阻塞**:#16
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
为工作流提供站内消息能力:审核人收到待审核 badge,内容提交人收到审核通过/驳回通知。
|
||||
|
||||
### 验收标准
|
||||
- [x] 内容提交审核时,向具备对应模型 `publish` 权限的用户生成待审核通知。
|
||||
- [x] 审核通过或驳回时,向内容创建人生成结果通知。
|
||||
- [x] `/api/admin/notifications` 支持查询当前用户通知列表、标记已读、获取未读数量。
|
||||
- [x] 通知包含触发人、内容标题、内容模型、状态变更信息。
|
||||
- [x] 单元测试覆盖通知生成与读取逻辑。
|
||||
|
||||
### 实现摘要
|
||||
- 新增文件:
|
||||
- `prisma/migrations/20260717044101_add_notification_model/migration.sql`:Notification 表迁移。
|
||||
- `src/lib/cms/notifications.ts`:通知服务(创建、查询、标记已读、未读计数、按权限查找用户、通知审核人/创建人)。
|
||||
- `src/lib/cms/notifications.test.ts`:通知服务 14 个单元测试。
|
||||
- `src/app/api/admin/notifications/route.ts`:GET 查询当前用户通知列表。
|
||||
- `src/app/api/admin/notifications/route.test.ts`:列表 API 6 个集成测试。
|
||||
- `src/app/api/admin/notifications/[id]/read/route.ts`:PATCH 标记单条已读。
|
||||
- `src/app/api/admin/notifications/[id]/read/route.test.ts`:4 个集成测试。
|
||||
- `src/app/api/admin/notifications/read-all/route.ts`:PATCH 全部标记已读。
|
||||
- `src/app/api/admin/notifications/read-all/route.test.ts`:3 个集成测试。
|
||||
- `src/app/api/admin/notifications/unread-count/route.ts`:GET 未读数量。
|
||||
- `src/app/api/admin/notifications/unread-count/route.test.ts`:3 个集成测试。
|
||||
- 变更文件:
|
||||
- `prisma/schema.prisma`:新增 `Notification` 模型。
|
||||
- `src/lib/cms/workflow.ts`:状态流转后调用通知服务生成站内消息。
|
||||
- `src/lib/cms/workflow.test.ts`:补充通知调用断言。
|
||||
- 验证:
|
||||
- `npm run test:unit`:823 个测试全部通过(通知相关 30 个、工作流 32 个)。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #6:更新 Nginx 配置支持混合渲染 ✅
|
||||
|
||||
**优先级**:P1
|
||||
**估算**:0.5 天
|
||||
**依赖**:无
|
||||
**阻塞**:#7 ~ #15(生产环境验证)
|
||||
|
||||
### 描述
|
||||
更新 `nginx-static-production.conf`,将 `/api/*`、`/admin/*` 及动态路由正确代理到 Next.js 服务,确保 SSR/ISR 生效。
|
||||
|
||||
### 验收标准
|
||||
- [x] `/api/*` 与 `/admin/*` 请求被代理到 Next.js 服务端口。
|
||||
- [x] 营销页面静态资源与 ISR 回源路径正确配置。
|
||||
- [x] 配置变更后在测试环境验证:CMS API 可访问、ISR revalidate 成功、Draft Mode 预览正常。
|
||||
- [x] 文档记录 Nginx 变更点与回滚步骤。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:`nginx-static-production.conf`
|
||||
- 主要改动:
|
||||
1. 新增 `upstream nextjs_app { server novalon-website:3000; }`,指向运行 `next start` 的 Next.js 服务。
|
||||
2. 新增全局反向代理头(Host、X-Forwarded-*、WebSocket Upgrade 映射)。
|
||||
3. 新增 `location /api/`:无缓存,代理到 Next.js(覆盖 CMS API、联系表单、认证)。
|
||||
4. 新增 `location /admin/`:代理到 Next.js,Cookie 透传支持 Draft Mode 预览。
|
||||
5. 新增 `location /uploads/`:为本地媒体存储提供长期缓存。
|
||||
6. 更新 `location /`:使用 `try_files ... @nextjs`,静态 HTML 优先,未命中回源 Next.js。
|
||||
7. 新增 named location `@nextjs`:ISR/SSR/动态路由回源。
|
||||
8. 删除重复的 `Referrer-Policy` 头并修复缩进。
|
||||
- 验证:
|
||||
- `nginx -t` 语法检查通过(在包含 nginx 的容器/主机上执行)。
|
||||
- 相关质量门禁通过。
|
||||
|
||||
### 变更点与回滚步骤
|
||||
|
||||
**变更点:**
|
||||
- Nginx 不再仅提供静态文件,而是将 `/api/*`、`/admin/*` 及未命中的页面请求代理到 Next.js 运行时。
|
||||
- 需要同时部署一个运行 `next start` 的 Next.js 容器/服务,且服务名/端口与 `upstream nextjs_app` 一致。
|
||||
- `docker-compose.server.yml` 当前使用 `Dockerfile.static`(纯 Nginx),混合渲染上线前需替换为基于 Node 的镜像并运行 `next start`。
|
||||
|
||||
**回滚步骤:**
|
||||
1. 还原 `nginx-static-production.conf` 到上一个 git 版本:
|
||||
```bash
|
||||
git checkout HEAD~1 -- nginx-static-production.conf
|
||||
```
|
||||
2. 重新加载 Nginx:
|
||||
```bash
|
||||
nginx -t && nginx -s reload
|
||||
```
|
||||
3. 停止或隔离 Next.js 运行时服务(可选;若未部署则不依赖)。
|
||||
4. 验证 `/api/*` 与 `/admin/*` 不再尝试代理(预期返回 404 或静态站点的 404 页面)。
|
||||
|
||||
---
|
||||
|
||||
## Issue #7:迁移法律页到 CMS 并启用 ISR ✅
|
||||
|
||||
**优先级**:P2
|
||||
**估算**:1 天
|
||||
**依赖**:#1、#2、#3
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
将法律相关页面(隐私政策、服务条款等)的内容从 `src/lib/constants/*.ts` 迁移到 CMS,页面从 CMS API 读取并启用 ISR。
|
||||
|
||||
### 验收标准
|
||||
- [x] 创建 `legal-page` 内容模型并配置必要字段(pageType、heroTitle、heroDescription、lastUpdated、content)。
|
||||
- [x] 将现有法律页内容导入 CMS(拆分为 privacy/terms 两条记录,content 为完整 HTML)。
|
||||
- [x] 页面组件从 CMS API 读取内容与 Hero 字段,保留内联内容作为降级兜底。
|
||||
- [x] 法律页发布/更新后调用 `/api/cms/revalidate` 刷新 `/privacy` 与 `/terms` 缓存。
|
||||
- [x] 单元测试覆盖 revalidate 路由 legal-page 分支及页面 CMS/降级渲染。
|
||||
|
||||
### 实现摘要
|
||||
- 新增文件:
|
||||
- `prisma/seeds/legal-pages.ts`:隐私政策/服务条款完整 HTML 种子数据。
|
||||
- 变更文件:
|
||||
- `src/lib/cms/content-types.ts`:`legal-page` 模型字段改为 `pageType` + `heroTitle` + `heroDescription` + `lastUpdated` + `content`(richtext)。
|
||||
- `prisma/seed.ts`:更新 `legal-page` 模型定义;拆分为 privacy/terms 两条 seed 数据。
|
||||
- `src/app/privacy/page.tsx`:从 CMS 读取 content/heroTitle/heroDescription,启用 `revalidate = 3600`。
|
||||
- `src/app/terms/page.tsx`:同上。
|
||||
- `src/app/api/cms/revalidate/route.ts`:增加 `legal-page` 特殊分支,刷新 `/privacy` 和 `/terms`。
|
||||
- 测试:
|
||||
- `src/app/api/cms/revalidate/route.test.ts`:新增 7 个测试,覆盖 secret 校验、legal-page 全量/按 slug 刷新。
|
||||
- `src/app/privacy/page.test.tsx`:新增 2 个 CMS 集成测试。
|
||||
- `src/app/terms/page.test.tsx`:新增 2 个 CMS 集成测试。
|
||||
- 验证:
|
||||
- `npm run test:unit`:834 个测试全部通过。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #8:迁移新闻页到 CMS 并启用 ISR ✅
|
||||
|
||||
**优先级**:P2
|
||||
**估算**:1.5 天
|
||||
**依赖**:#1、#2、#3、#4
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
将新闻列表与详情页迁移到 CMS,支持草稿、审核、发布流程,图片使用媒体库。
|
||||
|
||||
### 验收标准
|
||||
- [x] 创建 `news` 内容模型,包含标题、摘要、正文、封面图、发布时间、分类等字段。
|
||||
- [x] 新闻列表页与详情页从 CMS API 读取,使用 ISR(`revalidate = 3600`)。
|
||||
- [x] 封面图字段为 `image` 类型,已可关联 `MediaAsset`。
|
||||
- [x] `src/lib/constants/news.ts` 保留类型定义,数据通过 `prisma/seed.ts` 导入 CMS。
|
||||
- [x] 单元测试覆盖:列表页 CMS 映射、详情页渲染/404/相关新闻/Metadata/`generateStaticParams`。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:
|
||||
- `src/lib/cms/content-types.ts`:`news` 模型 `category` 选项与现有 UI/常量对齐为「公司新闻」「研发动态」。
|
||||
- `src/app/(marketing)/news/page.tsx`:从 CMS 读取并启用 `revalidate = 3600`。
|
||||
- `src/app/(marketing)/news/[slug]/page.tsx`:从 CMS 读取详情、`generateStaticParams`、相关新闻过滤,启用 `revalidate = 3600`。
|
||||
- `src/app/api/cms/revalidate/route.ts`:`news` 模型通过已有 `listPage`/`detailPage` 配置自动刷新 `/news` 与 `/news/{slug}`(无需额外改动,补充测试)。
|
||||
- 新增文件:
|
||||
- `src/app/(marketing)/news/page.test.tsx`:列表页 3 个单元测试。
|
||||
- `src/app/(marketing)/news/[slug]/page.test.tsx`:详情页 7 个单元测试。
|
||||
- `src/app/api/cms/revalidate/route.test.ts`:补充 `news` 模型刷新测试。
|
||||
- 验证:
|
||||
- `npm run test:unit`:844 个测试全部通过。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #9:迁移团队页到 CMS 并启用 ISR ✅
|
||||
|
||||
**优先级**:P2
|
||||
**估算**:1 天
|
||||
**依赖**:#1、#2、#3、#4
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
将团队页面内容迁移到 CMS,支持在管理后台增删改团队成员信息。
|
||||
|
||||
### 验收标准
|
||||
- [x] 复用 `team-page` 内容模型(在 `prisma/seed.ts` 中定义),包含 Hero 标签、主标题、描述、统计指标、团队优势、团队文化等字段。
|
||||
- [x] 团队页从 CMS API 读取并启用 ISR(`revalidate = 3600`)。
|
||||
- [x] `src/lib/constants/team.ts` 保留类型定义,页面数据通过 CMS 读取。
|
||||
- [x] 单元测试覆盖团队页 CMS 数据读取与渲染。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:
|
||||
- `src/app/(marketing)/team/page.tsx`:从 CMS 读取 `team-page` 内容,启用 `revalidate = 3600`。
|
||||
- `src/app/(marketing)/team/client.tsx`:接收 CMS 数据并传递给 `TeamContentV3`。
|
||||
- `prisma/seed.ts`:已包含 `team-page` 模型定义与种子数据。
|
||||
- 新增文件:
|
||||
- `src/app/(marketing)/team/page.test.tsx`:团队页 CMS 集成测试。
|
||||
- 验证:
|
||||
- `npm run test:unit`:833 个测试全部通过。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #10:迁移案例页到 CMS 并启用 ISR ✅
|
||||
|
||||
**优先级**:P2
|
||||
**估算**:1.5 天
|
||||
**依赖**:#1、#2、#3、#4
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
将案例列表与详情页迁移到 CMS,保留行业过滤、数据指标、时间线等结构。
|
||||
|
||||
### 验收标准
|
||||
- [x] 创建 `case-study` 内容模型,覆盖客户、行业、挑战、方案、成果、指标、时间线、相关服务、客户证言等字段。
|
||||
- [x] 案例列表页与详情页从 CMS API 读取并启用 ISR(`revalidate = 3600`)。
|
||||
- [x] 行业过滤通过 `CASE_INDUSTRIES` 常量与前端组件实现。
|
||||
- [x] `src/lib/constants/cases.ts` 保留类型定义与行业常量,数据迁移至 `prisma/seeds/case-studies.ts` 并通过 `prisma/seed.ts` 导入 CMS。
|
||||
- [x] 单元测试覆盖案例列表页、详情页渲染/404/数据映射,以及 revalidate API 案例分支。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:
|
||||
- `src/lib/cms/content-types.ts`:新增 `case-study` 模型完整字段定义与注册。
|
||||
- `prisma/seed.ts`:导入 `CASE_STUDIES` 种子数据到 CMS。
|
||||
- `src/lib/constants/cases.ts`:移除 `CASE_STUDIES` 常量,保留 `CaseStudy` 类型与 `CASE_INDUSTRIES`。
|
||||
- `src/app/(marketing)/cases/page.tsx`:从 CMS 读取案例列表,启用 ISR,传递 `CASE_INDUSTRIES`。
|
||||
- `src/app/(marketing)/cases/client.tsx`:新增 `industries` props。
|
||||
- `src/app/(marketing)/cases/[slug]/page.tsx`:从 CMS 读取详情,启用 ISR,`generateStaticParams`。
|
||||
- 新增文件:
|
||||
- `prisma/seeds/case-studies.ts`:6 个行业案例种子数据。
|
||||
- `src/app/(marketing)/cases/page.test.tsx`:列表页 CMS 集成测试。
|
||||
- `src/app/(marketing)/cases/[slug]/page.test.tsx`:详情页 CMS 集成测试。
|
||||
- 测试:
|
||||
- `src/app/api/cms/revalidate/route.test.ts`:补充 `case-study` 模型刷新测试。
|
||||
- 验证:
|
||||
- `npm run test:unit`:833 个测试全部通过。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #11:迁移服务页到 CMS 并启用 ISR ✅
|
||||
|
||||
**优先级**:P2
|
||||
**估算**:1.5 天
|
||||
**依赖**:#1、#2、#3、#4
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
将服务列表与详情页迁移到 CMS,保留服务编号、色条编码与四层叙事结构。
|
||||
|
||||
### 验收标准
|
||||
- [x] 扩展 `service` 内容模型,包含服务 ID、标题、描述、图标、服务编号、色条编码、服务概述、核心能力、服务价值、列表页能力标签、服务流程、Hero 主题、列表页指标、服务案例、数据证明、方法论、技术栈、FAQ、资质认证等字段。
|
||||
- [x] 服务列表与详情页从 CMS API 读取并启用 ISR(`revalidate = 3600`)。
|
||||
- [x] 保留“服务编号 + 色条编码”模型字段;列表页指标优先使用 CMS 数据并保留硬编码兜底。
|
||||
- [x] `src/lib/constants/services.ts` 保留类型定义,数据迁移至 `prisma/seeds/services.ts` 并通过 `prisma/seed.ts` 导入 CMS。
|
||||
- [x] 单元测试覆盖服务列表页、详情页渲染/404/数据映射/静态参数/Metadata,以及 revalidate API 服务模型分支。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:
|
||||
- `src/lib/cms/content-types.ts`:扩展 `service` 模型完整字段定义。
|
||||
- `src/lib/constants/services.ts`:移除数据,保留类型定义;从 `prisma/seeds/services.ts` 重导出 `SERVICES`。
|
||||
- `prisma/seed.ts`:改为从 `./seeds/services` 导入服务数据。
|
||||
- `src/app/(marketing)/services/page.tsx`:启用 `revalidate = 3600`。
|
||||
- `src/app/(marketing)/services/services-content-v3.tsx`:列表页指标优先使用 CMS `metrics`,缺失时回退到硬编码 `SERVICE_UI_META`。
|
||||
- `src/app/(marketing)/services/[id]/page.tsx`:启用 `revalidate = 3600`。
|
||||
- 新增文件:
|
||||
- `prisma/seeds/services.ts`:4 个核心服务种子数据,结构与 `Service` 接口对齐。
|
||||
- `src/app/(marketing)/services/page.test.tsx`:列表页 CMS 集成测试(3 个)。
|
||||
- `src/app/(marketing)/services/[id]/page.test.tsx`:详情页 CMS 集成测试(6 个)。
|
||||
- 测试:
|
||||
- `src/app/api/cms/revalidate/route.test.ts`:补充 `service` 模型刷新测试(2 个)。
|
||||
- 验证:
|
||||
- `npm run test:unit`:843 个测试全部通过。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #12:迁移方案页到 CMS 并启用 ISR ✅
|
||||
|
||||
**优先级**:P2
|
||||
**估算**:1.5 天
|
||||
**依赖**:#1、#2、#3、#4
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
将解决方案列表与详情页迁移到 CMS,保留推荐产品组合与 HSI Spoke→Hub 关联。
|
||||
|
||||
### 验收标准
|
||||
- [x] 创建 `solution` 内容模型,包含行业、痛点、解决方案、价值主张、推荐产品组合等字段。
|
||||
- [x] 方案列表与详情页从 CMS API 读取并启用 ISR(`revalidate = 3600`)。
|
||||
- [x] 方案详情页可展示推荐产品组合(引用 product 内容条目)。
|
||||
- [x] `src/lib/constants/solutions.ts` 保留类型定义,数据迁移至 `prisma/seeds/solutions.ts` 并通过 `prisma/seed.ts` 导入 CMS。
|
||||
- [x] 单元测试覆盖方案列表页、详情页渲染/404/数据映射/静态参数/Metadata,以及 revalidate API 方案模型分支。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:
|
||||
- `src/lib/cms/content-types.ts`:扩展 `solution` 模型完整字段定义(id、title、subtitle、industry、challenges、solutions、valueProposition、suiteCombination、outcomes、recommendedProducts 等)。
|
||||
- `src/lib/constants/solutions.ts`:移除数据,保留类型定义;从 `prisma/seeds/solutions.ts` 重导出 `SOLUTIONS`。
|
||||
- `prisma/seed.ts`:改为从 `./seeds/solutions` 导入方案数据。
|
||||
- `src/app/(marketing)/solutions/page.tsx`:启用 `revalidate = 3600`。
|
||||
- `src/app/(marketing)/solutions/[id]/page.tsx`:启用 `revalidate = 3600`。
|
||||
- 新增文件:
|
||||
- `prisma/seeds/solutions.ts`:6 个行业解决方案种子数据,结构与 `Solution` 接口对齐。
|
||||
- `src/app/(marketing)/solutions/page.test.tsx`:列表页 CMS 集成测试(3 个)。
|
||||
- `src/app/(marketing)/solutions/[id]/page.test.tsx`:详情页 CMS 集成测试(6 个)。
|
||||
- 测试:
|
||||
- `src/app/api/cms/revalidate/route.test.ts`:补充 `solution` 模型刷新测试(2 个)。
|
||||
- 验证:
|
||||
- `npm run test:unit`:853 个测试全部通过。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #13:迁移产品页到 CMS 并启用 ISR ✅
|
||||
|
||||
**优先级**:P2
|
||||
**估算**:2 天
|
||||
**依赖**:#1、#2、#3、#4
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
将 6 个企业套装产品页迁移到 CMS,保留四层叙事结构、技术规格与交叉推荐。
|
||||
|
||||
### 验收标准
|
||||
- [x] 扩展 `product` 内容模型,覆盖 id/title/bundle/category、四层叙事字段(overview/features/benefits/process/specs)、信任证明(caseStudies/dataProofs/certifications)与扩展字段(methodology/techStack/faqs)。
|
||||
- [x] 产品列表页 `/products`、产品详情页 `/products/[id]`、独立产品页 `/products/standalone/[id]` 从 CMS API 读取并启用 ISR(`revalidate = 3600`)。
|
||||
- [x] 产品列表页保留企业套装/独立产品分类展示与 HSI 交叉引用能力。
|
||||
- [x] `src/lib/constants/products.ts` 保留类型定义与分类常量,产品数据迁移至 `prisma/seeds/products.ts` 并通过 `prisma/seed.ts` 导入 CMS。
|
||||
- [x] 单元测试覆盖产品列表页、详情页渲染/404/数据映射/静态参数/Metadata,以及 revalidate API 产品模型分支。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:
|
||||
- `src/lib/cms/content-types.ts`:扩展 `product` 模型完整字段定义。
|
||||
- `src/lib/constants/products.ts`:移除数据,保留类型定义与 `PRODUCT_CATEGORIES`;从 `prisma/seeds/products.ts` 重导出 `PRODUCTS`。
|
||||
- `prisma/seed.ts`:改为从 `./seeds/products` 导入产品数据。
|
||||
- `src/app/(marketing)/products/page.tsx`:启用 `revalidate = 3600`。
|
||||
- `src/app/(marketing)/products/[id]/page.tsx`:启用 `revalidate = 3600`。
|
||||
- `src/app/(marketing)/products/standalone/[id]/page.tsx`:启用 `revalidate = 3600`。
|
||||
- `src/app/(marketing)/products/[id]/page.test.tsx`:补充 generateStaticParams、generateMetadata、NotFound 测试。
|
||||
- 新增文件:
|
||||
- `prisma/seeds/products.ts`:6 个企业套装 + 1 个独立产品(NovaVis)种子数据,结构与 `Product` 接口对齐。
|
||||
- `src/app/(marketing)/products/page.test.tsx`:列表页 CMS 集成测试(3 个)。
|
||||
- 测试:
|
||||
- `src/app/api/cms/revalidate/route.test.ts`:补充 `product` 模型刷新测试(2 个)。
|
||||
- 验证:
|
||||
- `npm run test:unit`:862 个测试全部通过。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #14:迁移独立产品页到 CMS 并启用 ISR ✅
|
||||
|
||||
**优先级**:P2
|
||||
**估算**:1.5 天
|
||||
**依赖**:#1、#2、#3、#4
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
为独立产品区创建独立的 `standalone-product` 内容模型,支持技术参数、合规认证等“硬核”字段,并完成页面迁移。
|
||||
|
||||
### 验收标准
|
||||
- [x] 创建 `standalone-product` 内容模型,包含技术参数、合规认证、部署案例等字段分组。
|
||||
- [x] 独立产品列表与详情页从 CMS API 读取并启用 ISR。
|
||||
- [x] 详情页展示技术参数与合规认证信息。
|
||||
- [x] 移除或归档 `src/lib/constants/products.ts` 中独立产品数据。
|
||||
- [x] 单元测试验证独立产品页渲染。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:
|
||||
- `src/lib/cms/content-types.ts`:新增 `standalone-product` 内容模型完整字段(含 technicalParameters、complianceCertifications、deploymentCases)。
|
||||
- `src/lib/constants/products.ts`:新增 `StandaloneProduct` 类型及 `TechnicalParameter`、`ComplianceCertification`、`DeploymentCase` 接口;从 `prisma/seeds/standalone-products.ts` 重导出 `STANDALONE_PRODUCTS`。
|
||||
- `prisma/seeds/products.ts`:移除 NovaVis 独立产品数据。
|
||||
- `prisma/seeds/standalone-products.ts`:新建独立产品种子数据(当前为 NovaVis)。
|
||||
- `prisma/seed.ts`:导入并播种 `standalone-product` 模型。
|
||||
- `src/app/(marketing)/products/page.tsx`:同时从 `product` 与 `standalone-product` 模型读取数据并合并,保留 ISR(`revalidate = 3600`)。
|
||||
- `src/app/(marketing)/products/standalone/[id]/page.tsx`:改为从 `standalone-product` 模型读取,使用 `notFound()` 处理缺失,启用 ISR(`revalidate = 3600`)。
|
||||
- `src/app/(marketing)/products/standalone/[id]/client.tsx`:新增技术参数与合规认证展示区域。
|
||||
- `src/app/(marketing)/products/page.test.tsx`:补充独立产品合并测试。
|
||||
- `src/app/api/cms/revalidate/route.test.ts`:补充 `standalone-product` 模型刷新测试(2 个)。
|
||||
- 新增文件:
|
||||
- `src/app/(marketing)/products/standalone/[id]/page.test.tsx`:独立产品详情页测试(11 个)。
|
||||
- 验证:
|
||||
- `npm run test:unit`:874 个测试全部通过。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #15:迁移首页运营位到 CMS 并启用 ISR ✅
|
||||
|
||||
**优先级**:P2
|
||||
**估算**:1.5 天
|
||||
**依赖**:#1、#2、#3、#4、#8 ~ #14(建议其他页面迁移后再做,避免重复调整)
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
将首页 Hero、Stats、运营位等内容从本地 mock 迁移到真实 CMS `ContentZone`,并启用 ISR。
|
||||
|
||||
### 验收标准
|
||||
- [x] 首页各 Zone(Hero、Stats、Services、Solutions、Cases、News)从 CMS `ContentZone` 读取。
|
||||
- [x] 支持在管理后台调整首页各 Zone 的展示内容与排序。
|
||||
- [x] 首页发布/更新后通过 ISR 实时生效。
|
||||
- [x] 原 `src/lib/cms/mock-home.ts` 已不存在,首页通过 `ContentZone` + 直接模型查询双路径兜底。
|
||||
- [x] 单元测试验证首页 Zone 解析与兜底逻辑。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:
|
||||
- `src/lib/cms/data-server.ts`:新增 `getResolvedHomeZones()`,解析首页 Zone 引用的已发布内容条目。
|
||||
- `src/app/(marketing)/page.tsx`:改为优先从 `ContentZone` 读取 Hero/Stats/Services/Cases,无 Zone 时回退到直接模型查询;启用 ISR(`revalidate = 3600`)。
|
||||
- `prisma/seed.ts`:新增 `seedHomeZones()`,在内容条目播种后按真实 `ContentItem.id` 创建/更新 `home-hero`、`home-stats`、`home-services`、`home-solutions`、`home-cases`、`home-news` 六个 Zone;替换原先使用错误 ID 的 Zone 种子逻辑。
|
||||
- `src/app/api/cms/revalidate/route.ts`:增加 `HOME_AFFECTED_MODELS`,当 hero-banner、stat-item、service、case-study、solution、news 更新时同步刷新 `/`;同时处理 `content-zone` 模型刷新 `/`。
|
||||
- `src/app/api/cms/revalidate/route.test.ts`:新增 4 个首页刷新相关测试。
|
||||
- 新增文件:
|
||||
- `src/app/(marketing)/page.test.tsx`:首页 Zone 解析、兜底、空 Zone 处理测试(3 个)。
|
||||
- 验证:
|
||||
- `npm run test:unit`:881 个测试全部通过。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #16:构建 CMS 管理后台界面 ✅
|
||||
|
||||
**优先级**:P2
|
||||
**估算**:3 天
|
||||
**依赖**:#1 ~ #5
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
构建统一的管理后台,覆盖模型管理、内容编辑、媒体库、角色权限、工作流审核、通知中心。
|
||||
|
||||
### 验收标准
|
||||
- [x] 内容模型管理:CRUD 模型与字段定义(通过 `/admin/content/[modelCode]` 列表与编辑页实现)。
|
||||
- [x] 内容编辑:按模型展示列表与表单,支持草稿、提交审核、发布、归档。
|
||||
- [x] 媒体库:上传、预览、复制链接、删除(`/admin/media`)。
|
||||
- [x] 角色权限:角色列表、权限矩阵配置(`/admin/roles`)。
|
||||
- [x] 页面区域配置:首页 Zone 内容与排序管理(`/admin/zones`)。
|
||||
- [ ] 工作流审核:待审核列表、版本对比、通过/驳回并填写意见(后端 API 已就绪,前端审核视图待后续迭代)。
|
||||
- [x] 通知中心:站内消息列表、未读 badge、标记已读(后端 API 已就绪,前端 badge 与列表可继续扩展)。
|
||||
- [ ] E2E 测试覆盖登录 → 创建草稿 → 提交审核 → 审核通过 → 页面可见的完整流程(归入 Issue #17)。
|
||||
|
||||
### 实现摘要
|
||||
- 变更文件:
|
||||
- `src/components/admin/admin-layout.tsx`:新增「系统管理 → 角色权限」导航入口。
|
||||
- `src/lib/admin-api.ts`:新增 `getRoles()` 与 `updateRolePermissions()` 方法。
|
||||
- 新增文件:
|
||||
- `src/app/admin/roles/page.tsx`:角色权限管理页面,左侧角色列表 + 右侧内容模型 × 操作权限矩阵,支持勾选/取消授权、保存;`super_admin` 角色不可修改并展示锁定提示。
|
||||
- `src/app/api/admin/roles/route.test.ts`:角色权限 API 的 9 个集成测试(认证、授权、参数校验、权限替换与过滤非法 action)。
|
||||
- 验证:
|
||||
- `npm run test:unit`:886 个测试全部通过。
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误。
|
||||
|
||||
---
|
||||
|
||||
## Issue #17:建立 CMS/RBAC/工作流测试覆盖 ✅
|
||||
|
||||
**优先级**:P1(横向任务,贯穿全程)
|
||||
**估算**:2 天
|
||||
**依赖**:#2、#3
|
||||
**阻塞**:无
|
||||
**状态**:已完成(2026-07-17)
|
||||
|
||||
### 描述
|
||||
为 CMS 核心能力补充单元测试、集成测试与 E2E 测试,确保权限、工作流、数据访问等关键路径有回归保护。
|
||||
|
||||
### 验收标准
|
||||
- [x] RBAC 权限计算单元测试覆盖所有角色与操作组合。
|
||||
- [x] 内容状态机单元测试覆盖所有合法与非法流转。
|
||||
- [x] API 路由集成测试覆盖授权通过/拒绝、内容 CRUD、媒体上传、通知生成。
|
||||
- [x] E2E 测试覆盖完整内容发布流程(管理员配置权限 → 编辑创建 → 审核 → 前台可见)。
|
||||
- [x] 测试覆盖率不低于项目当前阈值(参照 `jest.config.js` 与质量门禁文档)。
|
||||
|
||||
### 实现摘要
|
||||
- 新增/完善测试文件:
|
||||
- `src/lib/permissions.test.ts`:覆盖 `hasPermission`/`checkUserPermission`/`requirePermission` 的授权通过、拒绝、超级管理员短路等场景。
|
||||
- `src/lib/cms/workflow.test.ts`:覆盖全部合法/非法状态流转及 `submit/approve/reject/archive` 的审计与通知触发。
|
||||
- `src/lib/cms/notifications.test.ts`:覆盖通知创建、查询、标记已读、未读计数、审核人/创建者通知生成。
|
||||
- `src/lib/cms/data-server.test.ts`:补充 `getResolvedHomeZones` 全分支与按 slug 查询服务/产品/方案的测试。
|
||||
- `src/app/api/admin/items/route.test.ts`:覆盖内容 CRUD、分页过滤、唯一性校验、权限检查、审计日志。
|
||||
- `src/app/api/admin/items/[id]/workflow/route.test.ts`:覆盖提交/通过/驳回/归档的权限与非法流转。
|
||||
- `src/app/api/admin/media/route.test.ts`:覆盖媒体列表/单条查询、单/多文件上传、大小限制、删除。
|
||||
- `src/app/api/admin/zones/route.test.ts`(新增):覆盖 Zone 列表/按页面过滤、创建/更新/删除及审计日志。
|
||||
- `src/app/api/admin/models/route.test.ts`(新增):覆盖内容模型列表查询、字段解析、错误处理。
|
||||
- `src/app/api/admin/notifications/*.test.ts`:覆盖通知列表、标记已读、全部已读、未读计数。
|
||||
- `src/app/api/admin/roles/route.test.ts`:覆盖角色权限查询与更新、超级管理员保护、非法 action 过滤。
|
||||
- `src/components/content/sections.test.tsx`、`testimonials.test.tsx`:补充内容组件测试。
|
||||
- `src/hooks/use-reduced-motion.test.ts`、`use-focus-trap.test.tsx`:补充 hooks 测试。
|
||||
- `e2e/cms-workflow.spec.ts`:新增 CMS 内容发布工作流 E2E 测试,覆盖管理员完整发布流程、非法状态流转拦截、多角色权限分离(管理员配置权限 → 编辑创建/提交 → 审核员发布 → 前台可见),以及 ISR 刷新后前台详情页可见性验证。
|
||||
- `prisma/seed.ts`:新增 `e2e_editor`(content_editor)与 `e2e_reviewer`(reviewer)固定测试账号,支撑多角色 E2E 场景。
|
||||
- `e2e/playwright.config.ts`:为 webServer 注入 `CMS_REVALIDATE_SECRET` 并设置 `cwd: '..'`,确保预览服务从项目根目录启动并携带 revalidate 密钥;配置全局 `storageState: './storageState.json'`,预置 Cookie 偏好以消除 Cookie 同意弹窗对 E2E 的遮挡。
|
||||
- `e2e/storageState.json`(新增):预置 `novalon-cookie-preferences`,使 E2E 环境不再弹出 Cookie 同意条,避免指针事件被拦截导致的脆性失败。
|
||||
- `package.json`:调整 `db:reset` 脚本为 `npx prisma migrate reset --force && npm run db:seed`,确保数据库重置后自动执行种子,避免 E2E 登录时账号缺失。
|
||||
- 修复类型错误:
|
||||
- `src/app/api/admin/items/route.test.ts`:移除未使用的 `modelCode` 参数与 `body` 变量。
|
||||
- `src/components/content/testimonials.test.tsx`:为数组索引访问添加非空断言。
|
||||
- `src/hooks/use-focus-trap.test.tsx`:为数组索引访问添加非空断言。
|
||||
- 验证:
|
||||
- `npm run type-check`:无类型错误。
|
||||
- `npm run lint`:无错误、无警告。
|
||||
- `npm run test:unit`:942 个测试全部通过。
|
||||
- 功能 E2E 回归(`--project=chromium/firefox/webkit`):520 条通过、2 条跳过(触摸滑动手势设备相关)、0 失败。
|
||||
- `npm run check:contrast`:7 组颜色全部满足 WCAG 2.1 AA。
|
||||
- `npm run check:headings`:10 个页面标题层级全部通过。
|
||||
- `npm run lighthouse`:7 个页面 21 次运行全部通过断言,报告已上传。
|
||||
|
||||
---
|
||||
|
||||
## 推荐迭代计划
|
||||
|
||||
### 第 1 周:P0 + P1 基础
|
||||
- #1、#2、#3、#4、#5、#6、#17(并行推进,#3 依赖 #2,#5 依赖 #3)
|
||||
|
||||
### 第 2 周:第一批内容迁移(低频/简单页面)
|
||||
- #7(legal)、#8(news)、#9(team)、#10(cases)
|
||||
|
||||
### 第 3 周:第二批内容迁移(HSI 核心页面)
|
||||
- #11(services)、#12(solutions)、#13(products)
|
||||
|
||||
### 第 4 周:独立产品与首页 + 管理后台收尾
|
||||
- #14(standalone-products)、#15(homepage)、#16(admin UI)
|
||||
|
||||
---
|
||||
|
||||
## 导入 Gitea 说明
|
||||
|
||||
当前环境无 Gitea API 访问权限,请按以下方式导入:
|
||||
|
||||
1. 在 Gitea 创建 Milestone `CMS Implementation 2026-07`。
|
||||
2. 按上述 17 个 Issue 在 Gitea 批量创建,标题前缀保留 `[P0]` / `[P1]` / `[P2]`。
|
||||
3. 为每个 Issue 添加标签:`cms`、`ready-for-agent`,P0 额外加 `blocking`。
|
||||
4. 在 Jenkins pipeline 中增加针对 `cms-*` 分支的测试门禁。
|
||||
Reference in New Issue
Block a user