Files
novalon-website/docs/superpowers/plans/2026-07-01-novalon-cms-system.md
T
张翔 636bc4ecde docs(test): 添加设计文档、测试规范与 E2E 测试套件
- 新增 ADR 架构决策记录 (Design DNA 集成与深化)
- 新增 CMS 系统设计文档
- 新增实施计划文档 (Phase1-3)
- 新增 Bain 品牌升级设计规格
- 新增 E2E 分层测试套件 (P1-P4)
- 新增视觉回归测试配置
- 新增光效分析、视觉验证等辅助脚本
- 更新验收测试报告
2026-07-07 06:54:25 +08:00

672 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
- [ ] 安全扫描通过
- [ ] 内容迁移脚本验证通过