|
| 1 | +# OpenBlog —— 从零搭建的个人博客全栈实践 |
| 2 | + |
| 3 | +> 一个面向个人博客场景的现代化全栈项目,基于 Spring Boot 3 + Vue 3 构建,已上线运行于 [wecode.xin](https://www.wecode.xin/)。 |
| 4 | +
|
| 5 | +--- |
| 6 | + |
| 7 | +## 写在前面 |
| 8 | + |
| 9 | +作为一名开发者,我一直想拥有一个属于自己的博客空间——不只是为了写文章,更是为了亲手打造一个完整的、现代化的 Web 应用。于是,**OpenBlog** 诞生了。 |
| 10 | + |
| 11 | +这是一个从后端到前端、从数据库到缓存、从 SEO 到运维部署,全链路自研的个人博客系统。目前项目已开源在 GitHub([yyyCode/OpenBlog](https://github.com/yyyCode/OpenBlog.git)),线上站点 [wecode.xin](https://www.wecode.xin/) 稳定运行中。 |
| 12 | + |
| 13 | +本文将全面介绍 OpenBlog 的技术架构、功能设计与实现细节,希望能给同样想搭建个人博客的开发者一些参考。 |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## 项目概览 |
| 18 | + |
| 19 | +OpenBlog 定位为**面向个人博客场景的全栈项目**,覆盖了博客系统的完整功能链路: |
| 20 | + |
| 21 | +| 模块 | 功能说明 | |
| 22 | +|------|----------| |
| 23 | +| 账号系统 | 注册、登录、JWT 双 Token 鉴权(Access + Refresh)、用户资料管理 | |
| 24 | +| 文章系统 | Markdown 撰写、草稿/发布/定时发布、导入/导出、全文搜索 | |
| 25 | +| 评论系统 | 文章评论、回复嵌套、删除管理 | |
| 26 | +| 互动系统 | 点赞、收藏、关注,完整的用户互动链路 | |
| 27 | +| 媒体管理 | 图片上传(支持粘贴/拖拽)、缩略图生成、媒体库浏览、一键插入 | |
| 28 | +| SEO 优化 | 爬虫动态 SSR、Open Graph / JSON-LD 结构化数据、sitemap.xml 自动生成、百度站长推送 | |
| 29 | +| 站点配置 | 后台可视化配置站点标题、描述、版权等全局信息 | |
| 30 | +| 管理后台 | 文章管理、用户管理、评论审核、反馈处理、附件管理、更新日志等 | |
| 31 | +| 个性化 | Live2D 看板娘、飞书风格主题、亮暗主题切换 | |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +## 技术栈一览 |
| 36 | + |
| 37 | +### 后端 |
| 38 | + |
| 39 | +| 技术 | 版本 | 用途 | |
| 40 | +|------|------|------| |
| 41 | +| Java | **17** | 运行环境 | |
| 42 | +| Spring Boot | **3.5.x** | 核心框架(Web、Security、Validation、Data JPA) | |
| 43 | +| MyBatis-Plus | **3.5.x** | ORM 增强(与 JPA 并存,按场景选用) | |
| 44 | +| MySQL | **8.0** | 关系型数据库,含 FULLTEXT 全文索引 | |
| 45 | +| Redis | — | 两级缓存、计数器去重、限流器 | |
| 46 | +| JWT (jjwt) | **0.12.x** | 无状态鉴权 | |
| 47 | +| MinIO | — | 对象存储(图片等二进制资源,可选本地文件系统) | |
| 48 | +| flexmark | **0.64.x** | 服务端 Markdown → HTML 预渲染 | |
| 49 | +| Thymeleaf | — | SEO 爬虫页面服务端渲染模板引擎 | |
| 50 | +| Thumbnailator | — | 图片缩略图生成 | |
| 51 | +| Caffeine | — | 本地缓存 | |
| 52 | + |
| 53 | +### 前端 |
| 54 | + |
| 55 | +| 技术 | 版本 | 用途 | |
| 56 | +|------|------|------| |
| 57 | +| Vue | **3.5.x** | 前端框架 | |
| 58 | +| Vue Router | **4.x** | 前端路由 | |
| 59 | +| Vite | **8.x** | 构建工具 | |
| 60 | +| marked | **17.x** | Markdown 渲染(前端回退) | |
| 61 | +| DOMPurify | **3.x** | HTML 消毒,防止 XSS | |
| 62 | +| oh-my-live2d | **0.19.x** | Live2D 看板娘组件 | |
| 63 | + |
| 64 | +### 自定义框架模块 |
| 65 | + |
| 66 | +项目拆分为多模块 Maven 工程,其中两个自研框架模块值得单独说明: |
| 67 | + |
| 68 | +- **OpenBlog-framework-redis**:对 Redis 操作的统一封装,提供 `RedisOps` 接口(内置容错)、`RedisKeys` 统一 Key 管理、滑动窗口限流器等基础设施。业务模块不直接依赖 `StringRedisTemplate`。 |
| 69 | +- **OpenBlog-framework-elasticsearch**:预留的 Elasticsearch 集成模块,为后续搜索能力升级做准备。 |
| 70 | + |
| 71 | +--- |
| 72 | + |
| 73 | +## 架构设计亮点 |
| 74 | + |
| 75 | +### 1. 文章存储:元数据与正文分离 |
| 76 | + |
| 77 | +传统博客系统往往将文章标题、摘要、正文全部塞在一张表里。OpenBlog 采用了**分表存储**策略: |
| 78 | + |
| 79 | +``` |
| 80 | +┌─────────────────┐ ┌──────────────────────┐ |
| 81 | +│ articles │ 1:1 │ article_bodies │ |
| 82 | +│─────────────────│ │──────────────────────│ |
| 83 | +│ id │────────→│ article_id │ |
| 84 | +│ title │ │ content_markdown │ (MEDIUMTEXT) |
| 85 | +│ summary │ │ content_html │ (MEDIUMTEXT) |
| 86 | +│ status │ └──────────────────────┘ |
| 87 | +│ category_id │ |
| 88 | +│ author_id │ |
| 89 | +│ view_count │ |
| 90 | +│ like_count │ |
| 91 | +│ ... │ |
| 92 | +└─────────────────┘ |
| 93 | +``` |
| 94 | + |
| 95 | +**设计收益:** |
| 96 | +- 列表查询只检索元数据表,不拖出正文大字段,查询更轻量 |
| 97 | +- 正文独立做 FULLTEXT 全文索引,搜索更高效 |
| 98 | +- 服务端发布时用 flexmark **预渲染** Markdown 为 HTML,前端直接展示,避免每次客户端解析 |
| 99 | + |
| 100 | +### 2. 缓存架构:两级缓存 + 版本号失效 |
| 101 | + |
| 102 | +已发布文章的读取链路使用 Redis 做两级缓存: |
| 103 | + |
| 104 | +| 缓存层级 | TTL | 策略 | |
| 105 | +|----------|-----|------| |
| 106 | +| 文章正文缓存 | 30 分钟 | Cache-Aside:查无则回源 MySQL 并回写 | |
| 107 | +| 文章列表缓存 | 5 分钟 | **版本号机制**:写操作递增全局版本号,旧缓存自然过期 | |
| 108 | + |
| 109 | +``` |
| 110 | +读取流程:Redis → 命中返回 → 未命中查 MySQL → 回写 Redis |
| 111 | +写入流程:更新 MySQL → 删除/失效 Redis → 下次读取自动重建 |
| 112 | +``` |
| 113 | + |
| 114 | +**版本号机制的巧妙之处**:文章列表缓存 Key 中携带 `v{version}`,发布/删除文章时只需 `INCR version`,旧版本缓存无需逐个清理,自然淘汰。 |
| 115 | + |
| 116 | +**故障降级**:`RedisOps` 所有方法内置 try-catch,Redis 不可用时自动回退到数据库,不影响正常业务。 |
| 117 | + |
| 118 | +### 3. SEO 架构:动态 SSR 方案 |
| 119 | + |
| 120 | +SPA(单页应用)对搜索引擎不友好是个老问题。OpenBlog 没有采用 Nuxt.js 等重量级 SSR 框架,而是实现了一套**轻量级动态 SSR**: |
| 121 | + |
| 122 | +``` |
| 123 | +爬虫请求 → Nginx(User-Agent 检测) |
| 124 | + ├── 爬虫:转发到后端 SEO 控制器 → Thymeleaf 渲染 → 完整 HTML |
| 125 | + └── 普通用户:直接返回 Vue SPA |
| 126 | +``` |
| 127 | + |
| 128 | +配合以下组件形成完整 SEO 闭环: |
| 129 | + |
| 130 | +- **Open Graph / Twitter Card**:文章详情页注入社交分享元标签 |
| 131 | +- **JSON-LD 结构化数据**:帮助搜索引擎理解页面内容 |
| 132 | +- **sitemap.xml**:自动生成,包含所有已发布文章 URL |
| 133 | +- **百度站长推送**:文章发布时异步调用百度 API,加速收录 |
| 134 | + |
| 135 | +### 4. 媒体管理:粘贴即上传 |
| 136 | + |
| 137 | +文章编辑器做了细致的产品体验打磨: |
| 138 | + |
| 139 | +- 在编辑区 **Ctrl+V 粘贴截图**或**拖拽图片**,自动上传并插入 Markdown 图片语法 |
| 140 | +- 工具栏「插入图片」按钮支持直接选文件 |
| 141 | +- 「媒体库」弹窗浏览已上传图片,点击缩略图一键插入 |
| 142 | +- 上传自动生成缩略图,全屏预览、一键复制 URL |
| 143 | + |
| 144 | +--- |
| 145 | + |
| 146 | +## 项目结构一览 |
| 147 | + |
| 148 | +``` |
| 149 | +OpenBlog/ |
| 150 | +├── OpenBlog-framework-redis/ # Redis 框架封装(自研模块) |
| 151 | +├── OpenBlog-framework-elasticsearch/ # ES 集成预留(自研模块) |
| 152 | +├── OpenBlog-business/ # 业务主模块 |
| 153 | +│ └── src/main/ |
| 154 | +│ ├── java/com/yqz/openblog/ |
| 155 | +│ │ ├── article/ # 文章:实体、服务、缓存、导入导出 |
| 156 | +│ │ ├── category/ # 文章分类 |
| 157 | +│ │ ├── changelog/ # 更新日志 |
| 158 | +│ │ ├── comment/ # 评论系统 |
| 159 | +│ │ ├── interaction/ # 互动(点赞/收藏/关注) |
| 160 | +│ │ ├── media/ # 媒体管理(MinIO/本地+缩略图) |
| 161 | +│ │ ├── user/ # 用户认证与 JWT |
| 162 | +│ │ ├── seo/ # SEO:SSR渲染、sitemap、百度推送 |
| 163 | +│ │ ├── site/ # 站点全局配置 |
| 164 | +│ │ ├── controller/ # REST 控制器 |
| 165 | +│ │ └── config/ # Spring Security 等配置 |
| 166 | +│ └── resources/ |
| 167 | +│ ├── templates/seo/ # Thymeleaf SEO 模板 |
| 168 | +│ └── sql/ # 数据库脚本 |
| 169 | +├── vue/ # Vue 3 前端 |
| 170 | +│ └── src/ |
| 171 | +│ ├── api/ # HTTP 请求封装 |
| 172 | +│ ├── views/ # 页面组件(含管理后台全套页面) |
| 173 | +│ ├── components/ # 通用组件(Live2D、评论、文章卡片等) |
| 174 | +│ └── layouts/ # 布局组件(公开页+控制台双布局) |
| 175 | +├── docs/ # 设计文档与规划 |
| 176 | +└── pom.xml # 根 POM |
| 177 | +``` |
| 178 | + |
| 179 | +--- |
| 180 | + |
| 181 | +## 线上体验 |
| 182 | + |
| 183 | +OpenBlog 已部署上线,欢迎访问体验: |
| 184 | + |
| 185 | +**线上地址:[https://www.wecode.xin/](https://www.wecode.xin/)** |
| 186 | + |
| 187 | +你可以在这里: |
| 188 | +- 浏览已发布的文章,体验 Markdown 渲染效果 |
| 189 | +- 感受飞书风格主题与亮暗模式切换 |
| 190 | +- 查看 SEO 优化效果(右键查看源码,体验服务端渲染的结构化数据) |
| 191 | +- 与右下角 Live2D 看板娘互动 |
| 192 | + |
| 193 | +--- |
| 194 | + |
| 195 | +## 开源与贡献 |
| 196 | + |
| 197 | +项目完全开源,采用 Maven 多模块 + Vue 3 单页应用的经典前后端分离架构,适合作为个人博客搭建的参考实现,也适合 Java/Vue 开发者学习全栈项目实践。 |
| 198 | + |
| 199 | +**GitHub 仓库:[https://github.com/yyyCode/OpenBlog](https://github.com/yyyCode/OpenBlog.git)** |
| 200 | + |
| 201 | +### 本地运行 |
| 202 | + |
| 203 | +```bash |
| 204 | +# 1. 准备 MySQL 8 数据库与 Redis |
| 205 | + |
| 206 | +# 2. 启动后端(默认端口 8082) |
| 207 | +./mvnw spring-boot:run |
| 208 | + |
| 209 | +# 3. 启动前端 |
| 210 | +cd vue && npm install && npm run dev |
| 211 | +``` |
| 212 | + |
| 213 | +详细配置说明请参考仓库 [README](https://github.com/yyyCode/OpenBlog)。 |
| 214 | + |
| 215 | +--- |
| 216 | + |
| 217 | +## 写在最后 |
| 218 | + |
| 219 | +OpenBlog 是我个人在博客系统领域的一次完整实践,从最初的文章发布,到后续逐步加入缓存优化、SEO 支持、媒体管理、管理后台、站点配置等能力,迭代过程中踩了不少坑,也学到了很多。 |
| 220 | + |
| 221 | +技术选型上,没有盲目追新,而是选用了 Spring Boot + Vue 这套成熟稳定的组合,把精力集中在**架构设计的合理性**和**产品体验的打磨**上——比如文章分表存储降低查询开销、版本号机制优雅处理缓存失效、粘贴即上传降低写作摩擦、动态 SSR 兼顾 SEO 与开发体验。 |
| 222 | + |
| 223 | +如果你也在搭建个人博客,或者想学习 Spring Boot 3 + Vue 3 的全栈项目实践,希望 OpenBlog 能给你带来一些启发。欢迎 Star、提 Issue、交流讨论! |
| 224 | + |
| 225 | +--- |
| 226 | + |
| 227 | +*2026 年 7 月 · Spring Boot / Vue 3 / 全栈 / 个人博客 / 开源* |
0 commit comments