☰
SSM+Vue医院挂号系统源码实战:环境搭建、预约链路与避坑指南
2026/10/4 19:44:33 网站建设 项目流程

简介:这是一套面向高校计算机专业毕业设计与课程实践的医院门诊在线预约挂号管理系统完整源码,采用Java语言与SSM框架开发,前端基于Vue配合Ajax交互,后端整合Spring、SpringMVC、MyBatisPlus,数据库使用MySQL 5.7,构建工具为Maven,兼容JDK1.8,可在Eclipse、MyEclipse或IDEA中导入运行。资源包共640个文件,约15.61MB,其中152个java文件承载核心业务逻辑,111个vue文件与44个js文件构成前端页面与交互,另有25个xml配置、17个css样式及svg、jpg、png等图片素材,并附sql建库脚本与docx说明文档,目录结构清晰,便于按模块查阅。系统覆盖用户信息管理、图片与视频素材维护等基础功能,适合作为毕业设计选题参考或SSM加Vue技术栈的练手项目。目前已有140人学习下载,可帮助读者快速理解预约挂号业务的表结构设计、前后端分离调用方式与项目部署流程,节省从零搭建的时间成本。

1. 从一份带 .bak 的 SSM 挂号系统源码说起:它到底能跑出什么

如果你拿到的是一份医院门诊在线预约挂号管理系统的 Java 源码包,解压后第一眼看到的不是pom.xml,而是一堆update-password.vue.bak、main.js.bak、IndexMain.vue.bak、IndexAsideStatic.vue.bak、BreadCrumbs.vue.bak、IndexHeader.vue.bak,外加2-run.bat、3-build.bat、build.bat、run.bat四个批处理,那你大概率会先愣一下:这到底是能直接跑的成品,还是别人改到一半丢出来的半成品?我拆过不少基于 SSM 的毕业设计级项目,这种「前端 Vue 备份文件 + 后端 SSM + 一键 bat」的组合其实很典型——它是一套基于 Web 的医院门诊在线预约挂号系统,后端用 Spring + SpringMVC + MyBatisPlus,前端用 Vue 加 ajax 交互,数据库 MySQL 5.7,JDK 1.8,Maven 管依赖。核心业务就是科室、医生、排班、号源、预约订单、用户信息这几块,能解决「患者在线选科室选医生选时段、后台维护号源」这条主链路。适合谁?正在做 Java Web 课程设计或毕业设计、需要一套能跑通预约流程的 SSM 参考实现、并且愿意花半小时把环境对齐的人。下面我按「先立住原理、再动手复现、最后说坑」的顺序拆开讲。

2. 技术栈选型与工程结构:SSM + Vue 这套组合为什么这么搭

2.1 后端为什么是 SSM 而不是 SpringBoot

这套系统选 SSM(Spring + SpringMVC + MyBatisPlus)而不是 SpringBoot,不是技术落后,而是毕业设计场景下的现实选择。SpringBoot 把 Tomcat 内嵌、自动配置全包了,好处是启动快,坏处是「看不见的东西太多」——答辩时老师问「你的 DispatcherServlet 在哪配的」「事务管理器怎么声明的」,答不上来就尴尬。SSM 把web.xml、spring-mvc.xml、applicationContext.xml、mybatis-config.xml全摊在明面上,每一层职责清晰:Spring 管 Bean 和事务,SpringMVC 管请求映射和视图解析,MyBatisPlus 管数据访问并额外提供了BaseMapper的 CRUD 封装。

MyBatisPlus 在这里的价值要单独说。挂号系统里科室、医生、排班、订单都是标准单表增删改查,用BaseMapper<T>直接继承就能拿到selectById、insert、updateById、selectPage,省掉大量 XML。只有「按科室查医生」「按医生查可预约时段」这类多条件联查才需要自己写QueryWrapper或 XML。常见做法是:实体类加@TableName和@TableId,Service 层继承ServiceImpl,Controller 直接调page()做分页。

2.2 前端 Vue 与 .bak 文件的关系

那堆.bak文件是理解这份源码的关键。IndexMain.vue.bak、IndexAsideStatic.vue.bak、IndexHeader.vue.bak、BreadCrumbs.vue.bak说明前端原本有一套布局组件(主内容区、侧边栏、头部、面包屑),作者在改版时把旧版本备份成了.bak,新版本可能改名或合并了。update-password.vue.bak则是修改密码页面的旧版。main.js.bak是入口文件的备份。

这意味着:当前生效的前端文件不是.bak,而是同名去掉.bak的文件。如果你直接改.bak,页面不会有任何变化,这是新手最容易翻车的地方。正确做法是先确认目录下有没有对应的.vue和main.js,.bak只当参考。

2.3 目录结构与四个 bat 脚本

典型结构大致如下:

路径作用
src/main/java后端 Java 源码,按 controller/service/mapper/entity 分包
src/main/resourcesapplicationContext.xml、spring-mvc.xml、mybatis-config.xml、jdbc.properties
src/main/webappWEB-INF/web.xml、静态资源
前端目录(如vue/或src/).vue组件、main.js、router
pom.xmlMaven 依赖声明
2-run.bat/run.bat启动脚本
3-build.bat/build.bat构建打包脚本

2-run.bat和run.bat通常是同一逻辑的两个版本(一个带序号方便排序),内容多是mvn tomcat7:run或mvn spring-boot:run之类;3-build.bat和build.bat则是mvn clean package。先看 bat 内容再执行,别双击就跑,里面可能写死了本机路径。

3. 环境搭建与数据库初始化:把 JDK1.8、MySQL5.7、Maven 对齐

3.1 版本对齐是第一步

这套系统对版本敏感。JDK 必须是 1.8,MySQL 必须是 5.7(8.0 的驱动类和时区配置不同,容易连不上),Maven 用 3.6 以上即可。先验证:

java -version # 期望输出:java version "1.8.0_xxx" mvn -v # 期望输出:Apache Maven 3.x.x,且 Java version: 1.8 mysql --version # 期望输出:mysql Ver 14.14 Distrib 5.7.xx

逻辑说明:java -version确认 JDK 是 1.8,因为 SSM 老项目的pom.xml里<maven.compiler.source>常写死 1.8,用高版本 JDK 编译可能报Unsupported class file major version。mvn -v同时会打印 Maven 使用的 Java 版本,如果这里显示的是 11 或 17,说明JAVA_HOME指错了,需要改环境变量。mysql --version确认是 5.7 系列。

3.2 建库与导入 SQL

数据库名一般在jdbc.properties里,常见是hospital或yiyuan。先建库再导表:

CREATE DATABASE hospital DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE hospital; -- 然后执行源码包里的 .sql 文件 SOURCE /path/to/hospital.sql;

参数说明:utf8mb4是为了兼容中文和特殊字符,utf8mb4_general_ci是排序规则,5.7 下通用。SOURCE是 MySQL 客户端命令,路径换成你实际的.sql文件位置。导入后执行SHOW TABLES;确认科室表、医生表、排班表、订单表、用户表都在。

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=你的密码

逻辑说明:MySQL 5.7 用com.mysql.jdbc.Driver,8.0 才用com.mysql.cj.jdbc.Driver,写错会报ClassNotFoundException。useSSL=false关掉 SSL 握手,避免本地连接时的警告和超时。characterEncoding=utf8保证中文不乱码。改完保存,别改文件名。

3.4 用 bat 或命令行启动

先看run.bat内容,确认是mvn tomcat7:run还是别的。如果不想用 bat,直接命令行:

mvn clean package -DskipTests mvn tomcat7:run

逻辑说明:clean package清掉旧产物并打包,-DskipTests跳过测试加快速度。tomcat7:run是 tomcat7-maven-plugin 提供的启动目标,端口一般在pom.xml的<port>里,常见 8080。启动后浏览器访问http://localhost:8080/看首页是否出来。

4. 核心业务链路:号源、排班、预约订单怎么串起来

4.1 号源与排班的数据模型

挂号系统的核心不是「预约」这个动作,而是「号源」这个资源。排班表记录某医生某天某时段有多少号,号源表记录每个号的状态(可约/已约/停诊)。常见设计是:

表关键字段说明
doctorid, name, dept_id, title医生基本信息
scheduleid, doctor_id, work_date, time_slot, total_num排班,某天某时段总号数
orderid, user_id, schedule_id, status, create_time预约订单
userid, username, password, phone用户

预约时先查schedule剩余号数,再插order,同时把schedule的已约数加一。这三步必须在同一个事务里,否则并发下会超卖。

4.2 用 MyBatisPlus 写预约接口

Controller 层大致这样:

@RestController @RequestMapping("/api/order") public class OrderController { @Autowired private OrderService orderService; @PostMapping("/book") public Result book(@RequestBody BookDTO dto) { // dto 含 userId、scheduleId return orderService.book(dto); } }

Service 层是重点,事务和并发控制都在这里:

@Service public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService { @Autowired private ScheduleMapper scheduleMapper; @Override @Transactional(rollbackFor = Exception.class) public Result book(BookDTO dto) { // 1. 查排班,判断是否还有号 Schedule schedule = scheduleMapper.selectById(dto.getScheduleId()); if (schedule == null || schedule.getBookedNum() >= schedule.getTotalNum()) { return Result.fail("号源已满"); } // 2. 乐观锁式更新,避免并发超卖 int updated = scheduleMapper.increaseBooked(dto.getScheduleId()); if (updated == 0) { return Result.fail("手慢了,号已被抢"); } // 3. 写订单 Order order = new Order(); order.setUserId(dto.getUserId()); order.setScheduleId(dto.getScheduleId()); order.setStatus(0); // 0 待就诊 order.setCreateTime(new Date()); this.save(order); return Result.ok(order); } }

逻辑说明:@Transactional保证三步要么全成要么全回滚。increaseBooked是自定义 Mapper 方法,SQL 里带WHERE booked_num < total_num,靠数据库行锁保证并发安全,比先查后改更稳。参数status=0表示待就诊,后续可扩展 1 已就诊、2 已取消。

4.3 前端 Vue 怎么调这些接口

前端用 axios 或 ajax 调/api/order/book,IndexMain.vue这类组件负责渲染医生列表和时段按钮。注意.bak文件不生效,要改的是当前.vue。请求示例:

axios.post('/api/order/book', { userId: this.userId, scheduleId: this.selectedScheduleId }).then(res => { if (res.data.code === 200) { this.$message.success('预约成功'); } else { this.$message.error(res.data.msg); } });

逻辑说明:userId一般从登录后存的 session 或 localStorage 取,scheduleId是用户点选的时段。返回code判断成功失败,msg直接展示给用户。这套前后端分离的写法在 SSM 项目里通常靠@ResponseBody或@RestController返回 JSON。

5. 避坑与排查:那些让项目跑不起来的常见问题

5.1 改了 .bak 文件页面没反应

现象:修改IndexMain.vue.bak后刷新页面,布局毫无变化。原因:.bak是备份文件,不参与编译,真正生效的是同名.vue。解决:找到对应的.vue文件改,.bak只用来对比旧逻辑。

5.2 启动报 ClassNotFoundException: com.mysql.jdbc.Driver

现象:Tomcat 启动时抛驱动类找不到。原因:MySQL 8.0 的驱动类是com.mysql.cj.jdbc.Driver,而jdbc.properties里写的是 5.7 的旧类名,或者pom.xml里 mysql-connector 版本和数据库不匹配。解决:确认数据库是 5.7 就用com.mysql.jdbc.Driver并把 connector 版本锁到5.1.x;如果是 8.0,改类名并加serverTimezone=Asia/Shanghai。

5.3 中文乱码

现象:页面显示问号或乱码。原因:数据库字符集、连接串characterEncoding、Tomcat 的URIEncoding三处任一没对齐。解决:建库用utf8mb4,连接串加useUnicode=true&characterEncoding=utf8,web.xml或 server.xml 里确认URIEncoding="UTF-8"。

5.4 端口被占用

现象:mvn tomcat7:run报Address already in use。原因:8080 被别的进程占了。解决:netstat -ano | findstr 8080找到 PID,任务管理器结束,或改pom.xml里的<port>换一个。

5.5 预约并发下超卖

现象:两个用户同时预约最后一个号,都成功了。原因:先查后改没有锁。解决:用带条件的UPDATE ... WHERE booked_num < total_num,判断影响行数,为 0 就返回失败,配合@Transactional。

6. 进阶技巧:把号源校验做成可复用的拦截与验证

跑通主链路后,真正让这套系统「像个系统」的,是把号源校验从散落的 if 里抽出来。我一般会做一个自定义注解加拦截器,在进入预约接口前先校验排班是否存在、是否过期、是否停诊,把业务校验和参数校验分开。

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CheckSchedule { }

拦截器里读scheduleId参数,查一次排班,把结果塞进request属性,Controller 直接取,避免重复查库:

public class ScheduleInterceptor implements HandlerInterceptor { @Autowired private ScheduleMapper scheduleMapper; @Override public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception { String sid = req.getParameter("scheduleId"); if (sid == null) { resp.getWriter().write("{\"code\":400,\"msg\":\"缺少排班参数\"}"); return false; } Schedule schedule = scheduleMapper.selectById(Long.valueOf(sid)); if (schedule == null || schedule.getStatus() == 2) { resp.getWriter().write("{\"code\":400,\"msg\":\"该排班不可预约\"}"); return false; } req.setAttribute("schedule", schedule); return true; } }

逻辑说明:preHandle返回 false 直接中断请求,返回 true 才进 Controller。status == 2表示停诊。把排班对象放进 request,Controller 里(Schedule) request.getAttribute("schedule")就能拿到,省一次查询。参数上,scheduleId用Long.valueOf转换,注意捕获NumberFormatException,否则传个字母进来就 500。

验证方法很简单:用 Postman 或浏览器直接请求/api/order/book?scheduleId=999,看是否返回「该排班不可预约」;再请求一个正常 id,看是否进业务。两个都符合预期,说明拦截器生效。

还有个容易被忽略的点:号源释放。用户取消预约时,要把schedule的已约数减一,同样用带条件的UPDATE ... WHERE booked_num > 0,并和订单状态更新放同一事务。我见过太多项目只加不减,跑几天号源就全满了,这是血泪经验。

从那以后我每次拿到这类 SSM 源码,都强制先做三件事:确认.bak不生效、核对 JDK 和 MySQL 版本、把预约和取消两条链路的事务走一遍。希望帮到你。

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

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

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

立即咨询