ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue学生请假管理系统:从状态机设计到权限并发避坑

SpringBoot+Vue学生请假管理系统:从状态机设计到权限并发避坑 简介这份资源是一篇基于SpringBoot的学生请假管理系统毕业论文采用docx格式面向计算机相关专业需要完成毕业设计的学生及Java Web初学者。论文围绕传统请假流程纸质化、审批低效等痛点完整论述了使用JAVA语言、SpringBoot框架、Vue框架与MYSQL数据库构建线上请假平台的方案涵盖个人中心、班级管理、基础数据管理、辅导员管理、公告管理、老师管理、留言管理等模块的设计与实现。压缩包内仅含1个docx文件约3.05MB内容包含中英文摘要、关键词、目录及系统分析等章节结构完整可直接作为论文写作模板或项目开发参考。目前已有181人学习下载适合需要借鉴技术选型、模块划分与论文框架的读者帮助快速理清开发思路并完成毕业设计撰写。1. 学生请假管理系统从纸质审批到 SpringBoot 线上闭环到底省了谁的时间辅导员桌上那摞请假条学生跑三趟盖章、家长打电话确认、期末统计靠翻 Excel——这套流程在很多院系还在跑。基于 SpringBoot 的学生请假管理系统要解决的就是把「填单、审批、销假、归档」这条链路搬到线上让审批留痕、状态可查、统计自动出。它适合两类人一类是正在做毕设、需要一套能跑通的前后端分离项目的学生另一类是想给院系做内部工具、又不想引入太重技术栈的开发者。核心诉求很朴素——学生手机提交、辅导员一键审批、院系领导看汇总数据落库不丢。技术选型上SpringBoot 负责后端接口与权限Vue 负责前端交互MySQL 存业务数据这是当前最常见的组合。下面按「先立住原理、再动手复现、最后讲坑」的顺序拆开讲每一步都落到能抄的配置和代码上。2. 请假业务建模状态机、角色权限和数据库表怎么定2.1 先画状态流转再写代码请假单不是一张静态表它有一条明确的生命周期。常见做法是定义五个状态待提交草稿、待审批、已通过、已驳回、已销假。学生提交后进入待审批辅导员审批后变已通过或已驳回学生返校后点销假状态落到已销假。这条链路如果一开始没定清楚后面加审批层级、加撤回功能就会到处打补丁。我一般会在数据库里用一个status字段存状态码用整数而不是字符串方便索引和比较。状态码约定0 草稿、1 待审批、2 已通过、3 已驳回、4 已销假。同时留一个approve_time和approver_id审批动作发生时写入这样出问题能追溯是谁在什么时候批的。CREATE TABLE leave_application ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL COMMENT 学生ID, reason VARCHAR(255) NOT NULL COMMENT 请假事由, start_time DATETIME NOT NULL COMMENT 开始时间, end_time DATETIME NOT NULL COMMENT 结束时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0草稿 1待审批 2已通过 3已驳回 4已销假, approver_id BIGINT DEFAULT NULL COMMENT 审批人ID, approve_time DATETIME DEFAULT NULL COMMENT 审批时间, approve_remark VARCHAR(255) DEFAULT NULL COMMENT 审批意见, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_student (student_id), INDEX idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段建表语句的关键点有三个。第一status用TINYINT而不是VARCHAR因为状态判断是高频查询整数比较更快索引也更省空间。第二student_id和status都建了索引前者用于「我的请假记录」查询后者用于辅导员按状态筛选待办。第三approve_remark允许为空因为草稿和待审批阶段还没有审批意见强行设 NOT NULL 会导致插入失败。2.2 角色权限三种人看到的东西必须不一样系统里至少有三类角色学生、辅导员、院系管理员。学生只能看自己的单子、只能提交和销假辅导员能看自己管辖班级的待审批单、能审批管理员能看全量数据、能导出统计。权限如果只在前端做隐藏后端接口裸奔学生改个请求就能批自己的假这是毕设里最常见的翻车点。常见做法是用 SpringBoot 的拦截器或 Spring Security 做接口级鉴权。轻量方案是自定义注解加拦截器在需要权限的方法上标RequireRole(counselor)拦截器解析当前登录用户的角色不匹配就返回 403。用户角色存在user表的role字段登录成功后把角色写进 session 或 JWT。Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { String[] value(); }注解定义好之后拦截器里取当前用户角色和方法上标注的角色数组做交集判断。这里有个细节角色判断要放在业务逻辑之前且不能只判断「是不是登录了」必须判断「是不是这个角色」。我见过有人只校验 token 有效性就放行结果学生能调审批接口这就是权限模型没立住。2.3 表结构补齐用户表、班级表和关联关系光有请假单表不够还得有用户表和班级表。用户表存账号、密码哈希、姓名、角色、班级 ID。班级表存班级名和辅导员 ID。学生和班级是多对一辅导员和班级是一对多。这样辅导员查待办时先查自己带的班级再查这些班级学生的待审批单。CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL COMMENT BCrypt哈希, real_name VARCHAR(50) NOT NULL, role VARCHAR(20) NOT NULL COMMENT student/counselor/admin, class_id BIGINT DEFAULT NULL, INDEX idx_role (role) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;密码字段长度给到 100因为 BCrypt 哈希后是 60 位左右留余量。role用字符串存可读性好角色种类不多时性能差异可忽略。class_id允许为空因为管理员不属于任何班级。这几张表定好后面的接口才有地方落数据。3. 用 SpringBoot 搭后端从建项目到请假接口跑通3.1 建项目与依赖选择用 IDEA 新建 SpringBoot 项目或者用 Spring Initializr 勾选依赖。核心依赖四类spring-boot-starter-web提供 REST 接口mybatis-spring-boot-starter做数据库访问mysql-connector-java是驱动spring-boot-starter-validation做参数校验。如果要做登录态再加jjwt或直接用 session。版本上SpringBoot 2.7.x 和 3.x 都能跑但 3.x 要求 JDK 17如果学校机房还是 JDK 8就锁 2.7.x别硬上。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency依赖版本不要东拼西凑MyBatis starter 和 SpringBoot 版本有对应关系2.3.x 的 starter 配 SpringBoot 2.7.x 是稳的。如果启动报NoSuchMethodError八成是版本错配先查 starter 和 boot 的兼容表。3.2 配置文件数据源和 MyBatis 映射application.yml里配数据源和 MyBatis。数据库连接串要带时区否则插入时间会差 8 小时这是血泪经验。spring: datasource: url: jdbc:mysql://localhost:3306/leave_sys?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.leave.entity configuration: map-underscore-to-camel-case: trueserverTimezoneAsia/Shanghai必须加不然DATETIME字段存进去会偏移。map-underscore-to-camel-case打开后数据库的student_id能自动映射到 Java 的studentId省掉大量手动映射。mapper-locations指向 XML 文件目录如果用的是纯注解方式这行可以去掉但复杂查询还是 XML 更清晰。3.3 请假提交接口参数校验与状态初始化提交接口接收学生 ID、事由、起止时间校验通过后插入一条状态为 1待审批的记录。参数校验用Valid加注解别在 service 里写一堆 if。PostMapping(/leave/submit) public Result submit(RequestBody Valid LeaveSubmitDTO dto, HttpServletRequest request) { Long studentId (Long) request.getSession().getAttribute(userId); if (studentId null) { return Result.fail(未登录); } LeaveApplication app new LeaveApplication(); app.setStudentId(studentId); app.setReason(dto.getReason()); app.setStartTime(dto.getStartTime()); app.setEndTime(dto.getEndTime()); app.setStatus(1); // 直接进入待审批 leaveService.save(app); return Result.ok(); }这里学生 ID 从 session 取不从前端传防止学生伪造别人的 ID 提交。status直接设 1草稿态如果不需要可以砍掉减少状态分支。Result是统一返回体包含 code、msg、data 三个字段前端好处理。时间字段用LocalDateTimeJackson 默认能序列化如果前端传的是字符串加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。3.4 审批接口状态流转与并发控制辅导员审批时要把状态从 1 改成 2 或 3同时写入审批人和审批时间。这里有个并发问题两个辅导员同时点审批可能都读到状态 1然后都更新后写的覆盖先写的。解决办法是用乐观锁或条件更新。Update(UPDATE leave_application SET status #{status}, approver_id #{approverId}, approve_time NOW(), approve_remark #{remark} WHERE id #{id} AND status 1) int approve(Param(id) Long id, Param(status) Integer status, Param(approverId) Long approverId, Param(remark) String remark);SQL 里带AND status 1只有当前状态还是待审批时才更新返回影响行数为 0 就说明已经被别人处理过了接口返回「该申请已被处理」。这是最轻量的并发控制不用引入锁框架。审批通过后如果要做消息通知可以在这里发一条站内信或邮件但毕设阶段可以先不做避免依赖外部服务。4. 前端对接与联调Vue 页面怎么调后端接口4.1 登录态保持与请求拦截前端用 Vue 加 axios。登录成功后后端返回 token 或直接靠 session前端把 token 存 localStorage每次请求在 header 里带上。axios 加请求拦截器统一处理。axios.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] token; } return config; });如果后端用的是 session前端要设withCredentials: true且后端要配 CORS 允许携带凭证。token 方案更适合前后端分离部署session 方案在同域下更简单。选哪个看部署方式别两个混用。4.2 学生端提交与列表页学生端两个核心页面提交请假和我的记录。提交页用表单收集事由和时间时间用日期选择器提交前做非空校验。列表页调/leave/my接口后端根据 session 里的学生 ID 查记录按创建时间倒序。async function loadMyLeaves() { const res await axios.get(/api/leave/my); if (res.data.code 200) { this.leaves res.data.data; } else { this.$message.error(res.data.msg); } }列表里要显示状态标签待审批黄色、已通过绿色、已驳回红色学生一眼能看出进度。销假按钮只在状态为已通过时显示点了调/leave/checkin接口后端把状态改成 4。4.3 辅导员端待办列表与审批操作辅导员端调/leave/pending拿自己班级的待审批单每条后面跟「通过」和「驳回」两个按钮。驳回时要弹框让辅导员填意见意见会存进approve_remark。审批完刷新列表该条消失。async function approve(id, status, remark) { const res await axios.post(/api/leave/approve, { id, status, remark }); if (res.data.code 200) { this.$message.success(操作成功); this.loadPending(); } else { this.$message.error(res.data.msg); } }联调时最常见的问题是跨域。后端加CrossOrigin或在配置类里配全局 CORS允许前端域名和常用方法。如果带 token还要允许Authorization头。跨域报错看浏览器控制台别猜。5. 避坑与排查请假系统上线前必须过的五道坎5.1 时间字段差 8 小时现象学生提交的请假开始时间存进数据库后比实际少 8 小时或者前端显示时多 8 小时。原因JDBC 连接串没指定时区MySQL 驱动默认用 UTC。解决连接串加serverTimezoneAsia/Shanghai同时确认 MySQL 服务器时区是08:00。如果还不对检查实体类用的是LocalDateTime还是Date两者序列化行为不同。5.2 审批接口被越权调用现象学生账号能调通审批接口把自己的假批了。原因接口只校验了登录没校验角色。解决在审批接口上加角色拦截判断当前用户role是否为counselor或admin不是就返回 403。别指望前端隐藏按钮接口必须自己扛。5.3 并发审批导致状态覆盖现象两个辅导员同时审批同一条单子一个点通过一个点驳回最后状态是驳回但通过的那次操作也提示成功。原因先读后写没有条件更新。解决UPDATE 语句带AND status 1根据影响行数判断是否成功为 0 就提示「已被处理」。这个坑在毕设答辩时被问到概率很高。5.4 分页查询数据错乱现象待办列表翻到第二页数据和第一页重复或漏掉。原因分页 SQL 没加稳定排序MySQL 返回顺序不固定。解决ORDER BY create_time DESC, id DESC加 id 做次级排序保证顺序唯一。如果用 MyBatis 分页插件确认pageNum和pageSize传参正确别把pageNum从 0 开始算。5.5 前端刷新后登录态丢失现象学生登录后刷新页面又跳回登录页。原因token 存了但没在应用初始化时读取或者路由守卫判断逻辑写错。解决在路由守卫里先读 localStorage 的 token有就放行没有才跳登录。同时后端接口返回 401 时前端拦截器统一跳登录页别让用户卡在空白页。6. 让这套系统经得起答辩统计导出与状态回滚的进阶做法毕设答辩时老师最爱问两个问题数据能不能统计、操作能不能撤销。统计导出用一条 SQL 就能出结果按班级和状态分组计数导出成 Excel 用 EasyExcel 或 POI。状态回滚则是给已通过的单子加一个「撤销审批」入口但只允许管理员操作且要记录回滚日志。SELECT c.class_name, l.status, COUNT(*) AS cnt FROM leave_application l JOIN sys_user u ON l.student_id u.id JOIN class c ON u.class_id c.id GROUP BY c.class_name, l.status;这条 SQL 按班级和状态汇总前端拿去做柱状图或表格。导出时把结果集写进 Excel列名用中文别直接暴露字段名。回滚日志单独建一张leave_operate_log表记录操作人、操作类型、原状态、新状态、时间这样任何状态变更都有据可查。我自己的习惯是任何涉及状态变更的接口都先写日志再改状态日志插入失败就整个事务回滚。这样即使出问题也能从日志里还原操作路径。这套系统不难难的是把边界情况想全——权限、并发、时区、分页每一个都可能在答辩现场被追问。把这几处做扎实比堆一堆花哨页面管用。希望帮到你。本文还有配套的精品资源点击获取
返回列表