☰
SpringBoot学生宿舍管理系统全解析:从建表到部署的工程实践
2026/10/1 4:24:16 网站建设 项目流程

每年一到毕业设计季或者实训课结课,SpringBoot 学生宿舍管理系统就会成为热度最高的项目类型之一。我陆陆续续帮人看过不少这类系统的代码,也带过几个实训小组,发现大家的核心需求其实很一致:能跑起来、功能完整、答辩时能说清楚设计思路。这篇博客我就围绕自己最近完成的一套 SpringBoot 宿舍管理系统来完整复盘一遍,源码加文档总共两万多行,项目编号 25537。文章会从业务建模讲到数据库设计,再到核心代码的实现,最后聊一聊我在开发过程中踩过的真实坑,给准备做类似项目的朋友一份可以直接照着操作的参考。

这套系统面向的是高校宿舍管理场景,核心解决三个问题:学生信息和住宿分配怎么管、日常报修维修流程怎么走、卫生检查和违规记录怎么留档。后端使用 SpringBoot 2.x + MyBatis-Plus,前端采用 Thymeleaf 服务端渲染加 Bootstrap 布局,数据库用 MySQL 5.7。功能上覆盖了楼栋管理、宿舍分配、学生入住退宿、报修工单、卫生评分、访客登记、公告发布等常见模块。适合正在做课程设计、毕业设计,或者想快速上手 SpringBoot 综合项目的同学参考,也适合刚接触全栈开发的人用来理解一个完整业务系统是如何从零搭建起来的。

1. 学生宿舍管理系统的业务场景与需求拆解

1.1 宿舍管理到底要管什么

很多同学拿到这类项目标题后,第一反应是“不就是学生和宿舍两张表吗”,实际做起来才发现远没那么简单。学校宿舍管理的真实场景里,至少包含三类角色的诉求:宿管员要掌握每一栋楼、每一个房间的实时居住情况;辅导员要能查到自己带的学生住在哪、有没有晚归记录;维修工要接单、处理、反馈报修进度。

所以在设计系统之前,我习惯先把业务对象列出来。宿舍管理系统里最核心的业务对象有:楼栋、房间、床位、学生、教职工、维修工单、卫生检查记录、访客记录、公告、报修类型。这十个对象之间的关系大概是这样:一栋楼包含多个房间,一个房间通常住四到六人,每个学生绑定一个床位;报修工单由学生或宿管发起,分配给维修工处理;卫生检查由宿管定期录入,按房间维度打分。

把这些对象理清楚之后,功能模块就自然浮现出来了。系统划分成六大模块:系统管理、楼栋宿舍管理、学生管理、住宿管理(包含分配、入住、退宿、调宿)、后勤管理(报修、卫生)、信息发布。每个模块再往下拆就是具体的页面和接口,整个项目的骨架就出来了。

1.2 角色权限与业务流程梳理

做管理系统,权限设计是逃不开的一环。这个项目里我设计了四种角色:系统管理员、宿管员、辅导员、学生。系统管理员负责维护基础数据和账号分配;宿管员是系统的日常操作者,负责分配床位、录入卫生检查、处理报修;辅导员只拥有查询权限,主要看自己管辖范围内学生的住宿情况;学生登录后可以查看自己的床位信息、发布报修申请、查看公告。

业务流程方面,最核心的是两条线。第一条是新生入住流程:管理员导入新生名单,学生或管理员补充个人信息,系统根据宿舍楼的空闲床位自动分配或手动指定,生成入住记录。第二条是报修流程:学生提交报修申请,填写位置和问题描述,宿管员审核后指派给维修工,维修工处理完成后更新工单状态,学生确认完成。这两条流程贯穿整个系统,也是答辩时最容易被问到的业务逻辑。

1.3 功能菜单规划与页面结构

功能菜单我按照使用频率和角色差异来组织,没有把系统管理功能放在最前面,而是将高频操作前置。真正登录进来后,侧边栏的顺序是:首页数据看板、宿舍管理(楼栋维护、宿舍查询)、住宿管理(入住分配、调宿退宿、入住记录)、报修管理、卫生检查、访客登记、公告管理、系统管理(用户管理、角色管理、操作日志)。

首页数据看板是我比较推荐保留的模块,它虽然不涉及复杂业务,但对答辩展示非常加分。看板展示总楼栋数、总房间数、已入住人数、空闲床位数、待处理报修数、今日访客数这几个统计指标,下面再挂一个近七日报修趋势的简单图表。使用 MyBatis-Plus 的聚合查询就能实现,不需要额外引入重型图表库。

2. 技术选型与工程架构设计

2.1 为什么选 SpringBoot 2.x + MyBatis-Plus

SpringBoot 在高校实训和毕业设计里的统治地位不用多说,它最突出的优势是自动装配和起步依赖,能让项目从创建到跑起来控制在五分钟以内。我这次选用了 SpringBoot 2.7.x 而不是 3.x,原因很实际:大部分学校机房环境和教程资料还是基于 2.x,相关问题的解决方案更充足,遇到报错时检索成本低。如果将来有升级需求,2.7 到 3.x 的迁移也属于可控范围。

持久层我用的是 MyBatis-Plus,而不是原生 MyBatis 或者 Spring Data JPA。MyBatis-Plus 在 MyBatis 基础上封装了通用 Mapper 和通用 Service,单表 CRUD 可以直接继承 BaseMapper 和 ServiceImpl 完成,省下大量重复的 XML 编写工作。对于宿舍管理这类以单表操作和简单连表查询为主的系统,MyBatis-Plus 是效率和可控性最平衡的选择。它的条件构造器 LambdaQueryWrapper 在书写动态条件时比手拼 SQL 安全得多,不容易出现字符串拼接导致的注入问题。

2.2 服务端渲染还是前后端分离

这是一道需要正面回答的选择题。市面上现在很多教程导向是 SpringBoot + Vue 前后端分离,但我的建议是:如果目标是快速完成、稳定运行、答辩时能把业务讲清楚,服务端渲染反而是更好的方案。SpringBoot 整合 Thymeleaf 后,页面直接放在 templates 目录下,一个 Controller 既处理路由跳转又处理数据下发,开发效率很高,调试时也不需要额外启动一个 Node 服务。

当然,前后端分离方案也不是不能用,但工作量至少会多出三分之一。你需要额外处理跨域配置、Token 鉴权、接口文档、两个项目的独立部署等问题。而 Thymeleaf 方案配合 Spring Security 或拦截器做会话管理,所有代码在同一个工程里,部署时打一个 jar 包就完成。我在这个项目里选择 Thymeleaf,从开发到部署全程只用了常规的 IDEA 和 MySQL,少了很多环境层面的不确定性。

2.3 分层架构与包目录设计

工程结构我建议按照经典的三层架构组织,配合各种职责明确的包,这样后续维护起来思路清晰。项目的基础包名我用 com.school.dorm,下面按功能模块而不是单纯按技术分层来划分子包。因为宿舍管理系统的业务模块之间耦合度并不高,按模块分包能让你在写代码的时候更聚焦。

具体的包结构是这样的:config 包放配置类(拦截器、WebMvc 配置、MyBatis-Plus 分页插件);controller 包放控制器;service 包放接口,service.impl 放实现类;mapper 包放数据访问层接口;entity 包放数据库实体类;common 包放统一返回结果、异常处理、工具类;dto 包放前端传递的对象封装;vo 包放视图展示对象。按包职责分层的好处是出问题时能很快定位到代码层,答辩时被问到“项目如何分层”也能讲得清晰。

3. 数据库设计与核心表结构

3.1 学生、宿舍、楼栋的关系建模

数据库设计是整个项目的地基,地基歪了,后面所有功能都会别扭。学生和宿舍之间的关系我采用的是从物理床位出发的建模思路。真实世界里,一个学生入住后占用的是一张具体的床位,所以不能简单地把学生表和宿舍表做外键关联。中间需要一张 dorm_bed 床表,通过床位把宿舍房间和学生连接起来。

学生表我单独设计,不直接放 dorm_id 字段。student 表字段包含 id、学号、姓名、性别、出生日期、班级、学院、手机号、身份证号、辅导员ID、入住状态、创建时间等。这样设计的好处是:一个学生尚未分配宿舍时记录依然存在,体现了学生的基本身份和住宿状态相互独立的关系;后续如果要做调宿或退宿,只需要修改入住记录和床位的占用状态,而不用频繁操作学生表本身。

宿舍楼栋表、房间表、床位表三张表的层级关系是楼栋含房间、房间含床位。楼栋表包含 id、楼栋编号、楼栋名称、楼层数、宿管员姓名、联系电话。房间表包含 id、楼栋ID、房间编号、楼层、房间类型(四人间/六人间)、当前人数、容量、状态(启用/停用)。床位表包含 id、房间ID、床位编号、是否占用、学生ID。通过三表 join 查询,可以很容易得到“某栋楼有多少空闲床位”这类管理端高频问题。

3.2 入住与退宿流程的表设计

入住记录不能简单理解为“学生表加个宿舍字段”,因为学生大学期间完全可能调整宿舍,我们需要留存每一次住宿变更的历史。因此需要一张 dorm_record 住宿记录表,字段有 id、学生ID、房间ID、床ID、入住时间、退宿时间、入住类型(新生入住/转宿入住)、退宿类型、经办人ID、备注。

这里有一个设计细节值得说:床位表的 is_occupied 字段和住宿记录的退宿时间其实存在冗余。但为了查询效率,我保留了这种冗余。当学生退宿时,系统同步做两件事:更新床位的 is_occupied 为 0,更新对应的住宿记录的退宿时间为当前时间。这种情况属于典型的读多写少的场景,用一点冗余换取查询时的简单高效,是合理的取舍。需要注意的是写操作要包在事务里,避免只更新一半导致数据不一致。

3.3 报修、卫生、访客模块的数据模型

报修工单表 repair_order 是后勤管理模块的核心表,字段相对较多,包括 id、报修单号、学生ID、房间ID、报修类型ID、问题描述、上报时间、维修人员、处理时间、处理结果、工单状态、联系电话、紧急程度。工单号我采用日期加序号拼接的方式生成,比如 20250412 - 0001,这样看工单号就能大致判断上报时间,比自增主键在外观上更友好。

卫生检查表 health_check 记录每一次的评分情况,字段有 id、房间ID、检查日期、评分、检查项得分(被褥整洁、地面清洁、物品摆放、安全用电等子项)、检查人ID、备注。每一次检查是一条独立记录,方便做历史趋势统计。访客登记表 visitor_record 字段包含 id、学生姓名、来访者姓名、来访者电话、与学生的关系、来访时间、离开时间、被访学生ID、登记人。这张表结构简单,但注意来访时间和离开时间需要分开存储,方便统计晚归访客。

4. 核心功能实现与关键代码解析

4.1 登录鉴权与会话状态管理

登录功能我没有引入 Spring Security,因为宿舍管理系统的权限模型并不复杂,用拦截器加会话判断就可以完成,还能减少框架配置带来的理解成本。用户表 user 和角色表 role 是多对多关系,中间表 user_role 做关联。密码存储采用 MD5 加密加盐的方式,虽然放在现在的标准看 MD5 已经不算安全强度高的算法,但对于课程设计和内部系统,它仍然是一个可接受的、演示成本低的选择。

登录成功后,使用 HttpSession 保存当前用户对象,然后在拦截器里判断用户是否登录以及是否有对应角色访问权限。拦截器实现方式很简单:实现 HandlerInterceptor 接口,在 preHandle 方法里获取 session 中的用户信息,如果不存在就重定向到登录页,存在则继续放行。角色权限的校验,我是在方法上添加自定义注解 @RequireRole,在拦截器中根据注解配置的角色编码判断当前用户是否有权访问该接口。

4.2 宿舍自动分配与手动调宿的实现逻辑

宿舍分配是这类系统里技术含量最高的业务点。逻辑上,系统优先按照“同学院同班级优先安排同一楼层”的策略来分配,具体实现分三步:查询目标学院、班级学生对应的空闲床位;按楼栋顺序查找有空余床位的房间;按房间容量剩余情况选择入住的房间,将床位与学生绑定。

自动分配的核心其实是一个简单的贪心策略。我的实现流程是:先从请求里取班级信息,确认班内学生性别,因为男生宿舍和女生宿舍互不混合;查找到当前性别学生的宿舍楼列表,按顺序遍历每个楼栋的房间表;过滤房间状态为启用且未住满的房间,按照剩余人数降序排序,优先入住剩余床位多的房间,这样可以减少宿舍的碎片化占用;选定房间后查询该房间空闲床位,将第一个空闲床位分配给当前学生。整体来看,宿舍分配算法不需要复杂的数学模型,但需要细心处理好性别过滤和床位占用状态的并发更新,所以分配接口我加了 @Transactional 事务注解,确保床位占用和学生住宿记录要么同时生效,要么全部回滚。

4.3 报修工单的状态流转实现

报修工单的状态流转是整个系统里最适合体现工程思维的地方,它不是一个简单的字段更新,而是有明确的状态机约束。状态我设为四种:待审核、处理中、已完成、已取消。状态机的流转规则为:学生提交时初始状态为待审核;宿管审核通过并指派维修人员后变更为处理中;维修人员处理完成后变更为已完成;学生在审核通过前可以取消工单。

在数据库层面,状态字段用 int 类型存储,1 表示待审核,2 表示处理中,3 表示已完成,4 表示已取消。在代码层我要重点处理的是状态非法变更问题。比如一个已完成状态的工单不能被重新指派,一个已取消的工单不能变成处理中。我的实现是写一个状态变更校验服务,每次更新前先查询当前状态,然后跟允许变更的状态集合做比对,通过才执行更新操作。这种写法虽然多了一次数据库查询,但能有效保证数据流转的健壮性。

4.4 统计报表的 SQL 编写与看板接口

首页看板的统计接口用 MyBatis-Plus 的聚合查询就能完成。统计已入住人数时,可以用 select count 加 Wrapper 条件完成;统计各审核状态下的报修数量时,用 selectMaps 方法配合 QueryWrapper 的 groupBy 实现。但有一点要注意,MyBatis-Plus 单表聚合可以应对简单统计,遇到多表连查加分组聚合的复杂报表时,直接写 XML 里面的 SQL 会更高效。

比如近七日报修趋势的查询,原生 SQL 大概是这样的:

SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS day, COUNT(*) AS total FROM repair_order WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d') ORDER BY day;

这类 SQL 放在 XML 文件里维护比用 QueryWrapper 手拼要直观得多。在处理日期分组时还要注意时区问题,Java 后端插入的时间默认是东八区北京时间,但数据库连接配置需要明确指定 serverTimezone=Asia/Shanghai,否则在部分服务器环境下查询结果会差八个小时。

5. 部署运行与环境配置要点

5.1 本地开发环境与项目初始化

我在初始化项目时使用的是 IDEA 2023,JDK 1.8,Maven 3.8。SpringBoot 2.7 对 JDK 8 的兼容性是官方明确支持的,这也是我选择 2.7 的另一个原因。创建项目时在 Spring Initializr 中勾选 Spring Web、Thymeleaf、MyBatis Framework、MySQL Driver 四个依赖即可。工程创建完成后的第一件事就是检查 pom.xml 中 MyBatis-Plus 的版本,我使用的是 3.5.3 版本,这个版本对 SpringBoot 2.x 的支持比较稳定。

数据库准备阶段需要用初始化 SQL 脚本创建数据库和测试数据。我在 resources 目录下放了一个 db_dorm.sql 脚本,包含建库语句、建表语句和基础测试数据。用命令行或者 Navicat 执行脚本即可完成数据库部署。需要注意 MySQL 字符集必须设置为 utf8mb4,因为学生姓名和地址信息可能包含生僻字,utf8mb4 才能完整存储这类字符。

5.2 application.yml 关键配置解读

SpringBoot 项目的配置集中在 application.yml 中。我贴一份精简后的配置文件,把容易出错的部分加点解释:

server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/db_dorm?useUnicode=true&characterEncoding=utf8mb4&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: 你的数据库密码 thymeleaf: cache: false prefix: classpath:/templates/ suffix: .html mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0

配置文件里有几个容易踩坑的地方。第一是 allowPublicKeyRetrieval=true,这个参数在 MySQL 8.x 及以上版本是必须的,否则会在首次连接时报错。第二是 thymeleaf 的 cache: false,开发阶段必须关闭缓存,否则修改 HTML 页面后刷新浏览器看不到效果。第三是我开启了 MyBatis-Plus 的逻辑删除全局配置,这样 delete 操作会变成 update deleted = 1,对于学生退宿这种需要保留历史数据的场景很实用。

5.3 Docker 部署的简化方案

如果想把项目部署到服务器上给老师或同学演示,Docker 是目前最省事的方式。我没有安排复杂的容器编排,只写了一个简单的 Dockerfile 来打包后端服务,再配合 docker-compose 把 MySQL 和 SpringBoot 服务拉起来。Dockerfile 的核心内容非常简洁:

FROM openjdk:8-jdk-alpine VOLUME /tmp ARG JAR_FILE=target/dorm-server-1.0.0.jar COPY ${JAR_FILE} app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/app.jar"]

项目先执行 mvn clean package 打包成 jar 文件,然后把 jar 放到与 Dockerfile 同级的目录执行 docker build -t dorm-server . 构建镜像。用 Docker 部署需要注意的一个问题是容器内的时区默认是 UTC,所以 JVM 启动参数要手动指定时区。正确的 ENTRYPOINT 写法是ENTRYPOINT ["java", "-Duser.timezone=Asia/Shanghai", "-jar", "/app.jar"],或者通过环境变量 TZ 来设置,否则日志时间和数据库记录时间都会出现偏移。

6. 常见问题排查与开发心得

6.1 端口被占用与启动失败排查

SpringBoot 项目启动失败最常见的问题就是端口占用。默认 8080 端口经常会和本地已运行的进程冲突,报错信息会显示 Port 8080 was already in use。解决方案有两个:一是检查占用端口的进程并结束它,Windows 下执行 netstat -ano | findstr 8080 找到 PID,然后 taskkill /PID 进程号 /F;二是在 application.yml 中直接换一个端口,比如 8081。

还有一种启动失败的情况是数据库连接不上,报错内容通常包含 Communications link failure 或者 Access denied for user。前者是因为 MySQL 服务没有启动或者端口号不是默认的 3306,后者是账号密码错误或用户权限不足。我的排查习惯是先查看完整的报错堆栈,从中提取关键的一行错误信息,再去搜索解决方案,很少有人能完全不看报错信息就定位到问题的。

6.2 数据库连接池耗尽的处理思路

开发过程中我发现一个隐蔽的问题:页面打开后如果长时间不操作,再次点击时偶尔会报连接池耗尽错误。这通常是因为项目同时连接了多个数据源,或者某个循环查询逻辑没有正确关闭连接。在 SpringBoot 默认的 HikariCP 连接池中,如果某个事务内查询耗时过长,且并发请求量上来,连接池很容易被耗尽。

定位这个问题的思路是配置 HikariCP 的连接超时时间,并在 application.yml 中调大 maximum-pool-size 和 minimum-idle 参数。另外需要注意,如果开启了 MyBatis-Plus 的懒加载功能,跨会话访问懒加载属性会导致连接持续占用,需要确保懒加载属性都在事务内访问完毕后及时关闭会话。实际开发中我建议少用懒加载,直接写一个连表查询把数据查出来,简单直接。

6.3 时间字段序列化与前端显示不一致

前后端时间格式不一致是这类项目里百分百会碰到的问题。数据库里存的是 datetime 类型,Java 实体对应 LocalDateTime,但 Thymeleaf 模板渲染时间时如果不做格式化,页面上出现的是一长串很难看的时间戳或者英文格式的时间。我的解决方案是在实体类的日期字段上添加 @JsonFormat 注解,指定 pattern 为 yyyy-MM-dd HH:mm:ss。对于 Thymeleaf 页面渲染,可以使用 #temporals.format 工具类在模板中格式化,也可以在实体类中额外提供一个 get 返回格式化字符串的方法。

严谨做法是在全局配置中统一 Jackson 的日期序列化格式。具体的配置类里注入一个 Jackson2ObjectMapperBuilderCustomizer,设置日期格式为"yyyy-MM-dd HH:mm:ss",时区为东八区。这样所有接口返回的日期字段都会统一格式,不需要逐个字段加注解。

6.4 基于这套源码做二次开发的扩展建议

如果你拿到这套源码想改造成自己的项目,我建议优先改两个地方:一是把数据源替换成自己学校的信息,二是把前端页面磨得更美观一些。基础功能框架不用大动,但可以考虑能力外延:对接校园一卡通作为登录方式、增加大屏数据可视化页面、将报修分配改为根据维修工当前任务量自动派单。

扩展功能时需要重点关注原有代码的接口设计是否合理。如果发现 Controller 层直接操作了 HttpServletRequest 对象,建议封装参数为 DTO 传入,这样能提高方法的可测试性。如果要对数据库表增加字段,要同步修改 entity、mapper XML(如果有)、DTO 和前端表单多个地方,这个过程最容易出现遗漏,增字段时建议全局搜索一遍相关实体类。

最后分享一个我个人的开发习惯:每次修改代码后,我都会用浏览器开发者工具观察接口请求是否 200,响应体是否符合预期,然后再去检查页面效果。这个习惯能在开发后期帮你省掉大量排查的时间。技术选型上没有绝对的对错,但宿舍管理系统这类典型业务系统,选择 SpringBoot 加上 Thymeleaf 这种稳重组合,再加上合理的数据建模和事务控制,就已经足够支撑起一个高质量的项目交付。做这类项目最重要的不是功能多花哨,而是流程完整、代码规范、逻辑清晰,这三点做到了,不管答辩还是演示都很从容。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询