1. 先聊聊这个题目:宿舍管理系统为什么一直是毕设“常青树”
如果你正在为选题发愁,或者已经拿到了“基于微信小程序的学生宿舍管理系统”这套源码却不知道怎么下手,那我先讲几句真实情况,免得你在答辩前才慌。
学生宿舍管理系统这题,说难不难,说简单也不简单。难的是它要覆盖的角色和流程其实不少:学生要查宿舍、报修、查电费、签到;宿管要管入住退宿、查寝记录、处理报修;辅导员或系统管理员要管楼栋、宿舍、人员、公告。这套业务逻辑摊开来看,几乎把信息管理系统的典型功能都过了一遍,所以它才能这么多年一直在毕设选题里占一席之地——评委老师看到这题不会觉得太小或太大,功能说清楚、代码写干净,拿个不错的分数并不难。
关键的问题是:用微信小程序来做,比传统网页版好在哪?我的回答是三点:不用装App、入口在微信里天然方便、扫码和手机号登录可以直接复用微信生态。宿舍场景的学生基本人手一个微信,查寝时打开小程序签个到,比打开网页输入账号密码顺畅得多。而且微信小程序前端代码就是 javascript 系的 WXML/WXSS,后端你用 Java Spring Boot、Node.js、Python Flask 都行,技术栈选型特别自由,这也是导师普遍认可的原因。
说这些是想先把定位讲清楚:这篇内容不是给你贴一段完整源码抄交完事,而是把一套能跑通的宿舍管理系统的架构、数据库、核心接口、小程序端页面逻辑拆开,让你知道每个部分为什么这么写、答辩时怎么解释、拿到源码后怎么改成自己的东西。下面按实际开发顺序展开。
2. 系统该怎么分端:三端协作的边界与数据流
2.1 小程序端、管理后台、服务端的职责划分
宿舍管理系统如果只做一个小程序,那是做不全的。拿到的毕设源码一般会拆成三个工程:
- 微信小程序端:给学生和宿管用。学生看到的是我的房间、报修、电费查询、查寝签到、公告列表;宿管看到的是查寝任务、报修处理、入住退宿登记。
- 管理后台(Web):给辅导员和系统管理员用。负责楼栋管理、宿舍分配、学生信息维护、报修工单总览、查寝统计。很多毕设源码里管理后台做成了网页,用 Vue 或原生 HTML 都有。
- 后端服务:给两端提供接口,处理权限校验、业务逻辑、数据库读写。
这三端的边界如果不提前分清楚,写着写着就会乱。我见过很典型的错误做法:本来该在管理后台做的事,比如宿舍床位分配,结果在小程序端塞了个“管理页”,角色的权限判断全靠前端隐藏按钮。这种做法答辩时被老师一问就露馅——小程序端代码可以被反编译,前端隐藏不等于后端安全。
正确做法是:所有权限校验都必须在后端完成,前端只是把当前用户的角色 ID 带上来,由后端判断有没有操作权限。比如宿管要在小程序里处理报修,他发起请求时带的是 token,后端先解析出 userId,再去数据库查该用户是否绑定了宿管角色,角色对上了才放行。这一段逻辑我会在第 5 章给出具体实现。
2.2 数据流从一次查寝打卡说起
讲一个完整的数据流,你就知道三端是怎么配合的。假设宿管发布了一次“A 栋 301 查寝任务”,学生点开小程序签到:
学生端发起请求 →POST /api/checkin/create,body 里带{ taskId, studentId, status: "normal" },请求头带 token。后端先验 token → 判断该学生是否住在这个房间 → 判断此刻是否在查寝时间段内 → 写入一条查寝记录 → 返回成功。
小程序端收到成功后展示“已签到”;宿管端在小程序里刷新任务列表,能看到这次签到记录和签到率。管理后台可以根据全部记录做统计图表。
这套链路里,最容易被忽略的是**“时间段判断”**。很多初版代码只校验身份和房间,却不管时间,导致学生凌晨也可能签到成功。虽然毕设里导师一般不会较真到这个程度,但你在代码里主动加时间判断,答辩时就是一个可讲的点。
3. 数据库设计:宿舍管理业务的地基怎么打
3.1 核心表设计思路
数据库设计是答辩时命中率最高的问题之一。宿舍管理系统最忌讳的就是只建一张 user 表加一张 dorm 表,硬把所有状态堆在一行字段里。合理拆分后的核心表大致如下:
用户与权限相关
student:学生信息表。字段:student_id、name、gender、phone、id_card、dorm_id、building_id、status(在住/退宿)。admin:管理用户表。字段:admin_id、username、password_hash、role(宿管/辅导员/系统管理员)、building_id(宿管管理的楼栋)。
宿舍资源相关
building:楼栋表。字段:building_id、name(例如“梅苑 1 栋”)、floors。dorm:宿舍表。字段:dorm_id、building_id、room_no、max_beds、occupied_beds。bed:床位表。字段:bed_id、dorm_id、bed_no、student_id(空则表示空床)。
业务相关
checkin_task:查寝任务表。字段:task_id、building_id、date、start_time、end_time、publisher_id、status。checkin_record:查寝记录表。字段:record_id、task_id、student_id、status(正常/晚归/未归)、checkin_time。repair_order:报修工单表。字段:order_id、student_id、dorm_id、content、images、status(待处理/处理中/已完成)、create_time、handler_id。electricity_record:电费记录表。字段:record_id、dorm_id、month、usage_kwh、amount、paid_status。notice:公告表。字段:notice_id、title、content、publish_time、publisher_id。
这套设计的关键在于把“宿舍”和“床位”分开维护。有的毕设里只有dorm.occupied_beds一个数字,但这样查“哪个床位是空的”就得数,根本数不清。拆成bed表后,一个宿舍是否住满,一条SELECT COUNT(*) FROM bed WHERE dorm_id = ? AND student_id IS NULL就能算出来。
3.2 建表语句里容易被忽略的两个小细节
一是外键约束到底加不加。很多线上项目为了性能会放弃外键,但毕设我建议加,而且答辩时可以说“我通过外键保证了数据的引用完整性”。MySQL 里 InnoDB 支持外键,写建表语句时顺手加上,逻辑上干净很多。
二是时间字段的默认值。强烈建议给create_time设DEFAULT CURRENT_TIMESTAMP,给update_time设DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。后续所有业务表都不用手动维护这两个字段,省很多事。
CREATE TABLE repair_order ( order_id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, dorm_id INT NOT NULL, content VARCHAR(500) NOT NULL, images VARCHAR(1000), status TINYINT DEFAULT 0 COMMENT '0-待处理 1-处理中 2-已完成', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, handler_id INT, FOREIGN KEY (student_id) REFERENCES student(student_id), FOREIGN KEY (dorm_id) REFERENCES dorm(dorm_id) );4. 微信小程序端:登录、首页结构与请求封装
4.1 手机号一键登录的完整流程
小程序登录是每套毕设都绕不开的第一步,也是很多人的知识盲区。你自己导出源码后,第一件事应该是检查app.js里的登录逻辑是否能跑通。
微信小程序的手机号登录,官方推荐的做法是:小程序端先调wx.login()拿code,把code发给后端,后端拿这个code加上小程序的appid和secret去微信的接口换openid和session_key。之后你再用openid去自己库里查有没有这个学生,没有就自动注册一个账号。
至于“获取手机号”这个功能,微信目前的规则是必须要用户手动点击button open-type="getPhoneNumber"授权,不能静默获取。拿到code后还是要交给后端,用session_key解密才能得到真实手机号。很多毕设源码里把这步简化成“用户手输手机号”,说实话也能接受,但答辩如果问到,你要能说清楚——完整版应该是走微信授权手机号,简版是手动输入。
// app.js 简化版:login 流程 wx.login({ success: async (res) => { const { code } = res; const loginRes = await request({ url: '/api/auth/login', method: 'POST', data: { code } }); wx.setStorageSync('token', loginRes.data.token); } });4.2 TabBar 页面结构:学生视角看这个系统
小程序的页面结构我建议按角色分开。学生进入后显示的 TabBar 一般是:首页、查寝签到、报修、我的。宿管进入后则多一个“管理”Tab,里面放入住登记、查寝任务发布、报修处理。
首页建议展示这几块信息:当前绑定宿舍的楼栋、房间号、剩余电量以及“一键报修”入口。这里有个实用技巧:首页顶部用onPullDownRefresh做下拉刷新,很多毕业生会漏掉这个生命周期函数,导致首页数据永远停留在第一次进入时的状态,退出来重进才更新。加了之后用户体验会好很多,代码也不复杂。
// 首页数据加载与下拉刷新 Page({ onLoad() { this.fetchHomeData(); }, onPullDownRefresh() { this.fetchHomeData().finally(() => wx.stopPullDownRefresh()); }, async fetchHomeData() { const res = await request({ url: '/api/student/home', method: 'GET' }); this.setData({ dormInfo: res.data.dorm, electricity: res.data.electricity, notices: res.data.notices }); } });4.3 请求封装:别在每个页面里裸写 wx.request
这套源码如果你要改造成自己的项目,第一件事就是封装request函数。我见过太多初版代码在每个页面里直接wx.request({ url: 'http://localhost:8080' }),改一处接口就要全局搜替换,非常痛苦。
统一封装的核心是:把baseURL、token注入、统一错误处理三件事集中在一起。你可以建一个utils/request.js:
const BASE_URL = 'https://your-domain.com/api'; function request({ url, method = 'GET', data = {} }) { return new Promise((resolve, reject) => { const token = wx.getStorageSync('token'); wx.request({ url: BASE_URL + url, method, data, header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, success(res) { if (res.data.code === 0) { resolve(res.data); } else { wx.showToast({ title: res.data.msg || '请求失败', icon: 'none' }); reject(res.data); } }, fail(err) { wx.showToast({ title: '网络异常', icon: 'none' }); reject(err); } }); }); }封装完之后,页面里所有接口调用都变成:
const data = await request({ url: '/api/checkin/create', method: 'POST', data: payload });这个封装看着简单,但锻炼的是你对项目工程化的理解,写完后记得在代码注释里给自己写一句“统一在此处理 token 过期跳转登录”,答辩就能顺带说一嘴“我对小程序端做了统一鉴权处理”。
5. 后端接口与权限控制:核心业务逻辑怎么落地
5.1 用 Node.js 还是 Spring Boot?怎么选更稳
拿到的源码后端可能是 Java 的 Spring Boot,也可能是 Node.js 的 Express,两种都常见。如果你是 Java 方向,强烈建议别换技术栈,Spring Boot 的生态成熟、答辩老师也认可,唯一要注意的是 JDK 版本别太新,用 JDK 8 或 11 就够,否则第三方依赖容易出兼容问题。如果你自己更熟 Node.js,用 Express 或 Koa 都行,开发和部署都轻量。
下面我以一个 Express 风格的接口为例,拆解最关键的报修工单创建逻辑,因为它是宿舍系统里牵扯最多表的业务之一。
5.2 报修工单创建的完整链路
后端接到POST /api/repair/create后,要做四件事:验证 token → 拿学生信息 → 写入工单 → 返回数据。伪代码长这样:
router.post('/repair/create', authMiddleware, async (req, res) => { const studentId = req.user.id; const { content, images } = req.body; // 1. 检查学生是否存在且在住 const student = await db.query( 'SELECT * FROM student WHERE student_id = ? AND status = 1', [studentId] ); if (!student.length) { return res.json({ code: 1, msg: '学生不存在或已退宿' }); } // 2. 插入报修工单 const result = await db.query( 'INSERT INTO repair_order (student_id, dorm_id, content, images) VALUES (?, ?, ?, ?)', [studentId, student[0].dorm_id, content, images] ); res.json({ code: 0, data: { order_id: result.insertId } }); });这个例子其实就是完整的“后端权限校验 + 数据落库”最小模型。authMiddleware里做的是解析 JWT token,从 token 里拿到 userId,再挂到req.user上。如果你拿到的源码没有这个中间件,而是每个接口里重复写解析代码,那你应该在二次开发时把它抽出来。
5.3 查寝任务的时间窗口校验
查寝功能最核心的接口是“发布任务”和“学生签到”。发布任务的后端逻辑除了插入任务记录,还要做两个检查:一是同一楼栋同一天不能重复发任务,二是时间段必须合理,结束时间不能早于开始时间。
学生签到时,后端需要检查当前时间是否落在该任务的时间窗口内:
const now = new Date(); const start = new Date(task.start_time); const end = new Date(task.end_time); if (now < start || now > end) { return res.json({ code: 1, msg: '当前不在查寝时间段内' }); }这段逻辑答辩时非常值得讲,因为很多毕设源码写的是“学生随便点一下就签到成功”,而你的实现多了一层保护。哪怕功能细节不如预期完美,这种你明显自己思考过的点,老师是看得出来的。
5.4 JWT 鉴权要注意的两个坑
JWT 鉴权在毕设后端几乎是标配了,但有两个坑很常见。
第一个坑是密钥硬编码。源码里通常会有jwt.sign(payload, 'secret123'),这种写法本地跑没问题,但答辩如果你说“我部署到服务器上了”,老师就会问secret123放代码里安全吗。建议改成从环境变量读取:process.env.JWT_SECRET。
第二个坑是token 过期时间不设置。有的源码只签发 token 不设expiresIn,等于永远不失效,这在安全上是硬伤。加一行expiresIn: '7d'就体面很多。
const token = jwt.sign({ userId }, process.env.JWT_SECRET, { expiresIn: '7d' });6. 把“毕设源码”变成“自己的毕设”的关键几步
6.1 别把源码当答案,先跑通再重构
拿到一套源码,第一步永远是本地跑通,而不是打开 IDE 开始读代码。跑通的标准是:小程序能进去、能登录、数据库表都在、种子数据有的页面能展示出来。
跑通之后,我建议的改造顺序是:
- 改数据库种子数据:把张三李四改成你自己编的名字,楼栋名改成“梅苑”“兰苑”之类有校园味的名字。这一步工作量最小,但答辩时演示起来最自然。
- 改小程序端 UI 细节:比如首页轮播图、公告列表的样式、TabBar 图标这些,换掉默认的灰底白图标,用即梦或 iconfont 找一套更精致的图标换上。
- 补一两个“增量功能”:源码里没有的功能,你加一个,比如“宿舍评分”“离校申请审批”,加的时候后端写两张表加两个接口,前端加一个页面,这就足够在答辩时讲一个完整的“我独立开发的功能了”。
增量功能的选题建议贴近宿舍场景的小痛点,比如“换寝申请”——学生提交申请,宿管审批,审批通过后自动更新student.dorm_id和两个宿舍的occupied_beds。这个功能逻辑清晰、表结构简单、演示效果好,强烈推荐。
6.2 部署上线:域名 HTTPS 与小程序后台配置
小程序没法直接请求http://localhost接口,所以真正要在手机上预览,你必须有一个公网可访问的 HTTPS 接口地址。毕设阶段最省事的方案是:
- 买一台便宜的云服务器,学生机大概一年几十块。
- 后端服务部署上去,用 Nginx 做反向代理。
- 申请免费的 HTTPS 证书。
- 在小程序管理后台的“开发设置 - 服务器域名”里配置 request 合法域名。
这里有个特别容易卡住的点:微信小程序对 request 域名要求必须备案过的域名,纯 IP 地址是没法配置成合法域名的。所以如果你没有自己的域名,可以考虑用云开发(微信云托管)或内网穿透工具做演示,但注意有些内网穿透给的是临时域名,小程序后台可能校验不过。稳妥的做法还是注册域名 + 备案,毕设周期一般来得及。
6.3 常见报错与排查清单
最后放一份我帮学弟学妹调这类项目时最常遇到的报错清单,省得你在深夜对着控制台发愣:
| 现象 | 多半是 | 解决办法 |
|---|---|---|
小程序端提示request:fail | 未配置合法域名或者目标地址是 http | 检查小程序后台服务器域名;如果只是开发调试,可在开发者工具里勾选“不校验合法域名” |
| 登录接口报 401 | JWT 密钥不匹配或 token 过期 | 检查后端JWT_SECRET是否一致,重新登录 |
| 首页数据空白 | 种子数据没导进去或者请求路径写错 | 检查数据库表是否有数据,确认接口 URL 与后端路由匹配 |
| 接口能通但数据显示不出来 | 前后端字段名不一致 | 打开浏览器 Network 面板或小程序调试器的 Network,对比返回 JSON 字段与前端渲染字段 |
| 图片上传失败 | 未配置 uploadFile 合法域名 | 小程序后台的 uploadFile 域名单独配置,和 request 域名不通用 |
7. 最后说点实在的:答辩时怎么讲这套系统
很多人拿了源码,代码能跑,但一上台就讲得乱七八糟。我的建议是准备一张功能结构图,讲的时候按“用户故事”串:学生的一天(登录 → 看电费 → 报修 → 查寝签到)和宿管的一天(发查寝 → 看签到 → 处理报修 → 办入住退宿),把系统功能融入场景里讲,比干巴巴念功能列表强得多。
被问“这个项目的难点是什么”的时候,别只说“登录验证”,要把具体问题说出来:我是怎么用 JWT 做小程序端身份认证的、查寝签到是怎么避免重复签的、数据库的表是怎么拆到第三范式的。遇到不会的问题,诚实说“这块我还没有深入实现,源码里是这么写的”,也比瞎编强。
我个人带过几个做这个题目的学生,最大的体会是:别贪多。把学生端、宿管端、后台管理端的主干流程做到闭环,再加一个自己写的亮点功能,就足够在答辩里站住了。宿舍管理系统的上限不高,但它很适合用来检验一个人对“前端交互 + 后端接口 + 数据库设计”整条链路的掌握程度——这恰恰是毕设最想考察的东西。希望这篇拆解能让你少走一点弯路,至少拿到源码之后,知道第一步该干什么。