ARTICLE DETAIL

资讯详情

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

Java医院管理系统HIS源码部署与二次开发实战指南

Java医院管理系统HIS源码部署与二次开发实战指南 简介一份基于Spring Boot、Layui与Shiro构建的Java医院管理系统HIS完整源码适合医院信息化学习者、Java开发人员及毕业设计参考者用来理解门诊、住院、系统管理等核心业务流程与权限控制实现。压缩包约199.26MB共38171个文件以js、ts、json、md等为主包含前端页面脚本、Java后端代码、配置文档及小程序相关文件覆盖挂号、处方划价、入院登记、缴费、药品记账、出院结算等模块。已有1043人学习资料附带百度云盘中的视频教程与业务参考文档便于快速上手。项目数据库共36张表业务复杂度较高门诊侧涵盖用户挂号、处方/项目划价、项目检查、药品缴费与门诊患者库住院侧支持入院登记、住院缴费、药品/项目记账和出院结算并包含菜单、角色、用户、图标及数据源监控等系统管理功能。通过重复就诊校验、转住院前费用结清判断等细节可帮助读者掌握医院业务流程建模、Spring Boot整合Shiro权限管理及小程序开发要点适合项目复现与二次开发。1. HIS系统源码能不能直接跑先分清“能演示”和“能上线”做Java开发的十有八九都被问过“有没有医院管理系统源码”培训机构出来的第一反应是上网找一套HIS改巴改巴当项目经验。真到医院现场你会发现市面流传的所谓HIS源码绝大多数是“能演示”和“能上线”之间差着一整条河的半成品。这套Java医院管理系统HIS源码属于能落到本机跑、能拿来做二次开发底子、也能支撑小规模诊所信息化的那一类后端是Spring Boot那一整套带一个微信小程序端外加一份安装教程数据库脚本、启动步骤都齐。适合三类人想把Java基础、Spring Boot、小程序联调串成完整项目的求职者要给社区诊所做挂号收费系统的his实施工程师以及做毕业设计不想从头造轮子的学生。它能解决的是“从0到1把HIS跑起来”这个最现实的诉求至于能不能直接扛住三甲医院的并发那是另外一个问题。2. 读懂这套HIS源码的结构Spring Boot MyBatis-Plus MySQL的模块与表设计拿到任何一套源码第一件事不是急着启动而是先看包结构和数据库脚本。这套HIS的目录划分比较规矩后端是一个标准的Spring Boot工程前端小程序是独立的原生微信小程序工程两者通过HTTP接口通信。如果连模块边界都没分清楚就启动后面排错会非常被动。2.1 源码目录结构与模块划分看包名就能猜到业务流程后端源码打开后第一层是src/main/java下的包目录按controller、service、mapper、entity四层划分这是MyBatis-Plus项目的经典结构。实体类在entity包对应数据库表mapper包是数据访问层配合MyBatis-Plus的BaseMapper大部分单表CRUD不用手写SQLservice包放业务逻辑挂号、收费、退药这些操作都在这里controller包暴露给前端调用的REST接口。另外有一个config包放拦截器、跨域配置、MyBatis-Plus分页插件这个包决定了一堆接口能不能被小程序正常调用。com.clinic.his ├── controller # 对外接口层/api/patient、/api/register等 ├── service # 业务逻辑层挂号事务、收费事务 ├── mapper # 数据访问层继承BaseMapper ├── entity # 实体类对应数据库表 ├── config # 拦截器、跨域、MyBatis-Plus配置 └── common # 统一返回结果、异常处理逻辑说明controller只做参数接收和结果封装业务计算全部下沉到service层事务注解打在service方法上。这样的分层好处是将来要加一个“号源锁”功能不用动controller接口签名只需要在service里加逻辑。参数说明common包里通常有个Result类所有接口统一返回{code, msg, data}结构小程序端判断code200才取数据。如果你看到的返回结构不是这个先改这里而不是去改几十个接口。看完包结构再看application.yml。这里有几个关键配置项数据源url、username、passwordMyBatis-Plus的map-underscore-to-camel-case驼峰映射开关还有server.port端口号默认8080。真正动手前把这三样确认清楚后面能省一半排错时间。2.2 核心数据表患者、挂号、处方、收费怎么关联这套源码的数据库脚本通常叫his_db.sql或者类似的名字导入后大概会看到十几张表。HIS系统看着复杂核心逻辑其实就围绕几张主表转患者表、医生表、挂号表、处方表、收费表、药品库存表。表名核心字段作用patientid, name, id_card, phone, age患者基本信息doctorid, name, dept_id, title医生信息关联科室registerid, patient_id, doctor_id, reg_date, fee, status挂号记录状态0待诊1已诊2已退prescriptionid, register_id, drug_id, quantity, amount处方明细一条挂号多条药品chargeid, register_id, total_amount, pay_type, status收费记录记录实收金额drugid, name, stock, price, spec药品库存与价格关联关系走的是业务主键串联挂号表register.patient_id指向患者register.doctor_id指向医生医生看诊后在prescription里开药处方表的register_id指向挂号记录收费时生成一条chargecharge.register_id再指回挂号。也就是说一条挂号记录串起了“谁来就诊、看哪个大夫、开了什么药、收了多少钱”这条完整链路。建表语句里需要特别留意两个字段的设计register.status和charge.status。很多翻车现场就是前端把挂号记录显示出来了但状态一直没更新因为状态是0、1、2这种魔法数字前端枚举没配全。我一般会在阅读脚本时先把这些状态码整理成一份枚举对照表贴在小程序端的常量文件里。2.3 权限模型医生、护士、药师、管理员怎么控权HIS跟普通CRUD系统最大的区别在于权限敏感不是登录进去就能所有按钮都点的。这套源码的权限实现走的是经典的“用户-角色-菜单”三表模型后端用一个拦截器统一校验请求头里的token再根据当前用户的角色ID决定能不能访问某个接口。用户表(sys_user) → 角色表(sys_role) → 菜单表(sys_menu) 用户ID绑定角色角色ID绑定菜单列表逻辑说明登录接口拿到用户名密码后校验账户会返回一个token后续所有请求在Header里带Authorization: token。后端拦截器每个请求都会先解析token从Redis或者数据库里把用户角色拉出来再对照这个接口要求的角色编码做放行或拒绝。角色一般分管理员、医生、收费员、药师四档管理员拥有全部权限医生只能写处方收费员只能操作收费模块药师只能看出入库。参数说明路由权限判断通常在config包的Interceptor里如果你要临时放通某个接口做调试不用改数据库直接在WebMvcConfig里把该路径加到excludePathPatterns就行。但上线前记得删掉这是血泪教训。这里多说一句给his实施工程师的一个实用技巧拿到源码第一天先登录管理员账号把系统里每个角色点一遍记下权限盲区后面处理“为什么某科室看不见某某菜单”的需求时会快得多。3. 把Java后端拉起来从建库到Swagger接口验证的全过程这套源码自带安装教程但很多人按教程跑还是起不来问题不在源码在环境版本和配置细节。本机部署我建议按这个顺序走装JDK和MySQL → 导入SQL → 改application.yml → 启动 → 验证接口。3.1 环境准备JDK 8、MySQL 5.7/8.0、Maven 3.6的版本匹配先说版本这套HIS在JDK 8和Maven 3.6以上的环境下最稳。JDK 11理论上也能跑但部分老版本依赖在JDK 11下会有模块化报错比如java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException不折腾直接用JDK 8最省心。MySQL建议用5.7或8.0安装时字符集选utf8mb4避免后面存入患者名字里的生僻字变成问号。MySQL 8有一点要注意默认认证插件是caching_sha2_passwordSpring Boot连接时会报认证协议错误后面避坑章节详细说。Maven装完后在命令行确认一下环境变量生效java -version mvn -v mysql --version逻辑说明这三个命令分别验证Java编译器、Maven构建工具、MySQL客户端是否都能被命令行直接找到。任何一个报“不是内部或外部命令”都是环境变量没配好。参数说明Windows下配环境变量时JAVA_HOME指到JDK安装目录MAVEN_HOME指到Maven解压目录PATH里追加%JAVA_HOME%\bin和%MAVEN_HOME%\bin。不要指到bin目录本身这是新手最常见的配错点。3.2 导入数据库脚本初始化数据到底包含什么在MySQL里建一个空库然后把SQL脚本导进去。用命令行操作最直观mysql -u root -p CREATE DATABASE his_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; exit; mysql -u root -p his_db /path/to/his_db.sql逻辑说明第一步建库指定字符集和排序规则避免中文乱码第二步把SQL文件重定向导入到刚建好的空库。导入过程如果大量报错先看第一行错误信息通常是某个表已经存在或者SQL文件里自带USE语句指向了别的库名。参数说明COLLATE utf8mb4_general_ci是中文场景常用的排序规则兼容性比utf8mb4_unicode_ci更宽泛。如果你是本机演示用哪个排序规则影响不大如果是部署到生产环境且有跨库数据交互需求全库规则统一能少很多夜班电话。导入完成后登录MySQL确认一下表数量USE his_db; SHOW TABLES; SELECT COUNT(*) FROM sys_user;这里能看到初始化数据里带了一个管理员账号通常是admin/admin123这类默认密码具体以安装教程里写的为准。第一次登录后应立刻改掉内部系统最容易出问题的不是SQL注入而是默认密码流传太广。3.3 修改application.yml数据库连接与端口配置打开src/main/resources/application.yml重点改三个地方数据源地址、用户名、密码。spring: datasource: url: jdbc:mysql://localhost:3306/his_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 你自己的密码 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 server: port: 8080 mybatis-plus: configuration: map-underscore-to-camel-case: true逻辑说明url里的每个参数都有它存在的理由。serverTimezoneAsia/Shanghai解决MySQL 8下时间字段差8小时的问题useSSLfalse关闭SSL握手本机调试保平安allowPublicKeyRetrievaltrue配合MySQL 8的caching_sha2_password认证插件否则连接会直接报Public Key Retrieval is not allowed。参数说明map-underscore-to-camel-case: true让数据库的patient_id自动映射成实体类的patientId这套源码的实体类命名如果漏了这个配置MyBatis-Plus 查出来全是null。如果改完仍然查不到数据就去entity包里看看字段上有没有TableField注解把映射名写死。3.4 启动后端并用接口验证第一次跑通看哪几个信号配置改完进入项目根目录在命令行执行mvn spring-boot:run逻辑说明Maven先拉依赖再启动Spring Boot。首次启动会因为下载依赖包耗时几分钟进度走到Started Application in X.XX seconds才是启动成功之前所有的日志都是噪音。启动成功的标志性日志有三行第一行是Tomcat初始化的端口号确认是8080第二行是Completed initialization第三行是Started Application。看到第三行说明上下文没炸。然后打开浏览器访问接口文档页面验证接口是否能通http://localhost:8080/doc.html如果源码集成了knife4j或Swagger这个地址会列出全部接口清单如果没有集成就用下面这个命令验证最小接口curl http://localhost:8080/api/patient/list?page1size10逻辑说明curl发一个GET请求如果接口通返回的是JSON格式的分页数据里面带着code:200和患者列表如果返回404大概率是项目上下文路径改过比如server.servlet.context-path配了/his访问地址就得加前缀。参数说明page和size是MyBatis-Plus分页插件的标准参数page从1开始。有的接口参数名是current和pageSize两者含义相同只是命名不同报参数错误时先去controller里看RequestParam注解写的是什么名字。到这里后端已经能跑了。很多人在这一步就开始兴奋地打开小程序然后遇到一连串联调问题下一章说清楚小程序端怎么接上这套后端。4. 小程序端联调登录、挂号、下单这三条链路的对接细节小程序端是原生微信小程序工程拿到手后直接导入微信开发者工具导入时选“不使用云服务”和“不使用模板”因为这套小程序走的是自建后端接口跟微信云开发没关系。整个小程序端最核心的三个链路是微信登录换token、挂号页提交、个人信息查询这三条通了剩下的页面都是套模板。4.1 静态代码提示先在模拟器里看基建通不通微信开发者工具打开工程后第一件事不是点编译而是先看两个地方app.js里的全局变量和utils/request.js里的请求封装。大多数HIS小程序的接口请求都会统一封装在request.js里所有页面调用request()函数而不是直接写wx.request这样改接口地址只改一个文件就够了。// utils/request.js const BASE_URL http://localhost:8080 function request(path, method, data, header {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL path, method: method, data: data, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) || , ...header }, success(res) { if (res.data.code 200) { resolve(res.data.data) } else { wx.showToast({ title: res.data.msg, icon: none }) reject(res.data) } }, fail(err) { wx.showToast({ title: 网络异常, icon: none }) reject(err) } }) }) } module.exports { request, BASE_URL }逻辑说明所有请求自动带上本地缓存的token这是登录态保持的关键。后端统一返回code字段每层页面不用重复判错在封装里统一处理错误提示代码能砍掉三分之一。参数说明BASE_URL是本机调试最重要的配置默认是http://localhost:8080真机调试时localhost会指向手机自己所以手机预览必须把它改成电脑的局域网IP比如http://192.168.1.100:8080。这一步忘了改手机上一律是“网络异常”。4.2 微信登录换取手机号wx.login到后端code2Session登录流程是微信小程序连接HIS后端绕不开的第一关。用户点“微信登录”后小程序调wx.login拿到一个临时code传给后端后端拿这个code去微信服务器换openid和session_key再生成自己的token返回给小程序。小程序 wx.login() → 拿到code → 请求后端 /api/auth/login 后端 拿code appid secret 调微信 code2Session 接口 换取 openid → 查sys_user表有没有这个openid → 生成token返回前端// 小程序端登录页 wx.login({ success: (res) { if (res.code) { request(/api/auth/login, POST, { code: res.code }) .then(data { wx.setStorageSync(token, data.token) wx.setStorageSync(userInfo, JSON.stringify(data.userInfo)) wx.reLaunch({ url: /pages/index/index }) }) } else { console.log(登录失败, res.errMsg) } } })逻辑说明wx.login返回的code有效期只有五分钟且只能用一次所以登录接口必须实时获取不能缓存。后端拿到code后去微信官方接口换openid是标准做法这个步骤后端日志会打印openid排错时盯住这行日志如果连openid都没打印说明code传丢了或者appid密钥不对。参数说明后端appid和secret配置在application.yml里对应微信公众平台里“小程序”的AppID和AppSecret。注意这是小程序AppID不是公众号AppID两者长得一样但对着不同的后台。确认方法微信公众平台后台“开发管理-开发设置”里复制的那一串才是对的。很多这套源码的定制需求里用户点登录后要求自动带出手机号。实现方式有两种老接口getPhoneNumber返回动态令牌传给后端换手机号新接口走手机号快捷验证组件。后端处理方法一致拿到手机号后去sys_user表按手机号找用户找不到就自动创建一个“手机号用户”绑定openid第一次登录的账号密码置空这就是所谓的“静默注册”。4.3 挂号下单前端传参和后端校验的两个关键点挂号页面是这套系统业务价值最高的页面因为医院里最忙碌的场景就是号源分配。前端提交的挂号单核心字段是患者ID、医生ID、挂号日期、号源时间段。// POST /api/register/create 请求体 { patientId: 36, doctorId: 12, regDate: 2024-05-20, timeSlot: AM, regType: 1, fee: 15.00 }逻辑说明timeSlot字段常用两个值AM和PM代表上午和下午的号段regType表示普通号还是专家号不同号型对应不同挂号费。后端在service层要做三层校验号源是否存在、这个医生当天这个号段是否还有余号、这个患者今天有没有挂过同一个医生的号。参数说明fee这个字段前端传了后端也会再次从数据库里查出医生对应的挂号费覆盖掉防止用户篡改价格。这是医疗系统的硬性要求——金额一律以服务端计算为准。如果你在做二次开发记住这条规则否则上线第一天就会被人用改包价的方式薅羊毛。第二层校验是防止重复挂号的后端service层通常这样处理先在register表里查今天这个患者对这个医生有没有status0待诊的记录有就直接拒绝。并发场景下单靠查询不够需要给这张表加一个唯一索引(patient_id, doctor_id, reg_date)生产环境这套源码几乎全会在这一步加唯一索引不然连点两次“提交”就产生两条重复挂号。4.4 动态标题与页面栈小程序端几个高频改动的落点页面调通之后实施中最常改的是页面标题和登录态跳转。挂号成功页如果要在不同状态下显示不同标题比如“挂号成功”和“已退号”用wx.setNavigationBarTitle在onLoad里动态设置。退出登录则要记得清空本地缓存的token再跳回登录页这套源码里退出接口返回值跟登录接口结构一致前端照抄登录逻辑反着写就行。小程序端到这里基本能跑通“查看首页科室列表 → 选医生 → 提交挂号 → 查看挂号记录”的完整闭环。接下来是重头戏部署到服务器时一堆环境问题会接踵而至下一章把最常见的坑一次性讲透。5. 安装部署避坑指南从本机跑通到服务器上线的12个一致性坑本机能跑不代表服务器能跑服务器能跑不代表手机能访问。我把这套HIS从本机到Linux服务器部署过程中最常踩的坑汇总成五条每一条都是真实发生过的事故按“现象 → 原因 → 解决”的顺序说。5.1 MySQL 8密码加密方式导致后端启动直接失败现象后端启动日志报错java.sql.SQLException: Public Key Retrieval is not allowed或者Client does not support authentication protocol requested by server; consider upgrading MySQL client。原因MySQL 8默认认证插件是caching_sha2_password而JDBC驱动和它握手时默认不允许传输公钥。解决在application.yml的JDBC URL上加两个参数allowPublicKeyRetrievaltrueuseSSLfalse。如果是老项目用的mysql-connector-java 5.x驱动直接换成8.x版本的驱动包一劳永逸。url: jdbc:mysql://localhost:3306/his_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue5.2 服务器端口通了但手机小程序连不上现象本机浏览器访问http://服务器IP:8080能打开Swagger手机小程序却提示“网络异常”或request:fail。原因微信小程序要求请求地址必须是HTTPS且域名要在小程序后台配置为request合法域名直接用IP加HTTP线上版本会直接被微信拦截。解决本地开发时在微信开发者工具右上角“详情-本地设置”勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”真机预览时手机和电脑连同一WiFi就能访问。上线时则必须准备一个备案过的域名配好HTTPS证书在小程序后台“开发管理-服务器域名”里把request合法域名填进去。5.3 数据库时间比实际慢8小时挂号日期全错了现象小程序里挂号记录显示的时间比真实时间晚了8小时。原因JDBC连接字符串里没设置时区MySQL的serverTimezone默认用UTC比北京时间晚8小时。解决URL上强制加serverTimezoneAsia/Shanghai同时注意jackson.time-zoneGMT8也要配置否则接口返回的JSON时间字符串还是会转成UTC。改完这两处重启后端再查一次数据确认时间对上了再继续往下做。5.4 前端传参老报空指针JSON字段对不上现象接口文档里写的是patientId前端传了也能查到数据但换了另一页面的接口就报patientId is null。原因Java实体类用的是驼峰命名patientId而建表语句的字段是patient_id某个接口的RequestBody直接接收JSON并绑定实体时如果JSON字段名是patient_id而不是patientIdMyBatis-Plus虽然能映射数据库字段但Jackson在做JSON反序列化时不去做下划线转驼峰绑定就失败了。解决统一前后端字段风格小程序端全部用驼峰或者在application.yml里给Jackson加配置spring.jackson.property-naming-strategy: SNAKE_CASE完全不用因为一旦开启所有接口返回的字段全变下划线影响面太大。我一般的选择是保持前端驼峰后端不要动全局配置。5.5 Redis没启动后端整体起不来现象启动日志到一半报Unable to connect to Redis后端直接退出。原因这套源码的token和验证码缓存依赖Redis如果不启动RedisSpring容器初始化时创建Redis连接池的Bean就会失败整个应用启动不了。解决本地装一个Windows版Redis或者用Docker跑一个Redis实例默认端口6379启动后回到第3章的启动流程即可。如果本机是在做纯功能演示不想额外装Redis也可以把这个Bean改为内存缓存替代但这就属于二次开发范围了建议还是老老实实把Redis装起来生产环境早晚要用。现象里有一个共同点99%的启动失败都是配置不一致造成的不是源码本身的问题。所以在执行mvn spring-boot:run之前我强烈建议你先把下面这张检查表过一遍端口号有没有冲突、MySQL密码和yml里是不是一致、Redis起没起来、JDK版本是不是8。全部确认后启动再失败就逐行看日志而不是去网上复制一堆玄学依赖进来。6. 给HIS加一张统计报表并接入“我的”页面一套能复制到任意模块的路线后端和小程序都能跑了接下来最常用的需求是“加模块”比如领导要一张“每个科室本周就诊人次”的统计报表从数据库设计到小程序展示整条链路是固定的我把这套操作顺序讲清楚照着它复制到任意模块都能少走弯路。第一步在MySQL里写统计SQL先在客户端里验证结果再进代码SELECT d.dept_name, COUNT(r.id) AS visit_count FROM register r LEFT JOIN doctor d ON r.doctor_id d.id WHERE r.reg_date DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY d.dept_name ORDER BY visit_count DESC;第二步在Java后端的mapper层加方法MyBatis-Plus项目里用注解SQL最省事Select(SELECT d.dept_name AS deptName, COUNT(r.id) AS visitCount FROM register r LEFT JOIN doctor d ON r.doctor_id d.id WHERE r.reg_date DATE_SUB(CURDATE(), INTERVAL 7 DAY) GROUP BY d.dept_name ORDER BY visitCount DESC) ListMapString, Object countVisitByDept();逻辑说明统计报表的返回值用ListMapString, Object而不新建实体类是因为报表结构经常调整硬编码实体类会导致每次改字段都改Java文件不值得。前端拿到JSON直接渲染字段名和SQL别名一一对应。参数说明DATE_SUB(CURDATE(), INTERVAL 7 DAY)是MySQL计算近7天数据的标准写法如果要改成当月统计换成DATE_FORMAT(r.reg_date, %Y-%m) DATE_FORMAT(CURDATE(), %Y-%m)。这两种写法本系统里我都实测过第一种在数据量到十万级时记得在reg_date上加索引否则会全表扫。第三步在controller里暴露一个接口返回统一格式的JSON第四步在小程序utils/request.js的request方法上直接复用调用接口后渲染在“我的”页面的统计卡片上。整个流程是从SQL到接口到前端渲染的完整链路改SQL、加mapper、加service、加controller、小程序页面调接口五步走完。这套路线我复制到过药品库存预警、收费员日结、科室排班等多个模块每轮最多半天。但那以后我强制自己每次加统计SQL前先跑一遍EXPLAIN看索引原因是一次上线前一天“我的”页面统计接口到数据量上来时响应从300ms飙到3秒把“科室就诊人次”的锅甩给了“网络波动”排查了一个下午最后发现是reg_date没走索引。从那以后凡是涉及日期范围的统计我都要先看执行计划再往代码里放。希望这篇笔记和这套源码能让你在跑通HIS的路上少一点查日志到凌晨的经历能帮你把这个过程压缩到半天以内那就值了。本文还有配套的精品资源点击获取
返回列表