ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于SpringBoot+Vue+MySQL的医院病历管理系统设计与实现

基于SpringBoot+Vue+MySQL的医院病历管理系统设计与实现 毕业设计做了个医院病历管理系统用 SpringBoot Vue MySQL 这套组合整套完整代码、数据库脚本、论文和部署文档都整理好了。最近好多同学在问这个题目的思路今天就把它拆开说说从技术选型到数据库设计从后端接口到前端页面再到最后写论文和上线部署整个过程踩过的坑、绕过的弯都摊开聊一遍。不管你是刚拿到题目不知道怎么下手还是已经写了一半卡在某个细节上这篇都应该能帮你省不少时间。1. 项目整体设计与技术选型思路1.1 为什么选 SpringBoot Vue MySQL 这套组合医院病历管理系统作为典型的毕业设计选题考察的不只是增删改查而是对一个完整业务系统的理解和落地能力。选型时首先要考虑的是技术栈的普适性和上手难度。SpringBoot 在当前 Java 领域的统治地位就不用多说了简历上写 SpringBoot 几乎是标配Vue 在前端框架里对新手最友好中文文档完善组件化开发思路清晰MySQL 作为关系型数据库的入门首选处理病历这类结构化数据完全够用。这三样组合在一起既不会显得技术太老旧又不会因为过度设计让自己毕不了业。还有一个务实的考虑是参考资料多。SpringBoot Vue 的前后端分离项目在网上有大把成熟案例遇到问题搜一下就能找到解决思路。相反如果选一些冷门框架或者微服务架构虽然看着高大上但出问题的时候可能连个能问的人都没有答辩时老师也会追问为什么做这么复杂。1.2 业务模块拆解病历数据是怎么流动的拿到题目后第一件事绝对不是写代码而是把业务捋清楚。医院病历管理系统表面上就是“增删改查”但真正做起来涉及的角色和流程比想象中复杂。我当时的做法是先画一个大业务图患者挂号、医生接诊、书写病历、开立处方、药房发药、住院登记、病历归档。虽然没有必要做成一整套路由但必须让系统有这个逻辑线索。核心模块我拆成了五个系统管理用户登录、角色权限、菜单管理、操作日志。患者管理患者基本信息登记、就诊记录查询。医生工作站病历书写、诊断记录、处方开立、检验检查申请。病历管理病历查询、借阅审批、归档管理。统计报表科室工作量、疾病分布、病历质量抽查。这里要特别说一下毕业设计的功能不是越多越好而是要能形成一个完整的闭环。医生能开处方药房就能看到对应处方病历填写完患者档案里就能查到。这种业务闭环在写论文的时候可以画出时序图能充分体现你对系统的理解答辩时也更有底气。1.3 角色权限设计让简历上更出彩很多同学做管理系统只做了一个管理员角色所有功能所有人可见。这种做法的优点是简单缺点是答辩老师一问“你的系统怎么控制不同角色访问不同菜单”就卡住了。医院病历系统本身是强调权限隔离的场景医生只能看自己的患者护士只能执行医嘱管理员负责系统配置这是非常自然的业务需求。我设计的角色是管理员、医生、护士、药房四类。前端通过路由守卫控制页面访问后端通过拦截器加注解控制接口权限双重校验。具体的实现细节放在后面章节说但这里建议大家理解一点权限控制不是一个简单的开关而是前后端配合的体系。前端控制“看得到什么”后端控制“能操作什么”二者缺一不可。2. 数据库设计与核心表结构2.1 实体关系梳理先画 E-R 图再建表数据库设计是整个系统的地基。我见过太多同学不看业务直接建表做到一半发现字段不够或者关系理不清然后推倒重来。正确的顺序是先梳理实体再画关系图最后落成表结构。我的核心实体有这些用户医生/护士/药房人员统一放用户表、角色、患者、病历、诊断记录、处方、处方明细、科室。实体之间的关系很清晰一个用户属于一个科室一个科室有多个用户一个患者可以有多份病历一份病历属于一个患者一份病历可以有多个诊断记录一份诊断记录又可能关联一条处方一条处方包含多种药品。这对新手来说已经有点复杂需要用主外键把关系维护好。2.2 关键表的字段设计与注意事项拿病历主表举例我设计了这些字段病历编号、患者ID、患者姓名冗余存储方便查询、科室ID、接诊医生ID、主诉、现病史、既往史、体格检查、初步诊断、最终诊断、就诊类型门诊/住院、就诊时间、状态。这里面有几个点值得展开。冗余字段的使用其实很值得写进论文里。病历列表页面要把患者姓名、科室名称、医生姓名都显示出来如果全部通过联表查询SQL 会写得又臭又长性能也不见得高。我在病历表里直接冗余了患者姓名和医生姓名查列表的时候就省去两次 JOIN用空间换时间。很多同学以为数据库三大范式必须严格遵守但在实际项目里合理冗余是常见优化手段这一点可以在答辩的时候主动讲出来。就诊类型字段也很有讲究。门诊病历相对简单住院病历要记录入院时间、出院时间、病情转归。一开始我用一个字段表示类型后来发现住院的扩展信息没地方放只好单独建了一张住院信息表。如果不提前分析业务这种问题等代码写多了再改会非常痛苦。2.3 状态字段设计让病历流转更清晰病历不是一个静态的记录它有一个从“草稿”到“已归档”的生命周期。医生正在写的病历应该是草稿状态保存后变成待提交确认无误后提交归档。不同状态下允许的操作不一样比如草稿可以修改已归档的病历只能查看不能改。这种状态机设计不仅能体现业务理解还能防止操作事故。我在所有需要流转的业务表里都加了 status 字段用 int 表示比如0-草稿1-待审2-已归档-1-已作废。前端通过标签渲染成不同颜色后端在更新时先查数据库里的当前状态判断是否允许这个操作。核心代码就一句话但有时候需要结合事务一起处理否则并发场景下会产生脏数据。这部分放在后端实现章节细说。2.4 数据库脚本的编写规范源码配套的数据库脚本init.sql我做了几个版本一个是只含表结构不掺任何数据一个带基础用户和字典数据方便启动后直接登录测试。强烈建议把角色、菜单这些系统初始数据也写进脚本里否则部署的时候还得手动插数据非常麻烦。需要注意的坑是数据库字符集和排序规则。病历里会有大量的中文MySQL 建库的时候一定要用 utf8mb4而不是 utf8。utf8mb4 才是完整的 UTF-8可以存储表情符号等四字节字符排序规则用 utf8mb4_general_ci 就够用。有的同学直接把默认的 latin1 拿来用启动后中文全部乱码排查半天都不知道问题出在哪。此外所有表最好都带上 create_time、update_time 字段用数据库的默认值 CURRENT_TIMESTAMP 和 ON UPDATE CURRENT_TIMESTAMP 自动维护这个细节写进论文里也算一个设计亮点。3. 后端核心逻辑与接口实现3.1 项目分层结构与包规划后端项目我用的是经典的分层结构Controller 层接收请求Service 层处理业务Mapper 层访问数据库。实体类、DTO、VO 分开存放工具类统一放 util 包。包名用 com.xxx.hospital 这种格式结构清晰也方便写论文画架构图。很多人喜欢用 MyBatis-Plus 作为 ORM我也一样用了。它的优势是单表操作基本不用写 SQL分页插件非常成熟代码生成器还能根据表结构自动生成实体、Mapper、Service。不过要注意如果是 Sybase 或者 Oracle 数据库分页语法不一样但 MyBatis-Plus 封装好了换数据库也很省事。这一点在论文里也可以提一下。3.2 认证与权限JWT 拦截器 注解前后端分离项目的认证方案我选了 JWTJSON Web Token。用户登录成功后后端生成一个包含用户ID、角色、过期时间的 token 返回给前端。前端每次请求带上 token后端通过拦截器统一校验。这里要详细说说实现细节因为这是毕业设计答辩的高频考点。我在 WebMvcConfigurer 里配置了一个登录拦截器排除掉登录接口、验证码接口等白名单路径。拦截器里做两步第一步解析 token如果 token 不存在或过期直接返回 401第二步把解析出来的用户信息放到 ThreadLocal 里方便后续业务代码获取当前操作人。角色权限用自定义注解 AOP 实现写了一个 RequirePermission 注解标注在 Controller 方法上比如医生提交病历的方法就标上 RequirePermission(doctor)。AOP 切面里从 ThreadLocal 取当前用户角色如果不在允许列表里抛异常然后由全局异常处理器统一返回“权限不足”。这种方式的好处是权限判断和业务逻辑完全分离代码看起来非常清爽。3.3 病历新增、修改的状态控制与事务病历保存和提交是最核心的业务接口。保存草稿和正式提交是两个操作我对这两个逻辑做了区分保存草稿时只校验基本字段比如主诉、现病史正式提交时还会校验诊断是否填写、是否已选科室。状态字段贯穿了所有操作提交的 SQL 语句里一定要带 status 条件防止用户用旧数据覆盖新数据。还有一点是关于事务。病历主表插入是一条记录诊断记录是 N 条处方是 M 条这些数据要么全部成功要么全部失败绝对不能出现病历写了但诊断记录丢了一半的情况。ServiceImpl 方法上加 Transactional 注解默认遇到 RuntimeException 会回滚但要注意上传文件、调用外部接口这类操作尽量不要包在事务里否则容易造成长事务。这个点如果能在论文里写出来说明你真的理解事务机制。3.4 接口返回格式与全局异常处理所有接口统一返回 Result 对象里面包含 code、message、data 三个字段。code 为 200 表示成功401 表示未登录403 表示没权限500 表示服务器内部错误。前端拿到 result 之后统一判断 code不满足就弹错误提示。这个规则在前后端联调时能省大量沟通成本。全局异常处理器需要处理几类常见异常业务异常、参数校验异常、数据重复异常、全局兜底异常。尤其在参数校验上用 javax.validation 的注解NotBlank、Email、Pattern 等能在请求进入 Service 前就拦截非法数据避免脏数据入库。这部分代码量不大但收益极高是简历上可以写、答辩时可以讲的亮点。4. 前端 Vue 页面设计与交互4.1 前端目录结构与工程化实践前端我用 Vue 3 Vite Element Plus 组合。Vite 比 Webpack 启动速度快很多开发体验提升不是一点半点。目录结构上views 按模块分文件夹router 单独建目录api 目录里按业务域拆分文件如 user.js、medicalRecord.js、patient.js。每个接口请求用一个独立函数导出页面里按需引入避免把所有接口都写在一个大文件里。组件复用是前端项目质量的试金石。病历表单、患者信息弹窗、药品选择组件都是多个页面会用到的我把它们抽成公共组件放在 components 目录下。比如患者搜索组件接收一个 confirm 事件父组件拿到选择结果后做后续逻辑。用组件化的方式组织代码后期加功能的时候不用重复写前端页面改一处就能全局生效。4.2 病历表单的动态与校验病历填写页是整个前端最复杂的部分。主诉、现病史、既往史是长长的文本域诊断记录是动态增减的列表处方明细是药品选择表格每种字段的交互方式都不一样。动态表单我用 el-form 的 model 配合动态绑定的方式实现用 Element Plus 的 Form 组件校验。这里有个容易踩的坑动态表单项的 prop 名称要写成数组索引模式比如 diagnoses.0.diseaseName否则校验规则无法生效。我从网上学了这个技巧但不看官方文档真的发现不了调试了很久才搞明白。前端校验只是辅助后端必须也要校验这个原则在病历系统里尤其重要因为病历是医疗记录不能随便乱填。4.3 axios 封装与接口联调经验前端请求统一走 axios 实例。我在 axios 的请求拦截器里添加 token header响应拦截器里处理统一错误提示和 401 跳转登录。为了让代码更有体验拦截器里还做了 loading 状态控制所有请求自动加载动画省得每个页面手动处理。联调阶段有一个经验特别值得分享前端要用 mock 数据把页面全部跑通再跟后端联调。我做前端页面时先用本地 json 模拟接口数据把所有 UI 和交互调好等后端接口开发完再切到真实请求。这样做的好处是前后端可以并行开发不会因为一边卡住而拖慢整个进度。切换真实接口时只改 api 目录里的 baseURL或者用环境变量控制非常方便。5. 实操过程从零搭建到跑通部署5.1 本地环境准备工具清单要跑起来这套项目需要准备的工具包括JDK 1.8、Maven 3.6、MySQL 5.7、Node.js 14、npm 或 yarn、一个 Java IDE推荐 IDEA、一个前端编辑器VSCode 即可。这里要注意版本兼容问题SpringBoot 2.x 对 JDK 8 的支持最稳定Vite 对 Node 版本有要求太老或太新都可能报错。5.2 后端启动步骤与常见报错启动后端其实就几步先建好数据库执行 init.sql 脚本然后在 application.yml 里修改数据库用户名和密码最后运行 SpringBoot 的启动类。如果能启动控制台会打印 Tomcat 端口号和项目启动时间一般默认在 8080 端口。最常见的启动失败原因是数据库连接失败报错关键词是 Cannot create PoolableConnectionFactory 或 Communications link failure。这一步先检查 MySQL 服务有没有启动再检查账号密码是否正确然后检查数据库名称和地址是否匹配。如果报 Public Key Retrieval is not allowed在 JDBC 连接参数里加 allowPublicKeyRetrievaltrue 和 useSSLfalse。5.3 前端启动步骤与代理配置前端启动命令是 npm install 安装依赖然后 npm run dev 启动开发服务。Vite 默认端口是 5173如果想改到 vite.config.js 里配置 server.port。接口跨域问题在本地开发时用 Vite 的代理解决在 server.proxy 里配置把 /api 前缀转发到 http://localhost:8080。这一步配置好了前端开发时就不会被浏览器的同源策略拦死。如果 npm install 速度太慢或者报错建议用淘宝镜像源。还有一种常见报错是代码里使用了某个 ES6 语法但 Node 版本太低导致 Vite 编译失败。升级 Node 到最新 LTS 版本基本都能解决。5.4 前后端联调的关键步骤前后端都启动后先测试登录流程输入管理员账号密码进入首页如果菜单和数据显示正常说明基础链路通了。接下来逐个模块测试新增患者再给患者写病历保存草稿、提交归档去药房页面看处方查日志。整个流程走下来大问题基本都能暴露出来。联调阶段特别容易出的问题是字段名对不上。前端定义的字段为 patientName后端实体类用的字段可能是 name导致数据传过去后显示空白。为此我专门整理了一个接口文档就是一个 Excel 表格里面列出每个接口的 URL、请求方式、请求参数、返回参数联调时双方都按这个表格对能减少大量无意义的口水仗。5.5 项目打包与上线部署毕业设计如果只本地跑通答辩的时候风险比较大最好提前打包部署给老师演示的时候更稳。后端打包用 mvn clean package生成的是可执行的 jar 包。部署方式最简单的是直接把 jar 包放到有 Java 环境的服务器上用 nohup java -jar xxx.jar 后台启动。前端打包用 npm run build生成 dist 静态文件夹。我一开始对部署不太熟还想过直接用 Nginx 做前端服务器。但毕业设计并不需要真正上公网我最终是在本地演示后端 jar 包占 8080 端口前端用 Nginx 指到 dist 目录代理配置好 /api 请求指向 8080这样一个 Shell 脚本就能搞定整套系统重启。在部署文档里我写了两种部署方式一种是最简方式直接把后端 jar 跑起来前端页面通过 VSCode 插件 Live Server 打开另一种是完整 Nginx 部署方案。这样既照顾了新手也保留了一套完整的部署配置论文里还有内容可写。6. 论文与文档写作建议6.1 论文整体框架与写作节奏论文千万别最后临时抱佛脚最好和开发进度同步写。结构一般分成摘要、绪论背景与意义、国内外现状、需求分析、系统设计、系统实现、系统测试、总结与展望。每个章节要有的放矢比如需求和设计部分可以大量画用例图、时序图、E-R 图这些图不仅是论文的颜值担当也是答辩时展示的重点。开发的时候每完成一个模块就把对应的截图保存下来。系统的登录页、患者列表、病历编辑页、权限不足提示页这些都放到实现章节作配图。一张好图顶几百个字这点在论文写作中格外重要。6.2 论文里值得深挖的几个设计亮点为了让论文有深度一定要挑两三个技术点展开写而不是平铺直叙地介绍“我做了什么”。比如 JWT 认证机制的原理和实现、病历状态机的设计与并发控制、MySQL 索引优化在病历查询中的应用。每个亮点要写清楚“为什么这么做”和“对比其他方案有什么优势”而不是只贴代码。索引优化这个点很有说头。病历表数据量大了以后select count(*) 和分页查询会变慢。我在患者姓名和病历编号上建了索引查询效率高了不少。把这个优化前后的响应时间测试数据写进论文就能体现你有性能意识老师看了会觉得你不只是会写 CRUD而是有系统的思考。6.3 答辩高频问题与应对准备答辩老师常见问题就那几类为什么用这套技术栈、数据库表为什么这么设计、权限是怎么控制的、系统有哪些安全性措施、性能如何优化。每个问题都要提前准备五分钟左右的回答提纲。特别要注意的是答辩时不要只背概念要结合自己的项目讲。老师问“如何防止越权操作”如果回答“用拦截器加注解”会显得很单薄。最好说我先通过 JWT 获取当前用户身份然后通过 AOP 对标注了权限注解的方法做角色校验最后在 SQL 里绑定医生ID 或科室ID 作为数据权限过滤。把设计和实现串起来讲说服力就强很多。7. 常见问题排查与避坑手册7.1 启动类常见异常速查表我在开发过程中整理了下面这些高频异常做出来一个速查表遇到类似问题一眼就能定位。异常关键词常见原因解决方案Communications link failure数据库没启动 / 地址写错检查 MySQL 服务和连接 URLPublic Key Retrieval is not allowedMySQL 8 的加密规则问题连接参数加 allowPublicKeyRetrievaltrueuseSSLfalsePort 8080 was already in use端口被占用换端口或在任务管理器杀掉占用进程npm ERR! code ERESOLVE依赖冲突npm install --legacy-peer-depsCannot read properties of undefined字段名对不上 / 返回数据为空打印后端返回值检查字段映射Failed to bind properties under spring.datasourceyml 配置格式错误检查缩进和冒号后是否有空格7.2 部署时前端刷新 404 问题前端部署用 Nginx 时一个经典问题是打开首页能进入但刷新页面就 404。这是因为 Vue 是 SPA路由用的是 HTML5 History 模式刷新时 Nginx 会把请求转发到服务器文件系统找不到对应文件自然报 404。解决办法是在 nginx.conf 里加一句话location / { try_files $uri $uri/ /index.html; }。意思是所有请求先尝试匹配文件文件找不到的全部落到 index.html由前端路由接管。这个配置我研究了一个晚上才弄明白弄明白以后发现其实就一行配置的事。写论文的时候也可以加一段说明你对 Nginx 部署的理解。7.3 接口超时或并发修改问题病历提交时如果同时开两个页面可能造成重复提交覆盖问题。解决思路是前端提交按钮加 loading 状态点击后禁用后端在保存方法里判断状态字段如果已经是已归档就不允许再修改直接抛异常。这两个层面做下来虽然不能 100% 防住高并发但应付毕业设计的场景足够了。还有一种情况是接口返回慢前端容易把加载动画关掉导致用户以为卡死了。我在响应拦截器里统一处理错误提示在 loading 状态计时上做了开关只有请求超过两秒才显示相关信息。这样交互感受不那么生硬也更接近真实项目的用户体验。7.4 时间字段显示不准确问题前后端时间传递有个隐蔽坑后端返回的 LocalDateTime 默认格式是 “2024-06-01T10:30:00”前端展示出来不好看。解决方法是加 jackson 配置把时间字段统一格式化为 yyyy-MM-dd HH:mm:ss。数据库时区在 MySQL 连接 URL 里加上 serverTimezoneAsia/Shanghai 也能避免时区偏移否则会差出好几个小时。7.5 源码与数据库脚本的配合使用拿到源码以后很多人会漏掉 init.sql 里的账号初始化数据导致登录测试不成功。我习惯在脚本里预留一个 admin 账号密码用 BCrypt 加密后存入同时在 README 和部署文档里写明默认账号密码。这样部署的人不用想破脑袋去重置密码跑起来就能登录测试。另外数据库脚本尽量保证幂等就是执行两次不会报错或产生重复数据。实现方式是在建表语句前加 IF NOT EXISTS在插入字典数据前先 DELETE 再 INSERT。这些小细节看似不起眼但用起来很省心。最后再说两句个人经验要是从头再做一次我会把这些坑提前绕开第一数据库设计阶段一定多花时间把业务字段列全尤其是扩展字段比如住院号、过敏史、既往手术史后面再加字段真的很麻烦第二前端表单校验和后端校验要同步做只在一边做了容易出脏数据第三日志一定要打而且要有信息含量不然出了问题连排查入口都没有第四论文不要拖到最后边开发边写截图随时存答辩的时候才有素材用。还有一件事想提醒各位病历管理系统虽然叫“病历”但涉及患者的隐私信息做演示数据的时候千万不要用真实患者数据。系统里所有示例、测试数据一律用虚构的姓名、身份证号、联系方式这个底线要守住既是职业道德也是法律要求。放在论文里的数据也建议做好脱敏处理照片打码、号码打码这个细节我给好多人提过但总能遇到有人忽略。这套系统的源码、数据库脚本、论文初稿和部署文档我都整理好放在一起了拿到以后建议先按部署文档跑通一遍再对照本文把核心模块的代码逐行读一遍最后再动手改一改扩展功能。毕设本身是一个学习过程别把重点放在“交差”上。哪怕最后只改了一个小功能比如加一个打印病历的功能答辩的时候讲述起来也会更有底气因为你真的理解每一行代码是干什么的了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进