☰
基于Spring Boot的高校疫情管理系统设计与实现全攻略
2026/9/28 11:59:31 网站建设 项目流程

一说起毕业设计,“springboot高校师生疫情管理系统”这种题目几乎每年都能看到,各高校在选题库里翻来覆去也就是把这些需求换个壳。可你别小看这个题目,它表面上是“一个基于Spring Boot的管理系统”,实际上把Web开发里最常考的那套东西全串起来了:用户认证、角色权限、数据填报、流程审批、统计报表、文件上传,再加上后期部署上线。如果你能把这个题目真正做到能跑、能答辩、能演示,那你对Spring Boot整个生态的理解基本就过关了。

这篇文章我打算从需求拆解、技术选型、表结构设计、核心功能实现、踩坑实录这几个角度,完整还原做一个这类系统的全过程。不管你是在准备开题报告,还是已经进入编码阶段,都可以对照着检查自己的方案。文章里的实现方案不是我凭空编的,都是实际项目中反复用过的套路,你直接照着改就能用。

1. 这个毕业设计题目,到底在考察什么?

很多同学看到题目里有“疫情管理系统”,第一反应是去搜别人做好的成品,或者上来就打开Navicat建表。我劝你先别急,毕设这东西,最重要的不是“把系统做出来”,而是“把为什么这么做讲清楚”。题目里每个词的权重是不一样的。

1.1 从标题里读出的“隐藏需求”

先拆题目:“计算机毕业设计”定义了它的属性,这是教学性质的工程项目,重点在于完整性和可解释性;“springboot”定义了技术栈核心,说明服务端必须以Spring Boot为主体;“高校师生”定义了两个核心角色,而且这两类角色的权限边界天然不同;“疫情管理系统”定义了业务范围,核心是健康信息采集、异常预警、数据统计、消息通知。

这么一拆你会发现,常规增删改查的图书管理、商城系统根本满足不了题目的评分点。老师真正想看到的是:多角色权限控制、核心业务的表单流程、统计可视化、消息触达机制。如果你只做了个用户管理加一张信息表,哪怕界面做得再花哨,答辩时也很难自圆其说。

1.2 角色与业务流程梳理

高校场景下的疫情/健康管理系统,常见的角色就四类:系统管理员、院系管理员/辅导员、教师、学生。不同角色的关注点完全不同,这个差异就是权限设计的核心依据。

学生端最核心的操作是每日健康打卡、行程信息上报、异常情况登记、查看个人健康档案和通知。教师端除了自己要打卡,还要能查看所带班级或学院的学生填报表单,并对异常记录做初步处理。院系管理员负责审核汇总数据,导出表格上报,同时管理本学院的基础数据。系统管理员则负责用户账号、角色权限、公告发布、系统配置等全局功能。

真正的业务闭环是这样的:学生提交健康信息,系统自动校验是否有异常项,比如体温超过阈值、所在位置属于高风险区域、有疑似症状等,命中规则后自动标记异常并通知辅导员;辅导员查看异常详情,联系学生确认情况,填写处置结果;院系管理员按周期汇总数据,生成报表供决策使用。这个流程写清楚了,后面的数据库设计和接口设计就顺理成章了。

2. 技术选型:为什么偏偏是Spring Boot?

题目已经锁定了Spring Boot,但你仍然要在答辩时解释清楚“为什么选它、它解决了什么问题”。不懂原理,光会说“因为大家都在用”,老师追问两句就露馅了。

2.1 Spring Boot的核心价值在于“自动装配”和“约定优于配置”

传统SSM框架(Spring + SpringMVC + MyBatis)最大的痛点就是配置繁琐:要写web.xml、spring.xml、springmvc.xml,要手动配置数据源、事务管理器、视图解析器。Spring Boot把这一切都简化了,它通过自动装配机制,在项目启动时根据引入的依赖自动配置相应的Bean。

给你举个例子,spring-boot-starter-web这个依赖里包含了一个关键配置类ServletWebServerFactoryAutoConfiguration,它检测到classpath下有Tomcat相关类,就会自动创建Tomcat容器。你不需要写一行配置,项目就能跑起来。这就是“约定优于配置”,Spring Boot把常见的后台服务配置都做好了默认值,你只需要在application.yml里覆盖掉个性化部分就够用了。

如果你在答辩时能把“自动装配的原理”讲清楚,比如@SpringBootApplication是由@EnableAutoConfiguration激活的,后者通过AutoConfigurationImportSelector加载META-INF/spring.factories里的配置类,这个水平就不是背题了,是真正理解了框架。

2.2 配套技术栈的组合套路

我再推荐一套久经考验的组合,适合绝大多数毕设项目:

  • 持久层框架:MyBatis-Plus。我强烈建议你用这个,它把单表增删改查完全封装好了,自带分页插件,你只需要专心写多表关联查询就好。这个组合在毕设里非常常见。
  • 数据库:MySQL 8.x,注意时区设置,连接串里加上serverTimezone=Asia/Shanghai。
  • 认证授权:JWT + Spring Security或者JWT + 拦截器。如果你对安全框架不熟,直接用拦截器就行,毕业设计阶段完全够用。
  • 缓存:Redis。用来存JWT令牌、验证码、热点统计数据,注意不一定要用Spring Cache,直接用RedisTemplate也可以。
  • 前端:Vue 3 + Element Plus + Axios,经典前后端分离组合。
  • 接口文档:knife4j或springdoc-openapi,自动生成接口文档,答辩演示时非常加分。
  • 项目构建:Maven,别用Gradle了,打包部署的同学、网上的教程资源,Maven明显更顺手。

2.3 架构设计:前后端分离还是服务端渲染?

这里有一个所有做毕设的人都要做的选择。我见过太多同学,前端代码和服务端代码放在同一个工程里,用Thymeleaf模板渲染页面。其实对于以“信息填报+管理后台”为核心的业务场景,前端后端分离是更合适的方案,理由有三个:

第一,前后端各自独立开发和部署,接口联调思路清晰,代码很容易组织;第二,答辩评审演示时,前端页面可以用Vue的组件化方式把图表、表单做得很精致,视觉上体验好很多;第三,你可以在项目里合理引入@RequestBody交互方式,这本身就是面试高频考点,答辩时更有内容可说。

当然,前后端分离也意味着你要处理跨域问题。开发环境下用Vue脚手架代理转发一下就行,生产环境里你直接把前端build后的静态文件复制到Spring Boot项目的static目录下,交给Spring Boot一起托管,这样既做到了逻辑上的分层,又避免了部署时配置Nginx的麻烦。

3. 数据库设计与核心表结构

数据库是这类系统的命根子。如果表结构设计得稀烂,后面写Mapper代码会痛苦到怀疑人生。我见过不少人一上来就建了三十多张表,其实都是没想清楚。核心就这几张表,把关系理顺即可。

3.1 用户角色表与权限控制

首先是最基础的用户表。我的建议是不要只建一张sys_user表,把所有学生、老师、管理员都塞进去,而是拆开:

-- 用户账号表,只存登录凭证和基础个人信息 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50) NOT NULL, id_card VARCHAR(20), phone VARCHAR(20), department_id BIGINT COMMENT '所属院系', role_id BIGINT COMMENT '角色ID', status TINYINT DEFAULT 1 COMMENT '1正常 0禁用', create_time DATETIME, update_time DATETIME ); -- 角色表 CREATE TABLE sys_role ( id BIGINT PRIMARY KEY AUTO_INCREMENT, role_name VARCHAR(50) NOT NULL, role_code VARCHAR(50) NOT NULL, description VARCHAR(255) ); -- 角色权限关联表 CREATE TABLE sys_role_permission ( id BIGINT PRIMARY KEY AUTO_INCREMENT, role_id BIGINT, permission_id BIGINT );

这张设计会让你用起来特别舒服,因为核心的用户认证只需要去sys_user表里查账号,查到了再去对应角色表找权限。我建议你表结构里可以预留extend_info字段或者单独建一张student_info表,用来扩充学号、年级、班级等属性。很多现实系统会把个人信息冗余到其他表里,方便查询时少做关联。

3.2 健康信息与行程上报表

健康打卡是系统核心中的核心,很多同学设计成“每天一条记录”,那么主键怎么处理?我建议用“学生ID+日期”做唯一索引,这样能保证一个人一天只能提交一次。

CREATE TABLE health_report ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, report_date DATE NOT NULL, temperature DECIMAL(4,2) COMMENT '体温', health_status TINYINT COMMENT '1健康 2感冒 3疑似症状 4确诊', is_contact_risk TINYINT DEFAULT 0 COMMENT '是否接触高风险人员 0否 1是', current_location VARCHAR(200) COMMENT '当前所在位置', location_risk_level TINYINT COMMENT '0低风险 1中风险 2高风险', symptoms VARCHAR(500) COMMENT '症状描述,JSON或多选结果', travel_track TEXT COMMENT '近14天行程轨迹描述', is_abnormal TINYINT DEFAULT 0 COMMENT '是否异常,0正常 1异常', remark VARCHAR(255), create_time DATETIME, update_time DATETIME, UNIQUE KEY uk_user_date (user_id, report_date) );

这个is_abnormal字段非常关键,千万不要在查询时临时算。你可以在后端写一个规则引擎,每次提交表单时自动判断体温是否高于阈值、位置是否高风险、有没有症状等,命中了就把is_abnormal置为1。这样管理端的统计查询就只需要WHERE is_abnormal = 1,性能完全没问题。

行程上报表和这个结构类似,但要额外存出发地、目的地、交通工具、班次号、出发时间、到达时间这些字段,便于后续追溯。

3.3 数据统计查询的字段冗余设计

所谓“报表统计”,如果用多表join来实时算,数据一多就卡顿。我的做法是在查询时需要大量统计的维度字段上做冗余。比如在健康打卡表里冗余department_id、college_name,这样后台在统计时,直接按department_id分组就能出结果,不需要每次都join院系表。

我再建议你加一张report_summary统计汇总表,每天晚上用定时任务把前一天的打卡率、异常数、各院系情况预先算好存进去。管理员打开首页看到的大屏数据,就是从这张表里读的,查询速度会非常快。很多真实系统都是这样落地的,这也是面试时能拿出来讲的优化点。

4. 核心功能的实现与实操要点

前面讲的都是铺路,现在开始真正写代码。我按模块讲一下核心实现思路,特别注意那些容易踩坑的地方。

4.1 基于JWT的登录与权限控制

先说登录接口的整体流程。用户输入用户名密码,后端查询用户,核对密码。密码存储不要用明文,用BCrypt加密,Spring Security里自带BCryptPasswordEncoder。如果密码正确,就用用户的ID和角色信息生成一个JWT令牌,返回给前端。前端把Token存在localStorage里,之后每次请求都在请求头里带上Authorization: Bearer Token。

拦截器是权限控制的落点。我建议你写一个AuthInterceptor,继承HandlerInterceptorAdapter(新版直接实现HandlerInterceptor接口):

@Component public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口和静态资源 if ("/api/auth/login".equals(request.getRequestURI())) { return true; } String token = request.getHeader("Authorization"); if (token == null || token.isEmpty()) { response.setStatus(401); return false; } try { // 解析token并设置当前用户上下文 Long userId = JwtUtil.parseUserId(token.replace("Bearer ", "")); UserContext.setUserId(userId); return true; } catch (Exception e) { response.setStatus(401); return false; } } }

这里有几个细节你注意一下。拦截器没法直接拿到@RequestBody里的内容,所以某些业务上的权限校验,比如学生只能改自己的数据,必须在Service层再校验一次。再者,UserContext用ThreadLocal实现,记住在afterCompletion里清掉,否则线程复用时会串数据。

Spring Boot版本变化比较大,如果你用的是Spring Boot 2.6以上版本,跨域配置的写法有一点要注意。WebMvcConfigurerAdapter已经被废弃了,你要直接实现WebMvcConfigurer接口:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }

注意allowedOriginPatterns("*")和旧的allowedOrigins("*")的区别,如果开启了allowCredentials(true),用*会报错,这是很多人常遇到的问题。

4.2 健康信息填报模块的设计细节

填报接口是个典型的“当天提交后不可重复提交”的业务场景。我在写的时候,使用select count(*)检查是否存在记录,由于有unique key uk_user_date,哪怕并发下出现了重复提交,数据库层也能兜底。同时注意捕获DuplicateKeyException,返回“今天已经提交过”的提示,而不是500报错。

表单提交后,后端要做异常判断。这条规则建议单独写一个方法:

private boolean judgeAbnormal(HealthReportDTO dto) { if (dto.getTemperature() > 37.3) { return true; } if (dto.getLocationRiskLevel() != null && dto.getLocationRiskLevel() >= 1) { return true; } // 其他规则... return false; }

异常判断完成后,如果结果是异常,别忘了发送通知。这里可以使用Spring的事件机制,发布一个AbnormalReportEvent,再写一个@EventListener的方法异步处理通知逻辑。这样做的好处是把业务逻辑和通知逻辑解耦,代码看起来也上档次。

当然,由于我这里的规则比较简单,在真正实现一个毕设时,只要把自己的规则写清楚,加注释说明每个阈值的依据,纪律上就没问题了。

4.3 数据大屏与统计报表

管理首页的大屏展示,推荐用ECharts。前端拿到后端接口返回的统计数据,渲染柱状图和饼图。这类接口不要每次都去查明细表,强烈建议你在Mapper层直接聚合:

<select id="selectDailyReportCount" resultType="java.util.Map"> SELECT report_date AS date, COUNT(*) AS total, SUM(is_abnormal) AS abnormalCount FROM health_report WHERE report_date BETWEEN #{startDate} AND #{endDate} GROUP BY report_date </select>

如果你用了MyBatis-Plus,其实也可以选择selectMaps配合QueryWrapper的groupBy来写,效果是一样的。但对于分组聚合类复杂查询,我还是推荐写XML里的SQL语句,因为老师让你现场修改需求时,你画SQL更灵活。

关于日期处理,要特别注意时区问题。海外/时区影响不大,但对于国内学生,在开发期如果用的是本地Windows,时间大概率是正确的;部署到云服务器后要注意服务器时间是否UTC。我建议所有DateTime字段都用Java里的LocalDateTime,MySQL里用datetime类型,避免使用timestamp因为它在转换中会有时区坑。

4.4 文件上传与公告发布

公告模块一般要支持上传附件和插入图片。Spring Boot里做文件上传很简单,可以用MultipartFile接收文件,存储到本地目录或对象存储。如果你只做一个毕设,考虑到答辩时可能没有外网,建议直接存本地磁盘,并把静态资源映射配置好:

spring: servlet: multipart: max-file-size: 10MB mvc: static-path-pattern: /upload/**

Java代码里:

@Value("${file.upload-dir}") private String uploadDir; public String uploadFile(MultipartFile file) { String originalFilename = file.getOriginalFilename(); String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); String fileName = UUID.randomUUID().toString().replace("-", "") + extension; Path uploadPath = Paths.get(uploadDir); if (!Files.exists(uploadPath)) { Files.createDirectories(uploadPath); } file.transferTo(uploadPath.resolve(fileName).toFile()); return "/upload/" + fileName; }

这个逻辑虽然简单,但有几个点要提醒你:文件名一定不要用原始文件名,会出乱码,而且可能重名;文件存储路径尽量不要在项目内,否则重新打包会把上传的文件覆盖掉;上传文件类型要限制,比如只允许图片、PDF等,防止有人传了个JSP上去——虽然Spring Boot内嵌Tomcat不会直接执行,但不安全。

注意:即使只是毕业设计,也要养成好习惯。文件目录建议写在配置里,不要硬编码到代码中。这样部署到Linux服务器时只需要改配置即可,不用改代码重新打包。

5. 开发过程中的坑与排查实录

这部分是真正值钱的地方。我把我自己开发和带学生过程中遇到的典型问题整理出来,你遇到的时候可以直接照方抓药。

5.1 分页插件与多表联查的经典冲突

我的第一版实现里,用MyBatis-Plus的分页插件查列表,一切正常。后来发现在health_report表要关联sys_user表查学生姓名时,数据总是不对,总数对,但内容重复。

问题在于分页插件是先执行 count 查询,再执行 page 查询。如果你的SQL里有自定义JOIN,count查询和page查询的语义可能不一致。解决办法有两种:一种是直接手写完整的count SQL,用@Select或XML;另一种是改用PageHelper这个插件,它在某些复杂Join场景下处理得更好。

我更推荐的办法:对于列表展示这种场景,不要去连表查询,而是直接在health_report表里冗余real_name和student_no字段。查询时直接单表查询,再按需到用户表补名称。场景简单,性能还快。

5.2 请求参数里中文乱码和日期格式问题

前端传JSON给后端时,如果出现中文乱码,十有八九是过滤器编码问题。Spring Boot里直接配置:

server.servlet.encoding.force=true server.servlet.encoding.charset=UTF-8 server.servlet.encoding.enabled=true

日期格式问题也很常见。前端传2024-05-20这个字符串,后端LocalDate字段接收不了报400。解决办法是配置一个全局的Jackson自定义模块,或者用@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")注解。我建议在application.yml里全局配置:

spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8

注意,这样配置后,LocalDateTime并不会自动生效,你仍然需要引入jackson-datatype-jsr310。不过Spring Boot starter已经自动引入了,你只需要在实体字段上加上@JsonFormat注解或配置全局JavaTimeModule。

5.3 定时任务与自动提醒

每日健康打卡提醒,靠学生自觉是不可能的。你要做一个定时任务,每天早上8点推送提醒。写法很简单:

@Component public class ReportRemindTask { @Scheduled(cron = "0 0 8 * * ?") public void remind() { // 查询未打卡用户,推送通知 } }

注意在启动类或配置类上加上@EnableScheduling。另外默认的@Scheduled是单线程执行的,如果你有多个定时任务,强烈建议配一个TaskScheduler线程池,不然一个任务阻塞,其他任务全部排队:

@Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(5); return scheduler; }

5.4 项目打包与部署的细节

很多同学开发的时候好好的,mvn package之后一运行就报错,最常见的原因是配置文件里写死了本地路径,或者数据库没有随包迁移。我建议你从一开始就用application.yml配合application-prod.yml两套配置,本地开发用dev,打包部署用prod。启动命令:

java -jar system-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod

如果前后端分离,前端打包后放在src/main/resources/static下,执行mvn clean package时会被一并打进jar包。然后一个jar包直接扔到服务器上就能跑,不用装Nginx,非常省心。

服务器是Linux的话,注意给上传目录设置写权限,不然后端报“系统找不到指定路径”之类的错误,多半就是目录权限问题。

6. 给打算用这个题目做毕设的同学几句实在话

如果你现在刚拿到这个题目,还没写过一行代码,我建议你把时间花在刀刃上。以下是我反复强调给学生的几条经验。

6.1 怎么准备才能顺利通过答辩

答辩最怕的是老师问“这个功能怎么实现的”,你答不上来。所以我给你一个自检标准:项目里每一个模块,你都要能说清楚“前端在哪调的接口、后端哪个Controller接收、Service做了什么判断、数据存到了哪张表”。把这四条线理清了,答辩基本稳了。

功能上不用贪多。把健康打卡、异常管理、数据统计、公告通知这四个核心功能做到完整,比做一堆半吊子的功能有用得多。另外一定要录一段演示视频,防止现场Demo出意外。很多同学现场打开浏览器,数据库没连上、前端调不到接口,非常尴尬。

技术亮点一定要提前准备。比如连接池用HikariCP、敏感数据脱敏、接口幂等校验、Redis缓存热点数据、事务加上分析与回滚逻辑,随便挑两三个讲清楚,比介绍一堆SPA框架强得多。

6.2 后续可以这样扩展

这个题目如果想做得更漂亮,有几个性价比很高的扩展方向:

一是接入企业微信或短信通知,学生提交异常后触发消息推送,真实场景里这就是标配;二是增加数据导出功能,用EasyExcel导出每日汇总表,管理部门最需要的就是这个;三是做一个移动端适配页面或H5版本,现在管理系统的现场使用场景基本都在手机上。

如果你做完了核心功能,还有余力,挑其中一个方向做深,项目整体层次就完全不一样了。

最后我再说一点。做毕业设计的过程,其实最锻炼人的是“自己定义需求,然后一步步把需求变成代码”的能力。你照着这个思路搭出来的系统,不仅是一个能过答辩的作品,也会成为你简历上一段拿得出手的实战经历。动手写就完了,遇到问题再回头看看这篇文章,肯定能帮你少走不少弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询