这个项目是怎么搭起来的——架构、设计与新人上手指南
前言
这篇文章想做成一张地图:任何一个刚接触这个项目的初级开发,读完它之后应该能回答三个问题——代码放在哪、一个请求怎么从浏览器走到数据库、怎么把它在自己电脑上跑起来。
这个项目是一个个人全栈项目:一套 Spring Boot 后端,同时支撑管理后台、博客和主站三个前端,跑在家里的一台小服务器上,通过 frp 穿透到公网。它不大,但“五脏俱全”:登录权限、文件存储、定时任务、数据备份、AI 工具调用,你能在中小公司项目里见到的典型组件,这里都有。
不需要任何预备知识,遇到术语都会用大白话解释。如果你是完全的新人,建议从第九章「新人上手指南」开始动手,遇到不懂的概念再回到前面查。
目录: 一、先看一眼全景 二、技术栈与选型理由 三、代码结构导览 四、一个请求的生命周期 五、核心设计 六、MCP:让 AI 帮你写博客 七、部署与运维 八、未来规划 九、新人上手指南 结语
一、先看一眼全景
先建立第一印象:一套后端,多个前端,一个家。
三个关键词帮你记住它:
- 多入口,单后端:管理后台、博客、将来的主站,都是静态页面或 SSR 应用,共用同一套
/prod-api/接口。加一个新站点,只需要在网关加一段 nginx 配置,不用动后端。 - 家是机房:所有服务跑在家里一台 4 核 N6005、15G 内存的小机器上,用 Docker Compose 管理。没有 Kubernetes——对个人项目来说那是纯负担。
- 云只是门房:腾讯云那台服务器不做业务计算,只负责收公网请求,通过 frp 隧道转回家里。便宜,且家里 IP 变了也不怕。
二、技术栈与选型理由
新人最容易犯的错是"追新栈"。这个项目的选型逻辑很简单:主流、成熟、出问题搜得到答案。
| 层 | 技术 | 一句话理由 |
|---|---|---|
| 后端框架 | Spring Boot 4 + Java 21 | 国内公司主流,生态最大,record/密封类这些新特性真的省代码 |
| 数据库 | MySQL 8.4 | 关系型标配,Docker 官方镜像一把起 |
| 缓存 | Redis | 登录态、图形验证码、限流计数,都靠它 |
| 管理后台前端 | Vue 3 + Element Plus | 中后台经典组合,表单表格开箱即用 |
| 博客站 | Astro(SSR 模式) | 内容站要快、要 SEO,默认零 JS 输出正合适 |
| 部署 | Docker Compose | 单机五容器,compose up -d 一条命令拉起全部 |
| 网关 | Nginx | 静态资源、反向代理、按子域名分发,一个全能选手 |
| 公网入口 | frp | 家里没有公网 IP,内网穿透是标准解法 |
| 数据库迁移 | Flyway | 表结构变更写进 SQL 文件,跟代码一起版本化 |
还有两个"看不见但重要"的选择:
- JSON 库用 Jackson 3(
tools.jackson包路径,不是老的com.fasterxml)。Spring Boot 4 已经切换到 Jackson 3,跟着框架走最省心。 - 代码风格继承 RuoYi:Controller 返回
TableDataInfo/AjaxResult,Service 接口 + Impl 两层,@PreAuthorize管权限。熟悉这套的人看代码会像回家。
三、代码结构导览
打开仓库,先看 backend/——它是 Maven 多模块,一个模块一件事情:
backend/
├── haichao-common/ 公共地基:工具类、异常体系、通用领域对象
├── haichao-framework/ 框架层:安全配置、拦截器、端口抽象、配置属性
├── haichao-system/ 系统模块:用户、角色、菜单、字典、岗位
├── haichao-quartz/ 定时任务:博客定时发布、附件清理都在这
├── haichao-generator/ 代码生成器:建好表一键生成 CRUD 代码
├── haichao-blog/ 博客业务:文章、分类、标签、附件、MCP 工具
├── haichao-admin/ 启动模块:组装一切 + 全部 Controller
└── pom.xml 父 POM:统一管依赖版本
依赖方向是严格的,新人一定要建立这个意识:
haichao-admin ──▶ 各业务模块 ──▶ haichao-framework ──▶ haichao-common
common不依赖任何模块,它是地基;framework只依赖common,提供跨业务的基础设施;system、blog这些业务模块平级,互相不依赖;admin是组装车间,把所有人拉起来启动。
违反这条线会怎样? 比如 blog 模块如果直接 import system 的类,将来想拆服务就拆不出来了。项目里为此还引入了 port/ 目录做接口抽象(UserAuthPort、PermissionPort):业务模块需要"查用户"时面向接口编程,实现在 admin 侧注入。
推荐的新人阅读顺序:
- 仓库根目录的
CLAUDE.md(项目宪法,约定都在里面) haichao-framework的SecurityConfig.java——理解整个系统的门卫BlogPostController.java从头读到尾——一个标准 Controller 的全貌- 顺着它进
BlogPostServiceImpl——业务逻辑长什么样
四、一个请求的生命周期
架构不是背出来的,是跟出来的。我们跟着"发布一篇博客"这个请求走一遍全链路:
逐步解释每一站在干嘛,以及为什么需要它:
- 网关 Nginx:请求从公网进来先到它。它做三件事:终结 TLS(如有)、把
/prod-api/前缀剥掉转给后端、按域名决定这是哪个站点。后端只听127.0.0.1:8081——外界根本摸不到它,这是第一道物理隔离。 - Spring Security:系统的门卫。它检查请求头里的 JWT 令牌,解析出"你是谁、你有什么权限",再比对方法上的
@PreAuthorize("hasPermi('blog:post:add')")。不满足?403,请求到此为止。 - Controller:只做三件事——收参数、校验参数、调 Service。它不应该出现业务判断,那是 Service 的职责。
- Service:真正的业务规则都住在这,而且标了
@Transactional:插文章、插标签关联,要么都成功,要么都回滚。半篇文章存进数据库是最难查的 bug。 - Mapper:MyBatis 的 SQL 映射,唯一允许说 SQL 的地方。
- MySQL:数据落库。
记住这条链,你就理解了 90% 的 Web 项目。这个项目的特别之处只在于:链条外面包了一层 Nginx 多站点分发和一层 MCP 协议适配(见第六章)。
五、核心设计
5.1 登录与权限:RBAC
权限模型是经典五张表:用户 → 用户角色关联 → 角色 → 角色菜单关联 → 菜单/权限。落到代码上是一行注解:
@PreAuthorize("hasPermi('blog:post:add')")
@PostMapping
public AjaxResult add(@RequestBody BlogPostBo bo) { ... }
登录态用 JWT + Redis 组合:JWT 令牌自带身份信息(无状态,网关层不查库),Redis 存"这个令牌还有效吗"(可失效、可踢人下线)。两者互补,比纯任一面都可靠。
新人对权限体系只需记住:权限是一串字符串(如 blog:post:add),数据库里配置给角色,注解里声明给接口。加新功能时先去菜单管理里加权限数据,再写注解。
5.2 博客模块:两扇门
博客模块的设计哲学是公开读和管理写彻底分离:
/blog/pub/**——匿名可读。文章列表、详情、附件,放行在安全配置里,但只读。/blog/admin/**——必须登录 + 权限。写操作全在这里。
这个边界换来两个好性质:公开面可以被 CDN 缓存、被搜索引擎随便爬;写面可以收紧到最小权限。附件寻址也用了这个思路——公开附件用 UUID 文件名而不是自增 ID,防止别人遍历 id=1,2,3... 把草稿和已删除文章的附件全扒走。
还有一个实战踩出来的坑值得新人知道:MySQL 容器必须显式设 TZ: Asia/Shanghai。镜像默认 UTC,和 JVM 差 8 小时,所有用 now() 比较时间的逻辑(比如"定时发布时间到了就发布")都会静默失效——不报错,就是不准。这类问题Debug起来最费劲,所以写在 CLAUDE.md 里。
5.3 配置外置:代码里零秘密
数据库密码、JWT 密钥、令牌,全部走环境变量,集中在 deploy/setenv.env(gitignored)。代码和配置彻底分离,意味着:仓库可以公开,服务器上改密码不用重新打包。所有项目自有配置统一 HAICHAO_ 前缀,一眼可辨。
六、MCP:让 AI 帮你写博客
这是最新加入的能力,也是本文的发布方式——这篇文章就是通过 MCP 工具创建的。
MCP(Model Context Protocol)是 Anthropic 主导的开放协议,让 AI 客户端(Claude Desktop、各类 Agent)能调用外部系统的工具。可以把它理解成"AI 世界的 USB 接口":以前 AI 只能聊天,现在它能查你的数据库、建你的工单、发你的文章。
我们把博客能力包成了 9 个 MCP 工具:
| 工具 | 作用 |
|---|---|
blog_search_posts |
关键词检索文章(只回摘要,省上下文) |
blog_get_post |
按 slug/ID 读全文 |
blog_list_posts / blog_list_categories / blog_list_tags / blog_list_attachments |
盘点家底,拿到写文章要用的 ID |
blog_create_post |
新建文章(默认草稿) |
blog_update_post |
局部更新(只改显式传入的字段) |
blog_set_post_status |
发布/转草稿/下线——高危动作,独立权限 |
设计上坚持了三个原则:
- 不另起炉灶:MCP 客户端用 Bearer 令牌认证,令牌映射到一个真实账号,然后走系统原有的 RBAC。没有"MCP 专属权限体系"这种东西要维护。
- 默认关闭:
haichao.mcp.enabled=false时所有相关 Bean 都不装配,端点不存在。用HAICHAO_MCP_TOKEN环境变量注入,空令牌条目惰性不可匹配(fail-safe)。 - 写操作防误伤:创建默认进草稿箱;发布是单独的高危工具;局部更新用 merge 语义——数组字段不传表示"沿用现有的",传空数组才表示"清空"。AI 再聪明也可能误解意图,接口层要把错误关在笼子里。
对个人使用的价值场景:写新文章时让 AI 先检索自己的旧文做内链推荐——这是只有在你自己的博客上才成立的功能,别处买不到。
七、部署与运维
本地开发用 docker compose up -d,生产在 192.168.1.2 上同样一套。五个容器:
mysql(数据) redis(缓存) app(后端) nginx(80 端口,admin 站点) blog(Astro SSR)
运维上已经固定下来的几件事,新人接手前必须知道:
- 发版流程:本地改代码 → 传服务器(tar + scp,因为服务器不是 git 仓库)→
docker compose build app→up -d→ 验证。改 nginx 配置要restart而不是reload——宿主机覆盖文件会换 inode,reload 读不到新配置(这个坑踩过)。 - 备份:每天凌晨 3:30,
mysqldump单事务快照压缩后存到 NAS,保留 14 份。备份任务本身也纳入了监控——正在做的数据看板会把"昨天备份成功了吗"放到第一屏。 - NAS 挂载:附件和备份落在 NAS(CIFS),systemd 管开机自挂。NAS 离线时 Docker 栈不受影响,但附件会静默写进本地空目录——排查附件丢失先查挂载状态。
公网链路回顾一下:访客 → 腾讯云 80 → frps 隧道 → 家里网关 8088 → 后端/各前端。新增子站点只改网关 nginx,frp、云上、DNS 全不动——这是刻意的设计,入口永远只有一个。
八、未来规划
规划不是愿望清单,每一条都基于"已有的能力刚好能复用":
- 数字花园:文章间加
[[双链]],发布时解析成关系表,文章页显示"引用本文的其他文章",外加一个力导向关系图谱和一个"随便看看"随机漫游按钮。博客会从"时间线存档"变成"会长东西的网"。工程量一个周末。 - 数据看板:一页看全博客阅读量、服务器 CPU/磁盘、备份是否成功、frp 隧道是否存活。个人项目最怕"死了没人知道"。
- AI 摘要周报:周日自动汇总本周文章变动,AI 写成摘要邮件发给自己。SMTP 迟早要配,一发多收。
- 评论/留言板:先上 twikoo(半天),想练手再自己写,正好复用现有的敏感词过滤和审核队列思路。
- 博客与相册打通:NAS 上跑 Immich,家里和公网都能看照片,主站加
/photos页。7.3T 空间闲着也是闲着。
明确不做的:自建网盘(Immich 已覆盖,再套一层是纯维护负担)、自建邮件服务器(IP 无反解,发信进垃圾箱,收信还要常年开 25 端口)。
九、新人上手指南
9.1 本地跑起来
前置条件:JDK 21、Maven 3.9+、Node 20+、Docker(跑 MySQL 和 Redis 最省事)。
# 1. 起依赖(MySQL + Redis)
docker compose up -d mysql redis
# 2. 建库并导入种子数据(Flyway 会自动建表)
# 在 mysql 容器里执行 backend/sql/ 下的脚本
# 3. 启动后端(IDEA 直接跑 HaichaoApplication 也行)
mvn spring-boot:run -pl haichao-admin -am
# 4. 启动管理后台前端
cd frontend && npm install && npm run dev
# 5. 启动博客站(Astro)
cd blog && npm install && npm run dev
后端默认 8081 端口,前端 dev server 会自动代理 /prod-api/。默认账号 admin,密码见种子数据(生产环境已改,本地无所谓)。
9.2 写代码前必须知道的五条家规
- 分层不越界:Controller 不写业务,Service 不碰 HTTP,Mapper 不说业务话。依赖方向只能
admin → 业务 → framework → common。 - 异常走两条线:
ServiceException(业务异常)和BaseException(用户异常)各有归宿,全局异常处理器会转成对应文案。不要随便throw new RuntimeException。 - CSS 网格单列退化必须写
minmax(0, 1fr):裸1fr会被内容最小宽度顶爆,手机上整页变桌面缩略图——这个坑的修复记录在blog/global.css里。 - 测试要分层:纯逻辑写 Mockito 单测;要真数据库的加
@Tag("integration")(CI 会排除,本地没库也能编译通过)。 - 提交前看
CLAUDE.md:所有踩过的坑都沉淀在里面,它比任何人口头传授都可靠。
9.3 推荐第一个练手任务
去给博客文章加一个"预计阅读时长"字段:数据库加列(Flyway V7 迁移脚本)→ BO/VO 加字段 → Service 计算(正文按 400 字/分钟)→ 列表接口返回 → 前端显示。改动小,但完整走一遍"迁移→后端→前端→发布"全流程,比读三天代码都管用。
结语
这个项目最值得学的不是任何单个技术点,而是它的组织方式:一套后端支撑多个前端、秘密全部外置、运维动作脚本化、每个坑都有文字沉淀。这些习惯比框架版本重要得多——框架会过时,习惯不会。
下一步,去把第九章的练手任务做掉。遇到问题?先查 CLAUDE.md,再查 docs/ 目录,最后来问。欢迎上车。