我们学校在特殊时期要求所有学生宿舍实行封闭式管理每天要上报体温、登记出入、分配隔离房间还要处理临时留观学生的住宿安排。那段时间宿管员的工作量暴涨光靠纸笔和微信群根本顶不住。疫情结束后我复盘了一下发现这套基于JavaSpringBoot的宿舍管理系统不仅解决了当时最急迫的管理需求放到今天的智慧校园建设里也是完全能打的毕业设计选题。这篇文章我把完整的项目拆解思路、数据设计、核心接口实现和踩坑经验全部整理出来给正在做同类课题的同学一点参考。1. 疫情场景给宿舍管理增加了哪些必须落地的业务变化很多同学拿到这个题目第一反应是“宿舍管理系统不就是CRUD吗学生信息、宿舍信息、入住记录完事了”。但如果你把“疫情背景”这四个字当真就会发现传统的宿舍管理系统和这个项目之间隔着一整套业务逻辑的差距。1.1 盘点疫情为宿舍管理新增的五个核心业务场景我梳理了某高校在实行封闭管理期间的实际操作流程真正的刚需场景是下面这五个出入登记与归寝判定学生进出楼栋必须扫码或刷卡登记系统要自动判定该学生是否允许外出、是否在允许时段内返回、是否按时归寝。体温与健康状态上报每日两次体温打卡异常数据要触发预警宿管员和辅导员能看到本楼栋本班级的填报率。隔离宿舍分配出现发热或密接情况时需要快速从空闲床位中分配隔离房间并记录隔离起止时间。访客与跨楼栋流动管控特殊时期非本楼栋人员禁止入内确有需要的要审批并留痕。数据统计与上报学校管理层需要实时掌握各楼栋入住率、隔离人数、异常人数、晚归名单等汇总数据。这里面最难的不是“上报体温”这个动作本身而是“异常数据如何流转”。体温超过37.3度之后系统要自动通知谁、分配什么流程、记录哪些状态、最终由谁解除——这套状态流转逻辑才是疫情期间宿舍管理系统真正的核心价值所在。1.2 从业务场景推导系统功能边界结合上面五个场景系统的功能模块可以划分为以下几块基础信息管理学生档案、宿舍楼栋、房间床位、入住退宿。日常管理归寝登记、外出申请、晚归记录、访客登记。健康监测每日体温上报、异常记录、填报统计。隔离管理隔离宿舍分配、隔离期间状态追踪、解除隔离审批。消息通知异常体温提醒、归寝提醒、审批结果反馈。统计看板楼栋入住率、健康填报率、隔离人数、异常趋势。一个合格的毕业设计不需要把所有模块做到极致但至少要在“基础管理 健康监测 隔离管理”这三条主线上形成完整闭环否则题目里的“疫情背景”就只是个无关紧要的装饰词。1.3 系统的角色盘点与分析宿舍管理系统的角色比普通的管理系统要多一层因为宿舍场景天然涉及“学校—楼栋—学生”三级结构。我最终确定了四个角色系统管理员维护楼栋、房间、床位等基础数据管理账号。宿管员负责本楼栋的日常管理包括登记、审批、异常处理。辅导员查看本班学生的住宿与健康情况处理异常预警。学生每日健康打卡、进出登记、查看通知。值得一提的是辅导员这个角色。很多做这个题目的同学会遗漏这一层但实际业务流程里辅导员恰恰是疫情防控链条上最关键的一环——学生体温异常后对接家长、安排就医、上报学院的往往是辅导员。系统里给辅导员单独的视图权限既符合真实业务也能在答辩时体现你对需求的理解深度。2. 为什么选SpringBoot技术栈选型背后的现实逻辑2.1 课程设计与就业衔接的平衡这个题目选JavaSpringBoot有非常现实的理由。一方面高校Java课程体系里SpringBoot是绝对主线用自己熟悉的技术做毕业设计踩坑成本最低另一方面SpringBoot加Spring Security加MyBatis Plus的组合几乎是目前中小型企业级应用的标配做完这套项目你拿到的技术栈经验完全可以写进简历。我不建议在这个题目里强行上微服务或者分布式中间件。宿舍管理系统的并发量、数据量和使用场景决定了单体应用完全够用强行引入分布式架构只会让项目复杂度失控答辩的时候被问到底层原理反而容易翻车。2.2 同类方案对比SpringBoot与Django、Node.js的取舍我在做选型分析的时候也对比过其他方案这里直接给结论对比维度SpringBootDjangoNode.js(Express)类型安全强编译期报错中等动态类型弱运行时排查生态成熟度极高文档丰富高高与高校课程衔接最紧密部分院校使用较少部署复杂度中需要JDK环境低低答辩说服力强企业级主流中中这三个方案都能实现这套系统但从毕业设计的角度SpringBoot是性价比最高的选择。特别是JPA或MyBatis Plus带来的开发效率提升我后面会详细展开。2.3 项目技术架构与整体结构我这边的技术选型如下后端框架Spring Boot 2.7.x权限框架Spring Security JWTORM框架MyBatis Plus数据库MySQL 8.x前端Vue 3 Element Plus接口文档SpringDoc OpenAPI后端目录结构按功能模块分包清晰是第一诉求com.university.dormitory ├── config/ // 配置类Security、CORS、MyBatisPlus ├── controller/ // 接口层 ├── service/ // 业务逻辑层 ├── mapper/ // 数据访问层 ├── entity/ // 实体类 ├── dto/ // 请求响应对象 └── utils/ // 工具类JWT工具、日期工具这样的分包方式可能看起来普通但好处是各层职责一目了然写代码、调试、答辩讲架构都非常顺手。3. 数据模型拆解从实体关系到可落地的表结构数据模型是整个系统最重要的地基。我见过太多同学一上来就写代码写到后面发现隔离记录没地方存、晚归状态没法表示只能推翻重来。所以这一节值得仔细看。3.1 核心实体识别与关系梳理先列一下我最终确定的实体清单用户表user主键、用户名、密码、真实姓名、角色、手机号、关联学生ID。学生表student学号、姓名、性别、所属学院、班级、联系方式。宿舍楼栋表building楼栋编号、名称、楼层数、宿管员ID。宿舍房间表room房间号、所属楼栋、房间类型、床位数量。床位表bed所属房间、床位编号、状态空闲/入住/隔离禁用。入住记录表stay_record学生ID、床位ID、入住时间、退宿时间。出入登记表access_record学生ID、进出类型、时间、体温、登记方式。健康上报表health_report学生ID、上报日期、早间体温、晚间体温、健康状况。隔离记录表isolation_record学生ID、隔离床位ID、开始时间、预计结束时间、原因、状态。访客登记表visitor_record访客姓名、证件号、被访学生ID、来访时间、离开时间。通知表notification标题、内容、接收角色、发布时间。实体之间的关系不复杂关键在状态字段的设计上。比如床位不只是“有人/没人”还要区分“被隔离占用”这样分配隔离宿舍时才不会把正常住宿的学生挤出去。3.2 关键表结构设计的细节说明这里给出三张最关键的表的设计思路。健康上报表的核心在于唯一约束要确保一个学生一天只有一条记录CREATE TABLE health_report ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, report_date DATE NOT NULL, morning_temp DECIMAL(4, 2), evening_temp DECIMAL(4, 2), health_status VARCHAR(20) COMMENT 正常/乏力/咳嗽/其他, is_abnormal TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_date (student_id, report_date) );uk_student_date这个唯一索引是必须的。否则学生重复提交你只能靠Java代码判断并发场景下极容易出现重复数据。数据库层面的约束永远比代码层面的判断更可靠。隔离记录表要重点设计状态流转CREATE TABLE isolation_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, bed_id BIGINT NOT NULL COMMENT 隔离床位ID, start_time DATETIME NOT NULL, expected_end_time DATETIME, actual_end_time DATETIME, reason VARCHAR(255) COMMENT 发热/密接/其他, status TINYINT DEFAULT 0 COMMENT 0隔离中 1已解除 2已转院, handler_id BIGINT COMMENT 操作人ID, remark VARCHAR(500), create_time DATETIME DEFAULT CURRENT_TIMESTAMP );actual_end_time和expected_end_time分开存储是为了支持“提前解除”和“延长隔离”两种实际情况。很多项目只存一个结束时间后面做统计报表时发现数据根本对不上。3.3 学生状态机设计正常、隔离、请假的状态流转学生的宿舍状态不能直接用一个字符串字段存因为状态之间是有流转关系的。我设计了下面这个状态机在住正常默认状态可出入登记可发起外出申请。隔离观察从“在住”通过“新增隔离记录”进入期间禁止外出。请假外出从“在住”发起申请宿管员审批通过后进入。已退宿从“在住”通过“办理退宿”进入终态。状态机的好处是逻辑清晰权限判断也简单。比如学生保存体温异常之后前端按钮直接置灰“申请外出”后端接口也校验状态双重保险。一个状态机字段藏在学生主表里就行不需要单独建表但状态流转的操作日志一定要落库。后面答辩老师问起来你能拿出完整的状态变化时间线这项目的基本功就立住了。4. 核心功能模块实现重点接口的完整设计思路这一节我挑最核心的三个模块讲实现思路不贴全部代码只讲设计逻辑每个模块都带关键代码片段。4.1 出入登记与归寝判定出入登记在技术上很简单就是一个INSERT操作加一个状态判断但业务上有一个关键点识别“这是出楼还是进楼”。我的方案是前端传type字段IN/OUT后端根据当前学生的宿舍状态做校验。归寝判定比较有意思。系统需要结合宿舍楼的门禁时间规则判断一条“IN”记录是否属于晚归。我当初的做法是在access_record表里冗余了一个is_late字段由后端根据楼栋的归寝时间配置计算后写入而不是在查询时临时计算这样统计晚归名单时只需要一条SQL// 出入登记接口的校验核心逻辑 public AccessRecord registerAccess(AccessRegisterDTO dto) { Student student studentMapper.selectById(dto.getStudentId()); // 1. 隔离状态不允许外出 if (student.getStatus() StudentStatus.ISOLATED) { throw new BusinessException(隔离观察期间禁止外出); } // 2. 登记出入记录 AccessRecord record new AccessRecord(); record.setStudentId(dto.getStudentId()); record.setType(dto.getType()); record.setAccessTime(LocalDateTime.now()); // 3. 如果是进楼判断是否晚归 if (dto.getType() AccessType.IN) { record.setIsLate(checkLateReturn(record.getAccessTime())); } accessRecordMapper.insert(record); return record; }4.2 健康上报与异常预警健康上报是疫情期间使用频率最高的接口设计上要特别注意幂等性和异常处理。核心逻辑是先根据student_id report_date查是否已有记录有则更新无则插入。使用Redis分布式锁也可以但对于毕业设计场景数据库唯一索引加先查后插已经足够了。异常预警的触发链是保存上报记录时如果晨检或晚检体温 37.3度系统自动做三件事——更新学生的宿舍状态为“待观察”、生成一条待处理预警记录、向辅导员发送站内通知。这里有一个很小的设计细节值得分享预警记录的表结构里我加了一个processed字段和process_user_id字段用来记录“哪个辅导员处理了这条预警”。因为一个学生可能对接多个辅导员班主任、年级辅导员如果不记录处理人后续会出现责任推诿的情况。4.3 隔离宿舍分配的核心逻辑隔离宿舍分配不是简单的查空房它有两个约束条件。第一要找的床位必须是“空闲且标记为可隔离”的床位而不是所有空闲床位。实际场景中有些房间条件不适合做隔离间比如没有独立卫生间这部分房间在基础数据维护时要打上隔离可用标记。第二分配遵循“就近原则”。隔离房间要优先选学生所在楼栋的空闲房间不要跨楼栋分配。跨楼栋转运本身就增加交叉感染风险系统分配时要自动按这个优先级排序。核心代码逻辑可以抽象成三步public IsolationRecord assignIsolationBed(Long studentId, String reason) { // 1. 查学生当前所在楼栋 StayRecord currentStay stayRecordMapper.findCurrentByStudentId(studentId); // 2. 优先同楼栋、可隔离、空闲的床位 Bed bed bedMapper.findAvailableIsolationBed(currentStay.getBuildingId()); if (bed null) { // 3. 同楼栋没有再扩大到全校但要标记为跨楼栋 bed bedMapper.findAvailableIsolationBedGlobally(); } // 占床、更新学生状态、生成隔离记录... }4.4 统计看板的数据查询技巧管理端的统计看板需要展示入住率、填报率、隔离人数等指标。最忌讳的做法是每个指标都单独写一条查询语句然后在前端拼接系统一卡就是上百条SQL。我的做法是在dashboard模块里用一个聚合查询搞定主要指标。以“楼栋健康统计”为例SELECT b.id AS building_id, b.name AS building_name, COUNT(DISTINCT s.id) AS total_students, COUNT(DISTINCT CASE WHEN hr.is_abnormal 1 THEN hr.student_id END) AS abnormal_count, COUNT(DISTINCT ir.id) AS isolation_count FROM building b LEFT JOIN room r ON b.id r.building_id LEFT JOIN bed bd ON r.id bd.room_id LEFT JOIN stay_record sr ON bd.id sr.bed_id AND sr.leave_time IS NULL LEFT JOIN student s ON sr.student_id s.id LEFT JOIN health_report hr ON s.id hr.student_id AND hr.report_date CURDATE() LEFT JOIN isolation_record ir ON s.id ir.student_id AND ir.status 0 GROUP BY b.id, b.name;这条语句有些复杂但跑出来的结果正好就是楼栋管理页面顶部那张卡片的全部数据。看板页面打开时只调一次后端接口体验非常流畅。5. 权限与安全设计多角色系统的控制粒度宿舍管理系统涉及学生隐私数据体温、隔离记录权限设计不能只做前端按钮隐藏后端接口必须做真实校验否则某个学生调用接口就能看到全校同学的体温信息这在答辩时是致命的硬伤。5.1 三种核心角色的权限边界权限矩阵我做了这么一套设计功能学生宿管员辅导员系统管理员查看个人信息自己本楼栋本班级全部健康上报本人---查看健康数据本人本楼栋汇总本班级全部出入记录本人本楼栋本班级全部隔离管理查看本人楼栋内操作只读全部基础数据维护---全部关键在于“数据范围”的限定而不是简单用角色区分能不能访问。同一个账号角色都为“宿管员”A楼的宿管员就不应该查到B楼的数据。这个需求可以基于MyBatis Plus的拦截器实现数据权限给查询SQL自动追加building_id 当前用户所属楼栋的条件。5.2 后端实现Spring Security JWT 的配置细节Spring Security JWT在校园项目里已经是标配了但有几个细节特别容易踩坑。第一个是密码存储。直接用MD5存储是错误示范至少要加盐更合理的方案是使用BCryptPasswordEncoder。Spring Security内置了BCrypt支持用起来就是一行配置。第二个是JWT过期时间的设置。我看过很多同学把过期时间设成7天甚至30天对于宿舍管理系统来说太长了。学生可能共用一台电脑或者被别人借用账号token有效期建议控制在2小时左右配合前端刷新机制使用。// JWT工具类核心方法 public String generateToken(Long userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 2 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); }第三个坑是Security的放行规则。很多同学会把/api/**全部拦截导致前端无法匿名访问登录接口。正确的做法是放行登录、验证码、健康上报等少量公开接口其余全部走认证。CORS配置也要单独写好否则前端本地调试时请求直接被浏览器拦截。5.3 数据越权与水平权限漏洞的防御垂直越权和水平越权在答辩中是最容易被问到的安全知识点。垂直越权好理解——学生不能调用管理员接口水平越权则是同一个角色内学生A不能查看学生B的数据。防御水平越权最朴素的方案是在每个查询接口里强制要求当前登录用户ID与查询目标ID一致或存在归属关系。举个例子查询学生体温记录的接口GetMapping(/health/{studentId}) public Result getHealthRecord(PathVariable Long studentId) { // 获取当前登录用户 LoginUser currentUser SecurityUtils.getCurrentUser(); // 校验学生只能查自己宿管员只能查本楼栋辅导员只能查本班级 if (!dataPermissionService.canViewStudent(currentUser, studentId)) { return Result.fail(无权访问该学生数据); } // 正常查询... }这样即使前端泄露了某个学生的ID攻击者也调不通接口。数据权限校验统一收敛到一个 service 里比在控制器里散落着写判断更容易维护。6. 实测中遇到的坑与调优经验做这个项目前后花了大概两个月过程中踩了不少坑挑几个最有代表性的分享出来都是文档里不会写的细节。6.1 高峰期填报体温时的接口性能问题第一次联调测试时模拟300个学生同时提交体温上报数据库连接池直接被打满大量请求超时。排查后发现两个问题一是每来一个请求都新建数据库连接二是健康上报的SQL包含太多无用的字段查询。解决方案有两个层面。第一配置HikariCP连接池参数把最大连接数调到合理值我最终设的是maximum-pool-size: 50第二对库存表数据做缓存上报时先更新数据库再更新Redis缓存查询走缓存优先。宿舍管理系统的查询量远大于写入量引入缓存后看板页面和健康日报的打开速度提升非常明显。6.2 时间处理不当导致晚归判定错误这个坑特别隐蔽。服务器部署在云上时默认时区是UTC而校园应用场景是中国标准时间UTC8如果不显式设置时区数据库存的时间会比实际时间少8小时晚上11点的归寝记录会被判定为第二天下午3点晚归功能就完全失效了。解决方式是在数据库连接字符串和JVM启动参数两处同时指定时区# application.yml spring: jackson: time-zone: GMT8 datasource: url: jdbc:mysql://localhost:3306/dormitory?serverTimezoneAsia/Shanghai另外涉及日期的字段建议统一使用LocalDateTime不要用java.util.Date。LocalDateTime配合Jackson的JsonFormat注解前后端的时间解析问题能省掉一大半。6.3 前端展示大量列表数据时的分页规范宿舍管理系统的列表页非常多出入记录、健康上报、隔离记录、访客记录。写分页接口时有个细节MyBatis Plus的分页插件和自定义SQL联查时容易因为分页拦截器没有正确配置导致查出来的总条数不对。我的经验是配置分页插件时把DbType.MYSQL显式指定上自定义SQL里避免在select语句里面强行拼接limit统一交给分页插件处理。下面这个配置几乎可以用于所有后续项目Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }6.4 答辩演示时的几个隐藏加分项最后说几个答辩演示时的隐藏加分项。第一个是异常演示要提前准备好数据。答辩时不要现场去创建一个发热学生的数据提前造好一条“体温37.5度且已生成隔离记录”的数据演示时直接点开列表展示状态流转。第二个是权限控制的现场演示。登录学生账号故意访问一个只有辅导员能看的接口展示返回“403无权限”这一下就能证明你真正做了后端权限校验而不只是画了页面。第三个是日志系统。在关键操作分配隔离宿舍、解除隔离、审批外出加上操作日志记录答辩时被问到“这个操作是谁在什么时间做的”你能当场展示日志列表比任何口头解释都有说服力。第四个是异常处理要友好。前后端联调时后端抛出的异常不要直接返回500堆栈统一通过RestControllerAdvice做全局异常处理返回统一的错误码和提示信息。这个细节很多同学不做但你做了答辩老师一看就知道你是有工程经验的。做完这套系统我最大的感受是毕业设计的核心不在于用了多少高大上的技术而在于你是否真正想清楚了业务场景里的每一个流程节点。疫情背景下的宿舍管理系统表面上是信息管理实际上是一套完整的应急响应流程。把这套流程想透了做出来的系统自然有深度答辩也自然有底气。希望这篇拆解能帮你少走一些弯路。
阅读完成 · 觉得有帮助?