docs(test): 添加设计文档、测试规范与 E2E 测试套件

- 新增 ADR 架构决策记录 (Design DNA 集成与深化)
- 新增 CMS 系统设计文档
- 新增实施计划文档 (Phase1-3)
- 新增 Bain 品牌升级设计规格
- 新增 E2E 分层测试套件 (P1-P4)
- 新增视觉回归测试配置
- 新增光效分析、视觉验证等辅助脚本
- 更新验收测试报告
This commit is contained in:
张翔
2026-07-07 06:54:25 +08:00
parent 8def296301
commit 636bc4ecde
31 changed files with 9262 additions and 41 deletions
@@ -0,0 +1,671 @@
# Novalon CMS 自研系统实现计划
> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
**目标:** 构建与 Novalon 技术栈深度融合的自研 Headless CMS 系统,实现内容模型定义、内容管理、动态页面渲染、主题配置等核心能力。
**架构:** 采用 Broadleaf CMS 核心设计思想(ContentModel + ContentItem + ContentZone + ThemeField),基于 Java 21 + Spring Boot + PostgreSQL 构建后端服务,React + ProLayout 构建管理后台,Next.js 前端通过统一 SDK 接入。前端优先实现 SDK 与动态渲染层,后端 API 按接口契约并行开发。
**技术栈:**
- 后端:Java 21 + Spring Boot WebFlux + PostgreSQL + Flyway + Spring Security
- 管理端:React 18 + ProLayout + Zustand + Ant Design 5(对齐 novalon-manage-system
- 前端网站:Next.js 14 + TypeScript + ISR/SSG
- 数据校验:Zod(前端)+ Jakarta Validation(后端)
---
## 一、系统架构总览
### 1.1 整体架构图
```
┌─────────────────────────────────────────────────────────────┐
│ 前端展示层(Website) │
│ Next.js 14 + CMS SDK + 动态渲染器 │
└──────────────────────────────┬──────────────────────────────┘
│ RESTful API
┌──────────────────────────────▼──────────────────────────────┐
│ CMS 服务层(Backend
│ Java 21 + Spring Boot WebFlux + PostgreSQL + Flyway │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ 内容模型 │ │ 内容项 │ │ 内容区域 │ │ 主题配置 │ │
│ │ 管理 │ │ 管理 │ │ 管理 │ │ 管理 │ │
│ └──────────┘ └──────────┘ └──────────┘ └─────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ 媒体资源 │ │ 版本管理 │ │ 审计日志 │ │ 权限/SSO │ │
│ │ 管理 │ │ 与草稿 │ │ │ │ 集成 │ │
│ └──────────┘ └──────────┘ └──────────┘ └─────────────┘ │
└──────────────────────────────┬──────────────────────────────┘
┌──────────────────────────────▼──────────────────────────────┐
│ 管理后台(Admin) │
│ React 18 + ProLayout + Zustand + Ant Design 5 │
│ (对齐 novalon-manage-system 架构规范) │
└─────────────────────────────────────────────────────────────┘
```
### 1.2 核心领域模型(源自 Broadleaf CMS 设计)
| 模型 | 表名 | 职责 | 对应 Broadleaf 概念 |
|------|------|------|-------------------|
| ContentModel | `cms_content_model` | 定义内容类型的 schema(字段、类型、校验) | ContentModel |
| ContentItem | `cms_content_item` | 实际的内容数据,基于 model 动态字段 | ContentItem |
| ContentZone | `cms_content_zone` | 页面中的可编辑区域定义 | ContentZone |
| ThemeConfig | `cms_theme_config` | 站点主题、设置、SEO 等配置 | ThemeField |
| MediaAsset | `cms_media_asset` | 媒体资源管理 | - |
| ContentVersion | `cms_content_version` | 内容版本与草稿 | - |
| AuditLog | `cms_audit_log` | 操作审计日志 | - |
---
## 二、文件结构设计
### 2.1 当前项目(novalon-website)新增文件
```
src/
├── lib/
│ └── cms/ # CMS 前端 SDK
│ ├── types.ts # 类型定义(ContentModel/ContentItem 等)
│ ├── client.ts # API 客户端封装
│ ├── renderer.ts # 内容渲染工具函数
│ └── index.ts # barrel 导出
├── components/
│ └── cms/ # CMS 渲染组件
│ ├── ContentRenderer.tsx # 动态内容渲染器
│ ├── SectionRenderer.tsx # Section 级渲染器(ContentZone
│ ├── FieldRenderer.tsx # 字段级渲染器
│ └── index.ts
└── app/
└── api/
└── cms/ # 前端 BFF 层(可选)
└── [...path]/route.ts
```
### 2.2 后端服务(novalon-cms,独立项目)
```
novalon-cms/
├── cms-gateway/ # API 网关模块
├── cms-common/ # 公共工具模块
├── cms-db/ # 数据访问层(实体 + Repository
├── cms-content/ # 内容管理模块(model/item/zone
├── cms-media/ # 媒体资源模块
├── cms-theme/ # 主题配置模块
├── cms-admin/ # 管理端 API
└── cms-sys/ # 系统管理(用户/权限/审计)
```
### 2.3 管理后台(novalon-manage-system 扩展)
```
src/
├── pages/
│ └── cms/
│ ├── content-model/ # 内容模型管理
│ ├── content-item/ # 内容项管理
│ ├── content-zone/ # 内容区域管理
│ ├── media/ # 媒体库
│ └── theme/ # 主题配置
├── stores/
│ └── cms.ts # CMS 状态管理
└── services/
└── cms.ts # CMS API 服务
```
---
## 三、阶段划分与里程碑
### 阶段一:前端 SDK + 动态渲染(当前项目,立即启动)
**目标:** 在当前 Next.js 项目中建立 CMS 接入层,定义完整的类型系统和渲染架构,为后端 API 开发提供契约。
**交付物:**
- CMS 类型定义系统
- API Client SDK(带 mock 数据支持)
- 动态 Section 渲染器
- 案例模块迁移试点(从 constants → CMS 数据结构)
### 阶段二:后端核心服务(Java 团队并行)
**目标:** 实现 CMS 核心数据模型和 CRUD API。
**交付物:**
- 数据库表结构 + Flyway 迁移脚本
- ContentModel CRUD API
- ContentItem CRUD API + 版本管理
- ContentZone API
- 权限集成 + 审计日志
### 阶段三:管理后台开发
**目标:** 在 novalon-manage-system 中扩展 CMS 管理模块。
**交付物:**
- 内容模型可视化编辑器
- 内容项编辑器(动态表单)
- 媒体资源管理
- 主题配置面板
### 阶段四:全链路联调 + 内容迁移
**目标:** 前后端打通,完成所有内容模块迁移。
**交付物:**
- 全模块 CMS 接入
- 内容迁移脚本
- ISR/SSG 缓存策略
- 上线
---
## 四、详细任务清单
### 任务 1:CMS 类型系统定义
**文件:**
- 创建:`src/lib/cms/types.ts`
- 创建:`src/lib/cms/index.ts`
**内容:** 定义以下核心类型
```typescript
// 字段类型枚举
export type FieldType =
| 'text' // 单行文本
| 'textarea' // 多行文本
| 'richtext' // 富文本
| 'number' // 数字
| 'boolean' // 布尔
| 'date' // 日期
| 'datetime' // 日期时间
| 'image' // 图片
| 'media' // 媒体资源
| 'reference' // 引用其他内容项
| 'references' // 引用多个内容项
| 'json' // JSON 结构
| 'array' // 数组
| 'object'; // 嵌套对象
// 字段定义
export interface FieldDefinition {
name: string;
type: FieldType;
label: string;
required?: boolean;
description?: string;
placeholder?: string;
defaultValue?: unknown;
validation?: {
min?: number;
max?: number;
pattern?: string;
custom?: string;
};
options?: Array<{ label: string; value: string | number | boolean }>;
referenceModel?: string; // reference 类型关联的 model code
ui?: {
component?: string; // 指定 UI 组件
width?: 'full' | 'half' | 'third';
helpText?: string;
};
fields?: FieldDefinition[]; // object/array 类型的嵌套字段
}
// 内容模型
export interface ContentModel {
id: string;
code: string; // 唯一标识,如 'case-study'
name: string;
description?: string;
fields: FieldDefinition[];
isPageType: boolean; // 是否为页面类型(支持 URL 路由)
urlPattern?: string; // URL 模式,如 '/cases/{slug}'
hasVersions: boolean; // 是否启用版本管理
hasDraft: boolean; // 是否启用草稿
icon?: string;
createdAt: string;
updatedAt: string;
}
// 内容状态
export type ContentStatus = 'draft' | 'review' | 'published' | 'archived';
// 内容项
export interface ContentItem {
id: string;
modelId: string;
modelCode: string;
title: string;
slug?: string;
status: ContentStatus;
data: Record<string, unknown>; // 动态字段数据,结构由 model 定义
version: number;
publishedAt?: string;
createdBy: string;
updatedBy: string;
createdAt: string;
updatedAt: string;
}
// 内容区域
export interface ContentZone {
id: string;
code: string; // 区域唯一标识,如 'home-hero'
name: string;
description?: string;
pageCode?: string; // 所属页面
allowedModels: string[]; // 允许的内容模型 codes
items: Array<{
itemId: string;
item?: ContentItem;
sortOrder: number;
config?: Record<string, unknown>; // 区域级配置
}>;
}
// 主题配置
export interface ThemeConfig {
id: string;
siteName: string;
siteDescription: string;
logo?: string;
favicon?: string;
colors: {
primary: string;
secondary: string;
accent: string;
background: string;
text: string;
};
seo: {
defaultTitle: string;
defaultDescription: string;
defaultKeywords: string;
ogImage?: string;
};
social: {
wechat?: string;
weibo?: string;
linkedin?: string;
github?: string;
};
settings: Record<string, unknown>; // 其他自定义设置
}
// 分页查询参数
export interface ContentQueryParams {
modelCode: string;
status?: ContentStatus;
page?: number;
pageSize?: number;
sortBy?: string;
sortOrder?: 'asc' | 'desc';
filters?: Record<string, string | number | boolean>;
search?: string;
}
// 分页结果
export interface PaginatedResult<T> {
items: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}
```
---
### 任务 2CMS API Client SDK
**文件:**
- 创建:`src/lib/cms/client.ts`
- 修改:`src/lib/cms/index.ts`
**内容:** 封装 CMS API 客户端,支持真实 API 和 mock 模式
```typescript
// 配置
interface CmsClientConfig {
baseUrl: string;
apiKey?: string;
timeout?: number;
mockMode?: boolean; // 开发阶段使用 mock 数据
}
// 客户端类
class CmsClient {
constructor(config: CmsClientConfig) {}
// 内容模型相关
async getModels(): Promise<ContentModel[]> {}
async getModel(code: string): Promise<ContentModel> {}
// 内容项相关
async getItems(params: ContentQueryParams): Promise<PaginatedResult<ContentItem>> {}
async getItem(id: string): Promise<ContentItem> {}
async getItemBySlug(modelCode: string, slug: string): Promise<ContentItem> {}
// 内容区域相关
async getZone(code: string): Promise<ContentZone> {}
async getZonesByPage(pageCode: string): Promise<ContentZone[]> {}
// 主题配置相关
async getThemeConfig(): Promise<ThemeConfig> {}
// 工具方法
async withType<T extends Record<string, unknown>>(
item: ContentItem
): Promise<T> {}
}
```
---
### 任务 3:动态 Section 渲染器(ContentZone 实现)
**文件:**
- 创建:`src/components/cms/SectionRenderer.tsx`
- 创建:`src/components/cms/FieldRenderer.tsx`
- 创建:`src/components/cms/ContentRenderer.tsx`
- 创建:`src/components/cms/index.ts`
**核心设计思想:**
- ContentZone = 页面上的一个可编辑区域
- 每个 Zone 包含多个 ContentItem
- 每个 ContentItem 根据其 model type 选择对应的渲染组件
```typescript
// 组件注册表
interface SectionComponentRegistry {
[modelCode: string]: React.ComponentType<{ data: any; config?: any }>;
}
// SectionRenderer: 渲染单个 ContentZone
function SectionRenderer({ zoneCode, fallback }: {
zoneCode: string;
fallback?: React.ReactNode;
}) {}
// ContentRenderer: 渲染单个 ContentItem
function ContentRenderer({ item }: { item: ContentItem }) {}
// FieldRenderer: 渲染单个字段
function FieldRenderer({
field, value
}: {
field: FieldDefinition;
value: unknown;
}) {}
```
---
### 任务 4:案例模块迁移试点
**目标:** 将现有的案例数据从 `constants/cases.ts` 迁移为 CMS 数据结构,验证类型系统和渲染器的正确性。
**文件:**
- 修改:`src/lib/cms/mock-data.ts`(新建 mock 数据)
- 修改:`src/app/(marketing)/cases/cases-content-v1.tsx`
- 修改:`src/app/(marketing)/cases/[slug]/client.tsx`
**迁移内容:**
- 定义 `case-study` 内容模型(对应 CaseStudy 接口)
- 将 6 个案例转为 ContentItem 格式
- 列表页和详情页改为从 CMS Client 获取数据
- 保留 mock 模式,后端 API 完成后无缝切换
---
### 任务 5:后端 API 接口契约
**文件:**
- 创建:`docs/cms/api-contract.md`
**核心 API 列表:**
| 模块 | Method | Path | 说明 |
|------|--------|------|------|
| 内容模型 | GET | `/api/cms/models` | 获取所有内容模型 |
| 内容模型 | GET | `/api/cms/models/{code}` | 获取单个模型定义 |
| 内容模型 | POST | `/api/cms/models` | 创建内容模型 |
| 内容模型 | PUT | `/api/cms/models/{code}` | 更新内容模型 |
| 内容项 | GET | `/api/cms/items` | 分页查询内容项 |
| 内容项 | GET | `/api/cms/items/{id}` | 获取内容项详情 |
| 内容项 | GET | `/api/cms/items/slug/{modelCode}/{slug}` | 按 slug 获取 |
| 内容项 | POST | `/api/cms/items` | 创建内容项 |
| 内容项 | PUT | `/api/cms/items/{id}` | 更新内容项 |
| 内容项 | POST | `/api/cms/items/{id}/publish` | 发布内容 |
| 内容项 | POST | `/api/cms/items/{id}/unpublish` | 下架内容 |
| 内容区域 | GET | `/api/cms/zones/{code}` | 获取区域内容 |
| 内容区域 | PUT | `/api/cms/zones/{code}` | 更新区域配置 |
| 主题配置 | GET | `/api/cms/theme` | 获取主题配置 |
| 主题配置 | PUT | `/api/cms/theme` | 更新主题配置 |
| 媒体资源 | GET | `/api/cms/media` | 媒体列表 |
| 媒体资源 | POST | `/api/cms/media/upload` | 上传媒体 |
---
## 五、数据模型详细设计
### 5.1 cms_content_model(内容模型表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint PK | 主键 |
| code | varchar(100) UNIQUE | 模型唯一编码 |
| name | varchar(200) | 模型名称 |
| description | varchar(500) | 描述 |
| fields_json | jsonb | 字段定义 JSON |
| is_page_type | boolean | 是否为页面类型 |
| url_pattern | varchar(200) | URL 模式 |
| has_versions | boolean | 是否启用版本 |
| has_draft | boolean | 是否启用草稿 |
| icon | varchar(50) | 图标 |
| created_by | varchar(100) | 创建人 |
| updated_by | varchar(100) | 更新人 |
| created_at | timestamp | 创建时间 |
| updated_at | timestamp | 更新时间 |
| deleted | boolean | 逻辑删除 |
### 5.2 cms_content_item(内容项表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint PK | 主键 |
| model_id | bigint FK | 关联模型 ID |
| model_code | varchar(100) | 冗余模型编码 |
| title | varchar(500) | 内容标题 |
| slug | varchar(200) | URL slug |
| status | varchar(20) | 状态:draft/review/published/archived |
| data_json | jsonb | 内容数据 JSON |
| version | int | 当前版本号 |
| published_at | timestamp | 发布时间 |
| created_by | varchar(100) | 创建人 |
| updated_by | varchar(100) | 更新人 |
| created_at | timestamp | 创建时间 |
| updated_at | timestamp | 更新时间 |
| deleted | boolean | 逻辑删除 |
索引:`(model_code, status, created_at)``(model_code, slug)` 唯一索引
### 5.3 cms_content_version(内容版本表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint PK | 主键 |
| item_id | bigint FK | 内容项 ID |
| version | int | 版本号 |
| data_json | jsonb | 版本数据快照 |
| status | varchar(20) | 版本状态 |
| change_log | varchar(500) | 变更说明 |
| created_by | varchar(100) | 创建人 |
| created_at | timestamp | 创建时间 |
### 5.4 cms_content_zone(内容区域表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint PK | 主键 |
| code | varchar(100) UNIQUE | 区域编码 |
| name | varchar(200) | 区域名称 |
| description | varchar(500) | 描述 |
| page_code | varchar(100) | 所属页面 |
| allowed_models | varchar(500) | 允许的模型 codes(逗号分隔) |
| items_json | jsonb | 区域内内容项配置(顺序、附加配置) |
| created_at | timestamp | 创建时间 |
| updated_at | timestamp | 更新时间 |
### 5.5 cms_theme_config(主题配置表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint PK | 主键(单表单记录,id=1) |
| config_json | jsonb | 完整配置 JSON |
| updated_by | varchar(100) | 更新人 |
| updated_at | timestamp | 更新时间 |
### 5.6 cms_media_asset(媒体资源表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint PK | 主键 |
| name | varchar(200) | 文件名称 |
| path | varchar(500) | 存储路径 |
| url | varchar(500) | 访问 URL |
| mime_type | varchar(100) | MIME 类型 |
| size | bigint | 文件大小(字节) |
| width | int | 图片宽度 |
| height | int | 图片高度 |
| alt | varchar(200) | 替代文本 |
| storage_type | varchar(20) | 存储类型:local/oss/s3 |
| created_by | varchar(100) | 创建人 |
| created_at | timestamp | 创建时间 |
### 5.7 cms_audit_log(审计日志表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint PK | 主键 |
| module | varchar(50) | 模块:model/item/zone/theme/media |
| target_id | bigint | 操作对象 ID |
| action | varchar(50) | 操作:create/update/delete/publish/unpublish |
| operator | varchar(100) | 操作人 |
| before_json | jsonb | 操作前数据 |
| after_json | jsonb | 操作后数据 |
| ip | varchar(50) | IP 地址 |
| user_agent | varchar(500) | User Agent |
| created_at | timestamp | 操作时间 |
---
## 六、安全与权限设计
### 6.1 权限模型
| 角色 | 权限说明 |
|------|----------|
| 超级管理员 | 所有权限 |
| CMS 管理员 | 内容模型管理、内容管理、用户管理 |
| 内容编辑 | 内容创建、编辑、提交审核 |
| 内容审核 | 内容审核、发布、下架 |
| 访客 | 仅查看(前端 API) |
### 6.2 API 安全
- 管理端 API:走现有 SSO + 权限体系
- 前端 API:仅读取 published 状态内容,无需鉴权(或 API Key 限流)
- 敏感操作:写入操作必须有审计日志
- 数据隔离:按业务线/租户隔离(预留)
---
## 七、性能优化策略
### 7.1 前端侧
- **ISR/SSG**:内容页使用 Next.js ISR,内容变更时触发 revalidate
- **缓存策略**
- 内容列表:10 分钟缓存 + stale-while-revalidate
- 内容详情:1 小时缓存,发布时主动失效
- 主题配置:构建时注入,运行时热更新
- **图片优化**:使用 Next.js Image + CDN 加速
### 7.2 后端侧
- **数据库索引**:按查询模式建立合理索引
- **Redis 缓存**:热点内容数据缓存
- **读写分离**:读操作走从库
- **CDN 加速**:媒体资源走 CDN
---
## 八、与现有系统的集成
### 8.1 用户/权限集成
- 复用 novalon-manage-system 的用户体系和权限框架
- SSO 单点登录
- 统一的角色/权限管理界面
### 8.2 审计集成
- 接入现有审计日志系统
- CMS 操作日志同步到统一审计平台
### 8.3 监控集成
- 接入现有监控体系(Prometheus + Grafana
- 关键指标:API 响应时间、错误率、内容发布量
---
## 九、风险与应对
| 风险 | 概率 | 影响 | 应对策略 |
|------|------|------|----------|
| 动态字段查询性能 | 中 | 高 | 合理设计索引,必要时引入 ES 做全文检索 |
| 富文本编辑器选型 | 低 | 中 | 预研 Tiptap/Lexical,留接口抽象层可替换 |
| 内容迁移成本 | 中 | 中 | 开发迁移脚本,分批迁移,先迁移更新频繁的模块 |
| 管理后台开发量 | 高 | 中 | 优先开发核心功能,可视化编辑器可后置,先用动态表单 |
---
## 十、里程碑验收标准
### 阶段一验收(前端 SDK
- [ ] 完整的 TypeScript 类型定义
- [ ] CMS Client SDKmock 模式可用)
- [ ] 动态 Section 渲染器(支持 3 种以上内容类型)
- [ ] 案例模块试点完成(列表 + 详情)
- [ ] 单元测试覆盖率 ≥ 80%
### 阶段二验收(后端核心)
- [ ] 7 张核心表 + Flyway 迁移脚本
- [ ] 30+ 个 RESTful API
- [ ] 内容版本管理(草稿/发布/回滚)
- [ ] 审计日志完整
- [ ] 权限集成完成
- [ ] API 单元测试覆盖率 ≥ 80%
### 阶段三验收(管理后台)
- [ ] 内容模型可视化编辑器
- [ ] 动态表单内容编辑器
- [ ] 媒体资源管理(上传/列表/删除)
- [ ] 主题配置面板
- [ ] 操作审计日志查看
### 阶段四验收(全链路)
- [ ] 所有内容模块迁移完成(案例/新闻/产品/解决方案/服务)
- [ ] 前后端联调通过
- [ ] 性能达标(LCP < 2s
- [ ] 安全扫描通过
- [ ] 内容迁移脚本验证通过