学生公寓管理系统毕设实战:从数据库设计到权限控制完整实现 每年三四月都会有一批计算机专业的同学盯着“学生公寓管理系统”这个毕设题目发呆。我去年帮人把一套带源码的学生公寓管理系统从头到尾跑通包括环境搭建、数据库设计、权限控制、页面逻辑再到论文和答辩踩过的坑可以写满两页A4纸。这篇文章就把这个系统从需求到实现的完整链路拆开讲一遍适合正在选题、已经拿到源码但不知道从哪下手以及想把这个题目做得更漂亮一点的人。学生公寓管理系统之所以热门是因为它的业务边界非常清晰管理员、宿管、学生三类角色宿舍、床位、入住、退宿、报修、通知这些实体都是日常能接触到的。它不需要复杂的算法但能完完整整地考察一个计算机专业学生的数据库设计能力、增删改查基本功、权限控制思路和前端页面组织能力所以无论用 Java、PHP 还是 Python都能套出一套像样的毕设。1. 会选这个题目的学生一般都在纠结什么选“学生公寓管理系统”做毕设首先要搞明白一个核心问题这套系统到底在解决什么现实场景里的问题。很多同学拿到源码就急着跑起来结果连项目里的表结构都说不清答辩的时候老师一问就卡住非常可惜。1.1 学生公寓管理系统到底要管哪些场景高校的学生公寓日常运营其实有一套非常固定的流程。新生入学要分配宿舍楼和床位住进去之后会有调换宿舍、退宿离校的需求平时宿管要处理外来访客登记、大件物品搬出登记水电要定期查抄学生房间里的灯管、水龙头坏了要提交报修辅导员要发查寝通知和卫生检查结果。这一堆事情过去都靠 Excel 和纸质台账做一个管理系统本质上是把这几条流程搬到 Web 页面上让管理员、宿管、学生各有一个入口数据能查、记录能留、状态能跟。所以在做需求分析的时候不要一上来就想着“我要做多炫的功能”而是先把角色和流程画出来。我的建议是至少要有这么几个模块系统管理用户登录、账号管理、密码修改。宿舍管理宿舍楼信息、楼层数、房间数、房间床位容量、当前入住人数。学生管理学生基本信息、所属学院班级、当前所在宿舍、入住状态。入住退宿管理入住登记、退宿登记、调宿登记以及历史住宿记录。报修管理学生提交报修宿管接单派单维修工反馈结果学生确认完成。公告通知管理员发布查寝通知、停水停电提醒等。访客登记校外人员来访记录关联到被访学生。这套模块划分下来系统能做什么、每个角色能点什么按钮基本就清楚了。很多同学毕设做得像“花架子”就是因为没有把流程想透页面做了十几个但逻辑不闭环。1.2 为什么这个题目每年都会出现在推荐列表前几名学生公寓管理系统几乎常年挂在毕业设计推荐榜单上原因很实在第一业务模型直观数据库关系不复杂适合用来展示基本功第二网上参考项目多不管是 Java 还是 PHP都能找到完整源码遇到问题也容易查第三扩展空间大可以在基础 CRUD 上加入人脸识别、微信小程序端、可视化大屏、水电表自动抄读等功能想冲高分有方向。这对不同基础的同学都很友好。基础偏弱的用 JSPServletMySQL 也能把核心流程跑通基础好一点的用 Spring Boot Vue 前后端分离再配上 Redis 缓存和 JWT 登录拿出来就已经超过大部分本科同题目的作品。所以这个题目不是“太简单没含金量”而是“能不能做出区别度”的问题。2. 技术栈选型照着这套方案做最稳技术选型是整个毕设里最容易被忽视、却最能影响开发效率的一步。不少学弟学妹一上来就想着用最新版本框架结果依赖冲突一堆报错查半天白白浪费时间。我强烈建议用一套“资料最多、报错最少”的成熟组合。2.1 Spring Boot Vue 的组合为什么是首选如果你有一点点 Java 基础首选 Spring Boot 2.7.x MyBatis Plus MySQL 8 Vue 3 Element Plus。这个组合在 2025 年依然是市面上资料最密集的几乎你能遇到的每个报错都能在搜索引擎里找到解法。Spring Boot 负责提供后端接口MyBatis Plus 负责简化 SQL 操作Vue 3 配合 Element Plus 快速搭建管理后台页面整个项目切分清楚答辩讲起来也顺。用 MyBatis Plus 而不是原生 MyBatis 的原因很简单本科毕设大量的操作就是单表增删改查和简单联表查询MyBatis Plus 的BaseMapper直接帮你把常用的selectById、selectList、insert、updateById都封装好了少写很多重复 XML。但要注意毕业论文里不能通篇不提 SQL你得能讲清楚 MyBatis Plus 最终生成的 SQL 是什么样的至少要知道selectById对应的是SELECT * FROM 表 WHERE id ?。如果你的 Java 基础比较薄弱想降低风险也可以退一步用 Spring Boot Thymeleaf 服务端渲染或者直接用 Servlet JSP。这样不用考虑跨域、前后端联调、npm 构建这些额外问题一个 Tomcat 跑完所有事。缺点是页面表现力一般答辩的时候视觉效果会弱一些。2.2 后端和前端目录怎么拆才不乱拿到了带源码的学生公寓管理系统第一件事不是急着启动而是先把目录结构看明白。一个合格的后端项目通常会按分层结构组织典型的是src/main/java/com/example/dormitory ├── config // 配置类比如跨域、拦截器 ├── controller // 接口层只做参数接收和返回 ├── service // 业务逻辑层接口定义 │ └── impl // 业务实现 ├── mapper // 数据库操作层 ├── entity // 实体类对应数据库表 ├── dto // 接收前端参数的封装对象 └── common // 通用返回类、异常类、工具类前端 Vue 项目通常长这样src ├── api ├── assets ├── components ├── router ├── stores ├── views │ ├── LoginView.vue │ ├── DashboardView.vue │ ├── DormManage.vue │ ├── StudentManage.vue │ ├── CheckinManage.vue │ ├── RepairManage.vue │ └── NoticeManage.vue ├── App.vue └── main.js看到这种分层结构你心里就该有数Controller 里不应该出现复杂 SQL 逻辑Service 里不应该堆一大堆System.out前端页面组件应该是一个 View 对应一个业务模块。如果你拿到的源码是几百行代码全塞在 Controller 里的那种建议别直接用至少重构成这种结构否则答辩时老师翻代码印象分会很低。3. 数据库设计实战核心表结构和关键字段讲解学生公寓管理系统看似简单数据库设计才是真正拉开差距的地方。很多同学的表设计只停留在“学生表、宿舍表、报修表”这种粗糙粒度结果做入住、调宿、退宿记录的时候发现历史数据根本查不出来只能推倒重来。下面我把核心表关系理一遍再给出一份可以直接用的建表思路。3.1 实体关系梳理从学生到床位一共有几步学生最终落脚的地方是“床位”但床位不是孤立存在的。一个比较完整的链路是这样的宿舍楼dorm_building包含多个房间dorm_room每个房间包含多个床位dorm_bed一个学生入住时关联到具体的床位同时生成一条入住记录checkin_record。如果中间还有调宿就再生成一条新的入住记录并把旧记录标记为已退宿。为什么把“房间”和“床位”拆成两张表因为同一个房间的人数不是固定的如果只在房间表里放一个bed_count字段就没办法准确记录“张三睡在 1 号床李四睡在 2 号床”这种信息。把床位单独拎出来每个床位有自己的状态才能做到精确管理。同理为什么需要独立的入住记录表因为退宿之后历史住宿信息不能从学生表里直接删掉要通过记录表留存。整体实体关系大致是用户表扩展学生信息学生关联房间和床位房间关联宿舍楼报修单关联学生和房间公告表独立访客登记表关联学生系统操作记录可以单独建一张日志表也可以先不做。注意“用户”和“学生”不是一回事登录用的账号密码放在 sys_user 表学生的学院班级、学号等信息放在 student 扩展表通过 user_id 关联这是最稳妥的设计。3.2 核心建表SQL与字段避坑说明直接给一份可以动手建表的核心 SQL按这个思路设计基本不会出大问题CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL, password VARCHAR(100) NOT NULL, real_name VARCHAR(32) NULL, role TINYINT NOT NULL DEFAULT 3 COMMENT 1学生 2宿管 3管理员, phone VARCHAR(20) NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表; CREATE TABLE dorm_building ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL, floors INT NOT NULL, gender TINYINT NOT NULL COMMENT 1男 2女, manager VARCHAR(32) NULL, remark VARCHAR(255) NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宿舍楼表; CREATE TABLE dorm_room ( id BIGINT PRIMARY KEY AUTO_INCREMENT, building_id BIGINT NOT NULL, room_no VARCHAR(16) NOT NULL, floor_no INT NOT NULL, capacity INT NOT NULL COMMENT 房间床位数, used_num INT NOT NULL DEFAULT 0 COMMENT 已住人数, status TINYINT DEFAULT 0 COMMENT 0空闲 1部分占用 2已满, UNIQUE KEY uk_building_room (building_id, room_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宿舍房间表; CREATE TABLE dorm_bed ( id BIGINT PRIMARY KEY AUTO_INCREMENT, room_id BIGINT NOT NULL, bed_no VARCHAR(8) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0空闲 1占用, UNIQUE KEY uk_room_bed (room_id, bed_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT床位表; CREATE TABLE student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, student_no VARCHAR(32) NOT NULL, name VARCHAR(32) NOT NULL, college VARCHAR(64) NULL, class_name VARCHAR(64) NULL, gender TINYINT NOT NULL, phone VARCHAR(20) NULL, room_id BIGINT NULL, bed_id BIGINT NULL, status TINYINT DEFAULT 1 COMMENT 1在住 0退宿, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_no (student_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生信息表; CREATE TABLE checkin_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, room_id BIGINT NOT NULL, bed_id BIGINT NOT NULL, type TINYINT NOT NULL COMMENT 1入住 2退宿 3调宿, operator_id BIGINT NOT NULL COMMENT 操作人, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT入住记录表; CREATE TABLE repair_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, room_id BIGINT NOT NULL, content VARCHAR(500) NOT NULL, images VARCHAR(500) NULL, status TINYINT DEFAULT 0 COMMENT 0待处理 1处理中 2已完成 3已取消, assignee VARCHAR(32) NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, finish_time DATETIME NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT报修单表;这里有几个必须注意的点一律用utf8mb4不要用utf8否则保存 Emoji 表情或者特殊字符时容易报错。时间字段用DATETIME不要用VARCHAR否则按时间排序、统计月份报修量都会出问题。状态字段用TINYINT加注释不要用让人看不懂的字符串状态代码里写status 1的时候读者才知道 1 是什么含义。物理外键建议不加外键关系靠代码逻辑维护。本科毕设用物理外键插入删除时经常被约束卡住调试很痛苦。“房间状态”和“已住人数”是冗余字段入住/退宿时必须同步更新不然数据会不一致。4. 登录认证与角色权限三种角色怎么共存学生公寓管理系统里天然存在三类角色学生、宿管、管理员。麻烦的是这三类角色看到的菜单和能执行的操作不一样。怎么用一套登录逻辑支撑三种不同权限是很多毕设源码处理得最粗糙的地方也是答辩时老师最爱追问的点。4.1 登录接口设计一张用户表还是多张用户表网上源码有两种做法。第一种是建admin、student两张表分别存账号密码第二种是建一张sys_user表用role字段区分角色。我更推荐第二种理由很简单学生、宿管、管理员登录逻辑完全一样都是“账号密码”如果拆成多张表登录时还要判断这次输入的是哪种账号代码绕一大圈。所以设计上采用一张sys_user表存登录凭证role字段存角色标识学生的扩展信息单独放student表通过user_id关联。前端登录成功后后端返回token和role前端根据role动态渲染不同的菜单。比如角色是宿管默认首页就显示报修处理和来访登记角色是学生默认首页就显示我的宿舍和我的报修。登录接口的返回体建议统一封装结构大致是{ code: 200, msg: 登录成功, data: { token: eyJhbGciOiJIUzI1NiJ9...., role: 1, realName: 张三 } }前端拿到token存到localStorage后续每个请求都在请求头里带上这个 token后端每次从 token 里解析出用户 id 和角色再去判断能不能访问某个接口。4.2 权限拦截器和密码加密的落地方式权限控制最简单的做法是“后端拦截器 前端路由守卫”双保险。后端定义拦截器对除登录接口以外的所有/api/**请求做 token 校验核心代码类似public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(token); if (StringUtils.isBlank(token) || JwtUtil.parse(token) null) { response.setStatus(401); return false; } return true; }注意这里有一行关键的判断OPTIONS请求直接放行。前后端分离项目里浏览器跨域时会先发一个 OPTIONS 预检请求如果你在这层就把预检拦了前端会报“登录成功但后续请求全失败”的诡异问题后文的排错部分我还会详细说。密码加密方面不要再用明文或者简单的 MD5至少要换成 Bcrypt。Spring Boot 的spring-security-crypto包里自带BCryptPasswordEncoder用法就两行。数据库里存的密码是类似$2a$10$...的字符串即使数据库泄露也不能直接反推出明文这是一道最基本的加分安全项。前端路由守卫思路是在路由配置里给每个页面加meta.roles比如学生页面只能角色 1 访问全局前置守卫里如果没有 token 就跳登录页有 token 但角色不匹配就跳无权限页。前端守卫挡的是页面访问后端拦截器挡的是接口数据两层配合才叫完整。5. 核心业务流程拆解入住、退宿、报修与分配一个学生公寓管理系统能不能让人眼前一亮不取决于你用了多新的框架而取决于几个核心流程是不是严谨。入住、退宿、报修这三条主线流程跑顺了项目的主体工作量就完成了大半。5.1 入住登记和退宿的事务处理入住登记是系统里最需要谨慎的操作因为涉及学生、床位、房间三个维度的状态变化。我用伪代码还原一下核心逻辑方便你在源码里对照Transactional public void checkIn(CheckinDTO dto) { // 1. 查床位是否存在且空闲 DormBed bed dormBedMapper.selectById(dto.getBedId()); if (bed null || bed.getStatus() ! 0) { throw new BusinessException(床位不可用); } // 2. 用条件更新抢占床位防止并发分配同一个床位 int updated dormBedMapper.occupyBed(dto.getBedId()); if (updated 0) { throw new BusinessException(床位刚刚被占用请刷新后重试); } // 3. 更新房间人数和状态 dormRoomMapper.increaseUsed(dto.getRoomId()); // 4. 更新学生当前宿舍信息 studentMapper.bindRoom(dto.getStudentId(), dto.getRoomId(), dto.getBedId()); // 5. 写入入住记录 checkinRecordMapper.insert(...); }这段逻辑最关键的是第 2 步占用床位用的不是select再update而是直接UPDATE dorm_bed SET status 1 WHERE id ? AND status 0再检查受影响行数。为什么这么做因为“先查再改”在并发场景下会有漏洞两个宿管同时操作时可能都把同一个床位分配出去。条件更新让数据库自己保证同一时刻只有一个请求能改成功这是本科毕设里最容易被忽视、也最被答辩老师看重的细节。退宿流程是入住的逆操作但不用真的“删除”学生数据。把学生表的status改成 0床位状态改成 0房间used_num减 1同时写入一条type2的退宿记录。这样以后统计“这个学期有多少人退宿”“哪些床位的空置率最高”都有据可查。5.2 报修单的状态流转设计报修模块看起来就是“提交一个描述、管理员列表看一眼”但真正做起来状态管理容易乱。我的设计是一个报修单有四个状态0 待处理、1 处理中、2 已完成、3 已取消。提交报修之后宿管在待处理列表里“接单”把状态改成 1记录接单人如果涉及配件费用可以再加一个备注字段维修完成后宿管把状态改成 2写入完成时间学生端能在“我的报修”里看到状态从“待处理”变成“处理中”再到“已完成”。整个过程里每个状态变更都最好在repair_order表里记录status和操作时间答辩老师问“报修流程做了多久、平均处理时长”时直接写一条 SQL 查出来这种细节就是加分项。这里还有一个容易忽略的点报修单里最好带图片字段。虽然图片上传会引入文件存储问题但哪怕简单存一个服务器路径都比纯文本描述有说服力。前端用el-upload上传到后端的/upload接口返回一个图片 URL 存到images字段实际效果非常直观。5.3 宿舍分配逻辑按学院还是按入住时间宿舍分配是很多同学不知道怎么做“高级感”的地方。最简单也最合理的方案是“按条件检索空床 批量预分配”。举个例子某学院新生入学管理员选择学院、班级、性别系统自动查出所有符合条件的空床位并按“楼栋号、楼层号、房间号、床位号”排序从第一条开始依次分配。排序分配的好处是结果可预测宿管能一眼看懂“哪个学院住在哪几栋楼哪几层”。真正写代码时只要一个联表查询就能完成SELECT b.id AS bed_id, r.room_no, b.bed_no FROM dorm_bed b JOIN dorm_room r ON b.room_id r.id JOIN dorm_building d ON r.building_id d.id WHERE d.gender #{gender} AND b.status 0 ORDER BY r.room_no, b.bed_no如果要做得再精致一点可以支持“批量导入学生 Excel自动分宿舍”这会让系统非常有代入感。但核心逻辑不变先按性别过滤再按班级分组分配床位分配完自动更新房间状态。6. 前端页面与接口联调Vue项目的实操细节到了前端环节不少同学拿到源码后最头疼的就是“页面长什么样、接口怎么调用”。Vue 项目没有固定模板但学生公寓这种管理后台有非常成熟的套路可以参考。6.1 页面拆分和路由设计管理后台通常做法是“左侧菜单 右侧内容区”。主布局是Layout.vue里面放侧边栏菜单和内容router-view每个功能对应一个views下的页面组件。页面级别上我建议重点打磨这几个登录页简洁居中卡片背景加一张校园场景图。首页仪表盘统计卡片展示宿舍楼数、房间总数、当前在住人数、待处理报修数量下面可以用柱状图展示近一周报修趋势。学生管理页表格展示学生信息支持按学号、姓名、学院、宿舍楼筛选行内操作有“查看详情”“办理退宿”。宿舍管理页左侧树形结构展示楼栋-楼层右侧展示房间列表和实时占用率点击房间能展开床位图。报修管理页状态标签展示支持接单和处理。公告管理页富文本编辑器发通知。表格操作如果涉及二次确认比如退宿、删除记得加一个ElMessageBox.confirm弹窗防误操作的同时也显得项目成熟。页面风格统一用 Element Plus 的基础风格就够了不要自己写一堆混乱的scoped样式。6.2 axios封装、分页和跨域处理前端所有请求建议封装成一个http.js统一配置baseURL和拦截器import axios from axios const http axios.create({ baseURL: /api, timeout: 10000 }) http.interceptors.request.use(config { config.headers.token localStorage.getItem(token) || return config }) http.interceptors.response.use( res { if (res.data.code 200) return res.data if (res.data.code 401) { localStorage.removeItem(token) window.location.href /login } return Promise.reject(res.data) }, err Promise.reject(err) ) export default http所有接口调用都走这个封装实例后端返回统一结构{ code, msg, data }前端不用在每个组件里重复判断成功失败。分页是最常见的卡壳点。我的约定是前端传current当前页和size每页条数后端返回{ records: [...], total: 总数 }表格里用this.loading true const { data } await http.get(/student/page, { params: { current: this.current, size: this.size, keyword: this.keyword } }) this.tableData data.records this.total data.total this.loading false跨域处理建议优先用开发环境代理。Vue 项目的vite.config.js里配置server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这样前端代码里请求/api/student/page开发服务器会转发到后端8080端口浏览器视角里没有跨域自然也不会触发那一堆跨域报错。生产部署时再用 Nginx 做反向代理或者直接把前端打包后的静态文件放进 Spring Boot 的static目录里跑。7. 常见报错与排错经验我的实操排错记录代码跑不通的时候90% 的问题都集中在环境、跨域、数据库连接和数据一致性这几个方向。下面这张表是我整理的最高频问题速查直接对照解决可以省很多时间。7.1 高频问题速查表现象常见原因解决方法前端请求全部 404baseURL 路径和后端接口前缀不匹配统一在前端加/api后端 context-path 保持一致登录接口通了其他接口全被拦后端拦截器拦截了 OPTIONS 预检请求在拦截器里放行OPTIONS请求中文乱码数据库字符集不是 utf8mb4建库用utf8mb4连接串加characterEncodingutf8端口被占用上次项目没关干净换端口或者查杀占用进程数据库连接失败连接池配置、账号密码不对检查application.yml里的 url、username、passwordMyBatis Plus 查询不到数据表名和实体类名映射不上检查TableName注解和驼峰映射配置页面刷新后 404Vue Router 使用 history 模式改 hash 模式或后端配置转发到 index.html上传图片失败文件保存路径不存在后端在项目根目录自动创建 upload 文件夹并配置映射7.2 两个最容易让毕设“翻车”的隐藏问题第一个是并发入住导致的“床位超卖”。用最普通的“先查询空闲床位再执行 update”逻辑如果两个宿管同时给学生办入住极有可能出现两个人都查到同一个空床、然后又都更新成功的情况。解决方式我前面已经写过就是用条件更新UPDATE dorm_bed SET status 1 WHERE id ? AND status 0受影响行数为 0 就说明床位被抢了直接提示用户重新选择。这个问题的本质是数据库并发安全把它写进论文和答辩 PPT老师会觉得你确实认真做了。第二个问题是“源码环境不干净”。很多下载的资料里带着别人数据库的真实数据、默认账号密码、甚至上一届学长的名字。交项目之前务必做这些事清空student、checkin_record、repair_order等业务表里的测试数据新建一个干净的init.sql脚本把数据库连接密码改成自己的把系统名称、Logo、学校信息全部替换掉。如果你拿到的源码里后端application.yml暴露了数据库密码答辩现场演示时被老师看到印象分会直接打折扣。还有一个容易被忽视的坑是日志问题。开发和部署服务器的时间不同步容易导致报修记录时间显示成 8 小时前的旧时间。确认服务器时区在数据库连接串和application.yml里统一设置serverTimezoneAsia/ShanghaiMySQL 连接串同样加上。8. 论文撰写与答辩准备的实用建议系统做完只是第一步最后的论文和答辩同样决定最终成绩。我看到不少同学代码写得不错但论文把源码大段大段贴上去、测试报告只有两行字非常可惜。这里把我的论文写作顺序和答辩思路分享出来。8.1 论文章节怎么组织才不空洞我建议章节结构按这个顺序来摘要、绪论、需求分析、总体设计、详细设计、系统实现、系统测试、总结。其中最能体现工作量的是需求分析和详细设计两章。需求分析里不要只写一句“本系统满足学生公寓管理需求”要写清楚业务背景、角色分析、功能性需求和非功能性需求。最好画一个用例图把“学生提交报修”“宿管处理报修”“管理员维护宿舍楼信息”这些用例都列出来。总体设计这章节放系统架构图、功能模块图、数据库 ER 图。详细设计章节放核心表结构说明表和核心业务时序图像入住登记这种事关并发处理的关键流程值得单独拿出来讲。系统测试章节要有“用例表”不是随便写三条而是每个模块至少准备五条用例包含测试项、输入条件、预期结果、实际结果、是否通过。比如“学生提交报修时填写内容为空系统应弹窗阻止提交”这种用例写出来比空泛地写一句“系统经过测试稳定运行”有说服力得多。这里提醒一句论文里别整段贴源码。老师想看的是你的设计思路和解决问题的过程而不是让评阅人去逐行读代码。核心代码可以用短代码块辅助说明但篇幅要控制在页面的一小部分。8.2 答辩被问频率最高的问题与回答思路答辩现场学生公寓管理系统被问到的高频问题基本固定提前准备好回答要点基本就能稳住场面。“为什么选这个技术栈”回答思路Spring Boot 生态成熟、资料多Vue 组件化开发适合快速搭建后台MySQL 满足中小型系统数据存储需求选这套方案是兼顾开发效率和可维护性。“如果两个管理员同时给同一个学生办理入住怎么办”回答思路把条件更新的 SQL 说出来说明用数据库行锁保证同一时刻只有一个操作能成功。“用户密码怎么存储的”回答思路不清文存储用 BCrypt 加密即使数据库泄露也无法直接拿到明文。“三张角色表还是一张用户表”回答思路一张sys_user表通过角色字段区分学生扩展信息放student表登录时统一从sys_user查。“系统最大的难点是什么”回答思路不避讳说清楚床位并发分配问题和数据一致性问题以及对应的解决思路。“项目还有哪些不足和扩展点”回答思路可以提“目前没有接入实时消息推送后续可以加 WebSocket 通知报修进度”“没有做扫码开门后续可以接物联网设备”。答辩的核心原则是不会的不要硬编把你真实做过的流程讲透。一个只做了基础增删改查的学生能把“为什么这么设计”讲清楚比一个拿了大项目源码却讲不明白的同学更容易拿高分。最后再分享一个小经验不管你是自己一句句写的还是拿到一套学生公寓管理系统源码交之前一定要做一次“清洁彩排”。把默认账号密码、数据库口令、README 里别人的学校信息全部替换掉再把登录、入住、报修、退宿这四个主流程亲手从头到尾走三遍。老师问任何一个按钮你都能说出它背后的表结构和 SQL 怎么执行这个题目就真的稳了。