- 新增 ADR 架构决策记录 (Design DNA 集成与深化) - 新增 CMS 系统设计文档 - 新增实施计划文档 (Phase1-3) - 新增 Bain 品牌升级设计规格 - 新增 E2E 分层测试套件 (P1-P4) - 新增视觉回归测试配置 - 新增光效分析、视觉验证等辅助脚本 - 更新验收测试报告
672 lines
22 KiB
Markdown
672 lines
22 KiB
Markdown
# 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;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 任务 2:CMS 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 SDK(mock 模式可用)
|
||
- [ ] 动态 Section 渲染器(支持 3 种以上内容类型)
|
||
- [ ] 案例模块试点完成(列表 + 详情)
|
||
- [ ] 单元测试覆盖率 ≥ 80%
|
||
|
||
### 阶段二验收(后端核心)
|
||
|
||
- [ ] 7 张核心表 + Flyway 迁移脚本
|
||
- [ ] 30+ 个 RESTful API
|
||
- [ ] 内容版本管理(草稿/发布/回滚)
|
||
- [ ] 审计日志完整
|
||
- [ ] 权限集成完成
|
||
- [ ] API 单元测试覆盖率 ≥ 80%
|
||
|
||
### 阶段三验收(管理后台)
|
||
|
||
- [ ] 内容模型可视化编辑器
|
||
- [ ] 动态表单内容编辑器
|
||
- [ ] 媒体资源管理(上传/列表/删除)
|
||
- [ ] 主题配置面板
|
||
- [ ] 操作审计日志查看
|
||
|
||
### 阶段四验收(全链路)
|
||
|
||
- [ ] 所有内容模块迁移完成(案例/新闻/产品/解决方案/服务)
|
||
- [ ] 前后端联调通过
|
||
- [ ] 性能达标(LCP < 2s)
|
||
- [ ] 安全扫描通过
|
||
- [ ] 内容迁移脚本验证通过
|