资讯动态

HIS系统实战拆解:从部署避坑到二次开发

发布时间:2026/10/18 10:02:39 来源:尧图企业网站定制
简介这是一套完整的医院信息系统HIS源代码适合医疗信息化开发人员、医院信息科员工以及对医院业务流程建模感兴趣的软件工程师既可用于学习研究也可作为项目参考。系统采用Delphi开发界面基于VCL组件库构建覆盖药房管理、护士站、药库管理、住院结算等核心模块实现了药品库存自动跟踪、电子处方审核、护理记录、医嘱执行、住院费用结算与医保核算等业务功能。资源包共包含4290个文件其中pas、dfm、dpr等Delphi工程文件构成主体如1993个pas单元、1875个dfm窗体同时提供sql数据库脚本、资源配置、使用文档和维护工具压缩包大小约6.51MB便于直接导入开发环境进行编译和调试。已有2599人学习下载在同类HIS学习资源中具有一定的参考热度。读者可借助该源码深入理解HIS系统的模块划分、数据库表结构和典型业务流程并结合配套文档进行二次开发或功能扩展从而服务于实际医疗信息化项目建设。1. 一套完整的his系统值不值得下先看这四件事一套完整的his系统在资源包里往往顶着“毕业设计”“课设源码”“中小医院信息化”几个标签一起出现。名字越短坑往往越多——我拆过不下十套HIS资源真正能跑通挂号、收费、发药全流程的不到一半。这套资源给我的第一印象是模块面挺全门诊挂号、医生处方、收费结算、药房药库、系统权限都有前端是Vue后端是Spring Boot数据库脚本和部署文档齐全。适合三类人做医院信息化课设的学生、接中小诊所改造项目的开发者、想研究HIS业务模型的技术负责人。但值不值得下不看出身得按四步验证来看技术栈新旧、SQL脚本可执行性、业务流程闭环度、文档覆盖度。这篇文章就按这个顺序把它拆开。2. HIS系统的主干模块组成与技术栈选型2.1 门诊到药房四个核心模块的数据流长什么样HIS系统全称Hospital Information System医院信息系统。一套完整的his系统业务主线上至少有四个模块是必须闭环的门诊挂号、医生工作站开处方、收费结算、药房药库。这四块如果缺一块就只能叫“演示项目”不能叫“完整系统”。先看数据流。患者到院第一站是挂号前端选择科室、医生、就诊时段后端写入一条挂号记录同时扣减该医生的号源余量。接着患者进诊室医生在医生站界面看到待诊列表点开患者录入诊断和处方处方拆成主表和明细表落库。患者拿着处方去收费窗口收费员在系统里调出这条处方展示药品明细和总金额患者付款系统生成一条收费记录并把处方状态改成“已收费”。最后患者去药房药师扫描或输入挂号单号系统展示待发药清单药师确认发药系统扣减药品库存并把处方状态改成“已发药”。这条链路上有一个关键的贯穿字段挂号单号。从挂号、处方、收费到发药四个环节都拿它关联。这套资源里我用到的实体关系大致是这样his_registration表存挂号记录his_prescription存处方主表his_prescription_item存处方明细his_payment存收费记录his_drug_batch和his_drug_stock共同维护药品库存。挂号单号在每张业务表里都出现相当于这条业务链上的“业务主键”。判断一套HIS资源完整不完整不用看它宣传了多少模块直接把上面四个环节串联走一遍能挂上号、能开出方、能收上费、能发出药同时每个环节的状态流转正确这才算闭环。很多资源单看页面挺丰富实际上数据流是断的开完处方在收费界面查不到这就是典型的“假完整”。2.2 技术栈选型为什么前后端分离是这套资源的主力形态这套资源大概率用的是Spring Boot MyBatis MySQL Vue的组合辅以Redis做验证码和登录状态存储。这个选型几乎是这类HIS资源的默认配方原因很实际单机能跑、依赖好装、学习曲线平缓课程设计和中小型诊所的信息化改造都够用。后端用Spring Boot好处是内嵌Tomcat打包成jar直接java -jar就能起不用单独配置外部容器。MyBatis负责SQL映射HIS这类系统大量涉及复杂查询比如按科室统计挂号量、按药品分类汇总出库量使用XML手写SQL比JPA的自动生成更可控。前端用Vue技术栈加Element UI组件库表单、表格、弹窗这些HIS高频界面组件开箱即用。Redis在这套系统里承担两件事验证码的key存储和登录token的会话保持没有它登录环节会直接失效。需要强调边界。这架构是单体应用不是微服务也没有引入Nacos、Gateway、分布式事务这些组件。跨科室、跨院区的场景需要额外改造但它作为一套“完整HIS系统”的基座是合格的。在部署层面这套架构对机器要求极低2核4G的虚拟机带MySQL、Redis、后端服务、前端构建全流程毫无压力。这也是我推荐先拿它跑通业务主线的核心理由与其上来研究分布式不如先把单体业务吃透。2.3 资源完整度验证清单下载之后先看这五个文件拿到资源包别急着导入数据库。先用五分钟把这五个关键文件找到就能判断这套资源靠不靠谱。我把这套资源的验证清单整理成下面的表格按顺序检查即可。检测项看什么判断标准数据库脚本目录是否存在SQL脚本文件能在干净的MySQL 5.7中一次性导入无报错后端依赖文件pom.xml或build.gradle包含spring-boot-starter-web、mybatis、mysql驱动等核心依赖后端配置文件application.yml或properties数据源、Redis地址明文可改不是写死的生产库前端工程package.json和src目录能定位到登录页、挂号页、收费页组件部署文档README或部署说明有环境要求、启动步骤、默认账号说明上面五项全过这套资源基本可以进入实操环节。任何一项有问题都要提前记下来后面部署时大概率会踩坑。比如SQL脚本报错说明脚本是从别的数据库版本导出的需要做兼容处理具体怎么修后面避坑章节会讲。前端工程缺package.json意味着整个前端没法构建只能看静态页面这对二次开发来说基本等于残废。3. 数据库脚本拆解HIS核心表结构与外键关系3.1 先看表清单从SQL脚本还原HIS的业务边界数据库脚本是一套HIS系统最诚实的名片。页面可以造假脚本很难全套伪造。打开SQL脚本用文本编辑器搜索CREATE TABLE统计一下表数量再看表名和注释业务边界瞬间清晰。这套资源的表结构大致分成两类业务表和字典表。业务表记录“发生过的具体事件”比如who哪个患者、when什么时间、what做了什么操作、how much多少钱。字典表则是“对业务数据的解释”比如科室字典、药品分类字典、收费项目类型字典。业务表的特点是数据持续增长挂号记录、收费记录会越来越多字典表的特点是量小稳定基本只做增改。核心业务表我整理成了下表表名职责关键字段his_registration挂号记录register_no、patient_name、dept_id、doctor_id、statushis_prescription处方主表register_no、doctor_id、total_amount、statushis_prescription_item处方明细prescription_id、drug_id、quantity、unit_pricehis_payment收费记录payment_no、prescription_id、amount、pay_typehis_drug_batch药品批次drug_id、batch_no、expire_date、stock_quantityhis_drug_stock药品库存汇总drug_id、total_quantity、warning_linehis_user系统用户username、password、salt、real_name、dept_idhis_role角色role_name、role_codehis_menu菜单权限menu_name、parent_id、url、permission_code这些表之间的关联关系是理解整套系统的关键。挂号到处方通过register_no关联处方到明细通过prescription_id关联收费通过prescription_id关联到处方从而知道收了哪张方子的钱药库则通过drug_id关联批次和库存。把这五个关系理清楚HIS的业务模型就通了八成。3.2 三张核心表的DDL拆解字段类型与索引设计只看表清单还不足以复现这套系统必须深入到SQL层面。我挑三张最关键的建表语句拆解一下这三张表几乎决定了整套系统跑得顺不顺。-- 挂号记录表 CREATE TABLE his_registration ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键, register_no varchar(32) NOT NULL COMMENT 挂号单号全局唯一, patient_name varchar(64) NOT NULL COMMENT 患者姓名, dept_id bigint(20) NOT NULL COMMENT 科室ID, doctor_id bigint(20) NOT NULL COMMENT 医生ID, visit_date date NOT NULL COMMENT 就诊日期, period tinyint(4) DEFAULT 1 COMMENT 就诊时段 1上午 2下午, status tinyint(4) DEFAULT 0 COMMENT 状态 0待诊 1已就诊 2已退号, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_register_no (register_no), KEY idx_dept_date (dept_id, visit_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT挂号记录表;这段DDL里有三个设计经验值得参考。register_no字段建了唯一索引挂号单号作为全链路关联的业务主键任何重复都会导致处方、收费串号所以在数据库层面直接挡死比在业务代码里判断更可靠。联合索引idx_dept_date建立在dept_id和visit_date上这是高频查询路径护士每天上班查“某科室今天挂了多少号”这个索引能直接覆盖。字段状态status用tinyint而不是varchar查快照时更省空间也避免出现“已挂号”和“已挂 号”这种带空格的脏数据。再看处方明细表。药品金额、数量这类数值字段有严格精度要求建表时如果不注意类型后面算账全是坑。CREATE TABLE his_prescription_item ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键, prescription_id bigint(20) NOT NULL COMMENT 处方主表ID, drug_id bigint(20) NOT NULL COMMENT 药品ID, drug_name varchar(128) NOT NULL COMMENT 药品名称快照, specification varchar(64) DEFAULT NULL COMMENT 药品规格, quantity int(11) NOT NULL COMMENT 药品数量, unit_price decimal(10,2) NOT NULL COMMENT 单价两位小数, amount decimal(10,2) NOT NULL COMMENT 金额等于数量乘单价, PRIMARY KEY (id), KEY idx_prescription (prescription_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT处方明细表;这里最值得说的是两个点。第一金额相关字段全部用decimal(10,2)严禁用float或double。float是近似值IBM 和钱有关的数据出现0.01的误差就是事故decimal是精确十进制虽然占空间大一点但合规。第二drug_name字段单独存了一份快照而不是只存drug_id关联药品主表。原因很实际药品名称、规格会调整如果只存drug_id一年后看历史处方显示的是已经改过的药品名对账时说不清当时开的是什么。所以明细表里冗余一个drug_name字段是HIS系统的标准做法。3.3 药品库存与收费明细两个最容易被忽视的关联拆完建表语句还有两个关联关系要注意这两个地方是HIS资源里翻车率最高的位置。第一个是药品库存和批次的关系。这套HIS没有在his_drug_stock总库存表上直接扣减而是引入了his_drug_batch批次表。每次药品入库生成一个批次记录包含批号、生产日期、失效日期、入库数量。发药时优先扣失效日期最近的批次也就是先进先出原则。这个设计对药房业务至关重要药品有过期风险如果不按批次管理一批药过期在库房里都查不出来。代码里扣减库存的SQL大致是这样-- 扣减批次库存优先扣最早过期的批次 UPDATE his_drug_batch SET stock_quantity stock_quantity - 1 WHERE drug_id #{drugId} AND expire_date (SELECT MIN(expire_date) FROM his_drug_batch WHERE drug_id #{drugId} AND stock_quantity 0) AND stock_quantity 0;这段SQL的意思是找到该药品批次里最早过期且有库存的那一批扣掉1个单位。子查询先选出最早的过期日期外层再限定同一药品、同一批次、剩余库存大于0。这个写法相比“直接UPDATE总库存”的好处是每一次发药都能追溯到具体的批号效期管理不会失控。另一个容易忽略的点是库存数量字段必须用int扣减时要带条件stock_quantity 0防止负数库存。第二个是收费明细和处方明细的对账关系。收费记录his_payment通过prescription_id关联处方主表一个处方对应一条收费记录。但处方里的金额总额可能存在手工优惠、医保报销等因素导致实收金额不等于处方总额。这套系统在his_payment表里设计了amount实收、discount_amount优惠、insurance_amount医保报销三个字段三者关系是实收 处方总额 - 优惠 - 医保报销。日结对账时直接查当天支付的汇总和处方明细汇总比对两边对得上才算日清。很多不完整的HIS资源在设计收费表时只存一个total_amount完全无法处理医保、优惠、找零场景。所以下载资源后先看his_payment表里金额字段拆分得细不细就能判断设计者有没有真正做过医院项目。4. 本地部署与运行把源码变成一套能登录的系统4.1 环境准备JDK、MySQL、Redis、Node的版本卡点这一节是动手环节。先把环境准备好版本不对后面全是玄学报错。这套HIS资源在本地复现时我一般用这个组合JDK 1.8、MySQL 5.7、Redis 5.x、Maven 3.6、Node 14到16之间。JDK版本是最值得强调的一个卡点。Spring Boot 2.x在JDK 1.8下运行最稳如果你装了JDK 17启动时大概率会遇到CGLIB代理相关报错因为高版本JDK对动态代理的模块访问做了限制。这套资源本身没有升级到Spring Boot 3.x所以老老实实用JDK 8别一上来就用最新版。MySQL方面推荐5.7。这不是说8.0不行而是很多HIS资源包的SQL脚本是从5.7或更早的版本里导出的里面可能用了utf8mb4_general_ci这种排序规则拿到MySQL 8.0里会报Unknown collation错误。5.7兼容性更好脚本导入基本不会卡壳。如果你机器上已经装了8.0也可以后面用避坑章节里的替换方法处理但新手建议直接统一到5.7。Redis是这套HIS登录功能的前置条件。验证码的生成、存储、校验都走Redis不装或者不启动登录页面会出现验证码刷新不出来的问题。Node版本也要注意前端工程是基于Vue 2的用webpack构建Node 16是实测最稳的版本。Node 18以上在依赖安装时容易出现node-sass编译报错这个坑我在多个资源上踩过处理起来比较折腾。4.2 后端配置与启动数据源、Redis、端口三步配好环境就位后先改后端配置。找到src/main/resources/application.yml文件用文本编辑器打开把数据源和Redis指向本地。一个典型的配置如下server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/his_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 database: 0 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.his.demo.entity配置里的三个细节需要说明。url参数中serverTimezoneAsia/Shanghai必须带否则Linux服务器或Docker环境下查询出来的时间会差8小时挂号时间变成前一天晚上。driver-class-name写com.mysql.cj.jdbc.Driver是MySQL 8.x驱动的类名如果你的MySQL是5.7且驱动包版本匹配写com.mysql.jdbc.Driver也能跑但推荐统一用cj驱动。Redis的database: 0表示使用0号库默认无密码如果你本机Redis设置了密码需要额外加一行redis.password。配置改完在后端工程根目录执行Maven打包命令mvn clean package -DskipTests java -jar target/his-server.jar第一条命令会先清理旧的构建产物然后跳过测试重新打包-DSkipTests这个参数在打包阶段能省去跑单元测试的时间。第二条命令直接启动Spring Boot内嵌的Tomcat。启动日志里看到“Started HisApplication in xx.xxx seconds”说明后端已经起来了然后用命令确认端口监听netstat -ano | findstr 8080这条命令在Windows下查8080端口的监听状态如果没有任何输出说明端口被占用或启动失败去日志里搜索ERROR关键字定位问题。后端是整套系统的核心服务起不来后续所有环节都谈不上。4.3 前端工程启动与登录验证从npm install到管理员进系统后端起来了接着起前端。打开前端工程目录先安装依赖npm installnpm install的过程可能比较慢网络环境差的时候可以配一下镜像源把registry指向国内镜像即可具体命令就不展开了。依赖装完启动开发服务器npm run dev前端默认跑在8080或3000端口。注意这时候前后端是分离的两个进程前端页面要能访问后端接口需要检查前端工程的代理配置。打开vue.config.js或config/index.js找到devServer配置devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这段配置的意思是前端开发服务器跑在8081端口凡是请求路径以/api开头的接口都转发到后端的8080端口。changeOrigin: true表示改写请求头中的Host字段为target域名解决跨域问题。如果前端页面打开后接口全是404或跨域报错优先检查这段代理配置的端口和路径前缀是否匹配。浏览器里访问http://localhost:8081看到登录页输入这套资源的默认管理员账号。常见的是admin/admin123或admin/123456具体以你手里资源的部署文档为准。登录成功后跳转到首页左侧菜单能展开、能访问挂号收费页面说明前端到后端的链路已经通了。首次进去我一般先不改任何业务数据直接去系统管理里把管理员密码改掉这是这套资源的默认安全基线。5. HIS部署避坑与常见问题排查五个高频事故的完整复盘5.1 SQL脚本导入失败Unknown collation或语法报错现象在Navicat里打开SQL脚本执行弹出红色错误提示常见的是[ERR] 1273 - Unknown collation: utf8mb4_0900_ai_ci或者1064语法错误脚本执行到一半中断数据库中只创建了部分表。原因脚本大概率是从MySQL 8.0中导出的utf8mb4_0900_ai_ci是8.0的默认排序规则MySQL 5.7不认识。至于1064语法错误多半是脚本里用了窗口函数ROW_NUMBER、OVER等5.7不支持。解决拿文本编辑器打开SQL脚本用全局替换功能把utf8mb4_0900_ai_ci全部替换成utf8mb4_general_ci另存后重新导入。如果问题出在窗口函数最简单的办法是直接在MySQL 8.0环境里建库或者手工改写那几条SQL把窗口函数改成子查询关联。我一般优先换排序规则这一步十分钟内能解决。5.2 前端请求接口403或404跨域与代理路径不一致现象前端登录页能打开但输入账号密码点击登录F12控制台显示请求报403或404有的浏览器还提示跨域CORS错误。原因403通常是跨域被后端拦截404则是代理转发路径不对。前面讲到前端devServer需要把/api开头的请求代理到后端如果代理配置里的target端口写错了或者后端接口前缀不是/api请求就会转发到错误的地方。解决先看F12里请求的完整URL确认请求打到了哪个端口。再对比vue.config.js里proxy的target配置确保路径和端口完全一致。后端如果不只接一个前端地址最好再加一个全局CORS配置类允许本地前端地址跨域访问。我见过最隐蔽的坑是前端把端口写在8080后端启动时实际跑在8081两端都配了代理但端口写反排查半天。5.3 登录验证码不出现或刷新失败Redis未启动现象登录页上验证码图片一直转圈或显示裂图F12看到验证码接口请求报错错误信息里带有Redis连接超时或连接拒绝。原因这套资源的验证码流程是后端生成图片把校验值存到Redis设置过期时间再以流的形式返回图片。Redis没启动后端在写缓存时直接抛异常验证码自然出不来。解决先启动本地Redis服务。Windows下如果只是解压的绿色版直接命令行执行redis-server.exe看到“Redis is running in standalone mode”即为成功。Linux下用systemctl start redis或redis-server /etc/redis/redis.conf。启动后重新启动后端再刷新登录页验证码图片能正常显示就说明打通了。这个环节我建议放在后端启动之前做能少走一步弯路。5.4 并发挂号时号源超卖先查后改的并发缺陷现象两个挂号窗口同时操作明明号源只剩1个结果两个患者都挂上了同一个医生同个时段的号。查his_registration表同一医生同一时段多了一条记录但his_schedule里的号源余量变成了负数。原因这是典型的先select再update的并发问题。两个事务同时读到余量为1各自在业务代码里判断大于0后执行扣减两个update都成功库存就变成负数了。这套资源在初期版本里很可能没有做行锁需要二次修复。解决把“查了一遍再改”换成一条原子SQLUPDATE his_schedule SET remain_count remain_count - 1 WHERE id #{scheduleId} AND remain_count 0;这条SQL把“判断余量大于0”和“扣减1”合在一起数据库行锁会保证同一时刻只有一个事务能成功执行。执行后检查受影响行数等于1说明扣减成功等于0说明号源已经没了业务上提示“号源不足”即可。从那以后我搭HIS相关Demo时涉及扣减类操作一律走原子更新杜绝先查后改。5.5 查询出来的时间比本地时间早8小时时区错位现象挂号列表里所有记录的create_time都比当前本地时间少了8小时比如现在下午3点库里显示的却是早上7点。收费记录、发药记录同样如此。原因MySQL连接串和Jackson序列化两处时区设置不一致。数据库服务端用的UTC时区应用层用的北京时区GMT8两边一换算就差了8小时。更深一层是url里没有指定serverTimezone驱动用了默认时区。解决双保险设置。第一步回到application.yml把数据源url加上serverTimezoneAsia/Shanghai第二步在spring配置下补充spring: jackson: time-zone: GMT8前一处解决从MySQL查出“日期时间”类型时的转换基准后一处解决后端把Date对象序列化成JSON字符串时的时间偏移。两处都改保存重启后端再查时间就对齐了。这个坑在Windows本机不容易出现一到Linux服务器或Docker容器里就会暴露建议配置阶段一次性改好别等上线了再后悔。6. 顺着这套HIS加一个体检模块建表、接口、菜单的完整路径拿到一套能跑的HIS最大的价值不是直接部署到生产而是顺着它的骨架长出新模块。我以“体检登记”为例展示在这套资源上做二次开发的完整路径这个流程适用于任何新业务模块。第一步建表。在数据库脚本里追加一张his_exam_register表字段包括exam_no体检编号、patient_name患者姓名、package_id套餐ID、status状态、create_time。数据库层面只做两件事exam_no加唯一索引create_time给默认值CURRENT_TIMESTAMP。第二步后端接口。复制这套资源里已有的“科室管理”三层结构Controller接收请求、Service写业务校验、Mapper写SQL。RestController RequestMapping(/api/exam) public class ExamController { Autowired private ExamService examService; PostMapping(/register) public Result register(RequestBody ExamRegisterDTO dto) { examService.register(dto); return Result.success(); } }RequestBody接收前端传来的JSON参数Spring Boot自动反序列化成DTO对象。Service层要做的是生成exam_no、校验必填字段、调用Mapper插入数据。新增一个模块不需要改动既有代码这种“复制改”的方式是单体应用做二次开发最顺的路径。第三步前端页面。在src/views下新建exam.vue复用现有页面的el-form和el-table组件表单提交时调/api/exam/register接口。菜单配置需要在his_menu表里插入一条记录parent_id挂到系统管理下SQL大致是INSERT INTO his_menu(menu_name, parent_id, url, permission_code) VALUES(体检登记, 1, /exam, exam:register)。插入后刷新页面左侧菜单就会出现新入口。我栽过的跟头是在菜单表里只插了记录忘了给角色授权结果菜单显示了点进去提示无权限。从那以后每次拿新资源我都先建一个测试角色把全部菜单权限勾上再用这个角色验证页面功能能避免很多“看起来没反应”的假象。这套流程走完你对这套HIS资源的理解深度就超过了大多数只看过README的人。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑