10404dbb36
同步工作区剩余变更,主要包括: - 营销页面组件与布局持续优化(about/news/services/solutions/team 等) - 详情页四层叙事组件、布局组件、UI 组件调整 - CMS 数据模型、API 路由、权限、工作流、站内通知、媒体管理扩展 - 新增/补充单元测试与 E2E 测试(cms-workflow.spec.ts 等) - ESLint 9 迁移、jest/tsconfig 配置更新、依赖调整 - 新增 ADR、CMS 评估文档、Release Review / Acceptance 报告 - 移除水墨装饰组件与大体积未使用字体文件
702 lines
37 KiB
Markdown
702 lines
37 KiB
Markdown
# 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-*` 分支的测试门禁。
|