
简介这份资源是面向高校信息管理与信息系统、计算机相关专业学生及课程设计者的毕业设计论文文档围绕学生宿舍管理系统的设计与实现展开帮助读者理解管理信息系统的完整开发流程。系统采用C/S架构以C# 2005为前端开发工具、Microsoft SQL Server 2000为后端数据库涵盖学生基本信息管理、宿舍分配与调整、宿舍信息维护、登录与权限审计、数据备份恢复及网络远程管理等模块并涉及事务处理、索引优化、权限验证等难点技术方案。压缩包内共1个doc文件约924KB为完整的毕业论文正文包含任务书、中英文摘要、系统设计说明、测试方法与参考文献等章节结构规范可直接作为同类课题的写作参考与设计模板。目前已有157人学习浏览适合需要完成宿舍管理系统课程设计或毕业设计、希望借鉴软件工程规范流程与论文组织方式的读者参考使用。1. 学生宿舍管理系统设计与实现从查寝翻车到一张表管清住宿每年九月开学那两周后勤老师最怕的不是床位不够而是“谁住了哪张床”这件事说不清。纸质登记表传三代Excel 版本从 V1 改到 V7调宿、退宿、换寝全靠微信群里喊一嗓子最后对不上账。学生宿舍管理系统设计与实现要解决的就是把楼栋、房间、床位、学生、入住记录这几件事用一套数据库关系锁死让查寝、调宿、退宿、统计空床变成点几下按钮的事。它适合两类人一类是课程设计或毕设要做完整增删改查加权限的在校生另一类是后勤信息化岗想先跑通最小可用版本的工程师。下面按“先立数据模型、再搭后端、再补前端、最后排坑”的顺序讲代码用 Java Spring Boot MyBatis-Plus MySQL 这套最常见也最稳的组合前端用原生 HTML Thymeleaf 降低上手门槛你换成 Vue 或微信小程序只是换皮核心逻辑不变。2. 先把数据模型立住五张表锁死床位归属2.1 为什么不能只建一张“学生表”就开干新手最容易翻车的地方是把学生和床位塞进同一张表字段写成student_name、bed_no、room_no。这样做的直接后果是一个学生调宿历史记录全丢一个床位换人上一任住宿信息被覆盖想统计某栋楼历史入住率根本查不出来。正确做法是把“人”“房”“床”“关系”拆开用外键和中间表表达多对多与时间维度。我一般会建五张核心表building楼栋、room房间、bed床位、student学生、allocation入住分配记录。其中allocation是灵魂表它记录“哪个学生、从哪天到哪天、占了哪张床、状态是入住还是已退”。这样调宿不是改学生表而是关掉旧 allocation 再开一条新记录历史天然可追溯。2.2 建表 SQL 与字段含义-- 楼栋表一栋楼一条记录 CREATE TABLE building ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 楼栋名如1号楼, gender_type TINYINT NOT NULL DEFAULT 0 COMMENT 0混合 1男生 2女生, floor_count INT NOT NULL DEFAULT 6 COMMENT 楼层数 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 房间表挂在楼栋下 CREATE TABLE room ( id BIGINT PRIMARY KEY AUTO_INCREMENT, building_id BIGINT NOT NULL, room_no VARCHAR(20) NOT NULL COMMENT 房间号如301, capacity INT NOT NULL DEFAULT 4 COMMENT 标准床位数, status TINYINT NOT NULL DEFAULT 1 COMMENT 1可用 0停用, UNIQUE KEY uk_building_room (building_id, room_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 床位表房间下的具体床位 CREATE TABLE bed ( id BIGINT PRIMARY KEY AUTO_INCREMENT, room_id BIGINT NOT NULL, bed_no VARCHAR(10) NOT NULL COMMENT 床位号如A/B/C/D, status TINYINT NOT NULL DEFAULT 1 COMMENT 1空闲 2已占用 3维修, UNIQUE KEY uk_room_bed (room_id, bed_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 学生表只存人的基本信息 CREATE TABLE student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, sno VARCHAR(20) NOT NULL COMMENT 学号, name VARCHAR(30) NOT NULL, gender TINYINT NOT NULL COMMENT 1男 2女, college VARCHAR(50), phone VARCHAR(20), UNIQUE KEY uk_sno (sno) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 入住分配表核心关系表带时间维度 CREATE TABLE allocation ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, bed_id BIGINT NOT NULL, check_in DATE NOT NULL COMMENT 入住日期, check_out DATE DEFAULT NULL COMMENT 退宿日期NULL表示在住, status TINYINT NOT NULL DEFAULT 1 COMMENT 1在住 2已退宿 3已调宿, KEY idx_student (student_id), KEY idx_bed (bed_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字段设计上有三个关键点。第一bed.status和allocation.status是两套状态前者描述床位物理状态后者描述这次入住的生命周期不要混用。第二allocation.check_out用 NULL 表示“当前在住”这样查在住学生只需WHERE check_out IS NULL比额外加布尔字段更省心。第三room和bed都加了唯一键防止同一房间出现两个 301 或同一床位出现两个 A。2.3 调宿与退宿的状态流转调宿的本质是“关旧开新”必须放在一个事务里否则会出现学生同时占两张床的脏数据。Transactional(rollbackFor Exception.class) public void changeBed(Long studentId, Long newBedId) { // 1. 找到该生当前在住的分配记录 Allocation current allocationMapper.selectOne( new LambdaQueryWrapperAllocation() .eq(Allocation::getStudentId, studentId) .isNull(Allocation::getCheckOut)); if (current null) { throw new BizException(该学生当前没有在住记录); } // 2. 旧记录置为已调宿写入退宿日期 current.setStatus(3); current.setCheckOut(LocalDate.now()); allocationMapper.updateById(current); // 3. 旧床位释放为空闲 bedMapper.updateStatus(current.getBedId(), 1); // 4. 新床位校验并占用 Bed newBed bedMapper.selectById(newBedId); if (newBed null || newBed.getStatus() ! 1) { throw new BizException(目标床位不可用); } bedMapper.updateStatus(newBedId, 2); // 5. 插入新的分配记录 Allocation next new Allocation(); next.setStudentId(studentId); next.setBedId(newBedId); next.setCheckIn(LocalDate.now()); next.setStatus(1); allocationMapper.insert(next); }这段代码里Transactional是后悔药任何一步抛异常全部回滚。updateStatus建议用UPDATE bed SET status? WHERE id? AND status1这种带条件的更新靠数据库行锁防止并发抢床比先查后改更安全。参数上check_in和check_out用DATE而不是DATETIME因为宿舍管理按天算就够精确到秒反而增加对账麻烦。3. 后端接口怎么搭Spring Boot 分层与三个必调参数3.1 项目结构与依赖选择常见做法是标准三层controller接参、service写业务、mapper碰数据库。依赖上 Spring Boot 3.x 配 MyBatis-Plus 3.5.x 和 MySQL 8 驱动即可别一上来引一堆安全框架先把 CRUD 跑通。dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.5/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency3.2 分页查询在住学生三个参数决定体验宿舍管理最高频的接口是“按楼栋、按房间、按姓名查在住学生”必须分页否则一栋楼几千条直接拖垮前端。GetMapping(/allocation/page) public PageAllocationVO page( RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) Long buildingId, RequestParam(required false) String keyword) { PageAllocationVO page new Page(pageNum, pageSize); return allocationMapper.selectAllocationPage(page, buildingId, keyword); }三个必调参数pageNum从 1 开始而不是 0前端传错会直接空页pageSize默认给 10但要在服务端限制上限 100防止有人传 99999 拖库keyword要做LIKE CONCAT(%,#{keyword},%)而不是字符串拼接避免 SQL 注入。对应的 XML 里关联 student、bed、room、building 四张表用check_out IS NULL过滤在住。3.3 空床统计接口与缓存后勤最关心“现在还有多少空床”这个查询走bed表按楼栋分组即可但每次刷新都全表扫不划算。我一般加一层本地缓存用 Spring 的Cacheablekey 带 buildingId入住或退宿时手动清掉。Cacheable(value emptyBed, key #buildingId) public ListEmptyBedVO countEmptyBed(Long buildingId) { return bedMapper.countEmptyByBuilding(buildingId); } // 调宿/退宿成功后调用 CacheEvict(value emptyBed, allEntries true) public void refreshCache() { }注意CacheEvict要放在事务提交之后才生效如果放在同一个事务方法里缓存清了但事务回滚就会出现“显示有空床实际没有”的玄学问题。稳妥做法是把清缓存放到 controller 层等 service 返回成功再调。4. 前端与权限Thymeleaf 快速出界面角色别做太细4.1 用 Thymeleaf 渲染床位图前端不必上重型框架Thymeleaf 循环渲染房间和床位用 CSS 给不同状态上色一眼就能看出哪张床空着。div classroom th:eachroom : ${roomList} h4 th:text${room.roomNo} 房间/h4 span th:eachbed : ${room.beds} th:classappend${bed.status 1} ? bed-free : bed-used th:text${bed.bedNo} /span /divth:classappend根据bed.status动态加类名1 是绿色空闲2 是灰色占用3 是红色维修。数据由 controller 一次性组装好roomList每个 room 内嵌 beds避免前端多次请求。4.2 角色权限只分两级就够很多毕设把权限做成 RBAC 五张表结果自己都绕晕。宿舍管理实际只有两种人管理员能调宿、退宿、改床位状态和宿管/学生只能查。用 Spring Security 或最简单的拦截器加 session 判断即可。public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) { String uri req.getRequestURI(); if (uri.startsWith(/admin/)) { Object role req.getSession().getAttribute(role); if (!ADMIN.equals(role)) { resp.sendRedirect(/login); return false; } } return true; }把所有写操作放在/admin/前缀下拦截器统一拦读接口放行。这样既满足“设计与实现”里权限模块的要求又不会把工期拖长。5. 避坑与排查五个血泪教训5.1 并发抢床导致一床两人现象两个宿管同时给不同学生分配同一张空床结果两条 allocation 都成功。原因先查床位空闲再更新中间有窗口期。解决更新床位时带状态条件UPDATE bed SET status2 WHERE id? AND status1判断影响行数是否为 1为 0 就抛异常回滚。5.2 退宿后床位没释放现象学生退宿了床位图还显示占用。原因只改了 allocation 状态忘了同步 bed.status。解决把“改分配记录”和“改床位状态”放进同一个Transactional方法任何一步失败一起回滚别分两次接口调。5.3 中文楼栋名乱码现象插入“1号楼”变成问号。原因数据库连接串没指定字符集或建表用了 latin1。解决JDBC URL 加useUnicodetruecharacterEncodingutf8建表统一utf8mb4排序规则utf8mb4_general_ci。5.4 分页总数不对现象第一页显示 10 条总数却是 0。原因MyBatis-Plus 分页插件没注册或者自定义 SQL 里COUNT语句被LIMIT干扰。解决配置类里加MybatisPlusInterceptor并注册PaginationInnerInterceptor自定义分页 SQL 让插件自动生成 count。5.5 调宿后历史记录查不到现象学生调宿后查“曾住过 301 的人”查不出来。原因调宿时直接UPDATE allocation SET bed_id新床覆盖了旧记录。解决永远不要改历史 allocation 的 bed_id调宿是关旧开新旧记录保留原 bed_id 和 check_out。6. 进阶技巧用一条 SQL 验证数据一致性系统跑起来后最怕的是 allocation 和 bed 状态对不上。与其写一堆单元测试不如定期跑一条对账 SQL把“在住记录指向的床位状态不是已占用”和“已占用床位没有在住记录”两类脏数据一次性揪出来。-- 检查一在住记录对应的床位状态不是2已占用 SELECT a.id, a.student_id, a.bed_id, b.status FROM allocation a JOIN bed b ON a.bed_id b.id WHERE a.check_out IS NULL AND b.status 2; -- 检查二床位标记已占用但没有在住记录 SELECT b.id, b.room_id, b.bed_no FROM bed b LEFT JOIN allocation a ON a.bed_id b.id AND a.check_out IS NULL WHERE b.status 2 AND a.id IS NULL;这两条 SQL 我一般做成后台一个“数据体检”按钮管理员点一下出结果比事后人工翻表快得多。参数上不需要额外配置直接跑即可数据量大时给allocation.bed_id和bed.status加索引。另外一个小技巧allocation表数据只增不改时间久了会大但宿舍场景几年也就几万条完全不用分库分表。真要优化按check_out IS NULL建一个部分索引或者把已退宿记录归档到历史表查询在住时只扫小表。我自己做这类系统最大的教训是别在第一天就想着把权限、报表、微信推送全做完先把“建床、分床、调床、退床”这条主链路跑通用真实数据压一遍再补外围。数据模型一旦立住后面加功能都是顺手的事模型歪了写再多代码都是给未来挖坑。希望帮到你。本文还有配套的精品资源点击获取