简介:一套面向高校毕业设计/课程设计的微信小程序预约挂号系统完整项目包,覆盖管理员、医生、用户三类角色,并附有本地运行辅助配置。后台基于 Java 的 SSM 框架开发,结合 MySQL 数据库实现数据管理,小程序端通过微信开发者工具完成预约交互;核心业务涵盖科室信息维护、医生排班、在线预约、取消预约、调班申请与通知公告等模块,角色分工清晰、流程完整。压缩包共1210个文件,约18.92MB,文件类型覆盖png界面图片、js/wxml/wxss小程序前端文件、vue后台管理页面、java后端代码、json配置、sql数据库脚本与xml配置等,可对照目录按模块查阅,也方便二次开发。目前已有77人浏览学习。资源包内还包含安装、运行、构建等批处理脚本与工程配置文件,有助于快速跑通本地前后端环境;使用者可获取完整源码、数据库脚本及启动辅助工具,从而节省搭建时间,直接聚焦微信小程序与 SSM 整合开发的业务逻辑学习。
1. 为什么毕设题库里总有一款微信小程序预约挂号:三个角色一次讲清
翻一遍课程设计和毕业设计题目库,几乎每年都能看到基于微信小程序的预约挂号系统——这不是巧合。预约挂号同时覆盖了小程序端展示、后端管理、数据库关联查询三块硬功夫,一个题目正好把Java后端框架和移动端开发都练到位。这套资源解压后就是完整工程:小程序端负责用户注册登录、查看医生信息、发起预约操作,SSM后台管医生、科室、排班、审批,MySQL存全部业务数据。这类项目见得多了就能发现,它的优点是角色划分清楚——管理员、医生、用户三套权限边界明确,科室、排班、预约、调班申请这几张表之间的关联逻辑也完整。适合两类人:拿它交课程设计的在校生,以及想快速搭一套预约管理Demo的从业者。别急着双击1-install.bat,后面的坑值得先知道。
2. SSM后台与微信小程序的沟通方式:先搞清这套系统的骨架
2.1 三种角色的权限边界:管理员、医生、用户在数据库里怎么区分
这个系统一上来最值得看的就是角色设计。管理员、医生、用户不是简单的一个user表加role字段糊弄过去,而是拆成了独立的用户表、医生表,管理员直接作为后台账号维护。为什么这么拆?医生身上挂着一堆业务属性:所属科室、职称、擅长方向、排班记录,这些塞进用户表会让表结构又肿又乱;而小程序端的用户只需要openid、手机号、姓名几个字段。把两类差异很大的对象塞到同一张表里,后面写查询和权限控制都会被迫反复判断角色类型,徒增代码量。
我在本地看这套工程时,第一件事就是看数据库脚本里的字段注释。常见的设计是用户表长这样:
CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, openid VARCHAR(64) NOT NULL COMMENT '微信登录唯一标识', username VARCHAR(50) COMMENT '用户昵称', phone VARCHAR(20) COMMENT '手机号,用于接收预约提醒', create_time DATETIME COMMENT '注册时间', status TINYINT DEFAULT 1 COMMENT '1正常 0禁用' ); CREATE TABLE t_doctor ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), dept_id INT COMMENT '关联科室表', title VARCHAR(30) COMMENT '职称:主任医师/副主任医师', intro TEXT COMMENT '医生简介', status TINYINT DEFAULT 0 COMMENT '0待审核 1通过 2驳回' );注意医生表的status默认是0,说明医生注册后要管理员审核才能开始接诊。这是预约挂号系统里比较容易忽略的一环:医生不是注册完就能上岗,后台管理员有一个「医生管理」入口专门做审核。用户表不存密码,因为小程序端走微信登录,密码逻辑交给后台的管理员账号去管。
权限边界对应的功能菜单也分得很清楚:管理员后台有个人中心、用户管理、医生管理、科室信息管理、医生信息管理、排班信息管理、预约信息管理、取消预约管理、调班申请管理、系统管理;医生登录后只有医生信息管理、预约信息管理、取消预约管理、调班申请管理;用户就只能在微信小程序端看医生、看公告、发起预约。菜单越少,权限控制越不容易出漏洞,这个设计对课程设计答辩很友好。
2.2 SSM框架各司其职:Spring、SpringMVC、MyBatis分别扛什么活
后台采用Java生态里经典的SSM组合,也就是Spring + SpringMVC + MyBatis。有些同学上来就问「现在不都用Spring Boot吗,为什么还拿SSM做毕设」,这个问题在答辩时几乎必被问。背后原因一般是课程大纲还没更新到Spring Boot,或者导师明确要求用SSM来考察你对框架原理的理解。Spring Boot虽然把配置简化了,但SSM的手动装配反而能把「容器、路由、持久层」这三个概念讲得更透。
一次请求怎么穿过这三个框架?小程序端发一个请求到后台,SpringMVC的DispatcherServlet先接住,根据URL映射找到对应的Controller;Controller调Service,Service调Mapper接口;MyBatis把Mapper接口和XML里的SQL绑定起来,执行数据库操作,把结果一层层返回。Spring在这个过程里管的是对象创建和依赖注入——Controller、Service、Mapper这些Bean不需要手动new,全由Spring容器管理。
本地跑起来之前,先改jdbc.properties,这是整个系统能不能连上数据库的关键:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/hospital_booking?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=root三个参数最容易出错。第一是characterEncoding=utf8,如果漏掉,小程序端写入的中文会变成问号,用户姓名、科室名全乱码;第二是serverTimezone=Asia/Shanghai,MySQL 8.0默认时区是UTC,不加这个参数,预约时间会差8小时;第三是useSSL=false,本地调试没有SSL证书,MySQL 5.7以上版本默认会尝试SSL握手,报一堆warning,虽然不影响运行,但看着烦。
2.3 小程序端与后台的接口约定:URL怎么配、token怎么带
小程序和后台是两个独立的端,之间只靠HTTP接口通信。接口地址不会写死在每个页面里,一般在项目里抽一个config.js统一维护:
const baseUrl = 'http://localhost:8080/hospital_booking' function request(path, method, data) { return new Promise((resolve, reject) => { wx.request({ url: baseUrl + path, method: method, data: data, header: { 'Content-Type': 'application/json', 'Authorization': wx.getStorageSync('token') }, success: (res) => { if (res.data.code === 200) resolve(res.data.data) else reject(res.data.message) }, fail: reject }) }) } module.exports = { request }这个封装的思路是所有接口共用一套请求头,后端返回的数据也统一包一层结构,前端只判断code就够了。现在比较流行的是code为200表示成功,其他code分别对应用户不存在、登录过期、参数错误等业务异常。这样小程序端每个接口不需要单独处理异常场景,统一弹一个Toast提示message字段即可。
另一个值得注意的点是header里那个Authorization。小程序没有浏览器那种自动携带的Cookie,后端如果用session记录登录状态,小程序端是存不住的。常见做法是登录成功后后端返回一个token,小程序把它存进本地缓存,之后每个接口请求都带上这个token,后端统一拦截校验。如果这套系统里用的是SSM拦截器,可以看看HandlerInterceptor的实现,拦截器里白名单一般只有登录接口和静态资源。
提示:localhost只适合本地调试。真机预览时手机访问不到电脑上的localhost,要么在同一个局域网用电脑IP访问,要么后续部署到云服务器。答辩演示用开发者工具就够了。
3. 本地跑起来的关键三步:从1-install.bat到微信开发者工具
3.1 解压后的文件结构:.bak文件、.bat脚本和Eclipse工程标识
解压后的目录里能看到几个有点怪的文件。main.css.bak、update-password.vue.bak、IndexAsideStatic.vue.bak、BreadCrumbs.vue.bak、IndexHeader.vue.bak——这些不是垃圾文件,而是后台管理页面修改时的旧版本备份。以.vue.bak结尾的说明后台管理界面用了Vue组件方式开发,IndexHeader、BreadCrumbs这些组件名也印证了后台是一个典型的CMS式布局:顶部Header、侧边栏Aside、面包屑导航。.bak文件对运行没有影响,但能看出原开发者的工作节奏——改一个组件之前先把旧文件复制一份,改崩了还能退回去,这个习惯值得学着用。
另一个看点是.classpath和org.eclipse.wst.common.component。这两个文件是Eclipse的工程配置,说明原工程是用Eclipse开发并提交的。拿到工程后最稳的导入方式不是新建项目再拷贝源码,而是直接用Eclipse的Import -> Existing Projects into Workspace,让Eclipse按配置文件还原工程结构。如果强行用IDEA打开,SpringMVC的部署配置可能需要重新补一遍。
还可以顺手列一张文件清单,防止漏看:
| 文件名 | 作用 | 是否需要改动 |
|---|---|---|
| 1-install.bat | 初始化环境、导入依赖和数据库 | 按本机密码改 |
| 2-run.bat | 启动后台服务 | 一般不用改 |
| 3-build.bat | 重新打包构建 | 改代码后用 |
| *.vue.bak | Vue组件旧版备份 | 不需要动 |
| .classpath | Eclipse工程识别文件 | 不需要动 |
3.2 1-install.bat、2-run.bat、3-build.bat:一键脚本背后做了什么
这套脚本的设计思路是让运行者不用记maven命令。1-install.bat负责初始化环境,常见内容是清理并安装依赖:
@echo off echo [1/3] 安装项目依赖... call mvn clean install -DskipTests echo [2/3] 初始化数据库... mysql -uroot -proot < sql/init.sql echo [3/3] 安装完成 pause注意上面的脚本是按「最典型做法」补出来的,实际包里的脚本内容不一定完全一样,但要做的事八九不离十:先编译打包,再把SQL脚本导入MySQL。如果项目里带的是war包而不是jar包,2-run.bat一般会调用Tomcat或者直接启动嵌入式的Tomcat。运行完看到Tomcat started on port(s): 8080,说明后台起来了。
3-build.bat一般是给改动代码之后重新构建用的,日常调试基本用不上。真正操作时,很多人卡在1-install.bat不是因为代码问题,而是mysql命令行没进PATH,或者root密码和脚本里写的不一致。遇到这种情况,手动执行SQL脚本比改脚本更快:
mysql -uroot -p < sql/init.sql手动执行的好处是先确认自己能连上数据库,再谈导入SQL。密码错了会直接提示Access denied,而不是等脚本跑到一半才暴露问题。
3.3 小程序端导入与后台地址配置
后台跑起来之后轮到小程序端。打开微信开发者工具,选择「导入项目」,目录指向工程里的小程序文件夹。注意小程序项目的AppID可以用测试号,不需要自己注册,课程设计阶段完全够用。
导入后先改接口地址。开发者工具有个容易忽略的机制:默认会校验request合法域名,否则本地请求直接被拦。课程设计都是本地联调,不需要真的去配置服务器域名,直接关掉校验更省事:详情 -> 本地设置 -> 勾选「不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书」。这一步不做,小程序打开必白屏,而且是纯前端报错,后端日志里什么都看不到,排查起来很容易误判成后端问题。
注意:关闭域名校验仅限本地开发。如果你的项目要发布体验版,仍然必须在后台配置HTTPS域名。
4. 三张核心表的联动逻辑:科室、排班与预约的数据流转
4.1 科室信息管理:排班表为什么冗余科室名字段
管理员后台第一个业务模块是科室信息管理,这个表看着简单,但它是整个系统的起点。科室表的典型结构:
CREATE TABLE t_department ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT '科室名称', desc TEXT COMMENT '科室简介', sort INT DEFAULT 0 COMMENT '排序字段' );医生表通过dept_id关联科室表。到这里都还是常规设计,真正有意思的是排班表和预约表。做排班查询时,小程序端要展示的列表是「科室名 + 医生名 + 号源余量」,如果把科室名和医生名都靠联表查,每加载一页都要做两次JOIN,演示时数据量小看不出来,但代码写起来很烦。常见做法是在排班表里直接冗余科室名和医生名:
CREATE TABLE t_schedule ( id INT PRIMARY KEY AUTO_INCREMENT, dept_id INT NOT NULL, dept_name VARCHAR(50) COMMENT '冗余科室名', doctor_id INT NOT NULL, doctor_name VARCHAR(50) COMMENT '冗余医生名', schedule_date DATE NOT NULL COMMENT '排班日期', time_slot VARCHAR(20) COMMENT '上午/下午/晚间', total_num INT DEFAULT 30, booked_num INT DEFAULT 0, status TINYINT DEFAULT 1 COMMENT '1可预约 0停诊' );冗余字段违背了数据库第三范式,但在这种读多写少的业务场景里收益很高。排班列表一次查询直接出全部展示字段,不需要反复联表。答辩时如果有人问「为什么冗余」,回答「用空间换查询性能」就是标准答案。
科室信息管理页面通常提供增删改查,但删除科室前要检查该科室下有没有医生,不然会出现「科室已删除,医生还挂在空科室」的数据孤儿。常见实现是先查询医生表里dept_id是否还有引用,有引用就提示「该科室下存在医生,请先处理医生信息」。
4.2 预约信息表的状态流转:从已预约到已完成再到已取消
预约信息表是整套系统里数据变化最频繁的表。用户在小程序端选一个排班,发起预约,生成一条预约记录。后续动作有三种:医生在后台把预约标记成已完成;用户或管理员取消预约;预约时间到了没来,默认是爽约。状态字段的值一般是数字枚举:
CREATE TABLE t_appointment ( id INT PRIMARY KEY AUTO_INCREMENT, schedule_id INT NOT NULL, user_id INT NOT NULL, appoint_no VARCHAR(32) COMMENT '预约号', status TINYINT DEFAULT 0 COMMENT '0已预约 1已完成 2已取消 3爽约', create_time DATETIME, cancel_time DATETIME );预约成功之后,排班表的booked_num要加1;取消预约后要减1。这一步如果放在前端代码里做,会出现并发问题——两个用户同时取消,booked_num被减两次。正确做法是在同一个事务里更新预约状态和排班号源:
UPDATE t_appointment SET status = 2, cancel_time = NOW() WHERE id = ? AND status = 0; UPDATE t_schedule SET booked_num = booked_num - 1 WHERE id = ? AND booked_num > 0;两条UPDATE放一个事务里,后者通过受影响行数判断是否成功。这个细节做好了,答辩时能讲的东西就多了一个:你考虑过并发场景。很多同学写预约功能只做了INSERT,没有同步维护号源余量,虽然单机演示看不出来,但逻辑上是不完整的。
4.3 调班申请的实现思路:医生提交、管理员审批
调班申请是这套系统里比较少见的功能模块,也是和普通预约挂号Demo拉开差距的地方。医生如果某天临时有事,不能直接在排班表里改数据,而是先提交一条调班申请,注明原排班、目标时间、调班原因,管理员审核通过后才生效。
调班申请表的核心字段设计:
CREATE TABLE t_adjust_apply ( id INT PRIMARY KEY AUTO_INCREMENT, doctor_id INT NOT NULL, schedule_id INT NOT NULL COMMENT '原排班id', target_date DATE COMMENT '目标日期', target_slot VARCHAR(20), reason VARCHAR(255), status TINYINT DEFAULT 0 COMMENT '0待审核 1通过 2驳回', apply_time DATETIME, audit_time DATETIME );管理员在后台的「调班申请管理」里看到待审核列表,点通过时系统做两件事:把原排班状态改成停诊,把医生插入目标日期的排班。如果原排班已经有用户预约,常见做法是先电话通知用户,系统层面则不提供自动迁移,这一点在答辩时可以如实说明,反而显得考虑过真实业务。
医生端为什么没有直接改排班的权限,这也是权限设计的一个亮点。如果医生能直接改排班,用户已经预约的时段就会悄悄被改掉,产生纠纷。强制走调班审核流程等于给排班变更留了审计记录,谁申请的、什么时候批的、原排班改成什么,全部查得到。
5. 避坑:微信小程序预约挂号最常见的5个翻车现场
5.1 现象:小程序打开白屏,控制台报「不在以下合法域名列表中」
原因:微信开发者工具默认开启合法域名校验,本地调试用的http://localhost:8080不在列表里,请求被前端直接拦掉,后端没有收到任何请求。
解决:详情 -> 本地设置 -> 勾选「不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书」,改完重新编译。如果关闭校验后还是白屏,再看Network面板里有没有请求发出,确认是不是baseURL配错了。
5.2 现象:后台管理页面能打开,小程序登录却报500
原因:登录接口的URL配错了。最常见的是小程序端config.js里的baseURL写成https://或者是其他端口。另一个很隐蔽的原因:openid获取失败。微信登录需要调用微信接口换取openid,如果小程序AppID使用的是测试号,某些接口权限受限,后端拿不到openid,返回500。
解决:先打开浏览器直接访问「后台地址/api/user/login」,用POST工具测一下接口本身通不通。通,再抓小程序端的请求URL,把baseURL改成浏览器访问的那个地址。后端日志一般在Tomcat窗口或者logs目录下,500错误看堆栈最直接。
5.3 现象:1-install.bat执行到一半中断,提示Access denied for user
原因:脚本里写的MySQL账号密码与本机不一致。很多毕设包默认用户名root,密码是123456或root,而你自己机器上的root密码是另一个。
解决:先用mysql -uroot -p手动登录确认,登录成功后执行:
mysql -uroot -p < sql/init.sql如果不想手动敲命令,直接改jdbc.properties里对应的密码,然后重新执行install脚本。这时候还要同步检查applicationContext.xml里有没有写死的数据源,有些SSM项目会把数据库配置写在XML里,只改properties不生效。
5.4 现象:预约时间显示差8小时
原因:MySQL服务器的时区是UTC,JDBC连接串没有指定时区,后端查出来的时间比北京时间少8小时。这个坑在本地开发时最容易出现,因为Windows时区其实已经是东八区,但MySQL系统变量还是默认的UTC。
解决:jdbc.url里加上serverTimezone=Asia/Shanghai,改完重启应用。如果已经插入的测试数据时间错了,用SQL批量修正:
UPDATE t_appointment SET create_time = DATE_ADD(create_time, INTERVAL 8 HOUR);顺手记一个查时区的方法:执行SELECT NOW();看输出时间和当前时间对不对得上,对不上再改连接串,别盲目改MySQL全局时区,那个影响面太大。
5.5 现象:.bak文件在编辑器里打开是一堆乱码
原因:.bak后缀被系统当成未知文件,用文本编辑器打开时按系统默认编码解析,文件里的中文字符全花了。
解决:不要直接改后缀,先在IDE里确认编码是UTF-8,再把扩展名从.bak改成.vue打开。改后缀之前先复制一份,防止IDE识别出错把原文件搞坏。另外Maven构建时默认只编译特定目录下的文件,.bak不会被当成源码编译,所以它们躺在工程里不影响打包。
6. 从毕设到能演示的进阶技巧:把预约流程走通并录成演示视频
6.1 演示前必做的数据准备
演示最常见的翻车不是代码跑不起来,而是打开小程序看不到数据。预约流程必须有医生、有排班、有号源,不然演示时只能对着空页面解释。
按这个顺序准备数据:先在后台建两个科室(内科、外科),再注册两位医生并通过审核,给每个医生生成未来两天的排班,排班的号源不用改默认值,保持30个。最后用小程序端注册一个用户,真实走一遍预约。关键检查项是:科室下拉框有数据、医生列表能显示、点进排班后号源余量是30。这三点都正常,演示就不会卡壳。
6.2 一条让答辩稳住的演示路线
演示顺序就是业务流程顺序,不要跳着点菜单。第一次进系统先以用户身份登录小程序,去科室页找一个医生,发起预约,预约成功后截图;然后切到医生端登录,看到刚才的用户预约记录,把状态改成已完成;再切到管理员后台,从预约信息管理里看到这条记录。最后补一个调班申请:医生提交申请,管理员审批通过,回到排班信息管理里看到原排班已停诊。
这套流程把三个角色串成了一条完整链路,一共不到五分钟,但每个功能模块都覆盖到了。录屏记得把微信开发者工具的Network面板和后台控制台的日志同时录进去,评阅人看到真实请求比看口述更有说服力。这套资源本身已经打包好了运行脚本和数据库初始化SQL,下载后按这条主流程走一遍,基本不用再到处搜教程。
从那以后我每次拿到别人的毕设包,都强制自己先把主流程完整跑通再动代码,连数据库密码这种最不起眼的字段也会先核对一遍——很多看似神秘的问题,最后都落在基础配置上。希望帮到你。
本文还有配套的精品资源,点击获取