
简介这份资源是面向学校教务场景的校园请假管理信息系统适合管理信息系统方向的学生、教师及开发者参考学习用于理解如何将请假申请、审批流转与数据统计整合为自动化流程替代传统人工处理方式。压缩包共136个文件约3.26MB以39个cs源代码文件为核心配合14个resources与14个resx资源文件、若干config配置、xsd数据集、dll与exe程序集以及jpg、png界面素材构成一套可运行的Windows Forms项目结构便于梳理用户界面、数据库管理、业务逻辑、多级审批、报表统计与安全隐私等模块的实现思路。目前已有1971人学习下载。读者可从中获取需求分析、系统设计、编码实现到测试部署的完整实践案例对照源码理解请假期限、次数限制等业务规则的落地方式并在此基础上进行二次开发与功能优化。1. 校园请假管理信息系统从纸质假条到全流程数字化的落地拆解每到周五下午辅导员办公室里总会出现同一种混乱学生拿着纸质假条排队盖章有人代签有人补假有人请假三天却只填了一天的离校时间。教务处月底统计时发现某个学院缺勤名单和请假记录对不上追查下去才发现是假条在传递过程中丢了。这不是个别学校的问题而是大多数高校在请假管理上长期依赖纸质流程的必然结果。校园请假管理信息系统要解决的核心问题就一个把请假申请、审批、销假、统计四个环节全部搬到线上让每一条记录可追溯、可统计、可预警。这套系统适合谁做适合有基础 Web 开发能力、想拿一个真实业务场景练手的开发者也适合高校信息化部门里需要快速搭建轻量级管理工具的工程师。它不追求大而全追求的是流程闭环和数据准确。2. 需求拆解与角色建模谁在请假谁在审批谁在看报表2.1 四类角色与三条核心流程校园请假管理信息系统的角色模型比一般 OA 系统简单但比想象中多一层。常见做法是划分四类角色学生、辅导员、院系管理员、系统管理员。学生发起请假申请辅导员负责审批院系管理员查看本院系整体请假数据并处理异常系统管理员维护用户和基础数据。三条核心流程分别是请假申请流程、审批流转流程、销假与统计流程。请假申请流程里学生需要填写请假类型事假、病假、公假、起止时间、请假事由、离校去向、紧急联系人。审批流转流程的关键在于审批链的确定——一天以内的请假由辅导员审批即可三天以上需要院系管理员二次审批七天以上可能还需要学生处备案。销假与统计流程则是很多系统容易忽略的环节学生返校后需要主动销假系统自动计算实际请假时长并与申请时长做比对异常情况推送给辅导员。注意审批链的层级不要设计得太深。我见过一个系统设计了五级审批结果一个病假申请走了两天还没批下来学生直接放弃使用。三级审批链在大多数高校场景下已经够用。2.2 数据表设计五张核心表撑起整个系统数据库设计决定了后续开发的顺畅程度。以下是我一般会用的最小表结构基于 MySQL 8.0-- 用户表存储所有角色的基础信息 CREATE TABLE user ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 学号/工号, real_name VARCHAR(50) NOT NULL, role TINYINT NOT NULL COMMENT 1学生 2辅导员 3院系管理员 4系统管理员, dept_id INT NOT NULL COMMENT 院系ID, class_id INT DEFAULT NULL COMMENT 班级ID学生必填, phone VARCHAR(20) DEFAULT NULL, password_hash VARCHAR(128) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 请假申请表核心业务表 CREATE TABLE leave_application ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, student_id BIGINT UNSIGNED NOT NULL, leave_type TINYINT NOT NULL COMMENT 1事假 2病假 3公假, start_time DATETIME NOT NULL, end_time DATETIME NOT NULL, reason VARCHAR(500) NOT NULL, destination VARCHAR(200) DEFAULT NULL COMMENT 离校去向, emergency_contact VARCHAR(100) DEFAULT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待审批 1审批中 2已通过 3已驳回 4已销假, current_approver_id BIGINT UNSIGNED DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_student_status (student_id, status), KEY idx_approver (current_approver_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 审批记录表每次审批动作留痕 CREATE TABLE approval_record ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, application_id BIGINT UNSIGNED NOT NULL, approver_id BIGINT UNSIGNED NOT NULL, action TINYINT NOT NULL COMMENT 1通过 2驳回 3转交, comment VARCHAR(300) DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_application (application_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 销假记录表 CREATE TABLE leave_checkin ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, application_id BIGINT UNSIGNED NOT NULL, actual_return_time DATETIME NOT NULL, remark VARCHAR(300) DEFAULT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_application (application_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 审批链配置表按请假时长决定审批层级 CREATE TABLE approval_chain_config ( id INT NOT NULL AUTO_INCREMENT, min_days DECIMAL(4,1) NOT NULL COMMENT 最短请假天数, max_days DECIMAL(4,1) NOT NULL COMMENT 最长请假天数, approval_levels VARCHAR(100) NOT NULL COMMENT 审批角色链如 2,3, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这五张表的设计逻辑user表用role字段区分角色避免多张角色表带来的关联复杂度。leave_application表的status字段驱动整个流程状态机current_approver_id指向当前待处理人这样查询待办时只需要一个条件。approval_record表记录每一次审批动作方便追溯和审计。leave_checkin表用唯一索引保证一条申请只能销假一次。approval_chain_config表把审批规则从代码里抽出来后续调整审批层级不需要改代码。参数说明leave_type用 TINYINT 而不是 ENUM是为了后续扩展请假类型时不用改表结构。status的五个状态覆盖了从提交到销假的完整生命周期其中1审批中表示多级审批的中间态。approval_levels字段存的是角色 ID 的逗号分隔字符串比如2,3表示先辅导员审批再院系管理员审批。2.3 审批链的动态计算逻辑审批链不能写死在代码里。我一般会写一个独立的服务方法根据请假天数和配置表动态生成审批人列表# Python 示例根据请假天数计算审批链 from datetime import datetime def calculate_approval_chain(start_time, end_time, student_dept_id, db): 返回审批人ID列表按审批顺序排列 duration_days (end_time - start_time).total_seconds() / 86400 # 查询匹配的审批链配置 config db.query( SELECT approval_levels FROM approval_chain_config WHERE min_days %s AND max_days %s, (duration_days, duration_days) ) if not config: raise ValueError(未找到匹配的审批链配置) role_chain [int(r) for r in config[approval_levels].split(,)] approvers [] for role in role_chain: if role 2: # 辅导员按学生班级查找 approver db.query( SELECT id FROM user WHERE role2 AND class_id( SELECT class_id FROM user WHERE id%s), (student_id,) ) elif role 3: # 院系管理员按院系查找 approver db.query( SELECT id FROM user WHERE role3 AND dept_id%s, (student_dept_id,) ) if approver: approvers.append(approver[id]) return approvers这段代码的关键点请假天数用秒数差除以 86400 计算保留小数以便匹配配置表中的DECIMAL(4,1)字段。审批人查找时辅导员按班级匹配院系管理员按院系匹配这样同一个院系有多个管理员时也能正确路由。如果某个角色找不到对应审批人应该抛出异常而不是静默跳过否则申请会卡在流程中间。3. 技术选型与最小可运行版本搭建3.1 后端框架选型为什么我推荐 Spring Boot MyBatis-Plus校园请假管理信息系统的后端选型常见方案有三种Spring Boot MyBatis-Plus、Django DRF、Node.js Express。我一般会推荐 Spring Boot 方案原因有三个。第一高校信息化环境里 Java 生态最成熟后续对接教务系统、统一身份认证时Java 的 SDK 和文档最全。第二MyBatis-Plus 的代码生成器能根据表结构自动生成 CRUD 代码五张表的增删改查半小时就能搞定。第三Spring Security 做角色权限控制是现成的不需要自己造轮子。如果你更熟悉 PythonDjango 方案也能用但要注意 Django 的 ORM 在复杂查询比如按审批链过滤待办时不如 MyBatis 灵活。Node.js 方案适合快速原型但高校场景下长期维护的案例不多。3.2 用 Spring Boot 初始化项目并跑通第一个接口以下步骤基于 Spring Boot 3.2 和 MyBatis-Plus 3.5.5JDK 17# 用 Spring Initializr 生成项目骨架 curl https://start.spring.io/starter.zip \ -d typemaven-project \ -d languagejava \ -d bootVersion3.2.0 \ -d groupIdcom.campus \ -d artifactIdleave-system \ -d nameleave-system \ -d packageNamecom.campus.leave \ -d dependenciesweb,mysql,validation,lombok \ -o leave-system.zip unzip leave-system.zip -d leave-system cd leave-system然后在pom.xml里手动加入 MyBatis-Plus 依赖dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version3.5.5/version /dependency配置application.ymlspring: datasource: url: jdbc:mysql://localhost:3306/campus_leave?useUnicodetruecharacterEncodingutf8mb4 username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: id-type: auto logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0参数说明map-underscore-to-camel-case开启后数据库的real_name会自动映射到 Java 的realName省去手动写 ResultMap。logic-delete-field配置逻辑删除请假申请被删除时不会物理删除方便审计。3.3 请假申请接口的实现与参数校验核心接口是学生提交请假申请。以下是一个完整的 Controller 方法RestController RequestMapping(/api/leave) RequiredArgsConstructor public class LeaveApplicationController { private final LeaveApplicationService leaveService; PostMapping(/apply) public ResultLong apply(RequestBody Valid LeaveApplyRequest request, RequestAttribute Long userId) { // 校验时间合法性 if (request.getStartTime().isAfter(request.getEndTime())) { return Result.error(开始时间不能晚于结束时间); } if (request.getStartTime().isBefore(LocalDateTime.now())) { return Result.error(不能申请过去的请假); } // 校验请假天数上限 long days ChronoUnit.DAYS.between( request.getStartTime(), request.getEndTime()); if (days 30) { return Result.error(单次请假不能超过30天); } Long applicationId leaveService.createApplication(userId, request); return Result.success(applicationId); } }逻辑说明Valid触发 JSR-303 校验LeaveApplyRequest里用NotNull、Size注解约束字段。时间校验放在 Controller 层而不是 Service 层是为了快速失败避免无效数据进入业务逻辑。请假天数上限 30 天是硬编码的实际项目中应该从配置表读取。参数说明startTime和endTime用LocalDateTime类型前端传 ISO 8601 格式字符串。userId从RequestAttribute获取这是拦截器解析 JWT 后注入的不要从请求体里取否则会有越权风险。4. 审批流转与状态机把「谁批、什么时候批、批完干嘛」说清楚4.1 状态机设计五个状态与合法迁移路径请假申请的状态流转是整个系统最容易出 bug 的地方。我一般会用状态机模式来约束流转路径而不是在代码里到处写 if-else。五个状态的合法迁移如下当前状态允许操作目标状态操作角色0待审批审批通过1审批中或2已通过辅导员/院系管理员0待审批驳回3已驳回辅导员/院系管理员1审批中审批通过2已通过院系管理员1审批中驳回3已驳回院系管理员2已通过销假4已销假学生2已通过超时未销假2已通过标记异常系统定时任务关键规则状态只能按表里的路径迁移不允许跳转。比如不能从0待审批直接到4已销假。每次状态变更都要写一条approval_record保证审计线索完整。4.2 审批接口的实现与并发控制审批接口要处理两个问题权限校验和并发审批。权限校验确保只有当前审批人能操作并发控制防止两个管理员同时审批同一条申请。Transactional(rollbackFor Exception.class) public ResultVoid approve(Long applicationId, Long approverId, Integer action, String comment) { // 悲观锁锁定申请记录防止并发审批 LeaveApplication app applicationMapper.selectByIdForUpdate(applicationId); if (app null) { return Result.error(申请不存在); } if (!app.getCurrentApproverId().equals(approverId)) { return Result.error(您不是当前审批人); } if (app.getStatus() ! 0 app.getStatus() ! 1) { return Result.error(当前状态不允许审批); } if (action 1) { // 通过 // 查询是否还有下一级审批 ListLong chain getApprovalChain(app); int currentIndex chain.indexOf(approverId); if (currentIndex chain.size() - 1) { app.setStatus(1); // 审批中 app.setCurrentApproverId(chain.get(currentIndex 1)); } else { app.setStatus(2); // 已通过 app.setCurrentApproverId(null); } } else { // 驳回 app.setStatus(3); app.setCurrentApproverId(null); } applicationMapper.updateById(app); // 写入审批记录 ApprovalRecord record new ApprovalRecord(); record.setApplicationId(applicationId); record.setApproverId(approverId); record.setAction(action); record.setComment(comment); approvalRecordMapper.insert(record); return Result.success(); }逻辑说明selectByIdForUpdate对应 SQL 的SELECT ... FOR UPDATE在事务中锁定行记录。这样两个管理员同时点审批时只有一个能拿到锁另一个会等待或超时。getApprovalChain重新计算审批链而不是从申请记录里读是为了处理审批链配置变更的情况。参数说明action用 1 表示通过、2 表示驳回与approval_record表的action字段对应。comment是审批意见驳回时建议必填通过时可选。4.3 销假与超时预警的定时任务销假环节是很多系统的盲区。学生请假结束后不主动销假系统就不知道他是否按时返校。我一般会加一个定时任务每天凌晨检查已通过但未销假的申请Scheduled(cron 0 0 2 * * ?) // 每天凌晨2点执行 public void checkOverdueCheckin() { // 查找已通过、结束时间已过、但未销假的申请 ListLeaveApplication overdueList applicationMapper.selectList( new LambdaQueryWrapperLeaveApplication() .eq(LeaveApplication::getStatus, 2) .lt(LeaveApplication::getEndTime, LocalDateTime.now()) .notInSql(LeaveApplication::getId, SELECT application_id FROM leave_checkin) ); for (LeaveApplication app : overdueList) { // 推送给辅导员 notificationService.sendCheckinReminder(app); // 标记异常 app.setStatus(2); // 保持已通过状态但记录异常标记 applicationMapper.updateById(app); } }逻辑说明notInSql子查询找出没有销假记录的申请。推送提醒用站内信或邮件不要用短信成本高且高校场景下学生不一定看短信。异常标记可以加一个is_overdue字段这里为了简化没有加。参数说明cron表达式0 0 2 * * ?表示每天凌晨 2 点执行避开白天业务高峰期。如果学校规模大建议分页处理每批 500 条避免一次性加载过多数据。5. 避坑与排查上线后最容易翻车的五个地方5.1 审批人找不到导致申请卡死现象学生提交请假申请后状态一直是待审批但辅导员说没收到待办。原因审批链计算时辅导员是按班级匹配的但学生表里的class_id为空或者辅导员表里的class_id没有对应班级。解决在提交申请前做一次校验如果找不到对应审批人直接返回错误提示「未找到您的辅导员请联系管理员」。同时加一个定时任务扫描超过 24 小时未审批的申请通知系统管理员排查。5.2 时间重叠的请假申请没有拦截现象同一个学生同时存在两条已通过的请假申请时间范围重叠。原因提交申请时只校验了时间合法性没有校验与已有申请的时间冲突。解决在createApplication方法里加一个查询检查该学生在申请时间段内是否已有状态为 0、1、2 的申请SELECT COUNT(*) FROM leave_application WHERE student_id ? AND status IN (0, 1, 2) AND start_time ? AND end_time ?如果结果大于 0拒绝提交。注意边界条件start_time new_end且end_time new_start才算重叠等于不算。5.3 销假时间早于请假开始时间现象学生请假三天但第二天就销假了系统没有拦截。原因销假接口只校验了申请状态没有校验实际返校时间与请假时间的关系。解决销假时校验actual_return_time必须大于start_time且小于等于end_time加一个宽限期比如 24 小时。如果早于start_time说明学生根本没离校应该走撤销流程而不是销假。5.4 并发审批导致状态覆盖现象两个管理员同时审批同一条申请一个点通过一个点驳回最终状态不确定。原因没有加锁两个事务同时读取到status0然后各自更新。解决用SELECT ... FOR UPDATE锁定行记录或者在UPDATE语句里加状态条件UPDATE leave_application SET status ?, current_approver_id ? WHERE id ? AND status ? AND current_approver_id ?如果受影响行数为 0说明状态已被其他事务修改返回「申请状态已变更请刷新后重试」。5.5 统计报表数据对不上现象院系管理员看到的请假人数与教务处统计的不一致。原因统计时没有排除已驳回和已撤销的申请或者时间范围边界处理不一致。解决统一统计口径只统计status IN (2, 4)的申请即已通过和已销假的。时间范围用start_time落在统计周期内为准而不是created_at。另外跨天的请假申请在按天统计时要按天拆分否则会出现一天请假 3 天但只统计到 1 条记录的情况。6. 用 Flyway 做数据库版本管理让每次表结构变更都有后悔药系统上线后表结构变更是常态。今天加一个is_overdue字段明天改一个索引如果没有版本管理测试环境和生产环境的表结构很快就会不一致。我一般会用 Flyway 来管理数据库迁移每次变更写一个 SQL 文件按版本号顺序执行。在pom.xml里加入依赖dependency groupIdorg.flywaydb/groupId artifactIdflyway-core/artifactId /dependency dependency groupIdorg.flywaydb/groupId artifactIdflyway-mysql/artifactId /dependency然后在src/main/resources/db/migration目录下创建 SQL 文件命名规则是V{版本号}__{描述}.sql-- V1__init_schema.sql -- 初始建表语句就是第2章里的五张表 -- V2__add_overdue_flag.sql ALTER TABLE leave_application ADD COLUMN is_overdue TINYINT NOT NULL DEFAULT 0 COMMENT 是否超时未销假 AFTER status; -- V3__add_index_for_statistics.sql CREATE INDEX idx_dept_start_time ON leave_application (student_id, start_time);配置application.ymlspring: flyway: enabled: true locations: classpath:db/migration baseline-on-migrate: true validate-on-migrate: true参数说明baseline-on-migrate在已有数据库上首次启用 Flyway 时设为 true会创建一个基线版本不会重复执行已有表结构。validate-on-migrate开启校验如果本地 SQL 文件的校验和与数据库记录的不一致启动时会报错防止有人手动改了数据库导致版本混乱。一个血泪经验Flyway 的 SQL 文件一旦执行过就不要修改否则校验和会对不上。如果需要修正应该新增一个版本文件而不是改旧的。我见过有人直接改 V1 文件结果测试环境正常、生产环境启动报错排查了半天才发现是校验和不一致。另一个技巧在 CI/CD 流程里加一步flyway:validate每次合并代码前检查迁移文件是否合法。这样能在合并前发现问题而不是等到部署时才翻车。这套系统从需求拆解到上线核心工作量在后端接口和状态机前端可以用 Vue 或 React 快速搭。如果你只是想练手建议先把第 2 章的五张表和审批链逻辑跑通再加销假和统计。别一上来就搞微服务单体应用加模块化包结构足够支撑一个学校的使用量。希望帮到你。本文还有配套的精品资源点击获取