ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

SpringBoot+Vue+MySQL疫苗预约系统实战:从数据库设计到部署上线

2026/10/2 14:11:11 拓冰建站 浏览量
SpringBoot+Vue+MySQL疫苗预约系统实战:从数据库设计到部署上线 SpringBootVueMySQL这套组合在毕业设计里算是出场率最高的组合了没有之一。我当年带过不少学生做类似选题也帮人排查过不少预约类系统的Bug。疫苗发布和接种预约这个题目听起来不算新鲜但它背后涉及到的权限管理、库存扣减、时间窗口控制、消息通知这些业务逻辑做扎实了并不容易。这篇文章就结合这套疫苗发布和接种预约系统聊聊从需求拆解到数据库设计、从后端核心逻辑到前端页面实现、最后到部署上线的完整链路。内容偏实战很多细节是文档里不会写但实际开发中一定会碰到的。1. 系统功能蓝图一个疫苗预约平台到底要拆出哪些角色和流程拿到这个题目的时候我第一反应不是急着写代码而是先梳理业务。很多同学做毕设容易一上来就建表、写接口结果做到一半发现业务逻辑对不上推倒重来。疫苗发布和接种预约这个系统核心其实就三件事疫苗信息的发布管理、用户的接种预约、预约后的接种记录跟踪。但要支撑这三件事背后涉及的角色和状态流转比想象中复杂。先拆角色。系统至少要分三类用户普通注册用户、疫苗接种管理员、系统管理员。普通用户能做什么浏览疫苗列表、查看疫苗详情和剩余库存、提交预约申请、查询自己的预约记录、接种完成后查看接种凭证。管理员能做什么发布疫苗信息、维护疫苗库存、审核预约申请、确认接种结果、管理用户状态、查看预约统计报表。这里有个点容易被忽略疫苗不是普通商品它有批次、有效期、库存、适用人群这些属性。所以疫苗表不能简单设计成疫苗名称库存数量要考虑到批号管理和效期提醒。还有预约流程很多人以为预约就是把表单提交上去就完事了实际业务里还需要审核环节——管理员要确认预约人的身份信息和接种条件是否符合然后才能确定预约成功。预约状态流转也是核心设计点。我建议的状态机是这样待审核 - 已通过/已拒绝 - 待接种 - 已完成/已取消。每一步状态变更都要有对应的时间记录和操作人记录方便后续追溯。很多学生做到这里就省略了审核环节直接提交即成功这会让论文答辩的时候被老师质疑业务逻辑不完整。还有一个容易忽略的角色是接种点或者接种机构。同一个城市可能有多个接种点不同接种点提供的疫苗批次不一样预约名额也需要按接种点维度去控制。所以疫苗库存不能只放在疫苗表里要设计成疫苗-接种点-批次关联库存表这样才能支持多接种点独立管理。整个核心流程我建议用一句话描述清楚管理员发布疫苗批次并设置各接种点的可预约数量 - 用户浏览疫苗列表并选择接种点提交预约 - 管理员审核预约并确认 - 用户按时到接种点完成接种 - 管理员录入接种结果 - 系统生成接种记录凭证。把这条主线理出来数据库设计和接口设计就都有依据了。2. 技术选型逻辑SpringBootVueMySQL为什么是毕业设计的最优解技术选型这块我直接给结论对于大部分学生来说SpringBootVueMySQL就是现阶段的答案组合。如果你对系统并发要求极高、需要分布式事务、需要微服务拆分那另说但毕业设计场景下这套组合在开发效率、学习成本、答辩可讲性上是最均衡的。SpringBoot作为后端框架核心优势在于约定优于配置。你不用像传统SSM那样写一堆XML配置application.yml里配好数据源、MyBatis映射、端口号就能跑起来。SpringBoot内置的Tomcat也省去了单独配置服务器的麻烦打成一个Jar包就能运行这对后文要讲的部署环节非常友好。另外SpringBoot对Spring MVC、MyBatis、Druid连接池、JWT鉴权这些常用组件的集成都很顺滑写业务代码的时候可以专注在逻辑上。为什么选Vue而不选JSP或者Thymeleaf最直观的原因是前后端分离。Vue通过Axios调用后端接口渲染交给前端框架后端只负责提供JSON数据。这种模式有两个好处一是接口清晰前端工程师和后端工程师可以并行开发二是部署灵活前端打包后的dist目录可以被SpringBoot作为静态资源托管也可以独立部署到Nginx两种方式都行。Vue的组件化开发思路也贴近真实企业项目答辩的时候解释起来比较有说服力。而且Vue的学习曲线相对平缓配合Element UI做后台管理界面几天就能上手效果还很不错。MySQL这边就没太多好纠结的了。它免费、轻量、资料多Navicat或者命令行都能操作。对于预约系统这种规模的数据量MySQL的性能完全够用。需要注意的是设计表时用好InnoDB引擎、设置好字符集utf8mb4是标配不然存不了生僻字和表情符号、合理添加索引这三件事做好了数据层就稳了。最后说一句为什么这个组合能讲。毕业设计答辩的时候老师一定会问为什么用这个技术。SpringBoot可以提高开发效率、Vue实现了前后端分离和更好的用户体验、MySQL是成熟稳定的关系型数据库这三个理由每个都能展开讲不会没话说。反过来如果你用了特别冷门的技术栈老师不熟就容易多问而且你也未必能解释清楚。3. 数据库设计预约系统最容易翻车的地方数据库设计是这套系统能不能撑住业务的关键。我见过不少同学把疫苗库存直接放在疫苗信息表里预约成功就减库存取消预约就加库存看起来没问题但一旦涉及多接种点、多批次、审核流程这种设计马上崩。下面直接给出我整理后的核心表结构和设计理由。3.1 用户与角色表权限控制的地基用户表字段至少要覆盖用户名、密码、手机号、身份证号、姓名、年龄、角色类型、创建时间。密码不要明文存储建议用MD5加盐或者BCrypt加密。BCrypt是Spring Security自带的加密策略每次生成的hash不同安全性更好推荐优先使用。角色这块不建议在前端做死判断更好的方式是设计用户角色表user_role支持一个用户多个角色。虽然毕设场景下用户角色基本固定但用关联表实现更规范答辩的时候也有细节可讲。JWT令牌里存储用户ID和角色信息后端接口用拦截器校验角色权限普通用户和管理员的接口就能隔离开。3.2 疫苗与库存设计批次、效期、接种点缺一不可这是整套系统最有含金量的部分。我建议这样拆表疫苗信息表疫苗名称、生产厂家、适用人群、接种剂次第几针、疫苗类型灭活/重组/腺病毒载体批次表批次号、生产日期、有效期至、入库时间接种点表接种点名称、地址、联系电话、服务时间库存表关联疫苗批次ID 接种点ID 可预约余量 已预约数量这样的设计才能支撑起某接种点某批次疫苗还有多少可预约名额这种查询。库存表里的余量字段就是预约时候的防超卖关键。后面讲预约逻辑的时候会详细说怎么扣减。疫苗效期是个容易遗漏的点。预约页面应该根据当前日期自动过滤掉已过有效期的批次管理员登录后台也会有到期预警提示。这个功能实现起来不难但很体现系统完整性推荐加上。3.3 预约与接种记录状态流转的完整链条预约表字段建议这样预约编号、用户ID、疫苗批次ID、接种点ID、预约时间、预约日期、状态、审核人、审核时间、接种完成时间、取消原因。这里我特别说明一下为什么要把预约和接种分成两张表而不是一张表。预约单创建的时候接种还没有发生是一系列状态中的待接种接种完成后需要生成一条接种记录凭证上面有疫苗批号、生产厂家、接种机构、接种时间、下次接种时间如果有第二针这些字段跟预约单的关注点不完全一样。分成两张表业务边界更清楚查询各自领域的数据时也不容易混淆。预约状态字段建议用tinyint存数字0-待审核、1-已通过、2-待接种审核通过后进入待接种、3-已完成、4-已取消、5-已拒绝。数字比字符串省空间代码里用枚举常量定义可读性也不差。3.4 索引与事务并发场景下的保命设计预约表最频繁的查询条件是用户ID和状态所以联合索引user_id, status建议加上。库存表查询条件是批次ID接种点ID也建议设置联合唯一索引。预约编号这种业务字段要设置唯一索引防止重复预约。事务这块重点讲一下。用户提交预约的接口里涉及两步数据库操作第一步查库存余量第二步扣减库存并插入预约记录。这两步必须放到同一个事务里否则可能出现查询有余量但插入时库存已经被扣光的情况。用Transactional注解搭配数据库的行锁SELECT ... FOR UPDATE才能在并发环境下保证不超卖。这个问题在答辩的时候几乎是必问的提前准备好答案。4. 后端核心模块实现从登录鉴权到预约扣库的完整思路后端这块我按照入口 - 鉴权 - 权限 - 核心业务的顺序来拆。框架搭建无非是新建SpringBoot项目、引入依赖、配置数据源这些基础操作不展开重点讲几个核心模块的实现思路和细节。4.1 JWT登录鉴权无状态会话怎么设计项目用JWT而不是HttpSession主要是为了前后端分离的架构。前端登录成功后拿到一个token存到本地localStorage或sessionStorage每次请求在请求头里加上Authorization: Bearer {token}后端拦截器解析token获取用户信息不需要在服务端保存会话状态。实现上分三步走。第一步定义一个JWT工具类负责生成token和解析token。生成的时候把用户ID、用户名、角色类型作为claim放进去设置过期时间建议2小时用HMAC256算法签名。第二步写一个拦截器实现HandlerInterceptor接口。在preHandle方法里从请求头取出token调用JWT工具类解析解析成功就把用户信息放到request的attribute里传给后续的Controller解析失败直接返回401状态码和统一返回体。第三步写配置类注册拦截器并配置放行路径。登录接口、注册接口、验证码接口、疫苗列表和详情接口这些不需要登录就能访问的路径要放行。管理员的接口在拦截器里额外校验角色保证只有管理员能访问。这里有个经验要分享JWT令牌在拦截器中解析的用户信息建议直接作为参数传递到Controller层而不是在Controller里再查一次数据库。减少无谓的查询接口响应速度会明显提升。当然敏感操作比如修改密码需要到数据库里校验用户当前密码这个另说。4.2 疫苗发布与分页查询Conrtoller层该瘦身疫苗发布接口没什么高深技术就是管理员提交表单后端校验字段完整性然后插入数据库。但有几个字段必须校验疫苗名称不能为空适用人群不能为空批次号格式要合法。建议用Spring Validation框架的Valid注解配合实体类上的NotBlank、NotNull做参数校验代码干净且不容易漏。分页查询疫苗列表是用户端核心接口用MyBatis Plus的Page插件就可以。构造查询条件时支持按疫苗名称模糊搜索、按疫苗类型筛选、按接种点筛选。返回的数据结构直接用Page对象或者自定义分页返回体包含总条数、当前页数据列表、总页数这些字段。需要注意的细节是疫苗列表返回给用户的字段要脱敏。管理员可见的字段比如批次成本、入库数量不应该返回给普通用户。这里建议定义VOView Object类只包含页面展示需要的字段不要直接在Mapper层把DO返回给前端。很多人容易忽略这个虽然毕设评审不一定抓到但这是区分你和其他学生代码质量的关键点。4.3 预约核心逻辑防超卖、状态机、定时任务预约接口是整个后端最核心的部分我详细说实现。前端传参用户ID从token里解析、疫苗批次ID、接种点ID、预约日期和时段。后端逻辑第一步校验该用户是否已存在该疫苗批次的预约记录。比如这个疫苗需要打两针一针和二针预约记录不能冲突同一天不能有两个预约。用联合索引user_id, vaccine_batch_id可以快速查出是否有待完成状态的预约有就直接拒绝。第二步查询库存表的可预约余量。这里必须用SELECT * FROM stock WHERE vaccine_batch_id ? AND site_id ? FOR UPDATE也就是行级锁。锁住这一行之后判断余量是否大于0小于等于0就返回该接种点已约满。第三步扣减余量并插入预约记录。余量减1预约记录状态设为待审核。这两步和第二步的查询放在同一个事务里任何一步失败就整个回滚。这里注意一个细节加上FOR UPDATE之后这个接口同一时间只有一个人能操作同一个批次的库存并发预约时必须排队。如果预约请求量特别大会有一定的性能损耗但毕业设计这个量级完全不用担心。真要优化可以用乐观锁字段加版本号的方式但实现复杂度高一些必要性不大。审核操作也要说。管理员在后台看到待审核的预约单点击通过后预约状态从待审核变更为待接种同时生成一条接种记录草稿。点击拒绝则状态变为已拒绝并回补库存余量就是让可预约数量加1。这个回补逻辑必须和状态变更放在同一个事务里不能只改状态忘了加库存。定时任务我建议做两个。第一个是自动取消未支付或未确认的预约这个视具体业务而定通常可以设定超过X小时未确认自动取消第二个是预约接种日已过但管理员未录入结果的预约单自动标记为已完成或超时未接种。两种都用Spring的Scheduled注解配合cron表达式实现扫描条件和更新时间做成配置项方便后期调整。4.4 统计与导出给论文和答辩加分的隐藏模块预约统计模块是容易被忽略但值得做的功能。用ECharts在前端展示每日预约量、疫苗预约热度、各接种点饱和度等图表后端提供对应的聚合查询接口。聚合查询用MyBatis的XML文件写SQL配合COUNT和GROUP BY就能实现。比如按日期统计预约数量SQL大概长这样SELECT DATE(create_time) AS date, COUNT(*) AS total FROM appointment WHERE create_time BETWEEN #{start} AND #{end} GROUP BY DATE(create_time) ORDER BY date这个功能做出来一方面让整个系统看起来更完整另一方面答辩的时候有数据可讲还能顺便展示你的SQL水平。导出Excel功能可以用Apache POI或者EasyExcelEasyExcel更轻量官方文档也清楚。把预约列表导出成Excel管理员可以离线查看数据这个功能也很加分。5. 前端Vue实现要点动态路由、Axios封装、关键页面前端部分我的建议是直接用Vue3 Vite Element Plus的组合。相比Vue2 Vue CLIVue3的Composition API写起来更清晰Vite的启动速度也秒杀Webpack。Element Plus的组件丰富后台管理界面基本就是拼组件。5.1 登录页和Token管理前端鉴权的第一道门登录页比较简单表单加验证码提交。但Token管理这块有几个细节要处理好。第一登录成功后把token存储到sessionStorage还是localStorage我建议localStorage因为刷新页面token还在不会因为浏览器关闭就要重新登录。第二在Axios的请求拦截器里统一设置Authorization头这样所有接口都会自动带上token不用每个请求单独配置。// request.js axios.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config })第三响应拦截器里统一处理token过期。后端返回401状态码时前端清掉本地token并跳转到登录页同时用Element Plus的Message提示登录已过期请重新登录。唯一要注意的是登录接口本身不能被这个逻辑拦截不然登录的时候报401也会跳登录页形成死循环。5.2 路由守卫与动态权限菜单路由守卫是前端权限控制的关键。在router.beforeEach里判断如果用户访问的路径需要登录而本地没有token就跳转到登录页。如果已登录但访问的是登录页则跳转到首页。如果有token但访问的是管理员页面就检查本地存储的角色信息非管理员跳转到403页面。这里多说一个进阶实现根据后端返回的角色权限动态生成路由菜单。也就是管理员登录后能看到的菜单项和普通用户不同而不只是页面跳转拦截。实现思路是登录后请求后端的当前用户菜单接口拿到菜单数据后通过Vue Router的addRoute方法动态添加路由。这个功能在论文里可以单独立一节展现你的前端功力。5.3 预约页面的关键交互预约页面有几个交互细节直接影响用户体验和系统的正确性。第一个是疫苗批次选择和日期选择联动。用户选了一个疫苗批次后要马上展示该批次在各接种点的剩余名额。这里要注意的细节是已约满的接种点要置灰不可点击数据从后端实时拉取不能只靠页面初始化时的缓存。第二个是日期选择器的范围限制只能预约未来3-7天内的日期之前的日期直接禁用这一步在前端做一层限制后端也要再做一次校验防止绕过前端直接调接口。第三个是提交预约后的反馈。成功要提示预约成功等待管理员审核失败要展示明确的失败原因比如该批次疫苗在你选择的接种点已约满。页面组件化是一个容易被忽略但很重要的点。疫苗卡片、预约表单、预约记录表格都可以抽成独立的Vue组件父组件负责数据分发子组件负责展示和交互。组件化之后代码的可维护性会明显提升这在答辩的时候也是能体现工程能力的地方。5.4 状态展示与用户体验细节预约列表页有多个状态要直观展示给用户。我建议用Element Plus的Tag组件不同状态不同颜色待审核橙色、待接种蓝色、已完成绿色、已取消灰色、已拒绝红色。点击取消预约按钮时只有状态是待审核才能取消已经审核通过的预约要联系管理员前端把按钮禁用掉。还有一个细节是移动端适配。虽然毕设评审不会强制要求移动端完美显示但简单的栅格布局和流式宽度可以保证手机浏览器打开页面也能用。前端框架里用Element Plus的Row和Col布局合理设置span就能做到基本的响应式。6. 部署与打包从源码到可演示项目的完整路径部署这块是我辅导学生时踩坑最多的地方。很多同学代码都写完了但部署环节反复出问题。我把最稳妥的流程整理出来按步骤操作基本不会错。6.1 本地打包前端前端项目根目录执行npm run build默认生成dist目录。Vite默认的base是/如果你的系统要部署在服务器根路径下这个不用改如果部署在子目录比如http://ip:8080/vaccine/就需要在vite.config.js里设置base: /vaccine/这个细节特别容易踩坑部署完了发现页面白屏、资源404多半就是这个问题。打包完成后dist目录下会有index.html、assets目录等文件。这部分文件要么拷贝到SpringBoot的static目录要么部署到Nginx。6.2 两种部署方式对比与选择方式一前端dist目录拷贝进SpringBoot的src/main/resources/static里后端一起打包成单个Jar。这种方式最简单一个Jar全搞定演示的时候直接java -jar就能跑。但缺点是每次改前端都要重新打包后端比较繁琐。方式二前端dist目录部署到Nginx后端Jar单独运行Nginx配置反向代理把/api开头的请求转发到后端8080端口。这种方式更贴近真实生产环境前端后端独立部署、独立维护但配置稍微复杂一些。对于毕业设计我推荐方式一省事。单个Jar直接跑演示的时候不依赖额外环境。如果你想把部署写得更详细、显得更有深度可以在论文中把方式二也介绍一遍对比两种方式的优缺点是一种很好的加分写法。方式一的具体操作前端build完成把dist目录的内容拷贝到后端src/main/resources/static目录下重新打包后端然后用命令。mvn clean package -DskipTests打完的Jar包在target目录下运行命令java -jar vaccine-system-1.0.0.jar --server.port8080打开浏览器访问http://localhost:8080会自动跳转到前端的首页。注意SpringBoot会把static目录下的index.html作为默认欢迎页所以拷贝时不用做额外配置。但要确认没有在Controller里写了根路径的GetMapping(/)如果有会覆盖掉默认行为跳不到index.html了。6.3 服务器部署的环境准备服务器上部署所需的运行环境JDK 1.8建议JDK 8绝大多数毕设的项目版本都兼容8、MySQL 5.7、Maven如果没有用Docker。数据库导入用Navicat或者命令行都行。拿到项目里的sql文件后先在服务器上创建数据库再把sql文件导入。导入前检查一下sql文件里有没有指定数据库名的语句有的话要先创建对应的数据库。还有一个常见的坑字符集不一致导致中文乱码。创建数据库时要指定utf8mb4字符集CREATE DATABASE vaccine_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;6.4 部署后的自检清单系统启动后不要急着演示按清单快速自检一遍首页是否正常加载疫苗列表接口是否返回数据注册一个新用户看验证码接口是否正常登录后能否正常提交预约预约后库存是否有变化管理员账号能不能登录后台疫苗发布后前台能否看到JWT过期后是否自动跳转登录页这套自检流程是答辩前的最佳实践。同时建议把项目里的测试数据清空一部分、从管理员后台重新发布若干条疫苗信息、造几条不同状态的预约记录这样演示的时候数据干净、状态完整老师看到的系统是活的。6.5 论文与部署文档的配合写法论文里部署章节不用长篇大论贴后端源码重点是让阅读者看完文档就能把系统跑起来。我建议的文档结构是环境要求JDK、MySQL、Node版本、数据库配置sql脚本位置、导入步骤、前端打包npm install、npm run build、后端启动maven打包、java启动、访问地址与默认账号。每一步配一张截图文档长度控制在10-15页就足够了。源码数据库论文部署文档这个组合里部署文档的价值往往被低估。高质量的部署文档能减少大量答疑成本答辩的时候老师也会认可你的工程交付能力。7. 实测中的坑与优化方向这些细节让你的项目真正经得起问最后总结一下这套系统开发和实测过程中遇到的典型坑以及后续可以扩展的方向。第一个坑是库存超卖问题。有一个版本我没用FOR UPDATE直接用update stock set remain remain - 1 where remain 0这个原子SQL去扣减其实那条语句本身就具备防超卖能力因为MySQL在update时会锁行。但如果前面先select后update中间有时间差高并发下就超卖了。一定要保证查库存 扣库存 插预约在同一个事务里选一种方式一次性设计好。第二个坑是JWT的密钥写死在代码里。虽然是毕设但把密钥放到application.yml配置里作为项目配置项显得更规范。环境切换时也能用不同配置不会因为代码打包泄露。第三个坑是服务器时区问题。部署到服务器后发现预约时间和本地时间差8个小时。原因是MySQL连接串里的serverTimezone没设置或者设置成了UTC。解决办法是连接串加上serverTimezoneAsia/Shanghai并且服务器系统时区也要确认是Asia/Shanghai。第四个坑是前端动态路由刷新后404。用addRoute添加的动态路由浏览器一刷新就丢失了页面变成404。解决方案是在路由守卫里做一个标志位刷新后如果发现路由表状态不一致就重新创建动态路由再执行进入操作。优化方向上根据这套系统的后续空间我给出几个建议加入疫苗库存不足时的站内消息或者短信提醒需要接入消息队列或者短信服务商毕设做到站内消息就够了加入接种凭证PDF的生成与下载后端用iText或者POI的PDF组件实现加入用户体检信息表预约时自动校验适用人群条件比如某些疫苗不适合特定年龄人群加入Redis缓存疫苗列表和热点数据降低数据库压力这个优化在答辩时很有说服力用Spring Security框架替换手动拦截器实现更完整的安全控制体系这里面每一项都可以展开成一个小的优化章节写进论文扩充篇幅的同时还能体现你的技术视野。但实际代码改动建议控制在1-2个太多反而给答辩增加压力。这套系统从功能设计、数据库设计到代码实现和部署如果是第一次做全栈项目建议给自己预留三到四周的完整时间不要指望一周就把所有功能全部写完。优先保证核心主流程跑通再逐步往上面加功能。做项目的过程本身就是学习的过程把SpringBoot、Vue、MySQL这三条线吃透答辩的时候你自然底气十足。