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:
张翔
2026-07-25 08:04:01 +08:00
parent e35090b914
commit 10404dbb36
208 changed files with 18107 additions and 8330 deletions
@@ -0,0 +1,195 @@
# PRDNovalon 轻量 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)**部署模式,实现内容实时发布、草稿预览、版本回滚。
内容按页面类型**全量迁移**到 CMSlegal → 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 → archiveddraft → 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.jsCookie 透传支持 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] 首页各 ZoneHero、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 周:第一批内容迁移(低频/简单页面)
- #7legal)、#8news)、#9team)、#10cases
### 第 3 周:第二批内容迁移(HSI 核心页面)
- #11services)、#12solutions)、#13products
### 第 4 周:独立产品与首页 + 管理后台收尾
- #14standalone-products)、#15homepage)、#16admin 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-*` 分支的测试门禁。