
简介面向高校计算机相关专业学生和Java初学者这份学生宿舍管理系统源码可作为毕业设计与课程实训的完整参考项目。系统前端基于Bootstrap搭建页面后端采用SpringBoot框架与MySQL数据库并按管理员、宿管、学生三类角色划分权限管理员负责学生、楼宇、宿舍、入住、宿管及管理员信息的管理并支持数据导入和Excel表格导出宿管和学生则按各自权限范围查看、维护学生信息、宿舍信息和入住信息登录账号均存于数据库。压缩包共661个文件约72.37MB以js、css等前后端页面资源和Java、class源码为主体另附sql建库脚本、xml配置、少量说明文档和演示视频目录结构清晰。已有469人学习适合读者对照理解角色权限设计以及增删改查、导入导出的实现思路。1. 需求先立住宿舍管理不是“增删改查”难在四类角色同时在线每年做课程设计或毕业设计选题时“基于 Java 的学生宿舍管理系统”几乎是最容易被低估的一个。很多同学第一反应是不就是学生、宿舍、床位三张表再加几个增删改查页面真动手才发现难点根本不在写 CRUD而在于四类角色要同时在线——学生要看自己的床位和报修进度宿管员要排寝和查卫生辅导员要统计晚归和违规管理员要管楼栋与权限。任何一处数据不一致比如同一张床位被分配给两个学生整个方案的可信度就没了。这套方案要解决的就是这类“看起来简单、做起来容易翻车”的场景用 Java 技术栈把宿舍资源、住宿分配、日常检查、报修处理、权限控制串成一条完整链路适合正在做课程设计、毕业设计或者需要给学校交付一套可运行管理系统的开发者。下面按需求、架构、模块实现、避坑、验收五段推进结构可以直接套用。2. 总体架构设计分层结构与权限模型的职责划分2.1 为什么选 Spring Boot 而不是纯 Servlet学生宿舍管理系统的业务量不大但模块多、角色多、状态流转多。纯 Servlet 或 JSP 直连数据库的写法前期上手快后期每加一个“报修审批”就要在三四个文件里改 SQL维护成本陡增。我一般会选 Spring Boot MyBatis 的组合原因有三点一是依赖注入让 Service 层可以独立测试床位分配这种核心逻辑能单独写单元测试二是 MyBatis 的 XML 映射对复杂查询更友好比如“查询某楼栋所有空床位并按楼层排序”用注解反而啰嗦三是 Spring Security 或自研拦截器都能平滑接入权限校验。分层上按 Controller → Service → Mapper → 数据库四层组织DTO 与实体分离。Controller 只做参数校验和响应封装Service 承载业务规则Mapper 只写 SQL不允许在 Controller 里直接调 Mapper。这条约束看着教条但它是后期能不能加功能的分水岭。// BedAllocationService.java —— 床位分配核心逻辑业务规则集中在 Service 层 Service public class BedAllocationService { Autowired private BedMapper bedMapper; Autowired private StudentMapper studentMapper; /** * 为学生分配床位 * param studentId 学生 ID * param bedId 目标床位 ID * return 分配结果 */ Transactional(rollbackFor Exception.class) public Result allocate(Long studentId, Long bedId) { // 1. 校验学生是否存在且未分配 Student student studentMapper.selectById(studentId); if (student null || student.getBedId() ! null) { return Result.fail(学生不存在或已有床位); } // 2. 校验床位空闲使用行锁防止并发重复分配 Bed bed bedMapper.selectForUpdate(bedId); if (bed null || bed.getStatus() ! 0) { return Result.fail(床位不可用); } // 3. 双向更新床位标记占用学生记录床位号 bedMapper.updateStatus(bedId, 1, studentId); studentMapper.updateBedId(studentId, bedId); return Result.ok(分配成功); } }上面这段代码的关键不在语法而在两个设计点Transactional保证两次更新要么都成功要么都回滚selectForUpdate加行锁防止两个宿管员同时点“分配”导致一床两人。参数说明上bed.getStatus()约定 0 为空闲、1 为占用、2 为维修中这个状态字典要提前定死写进文档不然后面每个模块各写一套枚举联调时对不上。分配失败时优先看两点事务是否回滚、行锁是否生效这两处是并发问题的黑匣子。2.2 四类角色的权限模型怎么落地角色划分建议做成“角色—菜单—操作”三级而不是简单的角色判断。学生只能看自己的住宿信息、提交报修、查看卫生评分宿管员可以排寝、录入检查记录、处理报修辅导员可以跨楼栋查看统计报表管理员管楼栋、用户、权限。实现上用拦截器 注解的方式在方法上标注所需权限码拦截器解析当前登录用户的权限集合做比对。// 权限注解与拦截器配合方法级控制比 URL 级更细 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequirePerm { String value(); // 权限码如 bed:allocate } // 拦截器中的核心判断 String perm handlerMethod.getMethodAnnotation(RequirePerm.class).value(); SetString userPerms permissionService.getPermsByUserId(currentUserId); if (!userPerms.contains(perm)) { throw new BizException(无操作权限); }这里踩坑最多的是“权限码硬编码在页面里”。前端按钮显隐按权限码判断没问题但后端必须再校验一次否则改个请求参数就能越权。数据库里建议三张表sys_user、sys_role、sys_permission中间用关联表打通角色变更走配置不走代码答辩时也方便演示。3. 核心模块实现住宿分配、日常检查与报修闭环3.1 住宿分配与床位状态机床位是这套系统里状态最复杂的实体空闲、已占用、维修中、预留。分配、退宿、调寝、维修四个动作会驱动状态流转。实现上建议把状态流转规则收敛到一个枚举类里用方法判断“当前状态能否执行某动作”而不是在各处写 if-else。public enum BedStatus { FREE(0), OCCUPIED(1), REPAIRING(2), RESERVED(3); private final int code; BedStatus(int code) { this.code code; } public int getCode() { return code; } // 判断是否允许分配只有空闲和预留可以分配 public static boolean canAllocate(int current) { return current FREE.code || current RESERVED.code; } // 判断是否允许退宿只有占用状态可以退 public static boolean canCheckout(int current) { return current OCCUPIED.code; } }调寝是最容易出问题的动作学生从 A 床换到 B 床必须先释放 A 再占用 B顺序反了会出现“一人两床”顺序对了但中途失败会出现“一人无床”。解决办法是把调寝拆成“退宿 分配”两个事务步骤在同一事务内先释放再占用失败整体回滚。退宿时还要同步清除学生表里的bed_id这一点很多初版会漏导致学生已退宿但系统仍显示有床位。3.2 卫生检查与评分记录的录入日常检查包括卫生评分、晚归登记、违规记录三类数据结构相似可以抽成一张inspection_record表用type字段区分。检查记录要绑定楼栋、房间、检查人、检查时间、分值或描述。查询侧最常见的需求是“某学生本学期卫生平均分”和“某房间最近三次检查记录”前者用聚合查询后者用分页加排序。-- 某学生本学期卫生平均分注意按学号关联而非姓名 SELECT s.student_no, s.name, AVG(i.score) AS avg_score, COUNT(*) AS times FROM inspection_record i JOIN student s ON i.student_id s.id WHERE i.type HYGIENE AND i.check_time #{termStart} GROUP BY s.student_no, s.name ORDER BY avg_score ASC;参数说明上termStart由前端选择学期传入不要在 SQL 里写死日期按student_no分组而不是姓名避免重名合并。检查记录一旦提交不建议物理删除改为逻辑删除或作废标记否则统计报表会出现“分数凭空消失”的玄学问题。宿管员录错分值是高频场景所以录入界面要有二次确认后端保留修改日志。3.3 报修流程与消息状态流转报修是学生端使用频率最高的功能流程是学生提交 → 宿管员受理 → 派单给维修 → 完成并回填 → 学生确认。状态建议定为待受理、已受理、维修中、已完成、已取消五态。每次状态变更写一条流转日志表记录操作人、前后状态、时间、备注这张日志表在答辩演示和后期排查时价值极高。// 报修状态变更必须校验前置状态防止跳步 public Result changeStatus(Long repairId, String target, String remark) { RepairOrder order repairMapper.selectById(repairId); if (!RepairStateMachine.canTransfer(order.getStatus(), target)) { return Result.fail(当前状态不允许该操作); } repairMapper.updateStatus(repairId, target); repairLogMapper.insert(new RepairLog(repairId, order.getStatus(), target, remark)); return Result.ok(); }RepairStateMachine.canTransfer内部维护一张状态转移表比如“待受理”只能转“已受理”或“已取消”。不做这层校验前端页面绕过去就能把“已完成”改回“维修中”数据就乱了。学生端提交报修时还要限制频率同一房间同一天重复提交要做提示否则宿管端会被刷屏。4. 常见问题排查数据、并发与权限的三类翻车点4.1 床位重复分配现象两个学生显示同一床位或者床位表状态为占用但学生表没有对应记录。原因一般是分配逻辑没有事务包裹或者缺少行锁。解决分配方法加Transactional查询床位时用SELECT ... FOR UPDATE并在数据库层给“床位 ID 占用状态”加唯一约束兜底。上线前用并发脚本压一遍分配接口比事后补救省事得多。4.2 权限越权访问现象学生角色直接调管理员的接口能成功。原因多是只在页面隐藏了按钮后端接口没做权限校验。解决所有 Controller 方法统一走拦截器校验接口路径不按角色做前缀区分权限判断只认权限码。自测时用普通账号手动改请求参数能过才算通过。4.3 统计报表数据对不上现象辅导员看到的住宿率与管理员看到的差几个百分点。原因通常是统计口径不一致一边算“已占用床位/总床位”另一边算“已分配学生/总人数”维修中的床位归属没约定。解决把统计口径写进设计文档维修中床位统一算“不可用”且不计入分母或者单独列出前后端共用一套 SQL 视图。4.4 报修流程卡死现象报修单停在“维修中”无法推进或者学生看不到已完成记录。原因多半是状态流转校验过严但缺少回退入口或者查询条件漏了状态。解决给管理员保留“强制流转”入口并记日志学生端查询不按单一状态过滤改为按时间倒序展示全部。4.5 中文乱码与时间偏差现象检查记录里的备注出现乱码或者报修时间比实际差 8 小时。原因是数据库字符集不是 utf8mb4或 JDBC 连接未指定时区。解决建库时统一utf8mb4连接串加上serverTimezoneAsia/Shanghai实体类时间字段用LocalDateTime序列化格式在全局配置里定死。5. 验收与答辩怎么证明这套系统真的能用验收阶段最忌讳只演示“页面能点开”。我一般按三条线走功能线、数据线、异常线。功能线覆盖四类角色的主流程各一遍学生报修到完成、宿管排寝到退宿、辅导员查报表、管理员改权限数据线检查关键表的约束是否生效比如床位唯一约束、外键关联、逻辑删除字段异常线专门制造错误输入重复分配、越权请求、非法状态跳转看系统是否给出明确提示而不是 500 错误。答辩演示建议准备一份固定脚本按“登录不同角色 → 走一条完整业务闭环 → 展示数据库变化”的顺序讲比逐页翻菜单有说服力。演示数据提前准备好至少包含两个楼栋、若干空床与占用床、几条不同状态的报修单这样任何提问都能现场查。下面这张表可以直接放进答辩 PPT 的验收页验收项验收方法合格标准住宿分配并发调用分配接口无重复分配失败有提示权限控制学生账号调管理员接口返回无权限不暴露数据报修闭环走完五态流转每步有日志状态不可跳步统计报表对比手工核算结果口径一致误差为零数据完整性删除楼栋后查关联有外键约束或业务拦截有一点血泪经验值得说答辩前一定要把数据库初始化脚本跑一遍全新环境。我见过太多次在自己机器上一切正常、换台电脑建表就缺字段的情况原因是表结构是用图形工具改的没同步到脚本。从那以后我每次交付前都强制走一遍“空库建表 → 导入初始数据 → 跑主流程”的流程宁可多花半小时也不在演示现场找后悔药。希望这套结构和验收清单能帮到你照着改就能变成你自己的方案。本文还有配套的精品资源点击获取