| 模块 | 功能点 | 说明 |
|---|---|---|
| 文章展示 | 文章列表 | 分页展示所有已发布文章 |
| 文章详情 | 查看单篇文章完整内容 | |
| 分类筛选 | 按分类筛选文章 | |
| 标签筛选 | 按标签筛选文章 | |
| 搜索文章 | 关键词搜索文章标题/内容 | |
| 用户交互 | 用户注册/登录 | 邮箱注册、JWT认证 |
| 评论功能 | 发表/回复评论(需登录) | |
| 点赞收藏 | 文章点赞、收藏(需登录) | |
| 个人中心 | 查看我的文章、收藏、评论 |
| 模块 | 功能点 | 说明 |
|---|---|---|
| 仪表盘 | 数据统计 | 文章数、评论数、访问量统计 |
| 文章管理 | 文章CRUD | 创建、编辑、删除、发布文章 |
| 分类管理 | 增删改查文章分类 | |
| 标签管理 | 增删改查文章标签 | |
| 评论管理 | 评论审核 | 审核/回复/删除评论 |
| 用户管理 | 用户列表 | 查看/禁用/启用用户 |
| 角色权限 | 管理员/普通用户权限控制 | |
| 系统设置 | 站点配置 | 站点名称、Logo、SEO设置 |
-- 用户表
CREATE TABLE `users` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL UNIQUE,
`email` VARCHAR(100) NOT NULL UNIQUE,
`password_hash` VARCHAR(255) NOT NULL,
`avatar` VARCHAR(255) DEFAULT '/default-avatar.png',
`bio` TEXT,
`role` ENUM('user', 'admin') DEFAULT 'user',
`is_active` BOOLEAN DEFAULT TRUE,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 分类表
CREATE TABLE `categories` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL UNIQUE,
`slug` VARCHAR(50) NOT NULL UNIQUE,
`description` TEXT,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 标签表
CREATE TABLE `tags` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(30) NOT NULL UNIQUE,
`slug` VARCHAR(30) NOT NULL UNIQUE,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 文章表
CREATE TABLE `posts` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`title` VARCHAR(200) NOT NULL,
`slug` VARCHAR(200) NOT NULL UNIQUE,
`content` LONGTEXT NOT NULL,
`excerpt` TEXT,
`featured_image` VARCHAR(255),
`status` ENUM('draft', 'published', 'private') DEFAULT 'draft',
`view_count` INT DEFAULT 0,
`like_count` INT DEFAULT 0,
`category_id` INT,
`author_id` INT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`published_at` TIMESTAMP NULL,
FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`) ON DELETE SET NULL,
FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
INDEX `idx_status_published` (`status`, `published_at`),
INDEX `idx_category` (`category_id`)
);
-- 文章标签关联表
CREATE TABLE `post_tags` (
`post_id` INT NOT NULL,
`tag_id` INT NOT NULL,
PRIMARY KEY (`post_id`, `tag_id`),
FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`) ON DELETE CASCADE
);
-- 评论表
CREATE TABLE `comments` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`content` TEXT NOT NULL,
`post_id` INT NOT NULL,
`user_id` INT,
`parent_id` INT DEFAULT NULL,
`status` ENUM('pending', 'approved', 'spam') DEFAULT 'pending',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL,
FOREIGN KEY (`parent_id`) REFERENCES `comments`(`id`) ON DELETE CASCADE,
INDEX `idx_post_status` (`post_id`, `status`)
);
-- 收藏表
CREATE TABLE `favorites` (
`user_id` INT NOT NULL,
`post_id` INT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`, `post_id`),
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON DELETE CASCADE
);
-- 点赞表
CREATE TABLE `likes` (
`user_id` INT NOT NULL,
`post_id` INT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`, `post_id`),
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON DELETE CASCADE
);
// POST /api/auth/register
Request:
{
"username": "johndoe",
"email": "john@example.com",
"password": "Password123!"
}
Response:
{
"code": 200,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"username": "johndoe",
"email": "john@example.com",
"role": "user"
}
}
}
// GET /api/posts?page=1&limit=10&category=tech
Response:
{
"code": 200,
"data": {
"total": 100,
"page": 1,
"limit": 10,
"items": [
{
"id": 1,
"title": "Vue3 组合式API详解",
"slug": "vue3-composition-api",
"excerpt": "本文深入讲解Vue3的组合式API...",
"featured_image": "/images/vue3.jpg",
"view_count": 1500,
"like_count": 89,
"category": { "id": 1, "name": "前端" },
"tags": [{ "id": 1, "name": "Vue" }],
"author": { "id": 1, "username": "admin", "avatar": "/avatar.jpg" },
"created_at": "2024-01-15T08:30:00Z"
}
]
}
}
// POST /api/posts (Admin)
Request:
{
"title": "新文章标题",
"content": "Markdown格式内容...",
"category_id": 1,
"tag_ids": [1, 2],
"status": "published",
"featured_image": "/uploads/image.jpg"
}
// POST /api/posts/1/comments
Request:
{
"content": "这篇文章写得非常好!",
"parent_id": null
}
Response:
{
"code": 200,
"data": {
"id": 101,
"content": "这篇文章写得非常好!",
"user": { "username": "johndoe", "avatar": "/avatar.jpg" },
"created_at": "2024-01-16T10:30:00Z"
}
}
// GET /api/admin/stats
Response:
{
"code": 200,
"data": {
"posts_count": 156,
"comments_count": 892,
"users_count": 345,
"views_today": 1234,
"popular_posts": [
{ "id": 1, "title": "热门文章1", "views": 5000 }
]
}
}
| 层级 | 技术方案 |
|---|---|
| 前端 | Vue3 + Pinia + Element Plus + Markdown编辑器 |
| 后端 | Java Spring Boot |
| 数据库 | MySQL 8.0 |
| 部署 | Docker + Nginx |
- JWT 仅在功能描述中出现,缺少刷新令牌、吊销策略、密钥轮换策略。
- 权限模型粗粒度,需持续防御水平越权与垂直越权。
- 文章与评论内容存在富文本输入面,需严格输出转义与白名单过滤,防止存储型 XSS。
- 登录/注册/评论/点赞等接口需限流与风控,防止撞库和刷接口。
- 密码策略需明确(强度校验、哈希成本参数、重置流程)。
- 输入边界需统一(分页上限、字段长度、排序白名单、非法参数兜底)。
- 关键操作需审计日志(登录失败、权限变更、内容删除、评论审核)。
- 安全响应头(CSP、X-Frame-Options 等)和上传安全策略尚未纳入本轮。
- 高级 RBAC、设备管理、异常行为检测等属于后续增强项。
- 用户注册/登录(JWT)
- 文章列表与详情(分页、关键词、分类、标签筛选)
- 评论发布与展示
- 点赞/收藏(幂等处理)
- 简版管理端接口:文章 CRUD、分类 CRUD、基础统计
- MyBatis + 手工 SQL 建表脚本(schema.sql、data.sql)
- Vue3 + Element Plus + Pinia + Vue Router + Axios
- 页面:首页、文章详情、登录注册、管理仪表盘、文章管理、分类管理
- 响应式适配:移动端/平板/桌面断点布局
- 邮件激活、刷新令牌与多端会话管理
- 复杂 RBAC 权限矩阵
- 全文检索(ElasticSearch)
- 对象存储上传与图片审核
- 高级风控与告警系统
| 依赖 | 版本 | 说明 |
|---|---|---|
| JDK | 17+ | 后端运行环境 |
| Node.js | 20.19+ / 22.12+ | 前端运行环境 |
| MySQL | 8.0+ | 数据库 |
| Redis | 7.x+ | 缓存 |
| RustFS | - | S3 兼容对象存储(可选) |
创建数据库:
CREATE DATABASE my_blog DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;启动 Redis(默认端口 6379),无需额外配置。Spring Boot 自动连接 localhost:6379。
项目使用 RustFS 作为文件存储服务。如不需要文件上传功能可暂时跳过。
编辑 my_blog/src/main/resources/application.properties,修改数据库连接信息:
spring.datasource.url=jdbc:mysql://localhost:3306/my_blog?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=你的数据库密码将 JWT 密钥替换为一个随机字符串(至少 256 位):
jwt.secret=替换为你自己的长随机密钥可以使用以下命令生成:
# Linux / Mac
openssl rand -base64 64
# Windows PowerShell
[Convert]::ToBase64String((1..64 | ForEach-Object { Get-Random -Maximum 256 }))storage.provider=rustfs
storage.uploadUrl=http://127.0.0.1:9000/api/upload
storage.endpoint=http://127.0.0.1:9000
storage.bucket=my-bolg-bucket
storage.baseUrl=http://127.0.0.1:9000
storage.access-key-id=rustfsadmin
storage.secret-access-key=rustfsadmin项目启动时会自动执行 schema.sql 和 data.sql 建表并插入初始化数据。
cd my_blog
./mvnw spring-boot:run后端运行在 http://localhost:8080。
cd my_blog/frontend/my_blog_frontend
npm install前端开发时通过 Vite 代理转发 API 请求到后端。如需修改,编辑 vite.config.js:
export default defineConfig({
server: {
proxy: {
'/api': 'http://localhost:8080'
}
}
// ...
})npm run dev前端开发服务器运行在 http://localhost:5173。
project/
├── INSTALL.md # 本文件
└── my_blog/
├── pom.xml # Maven 配置
├── src/main/
│ ├── java/com/fun/my_blog/
│ │ ├── config/ # 配置类
│ │ ├── controller/ # 控制器
│ │ ├── domain/ # 实体类
│ │ ├── dto/ # 数据传输对象
│ │ ├── mapper/ # MyBatis Mapper
│ │ ├── security/ # Spring Security + JWT
│ │ └── service/ # 业务层
│ └── resources/
│ ├── application.properties
│ ├── schema.sql # 数据库建表
│ └── data.sql # 初始数据
└── frontend/
└── my_blog_frontend/ # Vue 3 前端项目
└── src/
| 服务 | 端口 |
|---|---|
| 后端 API | 8080 |
| 前端 Dev Server | 5173 |
| MySQL | 3306 |
| Redis | 6379 |
| RustFS | 9000 |
Q: 启动报错 "Access denied for user"
检查 application.properties 中的数据库用户名和密码是否正确。
Q: 启动报错 "Unknown database 'my_blog'"
需要先在 MySQL 中创建数据库:CREATE DATABASE my_blog;
Q: 前端请求 API 报 CORS 错误
确认 app.cors.allowed-origins 配置的前端地址与实际一致。
Q: 文件上传失败
确保 RustFS 服务已启动,且 storage.* 配置与 RustFS 实例匹配。如暂时不需要文件上传,可忽略该错误。


