
刚带完一个做师生健康信息管理系统毕业设计的学生整套东西从搭建框架到最终答辩跑通前后折腾了将近两个月。期间踩了不少坑也总结了一些对新手比较友好的做法。这篇就把这个项目从头到尾拆开聊聊把那些文档里不一定会写的东西一并讲清楚给正在做类似选题的人做个参考。这个项目说白了就是一套典型的前后端分离系统后端用 SpringBoot前端用 Vue数据库用 MySQL核心场景是管理师生的日常健康信息比如每日健康上报、体检记录、异常预警、统计分析这些。它解决的问题很具体——学校或者学院需要一套能替代纸质登记、Excel 汇总的健康信息管理工具。适合计算机相关专业拿来当毕业设计也适合想系统学习前后端分离项目开发流程的初级开发者。1. 项目整体设计与技术选型思路1.1 先搞清楚这套系统到底要管什么动工之前一定要把需求边界划清楚不然写着写着就收不住了。这套师生健康信息管理系统核心角色和业务其实很明确学生角色每日健康上报、查看个人体检记录、查看系统通知。教师角色健康信息查看、异常数据标记、所带班级的统计概览。管理员角色用户管理、健康数据审核、全校/全院数据统计分析、系统配置。功能范围确定之后系统的整体架构就清晰了。前端走 Vue Element Plus 这套组合做管理界面后端用 Spring Boot 提供 RESTful APIMySQL 负责持久化存储。前后端通过 JSON 格式交互用 JWT 做身份认证。这个架构在当前的企业级开发里非常普遍拿来作为毕业设计既有技术说服力又能锻炼完整的项目开发能力。1.2 为什么选这套技术栈而不是别的很多同学会纠结要不要追新比如用若依框架快速生成或者上微服务那套东西。我的建议很直接毕业设计的核心是让评委看到你对主流技术栈的掌握和思考不是越复杂越好。SpringBoot Vue MySQL 这个组合有几个实实在在的好处SpringBoot 简化了 SSM 时代的繁琐配置内嵌 Tomcat打 jar 包就能跑部署门槛低。Vue 的响应式机制和组件化开发和 SpringBoot 的 RESTful API 配合非常自然前后端职责清晰。MySQL 成熟稳定学习资料多出问题也好排查。这套技术栈和实际企业招聘需求直接挂钩写简历上说服力强。如果非要去追微服务、分布式那一套反而容易陷入架构复杂度里核心业务逻辑没写好答辩时被问到底层原理就比较被动。1.3 系统核心模块怎么拆分系统的功能按角色和业务拆分可以分为以下核心模块模块主要功能涉及角色登录认证JWT 登录、角色权限校验所有用户每日健康上报体温、症状、行程信息填写和提交学生健康数据管理体检记录维护、健康档案查询学生、教师异常预警体温异常标记、症状跟踪教师、管理员统计分析健康数据报表、趋势图、导出管理员系统管理用户管理、角色管理、公告管理管理员这个模块划分看起来常规但实际操作中要处理好两个容易忽略的点一个是健康上报的每日一次限制逻辑另一个是统计报表的维度设计。前者关系到学生端体验后者关系到管理员端的数据决策价值。2. 数据库设计是整套系统的地基2.1 核心表结构与字段设计思路数据库设计这块值得多花时间因为后面所有功能的实现都建立在表结构之上。这套系统的核心表我建议按以下思路设计-- 用户表 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL UNIQUE COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT 加密密码, real_name VARCHAR(50) COMMENT 真实姓名, role TINYINT NOT NULL COMMENT 角色1学生 2教师 3管理员, dept_id BIGINT COMMENT 所属部门/班级, status TINYINT DEFAULT 1 COMMENT 状态1正常 0禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;健康上报表的关键设计在于唯一约束这个直接关系到业务逻辑CREATE TABLE health_report ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, report_date DATE NOT NULL, temperature DECIMAL(4,1) COMMENT 体温, is_symptom TINYINT DEFAULT 0 COMMENT 是否有症状0无 1有, symptom_desc VARCHAR(255) COMMENT 症状描述, location VARCHAR(100) COMMENT 当前位置, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_user_date (user_id, report_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT健康上报表;uk_user_date这个联合唯一索引就是从数据库层面保证了一个用户一天只能上报一次这一条如果只靠后端逻辑去判断高并发下容易出现重复数据加了这个约束就万无一失了。2.2 权限相关的数据表怎么建用户与角色这里我没有单独建 role 表而是用sys_user表里的role字段直接区分。这种设计在角色固定且不超过三四个的场景下完全够用还能少写一张表的关联逻辑。如果是那种角色多、权限细到按钮级别的系统才需要引入真正的 RBAC 五张表标准设计。真正需要用心设计的是班级和用户的关系。学生要关联到班级班级要关联到教师这里建议设计成CREATE TABLE dept ( id BIGINT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(100) NOT NULL, parent_id BIGINT DEFAULT 0 COMMENT 父部门ID, leader_user_id BIGINT COMMENT 负责人即班主任, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT班级/部门表;这样学生通过sys_user里的dept_id关联到班级班级通过leader_user_id关联教师层层关系一目了然统计的时候也方便按班级聚合。2.3 建表的几个小提醒第一所有表的字符集统一用utf8mb4别用utf8否则遇到生僻字或者 emoji 表情存不进去就麻烦了。第二每张表都建议保留create_time和update_time字段排查数据问题的时间线全靠它们。第三字段注释一定要写后面写论文的数据字典部分会省非常多的力气。3. 后端核心业务逻辑的落地技巧3.1 登录认证与权限拦截SpringBoot 项目里整合 JWT 做登录认证这套流程虽然已经被写烂了但很多新手还是会踩到拦截器放行路径的坑。配置拦截规则时一定要把登录接口、静态资源、Swagger 文档这些路径放到放行列表里否则前端还没登录就被拦在门外了。Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns(/**) .excludePathPatterns( /api/auth/login, /api/auth/register, /doc.html, /webjars/**, /favicon.ico ); }这里有一个容易被忽视的细节前端 OPTIONS 预检请求。跨域情况下浏览器会先发一个 OPTIONS 请求如果拦截器没有放行 OPTIONS前端就会报 CORS 错误。处理方式是拦截器里直接判断请求方法if (OPTIONS.equals(request.getMethod())) { return true; }3.2 每日上报的防重复检查后端实现每日上报时虽然数据库有唯一索引兜底但业务层还是要有一次主动判断这样能给出友好的业务提示而不是直接抛数据库异常。这个逻辑放在 Service 层public R report(HealthReport report) { LambdaQueryWrapperHealthReport wrapper new LambdaQueryWrapper(); wrapper.eq(HealthReport::getUserId, report.getUserId()) .eq(HealthReport::getReportDate, report.getReportDate()); long count healthReportMapper.selectCount(wrapper); if (count 0) { return R.error(今日已上报请勿重复提交); } healthReportMapper.insert(report); return R.ok(); }3.3 统计报表的聚合查询管理员端最常用的功能就是统计比如按日期查看全校发热人数趋势、按班级对比上报率。这类需求用 MyBatis-Plus 的 LambdaQueryWrapper 不太好写复杂分组查询更高效的做法是直接在 Mapper 层写自定义 SQL用Select注解或者 XML 都行Select(SELECT report_date, COUNT(*) AS total, SUM(CASE WHEN temperature 37.3 THEN 1 ELSE 0 END) AS fever_count FROM health_report WHERE report_date BETWEEN #{startDate} AND #{endDate} GROUP BY report_date ORDER BY report_date) ListMapString, Object getHealthTrend(Param(startDate) String startDate, Param(endDate) String endDate);返回ListMapString, Object是因为聚合查询的结果没有固定的实体类对应用 Map 接收最灵活。前端拿到这个数据后通过 ECharts 直接渲染折线图交互体验很好论文里还能配一张截图作为系统亮点。3.4 统一返回体与全局异常处理项目里一定要定义一个统一的返回体R通常包含code、message、data三个字段。这样做的好处是前端可以统一处理响应状态不用每个接口都单独做错误判断。全局异常处理建议用RestControllerAdvice配合ExceptionHandler把业务异常、参数校验异常、系统异常分开处理。比如参数校验异常就返回 code 400业务异常返回 code 500 加提示信息。这一套东西写好后整个项目的代码整洁度会高一个档次论文的系统设计章节也有内容可以写。4. 前端 Vue 部分的开发要点4.1 项目搭建与请求封装前端这边用 Vue 3 Vite Element Plus Pinia Vue Router 这套组合。Vite 比 Webpack 快得多启动项目几乎秒开开发体验很好。项目创建直接npm create vuelatest health-frontend请求封装这一块是很多新手容易忽略却又特别重要的。直接用 axios 但要封装一个 request 工具类把 baseURL、请求拦截器加 token、响应拦截器统一处理错误状态都配好import axios from axios; const request axios.create({ baseURL: /api, timeout: 10000 }); // 请求拦截器附加 token request.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); // 响应拦截器统一处理错误 request.interceptors.response.use( response { return response.data; }, error { if (error.response error.response.status 401) { router.push(/login); } return Promise.reject(error); } );4.2 动态路由与权限控制学生、教师、管理员看到的菜单和页面不一样这个用动态路由实现最优雅。做法是登录成功后根据返回的角色信息动态添加对应的路由const asyncRoutes { student: [homeRoute, reportRoute, healthRecordRoute], teacher: [homeRoute, classStatsRoute, abnormalRoute], admin: [...allRoutes] }; function generateRoutes(role) { const routes asyncRoutes[role]; routes.forEach(route router.addRoute(route)); }配合 Vue Router 的beforeEach路由守卫做登录校验和角色校验这样即使前端路径被人猜到后端接口也有权限拦截兜底双重保障。这一块实现好之后答辩的时候基本被问不倒因为涉及了前端工程化和权限控制两个核心考点。4.3 健康上报表单的关键处理健康上报表单是学生端最常用的功能用户体验直接影响系统评分。这里有两个细节要处理好第一是日期默认值默认选中今天而且如果今天已经上报过进入页面就要给出提示并禁用提交按钮。这需要进入页面时先调一次查询接口看今天是否有记录。第二是体温的输入限制体温范围合理控制在 35.0 到 42.0 之间超过就给出异常提示。有人可能会说这算什么技术点但实际开发中这种细节非常多累积起来就是用户体验的差异。对于体温异常的情况前端还可以做个视觉提示比如体温超过 37.3 就红色高亮显示一目了然。5. 部署上线的完整实操记录5.1 本地环境准备整个项目跑起来之前先把环境准备好。我建议按这个清单对照检查JDK 1.8 或 11配置好 JAVA_HOME。Node.js 16 以上npm 或者 pnpm 都行。MySQL 5.7 或 8.0。Maven 3.6 以上。5.2 后端打包后端打包用 Maven有个细节很关键打包之前要确保测试类不阻断构建。很多同学在项目里写了单元测试然后打包时测试不通过jar 包就生成不了。可以跳过测试mvn clean package -DskipTests打包完成后target 目录下会生成一个xxx.jar文件这个是后端部署的核心产物。启动命令很简单java -jar health-system.jar --server.port80805.3 前端构建与 Nginx 部署前端开发模式下一艘是 npm run dev但部署上线必须要走构建流程把 Vue 项目编译成纯静态文件npm run build构建完成后会生成dist目录里面的文件就是前端最终部署的内容。推荐用 Nginx 进行托管配置反向代理把/api开头的请求转发到 SpringBoot 服务上server { listen 80; server_name localhost; root /opt/health/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 解决前端路由 history 模式刷新 404 的问题 location / { try_files $uri $uri/ /index.html; } }try_files这一行非常关键Vue Router 使用 history 模式时刷新非首页路径会 404加上这行配置后所有路由都回退到 index.html由前端路由接管。5.4 初始化脚本与项目启动数据库这块用 Navicat 导入项目里自带的health_system.sql就能把全部表和初始数据建好。建议管理员账号的密码通过 MD5 加盐存储日志记录登录行为这些安全细节在答辩时都是加分的点。启动顺序上我建议按数据库 - 后端 - 前端的顺序来进行。后端进程起来之后先用 Postman 测几个接口确认能通再启动 Nginx 服务前端页面这样排查问题比较快。6. 常见问题与排查技巧实录6.1 后端接口单独访问正常前端调用就报跨域这是前后端分离项目里最典型的一个问题。解决办法有两个层面后端加全局跨域配置或者前端用 Nginx 反向代理解决。开发环境下建议后端配置跨域写一个WebConfig类实现WebMvcConfigurer即可生产环境用 Nginx 反向代理前后端同源不存在跨域问题。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }6.2 数据库连接报错 Access denied大概率是账号密码或者权限问题。确认一下 MySQL 里有没有创建对应的用户并授权CREATE USER health% IDENTIFIED BY your_password; GRANT ALL PRIVILEGES ON health_system.* TO health%; FLUSH PRIVILEGES;还有一种情况是 MySQL 8.0 的认证插件跟 JDBC 驱动版本不兼容这时候要么升级 mysql-connector-java 版本要么在连接 URL 后面加参数。6.3 前端页面加载正常但接口 404很可能是 Nginx 代理路径配置不对。检查 proxy_pass 是否写成了带路径的形式比如proxy_pass http://127.0.0.1:8080/api/;这样会导致请求变成/api/api/xxx或者路径重复。建议统一不带末尾/api/后端接口路径本身已经带/api前缀。6.4 打包后启动 jar 包报端口占用先查端口被谁占了netstat -ano | findstr 8080确认是旧进程占用就结束掉对应的 PID或者换个端口启动。这里我一般直接改用--server.port8081省去杀进程的麻烦。6.5 系统上线后时间显示差了 8 小时典型的时区问题。MySQL 连接 URL 里要加上时区参数spring.datasource.urljdbc:mysql://localhost:3306/health_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai6.6 前端菜单正常但退出登录后再登录别的账号菜单没变这是前端常见的状态残留问题。退出登录时需要重置 Pinia 的 store动态添加的路由也要清掉。可以这么处理function resetRouter() { router.getRoutes().forEach(route { if (route.name route.name.startsWith(Role_)) { router.removeRoute(route.name); } }); }7. 照着做就能跑通的完整流程梳理为了方便直接上手我把整个项目从零到跑通的过程压缩成一套可执行的清单第一阶段搭建骨架约 2 天创建后端 SpringBoot 项目配置好依赖和 application.yml。创建前端 Vue 项目引入 Element Plus 和 Pinia。导入数据库脚本确认表结构完整。第二阶段核心功能实现约 3 周登录认证 权限拦截。学生端每日上报功能。教师端健康数据查询与异常标记。管理员端用户管理 统计分析。第三阶段界面完善约 1 周Element Plus 表格、表单、弹窗的组合使用。ECharts 统计图表的嵌入。第四阶段测试与部署约 3 天后端打包前端构建Nginx 配置。全套流程跑测试用例记录 bug 并修复。根据测试结果截图为论文准备素材。第五阶段论文整理约 1 周背景意义、需求分析、系统设计、功能实现、测试五章的结构。画功能结构图、E-R 图、流程图截图放关键功能页面。我自己带学生的过程中发现一个规律很多人不是卡在某个技术难点上而是被时间安排拖垮了。毕业设计最怕的是前期不着急最后一个月疯狂赶工这种情况做出来的东西质量和深度都很难保证。按上面的节奏稳步推进整个过程从技术实现到文档整理都会从容很多。最后说一点关于论文和源码的关系。论文的截图和实现的功能一定要一一对应评委很可能随便点一个界面问你对应的是论文里哪个章节的哪个功能点。系统里我强烈建议保留一段测试记录表把测试用例、预期结果、实际结果列成表格放到论文的测试章节这个细节会让整个论文显得非常扎实。这套系统做完之后前后端分离的开发模式、JWT 认证、权限控制、统计分析这一整套主流技术方案就都亲手玩过一遍了。后续想扩展的话体检报告上传、消息推送、移动端适配都是不错的方向。一个标准的信息管理系统要做到逻辑严谨、细节到位二次开发的空间很大。