又到了毕业设计的季节后台几乎天天有人来问同一个问题基于Spring Boot的XX管理系统到底怎么做问得最多的就是springboot高校师生疫情管理系统这类题目。这类系统看着普通网上模板也满天飞但真到了自己动手、答辩、跑通部署全流程的时候大多数人才发现事情没那么简单。我帮人改过也重写过不少这类项目今天就把整个从选题、建表、写代码、踩坑到答辩的完整思路拆开讲一遍。这篇文章适合正在选毕设题目、或者已经选了类似系统但不知道从哪下手的同学当然如果只是想在Spring Boot方面补一补实际项目的落地经验这里面的内容也值得过一遍。1. 这个题目为什么值得选从选题价值到技术覆盖面的完整盘点1.1 一个看似普通的题目其实正好踩在毕设评分点上很多人看到疫情管理系统这种题目会觉得太普通、不够高大上。但评判一个毕设好坏不是看题目多么前沿而是看业务是否完整、技术是否扎实、逻辑是否能自圆其说。拿这个系统来说它的核心业务场景非常清晰高校里的学生、教师、辅导员、院系管理员每天都要进行健康信息填报、异常情况申报、审批处理、数据统计。这个闭环本身就包含了信息采集、流程审批、数据可视化三个典型的企业开发场景。更关键的是这个题目的数据模型天然适合多角色参与。一个只有管理员和普通用户的系统权限设计做得再复杂也体现不出来。但这个系统里学生要填表、辅导员要审核异常、院系管理员要看统计、校级管理员要管全局每个角色的操作边界都很清晰RBAC权限模型落地起来顺理成章。答辩的时候老师最爱问的问题比如你这个角色权限是怎么控制的一个用户一天提交多次怎么办数据量大了统计报表怎么处理在这个题目里全都能找到真实对应的实现细节而不是牵强附会加上去的。1.2 技术覆盖面从增删改查到缓存中间件一步不落一个合格的Spring Boot毕设项目技术栈上最好能覆盖到这几个层次技术点在这个项目里的落点为什么需要Spring Boot 基础项目骨架、自动装配、配置管理毕设核心要求ORM 框架MyBatis-Plus 实现数据访问简化开发突出业务逻辑权限认证JWT / Sa-TokenSpring Security多角色访问控制缓存中间件Redis 缓存 Token 和热点统计结果体现中间件应用能力定时任务Scheduled 实现每日统计、自动催卡体现系统自动运行意识文件处理EasyExcel 导入导出师生数据管理类系统的刚需部署运维Docker docker-compose答辩演示与交付加分项这一套组合拳打下来既有业务深度又有技术广度。老师随便从哪个层面问你都能接得住。相比之下很多同学交上去的图书管理系统CRUD大杂烩就吃亏得多。1.3 先避开三个选题阶段就埋下的坑第一个坑直接下载网上的现成源码。很多所谓的源码其实是反编译出来的类名、变量名面目全非注释全部丢失想改一个字段就要全局排查半天改着改着心态直接崩掉。自己从零写一个虽然慢一点但代码是你自己的答辩时被问到任何细节都答得上来。第二个坑盲目堆技术。有的同学非要上 Elasticsearch、RocketMQ、微服务结果光是环境配置就花了两周业务还没做明白。毕设首先要完整其次才是高级。技术选型要服务业务不是为了好看而存在。第三个坑数据表设计不用脑子。用户、角色、菜单、打卡记录、审批记录全部堆在一张表里后期每写一个查询都在跟自己过不去。下一节我就详细说数据表到底应该怎么建。2. 项目骨架与核心数据表设计地基不打牢后面全是坑2.1 技术栈定版Spring Boot 2.7.18 JDK 8 是稳妥的组合技术选型上我强烈建议用 Spring Boot 2.7.x特别是 2.7.18。这个版本是 2.x 系列的最终维护版本稳定性和第三方兼容性都经过充分验证。别一上来就追 Spring Boot 3.x。3.x 基于 Spring Framework 6 和 JDK 17很多老牌数据库驱动、第三方 SDK 的兼容性还没完全跟上。学校机房电脑、答辩现场环境、你找的免费的部署服务器大概率还是 JDK 8 的环境用 2.7.18 几乎不存在环境层面的幺蛾子。项目管理工具用 Maven 就好别在这个阶段给自己找 Gradle 的麻烦。骨架直接去 Spring Initializr 生成依赖勾选这几个就够Spring Web、MyBatis-Plus手动引入、MySQL Driver、Redis、Lombok、Validation。如果你决定不用前后端分离而是用 Thymeleaf 做服务端渲染那记得加上 spring-boot-devtools否则改个模板就要重启一次开发效率极其感人。但说句实话现在做管理系统Vue Spring Boot 前后端分离才是主流推荐直接走这条路线。2.2 核心表设计用户、打卡、审批三大部分各有讲究数据库设计是我每次改项目时最先动刀的地方。这个系统我建议分成三组核心表。第一组是用户权限相关。用户表、角色表、用户角色关联表、菜单表、角色菜单关联表。用户表里除账号密码外要留出学院、班级、学号/工号这类业务字段密码字段请一定用 BCrypt 加密存储明文密码在毕业设计里属于会被老师当场质疑的硬伤。第二组是业务数据表。健康打卡记录表、异常信息申报表、审批记录表、公告通知表。打卡记录表要记录用户ID、打卡日期、体温、健康码状态、行程信息、备注、提交时间。异常申报表要记录异常类型、详细描述、涉及的行程地点、当前审批状态。审批记录表记录谁在什么时间审核了什么单子审核意见是什么——这是评审流水的关键。第三组是辅助支持表。比如学院表、班级表用于逐级统计比如操作日志表记录谁在什么时间查了什么数据这在答辩演示系统安全性时会非常加分。下面给一张打卡记录表的建表语句作为参考CREATE TABLE health_check_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL COMMENT 用户ID, check_date DATE NOT NULL COMMENT 打卡日期, temperature DECIMAL(4,2) NULL COMMENT 体温, health_code_status VARCHAR(20) NULL COMMENT 健康码状态, has_abnormal TINYINT NULL DEFAULT 0 COMMENT 是否有异常信息, abnormal_desc VARCHAR(500) NULL COMMENT 异常描述, location VARCHAR(200) NULL COMMENT 当前位置, create_time DATETIME NOT NULL COMMENT 提交时间, update_time DATETIME NULL COMMENT 更新时间, deleted TINYINT NOT NULL DEFAULT 0 COMMENT 逻辑删除标记, UNIQUE KEY uk_user_day (user_id, check_date), KEY idx_check_date (check_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT健康打卡记录表;这个表有两个地方值得细说。一个是uk_user_day唯一索引它保证了一个用户同一天只能有一条打卡记录这是防重复提交的数据库层面硬约束。业务代码做得再小心也架不住用户连点两次提交按钮有了这个索引兜底数据就不会脏。另一个是deleted字段。毕业设计阶段你可能觉得逻辑删除麻烦但一旦涉及统计数据不能丢和操作可追溯物理删除就是给自己挖坑。MyBatis-Plus 的TableLogic注解能让逻辑删除对业务代码几乎零侵入代价很小收益很大。2.3 通用字段与自动填充别把 create_time 这种字段交给手写每个表都有create_time、update_time还可能有deleted。如果你在业务代码里手动 set 这些字段会让代码变得又啰嗦又容易漏。MyBatis-Plus 的字段自动填充功能就是干这个的。实体类上这样标注Data public class HealthCheckRecord { TableId(type IdType.AUTO) private Long id; TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; TableLogic private Integer deleted; }再写一个配置类Configuration public class MyMetaObjectHandler implements MetaObjectHandler { Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, createTime, LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } }之后所有插入、更新操作都不需要再手动管时间字段。Batch 写入时时间戳统一统计数据也干净。3. 角色权限与接口安全答辩中最能加分的部分3.1 RBAC 模型落地五类角色的数据边界这个系统里我建议设计五种角色而不是两种学生只能提交自己的健康打卡、查看自己的记录、发起异常申报。教师功能与学生类似但在统计视图上拥有本班级的只读权限视业务需求而定。辅导员审批本班学生的异常申报查看本班打卡率统计。院系管理员汇总全院数据导出报表管理本院用户。校级管理员全局配置、账号管理、全院系数据查看。为什么分这么细因为谁能看什么数据是管理系统的灵魂。一个学生不应该看到全年级的体温汇总一个辅导员不应该拿到全校的师生名单。数据权限做出来了整个系统的安全设计就有了层次。接口层面可以在 Controller 上用PreAuthorize(hasRole(ADMIN))做粗粒度控制如果需要更细的权限粒度可以给按钮配置权限标识符比如student:checkin:submit、counselor:audit:approve前端拿到用户权限列表后再决定按钮是否渲染。这样就做到了接口级和按钮级双重控制。3.2 认证方案对比Spring Security 还是 Sa-Token这是每个做毕设的人都纠结过的问题。直接给结论方案优点缺点适合人群Spring Security JWT经典、全面、简历上好看配置繁琐学习成本高有充足时间啃文档的人Sa-TokenAPI 简单上手极快内置踢人下线、权限校验不如 Spring Security 名气大想快速跑通项目的人Shiro轻量但较老社区活跃度一般基本不推荐我的建议是如果你的毕设时间还充裕想借这个机会把 Spring Security 的过滤器链、认证流程搞明白那就选它答辩时很有话聊。如果时间已经比较紧张了Sa-Token 是务实的选择它把登录、权限校验、踢人下线这些功能封装得极其顺手几乎不用关心底层细节。无论选哪个都要注意一个问题前后端分离环境下登录接口成功之后返回给前端的是一个 Token前端每次请求在请求头里带上Authorization: Bearer token。后端除了登录、注册等白名单接口其他接口都需要通过认证且 401 时的返回体要统一方便前端统一处理。3.3 密码加密与 XSS 过滤全局过滤器里别只防 SQL 注入密码存储必须用 BCrypt这个前面提过这里再强调一次——Spring Security 自带的BCryptPasswordEncoder用起来很方便不要自己写 MD5 加盐不光不专业还有安全风险。除了 SQL 注入另一个容易忽视的是 XSS 攻击。比如用户在异常申报的备注里写了一段script标签如果不做转义处理这段脚本会在其他管理员查看审批记录时被执行。一个行之有效的做法是写一个全局过滤器对请求参数做统一的 HTML 转义。把替换为lt;把替换为gt;把单引号、双引号做编码处理就足够应付绝大多数场景。有人问上传 PDF 等文件时要怎么防 XSS——文件本身要限制扩展名和大小PDF 的元数据里也可能包含恶意代码这类问题就交给前端预览去做安全处理后端主要职责是拦截非法文件类型、限制文件大小并对文件名做脱敏处理避免路径穿越攻击。3.4 Redis 缓存 Token 与登录态刷新别把所有状态都塞进 JWT 里如果选了 JWT一个经典的坑就是Token 签发之后无法在服务端主动让它失效退出登录只是前端把 Token 丢了真正要踢人下线做不到。解决办法是用 Redis 做一个 Token 白名单。登录成功后把 Token 或 Token 的唯一 ID 存入 Redis并设置与 Token 过期时间一致的 TTL每次请求校验签名并且检查 Redis 里是否存在。退出登录时直接删除 Redis 中的记录立刻生效。而 Sa-Token 本身就内置了这个机制Token 默认存储在 Redis 中天然支持主动失效和踢人下线。这也是我比较推荐它的原因之一。4. 健康打卡与异常上报核心业务链路的完整实现4.1 每日打卡接口幂等设计与唯一索引的双保险打卡接口是最容易被高并发的场景之一——早上 8 点几千名学生同时提交。接口层面要做的事包括参数校验体温必须在合理范围健康信息不能为空使用 Validation 注解搞定。幂等控制同一个用户、同一天只能生成一条数据。代码里先按 user_id 和今天日期查一遍查不到再插入但查了再插存在并发窗口期所以前面那张表里uk_user_day唯一索引就是最终防线。缓存优化如果不想每次都查数据库可以用 Redis 存一个今日已打卡的标记比如checkin:20250411:userId存在就直接提示已打卡不存在才去操作数据库。这个方案可以用在查询频繁的场景下但不能取代数据库唯一索引。一个典型的打卡数据返回结构可以是这样{ code: 200, message: 打卡成功, data: { checkDate: 2025-04-11, temperature: 36.5, status: NORMAL } }4.2 异常申报审核流程用一个状态机管理所有流转异常申报这个功能核心是状态。我建议用整数字段表示状态0 待审核、1 审核通过、2 已驳回而不是字符串。这样数据库存储效率更高后续做统计也好过滤。审核操作的接口要严格校验操作者角色——只有辅导员或管理员能调用审批接口而且只能审批自己管辖范围内的学生数据。这一步除了权限校验还要做数据范围校验否则一个辅导员把其他院系学生的异常单子给批了那就有大问题。完整流程是学生提交申报数据状态为待审核辅导员查看待审核列表点击通过或驳回如果通过系统可以自动生成一条已关注的备忘记录并通知学生如果驳回必须填写驳回理由。注意驳回理由不能为空否则学生不知道原因容易产生二次投诉。4.3 定时统计与自动催卡Scheduled 的正确用法老师往往喜欢看系统是否具备自动化能力。这里有两个定时任务非常值得做每天 23:00 汇总当天打卡统计按学院、班级维度生成统计结果写入缓存或统计表。每天上午 10:00 检查未打卡人员给未打卡的学生发送站内消息提醒并生成未打卡记录给辅导员。Spring Boot 里用Scheduled(cron 0 0 23 * * ?)就能轻松实现。注意一个坑定时任务默认是单线程串行执行的如果你有多个定时任务建议配置一个ThreadPoolTaskScheduler设置线程池大小为 5 左右防止一个任务卡住导致其他任务全部阻塞。定时任务里尽量不要直接操作和返回业务对象建议内部使用 Service 层方法并且要处理异常不能因为一次统计失败就让整个线程池的任务都挂掉。4.4 个人健康信息的展示边界不能全字段直接往外抛这个系统涉及的是师生个人健康信息在做数据返回的时候要格外注意最小化原则涉及个人隐私部分脱敏显示。列表页面不要返回身份证号、手机号完整明文体温虽然不算极其敏感但对于无权限角色要避免批量返回。具体做法写一个 VO 层不直接返回实体类。实体类里的敏感字段用JsonIgnore或者直接在 VO 中剔除Service 层组装 VO 后再返给前端。虽然会多写一些转换代码但答辩时讲隐私保护这一块价值远超多写的那点代码。5. 开发期最影响进度的五个坑与完整排查思路5.1 MyBatis-Plus 自动填充失效没有生效的 createTime 到底哪去了有一次朋友的项目里createTime字段死活没有值排查过程非常典型。先检查实体类注解TableField(fill FieldFill.INSERT)有没有写错字段名是否和数据库列名对得上。大部分人的问题在这一步就解决了。但还有一部分人注解写对了填充依然不生效。这时候要去查META-INF/spring.factories或者Configuration——MetaObjectHandler实现类有没有被扫描到。如果实现类不在主启动类的子包下Spring 不会自动扫描到这个 Bean整个自动填充功能就静默失效了。排查思路总结先确认 Bean 被加载启动日志或 Debug 看上下文再确认注解字段名称正确最后确认调用的strictInsertFill方法参数与实体类字段一致。这三层查完99% 的问题都能定位。5.2 LocalDateTime 返回给前端成了时间戳LocalDateTime 作为接口返回值如果没配序列化前端拿到的是数组或者秒级时间戳非常反直觉。一个常见的全局配置是spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai但要注意date-format对 LocalDateTime 默认不生效它只对java.util.Date生效。LocalDateTime 需要单独配置 JavaTimeModule 或者加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)注解。如果你用了统一响应体不建议在响应体里做序列化处理更干净的做法是在全局 ObjectMapper 上注册JavaTimeModule。同时注意时区问题服务器时区建议在启动参数里显式指定-Duser.timezoneAsia/Shanghai否则在 Docker 容器里会出现日期差 8 小时的诡异情况。5.3 跨域请求出现两次Preflight 请求被拦截前后端分离后浏览器在发 POST、PUT、DELETE 请求或者自定义请求头时会先发一个 OPTIONS 预检请求。如果后端把 OPTIONS 请求拦截了浏览器会直接报跨域错误而且开发工具里能看到同一个接口被请求了两次。排查思路确认过滤器链中是否有东西拦截了所有请求统一跨域配置建议使用 CorsFilter并且要保证它的优先级高于权限过滤器。在 Spring Security 体系下还需要显式放行 OPTIONShttp.cors().and().csrf().disable() .authorizeRequests() .antMatchers(HttpMethod.OPTIONS, /**).permitAll()如果你在 Nginx 里也配了跨域头注意后端不要重复加 Access-Control-Allow-Origin重复的响应头在某些浏览器里会被判定为跨域失败。5.4 Excel 批量导入乱码与内存溢出用 Apache POI 的原始 API 直接解析几万行的 Excel很可能会出现 OOM。推荐用 EasyExcel它基于 SAX 模式解析占用内存少。乱码问题主要发生在读取 CSV 或导入模板集中表现是中文读出来变成乱码。确认文件流使用InputStream而不是用FileReader读取时设置编码Charset.forName(UTF-8)。如果你的部署环境是 Windows导出时注意设置响应头Content-Disposition里的文件名要经过URLEncoder.encode处理否则前端拿到的文件名是乱码。5.5 前后端联调时的字段命名不一致这个坑是团队协作项目的高发地。后端实体类用驼峰healthCodeStatus前端接口文档里写的是health_code_status联调时一个个字段对过去非常熬人。规范化做法数据库字段下划线实体类驼峰接口返回统一驼峰在 MyBatis-Plus 配置map-underscore-to-camel-case: true。接口文档用 Swagger 或 Apifox 生成前端照着文档对接不要两边各写各的。另一个隐藏问题Boolean 字段千万别命名为isAbnormal否则实体类的 getter 会生成getAbnormal()Jackson 解析时默认以为是属性abnormal导致数据对不上。字段名直接叫abnormal不要带is前缀。6. 从本地到部署再到答辩最后一公里的实操清单6.1 Docker 部署的最小可靠配置本地能跑不算本事能用 Docker 一键部署起来才是加分项。下面这个多阶段构建的 Dockerfile 是实践下来比较稳的方案FROM maven:3.8-jdk-8 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests FROM openjdk:8-jre WORKDIR /app COPY --frombuilder /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -Duser.timezoneAsia/Shanghai, -jar, app.jar]多阶段构建的目的是让最终镜像体积尽量小——构建阶段用带 Maven 的大镜像运行阶段只用 JRE 镜像没有多余的编译工具安全性也更好。Cgroup 内存限制、JVM 参数这些都是进阶项但至少要把-Duser.timezoneAsia/Shanghai带上否则容器默认 UTC 时区打卡日期统计会直接错一天。如果还有其他中间件用 docker-compose 编排 MySQL、Redis、应用三个容器最简单。MySQL 和 Redis 挂 volume 做持久化应用容器设置depends_on确保数据库先启动。记得应用里的数据库地址不能用 localhost要写成 compose 里的服务名比如jdbc:mysql://mysql:3306/xxdb这是新人最常见的启动失败原因。6.2 答辩高频问题与回答要点答辩环节老师问得最多的几乎就那几个提前准备一下现场就不会慌。第一个Spring Boot 自动装配原理是什么回答思路SpringBootApplication是组合注解其中EnableAutoConfiguration会通过AutoConfigurationImportSelector读取META-INF/spring.factories里的自动配置类按条件注解如ConditionalOnClass、ConditionalOnMissingBean决定是否加载对应配置。第二个你项目的角色权限是怎么实现的回答思路RBAC 五张表用户-角色-菜单关联接口用注解做权限标识前端按权限列表控制按钮显示JWT 认证 Redis 白名单或 Sa-Token控制登录态。第三个为什么用 MyBatis-Plus 而不直接用 MyBatis回答思路单表 CRUD 自动生成 SQL开发效率高内置逻辑删除、乐观锁、自动填充这些是实际项目中高频使用的能力复杂查询再用 XML 手写 SQL两者分工。第四个数据库查询慢怎么办回答思路先看执行计划是否走索引再考虑缓存热点数据到 Redis列表页要分页不能全量查询统计类数据可定时汇总避免实时 count。6.3 可以现场演示的加分小功能有几个不费太多功夫但演示效果很好的小功能强烈建议加上一是自定义 Banner。Spring Boot 启动时显示一个 ASCII Art 格式的项目名和版本网上有现成的 banner 生成器三分钟搞定。答辩现场启动项目时这个细节会让老师觉得你做事认真。二是操作日志。记录关键接口的访问行为比如谁在什么时间导出了哪一批数据。演示的时候先操作一遍再打开日志页面展示记录安全性的说服力直接拉满。三是全局异常处理。自定义一个RestControllerAdvice把参数校验异常、业务异常、未知异常统一包装成规范结构前后端联调和答辩演示时都更有底气。我在实际帮人改项目的过程里遇到过太多因为基础设计不合理导致后期反复返工的情况。最深的体会是毕设这个东西前期数据模型和权限模型想清楚后面写代码是真的顺前期贪快直接建表写 CRUD后期每加一个功能都在还债。这个系统看着不大但角色、流程、安全、统计、告警这些企业级系统的常见要素它都有做透了以后无论是放进简历还是面试吹水都拿得出手。希望这篇拆解能帮你少走几步弯路动手之前先停两分钟把表和权限理清楚后面的路会好走很多。