ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue+MyBatis+MySQL前后端分离医院后台管理系统实战

SpringBoot+Vue+MyBatis+MySQL前后端分离医院后台管理系统实战 平时做管理系统项目最常被问到的一个问题就是能不能有一套代码干净、业务完整、还能直接跑起来的前后端分离项目医院后台管理系统这个方向业务字段多、权限要求高、模块划分清晰用来练手或者做毕业设计、公司内训都非常合适。这次分享的这套 SpringBoot Vue MyBatis MySQL 前后端分离医院后台管理系统就是按这个标准整理的源码完整、部署链路清晰从数据库初始化到前端打包上线一条龙讲透。这篇文章适合谁后端想补前端工程化经验的、前端想摸清接口联调流程的、以及正在做医院类管理系统的同学都能从里面拿到能直接落地的方案。1. 项目定位与整体架构设计1.1 医院后台管理系统的核心需求拆解医院的业务系统和普通的后台管理系统有一个显著差异业务对象多、状态流转复杂、数据敏感程度高。一套合格的医院后台管理系统至少需要覆盖六大块门诊挂号、门诊医生工作站、收费划价、药房药库管理、住院管理、系统管理用户、角色、菜单、日志。单纯把这些模块堆在一起并不难难的是模块之间的数据关联。举个例子门诊医生开完处方之后收费处需要实时看到处方明细和费用合计药房收到收费成功的信息之后才能发药药库的库存又要在发药之后自动扣减。你会发现这根本不是几个表独立维护的问题而是一条完整的数据链。所以在设计这套系统时核心思路是先定主数据、再定业务流程、最后才写代码。主数据包括科室表、医生表、药品字典表、收费项目字典表、患者档案表。业务流程则是挂号 - 医生接诊 - 开处方/检查单 - 收费 - 药房发药/医技执行。代码结构完全围绕这条主线展开而不是一上来就堆 CRUD。1.2 为什么选择前后端分离架构早期医院系统大多是 JSP Spring MVC 的老架构页面和服务端代码混在一起改个按钮样式都要重启服务前端工程师很难介入。前后端分离的核心收益是职责边界清晰、并行开发效率高、后端接口可以同时被 Web 端和移动端复用。这套项目的后端只暴露 RESTful JSON 接口前端 Vue 应用通过 Axios 做数据交互。所有请求经过统一拦截器做 Token 校验静态资源完全独立部署不依赖后端容器。这样做还有一个实际好处后期如果要做自助机、小程序端的对接后端接口几乎不用动直接复用同一套鉴权机制和数据接口。1.3 技术选型背后的思考SpringBoot Vue MyBatis MySQL 这套组合在当前的 Java 管理系统开发中属于标配但每个组件选择都有具体理由。丨组件 丨 选择理由 丨 避坑说明 丨 丨------ 丨------ 丨------ 丨 丨 SpringBoot 2.x 丨 自动配置简化大量 XML 配置内嵌 Tomcat打 jar 包即可运行 丨 版本别选太高JDK8 配 SpringBoot 2.7.x 最稳 丨 丨 Vue 2 Vue Router Vuex 丨 生态成熟组件库丰富Element UI 直接可用 丨 国内大多数管理系统模板仍基于 Vue 2资料多 丨 丨 MyBatis 丨 半 ORMSQL 由开发人员掌控适合复杂多表关联查询 丨 注意 XML 映射文件和 Mapper 接口的绑定关系 丨 丨 MySQL 5.7 / 8.0 丨 医院级数据的可靠性要求下MySQL 是完全够用的开源方案 丨 5.7 与 8.0 的驱动名和连接参数略有差异 丨我强烈建议你按这套组合原样去跑不要一上来就换 MyBatis-Plus 或者 JPA。原生的 MyBatis 能让你把 SQL 看得清清楚楚尤其是多表关联和统计类 SQL排查问题时一眼就能定位。2. 核心技术点详解2.1 SpringBoot 后端服务搭建后端工程按职责分层Controller - Service - Mapper控制层只做参数接收和结果返回业务逻辑全部收敛在 Service 层数据访问统一走 Mapper 接口。这样的分层在遇到需求变更时改动的边界很清晰。工程创建建议直接在 start.spring.io 选择 Spring Web、MyBatis、MySQL Driver 三个依赖Java 版本用 8。生成之后在 pom.xml 里补充一个依赖但是不要忽视它就是 Druid 连接池和 Hutool 工具包前者做数据库连接管理和监控后者提供常用的日期、字符串、加密工具类。核心的配置文件 application.yml 需要注意几个关键项server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/hospital_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: root druid: initial-size: 5 min-idle: 5 max-active: 20 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.hospital.entity configuration: map-underscore-to-camel-case: true这里有两个特别容易踩坑的地方。第一个是serverTimezone如果你的 MySQL 是 8.0 版本不配这个参数启动就会报时区相关的异常。第二个是map-underscore-to-camel-case数据库字段是下划线命名Java 属性是驼峰命名不开启这个配置实体类属性全部赋值失败查出来的数据全是 null。接口设计上所有返回结果统一封装为 Result 对象结构包含 code、message、data 三个字段。比如登录成功返回 200、业务异常返回 500、Token 失效返回 401。前端用统一的方式去解析避免每个接口各写一套异常处理逻辑。2.2 Vue 前端工程化实践前端使用 Vue CLI 工程化脚手架创建项目开发环境通过代理解决跨域生产环境构建成纯静态文件之后可以用 Nginx 反代到后端接口。前端页面的目录结构尽量按业务模块拆views 下按 login、layout、system、outpatient、pharmacy、registration 等目录组织。路由采用嵌套路由Layout 组件作为整体框架内部包含侧边栏菜单和顶部导航栏子路由渲染在 RouterView 区域。权限控制这块实现思路是在登录成功后后端返回当前用户的角色编码和菜单列表前端将菜单列表动态注册为路由。也就是说不同角色登录之后侧边栏看到的菜单是不一样的。后端在返回菜单时已经做了过滤前端不需要再写复杂的权限判断逻辑。Axios 请求拦截器是前端的核心防护机制。我在封装时做了三件事请求头自动附加 Token、响应状态码统一处理、401 时自动跳转登录页并清除本地缓存。这里有个细节加载动画的控制最好放在拦截器里统一处理不要在每个页面单独调 Loading代码会干净很多。2.3 MyBatis 数据持久层设计MyBatis 的使用重点是 XML 映射文件的组织方式。这套项目里每个实体对应一个 Mapper 接口和一个 XML 文件XML 的 namespace 必须写接口的全限定名否则启动直接报错。一个比较典型的场景是分页查询。手动写 Limit 很繁琐而且在多表关联时容易出错用 PageHelper 插件是最实用的一种分页方案。引入依赖后在 MyBatis 配置里加一个拦截器插件声明调用 PageHelper.startPage(pageNum, pageSize) 之后紧跟查询语句插件会自动生成带 Count 的分页 SQL并返回 PageInfo 对象里面带有总条数、总页数等分页信息。动态 SQL 的使用频率也很高。多条件组合查询是后台管理系统的家常便饭如果给每一种条件组合都写独立的 SQL工作量是不可接受的。用where标签加if标签就能优雅解决条件都不传时查询所有传了哪个条件就拼接哪个条件。注意if的判断不只判断非 null还要判断空字符串否则前端传一个空字符串过来SQL 会拼出无意义的条件导致查不到数据。2.4 MySQL 数据库模型设计数据库是整套系统的地基表结构设计的好坏决定了后期开发是顺畅还是痛苦。这套项目共设计了 18 张核心表贯穿上述的六大业务模块。核心业务表之间必须要建物理外键吗我的建议是不建。逻辑外键就够了原因很现实物理外键在插入、更新数据时会有额外的约束检查性能损耗而且医院系统经常要做数据归档和分区物理外键会限制很多操作。关联关系靠应用层保证也就是 Service 层的事务来控制。事务的回滚机制用Transactional注解保证处方明细和主记录同时成功或者同时失败。主键统一使用自增 ID不推荐随机 UUID 做业务表主键因为 UUID 的无序性会导致 InnoDB 表页分裂严重写入性能下降明显。业务编号如挂号单号、处方号可以单独设计一个前缀加时间戳加序号的字段既满足可读性也能保证业务查询的需要。3. 系统核心功能模块与实现路径3.1 用户认证与权限管理登录认证用的是 JWT 方案。用户提交用户名密码后后端校验通过就生成一个 Token 返回前端。Token 中只放用户 ID、用户名、角色编码这几个非敏感信息过期时间设为 2 小时。后端提供一个拦截器校验请求头中的 Authorization解析 Token 并存入 ThreadLocal方便 Service 层随时获取当前登录用户信息。关于密码存储必须强调不要明文存。密码使用 BCrypt 加密BCrypt 的加密结果是 60 位的字符串每次加密得到的密文都不一样但校验结果是一致的。这可以有效防止彩虹表攻击。初始化管理员账号时代码里写一个 CommandLineRunner启动时检测到没有管理员就自动创建。权限这块除了动态菜单接口层面也要做一层防护。后端在拦截器里校验用户的角色编码再根据当前请求的 URL 判断该角色是否有访问权限。双端校验的最大意义是防绕过总有人可以直接调用接口而不经过前端页面。3.2 门诊业务链路实现门诊模块是整个系统联动性最强的部分。从患者挂号开始到医生开方、收费确认、药房发药这条链路涉及 4 个角色、5 张业务表。实现时必须做到一环扣一环每个环节的完成状态要实时更新。挂号环节的操作逻辑选择科室 - 选择医生 - 读取排班信息 - 选择号别专家号或普通号- 生成挂号记录并扣减当天的剩余号源。号源充足性判断和扣减动作要在一个事务里完成防止两个用户同时挂同一个剩余号源。这里不需要用到分布式锁MySQL 的行锁配合事务就够了核心是业务的原子性。医生工作站是医生角色的操作界面。选中一个待诊患者后可以查看患者的基本信息和历史就诊记录。开处方时医生在药品目录里检索药品每一味药品都要校验库存是否充足。处方保存时同时写入处方主表和处方明细表明细至少包含药品 ID、名称、规格、单价、数量、用法用量。收费环节读取处方明细的金额汇总数据展示给收费员确认收费成功后同时修改处方状态为已收费并自动扣减药品库存。这一串动作必须放在同一个事务方法里修改处方状态 扣减库存 生成收费记录。任何一个环节异常整体回滚绝不允许出现处方已收费但库存没扣的情况。3.3 药房与药库管理药品管理在业务上要分清两个仓库药库中心库房和药房门诊发药窗口。药库负责药品的采购入库、供应商管理、库存上下限预警药房负责日常发药药房库存不足时通过领药单从药库调拨。药品字典表的核心字段包括通用名、商品名、规格、厂家、批准文号、零售价、库存上限、库存下限。库存上下限用于触发预警当库存低于下限时在药库管理的主界面醒目地标红提示。实现方式很简单查询药品列表时SQL 里做一次IF(stock lower_limit, 1, 0) AS is_warning的判断前端根据这个字段来控制样式。药品入库时的批号和有效期字段要重点处理。医院的药房管理严格遵循先进先出原则发药时优先选择最早入库批次。所以在设计库存表时每一条批次记录都包含药品 ID、批号、生产日期、有效期、库存数量。出库操作时按有效期排序先扣最接近过期的批次这是医药流通行业的标准做法。3.4 数据统计与报表医院管理后台不能只做业务记录管理者必须能直观看到每天的运营数据。数据统计模块包含日门诊量趋势、医生工作量统计、科室收入排行、药品消耗 Top N。报表类的 SQL 和业务 SQL 的写法思路完全不同。业务 SQL 通常针对单条记录精确操作报表 SQL 则必须做数据聚合。以日门诊量为例核心 SQL 是按日期分组统计挂号记录数。只有分组还不够还需要连续多天都输出数据哪怕是 0。我当时是先查就诊日期在区间内的所有挂号记录做分组统计再在 Java 代码里按日期补零比 SQL 里去拼日期序列简单得多而且逻辑更好维护。在这个模块里查询性能不必过度优化。医院业务系统日数据量在几千到几万条级别报表查询基本都可以在几十毫秒内返回。真正需要关注的是查询条件的索引覆盖比如挂号记录表的visit_date字段和status字段务必建上联合索引。4. 部署环境准备与实战4.1 本地开发环境搭建先把基础环境准备好JDK 8、Maven 3.6、MySQL 5.7 或 8.0、Node.js 14、Nginx生产环境需要。新装 JDK 的同学要注意配置JAVA_HOME环境变量Maven 的settings.xml里把本地仓库路径和阿里云镜像配好不然依赖下载慢到怀疑人生。数据库初始化直接执行项目里的hospital_db.sql这个脚本包含了全部建表语句和初始数据其中初始数据包括管理员账号、科室字典、药品字典样例数据。用 Navicat 或命令行执行都行。执行完成之后可以先快速验证一下核心表的行数比如用户表至少有一行管理员记录科室表至少有几条数据再启动后端不然启动不报错但登录不了。前端依赖安装是另一个高频坑位。直接用 npm install 很容易出现因网络导致的依赖损坏。把 npm 源切换到国内镜像再执行安装基本一次通过。安装完成后执行 npm run serve 启动开发服务器默认端口 8081避免和后端 8080 冲突。4.2 后端项目启动与调试后端工程的启动方式很简单在项目根目录执行mvn spring-boot:run或者进入主类直接右键 Run。正常启动会在控制台看到 Spring Boot 的启动日志和一个端口占用提示。第一次启动碰到最多的就是 8080 端口被占用。排查方式执行netstat -ano | findstr 8080Windows或lsof -i:8080macOS/Linux查看占用进程要么改端口要么杀掉占用进程。不要直接在 application.yml 里随手改成 9090如果前端代理配置的还是 8080联调照样失败。后端启动后如何自测接口不必先等前端。直接用 Postman 或 Apifox 请求接口使用管理员账号登录拿到 Token 后在请求头里带上Authorization: Bearer token就能测试业务查询接口。用接口工具把后端全部跑通了再让前端介入联调可以节省大量时间。4.3 前端构建与联合部署开发阶段用npm run serve生产环境必须执行npm run build生成 dist 目录里面是纯静态文件。联调时如果你不想动用 Nginx可以先让 SpringBoot 的静态资源映射指向 dist把 Vue 打包到 SpringBoot 部署。这是一种临时方案但是生产环境不要这么做。生产环境的推荐方式就是 Nginx 托管静态文件 反向代理接口。配置核心就一个 server 块location / 指向 dist 目录location /api/ 做代理转发到后端服务。需要在 Nginx 里配置proxy_set_header Host $host和proxy_set_header X-Real-IP $remote_addr否则后端拿到的客户端 IP 全是 Nginx 的内网地址日志排查会迷路。后端服务的启动推荐打成 jar 包再用脚本守护运行。mvn clean package -DskipTests打包出可执行 jar然后用 nohup 后台启动。停机维护时先用 kill 停掉旧进程再启动新的 jar 包。如果同一台机器上有多个项目jar 包的端口区分要提前规划不能都默认 8080 起冲突。5. 常见问题与排查技巧实录5.1 数据源初始化常见错误数据库连接是启动阶段失败率最高的一环。报Access denied for user rootlocalhost就是密码错误报Unknown database hospital_db说明数据库还没建或者名字不对报Public Key Retrieval is not allowed是 MySQL 8.0 的 allowPublicKeyRetrieval 参数没设置需要在连接串里加allowPublicKeyRetrievaltrue。还有个隐蔽的问题字符集。数据库默认字符集不是 utf8mb4 时插入中文会变成问号。解决方法是建库时指定字符集执行CREATE DATABASE hospital_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;。字符集在数据库这一层解决是最彻底的做法。每一张表再单独去改 CHARSET费时费力还容易漏。5.2 跨域问题处理前后端分离项目跨域是不可避免的一道坎。开发环境里前端地址是 localhost:8081接口是 localhost:8080浏览器的同源策略会拦截跨域请求。开发环境下优先用 Vue CLI 的代理方案在 vue.config.js 里配置 devServer.proxy把 /api 开头的请求都转发到后端地址。这个方案的好处是代码不用写跨域注解生产环境也不受影响。万一要在后端层面解决跨域可以写一个 CorsFilter 配置类设置允许的域名、请求头和请求方法allowedOrigins 不要用*因为携带凭证的请求不允许用通配符。不过这套项目里还是推荐走代理方案生产环境由 Nginx 代理开发环境走 devServer 代理后端不参与跨域逻辑。5.3 前端联调时接口数据加载不出来开发模式配置了代理之后联通性仍然有问题时重点检查两处。第一处是请求的 URL 前缀和代理配置是否完全一致比如代理只转 /api但代码里请求的是 /apis那就对不上。第二处是后端注解问题前端请求方法是 POST后端接口却没有标注PostMapping而是用了RequestMapping这种情况很容易被忽略导致假死页面。如果你用的是 Axios 封装的统一请求工具先打印完整请求配置看实际走的 URL 是什么再带着 URL 和请求参数去 Postman 里直接请求后端接口。这一段流程能帮你在五分钟内定位是前端问题、代理问题还是后端问题效率远高于一段段断点去查。5.4 MyBatis 运行时报错排查Mapper 相关报错是整个后端工程里最常遇到的坑主要集中在这几类启动报Invalid bound statement是 XML 的 namespace 和接口全限定名不一致或者 XML 文件位置不在 mapper-locations 扫描范围内。查询结果全为 null检查是否开启了驼峰映射。报TooManyResultsException是实体类查询返回多条结果但接口方法返回类型是单个对象。报SQLSyntaxErrorException多半是表名或字段名用了 MySQL 的保留字比如desc、order。每一类报错都有固定的排查路径代码经验不足时最忌豪改 SQL应该先用控制台打印日志mybatis.configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl把预编译 SQL 和实际传参完整输出在日志中逐字对照 SQL 的拼装逻辑几乎可以一眼锁定问题。6. 一些统盘经验的再补充可能有人会问这套系统看起来业务覆盖面广代码量会不会很大实际核心代码行数大概在两万行上下接口数量三十多个对于一个 J2EE 方向的毕设或者中小型医院的内部系统来说体量刚刚好。代码行数不在于量多少而在于核心链路是否完整。如果有后续自己扩展的打算建议优先做两个方向第一个是接入 Redis 缓存药品目录和科室列表降低数据库的查询压力第二个是把报表模块从固定 SQL 改成在线拖拽式配置大屏现有的统计表结构依然可以复用。另有一个容易被低估的细节日志。你真上线跑业务的时候没有日志寸步难行。logback 的配置一定要提前写好按照 info、warn、error 分级输出到一个统一的日志目录文件按天滚动保留 30 天。后端所有的重要操作节点至少加一行日志包含操作人、操作类型和关联业务单号。没有业务日志的系统出问题就只能靠猜这在医院这种 7x24 小时运转的场景里是不可接受的。在医院业务系统里日志的详细程度也关系到合规审计。用户的每一次登录、每一个敏感操作都要有可追溯记录。把日志管理提前做好后面做等保测评或者内部审计时就是加分项而不是补课项。再提一个实践中容易忽视的管理问题账号安全策略要在上线前就把规矩立住密码次数限制比如连续输错 5 次锁定账号、Token 过期时间不要设太长、管理员的密码定期强制修改。这些功能实现成本很低却能在运维阶段省掉很多不必要的麻烦。权限做最小化分配不用的功能模块不要多开给用户这是安全管理的通用底线。最后交代一下上线后的日常运维手段。后端进程要保持健康状态最基础的就是一个检测脚本定时探测一次健康检查接口返回异常就重拉服务。数据库定时备份用 mysqldump 配合 cron 定时任务来完成日期标记再把备份文件同步到独立的备份服务器。这套操作跟着系统一起走上线才算是把一个管理系统真正做完整了。做系统从来不是写代码和让页面能点通就算数能一路稳定地跑下去才见功夫。
返回列表