不管你是准备拿它当毕设,还是想快速搞懂一个典型企业级前后端分离项目是怎么串起来的,这套基于 SpringBoot + Vue 的疫苗发布和接种预约系统都值得仔细过一遍。项目里不光包含完整源码,还配套了 MySQL 的 SQL 脚本和接口文档,基本把 Java Web 毕设里会用到的东西都带上了——用户端、管理端、预约流程、号源控制、权限校验,一个业务闭环下来,能学到的远不止“写几个增删改查接口”。
我拿到项目后先做的事,不是急着启动,而是把数据库表和核心接口捋了一遍。原因很简单:预约类系统最怕的是并发下“超卖”和“预约状态混乱”,如果表设计不合理、事务边界没划清楚,后面接口写得再漂亮也是白搭。这篇就把我对这个项目的理解、启动步骤、功能拆解、踩坑点以及答辩可能被问的问题一起整理出来,给准备做同类项目的朋友一个参考。
1. 项目整体思路与模块拆解
1.1 项目到底解决了什么问题
这个系统核心解决的是“疫苗信息发布”和“接种预约”两个环节的线上化。以前线下预约靠电话、排队,信息不透明、号源不可控,管理员想统计接种数据也非常麻烦。换成线上系统后,管理员可以在后台维护疫苗批次、库存、有效期,发布某一天的接种场次和可预约数量;普通用户在小程序或者网页端注册登录后,看到已发布的疫苗场次,选择合适的日期和时段,提交预约,到点再去现场接种。
整个业务可以拆成四条主线:
- 用户侧:注册、登录、查看疫苗资讯、查看接种公告、预约、取消预约、查看个人预约记录。
- 管理侧:疫苗信息管理、库存批次管理、接种场次发布、预约审核/核销、预约记录查询与统计。
- 公告侧:疫苗发布公告、接种注意事项、系统通知,类似一个轻量 CMS。
- 系统侧:用户管理、角色权限管理、操作日志。
从功能量来看,这差不多是毕设里中等偏上的复杂度,既有基础的信息管理,又有带业务规则的预约下单,还涉及权限控制,非常适合用于 Java Web 方向的毕业设计展示。
1.2 技术栈选型:为什么是 SpringBoot + Vue
SpringBoot 解决了传统 SSM 项目里大量 XML 配置的问题,内嵌 Tomcat,打 jar 包就能跑,部署特别省心。Vue 做前端页面很顺,组件化拆分让页面维护比 JSP 时代舒服太多。前后端分离后,后端只出 JSON 接口,前端通过 Axios 调接口,职责清楚,也方便以后把前端换成小程序或者 App。
选型上有一点要注意:项目里如果用的是 Vue 2 加 Element UI,那大概率配套的是 SpringBoot 2.x。SpringBoot 3 要求 JDK 17,并且部分第三方依赖的兼容方式不一样,所以跑项目前一定先确认 JDK 版本。如果你本机装的是高版本 JDK,建议直接在配置里把后端 JDK 改为 1.8 或者 11,这样可以少踩很多坑。
2. 数据库设计与 SQL 脚本解读
2.1 核心表有哪些
拿到 SQL 脚本后,建议先不要急着执行,先打开看下表结构。这个项目的核心表大概包括这几类:
- 用户表:保存账号、密码、姓名、证件类型、证件号、手机号、角色标识。密码通常是加密存储的,不要用明文。
- 疫苗表:维护疫苗名称、生产企业、批号、接种剂次、适用人群、规格、库存总量、剩余库存、有效期。
- 接种场次/预约计划表:保存接种点、日期、时间段、可预约总数、已预约数、剩余号数、状态。
- 预约记录表:关联用户和场次,保存预约时间、预约状态(待接种/已接种/已取消/已过期)、现场核销码或二维码。
- 公告通知表:用于疫苗发布和接种注意事项的内容展示。
- 角色权限相关表:如果项目用了 Spring Security 或 Shiro,通常还会有角色表、菜单表、用户角色关联表。
这些表之间的关系也很好理解:用户和预约记录是一对多,预约记录和接种场次是多对一,场次和疫苗是多对一。只要理清这三条关系,整个数据库的设计思路就通了。
2.2 SQL 脚本执行要改的三处地方
很多同学启动项目时报数据库连接失败,基本都是下面三处没改:
-- 1. 建库,用 utf8mb4 而不是 utf8 CREATE DATABASE `vaccine_appointment` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;第一处是字符集,疫苗名称、公告内容里如果有生僻字或者特殊符号,utf8mb4 更稳妥。第二处是数据库账号密码,脚本本身没有账号配置,要在后端application.yml里改成你自己的库名、用户名、密码。第三处是时区问题,MySQL 8 的驱动对时区要求比较严格,连接串里最好带上serverTimezone=Asia/Shanghai,否则插入时间可能报错。
另外脚本里如果带了初始管理员数据,账号一般会在 README 或者 SQL 注释里给出。默认管理员密码大多是admin123或者123456这样的简单密码,第一次登录后记得在系统里修改掉,答辩现场如果被老师看到弱口令,观感不太好。
3. 环境准备与快速启动全过程
3.1 环境要求
我实际运行这套项目使用的环境可以参考,但不一定完全一致:
| 环境 | 版本建议 |
|---|---|
| JDK | 1.8 或 11 |
| Maven | 3.6+ |
| MySQL | 5.7 或 8.0 |
| Redis | 如果项目用到缓存/分布式锁再装,纯预约扣库存可不装 |
| Node.js | 14 或 16,配合 Vue2 |
| IDE | IDEA 2020+ / VS Code |
注意:如果前端用的是 Vue CLI 4,Node 版本太高可能会在node-sass编译阶段报错。这时候最省事的办法不是换 Node,而是把package.json里的node-sass改成sass,或者用npm install时指定镜像源。
3.2 后端启动的五个步骤
后端启动顺序并不复杂,关键是别手快:
- 先用 Navicat 或者命令行执行 SQL 脚本,完成建库建表。
- 用 IDEA 打开后端目录,等 Maven 把依赖下载完。
- 修改
application.yml里的数据源配置,确认端口没被占用。 - 如果项目用了 Redis,先启动 Redis 服务,并确认
redis.host和redis.port配置正确。 - 启动 Application 主类,看到
Started ... Application的日志就说明成功。
启动时如果报Failed to configure a DataSource,大概率是数据库连接没配对;如果报端口被占用,在后端配置中修改server.port,比如改成 8081。前端里如果有写死的后端地址,也要同步改掉。
3.3 前端启动的四个步骤
前端部分按顺序执行下面四条命令:
npm install npm run servenpm install偶尔会卡住,可以换成:
npm install --registry=https://registry.npmmirror.com启动后终端会提示访问地址,默认一般是http://localhost:8080。如果项目里配置了代理,在vue.config.js里可以看到/api开头的请求被代理到哪个端口,前端访问和后端端口未必一致,这个要留意。
4. 核心功能实现:疫苗发布与接种预约
4.1 疫苗发布不是简单的“新增一条数据”
“疫苗发布”这个功能听起来就像普通的新增记录,实际做起来有几个隐含业务规则。比如发布前要确认疫苗库存充足,发布的场次日期不能早于当前日期,同一接种点在同一时间段不能重复发布两个场次,状态要支持“草稿、已发布、已关闭”。
如果项目里没有做这些校验,动手改的时候可以这样补:前端在提交表单时校验日期和库存,后端在 Service 层再校验一次。后端校验才是真正的防线,前端校验只是用户体验。判断重复场次的 SQL 可以理解为:
select count(*) from vaccine_schedule where site_id = #{siteId} and schedule_date = #{scheduleDate} and start_time = #{startTime} and status != 'CLOSED'只要查出的数量大于 0,就直接抛业务异常提示已有场次,不要再往数据库里插数据。
4.2 预约流程的本质是什么
预约流程本质上是一个“库存扣减 + 状态流转”的过程。用户提交预约时后端要做的动作包括:
- 先判断用户是否登录,并确认用户的身份信息是否完善。
- 查询场次,确认场次状态是已发布,且预约时间还在可预约范围内。
- 判断剩余号数是否大于 0,并校验该用户是否已经预约过同一场次。
- 扣减号源。
- 生成预约记录,初始状态为“待接种”。
- 返回预约凭据,比如预约码。
这里最容易忽略的是“幂等性”,也就是用户手抖点了两次提交,不能生成两条预约记录。简单方案是给预约记录表加唯一约束,比如user_id + schedule_id联合唯一,第二次再插入时数据库会直接报错。更好一点的做法是提交预约前先查一次是否已存在,再配合事务锁,把校验和插入放在同一个事务里。
4.3 防超卖用数据库原子更新就够了
毕设答辩时,老师大概率会问“并发预约你怎么控制”。如果项目里没有引入 Redis,直接用数据库就能防超卖。核心不是select再update,而是把剩余号数的判断放进 update 条件里:
update vaccine_schedule set remaining_count = remaining_count - 1 where id = #{scheduleId} and remaining_count > 0这条 SQL 执行后,如果返回影响行数为 1,说明扣减成功;如果返回 0,说明剩余号数已经不足。由于数据库更新会自动加行锁,这种方式在低并发场景下完全够用,并且不需要额外引入中间件。配合事务,把“扣减号源 + 创建预约记录”放在同一个方法里,加上@Transactional注解,基本就稳了。
如果你想展示更高阶的设计,可以在场次表加一个version字段做乐观锁,或者引入 Redis 做库存预热和 Lua 扣减。但这些属于加分项,对毕设来说不是必须,前提是你自己能够把这个方案讲清楚,否则反而容易被老师追问到答不上来。
5. 接口文档与实际开发中的权限控制
5.1 接口文档怎么看、怎么用
项目自带的接口文档一般有两种形式:一种是 Swagger 或 Knife4j 生成的在线接口页面,另一种是单独的 Markdown/Word 文档。如果是 Knife4j,后端启动后访问http://localhost:端口/doc.html就能看到在线接口列表,可以直接在页面里调试接口,比 Postman 还方便。
接口文档里的内容,重点关注三块:
- 请求路径和请求方法,比如
GET /api/vaccine/list。 - 请求参数和参数位置,是放在 URL 的 query 里,还是放在请求 body 的 JSON 里。
- 响应结构,很多项目会用统一响应体包装,比如
{code:200, message:"成功", data:...}。
看接口文档时要特别注意“参数必填项”,后端大多数框架都有参数校验,比如@NotBlank(message = "疫苗名称不能为空")。如果漏传参数,接口会返回 400 或者业务码,但不一定是系统崩溃,学会看响应里的 message 是排错的关键。
5.2 JWT 登录与权限设计
前端每次请求后端接口,都要在请求头里带Authorization: Bearer token。登录后后端会生成一个 JWT 字符串返回给前端,前端存在localStorage或sessionStorage里,之后请求通过 Axios 拦截器统一加请求头。
权限控制如果用的是 Spring Security,常见的配置是:
http.authorizeRequests() .antMatchers("/api/auth/**").permitAll() .antMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated();如果用的是拦截器,思路也类似,先放行登录注册接口,再校验 token,再识别角色。这个逻辑看起来简单,但有两个坑必须注意:一个是静态资源和预检请求 OPTIONS 要放行,否则前端跨域请求会直接被拦;另一个是 token 过期后的处理,前端需要在拦截器中根据返回码跳转到登录页,同时清理本地会话。
6. 运行项目时常见的五个坑与排查思路
6.1 后端启动失败
先看异常信息再动手,不要一上来就百度。常见原因有:
- 数据库连接不上,检查
application.yml里的 URL、账号、密码。 - 端口被占用,Windows 下用
netstat -ano | findstr 8080查看进程,或者直接改端口。 - Maven 依赖没下载完,IDEA 里刷新 Maven 项目,并把本地仓库的
lastUpdated文件清掉再重新拉取。 - Lombok 插件没装好,启动时报找不到
log对象或者 getter/setter,先在 IDEA 插件市场安装 Lombok,并开启注解处理。
6.2 前端页面空白或请求 404
页面空白先按 F12 看控制台报错。如果控制台报Cannot read property 'xxx' of undefined,通常是后端返回的数据结构和前端预期不一致,用接口对比文档看一下字段名。请求 404 时,看请求路径是相对路径还是绝对路径,注意 Vue 项目里publicPath如果配置不对,打包后资源路径也会出问题,开发模式下一般不用管。
开发模式下最常见的是跨域报错。后端如果没配置跨域,前端代理可以解决:在vue.config.js里添加 devServer 代理,把/api转发到后端地址,这样前端请求同源,后端不需要单独处理 CORS。
6.3 预约报错但没提示具体原因
排查思路按下面顺序走:
- 看后端控制台有没有 SQL 异常,特别是外键约束和字段不存在。
- 看前端打印的响应数据,有没有统一返回的 message。
- 用接口文档里的调试功能单独调一次预约接口,排除前端页面逻辑的干扰。
- 检查数据库里场次的状态是不是“已发布”,很多预约失败是因为前置状态不对。
- 检查用户有没有重复预约,联合唯一约束会返回
Duplicate entry,这种异常需要被捕获并转换成友好提示。
这类业务异常要用自定义异常处理,否则数据库底层的异常直接抛给前端,用户体验很差。例如可以建一个BusinessException,统一在 ControllerAdvice 里捕获,返回给前台一个可读的message。
7. 答辩前我建议重点准备的知识点
如果你是拿这个项目做 Java Web 毕业设计,功能做完只是第一步,答辩时老师更关心的是“你有没有真正理解这套系统”。下面几个问题,建议提前准备答案:
- 为什么用 SpringBoot 而不用传统的 SSM?围绕简化配置、内嵌容器、生态整合来答。
- 数据库表为什么要拆成用户表、场次表、预约记录表?用第三范式、避免数据冗余来答。
- 并发下如何防止同一个号源被重复预约?往上翻到第 4.3 节那一条原子更新 SQL,能把这条讲清楚基本就过关了。
- 如果预约人数突然暴增,系统会怎么表现?可以从数据库行锁竞争、接口响应变慢、前端超时这几个角度聊,再提出加缓存、限流、消息队列等优化方向。
- 前端如何做路由权限控制?比如根据用户角色动态生成路由表,或者在路由守卫里判断 token 是否有效。
我的切身体会是,这类项目的难点从来不是某个技术点本身,而是把业务规则和技术方案串起来。比如疫苗发布和接种预约看起来是两个模块,实际数据是联动的;接口文档看起来只是交付物,真排错时比什么都管用。如果你能顺着“数据库设计 -> 接口设计 -> 前端页面 -> 部署验证”这条线把它完整走一遍,这套源码能给你带来的提升,绝对超过“复制粘贴跑通”的效果。
最后分享一个我自己的小习惯:拿到这种带 SQL 脚本的项目,第一件事不是启动,而是先打开数据库看表注释和字段注释。注释写得越细,说明项目结构越规范,你后面读代码、改功能、写论文都会轻松很多。如果发现某些字段没有注释,稍微花点时间补上,答辩时老师翻数据库也不会觉得难看。这套系统本身就是很好的学习素材,关键是别只停在“能跑”的层面,多问几个为什么,收获会完全不一样。