commit 208212d84c93b6c9183e3090328788dedfe6cf68 Author: 冯聪聪 Date: Sun May 17 14:47:49 2026 +0800 文档初始化 diff --git a/doc/blog-system-requirements.md b/doc/blog-system-requirements.md new file mode 100644 index 0000000..72ba50f --- /dev/null +++ b/doc/blog-system-requirements.md @@ -0,0 +1,447 @@ +# 博客系统需求规格说明书 + +| 文档版本 | 修订日期 | 修订人 | 修订内容 | +| :------ | :------- | :----- | :------- | +| V1.0 | 2026-05-17 | — | 初始版本,完成全量需求定义 | + +--- + +## 1. 项目概述 + +### 1.1 项目背景 + +本博客系统旨在为内容创作者与读者提供一个稳定、安全、可扩展的写作与阅读平台。系统支持管理员对平台进行统一管控,注册用户可发布博客、互动评论,访客可浏览公开内容。 + +### 1.2 项目目标 + +- 构建博客内容管理系统,支持完整的博客发布与管理流程 +- 提供多级评论体系,促进读者与作者互动 +- 保障平台数据安全与访问控制 +- 支持主流浏览器与移动端适配 + +### 1.3 适用范围 + +本文档适用于产品经理、前端开发工程师、后端开发工程师、测试工程师及相关项目干系人,作为系统设计、开发实现与验收测试的统一依据。 + +--- + +## 2.功能需求清单 + +### 2.1 管理员登录 + +#### 2.1.1 功能描述 + +提供超级管理员(内置唯一账号)登录后台管理系统的身份认证功能。 + +#### 2.1.2 业务规则 + +| 编号 | 规则项 | 业务规则说明 | +| :--- | :----- | :----------- | +| FR-ADM-LOGIN-01 | 登录方式 | 支持「手机号+密码」或「用户名+密码」两种方式登录 | +| FR-ADM-LOGIN-02 | 账号唯一性 | 系统初始化时内置 **1 个** 超级管理员账号,不可新增、不可删除 | +| FR-ADM-LOGIN-03 | 验证码机制 | 连续 3 次密码输入错误后,需输入图形验证码;连续 5 次错误后,账号锁定 30 分钟 | +| FR-ADM-LOGIN-04 | 防暴力破解 | 同一 IP 在 5 分钟内登录失败超过 10 次,该 IP 将被临时封禁 15 分钟 | +| FR-ADM-LOGIN-05 | 登录日志 | 每次登录成功/失败均需记录日志,包含:时间、IP 地址、设备指纹、登录结果 | +| FR-ADM-LOGIN-06 | 会话管理 | 登录会话有效期为 8 小时,超时需重新认证;支持「记住我」功能(有效期 7 天) | + +#### 2.1.3 验收标准 + +| 编号 | 验证场景 | 预期结果 | +| :--- | :------- | :------- | +| AC-ADM-LOGIN-01 | 使用正确的用户名/手机号和密码登录 | 登录成功,跳转至后台仪表盘 | +| AC-ADM-LOGIN-02 | 使用错误的密码连续登录 3 次 | 第 4 次登录时出现图形验证码输入框 | +| AC-ADM-LOGIN-03 | 使用错误的密码连续登录 5 次 | 账号被锁定,提示「账号已被锁定,请 30 分钟后再试」 | +| AC-ADM-LOGIN-04 | 尝试创建第二个管理员账号 | 系统禁止操作,返回错误提示 | +| AC-ADM-LOGIN-05 | 查询登录日志 | 日志完整记录每次登录的时间、IP、设备指纹、结果 | + +--- + +### 2.2 用户注册 + +#### 2.2.1 功能描述 + +普通用户通过提供邮箱和密码即可自助注册成为系统注册用户。 + +#### 2.2.2 业务规则 + +| 编号 | 规则项 | 业务规则说明 | +| :--- | :----- | :----------- | +| FR-USER-REG-01 | 注册信息 | 用户需提供 **邮箱** 和 **密码** 两项必填信息完成注册 | +| FR-USER-REG-02 | 密码强度 | 密码长度 8~32 位,必须包含大写字母、小写字母、数字和特殊字符中的至少三类 | +| FR-USER-REG-03 | 邮箱唯一性 | 同一邮箱仅允许注册一个账号,注册时需发送验证邮件进行邮箱真实性校验 | +| FR-USER-REG-04 | 邮箱验证有效期 | 邮箱验证链接有效期为 24 小时,超时需重新发送 | +| FR-USER-REG-05 | 注册频率限制 | 同一 IP 每小时内最多发起 3 次注册请求 | +| FR-USER-REG-06 | 密码重置 | 暂不支持用户自助找回密码,仅管理员可在后台对用户密码进行重置操作 | + +#### 2.2.3 验收标准 + +| 编号 | 验证场景 | 预期结果 | +| :--- | :------- | :------- | +| AC-USER-REG-01 | 使用合法邮箱和合规密码注册 | 注册成功,系统发送邮箱验证邮件 | +| AC-USER-REG-02 | 使用已被注册的邮箱再次注册 | 提示「该邮箱已被注册」 | +| AC-USER-REG-03 | 输入不合规密码(如纯数字,少于 8 位) | 前端提示密码强度要求,阻止提交 | +| AC-USER-REG-04 | 同一 IP 在 1 小时内发起超过 3 次注册 | 第 4 次注册被限制,提示「注册过于频繁」 | +| AC-USER-REG-05 | 管理员在后台重置用户密码 | 用户收到新密码(或重置通知),可使用新密码登录 | + +--- + +### 2.3 用户登录 + +#### 2.3.1 功能描述 + +为已注册用户提供账号登录及会话管理功能,支持多端登录限制与第三方登录扩展。 + +#### 2.3.2 业务规则 + +| 编号 | 规则项 | 业务规则说明 | +| :--- | :----- | :----------- | +| FR-USER-LOGIN-01 | 登录方式 | 支持「邮箱+密码」方式登录 | +| FR-USER-LOGIN-02 | 多端登录限制 | 同一账号最多允许 **3 个活跃会话**(不同设备/浏览器),超过时最早会话被强制下线 | +| FR-USER-LOGIN-03 | 第三方登录扩展 | 系统需预留微信登录与 GitHub 登录的 **接口扩展点**(OAuth 2.0 协议),当前版本不实现但可配置开关 | +| FR-USER-LOGIN-04 | 密码错误策略 | 连续 5 次密码错误后,账号临时锁定 15 分钟 | +| FR-USER-LOGIN-05 | 忘记密码 | 点击「忘记密码」后,引导用户联系管理员重置(当前版本无自助找回功能) | +| FR-USER-LOGIN-06 | 登录状态保持 | 默认会话有效期 24 小时,勾选「记住我」后续有效期延长至 14 天 | + +#### 2.3.3 验收标准 + +| 编号 | 验证场景 | 预期结果 | +| :--- | :------- | :------- | +| AC-USER-LOGIN-01 | 使用正确邮箱和密码登录 | 登录成功,跳转至首页/个人中心 | +| AC-USER-LOGIN-02 | 同一账号在第 4 台设备上登录 | 最早登录的设备被强制下线,当前设备登录成功 | +| AC-USER-LOGIN-03 | 连续 5 次输入错误密码 | 账号被临时锁定 15 分钟 | +| AC-USER-LOGIN-04 | 点击「忘记密码」 | 页面提示「请联系管理员重置密码」,并提供管理员联系信息 | +| AC-USER-LOGIN-05 | 验证第三方登录接口预留 | 代码中存在可配置的 OAuth 2.0 扩展接口,配置文件可开关 | +| AC-USER-LOGIN-06 | 勾选「记住我」后关闭浏览器再打开 | 会话保持,无需重新登录 | + +--- + +### 2.4 博客发布 + +#### 2.4.1 功能描述 + +仅已登录用户可创建和发布博客文章,支持富文本编辑、草稿保存、发布状态管理及标签分类绑定。 + +#### 2.4.2 业务规则 + +| 编号 | 规则项 | 业务规则说明 | +| :--- | :----- | :----------- | +| FR-BLOG-PUB-01 | 登录校验 | **仅已登录用户** 可进入博客编辑页面;未登录用户操作时跳转至登录页 | +| FR-BLOG-PUB-02 | 富文本编辑器 | 编辑器支持:文字排版、图片上传/嵌入(支持 JPEG/PNG/GIF/WebP,单图 ≤10MB)、视频嵌入(支持 URL 识别与 iframe 嵌入) | +| FR-BLOG-PUB-03 | 草稿保存 | 编辑器支持手动保存草稿,草稿仅作者可见;系统每隔 30 秒自动保存当前编辑内容至草稿 | +| FR-BLOG-PUB-04 | 发布状态 | 发布时可选择文章可见性:**公开**、**私密**(仅作者可见)、**仅粉丝可见** | +| FR-BLOG-PUB-05 | 状态修改 | 已发布的文章可修改可见性状态,修改后立即生效 | +| FR-BLOG-PUB-06 | 标签与分类 | 一篇文章必须绑定 **1 个分类**,最多绑定 **5 个标签**;分类与标签由管理员在后台预定义 | +| FR-BLOG-PUB-07 | 内容安全 | 发布内容需经过 XSS 过滤;图片需进行安全检测,禁止上传可执行文件 | + +#### 2.4.3 验收标准 + +| 编号 | 验证场景 | 预期结果 | +| :--- | :------- | :------- | +| AC-BLOG-PUB-01 | 未登录用户访问编辑页 | 自动跳转至登录页面,登录后返回编辑页 | +| AC-BLOG-PUB-02 | 在编辑器中插入图片 | 图片成功上传并显示在编辑器中,不超过大小限制 | +| AC-BLOG-PUB-03 | 保存草稿后关闭页面再编辑 | 草稿内容完整保留,继续编辑 | +| AC-BLOG-PUB-04 | 发布文章设置为「私密」 | 发布后仅作者在登录状态下可见,其他用户无法访问 | +| AC-BLOG-PUB-05 | 将已发布文章状态从「公开」改为「私密」 | 修改后立即生效,访客无法再看到该文章 | +| AC-BLOG-PUB-06 | 发布时绑定 6 个标签 | 系统提示最多绑定 5 个标签,禁止提交 | +| AC-BLOG-PUB-07 | 在上传区域上传 .exe 文件 | 系统拒绝上传,提示仅支持图片格式 | + +--- + +### 2.5 博客列表查看 + +#### 2.5.1 功能描述 + +所有用户可浏览博客列表,支持多维度排序筛选、全文检索、分页加载及浏览量统计。 + +#### 2.5.2 业务规则 + +| 编号 | 规则项 | 业务规则说明 | +| :--- | :----- | :----------- | +| FR-BLOG-LIST-01 | 排序方式 | 支持按 **发布时间**(降序/升序)、**浏览量**(降序)排序 | +| FR-BLOG-LIST-02 | 分类/标签筛选 | 支持按分类筛选与按标签筛选,筛选条件可组合使用 | +| FR-BLOG-LIST-03 | 关键词全文检索 | 支持对文章 **标题** 和 **正文** 进行关键词全文检索,检索结果高亮显示关键词 | +| FR-BLOG-LIST-04 | 分页加载 | 每页固定显示 **15 篇**文章,支持页码跳转与上下翻页 | +| FR-BLOG-LIST-05 | 私密博客可见性 | **私密博客** 仅对文章作者可见;其他用户(包括管理员)在列表中不可见该文章 | +| FR-BLOG-LIST-06 | 仅粉丝可见 | 「仅粉丝可见」的文章仅对作者及关注该作者的用户可见 | +| FR-BLOG-LIST-07 | 浏览量统计 | 每次用户(同一用户去重,不包含作者本人)访问文章详情页,浏览量 +1;访客访问也计入 | +| FR-BLOG-LIST-08 | 浏览量数据展示 | 列表中每篇文章卡片需显示 **阅读量** 数据 | + +#### 2.5.3 验收标准 + +| 编号 | 验证场景 | 预期结果 | +| :--- | :------- | :------- | +| AC-BLOG-LIST-01 | 按「浏览量降序」排序 | 列表按浏览量从高到低排列,数据准确 | +| AC-BLOG-LIST-02 | 选择分类 A + 标签 B 组合筛选 | 列表仅显示同时满足分类 A 和标签 B 的文章 | +| AC-BLOG-LIST-03 | 搜索关键词「设计模式」 | 标题或正文包含「设计模式」的文章全部展示,关键词高亮 | +| AC-BLOG-LIST-04 | 访问第 2 页 | 第 3 页返回 15 篇文章(或无更多文章时提示「已加载全部」) | +| AC-BLOG-LIST-05 | 作者访问自己的私密文章 | 作者可在列表中看到该文章;其他用户无法看到 | +| AC-BLOG-LIST-06 | 作者本人访问自己的文章详情页 | 浏览量不增加 | +| AC-BLOG-LIST-07 | 列表中确认每篇文章的浏览量显示 | 每篇文章卡片均正确显示阅读量数字 | + +--- + +### 2.6 评论功能 + +#### 2.6.1 功能描述 + +为用户提供对博客文章进行评论互动的功能,支持多级回复、审核机制与违规内容管控。 + +#### 2.6.2 业务规则 + +| 编号 | 规则项 | 业务规则说明 | +| :--- | :----- | :----------- | +| FR-COMMENT-01 | 评论权限 | **已登录用户** 方可发表评论;访客可查看但不可发表评论 | +| FR-COMMENT-02 | 多级回复 | 支持楼中楼回复,最多嵌套 **3 级**(如:A → B → C,C 不可再回复);超过 3 级时自动将回复转为第 3 级的平级评论 | +| FR-COMMENT-03 | 评论审核 | 管理员可在后台开启/关闭「评论审核」开关;开启后用户发表的评论需管理员审核通过方可展示 | +| FR-COMMENT-04 | 评论者身份标识 | 评论者昵称旁显示身份标识:**作者**(该文章作者)、**普通用户** | +| FR-COMMENT-05 | 违规评论处理 | 管理员可对违规评论进行 **删除**(物理删除)或 **屏蔽**(逻辑隐藏,仅发表者可见) | +| FR-COMMENT-06 | 回复通知 | 当用户的评论被回复时,系统需发送站内通知;楼中楼回复需通知被回复人 | +| FR-COMMENT-07 | 内容安全 | 评论内容需过滤 XSS 脚本与敏感词,敏感词库由管理员维护 | + +#### 2.6.3 验收标准 + +| 编号 | 验证场景 | 预期结果 | +| :--- | :------- | :------- | +| AC-COMMENT-01 | 未登录用户点击「发表评论」 | 弹出登录提示或跳转至登录页 | +| AC-COMMENT-02 | 评论 A → B → C → D 四层嵌套回复 | D 的回复自动变为 C 的平级评论,不产生第 4 级 | +| AC-COMMENT-03 | 管理员开启评论审核 | 用户新提交的评论状态为「待审核」,前台不可见;管理员审核通过后显示 | +| AC-COMMENT-04 | 文章作者在评论区发言 | 评论旁显示「作者」标识 | +| AC-COMMENT-05 | 管理员对评论执行「屏蔽」 | 该评论对其他用户不可见,仅发表者登录后可见 | +| AC-COMMENT-06 | 用户 A 回复用户 B 的评论 | 用户 B 收到站内通知 | +| AC-COMMENT-07 | 评论中包含 | 脚本不可执行,HTML 被转义 | +| 接口 | 批量调用登录接口,检查频率限制是否生效 | 超过阈值后接口返回 429 状态码 | +| 密码存储 | 检查数据库用户表密码字段 | 存储为 bcrypt,不可逆 | + +#### 6.2.3 兼容性测试 + +| 测试项 | 测试方法 | 通过条件 | +| :----- | :------- | :------- | +| 浏览器兼容 | 在 Chrome/Edge/Safari 各最近 3 个版本中执行核心功能冒烟测试 | 核心功能在所有目标浏览器中表现一致 | +| 移动端适配 | 在 iPhone SE/iPhone 16/ | 页面布局正常,无元素溢出,触控交互正常 | + +### 6.3 验收流程 + +1. **提测阶段**:开发完成功能后,由测试团队依据本文档编写测试用例并执行测试 +2. **Bug 修复**:P0/P1 级缺陷需在 24 小时内修复并回归,P2 级缺陷需在交付前完成修复 +3. **验收通过条件**: + - 全部 P0/P1 级缺陷已修复且回归通过 + - 功能测试用例通过率 ≥ 99% + - 性能测试指标达标 + - 安全测试无高危漏洞 +4. **上线会签**:产品、开发、测试三方确认验收通过后,方可发布上线 + +--- + +> **文档结束** +> +> 本文档覆盖博客系统的全部核心功能需求与非功能性需求,可作为产品、开发、测试团队的需求基线文档。如有需求变更,需通过变更评审流程更新本文档并更新版本号。 \ No newline at end of file diff --git a/doc/blog-system-technical-design.md b/doc/blog-system-technical-design.md new file mode 100644 index 0000000..0abe699 --- /dev/null +++ b/doc/blog-system-technical-design.md @@ -0,0 +1,1516 @@ +# 博客系统技术设计说明书 + +| 文档版本 | 修订日期 | 修订人 | 修订内容 | +| :------ | :------- | :----- | :------- | +| 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 +``` + +执行上述命令时: +1. `blog-frontend` 模块通过 `frontend-maven-plugin` 自动安装 Node.js 依赖并执行 `npm run build` +2. 构建产物 (`dist/`) 通过 Maven 资源插件复制到 `blog-admin/src/main/resources/static/` +3. `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 ` 中,鉴权通过后访问受保护资源 | +| RefreshToken | 14 天(记住我)/ 24 小时 | 用于刷新 AccessToken,存储在 Redis 中 | + +### 2.4 统一响应格式 + +所有 API 接口统一返回如下结构: + +```json +{ + "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 | 用户名(管理员专用) | +| email | 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**: +```json +{ + "account": "admin | 13800138000", + "password": "Admin@2026", + "captchaKey": "UUID (失败3次后必传)", + "captchaCode": "验证码 (失败3次后必传)", + "deviceFingerprint": "设备指纹" +} +``` + +**Response (200)**: +```json +{ + "code": 200, + "message": "登录成功", + "data": { + "accessToken": "eyJhbGci...", + "refreshToken": "eyJhbGci...", + "expiresIn": 7200, + "userInfo": { + "id": 1, + "username": "admin", + "nickname": "超级管理员", + "avatar": null + } + } +} +``` + +**Response (429 - 触发限流)**: +```json +{ + "code": 429, + "message": "登录过于频繁,请15分钟后再试" +} +``` + +##### GET `/api/public/captcha` + +**Response**: +```json +{ + "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**: +```json +{ + "email": "user@example.com", + "password": "Abc@123456", + "confirmPassword": "Abc@123456" +} +``` + +**Response (200)**: +```json +{ + "code": 200, + "message": "注册成功,请查看邮箱完成验证" +} +``` + +**业务逻辑**: +1. 校验邮箱格式和密码强度 +2. 校验邮箱是否已存在 +3. 校验同 IP 注册频率(1h 内 ≤ 3 次) +4. 密码 bcrypt 加密 +5. 创建用户(status=0正常, email_verified=0) +6. 发送验证邮件(含 24h 有效期的 token) +7. 记录注册日志 + +##### POST `/api/app/auth/login` + +**Request**: +```json +{ + "email": "user@example.com", + "password": "Abc@123456", + "deviceFingerprint": "设备指纹", + "rememberMe": false +} +``` + +**Response (200)**: +```json +{ + "code": 200, + "data": { + "accessToken": "eyJhbGci...", + "refreshToken": "eyJhbGci...", + "expiresIn": 7200, + "userInfo": { + "id": 2, + "email": "user@example.com", + "nickname": "用户昵称", + "avatar": null + } + } +} +``` + +**业务逻辑**: +1. 验证邮箱 + 密码 +2. 检查账号是否锁定(locked_until > now 则拒绝) +3. 检查 email_verified +4. 多端限制检查(同一账号最多 3 个活跃会话) +5. 生成 AccessToken(2h 有效)+ RefreshToken(24h 或 14d) +6. 记录登录日志 +7. 重置登录失败计数 + +### 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**: +```json +{ + "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**: +```json +{ + "code": 200, + "data": { + "id": 1, + "title": "Spring Boot 入门指南", + "content": "

...

...

", + "author": {...}, + "category": {...}, + "tags": [...], + "viewCount": 1280, + "commentCount": 15, + "isLiked": false, + "visibility": 0, + "publishedAt": "2026-05-15T10:30:00" + } +} +``` + +**业务逻辑**: +1. 判断文章可见性: + - `status ≠ 已发布` → 404 + - `visibility = 私密` 且当前用户非作者 → 404 + - `visibility = 仅粉丝可见` 且当前用户非作者且非粉丝 → 404 +2. 浏览量更新(异步): + - 同一用户同一天仅计 1 次 + - 作者本人不计 + - 调用 `CompletableFuture` 异步写入 `blog_article_view` + +##### POST `/api/app/blog/articles` — 创建文章 + +**Request**: +```json +{ + "title": "文章标题", + "content": "

富文本内容

", + "categoryId": 1, + "tagIds": [1, 2, 3], + "coverImage": "https://...", + "visibility": 0, + "status": 0 +} +``` + +**参数说明**: +- `status=0` 保存为草稿,`status=1` 直接发布 +- `visibility` 仅在发布时生效 + +**Response (200)**: +```json +{ + "code": 200, + "data": { + "id": 42 + } +} +``` + +**业务逻辑**: +1. 校验标题非空、内容非空 +2. 校验分类存在且启用 +3. 校验标签数量 ≤ 5 +4. XSS 过滤 content(使用 Jsoup 清理) +5. 自动生成 summary(截取 content_text 前 120 字) +6. 设置 published_at(status=1 时) +7. 插入 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**: +```json +{ + "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**: +```json +{ + "articleId": 1, + "content": "写得很棒,学习了!", + "parentId": null, + "replyToId": null +} +``` + +**回复评论 Request**: +```json +{ + "articleId": 1, + "content": "感谢回复!", + "parentId": 5, + "replyToId": 8 +} +``` + +**Response (200)**: +```json +{ + "code": 200, + "data": { + "id": 10, + "status": 0 + } +} +``` + +> 当评论审核开启时 `status=0(待审核)`,否则 `status=1(已通过)` + +**业务逻辑**: +1. 校验文章存在且可见 +2. 校验 parentId 有效性: + - 如果 parentId 非空,确认父评论存在且属于同一文章 + - 检查层级:查询父评论的 level,如果父评论 level ≥ 3,则设置为第 3 级的平级评论 +3. 敏感词过滤(替换为 `***`) +4. XSS 过滤 +5. 判断评论者是否为文章作者(is_author) +6. 评论审核开关判断: + - 开启 → status=0(待审核) + - 关闭 → status=1(已通过) +7. 异步发送回复通知 + +#### GET `/api/public/comments/{articleId}` — 获取文章评论 + +**Query Parameters**: + +| 参数 | 类型 | 必填 | 默认值 | 说明 | +| :--- | :--- | :--: | :----- | :--- | +| page | Integer | N | 1 | 页码 | +| pageSize | Integer | N | 15 | 每页条数(顶级评论数) | + +**Response**: +```json +{ + "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 层规范 + +```java +@RestController +@RequestMapping("/api/app/blog/articles") +@Tag(name = "博客文章管理", description = "前台博客文章接口") +public class BlogArticleController { + + @GetMapping + @Operation(summary = "获取我的文章列表") + public ApiResult> listMyArticles( + @Valid PageParam pageParam, + @RequestParam(required = false) Long categoryId) { + // ... + } + + @PostMapping + @Operation(summary = "创建文章") + public ApiResult 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 限流注解使用 + +```java +@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 滑动窗口计数器实现: + +```java +// 登录接口限流:同一 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 核心配置 + +```yaml +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 多模块依赖管理 + +```xml + + + 3.4.x + 3.5.9 + 0.12.6 + 2.7.0 + 1.18.1 + +``` + +--- + +## 9. 构建与部署 + +### 9.1 一体化构建流程 + +``` +mvn clean package +``` + +执行步骤: +1. `blog-framework` → 编译 +2. `blog-module-system` → 编译 +3. `blog-module-blog` → 编译 +4. `blog-module-comment` → 编译 +5. `blog-frontend` → **自动安装 Node.js 依赖 → npm run build → 复制 dist/ 到 blog-admin 的 static 目录** +6. `blog-admin` → 打包为 FAT JAR(包含所有模块 + 前端静态资源) + +### 9.2 部署方式 + +```bash +# 生产环境启动 +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 初始化数据关键内容**: + +```sql +-- 超级管理员(密码: 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。