简介:面向高校毕业设计及Java Web课程设计的班主任学生宿舍管理系统完整资料包,适合需要完成相似选题或快速搭建SSM项目的开发者参考。压缩包共44个文件,以JSP动态页面、Java源码、DOC设计文档和PPT答辩模板为主,另有SQL数据库文件及辅助配置资源,整体约16.35MB,目录划分清晰,便于按模块查阅。系统功能覆盖学生基本信息管理、班级与学生个人积分考核、辅导员日常任务登记、家长信息维护、消息公布与留言板,以及用户权限和登录登出管理,支持数据导入导出与远程访问。开发环境采用Java与SSM(Spring、Spring MVC、MyBatis),基于JDK 1.8,可使用Eclipse或IntelliJ IDEA配合Maven运行。资源内还包含需求分析说明书、详细设计说明书、项目计划书、数据库设计说明书与三套PPT模板,便于理解设计流程并直接用于答辩展示。目前已有51人学习浏览,对正准备毕业设计或实训项目的同学有直接参考价值。
1. 班主任学生宿舍管理系统:不少毕业设计都做它,但大多栽在功能与论文对不上
围绕【毕业设计java】班主任学生宿舍管理系统(源代码+论文+PPT模板).zip,这套东西本质上是把「学生信息、宿舍分配、查寝记录、卫生评比、违规登记」做成一个能跑起来的后台管理系统。它比电商、外卖那类毕业设计要轻,却比单表增删改查多出“宿舍—学生”这条关联关系和几处权限判断,工作量正好卡在本科毕业设计的舒适区。适合两类人:一类是需要快速交付可用系统的应届生,另一类是面试前拿真实项目练手、想讲清楚一套完整 CRUD 的初级 Java 工程师。做个系统不难,难的是让它能过评阅老师的眼:代码里有的表、字段、接口,论文里必须出现同名同参的设计。下面按表结构、核心代码、答辩验证三条线把它拆透。
2. 先立住骨架:宿舍管理系统的角色、模块与表结构设计
2.1 三个角色背后是三套权限逻辑,别一上来就写代码
宿舍管理系统第一个容易翻车的点,是需求边界没定清楚就急着建工程。常见做法是先把角色盘清楚,因为每个角色的权限直接决定后端要写哪些接口、给哪些页面。
管理员维护宿舍楼栋和房间、设置床位数、重置密码;宿管员做卫生检查、违规登记、报修处理;班主任看本班学生的入住信息、查晚归记录、导出本班卫生成绩。这三类人就是系统的全部使用方,学生端通常只开放“查看我的床位”和“提交报修”两个入口,不用做成完整前台门户。
权限模型我一般建议直接在 user 表里放一个 role 字段,用 admin、teacher、manager、student 四个字符串区分,不要去拆完整的 RBAC 五张表。原因很实际:毕业设计答辩老师如果追问“权限怎么动态分配”“角色表怎么缓存”,完整 RBAC 反而会把你自己绕进去。用角色字段加一个注解鉴权,代码量少,演示效果也直观,答辩时你只需要讲清楚主流程,不用去背 Spring Security 的过滤器链。真正需要扩展时再拆分也不迟,毕设阶段最重要的是把主链路跑通。
2.2 核心表结构:宿舍表、入住表、检查表怎么拆
模块拆完后就要落表结构。常见做法是把学生表、宿舍表、入住记录表、卫生检查主表、检查明细表五张表打底,再视情况加报修记录和公告通知。
最容易踩的坑是宿舍和学生之间直接放外键:宿舍表里写 student_id,或者学生表里只写一个 room_id。这种设计在换宿舍、退宿的场景下会丢历史记录,老师问“这个房间以前住过谁”,你就答不上来。正确拆法是让住和退都走入住记录表,学生表里冗余一个 room_id 也只用做展示:
| 表名 | 关键字段 | 说明 |
|---|---|---|
| student | id、student_no(学号)、name、username、password、room_id、expire_time | expire_time 为 NULL 表示在籍,非 NULL 表示已退宿 |
| dormitory | id、building、room_no、bed_count、floor | 床位总数为宿舍容量,可在页面维护 |
| stay_record | id、student_id、room_id、bed_no、status、check_in_time、check_out_time | status 用 IN / OUT 区分在住和退宿,保留历史 |
| check_record | id、room_id、check_date、total_score | 一次检查对应一条主记录 |
| check_item | id、record_id、item_name、score、remark | 卫生评分明细,例如地面、床铺、阳台 |
student 表里加 expire_time 而不是删除标志位,这是毕业设计里很巧妙的一笔:它既是逻辑删除,又能做唯一索引。比如学号本来要做唯一约束,如果你用 deleted=0/1 做逻辑删除,退宿学生那条记录还占着学号,再添加同号学生就会报唯一键冲突;把 deleted 设计成 expire_time,NULL 不参与唯一索引冲突,新增和退宿互不影响。这个设计在后面第 4 章还会专门展开。
2.3 技术选型与项目结构:Spring Boot + MyBatis-Plus 的最小可用搭配
技术栈方面,后端用 Spring Boot 2.7 + MyBatis-Plus 3.5,认证用 Sa-Token,前端套一个 Vue3 后台管理系统模板加 Element Plus,数据库用 MySQL 8。比起 JSP 那套老东西,这个组合更接近企业开发习惯,而且开源模板遍地都是,页面不用从零画,把精力留给后端逻辑。论文里也好写“基于前后端分离架构”,比“基于 JSP + Servlet”显得成熟一档。
项目结构按最常见的四层来分,答辩老师看着也眼熟:
dorm-system/ ├── src/main/java/com/example/dorm/ │ ├── controller/ # 接口层:接收参数、返回统一结果 │ ├── service/ # 业务层:事务、权限判断、核心逻辑 │ ├── mapper/ # MyBatis-Plus Mapper 接口 │ ├── entity/ # 对应数据库表的实体类 │ ├── config/ # 拦截器、跨域、分页插件配置 │ └── common/ # 统一返回体 Result、全局异常捕获 ├── src/main/resources/ │ ├── mapper/ # 复杂查询 XML,简单查询用 MP 基类 │ └── application.yml ├── sql/ │ └── init.sql # 建库建表 + 初始化账号 └── frontend/ # Vue3 后台管理系统模板改造后的页面这里有个很实际的建议:用 MyBatis-Plus 的代码生成器生成实体、Mapper、Service 后,一定要和 sql/init.sql 里的建表语句手工对齐一遍。代码生成器只管单张表,不会帮你处理 expire_time 这种跨表逻辑,实体类里的字段名如果和表字段对不上,运行时才会报错,到时候排查更难受。
3. 跑通核心流程:登录鉴权、宿舍分配与卫生检查的落地代码
3.1 登录鉴权:用 Sa-Token 的注解,别手写拦截器和黑匣子工具类
很多 Java 毕业设计喜欢自己写 JWT 工具类或一套 HandlerInterceptor,代码很长不说,过期时间、密钥管理稍不注意就是黑匣子。更省事的做法是用 Sa-Token,它把登录态、踢人下线、权限注解都封装好了,几个注解就能完成角色控制。
先引入依赖并在 application.yml 里配置:
sa-token: token-name: satoken timeout: 7200 # 固定有效期 2 小时,单位秒 active-timeout: 1800 # 无操作 30 分钟就失效 is-concurrent: false # 同一账号不允许同时登录 is-share: false # 登录后旧 token 立即失效timeout 是整个 token 的绝对有效期,active-timeout 是滑动过期时间。is-concurrent 设成 false,配合 is-share 设成 false,就实现了“同一账号只能在一个浏览器在线,新登录会把旧登录挤下线”。这个演示效果在答辩现场非常直观,比讲一堆 JWT 原理更能让老师记住。
登录接口的写法:
@PostMapping("/login") public Result login(@RequestBody LoginDTO dto) { // 查询学生表,密码用 MD5 加固定盐比对 LambdaQueryWrapper<Student> qw = new LambdaQueryWrapper<>(); qw.eq(Student::getUsername, dto.getUsername()) .eq(Student::getPassword, Md5Util.encrypt(dto.getPassword())); Student student = studentMapper.selectOne(qw); if (student == null) { return Result.fail("账号或密码错误"); } // 登录成功,颁发 token 并放进会话 StpUtil.login(student.getId()); StpUtil.getSession().set("loginStudent", student); return Result.ok(StpUtil.getTokenValue()); }这里要注意,毕业设计里密码用 MD5 加固定盐是能讲得过去的,但论文里最好补一句“生产环境应替换为 BCrypt”。如果是被追问,这句话能救场。登录成功后前端拿到 token 放请求头,后续接口上加 @SaCheckRole("teacher") 这种注解就能按角色限流,比自己在业务代码里写 if 判断要干净得多。
3.2 宿舍分配与床位统计:分页查询加剩余床位拼接
宿舍管理的核心操作是分配宿舍。列表页要展示每个宿舍的已住人数和剩余床位,后端接口的写法是这样的:
@Override public DormitoryPageVO pageDormitories(DormitoryQuery query) { Page<Dormitory> page = new Page<>(query.getPageNum(), query.getPageSize()); LambdaQueryWrapper<Dormitory> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.hasText(query.getBuilding()), Dormitory::getBuilding, query.getBuilding()) .orderByAsc(Dormitory::getBuilding) .orderByAsc(Dormitory::getRoomNo); Page<Dormitory> result = dormitoryMapper.selectPage(page, wrapper); // 逐条计算已住人数和剩余床位 List<DormitoryVO> voList = result.getRecords().stream().map(d -> { DormitoryVO vo = new DormitoryVO(); BeanUtils.copyProperties(d, vo); Long occupied = stayRecordMapper.selectCount( new LambdaQueryWrapper<StayRecord>() .eq(StayRecord::getRoomId, d.getId()) .eq(StayRecord::getStatus, "IN")); vo.setOccupied(occupied.intValue()); vo.setRemain(d.getBedCount() - occupied.intValue()); return vo; }).collect(Collectors.toList()); // 组装分页返回体 DormitoryPageVO vo = new DormitoryPageVO(); vo.setRecords(voList); vo.setTotal(result.getTotal()); return vo; }这段有两个关键点。第一,已住人数不能 count student 表里 room_id 等于宿舍 id 的行,而应该 count 入住记录表里 status 为 IN 的记录,不然退宿历史会被算进占用数。第二,dormitory 表的 bed_count 是管理员手填的床位数,它和房间实际床铺数可能不一致,所以代码里最好做校验:当宿舍已有入住记录时,不允许修改床位数,否则会出现剩余床位为负数这种尴尬。
分配动作本身不复杂,但一定注意先插入入住记录、再更新学生表。不加事务的话,前一步成功、后一步失败,学生就被分配进了宿舍但入住记录里查不到,查寝报表直接漏人。这个事务注解在下一节一起处理。
3.3 卫生检查与违规登记:一对多批量提交的事务处理
卫生检查是典型的“一对多”场景:同一次查寝,管理员给一个宿舍打分,分数由多条项目组成,比如地面、床铺、阳台各 20 分。前端一次提交整个表单,后端用一个主表加一个明细表来收。
@PostMapping("/check/save") @SaCheckRole("manager") public Result saveCheck(@RequestBody @Validated CheckSaveDTO dto) { checkService.saveCheck(dto); return Result.ok("已保存"); }业务层里加事务,避免明细写一半失败导致脏数据:
@Service public class CheckServiceImpl implements CheckService { @Transactional(rollbackFor = Exception.class) @Override public void saveCheck(CheckSaveDTO dto) { CheckRecord record = new CheckRecord(); record.setRoomId(dto.getRoomId()); record.setCheckDate(dto.getCheckDate()); record.setTotalScore( dto.getItems().stream().mapToInt(CheckItemDTO::getScore).sum()); checkRecordMapper.insert(record); for (CheckItemDTO item : dto.getItems()) { CheckItem detail = new CheckItem(); detail.setRecordId(record.getId()); detail.setItemName(item.getItemName()); detail.setScore(item.getScore()); detail.setRemark(item.getRemark()); checkItemMapper.insert(detail); } } }这里有一个答辩常问的知识点:为什么 @Transactional 不写在 Controller 方法上?因为 Spring 事务基于 AOP 代理,在同一类内部调用自己的另一个方法时,注解会失效。把事务逻辑下沉到 Service 层,让 Controller 只做参数接收和结果包装,是更稳妥的分层习惯。另外 CheckSaveDTO 里的 items 列表最好这样声明:
public class CheckSaveDTO { @NotNull(message = "宿舍不能为空") private Long roomId; private LocalDateTime checkDate; @Valid private List<CheckItemDTO> items; }@Valid 加在 List 字段上,才能触发对列表中每个元素的校验,光在方法参数上加 @Validated 是不够的。这条细节在论文里点一句,比大段贴代码更能体现你踩过坑。
4. 避坑清单:这个毕设最常见的 5 个翻车点
4.1 论文里的表结构图和代码里的实体对不上,评阅老师一问就露馅
现象:论文里的数据库设计章节写了“宿舍表包含 room_name 字段”,代码实体类里对应字段却是 roomNo,评阅老师对照着看时一眼就能抓包。
原因:先画论文图再写代码,或者写完代码后直接复制旧的论文图,字段名、类型、注释都对不齐。
解决:写实体类时把注释当成论文素材来写,例如在字段上加 @ApiModelProperty("宿舍名称") 或用 // 注释标清楚中文含义。一个实体类对应论文里一个小节,写完代码立即回填论文中的表结构描述。最稳的做法是准备一张“字段映射表”,把实体字段、JSON 返回字段、论文中文名三列列出来,交稿前自查一遍。
4.2 MyBatis-Plus 分页查询全表返回,列表接口越翻越慢
现象:调用 selectPage 后,页面能翻页但数据不对,或者 total 显示的是全表总数,可 records 返回了所有行,接口耗时明显上升。
原因:MyBatis-Plus 的分页插件没有注册进 Spring 容器。selectPage 本身只是拼 LIMIT 参数,真正在 SQL 执行前改写语句的是 PaginationInnerInterceptor,它没生效时,分页查询退化成全表扫描。
解决:加一个配置类,把分页插件注册进去:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }加上这段后,分页才会有 COUNT 查询和正确的 LIMIT。排查这个问题时可以看控制台打印的 SQL,如果只有 SELECT 没有 SELECT COUNT,基本上就是这个原因。
4.3 LocalDateTime 返回给前端少 8 小时或格式是 ISO 串,演示时直接翻车
现象:前端列表里“入库时间”显示成 2024-06-01T02:14:00 或者比数据库里的时间整整少 8 小时,看起来像系统时间错了。
原因:Jackson 默认用 ISO 8601 格式输出 LocalDateTime,而且如果数据库连接串没指定时区,MySQL 驱动会用服务器本地时区解析,前后端时间就对不齐。
解决:在 application.yml 里统一配置格式和时区,同时把数据库连接 URL 的时区参数带上:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai datasource: url: jdbc:mysql://localhost:3306/dorm_system?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai需要注意,spring.jackson.date-format 对 java.util.Date 有效,对 LocalDateTime 要配序列化器才完全生效。毕业设计里最省事的做法是上面这段配置加上实体统一用 LocalDateTime,再不行就在前端做一层 formatDate 兜底,至少演示时不会露怯。
4.4 逻辑删除和唯一索引打架:退宿后重新录入学号报 Duplicate entry
现象:学生表用学号做唯一索引,用 MyBatis-Plus 的 @TableLogic 做逻辑删除。学生退宿后,再次录入同号学生,insert 直接报 Duplicate entry。
原因:逻辑删除不删物理数据,只是把 deleted 标志位从 0 改成 1。学号唯一索引仍然占用着,新插入的记录索引冲突。
解决:放弃 deleted 数字标志,改用 expire_time 字段,NULL 表示在籍、非 NULL 表示已退宿。唯一索引做成学号和过期时间的联合索引:
alter table student add expire_time datetime null; alter table student drop index uk_student_no; alter table student add unique index uk_no_expire (student_no, expire_time);MySQL 唯一索引允许多个 NULL 同时存在,所以退宿记录和新增记录不会互相冲突。查询在籍学生时统一拼一个 .isNull(Student::getExpireTime) 条件。这个设计方案比 @TableLogic 更贴合住宿管理的业务语义,论文里也好解释:保留历史、支持学籍回溯。
4.5 答辩现场前端页面起不来,源码包被老师当场拆穿
现象:答辩时换了一台电脑,前端项目只有一个 src 目录,node_modules 不存在,npm install 现场装依赖要跑十几分钟,紧张之下项目直接崩在台上。
原因:交付包里只有源代码,没有可运行产物,也没有写清楚启动步骤。答辩不是开发机,临时配置环境本来就是高风险动作。
解决:交付包按两档准备。第一档是开发用的完整源码;第二档是演示用的可运行包,它包含 Spring Boot 可执行 jar,前端打包后的 dist 静态文件也打进 jar 的 resources/static 目录。答辩时只需要先导入数据库脚本,再执行一条命令就能起服务:
mysql -uroot -p < sql/init.sql java -jar target/dorm-system.jar --server.port=8080另外在 README 里写一个“五分钟启动指南”,写明 JDK 版本、MySQL 版本、初始化账号,挡住现场 80% 的环境问题。这份文档通常不会有人细看,但它是一个很好的交付习惯,也是论文“系统运行环境”章节的直接素材。
5. 论文与答辩:让这套源代码不只是能跑
5.1 三个验证场景,证明系统能吃住追问
答辩时最怕老师问“你怎么证明系统是可靠的”。提前准备三个自测场景,每个都能用接口验证,比空口说“测过了”更有说服力。
| 验证场景 | 操作命令 | 期望结果 |
|---|---|---|
| 分页接口在大数据量下可用 | 在 student 表灌 5000 条数据后调用分页接口 | total 正确、单页耗时可控、SQL 含 LIMIT |
| 分配宿舍具备事务性 | 模拟重复提交同一床位分配 | 不出现入住记录和学生表不一致的情况 |
| 退宿后学号可复用 | 对某学生执行退宿后再次新增同号学生 | 无 Duplicate key 报错,页面正常展示 |
这三个点分别对应分页插件配置、@Transactional、expire_time 联合索引,都是上面踩过的坑。把这些验证过程截图放进论文的“系统测试”章节,整个项目的完成度立刻高一个档次。
5.2 数据字典、接口 URL、PPT 截图三对齐
交稿前给自己留半天做对齐检查:论文里的数据字典逐字段对实体类;论文里的接口设计表逐行对 Controller 的 @RequestMapping;PPT 里的功能截图必须出自当前版本代码,不能用旧版界面图。我给自己定的规矩是:交源码之前,先把论文里的数据字典对着实体类过一遍,再随手点几个页面截图替换 PPT 里的旧图。这个习惯帮我避开了很多毕业设计答辩的临时翻车,也算是一种性价比很高的“后悔药”。
进阶加分项可以在答辩时演示 Sa-Token 的单设备登录效果:用一个账号在另一个浏览器登录,之前的会话被踢下线。配合一句“宿舍管理系统面向在校师生,单设备约束可以防止账号共用”,比空讲安全机制更有画面感。希望帮到你,也祝你的答辩顺利一次过。
本文还有配套的精品资源,点击获取