ARTICLE DETAIL

资讯详情

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

基于Spring Boot的高校疫情管理系统设计与实现全攻略

基于Spring Boot的高校疫情管理系统设计与实现全攻略 一说起毕业设计“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注意时区设置连接串里加上serverTimezoneAsia/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 idselectDailyReportCount resultTypejava.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里的LocalDateTimeMySQL里用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里有自定义JOINcount查询和page查询的语义可能不一致。解决办法有两种一种是直接手写完整的count SQL用Select或XML另一种是改用PageHelper这个插件它在某些复杂Join场景下处理得更好。我更推荐的办法对于列表展示这种场景不要去连表查询而是直接在health_report表里冗余real_name和student_no字段。查询时直接单表查询再按需到用户表补名称。场景简单性能还快。5.2 请求参数里中文乱码和日期格式问题前端传JSON给后端时如果出现中文乱码十有八九是过滤器编码问题。Spring Boot里直接配置server.servlet.encoding.forcetrue server.servlet.encoding.charsetUTF-8 server.servlet.encoding.enabledtrue日期格式问题也很常见。前端传2024-05-20这个字符串后端LocalDate字段接收不了报400。解决办法是配置一个全局的Jackson自定义模块或者用JsonFormat(pattern yyyy-MM-dd, timezone GMT8)注解。我建议在application.yml里全局配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8注意这样配置后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.activeprod如果前后端分离前端打包后放在src/main/resources/static下执行mvn clean package时会被一并打进jar包。然后一个jar包直接扔到服务器上就能跑不用装Nginx非常省心。服务器是Linux的话注意给上传目录设置写权限不然后端报“系统找不到指定路径”之类的错误多半就是目录权限问题。6. 给打算用这个题目做毕设的同学几句实在话如果你现在刚拿到这个题目还没写过一行代码我建议你把时间花在刀刃上。以下是我反复强调给学生的几条经验。6.1 怎么准备才能顺利通过答辩答辩最怕的是老师问“这个功能怎么实现的”你答不上来。所以我给你一个自检标准项目里每一个模块你都要能说清楚“前端在哪调的接口、后端哪个Controller接收、Service做了什么判断、数据存到了哪张表”。把这四条线理清了答辩基本稳了。功能上不用贪多。把健康打卡、异常管理、数据统计、公告通知这四个核心功能做到完整比做一堆半吊子的功能有用得多。另外一定要录一段演示视频防止现场Demo出意外。很多同学现场打开浏览器数据库没连上、前端调不到接口非常尴尬。技术亮点一定要提前准备。比如连接池用HikariCP、敏感数据脱敏、接口幂等校验、Redis缓存热点数据、事务加上分析与回滚逻辑随便挑两三个讲清楚比介绍一堆SPA框架强得多。6.2 后续可以这样扩展这个题目如果想做得更漂亮有几个性价比很高的扩展方向一是接入企业微信或短信通知学生提交异常后触发消息推送真实场景里这就是标配二是增加数据导出功能用EasyExcel导出每日汇总表管理部门最需要的就是这个三是做一个移动端适配页面或H5版本现在管理系统的现场使用场景基本都在手机上。如果你做完了核心功能还有余力挑其中一个方向做深项目整体层次就完全不一样了。最后我再说一点。做毕业设计的过程其实最锻炼人的是“自己定义需求然后一步步把需求变成代码”的能力。你照着这个思路搭出来的系统不仅是一个能过答辩的作品也会成为你简历上一段拿得出手的实战经历。动手写就完了遇到问题再回头看看这篇文章肯定能帮你少走不少弯路。
返回列表