高校宿舍分配管理系统:基于SpringBoot+Vue的规则匹配与智能分配实践
2. 项目概述说实话每年毕业季我都会被大量类似选题淹没但高校宿舍分配管理系统这类题目从来都是计算机毕业设计里的常青树。原因很简单它既不是一个纯理论课题也不是一个纯CRUD的增删改查而是恰到好处地悬浮在业务逻辑有复杂度和技术栈相对固定好实现之间的位置。你拿它去展示SpringBoot Vue的前后端分离能力、去讲清楚自定义匹配算法的设计思路、去演示权限控制和事务处理都足够有料也不至于做到一半崩掉。这个题目本质要解决的是传统宿舍分配方式的痛点。我印象里很多学校至今还在用Excel表格排宿舍辅导员拿着一堆学号一个个往表格里粘贴遇到两个人想住一起但被拆开、某栋楼女生多男生少要临时调换、有人不想住上铺这类需求时表格逻辑几乎瘫痪。所以这套系统的核心价值就是把人工分配变成规则分配算法匹配在线管理让宿舍分配从靠经验、靠运气变成靠规则、靠数据。这篇文章我会把整个项目从需求拆解到模块设计、从数据库建模到核心算法选型、从实战踩坑到答辩准备从头到尾捋一遍。如果你是准备拿这个题目做毕业设计的学生或者是在校生想自己搭一套宿管系统做课设又或者只是对SpringBoot项目感兴趣想看看一个完整业务系统如何落地这篇文章应该都能给到你不少可以直接抄的作业。先提醒一点这个项目的核心难点不在于SpringBoot框架本身也不在于Vue前端的页面做得有多花哨而在于宿舍分配规则怎么设计、匹配算法怎么落地、以及遇到实际业务中的各种拼房需求时怎么兼容。很多同学答辩翻车不是代码写不出来而是被问到你这个匹配逻辑为什么不合理时答不上来。所以本文会在算法和业务规则这两块花比较多篇幅讲清楚为什么这么设计而不是简单给一份能跑通的代码就完事。2. 项目整体设计与思路拆解2.1 业务场景与核心需求要设计这个系统先要搞明白宿管科或者是学校学工处到底需要什么。我去翻过一些高校的后勤管理需求文档又结合自己做过的实际项目把核心需求归纳成这么几类第一类是学生信息管理。这个看起来简单实际上是最繁琐的。新生入学报到、老生转专业、休学复学、退宿走读每一个动作都会影响到宿舍床位的状态。系统至少要管住学生的基本档案、院系班级、学号、联系方式以及最重要的——住宿状态和床位关联。第二类是宿舍资源管理。宿舍楼有哪几栋、每栋有几层、每层有几个房间、每个房间住几个人、床位的朝向和上下铺属性、楼层有没有热水、楼栋是男生楼还是女生楼还是混合楼这些基础数据全部要建模。很多同学在这里偷懒只建一张房间表和一个床位表结果后面做分配时发现上铺/下铺这种属性根本没地方存只能返工。第三类是分配规则的灵活配置。这是整个项目最核心的部分。学校不同时期、不同批次新生入学、老生调整、调宿申请的分配规则完全不一样。比如新生可能是按院系集中住宿同院系优先分到一起老生调整可能是按晚归次数、卫生评分来分配文明宿舍还有按作息习惯匹配、按是否吸烟匹配、按是否考研匹配等等。规则不能写死要能配置。第四类是调宿与退宿流程管理。现实世界里学生住得不满意要换宿舍辅导员批不批、宿管怎么执行、空床位怎么释放、换宿记录怎么留存都要有清晰的流程。很多传统系统把调宿做成一个管理员手动改床位的功能但权限管理混乱学生也能偷偷改因此调宿必须在流程和角色权限上都做好设计。第五类是信息统计与报表导出。宿管科每学期要上报各种数据入住率、空床位数、各院系住宿分布、晚归登记明细等。系统至少要有统计仪表盘和Excel导出能力否则答辩时演示完功能后老师问一句你这系统怎么体现管理价值你只能愣住。2.2 技术选型为什么是SpringBoot Vue每次被问到这个题目我的回复都很统一后端用SpringBoot前端用Vue数据库用MySQL权限认证用JWT这是性价比最高、也最稳的毕业设计组合。SpringBoot最大的优势是开箱即用和生态成熟。你不需要像SSM时代那样写一堆XML配置文件也不需要关注复杂的环境搭建。创建一个工程引入spring-boot-starter-web、spring-boot-starter-data-jpa或者MyBatis-Plus再配个数据源一个能跑起来的接口服务几分钟就出来了。更重要的是SpringBoot的自动装配机制、starter机制、统一的异常处理、日志管理、参数校验这些点都能成为你答辩时讲技术亮点的素材。选Vue作为前端是因为毕业设计往往要求前后端分离Vue的生态和学习成本最适合学生。配合Element UI或者Element Plus这套组件库表格、表单、弹窗、分页这些管理后台最常见的界面元素全部有现成组件可以快速搭建出一套像模像样的后台管理界面。如果你精力够还可以再引入ECharts做统计图表效果会非常加分。数据库这一块如果你选择MySQL需要注意字符集、时区、事务隔离级别这些基础配置。另外Python爬虫抓数据、Excel导入新生名单都可以围绕MySQL来做兼容性非常好。如果你想让项目显得更前沿可以考虑接一个Redis做缓存把宿舍空闲状态、学生搜索等热点数据缓存起来还可以用MinIO做文件存储用于学生头像和宿舍照片上传。但记住这些都属于加分项不要因为追求技术栈新而把项目复杂度搞到超出自己的掌控范围。2.3 架构设计整个系统我建议采用标准的前后端分离架构。后端负责业务逻辑和数据处理暴露RESTful API前端通过Axios调用接口渲染页面处理用户交互。后端部分按经典的三层架构划分Controller层接收请求参数、校验参数、调用Service层Service层处理业务逻辑比如分配规则判定、床位锁释放、事务控制Mapper或者DAO层负责数据库操作。如果是用MyBatis-PlusService层可以直接继承IService接口很多通用CRUD都不用自己写能把精力集中在核心业务上。前端部分按页面维度组织模块登录页面、学生管理页面、宿舍管理页面、分配管理页面、调宿管理页面、统计报表页面、系统管理页面用户、角色、菜单权限。如果是用Vue 3 Element Plus配合Vue Router做前端路由、Pinia做全局状态管理工程化结构很清晰。还有一个很多毕设容易忽略的点前后端接口约定。我建议在动手写前端之前先把接口文档用Apifox或者Swagger定义好约定统一的返回格式比如{ code: 200, message: success, data: {...} }。这样两端联调的时候不会出现前端要的是这种结构后端返回的是另一种结构的尴尬局面。2.4 权限模型宿舍管理系统的角色大概是这么几类系统管理员超级管理员、宿管科老师、院系辅导员、楼栋管理员、学生。不同角色看到和操作的内容完全不同。管理员可以配置系统参数、管理所有用户、查看全部数据宿管科老师可以发布分配批次、执行分配、审批调宿辅导员能管理自己院系的学生、发起分配需求、查看本院住宿情况楼栋管理员管日常的报修、查寝、晚归登记学生只能看到自己的住宿信息、提交调宿申请。权限模型我推荐用RBAC基于角色的访问控制用户-角色-权限菜单/按钮/接口。后端用SpringSecurity JWT做认证和授权。把需要权限拦截的接口配置到Security的拦截规则里前端根据用户角色动态生成菜单这样权限管理这块的亮点足够让你在答辩时讲好几分钟。关于JWT的实现有一点要特别注意JWT是无状态的签发之后服务端不保存会话。你在代码里要做的是登录成功后生成token返回给前端前端存到localStorage或者Piniavuex每次请求通过Authorization请求头带上。然后后端写一个JWT拦截器解析token、从中取出用户ID和角色信息放入请求上下文比如ThreadLocal后续业务代码直接用当前登录用户的信息。多租户的场景也可以基于这个做数据隔离。2.5 为什么用分配批次而不是直接改床位这个设计思路是我最想强调的部分。很多初学者拿到这个题目做出来的系统就是管理员点一个学生再点一个空床位床位分配给该生——这是纯手动调整是传统的人工分配方式的电子化但完全谈不上智能分配平台。我想要的设计是引入分配批次这个概念。每一次分配都是一个独立的批次比如2024级新生宿舍分配批次、2023-2024学年老生宿舍调整批次。每个批次有自己的状态草稿、进行中、已完成、已发布、参与的宿舍楼范围、适用规则、优先级策略、截止时间等。为什么要这么做因为实际场景中不同批次面向的人群不同、规则不同、宿舍范围不同。如果做成直接改床位规则就无法统一也没办法做到按规则自动执行分配。而有了批次就可以把若干分配规则配置到批次里让系统遍历符合条件的未分配学生通过匹配算法去查找符合规则的组合。比如新生分配批次A规则是同院系优先、作息时间一致优先另一个批次B规则是考研学生优先分到安静的楼层。这两个批次并行不冲突。处理调宿的时候也是新建一个调宿批次收集完申请后统一处理。这种设计还有一个额外好处它让系统的业务逻辑变得立体。答辩时你可以画一个状态流转图批次创建-学生名单导入-规则配置-自动匹配-人工复核-结果发布-学生确认。这个流程本身就是一套完整的管理闭环比增删改查高级太多。3. 核心细节解析与实操要点3.1 数据库设计的关键点数据库设计是整个项目的基石我接触过太多因为表结构设计不合理导致后期代码越写越痛苦的情况。下面这些表是必须有的而且要注意字段设计的细节。第一张是学生表 student。字段包括学号主键或者用自增ID学号索引、姓名、性别、身份证号、手机号、院系ID、班级ID、入学年份、生源地、个人标签JSON格式存比如晚睡、考研、安静、无不良嗜好等。这里有个小技巧个人标签用JSON不要用关联表否则你要为了每个标签建一张表、每次匹配都要多表关联性能和维护性都会吃亏。第二张是宿舍楼表 dormitory_build。记录楼栋名称、楼栋编号、楼层数、朝向、性别限制男/女/混合、是否配有电梯、是否有独卫、楼栋地址、负责人。这里有一个容易忽略的点性别限制不要用枚举写死因为有些楼栋可能是双层女生、单层男生的混合模式。第三张是房间表 dormitory_room。字段房间ID、楼栋ID、楼层、房间号、房间类型四人间/六人间/八人间、是否配有空调、是否有阳台、房间朝向、所属性别限制、当前已住人数、最大容量、状态空闲/部分入住/已满/维修中。第四张是床位表 dormitory_bed。这个表经常被省略但它非常重要因为上铺/下铺这个属性只能存在床位层级。字段包括床位ID、房间ID、床位编号比如0201、0202、是上铺还是下铺、是否靠窗、是否靠近门。第五张是入住记录表 dormitory_assignment。记录学生ID、床位ID、批次ID、入住时间、退宿时间、操作人、状态有效/无效。这张表不止是为了记录历史也是为了支持查某个时间段内某个床位的占用情况这类统计需求。第六张是分配批次表 assign_batch。字段批次名称、批次类型新生/调整、参与宿舍楼列表用逗号分隔存一个字符串即可、状态、开始时间、结束时间、规则配置这一步用JSON存规则数组。第七张是调宿申请表 dormitory_change_apply。字段学号、原床位ID、目标床位ID、申请原因、状态待审核/通过/驳回、辅导员审核时间、宿管科审核时间。八张是公告/报修表。这个属于可选模块但加上会让系统更完整。报修表记录宿舍设施报修、处理状态、报修人公告表发布宿舍通知。外键约束建议不要读死尤其是床位和房间、学生和入住记录之间的关系尽量用逻辑外键代码层面维护而不是数据库物理外键。原因很简单项目运行中经常会有挪床位、释放床位这种操作物理外键约束会让这些操作变得繁琐而且一旦遇到批量导入学生数据外键会导致效率低下。数据库建表时字符串长度不要随意乱设比如手机号11位、学号不要设成自增整数很多学号带字母状态字段用tinyint时间字段统一用datetime而不是timestamp避免时区问题。这些细节虽然小但答辩时老师问起来你能解释清楚就是加分项。3.2 匹配算法设计从规则到分值宿舍匹配听起来像是一个算法题但真实场景下并不需要上什么模型需要的是规则引擎思想 贪心策略。我把分配过程简化成三个步骤收集规则、计算分值、排序分配。第一步配置规则。每一条规则都包含三个要素规则维度、权重、方向偏好。比如有这么几条规则规则维度权重偏好方向同院系10同院系优先作息时间5相同/相近作息优先是否吸烟4不吸烟者优先同住吸烟者尽量集中是否考研3考研学生按安静楼层优先分配第二步对于每一个待分配的学生系统扫描所有空床位或者空房间计算该学生放在这个床位上的适配分。这个适配分的值等于所有适用规则的加权分之和。比如A、B两人都是计算机学院的作息都为早睡早起都不吸烟那么A放到B旁边的空床位时同院系作息吸烟三项得分合计19分如果放到一个全是夜猫子、有吸烟同学的房间得分可能只有7分。第三步把所有待分配学生的候选床位按分值降序排列同时按学生的优先级比如报到时间、学号顺序排序然后依次分配座位。分配时要注意同一个房间或者同一张床不能被重复分配因此每次分配完都要刷新床位状态然后重新计算候选列表。这个算法本质上是贪心的虽然不一定能求得全局最优解但它有一个很现实的好处可解释性强。管理老师看到结果后能明确知道某个学生为什么被分到某个床位——因为有哪几条规则加分。这样他要去人工干预时也有依据。如果要进一步提升匹配质量有两个方向可以考虑第一是引入偏好填报模块。学生报名时先填自己的偏好比如期望的楼栋、期望的作息、是否介意吸烟室友、是否需要安静环境。然后系统把学生偏好也纳入规则计算这样学生参与感更强分配结果也更合理。不过需要控制的是如果每个学生都偏好同一个宿舍楼系统要做容量限制不能让某栋楼爆掉。第二是引入启发式策略。比如先把有特别需求的学生身体原因需要下铺、需要住低楼层优先处理再处理普通学生。这种优先级分配 通用分配的两阶段策略在实际场景中用得非常广泛。另外还有一个细节宿舍分配不是一次性完成的。很多学校是分批次来先分配第一批外省生再分配第二批本省生。每批结束后剩余空位状态要重新统计下一批再来分。所以代码里分配批次的状态机一定要严格管理不能让两个批次同时操作同一张床位否则会产生冲突。3.3 分配逻辑落地的代码结构下面我用伪代码的方式演示分配批次执行时核心逻辑应该怎么写方便大家照着实现。public void executeBatch(Long batchId) { // 1. 校验批次状态 AssignBatch batch assignBatchService.getById(batchId); if (batch.getStatus() ! AssignBatchStatus.DRAFT) { throw new BusinessException(批次非草稿状态无法执行分配); } // 2. 获取待分配学生列表 ListStudent students studentService.listUnassignedStudent(); // 3. 获取可用床位列表 ListBed availableBeds dormitoryBedService.listAvailableBeds( batch.getBuildings() ); // 4. 逐批处理优先级学生比如下铺需求 ListStudent priorityStudents students.stream() .filter(s - s.getNeedLowerBed() ! null s.getNeedLowerBed()) .collect(Collectors.toList()); for (Student student : priorityStudents) { Bed targetBed matchAlgorithm.findBestBed(student, availableBeds); if (targetBed ! null) { assignBed(student, targetBed, batchId); availableBeds.remove(targetBed); } } // 5. 处理普通学生 ListStudent normalStudents students.stream() .filter(s - s.getNeedLowerBed() null || !s.getNeedLowerBed()) .collect(Collectors.toList()); for (Student student : normalStudents) { Bed targetBed matchAlgorithm.findBestBed(student, availableBeds); if (targetBed ! null) { assignBed(student, targetBed, batchId); availableBeds.remove(targetBed); } } // 6. 更新批次状态 batch.setStatus(AssignBatchStatus.COMPLETED); assignBatchService.updateById(batch); }具体到matchAlgorithm.findBestBed实现的思路也比较直接遍历所有可用床位针对每个床位计算该学生与当前房间已住学生的相似度得分得分最高的床位即为推荐。这里有一段关键代码需要注意因为每次分配完目标房间的已住人员会发生变化所以计算分数必须用实时数据。public Bed findBestBed(Student student, ListBed availableBeds) { Bed bestBed null; double maxScore -1; for (Bed bed : availableBeds) { double score calculateScore(student, bed); if (score maxScore) { maxScore score; bestBed bed; } } return bestBed; } private double calculateScore(Student student, Bed bed) { Room room roomMapper.selectById(bed.getRoomId()); ListStudent roommates studentMapper.selectStudentsByRoomId(room.getId()); double score 0; // 规则同院系 for (Student roommate : roommates) { if (roommate.getDeptId().equals(student.getDeptId())) { score 10; } // 规则作息一致 if (roommate.getSleepTag().equals(student.getSleepTag())) { score 5; } // 规则吸烟偏好 if (roommate.getSmoking().equals(student.getSmoking())) { score 4; } // 规则考研优先 if (student.getIsPostgraduate() room.getFloor() 4) { score 3; } } return score; }这种写法不需要引入额外的规则引擎框架用最朴素的if-else即可完成规则的表达。如果你想做得更高级可以考虑把这些if-else改成策略模式定义MatchRule接口里面有一个match(Student, RoomContext)返回得分。写SameDeptRule、SleepPatternRule、SmokingRule、PostgraduateRule等实现类。然后在ScoreCalculator里根据配置的规则列表依次调用。这样做的好处是新增规则不需要改动分配逻辑只要在配置里新增一个策略实现。答辩时老师问怎么扩展规则这就是一个很好的亮点。但也要注意策略模式会让代码量增加不少如果毕设周期很紧、代码能力一般最简单的方式还是if-else。不必为了设计模式而设计模式。3.4 前端页面与交互设计前端不要贪多能覆盖以下核心页面就够了。登录页账号密码登录登录成功跳转首页不同角色登录后菜单项不同。首页仪表盘展示当前总宿舍数、总学生数、入住率、空床位数、待处理调宿申请数用ECharts画一个住宿分布饼图和近期入住趋势折线图。学生管理页支持学生信息的分页查询、条件筛选按院系、按班级、按关键字、新增、编辑、删除、导入Excel、导出Excel。导入Excel用阿里巴巴的EasyExcel可以很轻松地把一个模板文件的每一行转成对象列表。宿舍管理页左边树形结构展示楼栋、楼层、房间右边表格展示所选房间的床位信息支持点击床位查看当前入住学生支持对床位的状态修改维修、禁用、释放。分配管理页创建分配批次、配置规则、选择参与宿舍楼、导入待分配学生名单、点击执行分配按钮在前端展示分配进度和结果支持人工复核和手动调整把某个分配结果撤销、重新选择。调宿管理页学生提交调宿申请选择希望换到的楼栋、房间偏好填写原因辅导员审核列表宿管科审核列表。审核通过后系统自动释放原床位并占用新床位同步更新入住记录。系统管理页用户管理增删改查账号、角色管理给角色分配菜单和操作权限、菜单管理可动态生成前端菜单、操作日志列表。从交互细节来说有两点要特别重视就是提交表单时的校验和操作成功后的反馈。前端表单校验用Element Plus自带的规则校验比如学号必须填写、手机号格式校验、性别必选。后端也要再校验一遍参数不能只靠前端。操作成功后的提示用ElMessage组件弹出成功信息失败或异常则弹出错误信息。删除操作一定要求二次确认比如需要输入删除两个字再执行防止误操作。3.5 数据以及事务与并发细节宿舍分配这种业务非常吃并发控制。想想这个场景两个学生同时在调宿申请管理员又在执行新的分配批次如果都没有事务和锁的控制极有可能出现两个学生被分配到同一张床的错误。解决方案有三个层次第一层在分配逻辑上尽量串行。比如执行分配批次时用一个状态锁控制同一时间只有一个分配批次在执行。最简单的方式是在assign_batch表里加一个executing_flag字段执行前先update这个字段抢到锁的批次才能继续执行执行完再释放。第二层用数据库层级的悲观锁。查询可用床位时使用SELECT ... FOR UPDATE把这一行锁住直到事务提交才释放。注意这个操作必须在Transactional事务内执行才有意义。第三层用Redis分布式锁。如果不想在数据库层面加锁可以用Redis的SETNX命令实现一个简单的分布式锁。项目里引入Redis本来也不难还能在答辩时顺带提一句利用分布式锁防止超卖。但注意毕设项目用单机锁也够Redis锁属于加分项不是必需项。事务的处理同样重要。比如调宿申请通过这个操作涉及几步数据变更把原床位的状态改为空闲、把目标床位的状态改为已占用、把入住记录更新为目标床位、把申请状态改为已完成。这四步必须放在同一个事务里只要任何一步失败全部回滚。否则就会出现床已经占了但申请还是待审核的数据不一致问题。我说一个自己踩过的坑之前某一个小项目里在循环中调用了this.xxx()方法也就是在同一个类内部调同一个Service的另一个方法而这个方法上面标了Transactional。结果事务失效了数据写到一半出错导致脏数据。这个坑的原理是Spring的AOP代理不会在内部调用时生效。解决办法是把需要事务的方法放到另一个Service类里或者注入自己的代理对象再调用。如果你写毕设也遇到事务不生效先检查是不是这个原因。3.6 常见问题与排查技巧实录问题一前端请求接口报跨域错误。SpringBoot后端的CORS配置没有设置好就会报Access-Control-Allow-Origin的错误。解决方式很简单写一个配置类实现WebMvcConfigurer重写addCorsMappings方法允许所有来源和所有方法。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(*) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }注意如果是前后端分离并且你打算用JWT开发环境下前端端口一般是8080后端的端口是9090之类跨域问题是必现的。问题二时间字段比实际时间差8小时。最常见的坑是数据库连接串里没有配置serverTimezone或者用了中国标准时间但本机时区不同。解决办法是在jdbc连接串里加serverTimezoneAsia/Shanghai。url: jdbc:mysql://localhost:3306/dorm?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai问题三大批量学生导入时数据校验不通过。用EasyExcel导入时如果在Excel里填的数据格式不对比如手机号变成科学计数法或者学号为纯数字时前面有0被Excel自动省略掉导入后数据就错了。解决办法是在导入模板里把学号列设置为文本格式并在后端校验学号长度和格式。这个坑特别常见很多同学答辩演示导入功能时就会翻车。问题四在宿舍分配的结果里出现分配成功但学生无法查看到的情况。这个往往是因为分配完成之后没有把结果发布学生端的查询条件仍然只查状态已发布的记录。解决办法是在分配批次里增加一个发布状态切换管理员确认结果后手动点击发布发布后才允许学生查看。这个流程设计本身也符合真实的业务需求。4. 如何把项目做出差异化4.1 从管理系统升级到智慧校园坦白说每年毕业设计里叫宿舍管理系统的太多很多老师已经审美疲劳了。所以你这个题目给了一个很好的起点宿舍分配管理系统甚至后缀还带上了智慧校园和智能分配平台。要在答辩时体现智慧两个字我认为至少可以从三个方面去设计差异化。第一个是可视化大屏。宿舍管理后台的主页不要只放一个简单的表格而是做成一个数据大屏的布局左边是各楼栋入住率排行中间是今日入住/退宿动态右边是待办事项待审批调宿、待处理维修。大屏就是能直观体现平台价值的东西虽然实现不难但视觉效果直接拉满。第二个是主动式预警。比如超过一周未办理入住的新生、连续多次晚归的学生、某个房间用电异常如果接了数据源、低入住率楼栋的闲置预警。用定时任务Quartz或Spring自带的Scheduled每天跑一次把预警信息推送到管理员首页消息中心。这个设计思路很简单但是智能化的感觉一下就出来了。第三个是学生端自助服务。可以做一个学生的微信小程序或者H5页面推荐做H5就行省去小程序审核流程学生在手机上就能查自己的宿舍信息、扫码报修、申请调宿、查看公告。前后端分离的项目后端接口直接可以被手机端复用只需要再多写几个适合移动端展示的接口即可。这个扩展模块虽然工作量大一点但把它作为项目里的系统亮点去讲非常有说服力。4.2 代码质量与项目管理毕业设计论文里代码质量是评分的重头。有些同学的代码结构混乱所有业务逻辑全部堆在Controller里一个Controller上千行这种文章看到就头大。至少要做到Controller层只负责参数接收和结果返回Service层承载业务逻辑如果业务逻辑太复杂再抽一层Manager或者DomainService来承载领域逻辑。参数校验用Validated注解统一异常处理用RestControllerAdvice全局异常处理器日志用SLF4J打印在关键路径上。代码命名也要注意统一。类名用大驼峰方法名用小驼峰常量用大写下划线。不要出现aaa、bbb这种名字。单元测试如果能写几个就更好了特别是分配算法这块。你可以写一个匹配算法的单元测试构造一组学生和床位数据验证分配结果是否符合规则。这在答辩现场演示运行测试用例通过效果远超嘴上说说。4.3 配合论文与答辩毕设论文通常包含选题背景与意义、国内外研究现状、需求分析、系统设计、系统实现、系统测试、总结。要注意代码是一回事论文又是另一回事。很多同学代码写得很牛但论文里需求分析写成一堆空话这是非常吃亏的。我建议论文里的需求分析章节一定要把角色、用例图、业务流程图画清楚尤其是宿舍分配流程的泳道图。系统设计章节多贴E-R图和核心类图不要光贴代码。系统测试章节不要只写该系统运行稳定这种空话要列出具体的测试用例和测试结果比如输入10个学生和20个床位预期结果是什么实际结果是什么。答辩时很有可能老师会问这几个问题我提前给你列出来提前准备答案第一你的分配规则如果发生冲突怎么办比如同院系优先和作息时间优先同时存在时如何取舍答案就是权重设计权重高的规则优先。如果权重相同可以用一个优先级字段去打破平衡。第二这个系统如果用在真实的万人高校性能够吗不要慌你可以说针对大数据量做了分页查询引入了Redis缓存同时分配批次保证串行执行。就算没有真的做Redis也可以说在设计时预留了相关方案。第三你的系统安全怎么保证这就是重点讲JWT SpringSecurity 参数校验 统一异常处理。第四为什么这个分配结果是最优的这里要坦白说这是基于规则和贪心策略的最优解而不是全局最优解。如果要全局最优需要引入遗传算法或线性规划但那样的话复杂度会显著上升对于宿舍分配这种业务场景来说规则贪心已经足够满足需求。只要你真的熟悉自己的项目这些问题是完全能答上来的。怕的是代码不是你自己写的你连分配逻辑在哪一个类里都不清楚那答辩必挂。所以我还是建议哪怕项目的界面可以参照别人的设计代码也要自己一行一行敲出来这样出现问题你才知道去哪里找。5. 项目落地的环境搭建与运行5.1 开发环境准备下面这套环境是我自己常用的按这个来基本不会出问题。JDK版本JDK 8或JDK 11都可以。如果用的是SpringBoot 2.xJDK 8足够如果用的是SpringBoot 3.x那JDK必须17。毕业设计建议使用SpringBoot 2.7.x稳定且资料多。构建工具Maven 3.8。记得配置阿里云镜像否则拉依赖很可能慢到怀疑人生。数据库MySQL 5.7或8.0。MySQL 8.0在驱动类名、连接串上有一些变化如果你的连接串一直报错注意加一个cj。IDEIDEA旗舰版社区版凑合也能用但SpringBoot的插件支持没有旗舰版好建议优先旗舰版。前端工具Node.js 16npm或yarn都可以Vue CLI或Vite脚手架。如果你用IDEA新建SpringBoot项目刚开始可能会遇到Maven依赖一直下载不下来的问题直接去本地仓库看是不是缺少某些jar包大概率还是镜像源的问题。把settings.xml里的mirror改成阿里云然后重新reimport一下就好了。5.2 核心依赖与配置后端pom.xml的核心依赖大概长这样parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency dependency groupIdcom.alibaba/groupId artifactIdeasyexcel/artifactId version3.3.2/version /dependency /dependencies之所以推荐MyBatis-Plus而不是传统MyBatis是因为它在单表CRUD上省掉了大量手写XML映射还有非常方便的分页插件PaginationInnerInterceptor对毕设来说收益很大。application.yml里重点配置这几个东西spring: datasource: url: jdbc:mysql://localhost:3306/dorm?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 jwt: secret: yourSecretKey expire-days: 7这里有两个细节要说明一点。第一个是MyBatis-Plus的逻辑删除配置加上之后删除操作不会真的DELETE数据而是把deleted字段改为1这能保证入住记录可追溯。第二个是打印SQL日志开发阶段一定要开方便你在控制台看到每次执行的真实SQL排查问题时非常有用。5.3 两套环境下的启动步骤后端启动很简单配置好数据库连接后直接运行主类上的main方法就可以了。注意第一次启动会自动建表吗不会MyBatis-Plus没有自动建表能力需要你自己准备SQL脚本推荐用项目的数据库迁移工具比如直接用Navicat执行建表SQL。前端启动的步骤分两步先npm install安装依赖再npm run serve启动开发环境。如果npm install卡住或报错多半是镜像源问题把npm registry切换到淘宝镜像npm config set registry https://registry.npmmirror.com然后重新npm install基本就能解决。开发过程中前端和后端联调时建议在vue.config.js中配置代理避免直接面对跨域问题。也就是把前端的/api路径代理到后端的http://localhost:9090。module.exports { devServer: { proxy: { /api: { target: http://localhost:9090, changeOrigin: true } } } }5.4 演示数据与初始账号系统空跑起来其实看不出效果我建议你在初始化SQL脚本里直接塞入一些演示数据方便答辩时演示。初始化脚本可以分成三段第一段是基础字典数据比如导入院系表、楼栋表、房间表、床位表。模拟一栋楼、3个楼层、每层5个房间每个房间4张床位共60张床位。这个量级跑起来直观又不至于堆掉页面。第二段是用户数据创建管理员账号admin/admin123宿管老师账号dorm_admin/dorm123辅导员账号advisor/advisor123学生账号用student2024001/123456这样不同角色登录的演示效果能一步到位。第三段是模拟学生数据导入约40个学生覆盖不同院系、不同作息习惯、不同兴趣爱好和不同特殊情况需要下铺的学生、需要安静环境的学生。这些数据尽量多样性这样分配算法才能体现出匹配的效果。容易忽略的是分配完一批学生后要留几个空床位因为你要在答辩时现场演示调宿申请审批通过自动换床的完整流程如果前面都塞满了就没有可用的空床位来演示了。6. 部署与优化的思路补充毕设答辩后如果想让这个项目好看一点可以试着把它部署到服务器上这样演示时的效果完全不同。目前主流的部署方式是用Docker。第一步准备好后端构建产物。执行mvn clean package -DskipTests打出jar包。第二步写一个简单的DockerfileFROM openjdk:8-jre-alpine COPY target/dormitory-server.jar /app.jar ENTRYPOINT [java, -jar, /app.jar]第三步前端构建。执行npm run build生成dist目录然后用Nginx来托管。第四步如果你想把数据库也容器化可以写一个docker-compose.yml包含MySQL和Redis和后端服务和Nginx四个容器。虽然毕设不一定要求部署但如果你把它完整地跑起来并且在答辩时现场演示这个完成度直接甩开大部分同学。部署过程中容易踩的坑是服务器防火墙没放行端口、数据库数据没有初始化、跨域问题变成了代理未配置。建议本地先完全跑通再动手部署。7. 个人经验总结做了这么多年的项目我认为一个能让人满意的毕业设计最重要的不是代码量也不是用了多新的技术而是逻辑闭环。你的系统能不能讲出一个完整的故事谁来用、在什么场景下用、他遇到什么问题、你的系统如何解决、解决完如何反馈、数据如何沉淀。宿舍分配管理系统这个题目天然具备这个闭环所以它才会成为毕业设计里的常青树。如果你想拿高分一定要舍得在分配批次 规则匹配 人工复核这条主线上花时间把这里的逻辑写清楚、把边界情况处理好。一个能处理两个学生同时申请同一张床位的系统比一个界面华丽但功能单薄的系统评分高出不止一个档次。最后再分享一个小细节答辩前一定要抽时间把所有的演示数据和账号密码打印成一张A4纸放在手边。现场演示的时候当年你输入的账号记录找不到了或者分配结果没有提前准备好演示数据慌乱状态下很容易脑袋空空。提前把新生分配批次这组数据预先执行好把页面停留在一个即将发布的状态现场点一下发布整个演示的节奏就会非常顺畅。项目的价值往往就体现在这一个个细节里。