ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue+MySQL疫情管理系统设计与源码全解析:从建库到部署

SpringBoot+Vue+MySQL疫情管理系统设计与源码全解析:从建库到部署 做毕设选系统的时候“SpringBoot Vue MySQL”这三个词基本是绕不开的组合。今天我聊聊一套我正在打包整理的疫情防控管理系统管理平台它面向的是毕设、课设和学习场景后端是 SpringBoot 做接口服务前端是 Vue 做管理页面数据层统一走 MySQL。这套系统的价值不在于“疫情”这个题材本身而在于它把登录鉴权、角色权限、每日上报、异常流转、数据统计这一整条管理系统的链路完整串了起来。不管你是想快速交一个能演示的毕设还是想借着项目把 Java 后端和 Vue 前端的配合方式吃透这套源码都能直接拿来当骨架用。疫情管理系统这类项目在网上一搜一大堆但多数是烂大街的 CRUD 拼凑登录写得随意权限形同虚设统计图表靠写死数据撑场面。我之所以愿意推荐这套是因为它的模块划分、表结构设计和接口风格都相对规范既不会复杂到让新手看不懂又能让有点基础的人在答辩时讲出东西。下面我从整体设计、数据库、后端、前端、部署、常见坑这几个维度完整拆一遍顺便把我在实际运行中踩过的坑也列出来。1. 项目定位为什么它是毕设和课设的“稳妥选型”1.1 技术栈选择的底层逻辑先说说技术栈。SpringBoot 现在是 Java 后端的事实标准它内嵌 Tomcat不需要单独装容器打一个 jar 包就能跑这对于毕设演示和课设验收来说极其友好。Vue 作为前端框架组件化开发让页面逻辑清晰而且它和 SpringBoot 配合时只需要通过 HTTP 接口通信前后端可以分开调试这对一个人完成全栈项目的学生来说非常重要——后端跑起来用 Postman 测接口前端用 mock 数据开发页面两边并行推进最后联调。MySQL 则是关系型数据库里最普及的选择几乎每一台开发机上都能轻松安装Navicat 或 DataGrip 连上就能看数据。这套组合的生态太成熟了遇到任何问题搜索引擎一查就是几千篇解决方案不会让你卡死在环境问题上。1.2 疫情管理系统的业务闭环很多人看到“疫情防控”会犹豫觉得这个题材是不是过时了。其实你换个角度看它本质是一套“信息采集 审批流转 数据可视化”的管理系统跟企业里常见的“员工健康上报系统”“访客登记系统”“差旅报备系统”是高度一致的。疫情防控只是业务场景的壳壳里面的骨架才是你真正学到的东西。这套系统里有一个完整的业务闭环用户登录后每日提交健康打卡信息如果有出行计划或异常情况可以单独上报数据提交后管理员在后台可以审核、查询、统计最终以图表形式呈现在仪表盘上。这个闭环覆盖了“数据产生 → 数据流转 → 数据消费”的完整链路比单纯的增删改查项目高出一个档次。1.3 学习价值的三个层次这套系统对学习者的价值是分层的。第一层是入门者可以只看前端如何调接口、后端如何返回数据把整个数据流跑通第二层是进阶者可以研究 JWT 鉴权、拦截器、权限校验、多表联查这些核心机制看一个正经管理系统是怎么保护接口的第三层是应试者可以把项目作为自己简历上的亮点在面试时讲清楚“你是如何设计权限模型”“如何解决并发提交”“如何做数据统计的”这些深入问题。不管你是哪个层次这套系统都有东西可挖。2. 数据库设计表结构拆解与核心字段说明2.1 用户、角色与权限的表设计先看用户侧。用户表是整个系统的地基设计得不好后面所有功能都会跟着别扭。我这里用的用户表是标准的设计id 主键自增username 唯一索引password 存的是 BCrypt 加密后的密文绝不允许明文入库。然后单独一个 role 字段标识角色比如 0 表示管理员1 表示普通用户2 表示审核员。为什么要用数字而不是直接存字符串因为数字比较效率高而且可以配合枚举类做映射。真正的权限控制我用的是最轻量的方式——后端拦截器校验角色。不需要引入 Spring Security 或 Shiro 这种重量级框架因为系统体量不大角色就两三种用拦截器加注解的方式反而更好理解也更适合毕设答辩时讲清楚。表结构上如果要做更复杂的权限系统可以拆出 user_role、role_permission 等多对多关系表但那套模型面试官很喜欢问实际写起来对新手并不友好。我建议基础版本先做单表角色有余力再扩展。2.2 核心业务表健康打卡、行程上报与异常记录业务表是这套系统的灵魂。健康打卡表一般叫 health_report核心字段包括用户 id、填报日期、体温、是否咳嗽、是否乏力、是否有接触史、健康码状态绿/黄/红、填报时间。这里有一个关键设计用户 ID 和填报日期联合起来要做唯一约束保证同一个人一天只能填报一次后端在写入时也要先查询再插入防重复提交。很多同类型项目没加这个约束演示时连续点两次提交就会出现两条重复数据非常尴尬。行程上报表 travel_record 要记录出发地、目的地、出行日期、交通方式、是否经过重点地区、同行人员等。异常上报表 abnormal_report 则记录发热、咳嗽、核酸检测异常等突发情况状态字段要有一个审批状态待审核、已通过、已驳回。这三张表是系统业务的核心答辩时老师大概率会问“如果一个人连续多天没有打卡怎么办”你的设计里可以加一个统计查询用 SQL 的 GROUP BY 和日期差来判断漏打卡用户。2.3 统计与图表的数据来源设计仪表盘页面不是写死数据的。统计接口一般会做三个维度的查询今日打卡人数、今日异常人数、近 7 天打卡趋势。这些数据都可以通过简单的聚合 SQL 拿到。比如近 7 天趋势就是对 health_report 表按日期分组计数然后用 left join 或子查询补出没有打卡数据的日期补零。MySQL 里可以用一个临时数字表或者递归 CTE 生成日期序列但为了简单我建议直接在 Java 层做日期补齐代码量不大逻辑还清楚。这里列一下推荐的核心表表名用途关键字段sys_user用户表id, username, password, real_name, role, statushealth_report健康打卡表id, user_id, report_date, temperature, health_status, symptomtravel_record行程报备表id, user_id, depart, destination, travel_date, transportabnormal_report异常上报表id, user_id, abnormal_type, description, statusnotice公告通知表id, title, content, create_timeaccess_record出入登记表id, user_id, in_time, out_time, temperature数据库里可以把每一张表都加上 create_time 和 update_time 字段用 MyBatis-Plus 的自动填充功能维护不要每次手动 set 时间代码干净很多。3. 后端 SpringBoot 实现要点认证、拦截与核心接口3.1 依赖管理与项目初始化后端我用的是 SpringBoot 2.7.x MyBatis-Plus MySQL 驱动 JWT 工具 Hutool 工具包。SpringBoot 版本这里要特别提醒一句如果你用的是 SpringBoot 3.x那么对应的 Jakarta 命名空间变了很多旧教程里的 javax.servlet 包会直接报编译错误。对于大多数毕设项目来说2.7.x 是资料最多、最稳的版本别盲目追新。关键的 pom.xml 依赖可以这样配dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependencyMyBatis-Plus 是我强烈建议用的它省掉了大量单表 CRUD 的 XML 和 Mapper 代码。内置的 LambdaQueryWrapper 写条件查询非常顺手分页插件一句话配置就能用这对赶进度的毕设来说能省出大量时间。3.2 JWT 认证的完整流程JWT 认证是这套系统后端最值得讲的部分。先说流程用户登录提交用户名密码后端校验通过后生成一个包含用户 ID 和角色的 token 字符串返给前端前端把 token 存在 localStorage 里之后每次请求在 Header 里带一个Authorization: Bearer token后端拦截器先放行登录接口和静态资源其余接口统一拦截校验 token 的签名和过期时间通过后把用户信息放入 ThreadLocal接口里就能直接取当前登录用户。生成 token 的核心代码大致是这样public String generateToken(Integer userId, String role) { long now System.currentTimeMillis(); return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setIssuedAt(new Date(now)) .setExpiration(new Date(now 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); }校验 token 时要注意一个细节JWT 的.parseClaimsJws(token)方法在 token 过期或签名非法时都会抛异常所以拦截器里要用 try-catch 包住捕获异常后统一返回 401 状态码和“登录状态已失效”的提示。这里如果不做 catch异常会直接抛到全局异常处理器返回给前端的可能是一堆堆栈信息体验很糟糕。3.3 接口设计的规范与关键案例接口路径我统一用/api前缀然后按模块划分/api/auth/login、/api/user/info、/api/health/report、/api/health/records、/api/travel/report、/api/stats/dashboard。返回格式统一用 Result 对象封装code 表示状态码msg 表示消息data 表示数据。前端拿到 code 不是 200 时直接弹出 msg不用每次都判断 HTTP 状态码逻辑统一。健康打卡的 Controller 我建议写成这样只做参数接收和权限判断真正的业务逻辑放到 Service 层PostMapping(/report) public Result report(RequestBody HealthReportDTO dto, RequestAttribute(userId) Integer userId) { if (dto.getTemperature() null || dto.getTemperature() 0) { return Result.error(体温不能为空且必须大于0); } boolean success healthReportService.submit(userId, dto); return success ? Result.success(打卡成功) : Result.error(今日已打卡请勿重复提交); }参数校验这里别用大量 if-else能加NotNull、DecimalMin这类 Bean Validation 注解就加但要知道它的生效场景是配合Valid使用。我在实际项目里还遇到过一个问题前端传的体温是字符串后端对应字段是 BigDecimal类型转换失败时 SpringMVC 会直接抛出 MethodArgumentTypeMismatchException所以一定要写一个全局异常处理器兜底把这些常见异常转换成友好的 JSON 提示。3.4 后端实战避坑清单开发后端时最容易踩的坑有几个。第一个是时间字段的时区问题MySQL 连接串里如果没有serverTimezoneAsia/Shanghai要么报时区错误要么查询出来的时间差 8 个小时我一般直接写在配置里。第二个是 MyBatis-Plus 的updateById默认不会更新 null 字段如果你要清空某个字段必须用UpdateWrapper显式 set这一点记不住很容易出现“明明传了空值数据库却没更新”的诡异现象。第三个是分页查询的 total 字段不准这通常是分页插件没有配置拦截器导致的记得加一个 MybatisPlusInterceptor Bean。application.yml 里 MySQL 的配置我惯用的是spring: datasource: url: jdbc:mysql://localhost:3306/epidemic_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.DriverallowPublicKeyRetrievaltrue这个参数是我调了很久才加上的。MySQL 8.x 默认使用 caching_sha2_password 认证插件在客户端第一次连接时如果没有这个参数会报 “Public Key Retrieval is not allowed” 错误把参数加上立竿见影。4. 前端 Vue 实现要点工程创建、封装与页面拆解4.1 前端工程初始化与目录规划前端我用 Vue 3 Vite Element Plus 的组合。Vue 3 是现在的主流Vite 的启动速度比 Webpack 快一个量级开发体验非常舒服。创建项目用官方脚手架一把梭npm create vuelatest它会生成一个基础工程然后往里加 Element Plus、Axios、Vue Router、Pinia状态管理的轻量替代比 Vuex 更简洁。目录结构上我习惯这样组织src/api放接口定义src/router放路由配置src/store放状态管理src/views放页面组件src/utils放工具函数。这样的好处是接口、路由、页面互相隔离后面要改某个功能时目标非常明确。4.2 Axios 封装与路由守卫Axios 一定要封装。我见过太多项目直接在组件里写axios.get结果接口地址换一下要改十几个文件。封装后的核心逻辑是统一设置 baseURL、请求超时时间、请求拦截器从 localStorage 取 token 拼到 Header、响应拦截器处理 401 跳登录、统一弹出错误消息。核心代码大致就是const instance axios.create({ baseURL: /api, timeout: 10000 }) instance.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) instance.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.msg || 请求失败) return Promise.reject(new Error(res.msg)) } return res.data }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } )路由守卫的作用是控制页面访问权限。未登录用户访问首页或后台页面时直接踢回登录页管理员才能访问的页面比如用户管理、审批管理还要再校验角色。这里有一个容易被忽略的问题路由守卫里判断权限时用的角色信息应该从哪来我建议登录后把用户信息放进 Pinia store页面刷新时通过router.beforeEach里异步调用/api/user/info重新拉取这样就不会出现刷新后角色丢失的问题。4.3 核心页面与组件设计页面部分按功能划分登录页要做得整洁首页仪表盘建议用 ECharts 渲染统计图表健康打卡页就是一张表单加一个提交按钮记录查询页用表格展示历史记录并支持日期筛选管理页则需要一个可编辑的用户列表。仪表盘是答辩时最抓眼球的一页可以放三个统计卡片今日打卡人数、今日异常数、用户总数下面放一个打卡趋势折线图和一个异常类型占比饼图。ECharts 的配置项不复杂但有一个坑图表容器初始化时如果宽度为 0比如在隐藏的 tab 页里初始化图表会显示不出来。解决方法是调用chart.resize()或者在 nextTick 里初始化。Vue 组件通信这里我建议少用过于复杂的方法。父子组件用 props 和 emit跨页面状态用 Pinia尽量不要用 eventBus因为项目大了之后事件来源根本查不清。表格和表单我用 Element Plus 组件反馈快样式也统一。4.4 前后端联调的三个关键经验联调阶段最容易出的问题第一个是跨域。开发环境下我在 Vite 的vite.config.js里配置代理把/api代理到后端端口这样浏览器的请求就不是跨域了。第二个是日期格式。后端返回的 LocalDateTime 默认是一串带 T 的格式前端展示很难看我统一加了 Jackson 配置让后端返回yyyy-MM-dd HH:mm:ss格式。第三个是分页参数的命名对齐我前端传pageNum和pageSize后端就要接收同样名字的参数不要前端传 page、后端接 current约定好了能少很多口舌。5. 本地运行与打包部署从零到能演示5.1 环境准备和数据库初始化本地跑这套系统环境其实很简单JDK 1.8 或 11、MySQL 8.x、Node.js 16 以上这三个装好就差不多了。前两个不用多说Node.js 建议直接用 nvm 管理版本省得以后切换项目时被版本卡住。数据库初始化时先用 Navicat 或命令行创建数据库再导入项目里的sql脚本脚本里建表、插入测试数据一步到位。要注意 MySQL 的字符集一定要选 utf8mb4否则插入 emoji 表情或特殊字符时会报错。5.2 启动顺序和参数修改启动的顺序有讲究。先启动 MySQL确认数据库服务正常再启动后端看控制台有没有报错看到 Spring Boot 的启动成功日志就是稳了最后启动前端npm run dev后浏览器访问localhost:5173。后端端口默认 8080前端 Vite 默认 5173如果你同时跑过别的项目很有可能会撞端口这时候在后端application.yml里改server.port或者在前端代理配置里改后端地址就行。第一次跑起来后先用默认管理员账号登录挨个点一遍菜单功能。这个“冒烟测试”千万别跳过我见过太多人拿着源码跑完登录页就以为成功了等到答辩演示时才发现打卡提交报错、图表加载不出来。5.3 打包部署的两种常用方式答辩或演示阶段本地运行就够了但如果想部署到服务器上给老师一个在线地址工作量也不大。前端执行npm run build会生成dist文件夹里面的静态文件可以直接部署到 Nginx也可以拷贝到后端项目的src/main/resources/static目录跟后端一起打进 jar 包。后一种方式适合小项目但要注意 Vue Router 如果是 history 模式直接访问子路径会 404需要后端加一个路由兜底把所有非接口路径都转发到 index.html如果图省事用 hash 模式就没有这个问题。后端部署是执行mvn clean package -DskipTests生成的可执行 jar 用java -jar就能跑。我建议在服务器上用 systemd 进程守护这样 jar 崩了能自动拉起不用天天 ssh 上去手动重启。6. 典型问题排查与答辩高频考点6.1 运行期常见问题速查表我在多个环境跑这套项目时积累了一些问题整理成一张速查表按现象、原因、解决方式三列给出。现象原因解决方案启动时报端口被占用8080 被其他进程占用改端口或netstat -ano找到进程杀掉数据库连接失败密码错误或时区没配核对密码连接串加serverTimezoneAsia/Shanghai插入中文乱码数据库连接串没配编码加characterEncodingutf8数据库字符集改 utf8mb4前端接口 404代理配置没生效或地址不对检查 vite 代理确认后端在对应端口运行登录成功后刷新就掉线token 没持久化或用户信息未重新拉取检查 localStorage 和 beforeEach 里的用户信息恢复逻辑表格显示时间少 8 小时时区问题连接串改 Asia/Shanghai或使用jackson配置格式化图表空白容器初始化尺寸为 0调用chart.resize()或在nextTick中初始化MyBatis-Plus 分页 total 始终为 0分页插件未注册添加MybatisPlusInterceptorBean6.2 答辩时老师最爱问的几个点答辩环节的判断标准往往不是功能多花哨而是“这个项目是不是你自己做的核心原理你懂不懂”。高频问题有登录认证为什么用 JWT 而不用传统 Session你要能答出 JWT 无状态、适合前后端分离、多端使用方便这几个关键点同时坦诚它的缺陷比如无法主动登出、token 泄露风险这就叫有思考。“如何防止同一个用户重复提交健康打卡”也是一个常见追问。这里你就讲唯一约束加查询校验再补充一个高频场景“用户同时点两次提交怎么办”那就要用到数据库唯一索引兜底后一个插入会因为唯一键冲突报错被全局异常捕获后返回友好提示。这个进阶答案会让老师眼前一亮。还有“统计图表的数据库查询实现”和“权限控制怎么做”也都是常见问题。前者重点讲 SQL 聚合函数和日期补齐思路后者重点讲拦截器加注解加角色判断的三层配合。把这些细节吃透答辩讲话都会硬气很多。6.3 源码二次开发的建议路径如果你想在这套源码的基础上做出自己的东西我建议不要大改而是做“加法”。最轻松的是加一个导出功能把打卡记录导成 Excel这个功能技术含量不高但演示效果极好想上强度可以把报表模块换成数据大屏效果用 ECharts 的大屏模板撑场面再深一点可以用 Redis 做打卡接口的防重复提交或者用 RabbitMQ 做异常上报后的异步通知。每往上走一步都是在你简历上多写一行闪闪发光的技能点。我个人在实际操作中的体会是源码项目切忌拿过来就按 CtrlC 往自己的文档里粘。正确的打开方式是先本地跑通再画一张业务流程图把每张表的来龙去脉理清楚然后选一个模块自己手敲一遍。等你把health_report那张表从建表到接口到页面完整走通一次这个项目才真正属于你答辩时底气也会完全不同。最后再分享一个我调试源码的习惯无论多急都先在 Postman 里把后端接口测通再开前端页面否则你永远分不清报错到底来自哪一端。这套系统的源码我已经整理好了需要的可以直接照着上面的步骤跑有问题留言交流。
返回列表