ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Java SSM+Vue+SpringBoot疫苗接种管理系统开发实战

Java SSM+Vue+SpringBoot疫苗接种管理系统开发实战 java-ssm366疫苗接种管理系统vue-springboot说到这类项目我脑子里第一个蹦出来的关键词就是java、ssm、vue、springboot。凡是做过JavaWeb毕业设计或CRUD实战项目的人看到这个标题基本都能猜到七七八八——这是一个典型的前后端分离项目后端可以用SSM框架组合Spring SpringMVC MyBatis也可以用Spring Boot MyBatis Plus快速搭建前端走Vue生态整体做一套疫苗接种管理系统。这个项目能解决什么问题说白了就是把线下疫苗接种的登记、库存、预约、记录这些繁琐流程搬到线上。社区卫生院、疾控中心的工作人员不用再拿Excel记账居民也不用排队填单子数据一查就能出统计报表。如果你是Java初学者、正在准备毕业设计、或者想搞一个完整的前后端分离实战项目练手这套系统的思路和代码结构都挺值得拆一拆的。我也做过不少同类系统这篇文章就把我实际开发中踩过的坑、琢磨过的设计决策尽量原原本本讲给你听。1. 项目全景这套系统到底在解决什么问题1.1 从需求侧看疫苗接种管理的核心痛点很多同学拿到“疫苗接种管理系统”这个题目第一反应是这不就是增删改查吗疫苗表加个增删改查用户表加个增删改查不就完事了如果你真的这么想那做出来的东西大概率只是一个“表格管理系统”而不是业务系统。我自己的体会是疫苗接种管理至少有四个绕不开的核心痛点第一是疫苗批次与库存的强关联。疫苗不是普通商品它有批号、有效期、生产厂家一个批次的疫苗可能分散到不同接种点接种点之间还可能调拨。如果库存表只记录“还剩多少支”而不记录批次一旦某个批次出问题要召回你根本查不到哪些人打了这批疫苗。第二是预约与库存的联动。居民线上预约接种本质上就是在锁定某一天的疫苗库存。如果预约功能只管预约不管库存扣减那就有可能出现在线约了20个人、现场只剩5支疫苗的尴尬场面。这个联动关系处理不好系统上线第一天就会被投诉淹没。第三是接种记录的可追溯性。每一针疫苗打给谁、哪个医生打的、哪个批次的苗、接种时间是什么这些数据不仅是业务数据还是卫生监管的数据基础。系统设计时如果不考虑“每一条接种记录都能回溯到疫苗批次和接种人员”后面补起来会非常痛苦。第四是多角色权限隔离。系统至少有管理员、接种医生、普通用户三种角色。管理员管库存和统计医生管登记和录入用户管预约和查询三者的数据边界必须清晰。很多项目在这块偷懒结果就是“用户能看库存数据”、“医生能删用户账号”这种安全事故。理解了这四点你再看这个项目的标题就能明白它不是一个简单的CRUD练习而是一个需要考虑业务约束、角色权限、数据追溯的中小型管理系统。1.2 技术选型背后的取舍SSM还是SpringBoot MyBatis Plus标题里同时出现了ssm、springboot这其实是很多同学纠结的地方。我直接说结论如果这个项目是你自己从零写选Spring Boot MyBatis Plus会舒服很多如果课程要求必须用SSM那也别慌思路完全一样只是配置方式更繁琐。为什么我更推荐Spring Boot原因有三第一Spring Boot把Spring、SpringMVC的配置都自动装配掉了。SSM时代你要自己配web.xml、spring-mvc.xml、mybatis-config.xml任何一个XML写错项目启动就是一片红。Spring Boot里一个application.yml就能搞定大部分事情省下来的时间足够你把业务逻辑想清楚。第二MyBatis Plus对单表CRUD的杀伤力太大了。疫苗接种管理这类系统80%的数据库操作都是单表增删改查加条件查询。用MyBatis Plus的BaseMapper你连SQL都不用写直接selectList、insert、updateById就能完成基本操作。只有查报表、做复杂关联查询的时候才需要手写XML。这种开发体验对新手极其友好。第三项目部署更省心。SSM打war包要配TomcatSpring Boot直接mvn package打一个可执行jar服务器上java -jar就完了。我自己第一次部署SSM项目被ClassNotFound折磨了一整天换成Spring Boot之后部署时间压缩到十分钟以内。那SSM还有没有存在的价值有。如果你是计算机专业学生SSM是理解Spring生态底层原理的一条好路径——手动配过XML的人才能真正理解Spring Boot的“约定优于配置”到底帮你省了什么。但从“快速交付一个能用的系统”角度Spring Boot明显是更理性的选择。至于前端标题里的vue是主流解法。我建议用Vue 2 Element UI的经典组合原因很简单Element UI的中文文档全、组件全、案例多表格、表单、弹窗、分页这些后台管理高频组件开箱即用对新手非常友好。Vue 3 Element Plus也完全可以但如果你参考的现成代码大多是Vue 2硬上Vue 3反而会踩很多迁移的坑。2. 核心业务模块拆解2.1 用户端预约登记与接种记录查询用户端主要面向普通居民核心功能就两块预约接种、查询记录。预约接种的流程看起来简单——选疫苗、选日期、提交——但实际操作中的数据流转值得仔细设计。以我做的系统为例预约记录表里至少要存这些字段用户id、疫苗id、预约日期、预约时间段上午/下午、状态待接种/已完成/已取消、创建时间。这里有个关键决策预约时要不要立刻扣减库存我的做法是预约时锁定库存但不扣减。什么意思我的疫苗库存表里有一个locked_stock字段用户提交预约时available_stock先减去1locked_stock加上1。等到用户真正完成接种接种记录生成再把locked_stock减掉变成实际出库。如果用户取消预约就把available_stock加回来locked_stock对应减掉。这样做的好处是预约和库存是两个独立操作但数据始终保持一致不会出现库存显示有苗、实际预约超卖的情况。查询记录这块需要注意分页。用户的接种记录会随着时间增长一次性全查出来不仅慢前端渲染也卡。我用的是MyBatis Plus自带的分页插件前端传pageNum和pageSize两个参数后端返回total和records配合前端的分页组件就能实现流畅翻页。这里有一个容易犯的错分页查询一定要记得加ORDER BY我见过不少系统因为查询结果没有排序翻页到后面数据就乱了这个坑踩一次就长记性了。2.2 管理端疫苗库存、批次台账与数据统计管理端是系统最重的部分也是能拉开项目质量差距的部分。我强烈建议管理端至少包含三个模块疫苗管理、批次管理、统计报表。疫苗管理是基础档案对应vaccine表包括疫苗名称、生产厂家、适用年龄段、剂次说明、价格等静态信息。这里要提醒一句疫苗名称和厂家不要直接存在接种记录表里用id关联就行。很多新手图省事直接在接种记录表里存字符串名称结果后期疫苗改名或者录入不规范统计报表里全是脏数据。批次管理是疫苗领域的特色功能对应vaccine_batch表。每个批次关联一个疫苗id记录批号、生产日期、有效期、入库数量、剩余数量。批号必须做唯一约束因为它是疫苗追溯的关键索引。有效期字段建议用MySQL的DATE类型到期预警的SQL一眼就能写出来WHERE expire_date DATE_ADD(CURDATE(), INTERVAL 30 DAY)。统计报表是体现系统价值的功能。我这里做了两个核心数据一个是在库疫苗的批次有效期分布另一个是每日/每周接种量趋势。有效期分布用GROUP BY加COUNT就能搞定接种趋势用DATE_FORMAT(create_time, %Y-%m-%d)做日期分组然后计数。如果觉得还不够可以再做一个“疫苗利用率 已接种数 / 入库总数”的指标。这三个统计到手管理端的实用性立刻就出来了汇报也好答辩也好都拿得出手。2.3 数据库设计那几张表才是这套系统的底座数据库设计直接决定系统的上限。我按这套系统的业务把核心表的关系梳理如下user用户表存账号密码、姓名、身份证号、手机号、角色字段。vaccine疫苗档案表存疫苗名称、厂家、规格、说明。vaccine_batch疫苗批次表关联vaccine表存批号、有效期、入库量、剩余量。appointment预约表关联user和vaccine存预约日期、时间段、状态。inoculation_record接种记录表关联user、vaccine_batch和医生用户id存接种时间、接种部位、不良反应备注。这五张表构成了系统的核心骨架。我特别想强调两个设计细节第一user表里的角色字段我用的是role字段加整数类型1表示管理员、2表示医生、3表示普通用户。有些同学喜欢用String类型存admin、doctor查询倒是好理解但代码里到处都是魔法字符串不如整数加常量类管理清爽。当然如果项目规模更大角色权限会升级成role和permission多对多表结构但以这个项目的体量来说单字段角色完全够用。第二inoculation_record表一定要关联vaccine_batch而不是只关联vaccine这一点最容易被忽略。假设一个疫苗有A、B两个批次医生录入时选了A批次接种记录里就必须能定位到A批次否则万一A批次出问题需要召回你根本筛不出来谁打了这批苗。我见过很多所谓“疫苗系统”忽略了这层关系只能说他们没做过真正的追溯需求。具体的建表SQL一般通过MyBatis Plus的代码生成器或者数据库工具生成后面我会在实操一节详细演示。3. 后端实现的关键细节3.1 登录鉴权与Token过期处理管理系统必须有登录鉴权这一步几乎绕不开。我的方案很简单实用后端用JWT生成token前端登录后把token存到localStorage每次请求通过axios拦截器在header里带上Authorization: token后端用一个拦截器统一校验。JWT的好处是服务端不用存session天然适合前后端分离。我用的是jjwt这个库生成token时把用户id和角色塞进claims里过期时间设置为24小时。拦截器校验通过后把用户信息放到ThreadLocal里这样在controller里随时可以拿到当前登录用户。这里有几个细节必须注意第一拦截器要放行登录接口和注册接口不然用户连登录都进不去。第二token快过期时前端会请求失败我的做法是在axios的响应拦截器里判断HTTP状态码401然后清空本地token并跳转登录页同时给出“登录已过期请重新登录”的提示。第三密码存储绝对不能明文我用的是Spring Security自带的BCryptPasswordEncoder一次性密码加盐哈希数据库里存的永远不是原始密码。如果你不想引入Spring Security全家桶也可以用jbcrypt这个轻量级库效果一样。3.2 库存扣减与预约冲突的并发处理疫苗库存是典型的临界资源两个人同时预约最后一支疫苗系统必须保证只有一个人成功这就是并发控制要做的事。新手最容易犯的错误是“先查再改”——先select看一下库存大于0再update减1。在并发场景下两个请求可能同时查到库存是1然后同时执行减1操作结果变成-1。解决方案有几层从简单到复杂第一层利用数据库的行锁。MyBatis Plus的update语句可以写成UPDATE vaccine_batch SET remain_stock remain_stock - 1 WHERE id ? AND remain_stock 0MySQL在更新时会对这一行加锁后面的事务必须排队等。通过判断受影响行数是否为1就知道本次扣减是否成功。这是最简单也最可靠的做法推荐优先使用。第二层在预约表上做唯一约束。比如限制同一个用户同一天不能重复预约同一疫苗可以给appointment表建一个(user_id, vaccine_id, appointment_date)的三列唯一索引。这样一来即使两个请求并发进来数据库也会强制只让一个成功另一个直接抛重复键异常。第三层才是Redis分布式锁但说实话对于这个体量的项目用Redis锁属于过度设计。单机MySQL的原子更新加唯一约束已经完全够用而且实现简单、不用额外引入中间件、排查问题也方便。我一直强调一个原则先够用再优化为不存在的并发量提前设计复杂方案是自找麻烦。3.3 接口设计RESTful风格与统一返回结构前后端分离项目接口规范直接影响对接效率。我建议统一用RESTful风格并对返回结构做一个全局包装。拿预约接口举例GET /api/appointment/list分页查询预约列表。POST /api/appointment/add新增预约。PUT /api/appointment/cancel/{id}取消预约。GET /api/appointment/detail/{id}预约详情。返回结构我用一个统一的Result类包装字段就三个code、msg、data。code200表示成功code500表示业务失败data是具体返回数据。这里有一个实用技巧后端的Result.ok(data)和Result.error(msg)可以做成静态方法controller里一行代码就返回了代码观感会好很多。拦截器里如果校验失败也要走统一返回结构而不是直接抛一堆堆栈信息给前端。另外关于接口路径再提一句。很多人习惯写/api/selectAppointmentList这种动词式路径虽然也能跑通但不符合RESTful语义。我自己的项目里已经全部改成资源名词加HTTP方法的方式前端对接的人一眼就能看懂团队协作时效率提升不是一点点。4. 前端Vue的落地经验4.1 Vue环境配置的几个坑Vue环境配置是很多人的第一道坎我在这里浪费过不少时间先把结论写出来。如果你用Vue 2直接npm install -g vue/cli装全局脚手架然后vue create 你的项目名初始化项目。如果你用Vue 3npm create vuelatest是官方推荐的Vite方式。这里最需要注意的就是Node.js版本Vue 2的脚手架要求Node版本别太高Vue 3的Vite要求Node 16及以上。我遇到过无数次npm install报错查到最后都是Node版本和依赖库不兼容导致的。node_modules安装慢的问题建议直接把npm镜像切到国内源npm config set registry https://registry.npmmirror.com装依赖的速度能快好几倍。如果装一半失败先删掉node_modules和package-lock.json再重新装很多时候比逐个排查依赖冲突更有效率。还有那种“项目从别处拷过来本地怎么跑起来”的场景。第一步是确认对方用的Node版本第二步是在项目根目录执行npm install第三步是看package.json里的scripts一般就是npm run serve或者npm run dev。如果启动报tsconfig相关错误多半是Vue 3的TypeScript项目没有正确安装vue/tsconfig补一下依赖就能解决。4.2 页面组件划分与路由守卫前端项目一开始就要做好页面和组件的划分不然后期全是面条代码。按这套系统的页面量我建议把views目录按角色分文件夹views/admin放管理端的疫苗管理、批次管理、统计报表views/user放预约、记录查询views/common放登录、注册。每个页面内部如果有重复模块再抽组件到components目录比如疫苗信息卡片、分页工具栏这些。路由守卫是Vue里一个必须掌握的点。我用Vue Router的beforeEach全局前置守卫实现权限控制判断目标路由是否需要登录需要的话检查localStorage里有没有token有token再判断路由的meta.role和本地存的角色是否匹配不匹配就跳403页面。这样做的好处是即使有人手动修改前端路由地址也进不了无权访问的页面。当然真正的安全还得后端接口鉴权兜底前端路由守卫只是用户体验层面的拦截。4.3 axios封装与交互细节axios封装我每次做项目都会写因为不封装的代码后期改起来想死。我在utils/request.js里统一定义axios实例设置baseURL指向后端地址加上请求拦截器自动带token响应拦截器统一处理HTTP错误和业务错误码。具体交互细节上前端所有的页面状态变化loading、提交按钮禁用都跟接口状态绑定比如“提交预约”按钮点击后就禁用等后端返回成功再跳转避免用户重复提交产生多条预约记录。还有一个经常踩的坑是跨域问题。开发环境我在Vue的根目录下建vue.config.js配置devServer.proxy把/api代理到http://localhost:8080前端代码里就写相对路径/api/...从而避免开发时的CORS报错。生产环境我在后端用一个配置类统一处理CORS跨域允许指定域名访问。这两件事不做前端和后端永远联调不通。5. 从本地到上线的部署实操5.1 打包前必须做的三件事开发环境跑得再欢到了部署阶段如果不做检查照样会翻车。我每次打包前固定做三件事第一数据库初始化脚本重新执行一遍。很多数据库表结构是在开发过程中慢慢加字段改出来的本地数据库已经是最新结构但生产环境还是初始状态。我用MyBatis Plus的代码生成器或者直接执行SQL脚本建表优先保证create table语句是完整可执行的并提前把几条必要的基础数据比如管理员账号写进脚本。第二后端配置文件改成生产环境。application.yml里的数据库地址、密码、端口、文件上传路径全部要换成生产服务器的值。开发时连的localhost数据库部署到服务器还连localhost那肯定是连不上的。实际环境我一般用MySQL 5.7或8.0注意时区配置加上serverTimezoneAsia/Shanghai不然日期字段会出现各种诡异的偏移。第三前端构建前检查API地址环境变量。如果是Vite/Vue CLI项目我通常在.env.production里配置VITE_API_BASE_URL指向后端实际部署的域名或IP。构建命令是npm run build产物在dist目录里部署的时候把这个目录上传到服务器的静态资源目录即可。5.2 Spring Boot项目打包与可执行Jar启动后端打包我推荐用Maven的package命令生成一个xxx.jar文件上传到服务器用java -jar运行。这里有两个细节打包前要确认pom.xml里配置了spring-boot-maven-plugin并且mainClass指向你的启动类。否则打出来的jar不包含内嵌Tomcat运行时会报ClassNotFound。我就见过有人把没打完整依赖的jar拿上来部署折腾半天最后发现是打包插件没配全。启动命令不建议用java -jar app.jar这种裸命令因为CtrlC一按进程就没了。我习惯用nohup java -jar app.jar app.log 21 后台启动日志写到app.log里排查问题直接tail -f app.log。如果需要更规范的服务化管理可以写一个systemd服务文件配置ExecStart/usr/bin/java -jar /opt/app/app.jar这样开机自启、崩溃重启都有保障。5.3 宝塔面板部署前端项目的实操记录很多同学的服务器是Linux用宝塔面板管理网站。我自己的部署流程是这样的打开宝塔面板先装好Nginx和MySQL。然后创建站点把dist目录的内容上传到站点根目录再配置Nginx的location /api把请求反向代理到后端http://127.0.0.1:8080。这样用户访问站点域名看到的页面是前端静态资源请求接口时Nginx帮助转发到后端进程。这里要提醒一个坑后端接口路径的反向代理要带proxy_set_header配置否则后端拿到请求里的Host是Nginx而不是真实域名有些业务逻辑会受影响。另外权限控制方面不要在宝塔里关闭Nginx的防火墙规则太激进至少保证80端口和443端口放行。由于这个项目本身不需要任何额外通道访问外网部署起来体感上比那些依赖境外服务的项目顺畅太多。6. 高频问题排查速查我把这个项目开发过程中最常出现的问题整理成一个排查表你可以直接当成备查手册。问题现象可能原因解决方案前端接口请求403token未带或token失效检查axios请求拦截器是否从localStorage取token过期则引导重新登录CORS跨域报错后端未配置跨域或前端未走代理开发环境用devServer.proxy生产环境给后端加CORS配置类连接数据库报时区错误MySQL连接串少了serverTimezone参数在JDBC连接URL加上serverTimezoneAsia/Shanghai中文乱码数据表编码不是utf8mb4或后端未配置字符编码建表时指定CHARSETutf8mb4Spring Boot配置characterEncodingutf-8预约显示库存没扣减库存更新逻辑放在预约接口外或缺少事务在服务层方法上加Transactional保证预约与扣减同事务提交分页数据重复/缺失分页查询缺少ORDER BY给查询条件补上排序字段最好按主键或创建时间倒序Vue项目启动报tsconfigVue 3 TS项目缺少vue/tsconfig依赖安装依赖npm install -D vue/tsconfignpm install很慢未切换镜像源设置npmmirror源后重新安装打包后接口404Nginx未配置api反向代理在站点配置中添加location /api的proxy_pass指向后端端口数据统计报表显示0日期格式或SQL分组粒度不对用DATE_FORMAT统一日期格式再按天、周、月分组这是我整理出来的一套组合拳项目完成度直接体现在这些细节里。每次把表格里的对应项排掉系统基本就稳定了。写在最后的一点体会开发完这套疫苗接种管理系统我自己最深的感触是这类项目真正的分水岭从来不是框架用得花不花哨而是业务逻辑有没有闭环。预约能不能和库存对上账、接种记录能不能追溯到批次、报表能不能给管理者真正的决策依据——这些才是拿得出手的东西。框架只是表达业务的一种工具Spring Boot也好、SSM也好它们存在的意义不是让你写出更长的代码而是让你把业务里的关系理清、把复杂的事情变简单。如果你正在上手类似系统老老实实把业务闭环打通把并发扣库存这种硬骨头啃下来你会发现自己对面向对象和数据库的理解又深了一层。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表