55 KiB
博客系统技术设计说明书
| 文档版本 | 修订日期 | 修订人 | 修订内容 |
|---|---|---|---|
| V1.0 | 2026-05-17 | — | 初始版本,完整技术设计 |
1. 项目概述
1.1 项目定位
基于需求文档 blog-system-requirements.md 定义的博客系统功能需求与非功能性需求,本技术设计文档将作为 AI 编程的唯一输入依据,指导系统从零开始的完整开发。
1.2 技术栈选型
| 层级 | 技术 | 版本/说明 |
|---|---|---|
| 后端框架 | Spring Boot | 3.4.x(最新稳定版) |
| JDK | OpenJDK | 21(LTS) |
| 项目构建 | Maven | 3.9+ |
| 数据库 | MySQL | 8.0+ |
| 缓存 | Redis | 7.2.x(BSD 3-Clause 许可证,用于会话管理、验证码、限流计数) |
| ORM | MyBatis-Plus | 3.5.x |
| 认证鉴权 | Spring Security + JWT | 无状态令牌认证 |
| 接口文档 | SpringDoc OpenAPI | 2.x(Swagger UI) |
| 前端框架 | Vue.js | 3.x(Vite 构建) |
| UI 组件库 | Element Plus | 2.x |
| 富文本编辑器 | Tiptap(MIT 许可证)/ TinyMCE 社区版(LGPL,不使用 Premium) | 免费开源版 |
| 数据库迁移 | Flyway | 9.x |
1.3 项目结构(单体应用、前后端一体化构建)
整个项目为一个 Maven 多模块项目,前端构建产物集成到后端静态资源目录中,通过 mvn package 一次编译生成可直接部署的 FAT JAR。
blog-system/
├── pom.xml # 父 POM
├── blog-common/ # 公共模块
│ ├── pom.xml
│ └── src/main/java/com/blog/common/
│ ├── annotation/ # 自定义注解
│ ├── config/ # 公共配置
│ ├── constant/ # 常量定义
│ ├── enums/ # 枚举定义
│ ├── exception/ # 统一异常
│ ├── model/ # 公共模型(分页、统一响应)
│ └── util/ # 工具类
│
├── blog-framework/ # 框架核心模块
│ ├── pom.xml
│ └── src/main/java/com/blog/framework/
│ ├── config/ # Spring Boot 配置
│ ├── security/ # Spring Security + JWT
│ ├── interceptor/ # 拦截器
│ ├── aspect/ # AOP 切面
│ ├── filter/ # 过滤器
│ ├── handler/ # 全局异常处理
│ └── rateLimiter/ # 限流组件
│
├── blog-module-system/ # 系统管理模块
│ ├── pom.xml
│ └── src/main/java/com/blog/module/system/
│ ├── controller/
│ │ ├── admin/ # 后台管理接口
│ │ └── app/ # 前台用户接口
│ ├── service/
│ ├── mapper/
│ ├── entity/
│ └── dto/
│
├── blog-module-blog/ # 博客内容模块
│ ├── pom.xml
│ └── src/main/java/com/blog/module/blog/
│ ├── controller/
│ │ ├── admin/
│ │ ├── app/
│ │ └── api/ # 公开接口(无需认证)
│ ├── service/
│ ├── mapper/
│ ├── entity/
│ └── dto/
│
├── blog-module-comment/ # 评论模块
│ ├── pom.xml
│ └── src/main/java/com/blog/module/comment/
│ ├── controller/
│ ├── service/
│ ├── mapper/
│ ├── entity/
│ └── dto/
│
├── blog-admin/ # 后台管理 Web 模块
│ ├── pom.xml
│ └── src/main/
│ ├── java/com/blog/admin/
│ │ └── BlogAdminApplication.java
│ └── resources/
│ ├── application.yml
│ ├── application-dev.yml
│ └── application-prod.yml
│
└── blog-frontend/ # 前端工程(Vue 3)
├── package.json
├── vite.config.ts
├── index.html
└── src/
├── api/ # API 调用封装
├── assets/ # 静态资源
├── components/ # 公共组件
├── layouts/ # 布局组件
├── router/ # 路由
├── store/ # Pinia 状态管理
├── utils/ # 工具函数
├── views/ # 页面
│ ├── admin/ # 后台管理页面
│ └── front/ # 前台展示页面
└── App.vue
前后端一体化构建流程:
mvn clean package
执行上述命令时:
blog-frontend模块通过frontend-maven-plugin自动安装 Node.js 依赖并执行npm run build- 构建产物 (
dist/) 通过 Maven 资源插件复制到blog-admin/src/main/resources/static/ blog-admin模块打包为 FAT JAR,内嵌 Tomcat,直接通过java -jar blog-admin.jar启动
2. 系统架构设计
2.1 分层架构
系统采用经典的四层架构,严格遵循各层职责单一原则:
┌─────────────────────────────────────────────────────┐
│ Controller 层 │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ admin/ │ │ app/ │ │ api/ │
│ │ 后台管理接口 │ │ 前台用户接口 │ │ 公开接口 │
│ └──────┬────────┘ └──────┬────────┘ └──────┬─────┘│
│ │ │ │ │
├─────────┼──────────────────┼──────────────────┼────────┤
│ │ DTO / VO 数据校验与转换 │
├─────────┼──────────────────┼──────────────────┼────────┤
│ ▼ ▼ ▼ │
│ Service 层 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 接口定义(Interface) → 实现类(Impl) │ │
│ │ 事务管理 @Transactional │ │
│ │ 业务校验 → 调用 Mapper → 返回结果 │ │
│ └──────────────────────┬──────────────────────────┘ │
│ │ │
├─────────────────────────┼─────────────────────────────┤
│ ▼ │
│ Mapper 层 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ MyBatis-Plus BaseMapper 扩展 │ │
│ │ 自定义 XML / 注解 SQL │ │
│ └──────────────────────┬──────────────────────────┘ │
│ │ │
├─────────────────────────┼─────────────────────────────┤
│ ▼ │
│ Entity 层 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 数据库表映射实体(@TableName, @TableId) │ │
│ │ 逻辑删除 @TableLogic │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
2.2 请求处理链路
Client Request
│
▼
Filter Chain: CorsFilter → RateLimitFilter → XssFilter
│
▼
DispatcherServlet
│
▼
Interceptor: LoginInterceptor(校验 JWT Token)
│
▼
AOP Aspect: LogAspect(操作日志)→ RateLimitAspect(限流)
│
▼
Controller → 参数校验(@Valid / @Validated)
│
▼
Service → 业务处理
│
▼
Mapper → 数据库操作
│
▼
统一响应封装 → JSON 返回
2.3 认证鉴权架构
使用 JWT(JSON Web Token) 实现无状态认证,结合 Spring Security 进行权限控制。
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 客户端 │────▶│ LoginController │───▶│ AuthService │
│ (浏览器) │ │ /api/auth/* │ │ 认证服务 │
└──────────────┘ └──────────────┘ └──────┬───────┘
│
┌──────────────────────────────────┤
▼ ▼
┌────────────────┐ ┌──────────────────┐
│ 校验通过 │ │ Redis 缓存会话 │
│ 生成 AccessToken │◀───────────────│ 多端登录控制 │
│ + RefreshToken │ │ 最多 3 个活跃端 │
└────────┬───────┘ └──────────────────┘
│
▼
┌────────────────┐
│ 返回 Token 给 │
│ 客户端(HTTP Only│
│ Cookie 或 Header│
└────────────────┘
Token 策略:
| Token 类型 | 有效期 | 用途 |
|---|---|---|
| AccessToken | 2 小时 | 携带在请求头 Authorization: Bearer <token> 中,鉴权通过后访问受保护资源 |
| RefreshToken | 14 天(记住我)/ 24 小时 | 用于刷新 AccessToken,存储在 Redis 中 |
2.4 统一响应格式
所有 API 接口统一返回如下结构:
{
"code": 200,
"message": "success",
"data": {},
"timestamp": 1715932800000
}
| 字段 | 类型 | 说明 |
|---|---|---|
| code | Integer | 业务状态码(200=成功, 400=参数错误, 401=未认证, 403=无权限, 404=资源不存在, 429=请求过频, 500=服务端错误) |
| message | String | 提示信息 |
| data | Object | 响应数据,分页时包含 {records, total, page, pageSize} |
| timestamp | Long | 响应时间戳 |
2.5 异常体系
BusinessException (业务异常,code + message)
├── BadRequestException (400)
├── UnauthorizedException (401)
├── ForbiddenException (403)
├── ResourceNotFoundException (404)
├── RateLimitException (429)
└── DuplicateOperationException (409)
全局异常处理器 GlobalExceptionHandler 捕获所有异常并统一返回标准响应格式。
3. 模块划分与职责
3.1 模块总览
| 模块 | 职责 | 关键接口前缀 |
|---|---|---|
blog-module-system |
用户管理、角色权限、登录/注册、登录日志、系统配置 | /api/auth/**, /api/admin/system/** |
blog-module-blog |
博客文章 CRUD、分类管理、标签管理、草稿管理、全文检索 | /api/blog/**, /api/admin/blog/** |
blog-module-comment |
评论 CRUD、多级回复、评论审核、敏感词过滤、回复通知 | /api/comment/**, /api/admin/comment/** |
3.2 模块间依赖关系
blog-common (无依赖)
↑
blog-framework (依赖 blog-common)
↑
┌───────────────┼───────────────────┐
│ │ │
blog-module-system blog-module-blog──┐
│ │ │ │
│ │ │ │
└───────────────┼───────────────────┘ │
│ │
▼ ▼
blog-module-comment blog-module-blog
说明:
blog-module-system无业务模块依赖blog-module-blog依赖blog-module-system(获取用户信息、鉴权)blog-module-comment依赖blog-module-blog(获取文章信息)和blog-module-system(获取用户信息、通知)
3.3 业务能力清单
按需求文档功能需求编号映射的模块能力:
| 需求编号 | 功能点 | 所属模块 | 接口说明 |
|---|---|---|---|
| FR-ADM-LOGIN-01~06 | 管理员登录 | blog-module-system | 管理员认证、验证码、锁定、日志 |
| FR-USER-REG-01~06 | 用户注册 | blog-module-system | 邮箱注册、验证、频率限制 |
| FR-USER-LOGIN-01~06 | 用户登录 | blog-module-system | 邮箱登录、多端限制、第三方预留 |
| FR-BLOG-PUB-01~07 | 博客发布 | blog-module-blog | 富文本编辑、草稿、状态管理、标签 |
| FR-BLOG-LIST-01~08 | 博客列表查看 | blog-module-blog | 排序、筛选、检索、分页、浏览量 |
| FR-COMMENT-01~07 | 评论功能 | blog-module-comment | 多级回复、审核、屏蔽、通知 |
4. 数据模型设计
4.1 数据库 E-R 关系概览
User (1) ──────< Blog (N) # 用户拥有多篇博客
Blog (N) ──────< BlogTag (N) # 博客关联多个标签(中间表)
Tag (N) ──────< BlogTag (N) # 标签被多篇博客使用
Category (1) ──< Blog (N) # 分类下有多篇博客
Blog (1) ──────< Comment (N) # 博客有多条评论
Comment (N) ───< Comment (N) # 评论自关联(多级回复)
User (1) ──────< Comment (N) # 用户发表多条评论
User (1) ──────< UserFollow (N) # 用户关注关系
Blog (1) ──────< BlogView (N) # 博客浏览记录
User (1) ──────< Notification (N) # 用户接收多条通知
4.2 数据字典
4.2.1 用户表 sys_user
| 字段名 | 类型 | 长度 | 主键 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 | 用户 ID |
| username | VARCHAR | 50 | — | N | NULL | 用户名(管理员专用) |
| VARCHAR | 100 | — | Y | — | 邮箱(唯一索引) | |
| phone | VARCHAR | 20 | — | N | NULL | 手机号(管理员专用) |
| password | VARCHAR | 255 | — | Y | — | bcrypt 加盐哈希值 |
| nickname | VARCHAR | 50 | — | N | NULL | 昵称 |
| avatar | VARCHAR | 500 | — | N | NULL | 头像 URL |
| user_type | TINYINT | — | — | Y | 1 | 用户类型:0=超级管理员, 1=注册用户 |
| status | TINYINT | — | — | Y | 0 | 状态:0=正常, 1=锁定 |
| locked_until | DATETIME | — | — | N | NULL | 锁定截止时间 |
| login_fail_count | INT | — | — | Y | 0 | 连续登录失败次数 |
| email_verified | TINYINT | — | — | Y | 0 | 邮箱是否已验证:0=未验证, 1=已验证 |
| last_login_ip | VARCHAR | 50 | — | N | NULL | 最后登录 IP |
| last_login_time | DATETIME | — | — | N | NULL | 最后登录时间 |
| created_at | DATETIME | — | — | Y | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | — | — | Y | CURRENT_TIMESTAMP ON UPDATE | 更新时间 |
| deleted | TINYINT | — | — | Y | 0 | 逻辑删除:0=未删除, 1=已删除 |
索引:
uk_email:唯一索引(email, deleted)uk_username:唯一索引(username, deleted)idx_status:普通索引(status)
初始化数据:
- 系统初始化 Flyway 脚本自动插入超级管理员账号(username:
admin, password:Admin@2026,需登录后强制修改)
4.2.2 用户会话表 sys_user_session
| 字段名 | 类型 | 长度 | 主键 | 必填 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 ID |
| user_id | BIGINT | — | N | Y | 用户 ID(外键) |
| token | VARCHAR | 500 | — | Y | JWT Token(或标识) |
| device_fingerprint | VARCHAR | 200 | — | N | 设备指纹 |
| ip_address | VARCHAR | 50 | — | N | 登录 IP |
| user_agent | VARCHAR | 500 | — | N | 浏览器 UA |
| expire_time | DATETIME | — | — | Y | 过期时间 |
| created_at | DATETIME | — | — | Y | 创建时间 |
索引:
idx_user_id:普通索引(user_id)idx_token:唯一索引(token)
多端限制逻辑:
- 查询
sys_user_session统计 user_id 的活跃会话数(未过期的) - 如果 ≥ 3,删除最早的一条,再插入新会话
- 实际 Token 校验走 Redis 缓存,DB 做持久化兜底
4.2.3 登录日志表 sys_login_log
| 字段名 | 类型 | 长度 | 主键 | 必填 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 ID |
| user_id | BIGINT | — | — | N | 用户 ID(失败时可能为空) |
| username | VARCHAR | 50 | — | N | 登录时使用的用户名/邮箱 |
| login_type | TINYINT | — | — | Y | 登录类型:0=管理员登录, 1=用户登录 |
| result | TINYINT | — | — | Y | 结果:0=失败, 1=成功 |
| fail_reason | VARCHAR | 200 | — | N | 失败原因 |
| ip_address | VARCHAR | 50 | — | Y | 登录 IP |
| device_fingerprint | VARCHAR | 200 | — | N | 设备指纹 |
| created_at | DATETIME | — | — | Y | 创建时间 |
索引:
idx_user_id:普通索引(user_id)idx_created_at:普通索引(created_at)
4.2.4 博客分类表 blog_category
| 字段名 | 类型 | 长度 | 主键 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 | 分类 ID |
| name | VARCHAR | 50 | — | Y | — | 分类名称(唯一) |
| description | VARCHAR | 200 | — | N | NULL | 分类描述 |
| sort_order | INT | — | — | Y | 0 | 排序序号 |
| status | TINYINT | — | — | Y | 0 | 状态:0=启用, 1=禁用 |
| created_at | DATETIME | — | — | Y | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | — | — | Y | CURRENT_TIMESTAMP ON UPDATE | 更新时间 |
| deleted | TINYINT | — | — | Y | 0 | 逻辑删除 |
索引:uk_name:唯一索引(name, deleted)
4.2.5 博客标签表 blog_tag
| 字段名 | 类型 | 长度 | 主键 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 | 标签 ID |
| name | VARCHAR | 30 | — | Y | — | 标签名称(唯一) |
| created_at | DATETIME | — | — | Y | CURRENT_TIMESTAMP | 创建时间 |
| deleted | TINYINT | — | — | Y | 0 | 逻辑删除 |
4.2.6 博客文章表 blog_article
| 字段名 | 类型 | 长度 | 主键 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 | 文章 ID |
| author_id | BIGINT | — | — | Y | — | 作者用户 ID(外键) |
| category_id | BIGINT | — | — | N | NULL | 分类 ID(外键) |
| title | VARCHAR | 200 | — | Y | — | 文章标题 |
| summary | VARCHAR | 500 | — | N | NULL | 文章摘要(自动截取前 120 字) |
| content | LONGTEXT | — | — | Y | — | 文章正文(HTML 格式) |
| content_text | LONGTEXT | — | — | N | NULL | 纯文本内容(用于全文检索) |
| cover_image | VARCHAR | 500 | — | N | NULL | 封面图 URL |
| status | TINYINT | — | — | Y | 0 | 发布状态:0=草稿, 1=已发布, 2=回收站 |
| visibility | TINYINT | — | — | Y | 0 | 可见性:0=公开, 1=私密, 2=仅粉丝可见 |
| view_count | INT | — | — | Y | 0 | 浏览量 |
| comment_count | INT | — | — | Y | 0 | 评论数 |
| like_count | INT | — | — | Y | 0 | 点赞数 |
| is_top | TINYINT | — | — | Y | 0 | 是否置顶:0=否, 1=是 |
| published_at | DATETIME | — | — | N | NULL | 发布时间 |
| created_at | DATETIME | — | — | Y | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | DATETIME | — | — | Y | CURRENT_TIMESTAMP ON UPDATE | 更新时间 |
| deleted | TINYINT | — | — | Y | 0 | 逻辑删除 |
索引:
idx_author_id:普通索引(author_id)idx_category_id:普通索引(category_id)idx_status_visibility:联合索引(status, visibility)idx_published_at:普通索引(published_at)idx_view_count:普通索引(view_count)FULLTEXT idx_fulltext:全文索引(title, content_text)
4.2.7 文章-标签关联表 blog_article_tag
| 字段名 | 类型 | 长度 | 主键 | 必填 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 ID |
| article_id | BIGINT | — | — | Y | 文章 ID(外键) |
| tag_id | BIGINT | — | — | Y | 标签 ID(外键) |
索引:uk_article_tag:唯一索引(article_id, tag_id)
4.2.8 文章浏览记录表 blog_article_view
| 字段名 | 类型 | 长度 | 主键 | 必填 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 ID |
| article_id | BIGINT | — | — | Y | 文章 ID(外键) |
| user_id | BIGINT | — | — | N | 用户 ID(未登录为 NULL) |
| ip_address | VARCHAR | 50 | — | Y | 访问者 IP |
| created_at | DATETIME | — | — | Y | 访问时间 |
索引:
idx_article_date:联合索引(article_id, DATE(created_at))idx_user_article:唯一索引(user_id, article_id, DATE(created_at)),用于同一天同一用户去重
浏览量计数逻辑:
- 同一用户对同一篇文章每天只计入 1 次浏览量
- 作者本人访问不计入浏览量
- 未登录访客通过 IP + User-Agent 组合去重(每天仅计 1 次)
4.2.9 评论表 blog_comment
| 字段名 | 类型 | 长度 | 主键 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 | 评论 ID |
| article_id | BIGINT | — | — | Y | — | 文章 ID(外键) |
| user_id | BIGINT | — | — | Y | — | 评论者用户 ID(外键) |
| parent_id | BIGINT | — | — | N | NULL | 父评论 ID(NULL 表示顶级评论) |
| reply_to_id | BIGINT | — | — | N | NULL | 被回复的评论 ID |
| reply_to_user_id | BIGINT | — | — | N | NULL | 被回复的用户 ID |
| content | TEXT | — | — | Y | — | 评论内容(已过滤 XSS) |
| level | TINYINT | — | — | Y | 1 | 评论层级:1~3 |
| status | TINYINT | — | — | Y | 1 | 状态:0=待审核, 1=已通过, 2=已屏蔽, 3=已删除 |
| is_author | TINYINT | — | — | Y | 0 | 是否为文章作者:0=否, 1=是 |
| created_at | DATETIME | — | — | Y | CURRENT_TIMESTAMP | 创建时间 |
| deleted | TINYINT | — | — | Y | 0 | 逻辑删除 |
索引:
idx_article_id:普通索引(article_id)idx_parent_id:普通索引(parent_id)idx_user_id:普通索引(user_id)idx_status:普通索引(status)
4.2.10 用户关注表 sys_user_follow
| 字段名 | 类型 | 长度 | 主键 | 必填 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 ID |
| follower_id | BIGINT | — | — | Y | 关注者用户 ID |
| followee_id | BIGINT | — | — | Y | 被关注者用户 ID |
| created_at | DATETIME | — | — | Y | 创建时间 |
索引:uk_follower_followee:唯一索引(follower_id, followee_id)
4.2.11 通知表 sys_notification
| 字段名 | 类型 | 长度 | 主键 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 | 通知 ID |
| receiver_id | BIGINT | — | — | Y | — | 接收通知的用户 ID |
| sender_id | BIGINT | — | — | N | NULL | 触发通知的用户 ID |
| type | TINYINT | — | — | Y | — | 通知类型:0=回复评论, 1=点赞, 2=系统通知 |
| content | VARCHAR | 500 | — | Y | — | 通知内容 |
| related_id | BIGINT | — | — | N | NULL | 关联的业务 ID(如评论 ID) |
| is_read | TINYINT | — | — | Y | 0 | 是否已读:0=未读, 1=已读 |
| created_at | DATETIME | — | — | Y | CURRENT_TIMESTAMP | 创建时间 |
索引:
idx_receiver_read:联合索引(receiver_id, is_read)idx_created_at:普通索引(created_at)
4.2.12 系统配置表 sys_config
| 字段名 | 类型 | 长度 | 主键 | 必填 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 ID |
| config_key | VARCHAR | 100 | — | Y | 配置键(唯一) |
| config_value | TEXT | — | — | Y | 配置值 |
| description | VARCHAR | 200 | — | N | 配置说明 |
| updated_at | DATETIME | — | — | Y | 更新时间 |
初始配置数据:
| config_key | config_value | description |
|---|---|---|
| comment_audit_enabled | false | 评论审核开关 |
| oauth_wechat_enabled | false | 微信登录开关 |
| oauth_github_enabled | false | GitHub 登录开关 |
4.2.13 敏感词表 sys_sensitive_word
| 字段名 | 类型 | 长度 | 主键 | 必填 | 说明 |
|---|---|---|---|---|---|
| id | BIGINT | — | Y | Y | 自增 ID |
| word | VARCHAR | 100 | — | Y | 敏感词 |
| replacement | VARCHAR | 100 | — | Y | 替换词(默认 ***) |
| created_at | DATETIME | — | — | Y | 创建时间 |
5. API 接口设计
5.1 接口规范
- 基础路径:
/api - 公开接口:无需认证,路径前缀
/api/public/** - 前台用户接口:需 JWT 认证,路径前缀
/api/app/** - 后台管理接口:需 JWT 认证 + 管理员角色,路径前缀
/api/admin/** - 分页参数:统一使用
page(页码从 1 开始)和pageSize(默认 15) - 请求方法:遵循 RESTful 规范
5.2 认证模块接口
5.2.1 管理员登录
| 接口 | 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|---|
| 管理员登录 | POST | /api/admin/auth/login |
无 | 管理员账号密码登录 |
| 获取验证码 | GET | /api/public/captcha |
无 | 获取图形验证码 |
| 管理员退出 | POST | /api/admin/auth/logout |
需认证 | 退出并清除会话 |
| 获取管理员信息 | GET | /api/admin/auth/profile |
需认证 | 获取当前管理员信息 |
| 查询登录日志 | GET | /api/admin/auth/login-logs |
Admin | 分页查询登录日志 |
POST /api/admin/auth/login
Request:
{
"account": "admin | 13800138000",
"password": "Admin@2026",
"captchaKey": "UUID (失败3次后必传)",
"captchaCode": "验证码 (失败3次后必传)",
"deviceFingerprint": "设备指纹"
}
Response (200):
{
"code": 200,
"message": "登录成功",
"data": {
"accessToken": "eyJhbGci...",
"refreshToken": "eyJhbGci...",
"expiresIn": 7200,
"userInfo": {
"id": 1,
"username": "admin",
"nickname": "超级管理员",
"avatar": null
}
}
}
Response (429 - 触发限流):
{
"code": 429,
"message": "登录过于频繁,请15分钟后再试"
}
GET /api/public/captcha
Response:
{
"code": 200,
"data": {
"captchaKey": "a1b2c3d4-e5f6-...",
"captchaImage": "data:image/png;base64,iVBORw0KGgo..."
}
}
5.2.2 用户登录/注册
| 接口 | 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|---|
| 用户注册 | POST | /api/public/user/register |
无 | 邮箱注册 |
| 验证邮箱 | GET | /api/public/user/verify-email |
无 | 点击验证链接 |
| 重新发送验证邮件 | POST | /api/public/user/resend-verification |
无 | 重新发送验证邮件 |
| 用户登录 | POST | /api/app/auth/login |
无 | 邮箱密码登录 |
| 用户退出 | POST | /api/app/auth/logout |
需认证 | 退出登录 |
| 刷新 Token | POST | /api/app/auth/refresh |
无 | 使用 RefreshToken 刷新 |
| 获取用户信息 | GET | /api/app/auth/profile |
需认证 | 获取当前登录用户信息 |
| 管理员重置用户密码 | PUT | /api/admin/system/user/{userId}/reset-password |
Admin | 管理员重置指定用户密码 |
POST /api/public/user/register
Request:
{
"email": "user@example.com",
"password": "Abc@123456",
"confirmPassword": "Abc@123456"
}
Response (200):
{
"code": 200,
"message": "注册成功,请查看邮箱完成验证"
}
业务逻辑:
- 校验邮箱格式和密码强度
- 校验邮箱是否已存在
- 校验同 IP 注册频率(1h 内 ≤ 3 次)
- 密码 bcrypt 加密
- 创建用户(status=0正常, email_verified=0)
- 发送验证邮件(含 24h 有效期的 token)
- 记录注册日志
POST /api/app/auth/login
Request:
{
"email": "user@example.com",
"password": "Abc@123456",
"deviceFingerprint": "设备指纹",
"rememberMe": false
}
Response (200):
{
"code": 200,
"data": {
"accessToken": "eyJhbGci...",
"refreshToken": "eyJhbGci...",
"expiresIn": 7200,
"userInfo": {
"id": 2,
"email": "user@example.com",
"nickname": "用户昵称",
"avatar": null
}
}
}
业务逻辑:
- 验证邮箱 + 密码
- 检查账号是否锁定(locked_until > now 则拒绝)
- 检查 email_verified
- 多端限制检查(同一账号最多 3 个活跃会话)
- 生成 AccessToken(2h 有效)+ RefreshToken(24h 或 14d)
- 记录登录日志
- 重置登录失败计数
5.3 博客模块接口
5.3.1 博客文章 CRUD
| 接口 | 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|---|
| 创建文章 | POST | /api/app/blog/articles |
需认证 | 创建新文章(草稿或直接发布) |
| 编辑文章 | PUT | /api/app/blog/articles/{id} |
需认证 | 修改文章(仅作者) |
| 删除文章 | DELETE | /api/app/blog/articles/{id} |
需认证 | 逻辑删除(仅作者) |
| 获取文章详情 | GET | /api/public/blog/articles/{id} |
无 | 公开可见的文章详情 |
| 获取我的文章列表 | GET | /api/app/blog/articles |
需认证 | 获取当前用户的所有文章 |
| 获取公开文章列表 | GET | /api/public/blog/articles |
无 | 公开文章的列表浏览 |
| 修改文章可见性 | PATCH | /api/app/blog/articles/{id}/visibility |
需认证 | 修改发布状态 |
| 保存草稿 | POST | /api/app/blog/articles/draft |
需认证 | 保存为草稿 |
| 自动保存草稿 | PUT | /api/app/blog/articles/{id}/draft |
需认证 | 自动保存草稿 |
GET /api/public/blog/articles — 公开文章列表
Query Parameters:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | Integer | N | 1 | 页码 |
| pageSize | Integer | N | 15 | 每页条数 |
| categoryId | Long | N | — | 分类 ID 筛选 |
| tagId | Long | N | — | 标签 ID 筛选 |
| keyword | String | N | — | 关键词全文检索 |
| sortBy | String | N | publishedAt | 排序字段:publishedAt, viewCount |
| sortOrder | String | N | desc | 排序方向:asc, desc |
Response:
{
"code": 200,
"data": {
"records": [
{
"id": 1,
"title": "Spring Boot 入门指南",
"summary": "本文介绍如何使用 Spring Boot 快速构建 Web 应用...",
"coverImage": "https://...",
"author": {
"id": 2,
"nickname": "作者昵称",
"avatar": null
},
"category": {
"id": 1,
"name": "技术教程"
},
"tags": [
{"id": 1, "name": "Java"},
{"id": 2, "name": "Spring Boot"}
],
"viewCount": 1280,
"commentCount": 15,
"publishedAt": "2026-05-15T10:30:00"
}
],
"total": 100,
"page": 1,
"pageSize": 15
}
}
全文检索实现:
- 使用 MySQL
FULLTEXT INDEX+MATCH...AGAINST实现 - 关键词高亮由前端在搜索结果中处理
GET /api/public/blog/articles/{id} — 文章详情
Response:
{
"code": 200,
"data": {
"id": 1,
"title": "Spring Boot 入门指南",
"content": "<h1>...</h1><p>...</p>",
"author": {...},
"category": {...},
"tags": [...],
"viewCount": 1280,
"commentCount": 15,
"isLiked": false,
"visibility": 0,
"publishedAt": "2026-05-15T10:30:00"
}
}
业务逻辑:
- 判断文章可见性:
status ≠ 已发布→ 404visibility = 私密且当前用户非作者 → 404visibility = 仅粉丝可见且当前用户非作者且非粉丝 → 404
- 浏览量更新(异步):
- 同一用户同一天仅计 1 次
- 作者本人不计
- 调用
CompletableFuture异步写入blog_article_view
POST /api/app/blog/articles — 创建文章
Request:
{
"title": "文章标题",
"content": "<h1>富文本内容</h1>",
"categoryId": 1,
"tagIds": [1, 2, 3],
"coverImage": "https://...",
"visibility": 0,
"status": 0
}
参数说明:
status=0保存为草稿,status=1直接发布visibility仅在发布时生效
Response (200):
{
"code": 200,
"data": {
"id": 42
}
}
业务逻辑:
- 校验标题非空、内容非空
- 校验分类存在且启用
- 校验标签数量 ≤ 5
- XSS 过滤 content(使用 Jsoup 清理)
- 自动生成 summary(截取 content_text 前 120 字)
- 设置 published_at(status=1 时)
- 插入 blog_article 和 blog_article_tag
5.3.2 分类与标签管理
| 接口 | 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|---|
| 创建分类 | POST | /api/admin/blog/categories |
Admin | 新增分类 |
| 编辑分类 | PUT | /api/admin/blog/categories/{id} |
Admin | 修改分类 |
| 删除分类 | DELETE | /api/admin/blog/categories/{id} |
Admin | 删除分类(有文章时禁止删除) |
| 分类列表 | GET | /api/public/blog/categories |
无 | 获取所有分类 |
| 创建标签 | POST | /api/admin/blog/tags |
Admin | 新增标签 |
| 删除标签 | DELETE | /api/admin/blog/tags/{id} |
Admin | 删除标签 |
| 标签列表 | GET | /api/public/blog/tags |
无 | 获取所有标签 |
5.3.3 图片上传接口
| 接口 | 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|---|
| 上传图片 | POST | /api/app/upload/image |
需认证 | 上传文章图片 |
Request:multipart/form-data,字段名 file
Response:
{
"code": 200,
"data": {
"url": "https://cdn.example.com/images/2026/05/abc123.jpg"
}
}
业务规则:
- 支持格式:JPEG, PNG, GIF, WebP
- 单图限制:≤ 10MB
- 禁止上传 .exe, .js, .bat 等可执行文件
- 上传后进行图片安全扫描(依赖第三方服务)
5.4 评论模块接口
| 接口 | 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|---|
| 发表评论 | POST | /api/app/comments |
需认证 | 对文章发表评论或回复 |
| 获取文章评论 | GET | /api/public/comments/{articleId} |
无 | 获取指定文章的评论列表 |
| 删除评论 | DELETE | /api/admin/comments/{id} |
Admin | 物理删除 |
| 屏蔽评论 | PATCH | /api/admin/comments/{id}/block |
Admin | 逻辑屏蔽 |
| 审核评论 | PATCH | /api/admin/comments/{id}/approve |
Admin | 审核通过 |
| 批量审核评论 | POST | /api/admin/comments/batch-approve |
Admin | 批量审核 |
| 配置评论审核开关 | PUT | /api/admin/system/config/comment-audit |
Admin | 开启/关闭评论审核 |
POST /api/app/comments — 发表评论
Request:
{
"articleId": 1,
"content": "写得很棒,学习了!",
"parentId": null,
"replyToId": null
}
回复评论 Request:
{
"articleId": 1,
"content": "感谢回复!",
"parentId": 5,
"replyToId": 8
}
Response (200):
{
"code": 200,
"data": {
"id": 10,
"status": 0
}
}
当评论审核开启时
status=0(待审核),否则status=1(已通过)
业务逻辑:
- 校验文章存在且可见
- 校验 parentId 有效性:
- 如果 parentId 非空,确认父评论存在且属于同一文章
- 检查层级:查询父评论的 level,如果父评论 level ≥ 3,则设置为第 3 级的平级评论
- 敏感词过滤(替换为
***) - XSS 过滤
- 判断评论者是否为文章作者(is_author)
- 评论审核开关判断:
- 开启 → status=0(待审核)
- 关闭 → status=1(已通过)
- 异步发送回复通知
GET /api/public/comments/{articleId} — 获取文章评论
Query Parameters:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | Integer | N | 1 | 页码 |
| pageSize | Integer | N | 15 | 每页条数(顶级评论数) |
Response:
{
"code": 200,
"data": {
"records": [
{
"id": 1,
"content": "好文章!",
"user": {
"id": 2,
"nickname": "小明",
"avatar": null
},
"isAuthor": false,
"level": 1,
"createdAt": "2026-05-16T10:00:00",
"children": [
{
"id": 2,
"content": "谢谢支持!",
"user": {
"id": 1,
"nickname": "作者大大",
"avatar": null
},
"isAuthor": true,
"level": 2,
"replyToUserId": 2,
"createdAt": "2026-05-16T10:05:00",
"children": []
}
]
}
],
"total": 20,
"page": 1,
"pageSize": 15
}
}
5.5 管理后台接口
| 接口 | 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|---|
| 用户列表 | GET | /api/admin/system/users |
Admin | 分页查询用户列表 |
| 获取用户详情 | GET | /api/admin/system/users/{id} |
Admin | 获取指定用户信息 |
| 禁用/启用用户 | PATCH | /api/admin/system/users/{id}/status |
Admin | 修改用户状态 |
| 重置用户密码 | PUT | /api/admin/system/users/{id}/reset-password |
Admin | 重置密码 |
| 操作日志列表 | GET | /api/admin/system/logs |
Admin | 分页查询操作日志 |
| 系统配置列表 | GET | /api/admin/system/configs |
Admin | 获取所有配置 |
| 更新系统配置 | PUT | /api/admin/system/configs/{key} |
Admin | 更新配置项 |
| 敏感词列表 | GET | /api/admin/system/sensitive-words |
Admin | 分页查询敏感词 |
| 添加敏感词 | POST | /api/admin/system/sensitive-words |
Admin | 新增敏感词 |
| 删除敏感词 | DELETE | /api/admin/system/sensitive-words/{id} |
Admin | 删除敏感词 |
| 后台仪表盘统计 | GET | /api/admin/dashboard/stats |
Admin | 获取统计数据 |
5.6 OAuth 2.0 第三方登录扩展接口(预留)
| 接口 | 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|---|
| 微信登录授权 | GET | /api/oauth/wechat/authorize |
无 | 跳转微信授权页 |
| 微信登录回调 | GET | /api/oauth/wechat/callback |
无 | 微信回调处理 |
| GitHub 登录授权 | GET | /api/oauth/github/authorize |
无 | 跳转 GitHub 授权页 |
| GitHub 登录回调 | GET | /api/oauth/github/callback |
无 | GitHub 回调处理 |
说明:当前版本仅定义接口路径和空实现。当
sys_config表中oauth_wechat_enabled/oauth_github_enabled为true时启用。预留OAuth2Service接口和OAuth2UserInfo模型,后续接入时实现接口即可。
5.7 接口权限映射速查表
| 接口路径范围 | 角色要求 | 鉴权方式 |
|---|---|---|
/api/public/** |
无(允许匿名访问) | 无需 Token |
/api/app/** |
注册用户 | JWT Token + 用户角色校验 |
/api/admin/** |
超级管理员 | JWT Token + 管理员角色校验 |
/api/oauth/** |
无(OAuth 流程) | 无(暂为预留空实现) |
6. 核心业务流程
6.1 用户注册流程
用户填写注册表单 (邮箱 + 密码)
│
▼
前端校验:邮箱格式、密码强度、两次密码一致
│
▼
POST /api/public/user/register
│
▼
后端校验:
1. 邮箱格式正则校验
2. 密码强度校验(8-32位,至少3类字符)
3. 邮箱唯一性检查
4. IP 注册频率检查(1h ≤ 3次)
│
▼
密码 bcrypt 加密
│
▼
创建用户 (email_verified = 0)
│
▼
生成邮箱验证 Token(UUID,有效期 24h)
│
▼
发送验证邮件(含验证链接)
│
▼
返回「注册成功,请查看邮箱完成验证」
│
▼
用户点击验证链接 → GET /api/public/user/verify-email?token=xxx
│
▼
校验 Token 有效性 → 用户 email_verified = 1
│
▼
返回「邮箱验证成功,请登录」
6.2 管理员/用户登录流程(含防暴力破解)
用户输入账号密码
│
▼
后端查询账号是否存在
│
▼ (不存在)
返回「账号或密码错误」(模糊提示,不泄露具体字段)
│
▼ (存在)
检查用户状态:
- status=1 (锁定) 且 locked_until > now → 返回「账号已被锁定,请 X 分钟后重试」
- 否则继续
│
▼
验证密码(bcrypt.matches)
│
▼ (失败)
记录失败次数 + 1
│
▼ (失败次数分析)
- 失败 3 次(管理员)→ 下次需输入验证码
- 失败 5 次 → 锁定账号 N 分钟(管理员30min,用户15min)
- 同一 IP 10 次/5min → IP 封禁 15 分钟
│
▼ (成功)
重置失败计数
│
▼
多端登录控制(仅前台用户):
查询活跃会话数,≥ 3 个时剔除最早会话
│
▼
生成 JWT Token + RefreshToken
│
▼
记录登录日志(成功)
│
▼
返回 Token + 用户信息
6.3 博客发布流程
用户进入编辑器(需认证)
│
▼
用户编写内容(富文本)
支持:图片上传、视频嵌入
│
▼
每 30 秒自动保存草稿 (PUT /api/app/blog/articles/{id}/draft)
│
▼ (用户点击发布)
前端弹窗二次确认:选择可见性(公开/私密/仅粉丝可见)
│
▼
POST /api/app/blog/articles
│
▼
后端校验:
1. 标题、内容非空
2. 分类存在且启用
3. 标签数 ≤ 5
4. 图片/内容安全检测
5. XSS 过滤
│
▼
生成摘要(纯文本截取前 120 字)
│
▼
设置 published_at = now
插入 blog_article + blog_article_tag
│
▼
返回成功 + 文章 ID
│
▼
前端跳转到文章详情页
6.4 多级评论流程
用户查看文章 → 查看评论列表 (GET /api/public/comments/{articleId})
│
▼
用户填写评论内容 → 点击发表
│
▼
POST /api/app/comments
│
▼
后端校验:
1. 文章存在且用户有权限查看
2. 内容非空、≤ 2000 字
3. 敏感词过滤 → 替换为 ***
4. XSS 过滤
│
▼
↓ 是回复评论? ↓ 否,顶级评论
│ │
查询父评论信息 │
检查层级: │
parent.level = 1 → 新评论 level = 2 │
parent.level = 2 → 新评论 level = 3 │
parent.level ≥ 3 → 自动设为 level=3 的平级 │
│ │
│ ▼
判断评论者是否为文章作者 → 设置 is_author
│
▼
评论审核开关判断:
ON → status=0 (待审核)
OFF → status=1 (已通过)
│
▼
异步发送通知:
如果开启了审核且通过,或审核关闭:
回复评论 → 通知被回复人
│
▼
返回评论结果
6.5 浏览量统计流程
用户访问文章详情页
│
▼
后端接收 GET /api/public/blog/articles/{id} 请求
│
▼
判断是否需要计数:
1. 当前用户 == 文章作者 → 不计
2. 当前用户已登录:
- 查询 blog_article_view 中 user_id + today 是否有记录
- 有 → 不计;无 → 计 1 次
3. 当前用户未登录(访客):
- 查询 blog_article_view 中 ip_address + today 是否有记录
- 有 → 不计;无 → 计 1 次
│
▼ (需要计数)
使用 CompletableFuture.runAsync() 异步执行:
1. 插入浏览记录 blog_article_view
2. UPDATE blog_article SET view_count = view_count + 1 WHERE id = ?
│
▼
返回文章详情(含当前 view_count)
7. 开发规范
7.1 包命名规范
com.blog.{module}.{layer}
| 占位符 | 说明 | 示例 |
|---|---|---|
| {module} | 模块名 | common, framework, module.system, module.blog |
| {layer} | 分层 | controller, service, mapper, entity, dto, config |
7.2 命名约定
| 元素 | 规范 | 示例 |
|---|---|---|
| 类名 | UpperCamelCase | BlogArticleController, AuthService |
| 方法名 | lowerCamelCase | getUserById, createArticle |
| 变量名 | lowerCamelCase | articleList, currentUser |
| 常量 | UPPER_SNAKE_CASE | MAX_LOGIN_ATTEMPTS |
| 数据库表 | 小写+下划线 | blog_article, sys_user |
| 数据库字段 | 小写+下划线 | view_count, published_at |
| 请求映射路径 | 小写+连字符 | /api/public/blog/articles |
| JSON 字段 | lowerCamelCase | viewCount, publishedAt |
7.3 Controller 层规范
@RestController
@RequestMapping("/api/app/blog/articles")
@Tag(name = "博客文章管理", description = "前台博客文章接口")
public class BlogArticleController {
@GetMapping
@Operation(summary = "获取我的文章列表")
public ApiResult<PageResult<BlogArticleVO>> listMyArticles(
@Valid PageParam pageParam,
@RequestParam(required = false) Long categoryId) {
// ...
}
@PostMapping
@Operation(summary = "创建文章")
public ApiResult<IdVO> createArticle(@Valid @RequestBody ArticleCreateDTO dto) {
// ...
}
}
7.4 Service 层规范
- 每个 Service 先定义接口,再写实现类
- 接口命名:
XxxService(如BlogArticleService) - 实现类命名:
XxxServiceImpl(如BlogArticleServiceImpl) - 使用
@Service注解,事务方法加@Transactional(rollbackFor = Exception.class)
7.5 异常处理规范
- 禁止在 Controller 中使用 try-catch 捕获业务异常
- 业务异常直接
throw new BusinessException(code, message) - 由
GlobalExceptionHandler统一捕获并返回标准响应
7.6 日志规范
使用 Lombok @Slf4j 注解,日志级别使用规则:
| 级别 | 使用场景 |
|---|---|
| ERROR | 系统异常、数据库错误、调用第三方服务失败 |
| WARN | 参数校验失败、业务异常(如登录失败)、限流触发 |
| INFO | 关键业务流程(如用户注册成功、文章发布)、接口调用入参出参(可开关) |
| DEBUG | 开发调试信息,禁止在生产环境输出 |
7.7 安全编码规范
| 要求 | 实现方式 |
|---|---|
| 密码存储 | 始终使用 bcrypt 哈希,禁止 MD5/SHA1 明文存储 |
| SQL 注入防护 | 全程使用 MyBatis-Plus 参数化查询,禁止 ${} 拼接 |
| XSS 防护 | 用户输入输出前使用 Jsoup.clean() 过滤 HTML 标签 |
| CSRF 防护 | 前端请求需携带自定义 Header(如 X-Requested-With: XMLHttpRequest),后端校验 Origin |
| 敏感信息脱敏 | 日志中密码、Token 等使用 **** 替代 |
| 文件上传安全 | 校验 Content-Type 和文件魔术头,限制可执行文件上传 |
| 接口防刷 | 核心接口使用限流注解 @RateLimit |
7.8 限流注解使用
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
String key() default ""; // 限流 key
int maxCount() default 10; // 最大访问次数
int duration() default 60; // 时间窗口(秒)
TimeUnit unit() default TimeUnit.SECONDS;
}
使用 Redis 滑动窗口计数器实现:
// 登录接口限流:同一 IP 5分钟内最多 10 次
@RateLimit(key = "login:ip:#{ip}", maxCount = 10, duration = 300)
// 注册接口限流:同一 IP 1小时内最多 3 次
@RateLimit(key = "register:ip:#{ip}", maxCount = 3, duration = 3600)
// 评论提交限流:同一用户 1分钟内最多 5 次
@RateLimit(key = "comment:user:#{userId}", maxCount = 5, duration = 60)
8. 项目配置文件规范
8.1 application.yml 核心配置
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/blog_system?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: ${DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
data:
redis:
host: localhost
port: 6379
password: ${REDIS_PASSWORD}
servlet:
multipart:
max-file-size: 10MB
max-request-size: 50MB
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
jwt:
secret: ${JWT_SECRET}
access-token-expire: 7200 # AccessToken 有效期(秒)
refresh-token-expire: 86400 # RefreshToken 有效期(秒,记住我模式 14 天)
refresh-token-expire-remember: 1209600
app:
upload:
image-max-size: 10485760 # 10MB
allowed-types: image/jpeg,image/png,image/gif,image/webp
rate-limit:
login-max-attempts: 5 # 登录最大失败次数
login-lock-duration: 15 # 锁定分钟数(用户)
admin-login-lock-duration: 30 # 锁定分钟数(管理员)
ip-block-threshold: 10 # IP 封禁阈值
ip-block-duration: 15 # IP 封禁分钟数
8.2 Maven 多模块依赖管理
<!-- 父 POM 关键依赖版本管理 -->
<properties>
<spring-boot.version>3.4.x</spring-boot.version>
<mybatis-plus.version>3.5.9</mybatis-plus.version>
<jjwt.version>0.12.6</jjwt.version>
<springdoc.version>2.7.0</springdoc.version>
<jsoup.version>1.18.1</jsoup.version>
</properties>
9. 构建与部署
9.1 一体化构建流程
mvn clean package
执行步骤:
blog-framework→ 编译blog-module-system→ 编译blog-module-blog→ 编译blog-module-comment→ 编译blog-frontend→ 自动安装 Node.js 依赖 → npm run build → 复制 dist/ 到 blog-admin 的 static 目录blog-admin→ 打包为 FAT JAR(包含所有模块 + 前端静态资源)
9.2 部署方式
# 生产环境启动
java -jar blog-admin.jar --spring.profiles.active=prod
# 或使用外部配置
java -jar blog-admin.jar --spring.config.location=/path/to/application-prod.yml
FAT JAR 包含:
- 所有后端模块的字节码
- MySQL Driver、Redis Client 等依赖
- 前端静态资源(HTML/CSS/JS)
- 内嵌 Tomcat 容器
10. 数据库初始化脚本(Flyway)
Flyway 脚本位于 blog-admin/src/main/resources/db/migration/:
db/migration/
├── V1__init_schema.sql # 建表:sys_user, sys_user_session, sys_login_log, blog_category, blog_tag, blog_article, blog_article_tag, blog_comment, sys_user_follow, sys_notification, sys_config, sys_sensitive_word, blog_article_view
├── V2__init_data.sql # 初始化:超级管理员账号、默认分类、默认配置项
├── V3__init_indexes.sql # 建索引:全文索引、联合索引
└── V4__init_sensitive_words.sql # 初始化敏感词库
V2 初始化数据关键内容:
-- 超级管理员(密码: Admin@2026,bcrypt 哈希)
INSERT INTO sys_user (username, email, password, nickname, user_type, status, email_verified)
VALUES ('admin', 'admin@blogsystem.com',
'$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy',
'超级管理员', 0, 0, 1);
-- 默认分类
INSERT INTO blog_category (name, description) VALUES
('技术教程', '编程语言、框架、工具等技术教程'),
('生活随笔', '日常生活感悟与记录'),
('资源分享', '优质资源与工具推荐');
-- 默认配置
INSERT INTO sys_config (config_key, config_value, description) VALUES
('comment_audit_enabled', 'false', '评论审核开关'),
('oauth_wechat_enabled', 'false', '微信登录开关'),
('oauth_github_enabled', 'false', 'GitHub 登录开关');
11. 接口文档与测试
- 开发环境:访问
/swagger-ui.html查看 Swagger 接口文档(由 SpringDoc OpenAPI 自动生成) - API 测试:所有接口附带 JSON 请求/响应示例,可直接导入 Postman
- 单元测试:每个 Service 层方法需编写对应的单元测试,覆盖率 ≥ 80%(使用 JUnit 5 + Mockito)
- 集成测试:关键业务流程(登录、注册、发布、评论)需编写集成测试
文档结束
本文档基于
blog-system-requirements.md需求文档编写,覆盖了后端技术架构、模块划分、接口定义、数据模型、业务流程及开发规范六大核心内容。AI 编程可依据本文档中定义的模块结构、数据字段、接口路径和业务逻辑完成系统的完整开发。前后端一体化构建,通过mvn clean package一条命令即可生成可直接部署的 FAT JAR。