资讯动态

Spring Boot学生公寓管理系统实战:从数据库设计到并发权限避坑指南

发布时间:2026/9/9 6:14:19 来源:尧图企业网站定制
Springboot学生公寓管理系统这类项目我前后接触过好几茬。最早是给一个做智慧校园的朋友做技术评审后来陆续有学生拿来当毕业设计找我帮忙看代码。接触多了发现一个问题很多人不是写不出来而是没想清楚这东西到底要管哪些事结果数据库表建了三十多张功能互相打架源码看着热闹跑起来一团糟。如果你正在做或者打算做一个基于Spring Boot的学生公寓管理系统这篇内容应该能帮你省下不少弯路。我整理了完整的设计思路、表结构、核心接口和部署踩坑记录会告诉你每一步为什么这么设计也会把最容易翻车的并发分配、权限控制、跨域处理这些点拆开讲。不管你是拿来当毕业设计、课程设计还是想在公司内部做一套公寓管理工具照着这个思路走至少能少改三十次表结构。1. 先别急着写代码把公寓管理到底管什么盘清楚很多同学打开IDEA敲代码之前根本没有完整梳理过业务。看网上开源项目有个学生表就往里塞宿舍ID有个宿舍表就当所有寝室结构一样结果验收的时候甲方要求来访登记要能查历史退宿之后床位数要对得上你才发现原先的模型根本撑不住。1.1 传统手工管理模式下最痛的是哪几个环节我们逐个场景代入一下。宿管阿姨的登记本上住着哪个学生、哪个床位、电话多少、住哪栋哪层全靠手写。新学期迎新几百个新生入住一个一个手抄宿管大爷眼睛都快看花。半夜学生没带卡回不了楼登记本上找名字要翻三分钟。更麻烦的是月底水电费统计宿管拿着抄表本和入住名单对不对上半天根本算不清。再往后走公寓的椅子坏了、灯管不亮了学生找辅导员辅导员通知后勤后勤找维修工整个过程没有任何流转记录修没修、谁修的、修了几次全靠人脑记忆。外来回访客人进楼证件登记完全凭自觉出了安全问题根本追溯不到人。如果你把上述流程抽象成后端需求就会发现它其实涵盖了很多典型模块人员管理、房间状态管理、流程性事务处理、权限区分、甚至费用计算。用Spring Boot来做恰好合适因为它生态成熟做增删改查非常快应付这种中小型系统绰绰有余。1.2 使用者的角色到底有几个每种角色关心什么做管理系统第一件事就是把“谁在用”定清楚。学生公寓管理系统通常有四种角色很多新手一开始只设计了管理员和学生这绝对不够。系统管理员管的是全局比如楼栋录入、宿舍类型维护、账号分配、公告发布、数据统计他看到的是宏观数字。宿管员管的是具体某栋楼负责入住和退宿办理、晚间查寝信息录入、访客登记、报修单审核他关心的是床位数据准不准、今天哪些房间有报修、月底水电费怎么分摊。学生是日常使用的最大群体需要能提交报修、查看账单、登记访客信息、接收公告偶尔还能查一下自己住哪间宿舍舍友是谁。还有一类是辅导员或院系老师虽然不直接操作后台但需要查看学生入住分布、晚归记录、违规用电记录走的是“只读审核”权限。角色模型设计清楚之后后端权限模块才能有的放矢否则后面做Shiro或者Spring Security的时候你根本不知道接口该给谁放行。1.3 功能边界必修项和加分项划分我综合自己接过几个实际项目的经验整理了一张功能清单。类型功能模块说明核心必修楼栋与房间管理维护楼栋、楼层、房间号、房间类型、床位、朝向等基础档案核心必修学生入住退宿流程支持入住登记、分配宿舍、换宿、退宿记录完整历史核心必修宿管签到/查寝管理支持宿管按楼栋录入晚归、未归、查寝异常情况核心必修报修工单流转学生提交、宿管审核、维修工接单、完成回执与评价核心必修访客及大件物品出入登记记录访客姓名、证件号码、被访人、进出时间核心必修公告通知管理员发布学生列表浏览核心必修水电费与住宿费账单定期生成账单学生查询缴纳状态加分拓展门禁数据同步对接硬件或模拟门禁刷卡记录加分拓展可视化统计入住率、楼栋空床位、性别分布图表加分拓展学生晚归推送对接微信/邮件通知辅导员强烈建议第一版只做核心必修项。很多做毕设的同学失败就失败在功能膨胀上宿舍、学生、座位、卫生检查、活动报名一把抓单个模块都做成了半吊子数据库关系乱成一团。2. 技术选型不是越新越好我用这套组合的原因2.1 为什么Spring Boot而不是SSH或者Spring Cloud学生公寓管理系统属于典型的中小型单体应用用Spring Boot来承载再合适不过。Spring Boot最大的优势是自动配置和约定优于配置你不需要自己拼装一堆XML配置文件一个Maven工程拉下来就能启动。同时它对内嵌Tomcat的支持非常友好打包成可执行Jar包扔到Linux服务器就能跑部署成本极低。有些同学觉得学Spring Cloud微服务很高级非要拆成用户服务、房间服务、工单服务项目弄成三四套工程。我只能说这属于过度设计。公寓管理系统的并发量可能一天也就几百个请求单体应用完全能扛住而且调试和部署都很轻松。真正搞微服务之后你还要处理服务注册、负载均衡、分布式事务对毕设而言纯粹是给自己挖坑。2.2 ORM框架为什么要选MyBatis-Plus而不是纯MyBatis这里我非常推荐MyBatis-Plus。原生MyBatis写简单CRUD确实也比较方便但你要写很多Mapper接口和XML映射文件尤其是分页查询这种高频操作手写复杂SQL非常繁琐。MyBatis-Plus在MyBatis之上提供了BaseMapper用户表、宿舍表这些实体直接继承不需要写一行XML就能完成基本增删改查分页插件也是现成的。我目前的推荐组合是Java 8 或 11别盲目用Java 17或21原因后面讲Spring Boot 2.7.xMyBatis-Plus 3.5.xMySQL 5.7 或 8.0Redis可选主要做验证码缓存和接口防重复提交Knife4j 用于接口文档管理JWT 拦截器做认证授权不引入Spring Security因为角色简单自己控制更清晰前端根据情况选择如果走前后端分离就使用Vue 2 Element-UI如果用模板引擎则Thymeleaf这套选型的中庸之处在于它既不会让项目看起来特别简单也不会给自己增加太多新框架的学习成本。2.3 Spring Boot版本的选择暗藏一个大坑这里我要专门提醒热词里有一条是“springboot版本太高”这确实是很多人爆炸的点。Spring Boot 3.x发布于2022年底本身没有问题但它基于Jakarta EE规范过去用javax.servlet、javax.validation这一套包名全部改成了jakarta.*意味着大量老牌第三方组件的兼容版本都要跟着升级。很多同学从Gitee上拉下来一个老项目发现pom里写的是Spring Boot 2.7自己手动改成3.1之后Tomcat容器换了JSTL标签库也不兼容页面直接500。我的建议是如果项目涉及毕业设计而且你主要用MyBatis-Plus和通用工具类Spring Boot 2.7.x是更稳妥的选择教程多、问题答案多、时间也不会浪费在配置地狱里。2.4 业务架构上如何分层源码的组织架构也值得说明一下。很多初学者把所有代码写进Controller一个方法两百行这是在给自己埋雷。我习惯的分层方式是Controller层只负责参数接收、调用Service、返回统一结果Service层处理真正的业务规则具备事务边界Mapper层数据访问杜绝业务代码侵入entity包与数据库表对应的实体dto包接收前端请求相关的传输对象vo包返回给前端的数据视图对象隐藏敏感字段config包框架配置类common包全局异常处理、统一返回封装、JWT工具、常量定义注意一个容易犯的问题是实体类直接返回给前端。比如学生实体内包含手机号、身份证号如果直接序列化返回接口一旦暴露个人隐私全部泄漏。这种需求场景下后端一定要写VO来做字段裁剪。这个习惯越早养成越好上班以后没人会给你机会把数据库表直接返给前端。3. 数据库设计直接决定后面要改多少代码学生公寓管理系统的数据库设计是整个项目的灵魂。我常说宿舍和学生的关系如果设计错了后面每一个功能都会别扭。别再像有些半吊子教程那样直接在student表里搞一个room_id字段入住退宿全靠更新这个字段那么你根本回答不了“上学期这个学生住哪间”的问题。3.1 核心表拆解档案数据和流转数据要分开我们来梳理一下这个系统到底需要哪些核心表。第一类是基础档案表楼栋表building、宿舍表dormitory、床位表bed、学生档案表student、账号表sys_user、公告表notice。这一层是静态数据数据量相对稳定维护频率不高。第二类是业务流转表入住记录表live_record、报修工单表repair_order、水电费账单表fee_bill、访客记录表visitor_record、查寝记录表check_record。这一层是动态数据记录了“谁在什么时间干了什么”。第三类是关联配置表比如角色表role、菜单权限表menu、角色菜单关系表如果要用比较完整的权限管理可以考虑RBAC模型。很多新手会把student和live_record混淆。其实student表里存的是学生自然信息学号、姓名、性别、学院、班级、联系方式而live_record表存的是入住行为某个学生在某段时间住进了哪个宿舍哪个床位。业务上一个学生可能住了四年中间换过宿舍如果没有live_record表他的住宿历史就丢了。3.2 宿舍-床位-入住记录之间的关系该怎么落宿舍表设计上有些字段需要提前想清楚。比如一个房间是几人间是否混合住宿不同性别混住通常不允许靠字段约束是否为公共卫生间是否有空调。这些看似业务上的细节将来都会变成筛选条件。床位表bed可以单独建也可以不建。如果宿舍类型都是标准四人间、六人间不涉及随机分配床位很多项目选择在入住时为每个宿舍动态编号床位。但我建议还是建立bed表因为实际公寓管理系统中很多床位会处于故障、维修、临时封闭等状态不单独存床位数据根本表达不了这种精细状态。并且宿舍分配时直接落到床位上后续换宿、退宿追踪会更准确。关键关联设计如下dormitory表有building_id外键room_no房间号bed_count床位总数bed_used当前已用床位数gender_type枚举男寝/女寝bed表中dorm_id指宿舍bed_no是床号status字段表示可用/占用/维修live_record表记录student_id、dorm_id、bed_id、check_in_time、check_out_timecheck_out_time为空代表当前正在住为了确保一个学生同一时间不能有两处有效住宿可以在live_record表中添加一个条件唯一索引专门约束“未退宿数据的学号唯一”。MySQL支持函数索引或者虚拟列简单点可以做退宿标记字段 UNIQUE KEY uk_student_active (student_id, is_active) 这样在数据库层面就成了最后防线比代码判重可靠得多。3.3 流水表最容易忽略的几个字段预算充足的设计总是提前把“审计”字段留好。比如每个表都安排create_time、update_time两个时间字段新增记录时自动填充。MyBatis-Plus可以用MetaObjectHandler实现也可以直接在实体类上使用TableField(fill FieldFill.INSERT)。还有一个常见错误是使用浮点数存储水电费金额。金融计算之后很容易出现小数点精度问题比如0.10.2不等于0.3。账单表里面金额字段请使用DECIMAL(10,2)也就是定点数。这个坑我在代码评审的时候至少看到十次以上大家引以为戒。报修单表和访客登记表里有个外键概念容易搞乱。报修单的提交人可能是学生处理人是后勤维修师傅而维修师傅也可能单独作为一张员工表存在。如果不想把员工系统做太复杂可以做一张简单的handler表或者直接用账号表的用户ID来取代。访客登记表则需要记录访问的宿舍楼栋和宿舍房间号但和student表的关系要注意因为这些年来宿舍变迁访客登记时要以当时地址为准最好冗余一个dorm_info文本字段防止学生已经毕业导致关联数据查不出来。3.4 状态字段别用字符串随缘填枚举化之后少很多Bug我在项目里习惯把状态相关的字段全部设计成字符类型但约束值范围。例如bed.status只有0可用、1占用、2维修、3停用几个取值repair_order.status有0待审核、1已派单、2维修中、3待确认、4已完成、5已关闭。这些取值在Java层用枚举类维护数据库中写注释说明配合索引可以做状态筛选。你要是不好理解打个比方说状态机正如洗衣机面板上的模式你让洗衣模式保持“标准、轻柔、快洗”几个选项使用洗衣机的用户永远不会想到去拧一个不存在的“烘干加热”按钮。把状态约束住前端的下拉选项、后端逻辑判断、数据库统计三处就不会出现鸡同鸭讲。4. 后端实现时的核心场景与代码落地当我评估一个学生公寓管理系统的源码好坏时不会只看它能不能跑通登录注册而是重点看三个场景权限机制、入住分配、以及状态怎么流转。4.1 认证授权方案为什么我用JWT而不是Session做管理系统的登录目前大家普遍采用JWT。JWT的原理可以简单理解成用户登录成功后后端根据用户身份信息生成一段带签名的令牌字符串前端携带这个令牌访问受保护接口后端验签通过即放行。它天然适合前后端分离项目多终端登录也比较自然。流程用户提交用户名密码后端校验通过后返回token前端存储token每次请求头自动带上Authorization: Bearer token后端的拦截器解析token并获取当前用户角色判断是否有权限与Session的区别在于Session数据存在服务端内存中适用于同一个Servlet容器内的请求一旦需要做多节点部署或者小程序、App共用登录态就非常麻烦。JWT自包含令牌服务端不需要保留会话状态但要注意token过期逻辑不然会过期无感。学生公寓管理系统的角色不多因此可以按角色写一个判断注解哪些接口允许宿管、哪些只允许管理员。我自己的简便做法是使用一个自定义注解RequireRole在HandlerInterceptor中判断用户角色是否在允许集合中。如果角色多、权限细就可以考虑接入Spring Security或Sa-Token的权限框架。4.2 最核心的批量入住场景怎么写才不至于半夜被电话吵醒宿舍入住的分配功能隐藏着一个典型并发问题。每年开学会有大批新生同时办理入住假设现在只剩下最后一个床位两个宿管员同时办理分配到最后一间两个请求同时查到床位可用同时执行更新就会导致一个床位被分给两个学生。我先给你看一个错误示例// 错误示例先查再改不锁资源 Bed bed bedMapper.selectById(bedId); if (0.equals(bed.getStatus())) { bed.setStatus(1); bedMapper.updateById(bed); liveRecordService.insert(studentId, bed.getDormId(), bedId); }这样在高并发下两个线程看到的都是status0于是都执行了更新。正确做法是在事务内部加锁行锁定该床位后再判断状态保证每个时刻只有一个线程在操作该床位。Transactional(rollbackFor Exception.class) public void checkIn(CheckInRequest request) { // 1. 查询学生档案是否存在且未注销 Student student studentMapper.selectById(request.getStudentId()); if (student null) { throw new BusinessException(学生不存在); } // 2. 检查学生是否已有有效入住记录 LambdaQueryWrapperLiveRecord activeWrapper Wrappers.lambdaQuery(); activeWrapper.eq(LiveRecord::getStudentId, request.getStudentId()) .eq(LiveRecord::getIsActive, 1); if (liveRecordMapper.selectCount(activeWrapper) 0) { throw new BusinessException(该学生当前仍有有效入住记录请先办理退宿); } // 3. 锁定床位行防止并发抢占 Bed bed bedMapper.selectBedByIdForUpdate(request.getBedId()); if (bed null || !0.equals(bed.getStatus())) { throw new BusinessException(床位状态不可用); } // 4. 更新床位状态 bed.setStatus(1); bedMapper.updateById(bed); // 5. 插入入住记录 LiveRecord record new LiveRecord(); record.setStudentId(request.getStudentId()); record.setDormId(bed.getDormId()); record.setBedId(bed.getId()); record.setCheckInTime(new Date()); record.setIsActive(1); liveRecordMapper.insert(record); // 6. 同步宿舍已用床位数量并判断是否已满 dormitoryMapper.increaseUsedCount(bed.getDormId()); }注意selectBedByIdForUpdate需要自定义SQL本质是执行SELECT ... FOR UPDATE这样MySQL Innodb引擎会给该行加排他锁。本事务未提交前其他事务必须等待。这是解决并发分配的底层保障。再配合唯一索引数据库层兜底这个问题才算真正稳住。这样的代码写出来拿来当面试亮点也是完全能打的。4.3 退宿舍和换宿舍容易忽略的逻辑退宿不是把床位状态改成0就结束了。退宿要同时完成以下动作从业务关系上关闭这张入住单把live_record的check_out_time置为当前时间将is_active置为0检查学生名下有没有未结清的水电账单或未完成报修单如果有则系统拦截或提示床位状态改为0可用宿舍usedCount减一换宿可以看成是“退宿入住”的组合操作不过为了保留历史链路需要一口气包在一个Transactional里先把旧入住记录关闭再清理新床位的占用状态再插入新的live_record。如果第一步成功、第二步失败就必须整体回滚所以千万别在两个事务里分开写。如果你想要文档里把这些说明写清楚我会在配套文档的“核心流程设计”部分单列一张状态流转表。4.4 报修工单怎么做流转闭环报修模块很多人做成最简单的“学生提交管理员修改状态”就完事这缺少了流程感。我建议至少包含五个阶段学生提交报修单附上文字描述和图片地址宿管审核是否同意维修如果是人为损坏则驳回并备注原因维修人员接单状态转为维修中维修完成填完处理结果状态待确认学生确认维修完成工单关闭每张报修单要有创建时间、修改时间、记录操作人。再往后可以扩展“维修评价”但第一版不必做。前端只要一个按钮就能流转后端通过updateStatus接口做状态机校验防止学生跳过宿管直接改成已处理。状态机校验这部分本质是把“流程状态允许的下一步变迁”写成一个Map状态变迁不合法直接抛异常。4.5 水电账单到底怎么生成公寓的水电费往往一个月一个周期。管理处每月导入一次各房间表读数系统算出本期用量和金额给每位在住学生生成一条费用记录。这里需要注意产生账单的时间点应与live_record表有效入住记录关联若月中入住可以按入住日期计算分摊。我在源代码里默认实现了两种账单模式按宿舍总费用平摊到每个在住学生按房间独立计费缴费责任到室长或全体住宿生哪种更合理要看不同学校的管理规定所以代码里把账单策略类单独放置方便调整。金额一律用BigDecimal计算账单状态从待缴、已缴费、已开票逐步推进。5. 前端和接口设计直接影响项目好不好演示很多人写后端完全不考虑前端调用是否方便结果自己做演示页面的时候越写越难受。我建议从开发第一天就约定好统一返回结构。5.1 后端统一返回体该怎么设计一个典型的前后端分离项目统一响应类通常包含code、message、data三个字段。例如{ code: 200, message: 操作成功, data: { list: [], total: 10 } }后端创建一个Result类泛型封装成功、失败、分页全局异常处理器兜住所有业务异常确保传给前端的错误信息是中文可读的。JWT过期时返回401状态码前端全局拦截之后跳到登录页这才是完整的处理链路。接口文档使用Knife4j生成相比老牌SwaggerKnife4j的界面更清爽接口分组和调试也顺手得多。下载源码之后只要在配置中开启Knife4j相关页面就可以在本地直接查看和调试每个接口了。5.2 前端宿舍分配页面怎么避免反人类宿舍分配页面是所有前端页面里最容易做烂的。一个合理的分配界面应该具备两级选择先筛选楼栋和性别再展示空闲宿舍及床位状态。床位的可用状态用不同颜色标识绿色代表可用、灰色代表占用、红色代表维修。宿管点击某个空床位后右侧回显学生信息二次确认完成分配。如果前端选择直接在后端传宿舍ID而不限制性别系统就会出现男生入女寝的严重业务事故。因此后端接口一定要根据当前登录宿管管辖楼栋判断也要在宿舍数据校验时同时校验性别类型。前后端双重校验不是为了给谁添麻烦而是为了真实业务安全。5.3 常用接口清单预览我在源码中整理过一份典型的接口清单可以对照检查自己的项目是否完整POST /api/auth/login登录POST /api/auth/logout退出GET /api/dorm/page宿舍分页查询支持楼栋、状态、房间号条件POST /api/dorm新增宿舍PUT /api/dorm修改宿舍信息GET /api/bed/list?dormIdxx查询某宿舍所有床位POST /api/live/checkIn办理入住POST /api/live/checkOut办理退宿POST /api/live/changeRoom换宿GET /api/repair/page报修单分页POST /api/repair提交报修PUT /api/repair/handle处理报修单POST /api/visitor访客登记GET /api/visitor/page访客查询GET /api/fee/page费用流水POST /api/fee/order生成账单GET /api/notice/list公告列表接口命名尽量使用REST语义但考虑到国内项目的实际成熟度POST配合具体动作接口也是常见做法关键是风格要统一不要一个方法叫/getUser另一个叫deleteStudentById。6. 安全与数据保护答辩时最容易被追问的点学生公寓管理系统涉及大量学生隐私数据你在源码里如果手机号明文返回、身份证号到处打印、密码在数据库里直接存原文那面试官或老师肯定要抓住追问。这块做好了反而能成为加分项。6.1 密码不能明文存储原则非常简单用户注册或初始密码生成后不直接以明文形式入库而应进行不可逆加密。Spring Security中自带的BCryptPasswordEncoder是常见做法但如果不引入Spring Security也可以使用jBCrypt库。比如一个初始密码是123456加密后存到数据库里的结果是 $2a$10$NecpS4S...即使数据库泄露攻击者也不能直接拿到明文密码。登录校验时也是拿用户输入明文密码去和密文比对。这部分做好无论毕设还是生产项目都是基本素养。需要提醒一点MD5不是不能用但一定要加盐。不加盐的MD5在彩虹表面前基本等于裸奔。我仍然推荐BCrypt它在生成时自带了随机盐性价比最高。6.2 接口层应该拦截和防什么哪怕是一个毕设基本安全规则也值得做。首先是统一统一鉴权拦截器放行登录、验证码、Knife4j资源等公开接口其余所有/api/**请求都必须校验token。其次是防SQL注入。使用MyBatis-Plus的LambdaQueryWrapper基本能规避大部分拼接SQL注入风险但在编写原生XML SQL时绝对不要使用${}接收用户输入必须一律使用#{}参数占位。然后是防XSS虽然前后端分离项目中React或Vue本身有默认转义但演示系统也要关注用户留言、公告内容中的

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价