☰
JSP学生疫情信息管理系统:从需求拆解到部署演示全攻略
2026/10/13 7:23:33 网站建设 项目流程

每年一到毕业设计季,Java Web方向里“JSP学生疫情信息管理系统”这类题目几乎年年上榜。我自己当年做的就是这一挂,一套JSP+Servlet+MySQL的经典组合,把学生健康信息上报、辅导员审核查看、管理员统计汇总整个流程完整跑通。这个系统说白了就是一个典型的信息管理系统,只是业务对象从普通的“学生信息”换成了“健康状态信息”,核心逻辑仍然围绕数据的增删改查、权限控制和报表统计展开。对于计算机专业、信息管理专业的学生来说,它覆盖面广、难度适中,既能展示前端页面能力,又能体现后端业务逻辑水平,还非常适合答辩时讲清楚MVC架构。这篇文章我会把这个毕设从需求拆分、数据库设计、核心代码实现到部署演示、避坑心得完整过一遍,给正在纠结选题或卡在编码阶段的同学一份可以直接抄作业的参考。

1. 项目整体拆解:为什么JSP+Servlet依然是毕设的稳妥选择

1.1 技术栈选型解读

拿到这个题目,很多同学第一反应是“都什么年代了还写JSP,Spring Boot不香吗”。我也理解这种想法,但从毕业设计的实际场景出发,JSP+Servlet依然是很多高校软件工程、计算机科学专业教学大纲里的默认组合。原因有几个层面:

第一,课程衔接问题。大部分高校在大二或大三的Java Web课程里就是按JSP、Servlet、JavaBean、JDBC这条线教的,考试、实验、课程设计都围绕这套体系展开。毕业设计如果突然换成Spring Boot、MyBatis、Vue全家桶,等于把前三年学的路线全部推翻重来,光环境搭建和框架理解就要额外占掉两周时间,风险很高。而用JSP+Servlet,在课堂上已经踩过的坑会少很多,代码链路短,调试起来容易定位问题。

第二,答辩评审的偏好。毕设答辩的评审老师通常更关注你是否真正理解了“请求—处理—响应”这条Web开发链路,而不是你有没有堆砌新技术。一个能把Servlet生命周期、Session管理、请求转发与重定向、数据库连接池这些概念讲得清清楚楚的学生,往往比甩出一堆前端框架名词但一问三不知的学生拿分更高。JSP+Servlet恰恰能把这条链路暴露在最表层,坏处是代码显得原始,好处是原理非常直观。

第三,时间成本与稳定性。毕业设计的时间本来就紧张,还有论文、查重、答辩PPT各种事情。老技术栈的参考资料多到读不完,CSDN、博客园、GitHub上一抓一大把,任何一个报错都有人遇到过。相比Spring Boot整合一堆依赖然后被版本冲突折磨,JSP+Servlet的出错点反而更少。

当然,如果你的指导老师明确要求用新框架,或者你自己对Spring Boot已经非常熟,那完全可以升级,但底层的业务设计思路是不变的。这个题目真正考察的重点在“业务流程是否合理”和“系统功能是否完整”,技术选型只是实现方式。

1.2 功能模块的合理划分

一个完整的学生健康信息管理系统,单从字面上看就是把学生的健康状况录入、存储、展示,但落到实际场景,至少要拆出三个角色和四条业务线。

三个角色分别是:普通学生、辅导员(或班级管理员)、系统管理员。

  • 学生的需求:每日填报个人健康信息,包括体温、身体状况、当前所在地、是否接触过异常人员等;查看自己历史填报记录;修改当天填错的信息。
  • 辅导员的需求:查看本班学生的填报情况,包括已填报/未填报名单;查看异常信息并做出标记处理;查看简单的统计报表。
  • 管理员的需求:管理学生账号和班级信息;重置密码、禁用账号;查看全校或全系的统计概览;导出统计报表。

四条业务线则对应:登录认证与权限控制、健康信息填报与修改、信息查询与异常管理、统计报表与数据导出。

这四条业务线并不是孤立存在的。学生填报后数据写入数据库,辅导员和管理员查询的就是这些数据,统计报表也是基于这些数据实时计算。一个典型的场景是:早上8点截止上报,辅导员登录后台查看未上报名单,打电话催一催;管理员则看全校的整体上报率曲线,判断哪一级哪一班填得最不积极。

把这四条业务线理清楚,系统的骨架就出来了。很多同学的毕设做不好,问题不在写代码,而是一开始没有把角色和权限的边界定义清楚,结果做着做着发现页面之间互相乱跳,谁都能看任何数据,逻辑乱成一团。建议动手写代码之前,先画一张简单的角色权限矩阵表,哪怕只用表格列出来,后期开发效率也会高很多。

2. 数据库设计与核心业务建模

2.1 表结构设计与关系说明

数据库设计是我认为整个项目里最值得花时间的地方。很多同学一上来就建一张大表,把学生信息、填报信息、账号密码全部塞进去,后面统计查询时发现根本没法写SQL,只能反复拼字符串,痛苦不堪。一个合理的数据库设计,至少在3NF(第三范式)基础上做适度冗余,既保证数据一致性,又让查询别太绕。

以“JSP学生疫情信息管理系统”为例,我建议至少拆出这几张表:

  • t_admin:管理员表,字段包括管理员ID、登录名、密码、姓名、角色级别、创建时间。
  • t_student:学生表,字段包括学生ID、学号、姓名、性别、班级ID、手机号、邮箱、登录密码、状态(正常/禁用)、创建时间。
  • t_class:班级表,字段包括班级ID、班级名称、所属学院或专业、辅导员ID。
  • t_teacher:辅导员表,字段包括教师ID、工号、姓名、手机号、关联班级ID、登录密码。
  • t_report:健康上报信息表,字段包括上报ID、学生ID、上报日期、体温、健康状况(良好/感冒/其他)、是否接触异常人群、当前所在地、备注、上报时间、更新时间。
  • t_notice:公告表(可选),字段包括公告ID、标题、内容、发布人、发布时间,用于管理员发布健康提示或填报截止提醒。

这几张表的关系非常清晰:一个班级有多个学生,一个辅导员可以带多个班级(也可以只带一个,看学校实际组织架构),一个学生每天可以有多条上报记录(但逻辑上系统应限制每天只能上报一条,否则会出现重复统计的问题,这个我在后面代码部分会专门处理)。

需要特别注意的是t_report这张表的设计。体温字段建议用DECIMAL(4,1)而不是FLOAT,因为体温一般保留一位小数,DECIMAL在Java里对应BigDecimal,避免浮点运算精度问题;上报日期用DATE类型即可,上报时间用TIMESTAMP类型,插入时默认取当前时间。另外,建议给“学生ID+上报日期”加上联合唯一索引,这是防止重复上报最有效的数据库层面手段。

CREATE TABLE t_report ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, report_date DATE NOT NULL, temperature DECIMAL(4,1) NOT NULL, health_status VARCHAR(20) DEFAULT '正常', is_contact VARCHAR(2) DEFAULT '否', location VARCHAR(100), remark VARCHAR(255), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_student_date (student_id, report_date), FOREIGN KEY (student_id) REFERENCES t_student(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

批量导入SQL文件时,我最常遇到的坑是MySQL版本对外键约束顺序敏感,如果先建t_student再建t_report就没问题,反过来就会报“无法创建表”的错误。另外字符集建议直接用utf8mb4而不是utf8,因为utf8在MySQL里其实只支持最多3字节的字符,有些特殊符号会存不进去,虽然JSP页面里一般不涉及,但用utf8mb4能省掉很多潜在乱码烦恼。

2.2 关键SQL与后续扩展字段

数据库建成之后,先别急着写Java代码,我习惯先把几条核心查询SQL写好,确认业务逻辑能跑通再动手。因为如果连SQL都写不出来,Mapper或Dao层基本就是在瞎写。

最核心的两条查询是:

统计每个班级当天的上报率:

SELECT c.class_name, COUNT(DISTINCT s.id) AS total_students, COUNT(DISTINCT r.student_id) AS reported_count, ROUND(COUNT(DISTINCT r.student_id) / COUNT(DISTINCT s.id) * 100, 1) AS report_rate FROM t_class c LEFT JOIN t_student s ON c.id = s.class_id LEFT JOIN t_report r ON s.id = r.student_id AND r.report_date = CURDATE() GROUP BY c.id;

查询某天体温异常的学生名单:

SELECT s.student_no, s.student_name, c.class_name, r.temperature, r.health_status, r.location, r.create_time FROM t_report r JOIN t_student s ON r.student_id = s.id JOIN t_class c ON s.class_id = c.id WHERE r.report_date = '2025-05-10' AND (r.temperature >= 37.3 OR r.health_status != '正常');

我个人建议t_report表里再加一个is_abnormal字段,类型为TINYINT,默认0,当学生体温超过阈值或健康状况异常时,在插入或更新时把is_abnormal置为1。为什么要加这个冗余字段?因为每次统计异常人数时都要写一大堆跨表条件,有了这个字段直接COUNT一下就行,报表页面的响应速度会快很多,代码也更简洁。这是典型的“用空间换时间”的思路,在毕业设计里完全够用。

3. 核心功能实现:从登录到健康上报

3.1 登录与角色权限控制

登录功能是所有Web项目的第一步,但很多学生就是这第一步写得最随意。最常见的问题是:用Get方式传用户名密码、密码明文存数据库、不设置Session有效期、登录后所有角色共用同一个首页。这个系统涉及学生、辅导员、管理员三种角色,权限必须从一开始就分开,不然后面全是补丁。

我的做法是:登录表单提交到LoginServlet,在Servlet里先从数据库查询账号对应的角色类型,然后把角色类型连同用户基本信息一起存进Session。

@WebServlet("/login") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); String role = request.getParameter("role"); UserDao dao = new UserDao(); User user = dao.login(username, password, role); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("currentUser", user); session.setAttribute("role", role); response.sendRedirect("index.jsp"); } else { request.setAttribute("error", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } } }

需要强调一点:登录校验只能防住老老实实走页面的用户,如果你不做权限过滤,访问者直接在浏览器地址栏输入某个Servlet路径就能绕过登录页面。所以必须再写一个Filter或拦截器,对所有受保护的资源做Session判断。

@WebFilter("/*") public class AuthFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; HttpSession session = request.getSession(false); String uri = request.getRequestURI(); if (uri.endsWith("login.jsp") || uri.endsWith("/login") || uri.contains("/static/")) { chain.doFilter(request, response); return; } if (session == null || session.getAttribute("currentUser") == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } String role = (String) session.getAttribute("role"); if (uri.contains("/admin/") && !"admin".equals(role)) { response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } chain.doFilter(request, response); } }

密码存储方面,哪怕只是毕设,也不要明文存数据库。用Java自带的MessageDigest做一次MD5加盐,或者直接用简单的SHA-256,代码量很少,但答辩时老师看到你会考虑密码安全,印象分会高不少。

3.2 健康信息填报与后台处理

健康上报页面是整个系统的核心用户操作入口。学生登录后,选择日期(一般默认当天),填写体温、健康状况、是否接触异常人群、当前所在地和备注,提交到ReportServlet。

这个Servlet的逻辑比LoginServlet复杂,因为有几个边界条件要处理:

第一,填报日期必须校验,不能提交过去日期,也不能提交未来日期太离谱的日期。第二,当天是否已经填报过,如果填过就转入更新逻辑而不是插入。第三,体温范围要校验,低于35或高于45明显属于非法输入。

我在写这段代码时,把“判重”和“插入/更新”完全分开,避免代码纠缠不清。

@WebServlet("/submitReport") public class ReportServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); User user = (User) session.getAttribute("currentUser"); String reportDateStr = request.getParameter("reportDate"); String temperatureStr = request.getParameter("temperature"); String healthStatus = request.getParameter("healthStatus"); String isContact = request.getParameter("isContact"); String location = request.getParameter("location"); String remark = request.getParameter("remark"); ReportDao dao = new ReportDao(); boolean exists = dao.isReported(user.getStudentId(), reportDateStr); if (exists) { dao.updateReport(user.getStudentId(), reportDateStr, temperatureStr, healthStatus, isContact, location, remark); request.setAttribute("message", "今日已上报,信息已更新"); } else { dao.insertReport(user.getStudentId(), reportDateStr, temperatureStr, healthStatus, isContact, location, remark); request.setAttribute("message", "上报成功"); } request.getRequestDispatcher("report_success.jsp").forward(request, response); } }

有一个很容易被忽略的点:JSP页面上用form表单提交时,一定要确保所有表单控件的name属性与Servlet里的getParameter方法参数完全一致。这个错误非常低级但出现频率极高,我见过太多同学因为一个单词拼写不一致,花了半小时在断点调试上,最后发现只是name写错。

3.3 数据统计与异常预警

统计功能是区分“普通课设”和“优秀毕设”的分水岭。最简单的做法是在JSP页面里用Scriptlet写死几个数字,一看就站不住脚。更合理的做法是把统计信息通过Servlet查询后,渲染到JSP页面,配合图表库直观展示。

这里常用的图表库有两种选择:一是前端引入ECharts,Servlet把统计数据输出为JSON格式,页面用Ajax请求后动态渲染;二是后端用JFreeChart生成图片嵌入页面。我更推荐第一种,因为ECharts的交互体验和视觉效果远胜JFreeChart,而且不涉及图片临时文件的生成和清理问题。

我当时的实现路径是:写一个StatisticsServlet,查询当日全校总人数、已上报人数、上报率、异常人数,以及各班级上报率排名,把这些数据放入一个Map,再用Jackson的ObjectMapper转成JSON返回前端。前端页面里直接用fetch请求这个Servlet,拿到数据后喂给ECharts。

fetch('statisticsServlet') .then(response => response.json()) .then(data => { var chart = echarts.init(document.getElementById('chart')); chart.setOption({ title: { text: '各班级上报率' }, xAxis: { data: data.classNames }, yAxis: { type: 'value', max: 100 }, series: [{ type: 'bar', data: data.reportRates }] }); });

ECharts的引入对网络环境有要求,如果你在内网测试无法访问CDN,我建议直接把echarts.min.js文件下载到webapp/js目录下,本地引用,这样最稳。

异常预警方面,简单有效的做法是后台列表页把体温超过37.3度的记录整行标红,辅导员一眼就能看到。更进阶一点,可以在学生填报时,如果体温异常或健康状况不是正常,页面弹出提醒文字“您的状态异常,请及时联系辅导员”,同时在数据库里记录一条预警日志。当年我还在系统里加了一个简单的通知功能,超过30分钟未填报的学生会自动生成待提醒列表,这个功能虽然代码不多,但辅导员使用后觉得非常实用,答辩效果很好。

4. 环境搭建与部署调试记录

4.1 IDEA + Tomcat + MySQL 环境清单

再干净的代码,环境搭不起来也白搭。我强烈建议用一个经过验证的组合,不要盲目追最新版本。

推荐组合:JDK 8、Tomcat 8.5、MySQL 5.7、IDEA 2020或2021版本、Maven或直接手动导入lib目录下的JDBC驱动包。

为什么不用JDK 17或Tomcat 10?因为Tomcat 10及以上的Jakarta EE包名变动很大,很多老教程里的javax.servlet全部要改成jakarta.servlet,对新手来说是个不小的心智负担。而JDK 8和Tomcat 8.5这一套,在网上能找到的教程资料最多,几乎所有报错都能直接搜索到解决方案。用老一点的技术不是落后,是为了让你把精力放在业务逻辑上,而不是卡在环境配置的泥潭里。

部署配置时需要注意几个关键点:

IDEA里配置Tomcat时,要检查“Deployment”选项卡里是否已经把项目的war包或Exploded包添加进来。很多同学点运行时页面404,一问原因,就是Deployment里没加artifact。

数据库连接信息建议统一写在一个config.properties文件里,并通过一个专门的DBHelper类读取:

public class DBHelper { private static String url = "jdbc:mysql://localhost:3306/health_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; private static String user = "root"; private static String password = "123456"; static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws Exception { return DriverManager.getConnection(url, user, password); } }

注意URL里的几个参数含义:useSSL=false表示不启用SSL加密连接,若不写这个参数,新版JDBC驱动默认会尝试SSL握手,如果MySQL端没有配置证书,会抛一堆告警甚至报错;serverTimezone=Asia/Shanghai则是解决驱动与MySQL服务器之间时区不一致问题;characterEncoding=utf8保证中文字符传输正常。这三项是老版本MySQL通过JDBC连接时最常见的三个报错点,提前写好能省掉不少烦心事。

4.2 部署与演示流程

启动了Tomcat之后,第一次访问项目可能会遇到页面样式丢失、图片不加载这类问题。

最常见的原因是JSP页面里用了绝对路径,比如src="/images/banner.png",这里的根路径不是你的工程路径,而是服务器根路径,所以一旦项目名字发生变化图片就全挂。解决方法是JSP页面顶部添加<base href="${pageContext.request.contextPath}/">,之后所有资源引用都写成相对路径,这样就不会受工程名影响。

另一个常见问题是数据库里中文乱码,特别是从前端表单提交的中文数据存储后变成问号。这个问题的排查顺序我建议是:先看MySQL数据库和表的字符集,再看JDBC连接URL里是否配置了characterEncoding=utf8,再看JSP页面顶部是否设置了pageEncoding="UTF-8"。这三层只要有一层断了,中文就保不住。我当年踩过一次后,现在写代码前都先把所有字符集统一成UTF-8,一种一种去排查实在太浪费时间。

演示的时候,最理想的流程是:

  1. 用管理员账号登录,展示统计首页,让老师看到数据图表;
  2. 切换到学生账号,现场填报一条健康信息,注意体温填一个正常范围内的值;
  3. 切换回辅导员账号,刷新列表,展示刚填报的记录,并展示未上报名单;
  4. 再切到管理员页面,打开导出按钮,下载一张Excel报表。

整个过程控制在五分钟以内,且每一步都有明显的数据变化,老师的直观感受就会是“系统是能跑通的,不是静态页面”,比任何口头介绍都有效。

导出Excel是我特别推荐加的一个功能,代码量不多,网上有现成的工具类,比如用Apache POI的HSSFWorkbook生成xls文件。导入POI依赖后,核心代码大概只有二十几行,但演示“管理员一键导出统计表”这个动作非常有说服力,会让整个系统的完整性大幅提升。

5. 毕业设计避坑指南与答辩加分小技巧

5.1 常见问题速查表

我把做这个项目时遇到频率最高的问题整理成了一张表,适合在开发时随时对照排查:

问题现象可能原因解决思路
Tomcat启动后页面404Deployment里没添加Artifact检查IDEA的Deployment配置,重新部署
数据库连接超时或拒绝连接MySQL服务未启动,或端口错误确认3306端口被MySQL占用,服务已启动
启动时报端口被占用Tomcat配置端口已被其他程序占用修改Tomcat的server.xml端口号
页面中文乱码JSP编码、JDBC编码或数据库编码不一致全部统一为UTF-8,并重启验证
刷新页面后重复上报数据表单重复提交未处理用response.sendRedirect()重定向方式做Post-Redirect-Get模式
上传到服务器后images加载失败资源路径写死绝对路径改用相对路径并配合${pageContext.request.contextPath}
学生重复上报时报主键冲突未处理唯一索引或未做业务层判重先查询再插入,或捕获DuplicateKeyException后更新

这里特别展开说明一下重复提交的问题。学生在上报页面点了一次提交,网络卡顿,他又点了一次,如果代码里没有判重,数据库就会出现两条同一日期、同一学生的记录。这个问题的优雅解法就是在业务层先查再插,并结合数据库层的联合唯一索引兜底。即使并发请求同时到达,数据库也可以保证只有一条数据插入成功,另一条会因唯一索引冲突而报错,再捕获异常转成更新操作即可。

5.2 答辩时怎么把这个系统讲出亮点

毕设做得好,还要讲得好。答辩时间有限,老师不会逐行看你代码,但一定会问几个核心问题。我在答辩前把这些问题的答案想得很熟:

第一个高频问题:项目是不是一个人完成,有没有参考开源项目?这个问题不是为难你,是想了解你的工作量和独立思考能力。回答时不要回避使用了哪些资料,但要突出你做的事情。比如你虽然参考了某个通用管理系统的基础代码,但在健康上报的业务逻辑、每日判重策略、预警机制上做过自己的设计和调整,这些原创部分才是重心。

第二个高频问题:系统的权限是如何控制的?这个问题与代码实现直接挂钩,如果你写了AuthFilter,就照着Filter的执行顺序讲,说明哪些页面需要登录才能访问,哪些角色能访问哪些模块。如果能顺带提一句“敏感数据在查询时加上了角色条件的限制”,会显得更专业。

第三个高频问题:数据库为什么要这样设计?这是展示基本功的好机会。你可以从业务角度说明为什么需要t_report每天多条记录和唯一索引的关系,也可以从查询效率角度说明冗余字段is_abnormal的意义,会让老师觉得你是真的思考过数据模型,而不是照抄模板。

第四个高频问题:还有什么可以优化的地方?这个问题几乎一定会问。建议提前准备三个发展方向,比如引入Redis缓存高频统计数据、用Vue或React重构前端页面、增加邮件或短信自动提醒功能。不用长篇大论,一句话点出思路即可。

前面这些都是技术层面的准备,最后还有一个容易忽略的点:答辩前一定要把数据库里的演示数据准备好,而且数据量不要太少。我见过有同学数据库里只有三条测试数据,统计图表光秃秃一片,看着非常没有说服力。建议写一段随机生成数据的小程序或者SQL,造个几百条真实感强的模拟数据,让图表有看头,演示时视觉效果会好很多。

我个人做完这套系统的最大感受是:先跑通再优化,永远不要在功能没完成前沉迷于重构代码。JSP学生健康信息管理系统这个题目看着朴素,但只要你把用户角色权限拆清楚,把数据上报和统计报表这两个核心环节做实,就已经超过大多数同题目的作品了。如果后面还有时间,你甚至可以在不改动后端核心逻辑的前提下,把前端页面换成响应式布局,或者在移动端做一个简单的H5填报入口,整个项目的完整度和实用性会更上一层楼。希望这份拆解能给你省下一些踩坑时间,祝你的毕设一次通过、答辩顺利。

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

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

立即咨询