
很多同学毕设选“宿舍管理系统”源码下载下来却发现不知道从哪讲起、哪段代码能突出亮点、被老师一问就卡壳。这个题目看着简单真正动手才发现坑不少。我前后带过几届学弟学妹做过这套系统也帮人远程调过不少跑不起来的 SSM 和 Django 代码这篇就把从需求分析到最终答辩的全流程经验一次说清楚。无论你手里是一份独立开发的源码还是网上找的“源码LW调试文档”整套资料这篇都能帮你把它变成真正能讲明白、能改能扩展的项目。1. 这个系统到底解决什么问题宿舍管理的业务痛点和需求边界1.1 宿舍管理难在哪里宿舍管理的核心不是“登记宿舍号”而是大量日常事务的流转。以前靠宿管手写台账的时候最头疼的是三件事查某个学生住哪间房要翻半小时纸质表学生报修了不知道师傅上门没有月底核算水电费全靠Excel人工填。换成管理系统之后本质是要把这些离散的事务变成有状态、有记录、可追溯的流程。所以做需求分析时不要只盯着“学生表”“宿舍表”两个对象至少要把下面几条业务流程理清楚入住流程新生报到或转宿时先查空床位再分配宿舍同时生成入住记录。退宿流程清空床位、结算水电、更新宿舍已住人数。报修流程学生提交报修单宿管审核派单维修工接单回填结果学生确认。水电费流程定期抄表生成账单学生缴费记录缴费状态。访客流程访客登记离校销记保障楼栋安全。这些流程看起来简单但它们之间是有状态依赖的。比如退宿时发现水电没结清就不能释放床位报修单处于“派单中”时学生不能重复提交。系统真正的价值就是把这种状态约束落到代码和数据库里。1.2 SSM和Django怎么分工很多人在一个系统里同时看到 SSM 和 Django 会困惑这不是两套技术栈吗其实这正是这个项目的特色也是它的加分点。合理的设计是让两套框架各自负责擅长的部分SSMSpring SpringMVC MyBatis负责管理端和核心业务也就是管理员、宿管用的后台管理平台包含宿舍分配、学生管理、报修审批、数据统计这些重业务逻辑的功能。Django 负责学生端和对外接口比如学生微信端或移动端的 API 服务包含登录、查宿舍、提交报修、查账单这些轻量级高频操作。这样分工的动机很简单后端管理系统强调事务控制、权限隔离和复杂查询这是 Spring 生态的老本行学生端接口更看重快速开发、清晰的数据序列化和灵活的身份认证Django REST Framework 在这点上比 SSM 写 Controller 再手工转 JSON 快得多。我在实际带项目时还遇到过一种情况有些同学拿到的标题里含“SSMDjango”但下载的源码其实只有 SSM 一套。这种也不用慌可以保留 SSM 作为唯一后端把 Django 的角色改成一个数据可视化模块或爬虫只要在文档里讲清楚“为什么额外引入 Django”就成了设计亮点而不是结构冗余。2. 技术架构设计双技术栈如何整合到一个系统里2.1 总体架构与分层整个系统最常见的分层是表现层管理端是 JSP 或 Thymeleaf 页面学生端是微信小程序或 H5通过 HTTP 调用后端接口。控制层SSM 的 SpringMVC ControllerDjango 的 View / DRF 的 APIView。业务层SSM 用 Service 接口 实现类Django 用 service 层或者直接在 model 里写业务方法。持久层MyBatis 的 Mapper 接口 XML 映射Django 的 ORM Model。数据库两边共用同一个 MySQL 实例按业务前缀区分表。设计时一定要明确数据库是两套框架的唯一交集两边都只操作自己的表不要互相侵入对方的模块。比如学生端只通过 Django 写 student_profile、repair_order 这些表管理端只通过 SSM 写 dormitory、user、check_record 这些表。如果某张表两边都要操作就约定一方只读、一方只写否则很容易出现事务边界混乱。2.2 两边如何对接接口、数据库与登录状态双框架整合最常踩的坑是登录状态不互通。SSM 管理端习惯用 HttpSession 保存登录状态Django 端如果也用 session两边的 session id 互相不认。我的做法是SSM 管理端继续用 Session 拦截器校验保持管理后台的传统实现方式课上也好解释“SpringMVC 拦截器如何实现登录控制”。Django 学生端改用 JWTJSON Web Token登录接口签发 token之后每次请求在 Authorization 头里带上 token由 Django 端的中间件统一校验。这样两套框架的登录态完全隔离互不干扰职责清晰。数据库层面呢两边的用户信息如果都存 user 表会冲突我会拆成 sys_user系统用户管理员和宿管和 student学生学生表里的账号密码字段由 Django 端写入和校验SSM 端需要展示学生信息时只读联查。2.3 环境与工程结构如果你打算自己从头搭建参考下面的工程结构dormitory-system/ ├── dorm-admin/ # SSM 管理端 │ ├── src/main/java │ │ ├── com.dorm.controller │ │ ├── com.dorm.service │ │ ├── com.dorm.mapper │ │ └── com.dorm.entity │ ├── src/main/resources │ │ ├── spring-context.xml │ │ ├── spring-mvc.xml │ │ ├── spring-mybatis.xml │ │ └── mapper/*.xml │ └── src/main/webapp/WEB-INF ├── dorm-student/ # Django 学生端 │ ├── manage.py │ ├── dorm_uauth/ # 认证 app │ ├── repair/ # 报修 app │ ├── billing/ # 账单 app │ └── utils/ └── docs/ # 论文 调试文档Maven 管理 SSM 依赖时重点关注 spring-webmvc、mybatis、mybatis-spring、mysql-connector-java、druid 连接池Java 版本建议 8 或 11太高会出现 Tomcat 版本兼容问题。Django 端我用 3.2 LTS配合djangorestframework、pyjwt、drf-yasg接口文档这几个包足够覆盖整个学生端。3. 数据库设计与核心表结构宿舍分配的状态机是关键3.1 核心表设计宿舍管理系统的表不需要太多但每张表都承担明确职责。我常用的核心表如下表名关键字段说明dorm_buildingbuilding_id, name, manager_name楼栋信息dorm_roomroom_id, building_id, room_no, bed_count, used_beds, status房间信息status 标记状态studentstudent_no, name, gender, college, class_name, phone学生基本信息不含宿舍外键check_in_recordrecord_id, student_no, room_id, bed_no, check_in_time, status入住/退宿流水repair_orderorder_id, student_no, room_id, type, description, status, handler报修工单billbill_id, student_no, room_id, bill_type, amount, period, status水电等账单sys_useruser_id, username, password, role管理员/宿管账号noticenotice_id, title, content, publish_time公告信息一个常见的设计误区是把 dorm_room 里加一个 student_no 字段直接表示“谁住这里”。这在一人一房的情境下勉强能用但在宿舍场景里一间房可能住 2 人、4 人、6 人必须通过 check_in_record 这张流水表来维护“房间和学生的当前关系”。我在带项目时反复强调有人住的字段永远用状态字段或关联表不要用“迁移后残留”的单个字段硬撑。3.2 宿舍分配状态机的实现宿舍分配是整个系统的业务核心也是一张状态图能讲清楚的部分。房间状态我设计为空闲0床位数未满可以继续分配。已满1已住人数等于床位数不可分配。维护2房间处于维修中暂停分配。床位状态细化到每个床位空闲、已分配、维修中。分配学生时事务里做三件事查可用房间和床位更新床位状态生成入住流水。这三件事必须在同一个数据库事务里完成否则会出现“流水生成但床位没改”的数据不一致。从 MyBatis 的实现角度Service 方法上直接加Transactional注意抛出的异常要是RuntimeException才能触发回滚。Spring 默认只对运行时异常回滚检查异常不会自动回滚这是毕设代码里最常见的问题。3.3 水电、报修等扩展业务的数据建模报修单是一个经典的“状态流转”业务建议用一个 status 字段标识待审核(0) - 已派单(1) - 处理中(2) - 已完成(3) - 已确认(4)还可以加一个cancel状态让学生撤销但要限定只有“待审核”状态才能撤销。在 Django 端实现时用 choices 定义状态值每次更新都用 QuerySet 的update()方法并结合条件过滤比先取出 Python 对象再改字段更安全。比如RepairOrder.objects.filter( order_idorder_id, statusRepairOrder.Status.WAIT ).update(statusRepairOrder.Status.CANCEL)这样能避免并发情况下两个人同时改同一单的状态。账单数据建议按月生成比如每月 1 号跑一个定时任务或由管理员在 SSM 端点击“生成当月账单”根据宿舍房间上月和本月的水电表读数差值乘以单价得到金额写进 bill 表学生端通过 Django 接口查到未缴账单缴费后更新 status。4. 代码实现中值得记录的细节与踩坑4.1 MyBatis 动态SQL与复杂查询SSM 端很多功能都是“按条件搜索列表”这种需求用 MyBatis 的动态 SQL 特别合适。比如学生按姓名、学院、宿舍楼组合查询select idselectStudentByCondition resultTypecom.dorm.entity.StudentVo SELECT s.student_no, s.name, s.college, r.building_id, r.room_no FROM student s LEFT JOIN check_in_record c ON s.student_no c.student_no AND c.status 1 LEFT JOIN dorm_room r ON c.room_id r.room_id where if testname ! null and name ! AND s.name LIKE CONCAT(%, #{name}, %) /if if testcollege ! null and college ! AND s.college #{college} /if if testbuildingId ! null AND r.building_id #{buildingId} /if /where ORDER BY s.student_no /select注意LEFT JOIN的写法它需要关联当前有效的入住记录c.status 1而不是直接 join student 和 room 两张表。如果你用 inner join 或者不带状态条件退宿过的学生就会查到历史房间信息。这个坑我在调试别人代码时出现过不止一次。4.2 Django ORM 的常用操作与删除对象Django 端也要处理不少数据操作。学生端最常见的几个操作模式查询当前学生的未缴账单Bill.objects.filter(student_norequest.user, status__in[0, 1]).order_by(-period)分页DRF 的分页类配置PageNumberPagination每页默认 10 条。删除对象比如退宿申请审批通过后删除对应的床位占位记录用CheckInRecord.objects.filter(record_idid).delete()。说个细节.delete()是有返回值的返回一个(total_deleted, dict)元组里面是每个表删了几行。如果你写了result queryset.delete()拿到的不是行数而是元组别搞混。还有一点Django 的删除默认是软删除吗不是它是物理删除。所以要养成习惯涉及学生历史流水的数据千万不要物理删除应该加一个is_active字段用update(is_activeFalse)做“逻辑删除”。我见过有同学把入住记录直接 delete 掉查历史入住情况的时候一片空白最后还是从备份里恢复的。4.3 并发抢房与数据一致性宿舍分配在新生报到季会有真实的高并发场景多个同学同时抢同一间房的最后一个床位。数据库层面如果只做“先查床位数再 update”必然出现超卖。解决办法有两个一是乐观锁。在 dorm_room 表加version字段更新时带上版本号update iddecreaseUsedBeds UPDATE dorm_room SET used_beds used_beds 1, version version 1 WHERE room_id #{roomId} AND version #{version} AND used_beds bed_count /update如果受影响行数为 0说明房间被别人抢了事务回滚并提示“该房间已满”。这是面试时极容易问到的点也是真正值得写进论文的设计。二是数据库行锁在 MySQL 的 InnoDB 引擎下用SELECT ... FOR UPDATE锁住房间行再执行更新。行锁实现直观但并发性能略差。宿舍分配的并发量远没有秒杀那么高两种方式都够用我一般推荐乐观锁因为它不依赖长事务。4.4 两套系统的时间与状态同步Django 端和 SSM 端如果部署在同一台机器上绕不开时区问题。MySQL 连接串里设置serverTimezoneAsia/ShanghaiDjango 的settings.py里设置TIME_ZONE Asia/Shanghai同时USE_TZ True后存进数据库的是 UTC 时间。如果你发现学生端展示的“报修时间”比实际慢了 8 小时八成就是时区设置不一致。建议两边的插入时间都直接取数据库当前时间NOW()业务侧不要自己拼时间字符串统一以数据库时间为准。5. 权限控制与接口安全三种角色怎么管5.1 SSM端拦截器与Django端中间件的权限校验宿舍管理系统的用户角色通常有三种管理员、宿管、学生。权限设计上我建议走“角色-菜单-操作”的轻量 RBAC管理员拥有全部菜单可以分配宿舍、管理楼栋、查看报表。宿管只管理自己负责的楼栋可以处理报修、录入水电读数、发布公告。学生只看到自己的宿舍、报修单和账单。SSM 端实现权限控制最经典的方式是自定义拦截器继承HandlerInterceptorAdapter在preHandle里判断 Session 中的登录用户以及访问路径的角色要求public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { SysUser user (SysUser) request.getSession().getAttribute(loginUser); if (user null) { response.sendRedirect(request.getContextPath() /login); return false; } // 检查角色和路径权限 if (user.getRole() 1 !hasPermission(request.getRequestURI())) { response.setStatus(403); return false; } return true; }在spring-mvc.xml里用mvc:interceptors配置拦截路径/admin/**必须登录/admin/super/**必须管理员存在跨角色需求时再细化。Django 端用 DRF 的话直接对视图加IsAuthenticated权限类再配合自定义IsStudent这类权限类保证学生只能访问自己的数据。CommonMeta 层再做一层“行级权限”控制比如报修列表强制过滤student_norequest.user防止越权看别人的报修单。5.2 Token、密码存储与日志记录学生端用 JWT 后要特别注意 token 的过期时间。我给毕设项目用的是 2 小时过期用户长期使用时改用一个 7 天的 refresh token 或在密码中增加 remember 逻辑。Django 里校验 token 的中间件可以这样写class JWTAuthMiddleware: def __init__(self, get_response): self.get_response get_response def __call__(self, request): auth request.headers.get(Authorization) if auth and auth.startswith(Bearer ): token auth.split( )[1] payload verify_jwt_token(token) if payload: request.user_id payload[user_id] return self.get_response(request)注意Django 自带request.user在 REST Framework 里会被 DRF 的认证机制覆盖所以不要在普通中间件里强行给request.user赋值用一个自定义属性request.user_id更安全。密码存储方面SSM 端用BCryptPasswordEncoder或spring-security-crypto里的BCrypt无论如何不要用 MD5 明文存。Django 的django.contrib.auth.hashers自带 PBKDF2 加密直接用make_password和check_password就行。论文里写清楚“密码不可逆加密存储”是很稳妥的技术主张。日志方面SSM 配置 Logback 输出到文件Django 用 logging 配置记录请求方法、路径、状态码虽然不影响功能但可以应对“如何证明你的系统有安全意识”这类提问。6. 答辩讲解与功能演示亮点怎么讲追问怎么答6.1 演示流程设计答辩时最怕的不是不知道系统怎么用而是从头到尾没有章法把最有含金量的功能一笔带过。我建议按下面的顺序演示先讲登录展示管理员、宿管、学生三种角色的差异证明你做了权限控制。演示宿舍分配选一个房间分配两位学生进去用学生的视角登录能看到自己的宿舍和床位信息。演示报修全流程学生提交报修 - 宿管派单 - 维修工处理完成 - 学生确认。重点切换到数据库或日志展示状态变化证明你的状态流程清晰。演示报表统计按楼栋统计入住率、按学院统计住宿人数。这个功能建议用 ECharts 画柱状图视觉效果好答辩很加分。回到代码层面展示一两个亮点比如乐观锁的 update 语句、JWT 校验中间件、MyBatis 动态 SQL 查询。整个演示控制在十分钟左右老师会顺着你的流程追问你能讲清楚每一步的数据流向这个项目基本就稳了。6.2 高频追问与应答思路我整理了答辩和面试中反复出现的几个问题给出的都是能直接用的应答思路为什么用 SSM 而不是 SpringBoot不要只说“老师指定用 SSM”。可以说SSM 通过 XML 显式配置 Spring 容器、事务管理器、MyBatis 映射让我对框架的底层整合过程理解更深SpringBoot 虽好但把很多细节包起来了毕设阶段用 SSM 反而能展示对框架原理的掌握。为什么同时用 SSM 和 Django不统一一套说是两个系统的定位不同管理端是重业务、强事务的系统用了 Java 生态的成熟事务管理和权限控制学生端是高频轻量接口Django 的 ORM 和 DRF 能快速开发、方便联调。两边共用同一套数据库通过接口和表前缀解耦。并发抢房怎么处理的答乐观锁或行锁重点说 update 语句带 version 条件受影响行数为 0 说明冲突直接抛异常回滚。如果老师追问性能可以答“宿舍分配场景并发量不高乐观锁足够避免长事务锁表”。登录状态怎么跨系统保持答 SSM 用 SessionDjango 用 JWT两者不共享学生端接口用 token 鉴权管理端用 Session 拦截器。这其实是两套独立的认证体系各有分工。数据库事务边界在哪比如分配宿舍在一个 Service 方法内开启事务完成查房、更新床位、插入住记录三步任何一个环节失败整体回滚。要想不被问倒建议把几个核心 Service 方法上的Transactional都标出来弄清楚为什么这些方法需要事务。前端数据量大了怎么办答 MySQL 分页配合索引。重点给dorm_room的building_id、used_beds建联合索引给repair_order的student_no、status建索引查询效率明显提高。Django 端再配一个PageNumberPagination分页完全能支撑万人级学校的使用。我个人带项目的体感是这套“SSM 管理端 Django 学生端”的混合架构只要讲清楚设计动机和边界是比单一框架更出彩的地方。很多同学毕设代码写完了但不会讲核心问题就是对关键决策缺少“为什么”的思考。你要把每个设计点都当成面试题来准备——这不是背答案而是理解当初为什么这么选、踩了什么坑才改成这样。如果你手头的源码和文档结构比较乱先把上面的表结构和状态设计对一遍再按演示流程理出主线答辩时会从容很多。最后补一句正式答辩前一定把宿舍满房、重复报修、错误账号登录这类边界情况亲手测一遍因为这些往往就是老师想点开看看的地方。