18 KiB
18 KiB
新闻资讯模块设计方案
1. 概述
1.1 模块定位
新闻资讯模块是飞点健康 CMS 系统的核心内容管理模块,用于发布和管理企业新闻、行业资讯、产品动态等内容。该模块与顶级菜单"新闻动态"关联,提供完整的栏目管理和资讯发布功能。
1.2 设计目标
- 支持双格式发布:Markdown + HTML 富文本
- 复用现有成熟代码(personal-blog)
- 支持图片资源管理(可作为独立顶级模块)
- 提供良好的编辑体验和预览功能
1.3 功能范围
┌─────────────────────────────────────────────────────────────┐
│ 新闻资讯模块 │
├─────────────────┬─────────────────┬─────────────────────────┤
│ 栏目管理 │ 资讯管理 │ 图片管理 │
├─────────────────┼─────────────────┼─────────────────────────┤
│ • 创建栏目 │ • 创建资讯 │ • 上传图片 │
│ • 编辑栏目 │ • 编辑资讯 │ • 图片列表 │
│ • 删除栏目 │ • 删除资讯 │ • 插入到文章 │
│ • 栏目排序 │ • 发布/草稿 │ • 删除图片 │
│ │ • Markdown编辑 │ │
│ │ • HTML富文本 │ │
│ │ • 预览功能 │ │
└─────────────────┴─────────────────┴─────────────────────────┘
2. 数据库设计
2.1 数据表关系图
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ news_categories│ │ news_posts │ │ images │
│ (栏目表) │◄────────│ (资讯表) │────────►│ (图片表) │
├─────────────────┤ 1:N ├─────────────────┤ N:M ├─────────────────┤
│ id (PK) │ │ id (PK) │ │ id (PK) │
│ name │ │ title │ │ filename │
│ slug (UQ) │ │ slug (UQ) │ │ file_path │
│ description │ │ content │ │ thumb_path │
│ sort_order │ │ content_type │◄───────│ description │
│ is_enabled │ │ summary │ │ file_size │
│ created_at │ │ cover_image │◄───────│ width │
│ updated_at │ │ category_id(FK) │ │ height │
└─────────────────┘ │ is_published │ │ created_at │
│ is_draft │ └─────────────────┘
│ view_count │
│ created_at │
│ updated_at │
└─────────────────┘
│
│ 1:N
▼
┌─────────────────┐
│ news_post_images│
│ (文章图片关联) │
├─────────────────┤
│ post_id (FK) │
│ image_id (FK) │
└─────────────────┘
2.2 表结构定义
2.2.1 news_categories - 栏目表
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | INTEGER | PK, AI | 主键 |
| name | VARCHAR(50) | NOT NULL | 栏目名称 |
| slug | VARCHAR(50) | UNIQUE, NOT NULL | URL标识(如:company-news) |
| description | VARCHAR(200) | NULL | 栏目描述 |
| sort_order | INTEGER | DEFAULT 0 | 排序权重 |
| is_enabled | BOOLEAN | DEFAULT 1 | 是否启用 |
| created_at | DATETIME | DEFAULT NOW | 创建时间 |
| updated_at | DATETIME | DEFAULT NOW | 更新时间 |
CREATE TABLE news_categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(50) NOT NULL,
slug VARCHAR(50) UNIQUE NOT NULL,
description VARCHAR(200),
sort_order INTEGER DEFAULT 0,
is_enabled BOOLEAN DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
2.2.2 news_posts - 资讯表
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | INTEGER | PK, AI | 主键 |
| title | VARCHAR(200) | NOT NULL | 资讯标题 |
| slug | VARCHAR(200) | UNIQUE, NOT NULL | URL友好的标识 |
| content | TEXT | NOT NULL | 内容 |
| content_type | VARCHAR(10) | DEFAULT 'markdown' | 内容类型:markdown/html |
| summary | VARCHAR(500) | NULL | 摘要(自动生成或手动填写) |
| cover_image | VARCHAR(500) | NULL | 封面图路径 |
| category_id | INTEGER | FK -> news_categories | 栏目ID(必填) |
| is_published | BOOLEAN | DEFAULT 0 | 是否发布 |
| is_draft | BOOLEAN | DEFAULT 1 | 是否草稿 |
| view_count | INTEGER | DEFAULT 0 | 浏览次数 |
| created_at | DATETIME | DEFAULT NOW | 创建时间 |
| updated_at | DATETIME | DEFAULT NOW | 更新时间 |
CREATE TABLE news_posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title VARCHAR(200) NOT NULL,
slug VARCHAR(200) UNIQUE NOT NULL,
content TEXT NOT NULL,
content_type VARCHAR(10) DEFAULT 'markdown',
summary VARCHAR(500),
cover_image VARCHAR(500),
category_id INTEGER NOT NULL,
is_published BOOLEAN DEFAULT 0,
is_draft BOOLEAN DEFAULT 1,
view_count INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (category_id) REFERENCES news_categories(id) ON DELETE RESTRICT
);
2.2.3 images - 图片表(复用/扩展)
复用 personal-blog 的图片表结构,但提升为系统级资源表:
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
| id | INTEGER | PK, AI | 主键 |
| filename | VARCHAR(255) | NOT NULL | 存储文件名 |
| original_name | VARCHAR(255) | NULL | 原始文件名 |
| file_path | VARCHAR(500) | NOT NULL | 文件路径 |
| thumb_path | VARCHAR(500) | NULL | 缩略图路径 |
| description | VARCHAR(500) | NULL | 图片描述 |
| file_size | INTEGER | NULL | 文件大小(字节) |
| width | INTEGER | NULL | 图片宽度 |
| height | INTEGER | NULL | 图片高度 |
| module | VARCHAR(20) | DEFAULT 'news' | 所属模块:news/product/page等 |
| created_at | DATETIME | DEFAULT NOW | 创建时间 |
CREATE TABLE images (
id INTEGER PRIMARY KEY AUTOINCREMENT,
filename VARCHAR(255) NOT NULL,
original_name VARCHAR(255),
file_path VARCHAR(500) NOT NULL,
thumb_path VARCHAR(500),
description VARCHAR(500),
file_size INTEGER,
width INTEGER,
height INTEGER,
module VARCHAR(20) DEFAULT 'news',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
3. 后端 API 设计
3.1 路由结构
/api/v1/
├── /news/
│ ├── GET /categories # 获取栏目列表
│ ├── POST /categories # 创建栏目
│ ├── GET /categories/{id} # 获取栏目详情
│ ├── PUT /categories/{id} # 更新栏目
│ ├── DELETE /categories/{id} # 删除栏目
│ ├── GET /posts # 获取资讯列表(分页、筛选)
│ ├── POST /posts # 创建资讯
│ ├── GET /posts/{id} # 获取资讯详情
│ ├── PUT /posts/{id} # 更新资讯
│ ├── DELETE /posts/{id} # 删除资讯
│ ├── POST /posts/{id}/publish # 发布资讯
│ ├── POST /posts/{id}/unpublish # 取消发布
│ └── GET /posts/slug/{slug} # 通过slug获取(访客端)
└── /images/
├── GET / # 获取图片列表(分页)
├── POST /upload # 上传图片
├── DELETE /{id} # 删除图片
└── POST /{id}/insert # 获取图片插入URL
3.2 核心 API 定义
3.2.1 栏目管理 API
GET /api/v1/news/categories
// Response
{
"items": [
{
"id": 1,
"name": "公司新闻",
"slug": "company-news",
"description": "飞点健康最新动态",
"sort_order": 1,
"is_enabled": true,
"post_count": 15,
"created_at": "2024-01-15T08:00:00"
}
],
"total": 5
}
POST /api/v1/news/categories
// Request
{
"name": "公司新闻",
"slug": "company-news",
"description": "飞点健康最新动态",
"sort_order": 1
}
3.2.2 资讯管理 API
GET /api/v1/news/posts?page=1&size=10&category_id=1&keyword=xxx
// Response
{
"items": [
{
"id": 1,
"title": "飞点健康完成A轮融资",
"slug": "feidian-series-a",
"summary": "今日,飞点健康宣布完成数千万元A轮融资...",
"cover_image": "/uploads/covers/xxx.jpg",
"category": {
"id": 1,
"name": "公司新闻"
},
"content_type": "markdown",
"is_published": true,
"is_draft": false,
"view_count": 1250,
"created_at": "2024-01-20T10:30:00",
"updated_at": "2024-01-20T10:30:00"
}
],
"total": 50,
"page": 1,
"size": 10
}
POST /api/v1/news/posts
// Request
{
"title": "飞点健康完成A轮融资",
"slug": "feidian-series-a",
"content": "# 飞点健康完成A轮融资\n\n今日...",
"content_type": "markdown",
"summary": "今日,飞点健康宣布完成数千万元A轮融资...",
"cover_image": "/uploads/covers/xxx.jpg",
"category_id": 1,
"is_draft": false
}
4. 前端设计
4.1 页面结构
/admin/news # 资讯管理首页(列表)
/admin/news/posts # 资讯列表
/admin/news/posts/new # 新建资讯
/admin/news/posts/:id/edit # 编辑资讯
/admin/news/categories # 栏目管理
/admin/images # 图片管理(可作为顶级菜单)
/news # 访客端新闻列表页
/news/:slug # 访客端新闻详情页
4.2 组件设计
4.2.1 管理端组件
src/pages/admin/news/
├── NewsLayout.jsx # 新闻模块布局(侧边栏导航)
├── PostList.jsx # 资讯列表页
├── PostEdit.jsx # 资讯编辑页(双编辑器支持)
├── CategoryList.jsx # 栏目列表页
└── components/
├── MarkdownEditor.jsx # Markdown编辑器(复用blog)
├── HtmlEditor.jsx # HTML富文本编辑器
├── PostForm.jsx # 资讯表单(标题、栏目、封面等)
├── CategoryForm.jsx # 栏目表单
├── ImagePicker.jsx # 图片选择器
└── PostPreview.jsx # 文章预览组件
src/pages/admin/images/
├── ImageManager.jsx # 图片管理页(可作为独立模块)
└── components/
├── ImageUploader.jsx # 图片上传组件
├── ImageGrid.jsx # 图片网格展示
└── ImageDetail.jsx # 图片详情/编辑
4.2.2 编辑器选型
| 格式 | 编辑器 | 特点 | 图片支持 |
|---|---|---|---|
| Markdown | react-markdown-editor-lite | 轻量、性能好 | 支持粘贴/拖拽上传 |
| HTML | TinyMCE 6 | 功能丰富、稳定 | 内置图片上传插件 |
TinyMCE 配置要点:
{
plugins: 'image link media table code fullscreen',
toolbar: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright | bullist numlist | image media link | table | code fullscreen',
images_upload_url: '/api/v1/images/upload',
images_upload_handler: customUploadHandler
}
4.3 路由配置
// 添加到 AdminLayout 侧边栏
{
path: '/admin/news',
label: '新闻资讯',
icon: Newspaper,
children: [
{ path: '/admin/news/posts', label: '资讯管理' },
{ path: '/admin/news/categories', label: '栏目管理' }
]
}
// 独立图片管理(可作为顶级菜单)
{
path: '/admin/images',
label: '图片管理',
icon: Image
}
5. 复用代码清单
5.1 从 personal-blog 复用
| 组件/模块 | 源文件路径 | 目标路径 | 修改点 |
|---|---|---|---|
| 图片模型 | backend/models.py Image | backend/app/models/image.py | 添加 module 字段 |
| 图片上传API | backend/routers/images.py | backend/app/routers/images.py | 适配新路由前缀 |
| 图片管理页 | frontend/src/pages/admin/Images.jsx | frontend/src/pages/admin/images/ImageManager.jsx | 样式统一 |
| Markdown编辑器 | 无(需新增) | frontend/src/pages/admin/news/components/MarkdownEditor.jsx | - |
5.2 复用策略
- 图片管理:完全复用 blog 的图片表结构,添加
module字段区分来源 - 文件上传:复用上传逻辑,统一存储到
/uploads/news/ - 缩略图生成:复用 PIL 缩略图生成逻辑
6. 访客端展示
6.1 新闻列表页 (/news)
- 顶部显示栏目筛选标签
- 列表展示:封面图、标题、摘要、发布时间、浏览量
- 支持分页加载
6.2 新闻详情页 (/news/:slug)
- 文章标题、发布时间、栏目
- 根据 content_type 渲染不同格式
- Markdown: 使用 react-markdown + remark-gfm
- HTML: 使用 dangerouslySetInnerHTML(需 XSS 过滤)
6.3 首页新闻模块联动
home_modules 表中 type=news 的模块,从 news_posts 表读取最新 N 条数据展示。
7. 实现步骤
Phase 1: 数据库与后端 API(预计 2-3 天)
- 创建数据库表(categories, posts, images)
- 实现栏目管理 API
- 实现资讯管理 API
- 复用/改造图片上传 API
- 编写 API 测试
Phase 2: 管理端前端(预计 3-4 天)
- 创建新闻模块路由和布局
- 实现栏目管理页面
- 实现 Markdown 编辑器组件
- 实现 HTML 编辑器组件(TinyMCE 集成)
- 实现资讯列表页
- 实现资讯编辑页(双编辑器切换)
- 实现图片管理页面
Phase 3: 访客端与联调(预计 2 天)
- 实现新闻列表页
- 实现新闻详情页(支持 Markdown/HTML 渲染)
- 联调首页新闻模块数据
- 顶部菜单"新闻动态"链接配置
Phase 4: 优化与测试(预计 1-2 天)
- 图片懒加载优化
- XSS 防护(HTML 内容过滤)
- 移动端适配
- 性能测试
8. 技术要点
8.1 XSS 防护
对于 HTML 格式的内容,必须做 XSS 过滤:
# 后端存储前过滤(可选)
from bleach import clean
allowed_tags = ['p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'ul', 'ol', 'li', 'a', 'img', 'table', 'thead', 'tbody', 'tr', 'td', 'th']
allowed_attrs = {'a': ['href', 'title'], 'img': ['src', 'alt', 'width', 'height']}
cleaned_html = clean(raw_html, tags=allowed_tags, attributes=allowed_attrs)
8.2 图片存储规范
uploads/
├── news/ # 新闻资讯图片
│ ├── 2024/01/ # 按年月组织
│ │ ├── xxx.jpg
│ │ └── thumbnails/ # 缩略图
│ └── ...
├── products/ # 产品图片
├── pages/ # 页面图片
└── common/ # 通用图片
8.3 slug 生成规则
def generate_slug(title: str) -> str:
"""将标题转换为 URL 友好的 slug"""
# 1. 移除特殊字符
# 2. 空格替换为连字符
# 3. 转小写
# 4. 处理重复(添加数字后缀)
9. 扩展性考虑
9.1 未来可扩展功能
- 文章标签系统
- 文章评论功能
- 文章定时发布
- 文章版本历史
- 文章 SEO 元数据管理
- 多语言支持
9.2 图片管理升级
当前作为新闻模块子功能,后续可提升为系统级资源中心,支持:
- 图片分类/标签
- 批量上传
- 图片搜索
- 在线编辑(裁剪、滤镜)
10. 参考资源
10.1 编辑器文档
- TinyMCE React: https://www.tiny.cloud/docs/tinymce/6/react-cloud/
- react-markdown-editor-lite: https://github.com/HarryChen0506/react-markdown-editor-lite
10.2 代码参考
- personal-blog Markdown 编辑实现
- personal-blog 图片管理实现
文档版本: 1.0
创建时间: 2026-04-20
作者: nanobot