资讯动态

基于SpringBoot的高校学生请假管理系统:从需求到部署全解析

发布时间:2026/9/28 14:24:58 来源:尧图企业网站定制
每年来到毕业设计高峰季“请假管理系统”这类题目都会被反复点名。说实话这个选题表面看着像常规CRUD实际做起来非常考验业务流程梳理能力——请假不只是一张表增删改查背后还有多角色审批、状态流转、考勤联动、数据统计这些硬逻辑。拿来做毕业设计功能边界清晰、评分点丰富也确实能写进简历作为完整的项目经历。本篇文章我从头到尾拆一遍这套基于SpringBoot的高校学生请假管理系统从需求怎么拆、表怎么设计、权限怎么做、请假流程的状态机怎么实现到联调和部署阶段最容易踩的坑全部摊开来讲。无论你是计算机专业正在做毕设的学生还是想把审批类系统逻辑摸熟的后端初学者这篇文章应该都能给你省下不少摸索的时间。1. 选题定位与需求全景拆解1.1 为什么“请假管理”是毕业设计的经典选题高校的请假场景有一个非常典型的特点流程规则清晰但线下处理极其低效。学生要找辅导员拿纸质假条、跑办公室签字盖章、课程考勤时还得人工核对已批准名单。不同角色学生、辅导员、院系管理员在这个流程里关注的信息完全不一样所以系统一旦做起来“用户—流程—数据”三条线都能完整呈现。从毕业设计的评分角度来说这个选题特别占便宜需求不需要编造人人都见过真实场景核心功能数量适中既不是只有一个表的“玩具系统”也不像电商平台那样动辄十几个模块做不完技术上该体现的东西——多表关联、状态流转、权限拦截、文件上传、报表导出——它全都有。放到简历里也拿得出手面试官问到“你这个审批流程是怎么设计的”“多角色权限怎么做”都是能讲出实际方案的话题点。1.2 三类核心角色与功能边界划定做系统前第一步不是写代码而是把角色和边界画清楚。这套系统我建议划分三个角色不多不少学生提交请假申请、查看审批进度、销假、查看个人请假记录和考勤记录。辅导员/班主任审批本班学生的请假申请、查看班级考勤和请假统计、驳回并填写原因。院系管理员用户管理、班级管理、全院请假数据统计、导出Excel报表。这里有个容易犯的设计错误把管理员功能做得太多太杂抢占了其他角色的操作入口比如辅导员还需要管理员帮自己重置密码这种离谱需求。记住一个原则——每个角色的功能入口不超过六个。功能越收敛代码结构越清爽演示的时候也越流畅。1.3 核心业务流程梳理这个系统的业务主线就一条学生提交申请 → 辅导员审批通过/驳回 → 通过后关联考勤记录 → 请假结束销假 → 数据进入统计报表围绕主线还有三条支线请假申请可以撤销、审批可以被追回重新处理视设计需要建议不做追回用撤销重新提交代替、超期未销假系统自动标记。每一条线都对应数据库里的一条状态路径。建议把“考勤记录”和“审批记录”独立成表不要塞在请假单的字段里。原因后面讲表设计时会细说这里先记住结论凡是具有“一人多次操作”特征的业务数据都应该拆子表。2. 技术选型与整体架构设计方案2.1 技术栈选型对照及理由这套系统我推荐一套目前生态最成熟、资料最好找的组合直接照搬即可层级选型说明开发语言Java 8生态兼容性最强大部分服务器都支持不要再用11或17给自己找麻烦后端框架Spring Boot 2.7.x2.7依然是毕业设计最稳妥的版本资料多、坑少ORM框架MyBatis Plus 3.5.x单表CRUD零SQL分页和条件构造器写起来效率很高权限认证Spring Security JWT方案成熟后续想扩展登录接口也容易数据库MySQL 8.0免费、通用面试也常问前端方案Vue 3 Element Plus前后端分离项目视觉效果好API设计能力也能体现出来缓存可选Redis用于缓存登录Token或学生基础信息体现技术广度报表导出Apache POI处理Excel导出简单稳定构建工具Maven团队协作场景事实标准不折腾Gradle有同学想用Spring Boot 3.x我的建议是如果教务系统/学校服务器用的JDK版本不确认那就不要冒险。3.x要求JDK17起步而且很多第三方依赖包还停留在javax命名空间下会遇到大量兼容问题。毕设的核心目标是稳定跑通全流程不是追求版本最新。2.2 项目工程结构划分如果是前后端分离后端工程建议按以下结构分包不要一锅粥地放在一个包里com.xxx.leave ├── config # 配置类SecurityConfig、CorsConfig、MybatisPlusConfig ├── controller # 接口层 ├── service # 业务逻辑层 ├── service.impl ├── mapper # MyBatis Plus的Mapper接口 ├── entity # 数据库实体类 ├── dto # 请求参数对象区分于实体防止多余字段注入 ├── vo # 返回结果对象给前端组装好的数据 ├── common # 统一返回结果、异常处理、常量类 ├── utils # 工具类JWT工具、日期工具、Excel导出工具 └── LeaveApplication.java很多毕设代码里entity、dto、vo三个包不分家前端传什么字段就直接往实体上怼这样做在答辩时是个大减分项——评审老师一眼就看出分层意识薄弱。DTO和VO的核心价值在于控制数据边界接收的参数经过校验返回的字段不泄露密码等敏感信息。2.3 Maven依赖清单与版本避坑参考下面这组依赖是完整跑通过项目的组合直接复制可省不少排查依赖冲突的时间parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version5.2.5/version /dependency /dependencies注意jjwt0.9.1依赖jackson-databind如果你的项目里指定了更高版本的Jackson要以高版本为准否则序列化实体时会遇到NoSuchMethodError。这个问题当年困扰了我一晚最后定位到是版本冲突。3. 数据库表设计业务落地的核心根基3.1 核心数据表清单总览请假管理系统的表数量控制在6~8张最合理太少说明业务拆分不够太多则给自己制造不必要的维护负担。我推荐的完整表清单如下表名用途关键说明sys_user用户表三种角色用role字段区分不建多张表sys_class班级表院系、专业、年级信息可放此表t_leave请假单表学生申请落库状态机核心表t_leave_approval审批记录表每一条审批操作留痕t_attendance考勤记录表学生每日考勤状态关联请假单sys_notice通知公告表系统通知与消息提醒可做选配sys_file附件表选配病假证明材料上传后记录路径3.2 请假单表设计详解t_leave表是整个系统的数据心脏字段设计要有意识地兼顾查询效率和数据扩展性CREATE TABLE t_leave ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, leave_no VARCHAR(32) NOT NULL COMMENT 请假单编号业务编号, student_id BIGINT NOT NULL COMMENT 学生用户ID, class_id BIGINT NOT NULL COMMENT 班级ID冗余存储方便统计, leave_type TINYINT NOT NULL COMMENT 请假类型1事假 2病假 3其他, start_time DATETIME NOT NULL COMMENT 开始时间, end_time DATETIME NOT NULL COMMENT 结束时间, reason VARCHAR(500) DEFAULT NULL COMMENT 请假原因, file_url VARCHAR(255) DEFAULT NULL COMMENT 附件路径病假证明等, status VARCHAR(20) NOT NULL DEFAULT PENDING COMMENT 状态PENDING审批中 APPROVED已通过 REJECTED已驳回 CANCELLED已撤销 FINISHED已销假, create_time DATETIME NOT NULL COMMENT 提交时间, update_time DATETIME NOT NULL COMMENT 更新时间, PRIMARY KEY (id), KEY idx_student (student_id), KEY idx_status (status), KEY idx_class_time (class_id, start_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT请假申请表;这里三个设计点答辩时能讲出彩其一class_id冗余存储在请假单中。虽然理论上可以通过student_id关联班级表查询但请假统计是高频操作按班级、按时间聚合冗余字段能省掉一次关联查询。代价是数据冗余产生一致性维护成本但这个场景中是提交时写入、之后不随班级调整而变所以是划算的。其二状态字段用字符串而不是数字。PENDING、APPROVED这种写法在日志排查、代码阅读时极为直观WHERE status APPROVED一眼就能看懂。不要为了省那几字节的存储用0、1、2的数字纯属自我折磨。其三单独设一个业务编号leave_no形式上可以是L20241201001这种规则用日期当天序号生成。虽然主键ID也能标识一条记录但给学生看“请假单编号”要比“ID108”专业得多答辩演示时印象分会差很多。3.3 审批记录表与考勤记录表的设计意图审批记录表t_leave_approval的作用是每一次审批动作都落一条数据而不是只有最新状态。设计如下CREATE TABLE t_leave_approval ( id BIGINT NOT NULL AUTO_INCREMENT, leave_id BIGINT NOT NULL COMMENT 请假单ID, approver_id BIGINT NOT NULL COMMENT 审批人ID, action VARCHAR(20) NOT NULL COMMENT 动作APPROVE通过 REJECT驳回, comment VARCHAR(255) DEFAULT NULL COMMENT 审批意见, create_time DATETIME NOT NULL COMMENT 审批时间, PRIMARY KEY (id), KEY idx_leave (leave_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT请假审批记录表;为什么审批记录必须拆独立表如果只在请假单表里维护一个status字段那么历史流程完全丢失——谁批准的、什么时候批准的、批了什么意见全查不到。这类操作日志天然就是“一对多”结构任何一对多结构都不该用拼接字符串或者JSON字段来存。我在项目里将审批记录和请假单主表分开之后后端实现、前端时间线展示、答辩说明都变得非常顺。考勤记录表t_attendance设计时给每天每个学生存一条记录CREATE TABLE t_attendance ( id BIGINT NOT NULL AUTO_INCREMENT, student_id BIGINT NOT NULL, class_id BIGINT NOT NULL, att_date DATE NOT NULL COMMENT 考勤日期, att_status VARCHAR(20) NOT NULL COMMENT NORMAL正常 LEAVE请假 LATE迟到 ABSENT缺勤, leave_id BIGINT DEFAULT NULL COMMENT 关联请假单ID来源追溯, PRIMARY KEY (id), UNIQUE KEY uk_student_date (student_id, att_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT考勤记录表;核心约束在UNIQUE KEY (student_id, att_date)上保证每个学生一天只一条考勤记录不会因为重复导入产生脏数据。请假审批通过之后通过业务接口把日期范围内记录的status更新为LEAVE同时记下请假单ID作为来源数据就能追溯。3.4 用户表与班级表用户表不建多张表来区分学生/教师而是用role字段统一存储理由很简单现在只有三种角色建三张表会制造大量重复代码如果以后扩展一个“院系秘书”或“教务处长”角色在角色表里加一行就行。核心字段为id、username、passwordBCrypt加密后存储、real_name、roleSTUDENT/TEACHER/ADMIN、class_id学生才有老师可为空、student_no学号、phone、email、status启用/禁用。这里提醒一个关键规范密码字段一定不要明文存库。Spring Security自带的BCryptPasswordEncoder直接用就行存出来的是一段随机盐值加密串。答辩老师对这一点几乎必问规范做法能拿不少印象分。4. 核心功能实现状态机、权限与业务联动4.1 请假审批状态机的设计状态机是这套系统最能体现工程素养的地方。请假单的所有状态流转必须受约束不能出现“已驳回的申请还能被审批通过”这种逻辑灾难。先定义状态全集PENDING待审批学生提交后的初始状态APPROVED已通过辅导员审批同意REJECTED已驳回学生可修改后重新提交CANCELLED已撤销学生主动取消FINISHED已销假请假结束后标记完成合法流转只有五条路径当前状态操作目标状态PENDING辅导员审批通过APPROVEDPENDING辅导员审批驳回REJECTEDPENDING学生撤销CANCELLEDAPPROVED学生/系统销假FINISHEDREJECTED学生重新提交PENDING生成新数据代码层面如何防止并发操作导致状态错乱最省事的方案是在Update SQL里加状态条件也就是MyBatis Plus的updateWrapper.eq(status, PENDING)只允许当前状态为PENDING时执行更新同时检查更新受影响行数如果返回0说明状态已被别人改过了直接抛业务异常提示“该申请已被处理”。这是乐观锁思想的简单应用能应对绝大多数并发场景。4.2 Security认证授权落地后端接口的权限控制用Spring Security JWT核心配置分三块第一块SecurityConfig配置放行规则。登录注册接口放行其余接口全部走认证然后按角色限制路径/api/student/**需要STUDENT角色、/api/teacher/**需要TEACHER角色、/api/admin/**需要ADMIN角色。Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(/api/auth/login, /api/auth/register).permitAll() .antMatchers(/api/student/**).hasRole(STUDENT) .antMatchers(/api/teacher/**).hasRole(TEACHER) .antMatchers(/api/admin/**).hasRole(ADMIN) .anyRequest().authenticated() .and() .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } }第二块JWT校验过滤器。从请求头Authorization: Bearer token中解析token提取用户ID和角色放入SecurityContext。这里最容易被忽略的是过滤器执行顺序——必须在UsernamePasswordAuthenticationFilter之前否则SecurityContext还没来得及写入就被后续权限判断拦截了。第三块接口级别的细粒度校验。URL拦截只能管到“这个接口属于哪个角色”管不到“这个辅导员只能审批自己班的学生”。所以业务方法里要再做一层数据范围校验例如获取当前登录用户的班级ID和请假单中记录的班级ID比对不一致直接抛“无权审批该申请”。这种二维校验角色数据范围是答辩时的高频考点一定得想清楚。4.3 请假通过后自动联动考勤审批通过的瞬间系统需要把请假时间段内的考勤记录更新为“请假”状态。直接写同步逻辑会耦合两套业务推荐用Spring事件机制解耦// 审批通过后发布事件 applicationEventPublisher.publishEvent(new LeaveApprovedEvent(leaveId)); // 监听器中处理考勤联动 EventListener Transactional public void onLeaveApproved(LeaveApprovedEvent event) { Leave leave leaveService.getById(event.getLeaveId()); // 计算请假跨越的每一天批量更新考勤记录 attendanceService.markAsLeave(leave.getStudentId(), leave.getStartTime(), leave.getEndTime(), leave.getId()); }好处在于审批服务不需要关心考勤表怎么改以后新增“给家长发送短信通知”时再加一个监听器即可原有代码一行不动。这就是发布订阅思想在业务落地中的价值。除了考勤联动还可以让事件监听器顺带往通知表写一条“您的请假已通过审批”的消息记录学生登录后就能看到。4.4 报表统计与Excel导出请假统计模块推荐用SQL聚合而不是Java代码里循环累加。比如统计某班级某月每天请假人数public ListMapString, Object countLeaveByDay(Long classId, String month) { // 使用MyBatis Plus的QueryWrapper或者自定义SQL // 核心SQL: SELECT DATE(start_time) as day, COUNT(*) as cnt // FROM t_leave WHERE class_id ? AND status IN (APPROVED,FINISHED) // AND DATE_FORMAT(start_time, %Y-%m) ? GROUP BY DATE(start_time) }导出Excel用POI实现时有一个高频翻车点不要每次都创建Workbook写完整样式导出前先准备好模板文件。直接用代码全量创建样式碰到超过几万行数据时性能下降严重。毕设级别数据量虽小但模板方案的代码会更短更清晰维护也方便。还有一种做法更省事——用EasyExcel替代POI几行代码就能搞定带表头的导出如果你的项目允许引入额外依赖果断用EasyExcel。4.5 销假逻辑细节销假有两种触发方式学生手动销假请假结束日期到了之后学生点击“销假”按钮系统将状态从APPROVED更新为FINISHED。系统自动销假定时任务每天凌晨扫描把end_time 当前时间且状态仍为APPROVED的记录批量更新为FINISHED避免学生漏操作导致考勤联动不完整。定时任务用Spring自带的Scheduled(cron 0 0 2 * * ?)即可注意给定时任务方法加Transactional时要确保方法在事务代理中执行自调用会导致注解失效后面第5节细说。5. 联调部署阶段的高频问题排查实录5.1 依赖与版本类问题问题一引入了mybatis-plus但启动报错Invalid bound statement绝大多数原因是Mapper接口扫描路径没配置对或者Mapper接口和XML文件没有放在对应的目录下。排查思路确认启动类上有MapperScan(com.xxx.leave.mapper)如果用了XML检查application.yml里mybatis-plus.mapper-locations是否指向classpath:/mapper/**/*.xml。问题二Lombok失效实体类getter/setter编译不过Idea里勾选Settings → Build → Compiler → Annotation Processors → Enable annotation processing然后重启工程。很多新手卡在这一步不是依赖问题而是IDE的注解处理器没开。问题三Spring Boot 2.7下引入POI 5.x后启动冲突POI 5.x依赖了commons-collections4如果项目里引入了commons-collections老版本运行时会报NoClassDefFoundError。用mvn dependency:tree查一下依赖冲突把旧版本排除掉即可。5.2 时间处理与数据一致性MySQL时区问题连接串一定要带serverTimezoneAsia/Shanghai否则系统时间和数据库时间相差8小时考勤联动时会错位一整天。至于MySQL 8.0驱动需要用com.mysql.cj.jdbc.Driver而不是旧版com.mysql.jdbc.Driver这一点网上老资料经常踩坑。LocalDateTime序列化格式前端看到的时间是“2024-06-01T10:30:00”这种带T的格式在application.yml里配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8事务自调用问题同类内部方法直接调用时Transactional会失效。比如LeaveServiceImpl里的approve()调用了同类里的updateStatus()后者加了事务注解也不会生效。解法很简单把事务方法放到另一个Service类中通过Spring注入调用或者自己注入代理对象。还有一个常见的脏数据场景审批通过事件监听器内部更新考勤表抛异常结果请假单状态已改成APPROVED。解决办法是让监听器方法加Transactional并且把发布事件的代码和审批更新的代码放在同一个事务方法里事件监听器默认是同步执行的此时异常会回滚整个事务。这也是为什么上面代码里特意写了Transactional注解。5.3 前后端联调与部署跨域问题前后端分离项目Vue运行在8080端口SpringBoot跑在8081直接请求会被浏览器拦截。后端配置CorsFilter即可Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); config.addAllowedMethod(*); config.addAllowedHeader(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }注意当你同时引入了Spring Security时CorsFilter注册顺序要在安全过滤器之前否则跨域预检请求OPTIONS在前置过滤器就被拦截了。方法是在SecurityConfig里显式加http.cors()。文件上传大小限制SpringBoot默认最大单文件1MB上传病假证明经常报错配置改一下即可spring: servlet: multipart: max-file-size: 10MB max-request-size: 10MB部署打包执行mvn clean package -DskipTests跳过测试打包避免测试用例影响构建。如果服务器上只有一个MySQL别把数据库密码直接写在application.yml里用application-prod.yml区分环境配置打包时用--spring.profiles.activeprod指定。虽然毕设不需要生产级安全但养成这个习惯以后工作项目会少挨很多骂。5.4 逻辑Bug类问题速查表现象可能原因解决思路学生能审批别人的请假只做了URL级权限没做数据范围校验在Service层获取当前用户班级ID与请假单班级ID比对驳回后的请假还能被再次审批状态更新语句没带状态条件使用UpdateWrapper增加status PENDING条件考勤记录重复插入缺少唯一索引给(student_id, att_date)加唯一约束撤销后考勤记录未还原撤销事件未联动考勤撤销时监听事件并恢复考勤为NORMAL导出的Excel打开乱码流未设置编码或没加Excel文件头使用EasyExcel或POI时正确设置文件名编码响应头6. 可扩展的方向与个人实操体会做完一期核心系统后如果有余力更值得投入的是以下三个方向第一加入消息通知机制。目前通知表只是简单存储消息下一步可以用WebSocket实时推送给在线用户前端轮询改成长连接整个系统体验会有明显提升。WebSocket和心跳检测机制又是面试常考点一鱼两吃。第二把统计图表化。前端用ECharts画几个图表——班级出勤率趋势、请假类型分布饼图、辅导员审批时长柱状图。视觉冲击力强、演示效果好而且前端模板代码网上现成的很多改改数据源就行。这一步对毕设答辩“演示效果”的加分作用非常明显。第三后端加一层缓存。把班级基本信息、请假类型字典这类读多写少的数据放进Redis或者至少做本地缓存Spring Cache。答到“如何优化查询性能”时就有实际案例可讲不是纸上谈兵。如果让我对做这套系统的同学说一句最掏心窝的建议那就是功能不在多在于把核心流程做完整把代码写干净。我见过太多人堆了七八个模块结果每个模块都是半成品演示到一半逻辑报错答辩现场全程尴尬。反过来把状态机控制严谨、权限校验无遗漏、事务边界清晰、关键接口加参数校验这一套做完哪怕模块数量少一个答辩老师的印象也会好很多。再补充一个实操小技巧开发时把关键状态流转的日志打出来控制台输出“状态由PENDING变为APPROVED”这类信息现场演示时评委看到日志会有极强的真实感比你口头解释半小时管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑