SpringBoot+Vue医疗服务系统毕业设计:前后端分离实战与避坑指南

发布时间:2026/10/28 15:51:28
SpringBoot+Vue医疗服务系统毕业设计:前后端分离实战与避坑指南 简介一套基于SpringBoot与Vue的医疗服务系统完整源码和配套数据库来自个人毕业设计项目答辩评审得分98分。代码经过调试测试可直接运行适用于计算机、通信、人工智能、自动化等专业学生的毕业设计、课程设计或期末大作业也可作为Java Web前后端分离开发的学习案例。资源包共141个文件压缩后大小12.72MB主要包括32个Java后端源码、28个Vue前端组件、2个SQL数据库初始化脚本另有JS、CSS、XML配置、项目文档及png/jpg等界面图片完整覆盖从后端接口到前端页面的工程结构。目前已有73人学习下载。相比零散源码该资源提供全套可运行工程与数据库脚本能显著降低环境搭建和调试验证成本基础较强的学习者还可基于现有模块扩展预约挂号、在线问诊等业务整体具有很高的参考与二次开发价值。1. 医疗服务系统毕业设计SpringBootVue 到底在做什么如果你正在为毕业设计发愁手里攥着「基于SpringBootVue的医疗服务系统源码数据库」这个标题那大概率想要的是一个能演示、能答辩、能拿得出手的项目。这套系统的核心并不复杂前端用 Vue 负责页面和用户操作后端用 SpringBoot 提供接口和数据服务MySQL 存业务数据。它覆盖了挂号、问诊、药品管理、用户登录这些典型场景恰好是医疗信息化的入门级需求。这个方案适合两类人一是想快速跑通一个完整前后端分离项目的学生二是想借这个题目练手 SpringBoot 和 Vue 联调的新手。源码和数据库都有了但如果你只是无脑启动很容易卡在环境配置、版本冲突和联调问题上。这篇文章我会按真实做项目的顺序把选型、搭建、核心模块、踩坑点和拿高分的小技巧一次讲清楚让你拿到这套东西后不是黑匣子而是真正能改、能讲、能扩展的作品。2. 技术选型为什么是 SpringBootVue前后端分离的骨架与数据库落点2.1 SpringBoot 负责什么后端接口与业务状态在医疗服务系统里SpringBoot 承担的是「服务端」角色。它要做的事很明确接收前端发来的 HTTP 请求校验参数调用 Service 层处理业务逻辑比如挂号是否冲突、药品库存够不够再通过 Mapper 或 Repository 操作数据库最后把结果封装成 JSON 返回给前端。SpringBoot 相比传统 Spring MVC 最大的优势是自动配置——你不用再手写一堆 XML 配置数据源、包扫描、视图解析器一个spring-boot-starter-web依赖加上application.yml就能把 Web 服务跑起来。用 SpringBoot 做这种单体毕业设计还有一个实际理由部署简单。它内置 Tomcat打成一个 jar 包直接java -jar就能启动不需要单独装 Tomcat 再丢 war 包。对答辩现场来说这能少一半环境问题。后端代码结构一般按Controller - Service - Mapper分层业务逻辑写在 ServiceController 只做参数接收和结果返回。医疗服务系统里的「挂号」「开处方」「药品入库」这些操作本质上都是对数据库表的增删改查所以后端的工作量并不大关键是表结构设计要合理。2.2 Vue 负责什么页面交互与路由Vue 在这套系统里负责所有用户看得到的东西登录页、挂号页、医生工作台、药品管理表格。它通过 axios 请求后端接口拿到 JSON 数据后渲染到页面上。Vue 是渐进式框架你不需要引入全家桶只用 Vue Router 做页面跳转、Vuex 或 Pinia 存登录状态配合 Element UI 或 Ant Design Vue 就能做出像样的后台界面。为什么不用传统的 JSP Thymeleaf因为前后端分离是现在行业的主流做法也是毕业设计答辩时的一个加分点。你可以明确告诉评委前端和后端可以独立开发、独立部署前端打包后的静态文件也能放进 SpringBoot 的static目录里统一部署。这一点在后面联调章节我会细说。Vue 里有一件事很容易踩坑就是路由跳转和组件之间的参数传递。比如从挂号列表点到「新增挂号」你可能需要用this.$router.push带上医生 ID 和排班 ID这些参数如果没传对后端接口就查不到数据。2.3 数据库设计MySQL 表结构怎么拆数据库是这套系统里最容易拉开分数差距的部分。给的是现成 SQL 脚本但你要能讲清楚为什么这么建表以及表之间的关系。一个常规的医疗服务系统至少需要这几张表表名核心字段作用sys_userid, username, password, role登录账号role 区分管理员/医生/患者patient_infoid, user_id, name, id_card, phone患者基本资料doctor_infoid, user_id, name, department, title医生信息和科室departmentid, dept_name, intro科室表registrationid, patient_id, doctor_id, visit_time, status挂号记录drugid, drug_name, spec, stock, price药品库存prescriptionid, registration_id, doctor_id, create_time处方单头prescription_itemid, prescription_id, drug_id, quantity, dosage处方明细这种设计遵循了第三范式药品信息存在drug表处方明细只存drug_id不会出现重复冗余。挂号表和处方表是核心关联表后面做联查时会频繁用到 JOIN。你拿到 SQL 脚本后第一件事应该是用 Navicat 或 DataGrip 导入然后打开「模型」功能把表关系图画出来——答办时直接展示这张 ER 图比空口讲设计有效得多。3. 把系统跑起来从源码导入到前后端联调的最小步骤3.1 后端启动Maven 配置与 SpringBoot 版本坑拿到源码后先用 IntelliJ IDEA 打开后端目录。常见结构是一个 Maven 工程pom.xml里声明了 SpringBoot 父依赖、MyBatis-Plus 或 JPA 依赖。这里最大的坑是 SpringBoot 版本。网上很多毕业设计源码的 SpringBoot 版本停在 2.x而你本地的 JDK 可能是 17 甚至 21。如果版本搭配不对启动会直接报UnsupportedClassVersionError或者Invalid value for --release。我一般会先检查pom.xml里的spring-boot-starter-parent版本再确认本地 JDK 版本。常见的稳定搭配是parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent这个版本支持 JDK 8 和 JDK 11大多数学校机房的环境都能跑。如果你机器上装的是 JDK 17建议装一个 JDK 11 或者改用 SpringBoot 2.7.18不要硬上 SpringBoot 3.x因为 3.x 的javax包改成了jakarta源码里大量import javax.servlet会全部报错。修改完版本后点击 Maven 面板的reload重新加载依赖然后运行src/main/java下带SpringBootApplication注解的启动类。后端跑通后控制台会出现 Spring logo 和端口号。默认端口在application.yml里配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/medical?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver这里需要注意serverTimezone必须设置否则连接 MySQL 8.x 会报时区错误。password要改成你本地的数据库密码源码默认写的常常是123456不改的话启动会报数据库连接失败。3.2 前端启动Vue 环境配置与依赖安装前端目录一般叫medical-ui或者vue-admin里面有package.json。首先确保你安装了 Node.js推荐 16 或 18 版本太高的 Node 20 有时会和旧版 Vue CLI 项目产生兼容问题。然后打开终端进入前端目录执行依赖安装npm install如果网络不好或者安装超时可以用淘宝镜像npm install --registryhttps://registry.npmmirror.com安装完成后启动开发服务器npm run dev启动成功后会看到Local: http://localhost:8081/这样的提示。注意前端端口和后端端口不能一样否则会冲突。接着要改前端里配置的后端地址一般在一个叫src/utils/request.js或者.env.development文件里。你需要把请求基础路径指向后端// src/utils/request.js 中常见配置 const service axios.create({ baseURL: http://localhost:8080/api, // 后端接口前缀要和后端Controller的RequestMapping对应 timeout: 10000 })这一步是前后端联调的命门。如果后端接口路径写的是/api/user/login前端baseURL就必须带/api否则会 404。建议先不依赖前端页面用浏览器直接访问http://localhost:8080/api/user/login测试后端是否响应能返回 JSON 再继续调页面。3.3 数据库初始化SQL 脚本导入与账号密码修改源码包里通常有一个medical.sql或db/目录下的 SQL 文件。用 Navicat 新建一个名为medical的数据库字符集选utf8mb4然后右键数据库选择「运行 SQL 文件」导入。导入完成后检查一下表数量正常应该有上面说的 7 张表左右另外还有菜单表或日志表。导入后最常见的问题是两个一是 SQL 文件里的数据库名和你现在建的库名不一致二是密码加密方式不匹配。SpringBoot 项目里如果用了mybatis-plus登录校验可能用的是MD5加密后的密码你直接往sys_user表插一条明文密码是登不进去的。建议先用源码初始化好的账号测试比如 admin/123456。如果账号密码不对去后端代码里找PasswordEncoder或MD5Util的加密逻辑把数据库里的密码改成对应加密串。不要改代码去兼容明文答辩时被问「密码为什么明文存储」会很尴尬。数据库连接成功的标志是启动日志里出现HikariPool-1 - Start completed。看到这行说明后端和数据库之间的通道已经打通。4. 核心模块拆解挂号、问诊、药品管理怎么做4.1 用户登录与 JWT 鉴权医疗服务系统的登录逻辑大同小异前端提交账号密码后端校验成功签发一个 token前端把 token 存到localStorage之后每次请求都带上它后端拦截器校验 token 是否合法。这样做的好处是后端接口不需要像 session 那样记忆客户端状态对前后端分离的项目来说是标准做法。后端登录接口的典型写法RestController RequestMapping(/api/user) public class UserController { Autowired private UserService userService; PostMapping(/login) public Result login(RequestBody LoginDTO loginDTO) { // 1. 按用户名查询用户 User user userService.findByUsername(loginDTO.getUsername()); // 2. 校验密码此处用MD5对比 if (user null || !user.getPassword().equals(MD5Util.md5(loginDTO.getPassword()))) { return Result.error(用户名或密码错误); } // 3. 生成JWT有效期2小时 String token JwtUtil.generateToken(user.getId(), user.getRole()); return Result.ok(token); } }这里JwtUtil通常用jjwt库实现生成一个包含userId和role的 token。前端拿到 token 后在axios拦截器里统一加到请求头service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config })要注意拦截器的触发时机。我见过很多翻车案例都是前端在请求拦截器里取了localStorage但登录成功时还没跳转页面所以需要确保登录后先setItem再跳转。另外后端拦截器要放行/api/user/login这个路径否则登录请求本身也会被拦截。通常用HandlerInterceptor加注册配置excludePathPatterns里放行登录和静态资源。4.2 挂号模块前后端数据交互挂号是医疗服务系统的核心场景。患者选择科室、医生、就诊时间段提交后生成一条挂号记录。这里要处理一个关键校验同一个医生在同一时间段不能重复挂号。后端 Service 层的实现大概是Override public Result submitRegistration(RegistrationVO vo) { // 1. 校验患者和医生是否存在 Patient patient patientMapper.selectById(vo.getPatientId()); Doctor doctor doctorMapper.selectById(vo.getDoctorId()); if (patient null || doctor null) { return Result.error(患者或医生不存在); } // 2. 校验该时间段是否已被预约 LambdaQueryWrapperRegistration wrapper new LambdaQueryWrapper(); wrapper.eq(Registration::getDoctorId, vo.getDoctorId()) .eq(Registration::getVisitTime, vo.getVisitTime()) .eq(Registration::getStatus, 1); Long count registrationMapper.selectCount(wrapper); if (count 0) { return Result.error(该时间段已被预约); } // 3. 插入挂号记录 Registration registration new Registration(); registration.setPatientId(vo.getPatientId()); registration.setDoctorId(vo.getDoctorId()); registration.setVisitTime(vo.getVisitTime()); registration.setStatus(1); registrationMapper.insert(registration); return Result.ok(); }这段代码里用了 MyBatis-Plus 的LambdaQueryWrapper比手写 XML 简单得多。selectCount返回的是数据库记录数通过 status1 过滤掉已取消的挂号避免把历史记录也算进去。时间字段visitTime建议用字符串类型存比如2025-06-01 09:00这样前端展示简单后端比较也直接。如果你用datetime类型就有可能出现时区转换和 JSON 序列化格式问题这在第五章会讲。前端对应的挂号页面提交函数大致是async submitForm() { const res await request.post(/api/registration, this.form) if (res.code 200) { this.$message.success(挂号成功) this.loadList() } else { this.$message.error(res.msg) } }这里要注意this.form里字段名必须和后端RegistrationVO的属性名一致。如果后端用patientId前端传patient_idJackson 默认开启驼峰映射还好但如果改了配置就会报 null。建议前后端约定好统一用驼峰命名。4.3 药品管理增删改查的典型实现药品管理是后台模块主要就是「增删改查」。这类代码最能体现一个学生的基本功。很多人直接照抄网上代码insert和update的 SQL 写错字段或者删除时没有做外键约束校验。常见做法是后端提供一个 REST 风格接口RestController RequestMapping(/api/drug) public class DrugController { GetMapping(/list) public Result list(RequestParam(required false) String keyword, RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size) { PageDrug pageInfo drugService.pageQuery(keyword, page, size); return Result.ok(pageInfo); } PostMapping(/save) public Result save(RequestBody Drug drug) { if (drug.getId() null) { drugMapper.insert(drug); } else { drugMapper.updateById(drug); } return Result.ok(); } DeleteMapping(/delete/{id}) public Result delete(PathVariable Long id) { // 先检查处方明细是否有引用 Long count prescriptionItemMapper.selectCount( new LambdaQueryWrapperPrescriptionItem().eq(PrescriptionItem::getDrugId, id)); if (count 0) { return Result.error(该药品已被处方引用不能删除); } drugMapper.deleteById(id); return Result.ok(); } }save接口里用drug.id判断是新增还是更新这是一个很实用的技巧前端新增时id为空编辑时id有值避免了写两个接口。删除药品时的引用校验是很多项目里缺失的一环如果没有这一步你会删掉一个正在被处方使用中的药品导致后续统计报表和处方查询出现空指针。前端表格配合 Element UI 的el-table点编辑时把当前行对象传回弹窗保存时再调用同一个save接口即可。5. SpringBootVue 联调避坑5 个常见问题与排查5.1 现象前端请求被 CORS 策略拦截浏览器控制台报Access to XMLHttpRequest at http://localhost:8080/api/... from origin http://localhost:8081 has been blocked by CORS policy。原因是前端 8081 端口请求后端 8080 端口跨域了。解决办法是后端加一个跨域配置不用每写一个接口都加注解统一实现Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(http://localhost:8081) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }allowedOrigins只写你前端的地址不要用*因为allowCredentials(true)时浏览器不允许通配符。如果用的是 SpringSecurity还需要在 Security 配置里把 CORS 打开。另外注意OPTIONS预检请求要放行不然也会报错。5.2 现象Vue 打包后放进 SpringBoot 出现 404开发模式联调没问题打包前端npm run build后生成了dist目录你想把它扔进 SpringBoot 的src/main/resources/static启动后访问页面却空白或 404。原因通常是路径问题。Vue 默认的publicPath是/而你访问端口是 8080应该能直接打开首页。如果首页能打开但刷新子路由时 404说明 Vue Router 用了 history 模式需要把 SpringBoot 的 404 错误转发到 index.html。比较省事的方式是后端加一个路由重写Controller public class PageForwardController { RequestMapping(value {/, /login, /register, /admin/**}) public String forward() { return forward:/index.html; } }但这样做会影响后端/api路径所以要排除 API。更推荐的做法是直接用 Nginx 部署前端静态文件反向代理到后端 8080这样前后端完全分离答辩时还能多讲一个「Nginx 静态资源服务与反向代理」的知识点。5.3 现象后端返回的时间字段变成一串数字比如visitTime在 JSON 里显示为1717232400000前端渲染不出正常日期。原因是 Jackson 默认把java.util.Date序列化为 timestamp。解决方式是在application.yml里配置统一格式化spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8适用对象是Date类型。如果你用的是LocalDateTime上面的配置不生效需要额外加依赖jackson-datatype-jsr310并在字段上写JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。如果数据库存的是字符串那前端直接用就行这也是我前面建议visitTime存字符串的原因至少能少踩一个坑。5.4 现象后端端口被占用或配置文件不生效启动后端时报Port 8080 was already in use。通常是上次启动没关掉或者你自己开了别的服务。排查方法netstat -ano | findstr 8080 # Windows lsof -i:8080 # Mac/Linux找到占用进程后杀掉就行。更隐蔽的问题是配置文件没生效。源码里可能有application.yml和application-dev.yml两份配置但启动时没有指定激活 profile启动类默认读取application.yml里的spring.profiles.active。如果你改了application-dev.yml里的端口却忘记改application.yml那启动的还是旧配置。建议只保留一份配置文件或者启动参数里加--spring.profiles.activedev避免自己迷惑。5.5 现象SpringBoot 版本太高导致一堆依赖报错这个问题在前端也存在。如果你把pom.xml里版本改成 3.x 后再改回 2.x但 Maven 仓库里已经缓存了新版本依赖偶尔会清不掉。排查时先mvn clean再mvn clean package -DskipTests。如果还报错删掉本地仓库里的org/springframework/boot目录重新下载。前端node_modules也是一样遇到莫名其妙的问题删掉node_modules和package-lock.json再npm install这是最后的后悔药。6. 让毕业设计从「能跑」到「高分」验证与进阶技巧6.1 用 Swagger 生成接口文档答辩时评委最怕你讲不清楚接口。引入 springfox 或 springdoc 依赖后启动项目访问http://localhost:8080/swagger-ui/index.html就能看到所有接口的定义、请求参数和返回结构。你平时调试也方便不需要像在黑匣子里一样猜参数。记得在 Controller 上加Api和ApiOperation注解这样文档显示的是你写的中文描述而不是方法名。这是在答辩现场最直观「这项目是自己写的」的证据。6.2 给登录态加 Redis 缓存如果你的系统只用了 JWT登录状态是没法和后端互动的——token 一旦签发了就只能等它过期。一个低成本的升级是把 token 存到 Redis设置过期时间每次请求时刷新过期时间。这样用户退出时后端可以删掉这个 token真正实现「让账号失效」。Redis 不是必须的模块但这一个点就够你答辩时扩展「缓存一致性」「session 与 token 的区别」这些问题了。具体操作就是在登录接口里加一行redisTemplate.opsForValue().set(login: userId, token, 2, TimeUnit.HOURS);然后拦截器里校验这个 key 是否存在。6.3 部署到服务器前的检查清单很多同学在本地跑得好好的一到部署就翻车。我总结几个必查项第一MySQL 的版本是否和本地一致8.x 的驱动和 5.x 的驱动在pom.xml里写法不同第二后端配置文件里的数据库 IP 要改成服务器 IP不要用localhost第三前端接口地址baseURL要改成服务器公网 IP 或者域名而不是localhost:8080否则手机访问你的页面时请求都打在手机自己身上第四服务器安全组放行 8080 和前端端口不然外面访问不到。我自己的习惯是本地完全跑通后先把后端打成 jar 包用java -jar target/medical.jar启动并测试接口然后再打包前端用 Nginx 托管静态文件。两步分开验证哪个环节出错一目了然。这套流程也是我在带学弟学妹时最常强调的——毕业设计不是写作文是「能跑、能改、能讲」。把环境坑提前排掉把接口文档生成好把核心业务逻辑讲清楚分数自然不低。希望这篇文章能帮你把这套 SpringBootVue 医疗服务系统真正吃透答辩时也能少一点紧张多一点底气。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询