简介:基于微信小程序的琴房管理系统设计与实现,是一套可直接运行的毕业设计/课程设计项目,面向高校计算机相关专业学生、小程序开发者及需要完成预约类系统设计的读者。系统包含管理员与学生两类角色:管理员可在后台维护学生信息、发布琴房公告并推送给学生;学生通过小程序前台浏览网站介绍、查看在线交流与信息公告,登录后即可查看空闲琴房并完成预约,覆盖琴房管理的完整业务流程。资源包共1105个文件,压缩后约25.4MB,包含Vue组件、JavaScript脚本、Java后端类、微信小程序wxml/wxss页面样式、PNG/SVG图片、SQL数据库脚本,分别覆盖前端界面、交互逻辑、后端服务与数据存储,同时提供说明文档与演示视频,并附一键安装、运行、构建脚本和关键控制器类、备份文件,便于快速部署、调试与二次开发。已有188人学习下载,适合作为毕业设计、课程设计或小程序实战练习的完整参考资料。
1. 微信小程序琴房管理系统:毕设源码怎么当脚手架用
琴房预约最烦的不是弹琴,是抢时间段。一个学校的琴房就那么多,高峰时段挤在一起,管理员拿 Excel 排表,学生一趟趟跑教务处问有没有空房——这套基于微信小程序的琴房管理系统,就是把"查房、预约、管房"三个动作搬到了手机和后台。资源里带完整源码、说明文档、LW(论文/设计文档)和演示视频,属于典型的毕业设计成套资源,同时也是一座可以二次开发的脚手架。
适合谁?第一是拿它交毕设或课程设计的人,功能点和文档体系完整;第二是想研究微信小程序 + Spring Boot 前后端分离怎么组织工程的人,工程结构清晰;第三是想把它改成通用预约系统(自习室、健身房、实验室)的人,因为核心的预约流程和权限模型是通用的。后面我会按"功能拆解 → 本地运行 → 避坑排查 → 演示验证"的顺序把它讲透,信息密度按可复现的标准来。
2. 功能拆解与角色权限:学生端、管理端各管什么事
2.1 双角色权限模型:为什么必须把学生和管理员分开
从摘要和源码里的 CommonController 能看出来,系统的用户只有两类:学生和管理员。这是这类预约系统的标准简化——不引入教师、教务等角色,把权限模型压到最小,却能把"预约"这个核心闭环跑通。对于毕设答辩来说,这种设计的优点是一句话就能讲清楚边界:学生是预约的使用者,管理员是数据和内容的维护者。
学生端不做登录后的复杂管理,核心操作是"看"和"约":打开首页看琴房介绍、信息公告、在线交流区,登录后能查看琴房列表和琴房详情,选中空闲时间段发起预约。管理员端则通过后台登录页面进入,权限包括学生信息管理(查看学生列表、增删改查、重置状态)和公告管理(发布、编辑、下线琴房公告和文章公告)。从这些权限划分可以看出设计意图:两类用户的数据在数据库层面天然用不同的表承载,互不越权。
我一般会建议把权限讲成"两条链路":学生链路是"注册/登录 → 浏览公告 → 查看琴房 → 提交预约 → 查看我的预约";管理员链路是"后台登录 → 维护学生信息 → 发布公告 → 管理预约数据"。这样的表述在论文和 PPT 里都容易画成图,也方便评委快速捕捉系统边界。演示视频的录制顺序也可以完全按这两条链路来,后面第 5 章会展开。
2.2 琴房预约的状态流转与表设计
预约系统最核心的不是界面,是预约状态怎么流转。常见的设计是四态:可约、已预约、使用中、已结束,有的版本还有"已取消"。学生提交预约时,前端把琴房 ID、日期、开始时间、结束时间、用途一起提交;后端先查该时间段是否已被占用,空闲则写入预约记录,并把琴房状态置为"已预约"。
对应到数据库,核心表至少要有这几张。为了让演示视频和文档里的数据对得上,我按下面的结构建,字段名和说明如下:
| 表名 | 关键字段 | 作用 |
|---|---|---|
| student | id, username, password, name, major, phone | 学生账号与基本信息 |
| admin | id, username, password | 管理员登录凭证 |
| music_room | id, room_no, location, capacity, status, open_time, close_time | 琴房基础信息与当前状态 |
| reservation | id, student_id, room_id, reserve_date, start_time, end_time, purpose, status, create_time | 预约记录与状态 |
| announcement | id, title, content, publish_time, publisher_id | 公告与文章内容 |
建表时有一个点容易翻车:reservation表的student_id和room_id需要保留外键字段和索引,但物理外键可以不加。原因是加了 FOREIGN KEY 约束后,删除琴房或学生会受约束卡住,演示时想清理脏数据反而麻烦。我在实体表上保留字段和普通索引,代码里用事务保证一致性,这样两端都灵活。
CREATE TABLE reservation ( id BIGINT AUTO_INCREMENT PRIMARY KEY, student_id BIGINT NOT NULL, room_id BIGINT NOT NULL, reserve_date DATE NOT NULL, start_time VARCHAR(5) NOT NULL, end_time VARCHAR(5) NOT NULL, purpose VARCHAR(100), status TINYINT DEFAULT 0 COMMENT '0待使用 1已使用 2已取消', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_room_time (room_id, reserve_date, start_time, end_time), KEY idx_student (student_id) );这段 SQL 的关键在idx_room_time这个联合索引,它是预约冲突查询的加速器。后端查冲突时执行的 SQL 是"同一琴房、同一天、且时间区间有重叠"的预约记录,联合索引能让这条查询直接走索引而不是全表扫。status用 TINYINT 存而不是字符串,是为了排序、统计和扩展时更好维护,比如以后加"已取消"状态不用改表结构,只加一个枚举值。
源码包里管理后台的页面结构也能印证分层:IndexMain.vue 是主框架布局,IndexAsideStatic.vue 是左侧菜单,IndexHeader.vue 是顶部栏,BreadCrumbs.vue 是面包屑导航,这些 .vue.bak 文件说明后台是 Vue 单页应用结构,小程序端则是单独一套工程。想改造成通用预约系统的人,这套前后端分离的骨架可以整体平移,换掉业务字段就行。
2.3 时间重叠判断:预约冲突的后端写法
判断时间重叠是预约系统最典型的边界场景。比如学生 A 预约了 14:00-15:00,学生 B 想约 14:30-15:30,这条数据必须被拦下来。常见做法是在后端用一个区间重叠查询:
// 判断同一琴房同一天是否已存在时间重叠的预约 int count = reservationMapper .selectConflict(roomId, reserveDate, startTime, endTime); if (count > 0) { return Result.fail("该时段已被预约,请选择其他时间"); }对应的 SQL 是:
SELECT COUNT(*) FROM reservation WHERE room_id = #{roomId} AND reserve_date = #{reserveDate} AND status = 0 AND start_time < #{endTime} AND end_time > #{startTime};重叠判断的数学表达是newStart < oldEnd && newEnd > oldStart,只要两个时间区间满足这个条件,就是冲突。这里的start_time和end_time用字符串存时间('14:00'),比较时依赖字符串字典序,前提是格式必须统一补零;更稳妥的做法是在 Java 侧转成 LocalTime 比较后再落库,避免出现"9:00"和"09:00"这种格式不一致导致判断失效。
需要提醒的是,这段判断在高并发下不是绝对安全的。两个请求同时查到"无冲突",就可能都写成功,造成超卖。毕设场景下一般够用;想更稳可以给预约表加唯一索引(琴房、日期、开始时间)从数据库层兜底,或者用事务加行锁。这个问题我放到第 4 章避坑里细说,因为在答辩现场被问"并发怎么办"的概率比想象中高得多。
3. 把工程跑起来:install/run/build 三个脚本与前后端联调
3.1 从工程文件先看懂这个项目的三层结构
压缩包里的文件列表暴露了很多结构信息。.vue.bak文件说明管理后台是 Vue 写的,.bak是作者改代码前留的备份,保留这些文件不影响运行但占体积;后端有 CommonController.class,说明核心是 Java Spring Boot 结构,Controller 统一收口请求;main.css.bak是主样式表备份。三个批处理文件是快速启动的关键:
- 1-install.bat:首次拿到工程时安装依赖,常见做法是执行
mvn clean install(后端)和npm install(前端)。 - 2-run.bat:启动后端服务,通常会先起 MySQL,再执行
mvn spring-boot:run或java -jar。 - 3-build.bat:打生产包,管理后台执行
npm run build,产物是 dist 目录。
为什么不直接一条命令跑完?因为小程序端不在这个构建链路里——小程序代码由微信开发者工具直接编译预览,不走 npm build。三个脚本分开写,本质是把"后端依赖安装、后端运行、前端打包"三条指令解耦,避免首次运行报错时不知道卡在哪一步。我刚拿到工程时会先逐个打开这三个 .bat 看里面的命令,确认路径和本机环境是否对得上,再决定是直接跑还是手动执行。
3.2 后端启动前的数据库初始化
后端启动前必须先把数据库建好,否则 2-run.bat 起来后会有大量连接异常。源码包里一般附带 SQL 脚本,如果没有,就按第 2 章的表结构手动执行。建库时注意字符集要显式指定,避免 MySQL 默认字符集导致后续中文乱码:
mysql -uroot -p CREATE DATABASE IF NOT EXISTS piano_room DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE piano_room; SOURCE /your/path/init.sql;选 utf8mb4 而不是 utf8 的原因:琴房公告、学生姓名里可能输入 emoji 表情或生僻字,utf8mb4 是完整的 UTF-8 四字节实现,能避免"输入特殊符号后插入报错"的经典问题。COLLATE utf8mb4_unicode_ci影响排序和字符串比较规则,统一即可。执行完SHOW TABLES;确认表都在,再执行 2-run.bat。这一步花两分钟,能省掉后面排查接口 500 的一大半时间。
3.3 后端配置里必须改的三个参数
Spring Boot 的application.yml里,数据库连接、端口、JSON 序列化是最容易出问题的三个地方。重点检查这一段:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/piano_room?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8这里三个参数分别解决三类问题。useSSL=false:本地开发 MySQL 默认不自签证书,不关掉会在握手时报 SSL 警告甚至拒绝连接;serverTimezone=Asia/Shanghai:不配的话 MySQL 8 驱动会用服务器默认时区,和本机差 8 小时,预约时间全部偏移;jackson的time-zone: GMT+8保证后端返回给小程序的时间字符串是北京时间。这些参数在毕设答辩演示时最容易暴露,因为评委大概率会问"为什么你这里要写死时区",答案就是:避免默认时区把预约时间显示差 8 个小时。
3.4 微信开发者工具导入小程序端
小程序端如果是 uni-app 工程,先用命令行把依赖装上,再用微信开发者工具导入。注意导入的是编译后的目录而不是源码目录:
npm install npm run dev:mp-weixinnpm run dev:mp-weixin是 uni-app 把源码编译输出到dist/dev/mp-weixin的常规命令。打开微信开发者工具时选择"导入项目",目录指向dist/dev/mp-weixin,AppID 可以先选测试号,本地调试不需要正式 AppID。首次预览需要在工具栏勾选"不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书",否则 request 请求会被拦,页面数据空转。
真机预览时手机和电脑必须在同一局域网,后端地址要改成电脑的局域网 IP,不能是 localhost。我本地演示时一般把 request 的 baseURL 单独抽到一个 config 文件里,换环境只改一处:开发者工具用http://localhost:8080,真机预览改成http://192.168.x.x:8080,不用全局搜索替换。上线前再把这一处换成已备案的 HTTPS 域名,并关掉"不校验合法域名"的勾选。
3.5 小程序端的导航与列表加载两个高频改造点
如果要改造成自己的毕设界面,最先会碰两个点:自定义导航栏高度和列表加载更多。琴房列表页如果用了自定义顶部导航,不同机型(iPhone 刘海屏、安卓状态栏高度不一)上导航栏高度不一样;常见做法是用官方 API 拿胶囊按钮位置计算导航栏高度:
// pages.json 里开启自定义导航后,动态计算导航栏高度 const menuRect = uni.getMenuButtonBoundingClientRect() const statusBarHeight = uni.getSystemInfoSync().statusBarHeight this.navBarHeight = menuRect.height + (menuRect.top - statusBarHeight) * 2 + statusBarHeight这段计算逻辑在旧版 iPhone 和新款安卓机上都能对齐,因为它是基于胶囊实际位置反推,而不是写死 44px。琴房列表如果用分页加载更多,列表页一般就是onReachBottom触发下一页请求,维护一个pageNum和hasMore标志,请求成功后追加数据而不是覆盖。这两处在论文的功能实现章节里都是可以展开讲细的点,评委也爱问。
4. 避坑指南:本地跑通却演示翻车的 5 个高频问题
4.1 开发者工具里数据正常,真机预览一片空白
现象:开发者工具模拟器里页面正常,换成真机预览后页面白屏或接口全部无数据。
原因:真机上请求的地址还是 localhost,手机访问不到电脑;另一个原因是真机模式对 request 合法域名校验比工具内更严格,没勾选跳过校验就会被拦。
解决:把 baseURL 从http://localhost:8080改成电脑局域网 IP,同时在详情面板勾选"不校验合法域名"。如果线上部署过,直接把 request 域名换成已备案的 HTTPS 域名。改完清缓存重新编译,别用旧包。
4.2 两个学生同时预约同一琴房,都成功了
现象:并发测试或同学同时点预约,同一琴房的同一时段出现两条预约记录。
原因:代码里"查冲突 → 写入"两个操作不是原子的,两个请求同时通过查冲突这一步,都进入写入。
解决:最省事的兜底是给 reservation 表加唯一索引(room_id, reserve_date, start_time, end_time),重复写入直接抛 DuplicateKeyException,代码里捕获后提示"该时段已被预约"。要更严谨就开启事务,冲突检查用SELECT ... FOR UPDATE锁住该琴房的行。毕设答辩论到这一层已经能拿分了。
4.3.bak文件被当成源码参与编译
现象:首次 npm install 或编译时出现奇怪语法报错,报错文件指向.vue.bak这类备份文件。
原因:编辑器保存时生成的备份文件还在 src 目录下,部分构建工具的规则会把它们当源文件处理。
解决:删除或移走所有.bak文件再构建。执行find . -name "*.bak" -delete后重新 install。
注意:删除前先确认压缩包里还有原始源文件,或者把备份统一移出工程目录,避免回滚无门。
4.4 后台管理页面表格能开,但数据全是乱码
现象:管理后台学生列表、公告列表中文显示成???或方块。
原因:数据库或表用了 latin1 字符集,或者 JDBC 连接串没带characterEncoding=utf8,两边按各自默认编码读写。
解决:建库时用DEFAULT CHARACTER SET utf8mb4(见 3.2),已有库则执行ALTER DATABASE piano_room CHARACTER SET utf8mb4;并检查每张表的字符集。连接串同时带上characterEncoding=utf8,改完重启后端。乱码数据如果已经写入,需要先清掉再重新导入,连接串改完不会自动修复历史数据。
4.5 预约成功后列表里时间少了 8 小时
现象:学生预约 14:00,管理后台看到的是 06:00。
原因:MySQL 连接未指定 serverTimezone,或者后端 JSON 序列化用了默认时区,时间被按 UTC 处理。
解决:连接串加serverTimezone=Asia/Shanghai,jackson 配置time-zone: GMT+8(见 3.3)。如果数据已经错乱,清洗时统一按DATE_ADD(create_time, INTERVAL 8 HOUR)修正后再对外展示。这个坑在 Windows 和 Linux 上表现还不一样,越早配好越省事。
5. 把演示视频录好:一条可复现的预约验证链路
5.1 演示前强制走一遍链路脚本
录演示视频最怕的不是功能少,是操作和数据库对不上。我的习惯是每次录之前强制走一遍下面的链路,角色、数据、SQL 三者对齐了再开录。
演示链路按两条角色线走:
- 管理员后台新增一间琴房,发布一条公告。
- 学生端注册新账号,登录。
- 首页看到公告、琴房列表,进入琴房详情。
- 选中明天 14:00-15:00,提交预约。
- 返回"我的预约",状态为待使用。
- 再用同一账号预约同一时间,系统提示"该时段已被预约"——这是冲突验证点。
- 切到管理员后台,看到该预约记录,完整体现数据打通。
录制时实时查库佐证,让视频里的每一个界面操作都能和数据表对上:
SELECT s.name AS student, r.room_no, res.reserve_date, res.start_time, res.end_time, res.status FROM reservation res JOIN student s ON res.student_id = s.id JOIN music_room r ON res.room_id = r.id ORDER BY res.create_time DESC;这一步的价值在于,评委问"你这是真的存数据库了吗"的时候,可以直接切到这条 SQL 的结果展示出来,比口头解释有力得多。注意第 6 步别真的提交一条重复预约去制造脏数据,让系统在提交前拦下来即可,否则库里多一条无意义的失败记录,后续查数会被干扰。
我自己拍演示视频时翻过一次车——学生端和管理员用的同一个账号录,角色切换没讲清,评委追问了两分钟。从那以后我每次录演示前都先强制走一遍上面的链路脚本,把角色、数据、SQL 三者对齐再开录。这套思路对后续任何预约类毕设都通用,希望帮到你。
本文还有配套的精品资源,点击获取