☰
医院挂号预约小程序源码解析:Java全栈毕设工程跑通与避坑指南
2026/10/9 5:56:42 网站建设 项目流程

简介:医院挂号预约系统微信小程序毕业设计项目,面向计算机相关专业毕业设计或课程设计人群,提供一套基于Java后端、微信小程序前端与MySQL数据库的完整可运行源码,亦可用于课程设计答辩演示或二次开发起点。系统含管理员与用户双角色,覆盖用户管理、医生/医院/科室信息维护、预约与取消、留言板、公告资讯等典型模块,包含前端小程序页面与后端服务分层组织,接口与页面划分清晰,适合需要快速搭建同类型管理系统或理解前后端交互逻辑的开发者参考。压缩包内含1321个文件,大小27.14MB,主要文件包括Java后端源码、小程序前端页面、vue组件、SQL数据库脚本、XML配置及多张图片素材,另有PDF使用说明和开发工具配置文件,目录结构完整,便于按模块查看和部署。源码附带数据库文件与详细环境说明(JDK1.8、MySQL5.7、Tomcat7及微信开发者工具),可帮助毕业生节省环境搭建时间,快速跑通项目并在此基础上进行功能扩展或二次开发;已有78人学习浏览。

1. 医院挂号预约小程序源码,毕业设计能直接改的Java全栈工程

如果你现在手里拿到的是一个医院挂号预约系统的微信小程序压缩包,里面同时有Java后端、MySQL数据库文件、小程序前端和一堆.bat批处理脚本,这说明你拿到的不是那种缺胳膊少腿的残缺源码。这个项目对应的是典型的本科毕业设计选题:微信小程序端给用户注册、登录、查看医院和医生信息、在科室里预约和取消预约;后台管理端给管理员维护用户、医生、医院、科室、预约记录、取消记录、留言板以及系统公告。技术栈是Java + JDK1.8 + MySQL5.7 + Tomcat7,这套组合在高校毕设里非常成熟,重点不是技术新潮,而是结构完整、能跑通、能讲清楚。适合两类人:一类是准备Java方向毕业设计或课程设计的学生,另一类是刚学完Java Web想拿一个全栈案例练手的小程序开发者。

2. 拆工程结构:从 .bat 到 .vue.bak,先搞清项目里有什么

2.1 三个批处理脚本:install、run、build 到底做了什么

打开压缩包先看根目录,你会看到1-install.bat、2-run.bat、3-build.bat。这三个文件是作者为了让环境一键跑起来写的。我一般拿到源码第一件事不是import到IDE,而是先看脚本内容,能省掉很多手动配置。

1-install.bat的常见做法是执行mvn install把Maven依赖装进本地仓库,同时把数据库脚本放到固定目录,方便后面导入。里面一般长这样:

@echo off cd /d %~dp0 echo [1/3] 编译后端项目... call mvn clean install -DskipTests echo [2/3] 拷贝数据库初始化脚本... copy /Y sql\hospital.sql mysql-init\init.sql echo 安装完成,请先导入数据库后再执行2-run.bat pause

逻辑不复杂:mvn clean install -DskipTests是把Java后端打成war包并跳过单元测试,避免测试类报错中断编译。copy把SQL脚本复制到mysql-init目录,这是为了让数据库初始化脚本集中管理,不用翻遍全项目去找建表语句。cd /d %~dp0是让脚本从任何路径双击都自动切换到自身所在目录,这是批处理最常见的坑——少了它,后面的相对路径全部失效。

2-run.bat负责启动Tomcat或直接用Maven插件跑后端。常见内容是:

@echo off cd /d %~dp0 echo 启动Tomcat... call startup.bat echo Tomcat已启动,请访问 http://localhost:8080/hospital/

这里的startup.bat需要系统环境变量里配好CATALINA_HOME,否则会报“找不到命令”。所以很多工程会改成用mvn tomcat7:run这种插件方式,Maven3.3对tomcat7-maven-plugin的兼容性最好,直接用插件启动能避开Tomcat目录配置的问题。3-build.bat通常是给后台Vue管理端或小程序端用的构建脚本,比如执行npm run build把.vue组件编译成静态资源。三个脚本的顺序很关键:必须先装依赖、再导数据库、最后构建前端。

2.2 前后端分离又没完全分离:Java后端 + Vue后台 + 微信小程序

这个项目的结构不是单层的。后端是Java的SSM工程,SpringMVC负责接口;后台管理端用的是Vue组件,你会看到main.css.bak、IndexMain.vue.bak、IndexAsideStatic.vue.bak这类文件。.vue.bak是Vue单文件组件的备份,IndexMain.vue是主框架,IndexAsideStatic.vue是侧边栏菜单,BreadCrumbs.vue是面包屑导航,update-password.vue.bak是密码修改页的备份。这些备份文件不是无用垃圾,改坏了可以拿.bak把原文件覆盖回去,比git恢复还直观。

微信小程序端是独立的前端工程,里面放pages目录、utils工具类、app.js全局逻辑。小程序的接口通过HTTP请求指向后端的/hospital这个context路径,JSON格式交互。前后端通过HTTP协议通信,但又在同一个压缩包里交付,所以叫“分离又没完全分离”——没有独立的网关和前端部署,更适合毕业设计演示,不需要额外搭Nginx,Tomcat一个容器全搞定。

2.3 那些.bak文件不是垃圾,是修改前的后悔药

很多初学者会把.bak文件当成无用文件直接删掉,我强烈建议不要删。update-password.vue.bak保留了修改前的版本,当你把密码修改页改到接口报错时,直接执行cp update-password.vue.bak update-password.vue就能回滚。我见过有人删了.bak之后改坏了整个后台页面,又找不到原版,最后花两天重写。血泪经验:拿到工程先保留所有.bak,哪怕解压后体积大一点也值。.classpath文件是Eclipse的工程配置,如果你用Eclipse导入,这个文件能帮你自动识别源目录和JAR包;用Idea的话它会被忽略,但也不要删。

3. 数据库设计与初始化:mysql 5.7下建库、导数据、改连接

3.1 先看懂表关系:用户、医生、科室、预约是怎么关联的

医院挂号系统的核心不是界面,是表关系。先打开sql目录下的脚本,或直接用Navicat连接后看库。典型的设计表大概有这些:

表名作用关键字段
user小程序注册用户id, username, password, phone
hospital医院信息id, name, address, level
department科室信息id, hospital_id, name, intro
doctor医生信息id, department_id, name, title, schedule
appointment预约记录id, user_id, doctor_id, dept_id, date, time, status
notice公告资讯id, title, content, create_time
message留言板id, user_id, content, reply

关系是一个医院有多个科室,一个科室有多个医生,一个用户可以预约多个医生。预约表里通常同时带doctor_id和department_id,这是为了方便小程序端“在科室信息里面进行预约”——用户先选科室,再选该科室下的医生,所以预约记录要冗余科室ID,否则列表查询时要多join一次。管理员端的“医生信息管理”“科室信息管理”就是维护这些表的增删改查。个别设计还会把医生表跟科室表做逻辑外键,但实际上MyBatis里直接用关联查询更灵活。

3.2 SQL脚本怎么导入:Navicat11和命令行两种方式

如果用的是Navicat11,操作方法是:新建一个连接,库名建议用hospital,字符集选utf8mb4,然后把.sql文件拖进去执行。但Navicat11有些老版本对utf8mb4支持不太好,所以更稳妥的是用命令行。我一般习惯这么做:

mysql -uroot -p123456 CREATE DATABASE hospital DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE hospital; SOURCE /path/to/hospital.sql;

参数说明:utf8mb4比utf8多支持表情符号,小程序留言板里用户发emoji时,utf8会报 “Incorrect string value” 错误,这是常见的翻车点。SOURCE后面必须用绝对路径,用/分隔,Windows下也别用反斜杠,否则MySQL解析会出错。导入后随便查一下appointment表有无几条测试数据,有数据说明脚本完整。如果导入时报 “Unknown character set”,先打开SQL文件,把头部改成SET NAMES utf8mb4;再执行。

3.3 改数据库连接:jdbc.properties配置里的三个位置

后端连数据库的配置一般在src/main/resources/jdbc.properties。这个文件短,但改错一个参数后端就起不来。默认内容长这样:

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/hospital?useUnicode=true&characterEncoding=utf8&useSSL=false jdbc.username=root jdbc.password=123456

需要改的就是jdbc.url里的localhost:3306、jdbc.username、jdbc.password三处。有三个坑:第一,MySQL5.7的驱动类名是com.mysql.jdbc.Driver,如果你机器上装了MySQL8.0并换成了新版驱动,要改成com.mysql.cj.jdbc.Driver,否则启动报 “ClassNotFoundException”。第二,URL里务必带useSSL=false,Tomcat7自带的SSL配置经常跟新版驱动冲突,不带会在启动时报SSL握手警告。第三,密码不要带#或&这种特殊字符,会被当成URL参数截断,导致Access denied。如果改了还是连不上,先确认你输入的是不是root密码,资源里默认通常是root/123456,但也有作者本机密码是root,看jdbc.properties原值最靠谱。

4. 后端工程跑起来:Maven3.3 + Tomcat7 + JDK1.8的兼容性

4.1 JDK和Tomcat版本为什么是“老组合”

这个项目指定JDK1.8、MySQL5.7、Maven3.3、Tomcat7,看着老,但这是毕业设计里最稳的组合。Tomcat7对应Java EE 6,支持Servlet 3.0,和SSM框架的依赖兼容性最好。如果你非要用Tomcat9甚至Tomcat10,SSM里用的javax.servlet包在Tomcat10里被替换成jakarta.servlet,代码里的import javax.servlet.*全部报红,改起来涉及几十个文件,直接翻车。JDK1.8编译出来的class文件用Tomcat7运行没有任何问题,但如果你本机装的是JDK17,Maven3.3会识别不了,编译直接报 “Unsupported class file major version 61”。所以我一般会在本机保留一个JDK8,专门跑这类毕设项目,别的项目用JDK17互不影响。

Maven3.3是配套JDK1.8的版本,在pom.xml里maven-compiler-plugin的source和target都是1.8。不要自动升级Maven到3.6或3.9,很多老插件的pluginManagement在Maven3.6下会报 “Unsupported major.minor version 52.0”,这个错误很抽象,其实就是Maven和JDK版本不匹配。记住一个原则:拿到源码,先看pom.xml里<java.version>和<maven.compiler.source>,把你本地的Maven、JDK对齐到和它一样。

4.2 从导入到启动:Eclipse/Idea里最稳的四个步骤

我拿到这种工程,不管用Eclipse还是Idea,都按固定顺序来,能避免八成启动失败。

第一步,确认版本。命令行分别执行:

java -version mvn -v

必须看到1.8字样。如果java -version显示17或21,去控制面板或.bash_profile里把JDK切到8。

第二步,导入Maven工程。Eclipse用 Import → Existing Maven Projects,选到包含pom.xml的根目录;Idea用 Open 直接选pom.xml。导入后等依赖下载完,检查pom.xml里有没有spring-webmvc、mybatis、mysql-connector-java这几个坐标。缺少任何一个,启动都会报 “NoSuchBeanDefinitionException”。

第三步,配置Tomcat7。Idea里先添加 Tomcat Server,再把war exploded部署到Server中,Application context填/hospital。这里有个关键:context路径必须跟小程序端wx.request里的url前缀匹配。比如小程序端写的http://localhost:8080/hospital/api/login,你context写成了/Hospital,大小写不一致也会404。如果小程序端写了端口8081,你Tomcat跑8080,那更连不上。

第四步,启动前先确认数据库已经导入。否则Spring启动时SqlSessionFactoryBean初始化失败,报Failed to configure a DataSource。我习惯是先启动MySQL服务,再启动Tomcat,看到Initializing Spring root WebApplicationContext日志后再等三秒。启动成功后访问http://localhost:8080/hospital/login,能看到后台登录页说明后端起来了。如果页面报404,先检查Tomcat的部署列表里有没有这个app,没有就重新deploy。

4.3 后端接口长什么样:以预约模块为例看Controller和Service

你如果在答辩时要讲代码,不需要讲整个项目,把一个核心接口讲透就够了。以“小程序提交预约”为例,经典Controller是这样:

@Controller @RequestMapping("/api/appointment") public class AppointmentController { @Autowired private AppointmentService appointmentService; @RequestMapping(value = "/add", method = RequestMethod.POST) @ResponseBody public Result add(@RequestBody Appointment appointment) { // 1. 校验用户是否已登录 if (appointment.getUserId() == null) { return Result.error("未登录"); } // 2. 校验医生和科室参数 if (appointment.getDoctorId() == null || appointment.getDepartmentId() == null) { return Result.error("医生或科室参数缺失"); } // 3. 调用service插入预约,返回自增id int id = appointmentService.addAppointment(appointment); if (id > 0) { return Result.success("预约成功", id); } return Result.error("预约失败,可能已有重复预约"); } }

这里的@RequestBody表示接收JSON字符串,所以小程序端必须用wx.request的method: 'POST',并且设置header['content-type']为application/json。有些同学按表单方式传data: 'user_id=1&doctor_id=2',后端用@RequestBody解析会拿到null。反过来,如果Controller写的是@RequestParam,小程序端就要用form格式传,或者手动拼在url后面。参数说明:Appointment实体里的date建议用String接收yyyy-MM-dd,time用HH:mm,不要直接用java.util.Date接前端字符串,因为JSON解析日期格式经常报400。等跑通基础流程后,再在Service里做重复预约的校验,第五章会专门讲这个坑。

5. 小程序端常见问题与避坑:导航栏高度、手机号授权、预约冲突的排查记录

这一章是踩坑合集。很多毕设源码后台能启动,但小程序一跑就废,问题集中在下面几个点。我会先理清楚小程序和后端的请求链路,再一个个给排查方法。

5.1 先理一遍小程序端请求链路:wx.request → Java接口 → MySQL

小程序端的代码不是直接从页面跳数据库的。完整链路是:wx.request发HTTP请求 → Tomcat接收 → SpringMVC路由到Controller → Service业务逻辑 → MyBatis操作MySQL → 结果JSON返回。排错时按这条链路一步步看,就像查网络分层模型一样,先看请求到没到后端。

最常翻车的点是wx.request的url。小程序开发工具里如果这样写:

wx.request({ url: 'http://localhost:8080/hospital/api/appointment/add', method: 'POST', header: { 'content-type': 'application/json' }, data: { doctorId: 1, departmentId: 2, date: '2025-04-10', time: '09:00' }, success: res => console.log(res.data) })

在开发者工具模拟器里可能能通,因为模拟器的localhost指向电脑本机。但真机预览时,手机访问localhost是访问手机自己,不是电脑。这里必须改成电脑的局域网IP,比如http://192.168.1.101:8080/hospital/...。同时,如果以后要发布体验版,需要在小程序后台“开发设置”→服务器域名里配置HTTPS域名。本地调试阶段,可以勾选开发者工具右上角的“不校验合法域名、web-view、TLS版本以及HTTPS证书”来绕过。

5.2 问题一:真机调试连不上本地后端,一直转圈

现象:手机打开小程序,请求一直pending,最后报errMsg: request:fail。

原因:手机和电脑不在同一个WiFi,或者Tomcat只监听了127.0.0.1。Tomcat默认监听所有网卡,但如果你在Idea里用Tomcat集成启动,可能被配置成只监听localhost。

解决:先用电脑查IP,再检查Tomcat端口状态。命令行执行:

ipconfig telnet 192.168.1.101 8080

ipconfig能看到无线网卡的IPv4地址,比如192.168.1.101。telnet能通说明网络通畅,通不了就去Windows防火墙里放行8080端口。然后确认apache-tomcat/conf/server.xml里<Connector>没有写address="127.0.0.1",如果写了就把它注释掉。改完重启Tomcat,再把小程序url里的localhost替换成局域网IP。注意,iPhone和电脑连的WiFi如果开了“AP隔离”,即使在同一路由器下也互相访问不了,这种人少的局部网络故障就改用Android手机连热点测。

5.3 问题二:顶部导航栏高度在iPhone上遮住内容

现象:小程序里用了自定义导航栏,在iPhone X及以上的机型上,胶囊按钮和状态栏重叠,页面标题被顶到屏幕外面。这是微信小程序顶部导航栏高度最经典的坑。

原因:自定义导航栏后,小程序不再自动让开状态栏,需要你手动获取statusBarHeight和胶囊按钮位置来计算导航栏总高度。不同机型的状态栏高度不一样,iPhone的刘海屏是44px,很多安卓是24px,写死一个数就会在某些机型上翻车。

解决:在app.js里计算一次并存入globalData:

App({ onLaunch() { const sys = wx.getSystemInfoSync() const menu = wx.getMenuButtonBoundingClientRect() this.globalData.statusBarHeight = sys.statusBarHeight this.globalData.navBarHeight = (menu.top - sys.statusBarHeight) * 2 + menu.height } })

然后在你自定义导航栏的页面wxss里,用padding-top撑开安全区。statusBarHeight是状态栏高度,navBarHeight是状态栏到胶囊按钮底部的完整导航栏高度。如果你压根不用自定义导航栏,就用小程序默认导航栏,这个坑自动消失。但毕设项目里为了好看,很多作者都写了自定义导航栏,跑真机时一定要测iPhone机型。

5.4 问题三:登录时手机号授权弹窗不出现

现象:点击“获取手机号”按钮没有反应,或者控制台提示getPhoneNumber需要用户点击行为。

原因:微信的规则是手机号授权必须由用户主动点击<button open-type="getPhoneNumber">按钮才能触发,不能在onLoad或onShow里自动调wx.getPhoneNumber。另外,该接口需要小程序完成企业或个人主体认证,个人开发者的小程序调用会直接报功能未开通。

解决:在页面里放显式按钮,回调函数里取e.detail.code:

<button open-type="getPhoneNumber" bindgetphonenumber="onGetPhone">获取手机号</button>
onGetPhone(e) { if (e.detail.code) { wx.request({ url: 'http://192.168.1.101:8080/hospital/api/user/phone', method: 'POST', data: { code: e.detail.code }, success: res => console.log('手机号绑定成功', res.data) }) } else { console.log('用户拒绝了授权') } }

这里的e.detail.code是临时code,需要后端拿着它去调微信接口换手机号,不能直接信任前端传过来的明文手机号。毕业设计演示时如果没有认证主体,可以做个“模拟登录”,让用户手动填手机号,反正管理员端能看到手机号就行。重点是别在onLoad里自动弹窗,微信官方已经把这个口子堵死了。

5.5 问题四:重复预约同一时段没被拦截

现象:同一个用户对同一个医生同一时段,点了两次预约,生成了两条记录,管理员后台看到重复数据。

原因:后端没有做唯一性校验。预约表里没有联合唯一索引,Service里也没有查询重复的逻辑。

解决:两层都做。先给appointment表加联合唯一索引:

ALTER TABLE appointment ADD UNIQUE KEY uk_user_doctor_date_time (user_id, doctor_id, date, time);

这样数据库层面能拦截重复,但第二次插入会抛DuplicateKeyException,用户看到的是500错误页。所以还要在Service里先查再插,给用户友好提示:

public int addAppointment(Appointment appointment) { int count = appointmentMapper.checkExist( appointment.getUserId(), appointment.getDoctorId(), appointment.getDate(), appointment.getTime()); if (count > 0) { return 0; } return appointmentMapper.insert(appointment); }

这个checkExist对应的SQL是SELECT COUNT(*) FROM appointment WHERE user_id=#{userId} AND doctor_id=#{doctorId} AND date=#{date} AND time=#{time} AND status != 2,注意把“已取消”的记录排除掉,否则用户取消后再约同个时段会被误拦截。我一般两个方案都做:数据库唯一索引兜底,Service查重给提示。只加索引不写查重,用户体会不到“友好提示”;只写查重不加索引,并发双击下仍可能插两条。

5.6 问题五:返回时间格式变成一串数字

现象:预约列表页里时间显示成1714567890这种数字,用户“我的预约”页一片乱码。

原因:后端Date类型序列化时没指定格式,SpringMVC默认调用了Jackson,把Date转成了时间戳。

解决:在实体字段上加@JsonFormat,或者写全局配置。最省事的是字段注解:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime;

加了这行之后,接口返回的JSON就是2025-04-10 09:00:00这种正常文本。注意timezone必须写GMT+8,否则服务器时区是UTC,返回的时间会比本地时间少8小时。这个问题在毕设答辩时很容易被评委注意到,因为他们会拿手机小程序给你现场演示,看到一个时间戳数字特别尴尬。

6. 进阶:从毕业设计到能演示的挂号系统——会话保持、状态机与数据统计

6.1 用Token或Session保持登录态

这个小程序的后端接口通常靠sessionId判断是否已登录,但小程序没有Cookie机制,所以更稳的做法是登录成功后后端生成一个UUID形式的token,存到内存Map或redis里,小程序每次请求放在header['Authorization']中。后端用一个SpringMVC拦截器统一校验,省得每个Controller都写一遍“if user==null”。如果不想引redis,直接用ConcurrentHashMap也行,毕设场景没有高并发压力,Map足够。答辩时你还能说“这里考虑了扩展性,后期可以替换成redis”。

6.2 预约状态机

预约状态建议用数字:0待就诊、1已完成、2已取消。取消预约不是删除记录,而是把状态改成2。管理员后台的“预约取消管理”就是查状态为2的列表。用户端“我的预约”还能显示历史记录,比你直接delete更有说服力。关键SQL是更新状态时带上条件WHERE id=#{id} AND user_id=#{userId} AND status=0,这样即使用户同时点了两次取消,也只成功一次,且不知道别人的预约。

6.3 一个加分的小功能

在管理员首页加一个统计接口,统计每个科室的医生数量和预约数,用一条GROUP BY就能完成。比如:

SELECT d.name AS department_name, COUNT(DISTINCT doc.id) AS doctor_count, COUNT(app.id) AS appointment_count FROM department d LEFT JOIN doctor doc ON doc.department_id = d.id LEFT JOIN appointment app ON app.department_id = d.id GROUP BY d.id, d.name;

返回结果里appointment_count是预约总量,可以配一个小程序或Vue图表,答辩的演示效果远好于空白的个人中心。如果你还想更亮眼,可以统计公告里最近三天的资讯数,或者留言板的回复率。从那以后,我每次跑这类毕设源码,都会先按“脚本、数据库、后端、小程序、常见坑”这个顺序过一遍,而不是急着写代码。遇到.bak文件也不删,先留着——那是你的后悔药。希望帮到你,把这份资源跑通,再把它改成你自己的东西。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询