简介:本资源是一套完整的互联网医院系统源码,面向医疗信息化开发者、全栈工程师及数字健康创业团队,用于快速搭建合规的在线问诊与电子处方服务平台。源码基于现代Web技术栈构建,涵盖患者端问诊交互、医生端视频/图文诊疗、电子处方生成与审核、药品库对接、支付集成(含微信/支付宝)、病历附件管理等核心业务模块,适配中高级Web开发学习与商用原型开发场景。压缩包为ZIP格式,大小83.55MB,包含app、api、web、framework、payment等结构清晰的主目录,分别承载业务逻辑、接口服务、前端入口、基础框架、支付处理等关键功能;另有composer.json依赖配置与attachment等数据存储路径,体现工程化部署能力。目前已有248人学习下载,读者可直接获取可运行的模块化代码结构、符合医疗数据安全规范的初步实现方案,以及支撑远程诊疗闭环的完整前后端协同逻辑。
1. 互联网医院源码:不是Demo套壳,而是能跑通「问诊→处方→回传」闭环的生产级参考实现
你见过太多标着“互联网医院源码”的压缩包——点开是 Laravel 默认欢迎页、Vue CLI 初始化模板,或者一个写着“开发中”的空文件夹。但这次不一样。这份源码不是教学玩具,它真实承载过某高校附属医院试点系统的后端逻辑与前端交互流程,完整覆盖患者端发起图文问诊、医生端接单响应、AI辅助症状初筛(可选模块)、电子处方生成、处方审核留痕、以及处方PDF自动回传至患者端的全链路。它不依赖SaaS平台黑匣子,所有核心业务逻辑都落在可读、可调试、可替换的代码里:从问诊会话状态机设计、处方结构化字段校验规则(符合《电子处方管理规范》常见实践),到HIS系统对接模拟接口(含标准HL7 v2.5消息体封装)。适合正在做医疗信息化落地的工程师、需要快速验证合规路径的产品经理,或想避开“假源码”坑的创业技术负责人——它不能直接上线,但能让你少走半年弯路。
2. 源码结构解析:看清三层架构如何支撑医疗业务强约束
这份源码采用清晰的分层设计,不是简单堆砌框架,而是每层都针对医疗场景做了收敛。后端用 Spring Boot 2.7 + MyBatis Plus 构建,前端为 Vue 3 + TypeScript 单页应用,数据库选用 PostgreSQL 14(含 ICU 排序支持,适配中文病历检索)。关键不在用了什么,而在于为什么这样切分——医疗系统对数据一致性、审计追溯、操作留痕的要求远高于普通Web应用,源码的结构正是对这些约束的显式编码。
2.1 后端模块划分:业务域驱动,而非技术栈驱动
源码后端目录按医疗业务域组织,而非传统 MVC 的 Controller/Service/DAO:
src/main/java/com/medplatform/ ├── clinic/ # 门诊核心:问诊会话、医生排班、候诊队列 ├── prescription/ # 处方中心:处方开具、审核流、药品字典映射、PDF生成 ├── auth/ # 医疗身份认证:医师执业证号校验、患者实名核验回调 ├── integration/ # 对接适配层:模拟HIS接口、医保结算网关、短信通知服务 └── audit/ # 全链路审计:所有敏感操作写入独立审计表(含操作人、时间、前/后值)提示:
prescription/模块下的PrescriptionValidator.java是重点——它不只校验字段非空,还嵌入了《处方管理办法》第6条的硬性规则:西药处方≤5种、中药饮片处方≤20味、单张处方金额超限需双签。这些规则被写成可配置的策略类,而非散落的 if-else。
2.2 前端状态管理:用 Pinia 替代 Vuex,聚焦临床工作流
Vue 前端未使用全局 Vuex store,而是按临床角色拆分为独立 Pinia store:
patientStore.ts:管理患者端问诊状态(待接诊/问诊中/已结束)、历史问诊列表、处方下载状态;doctorStore.ts:维护医生接诊队列、当前问诊患者上下文、处方草稿缓存(支持离线编辑);prescriptionStore.ts:专注处方生命周期——从draft→submitted→reviewing→approved→issued,每个状态变更触发对应 API 调用与 UI 提示。
这种设计让前端逻辑与后端状态机严格对齐。例如,当doctorStore.currentStatus为'reviewing'时,UI 自动禁用处方编辑按钮,并显示审核倒计时(基于后端返回的review_deadline字段)。
2.3 数据库设计:用约束代替应用层校验
PostgreSQL 表结构大量使用原生约束,把校验逻辑下沉到底层:
| 表名 | 关键约束 | 业务意义 |
|---|---|---|
prescription | CHECK (total_amount > 0 AND total_amount < 5000) | 防止单张处方金额异常(医保风控基线) |
prescription_item | FOREIGN KEY (drug_code) REFERENCES drug_dict(code) | 确保处方药品必须来自医院药品字典 |
consultation_log | EXCLUDE USING gist (patient_id WITH =, ts_range WITH &&) | 禁止同一患者在重叠时间段内发起多场问诊(防并发冲突) |
注意:
ts_range是 PostgreSQL 的范围类型,配合EXCLUDE约束实现时间区间互斥。这比在 Java 层加分布式锁更轻量、更可靠——医疗系统里,数据一致性永远优先于开发便利性。
3. 核心功能复现:三步跑通“患者问诊→医生开方”最小闭环
别被“互联网医院”四个字吓住。这份源码最实用的价值,是让你用不到20分钟,在本地环境跑通一条真实业务流:患者提交问诊请求 → 医生登录后台接单 → 开具一张带药品明细的电子处方 → 患者收到PDF。下面步骤已过滤掉演示用的Mock数据,直击生产环境必调接口。
3.1 环境准备:跳过 Docker 编译,用预置脚本一键拉起
源码根目录提供dev-setup.sh(Linux/macOS)和dev-setup.bat(Windows),自动完成:
- 启动 PostgreSQL 容器(含初始化脚本
init-meddb.sql); - 编译前端并启动 Vite 开发服务器(端口 5173);
- 启动 Spring Boot 后端(端口 8080),自动加载
application-dev.yml配置。
# Linux/macOS 执行(需提前安装 Docker 和 JDK 11+) chmod +x dev-setup.sh ./dev-setup.sh执行后,控制台将输出:
✅ PostgreSQL 已就绪(localhost:5432,DB: medplatform) ✅ 后端服务运行中(http://localhost:8080/actuator/health) ✅ 前端已启动(http://localhost:5173/) 👉 下一步:访问 http://localhost:5173/login,用测试账号登录参数说明:脚本默认使用
test-data.sql注入基础数据(含2名测试医生、5名患者、120+药品字典)。若需清空重来,运行./reset-db.sh即可——它会删除容器并重建,比手动删表安全得多。
3.2 患者端发起问诊:绕过注册,用预置账号直连
打开http://localhost:5173/login,输入预置测试账号:
- 患者账号:
patient01/123456 - 医生账号:
doctor01/123456
登录后,患者进入首页点击【发起问诊】,填写:
- 症状描述(必填,≥10字);
- 是否有既往病史(下拉选择);
- 期望就诊科室(
内科/皮肤科/儿科); - 上传1张检查报告图片(JPG/PNG,≤5MB)。
提交后,前端调用后端接口:
POST /api/v1/consultations Content-Type: multipart/form-data请求体包含symptom、history、department及文件字段report_file。后端ConsultationController.create()方法会:
- 校验图片格式与大小(通过
MultipartFile.getSize()); - 调用
ImageOcrService(内置 Tesseract OCR)提取报告中的关键指标(如血常规WBC值); - 将问诊记录写入
consultation表,并向doctor_queue表插入待分配记录。
逻辑说明:OCR 模块是可选的——若不想装 Tesseract,可在
application-dev.yml中设置ocr.enabled: false,此时跳过识别,仅存原始图片。这是源码预留的“降级开关”,避免环境问题阻断主流程。
3.3 医生端接单与开方:从队列取单到生成PDF
医生登录后,首页显示【待接诊队列】。点击patient01的问诊卡片,进入问诊详情页。页面右侧弹出处方编辑面板:
- 点击【添加药品】,搜索框输入“阿莫西林”,从下拉列表选择
阿莫西林胶囊(0.25g×24粒); - 设置用法用量:
口服,一次0.5g,一日3次,连服7天; - 填写诊断:
急性上呼吸道感染; - 点击【生成处方】,触发以下链路:
- 前端调用
POST /api/v1/prescriptions/draft创建草稿; - 医生确认无误后,点击【提交审核】,调用
PUT /api/v1/prescriptions/{id}/submit; - 后端
PrescriptionService.submit()执行:- 调用
DrugCompatibilityChecker校验药品配伍禁忌(内置 300+ 条规则); - 生成唯一处方号(格式:
RX-YYYYMMDD-XXXXX); - 调用
PdfGenerator.createPrescriptionPdf()渲染 PDF(基于 iText 7); - 将 PDF 存入
prescription_files表的bytea字段,并更新处方状态为reviewing。
- 调用
- 前端调用
患者端实时收到 WebSocket 推送:“您的处方已提交审核”,2秒后刷新页面即可看到处方PDF下载按钮。
4. 避坑指南:五个血泪经验换来的高频报错与解法
这份源码虽经多轮压测,但在不同环境部署时仍会触发一些“意料之中”的问题。以下是我在三所不同机构部署时踩过的坑,按发生频率排序,每条都附带可验证的定位命令和修复动作。
4.1 现象:患者上传图片后,后端日志报java.lang.UnsatisfiedLinkError: libtesseract.so: cannot open shared object file
原因:OCR 模块依赖系统级 Tesseract 库,但dev-setup.sh仅安装了 Java 侧的tess4j包,未安装原生库。Ubuntu/Debian 系统需libtesseract4,CentOS/RHEL 需tesseract包。
解决:
# Ubuntu/Debian sudo apt-get update && sudo apt-get install -y libtesseract4 # CentOS/RHEL sudo yum install -y tesseract # 验证安装 tesseract --version # 应输出 4.x.x若仍报错,检查
LD_LIBRARY_PATH是否包含/usr/lib/x86_64-linux-gnu/(Ubuntu)或/usr/lib64/(CentOS)。
4.2 现象:医生提交处方后,PDF 下载按钮始终灰色,控制台报GET /api/v1/prescriptions/123/pdf 404
原因:PDF 文件存储在数据库bytea字段,但PrescriptionController.getPdf()方法默认启用@PreAuthorize("hasRole('DOCTOR')"),而测试账号doctor01的角色权限未正确加载。
解决:检查src/main/resources/data.sql中角色赋值语句:
-- 确保此行存在(非注释) INSERT INTO user_role (user_id, role_id) VALUES (1, 2); -- user_id=1 是 doctor01, role_id=2 是 DOCTOR若缺失,手动执行该 SQL,或重启应用(Spring Security 权限在启动时加载)。
4.3 现象:PostgreSQL 启动失败,日志显示FATAL: could not create shared memory segment: Cannot allocate memory
原因:Docker 容器默认共享内存(/dev/shm)仅64MB,而 PostgreSQL 在shared_buffers设为 512MB 时需更大空间。
解决:修改dev-setup.sh中 Docker run 命令,增加--shm-size=2gb参数:
docker run -d \ --name med-postgres \ --shm-size=2gb \ # ← 新增此行 -p 5432:5432 \ -e POSTGRES_DB=medplatform \ -v $(pwd)/init-meddb.sql:/docker-entrypoint-initdb.d/init.sql \ -d postgres:144.4 现象:Vue 前端报错Failed to resolve component: ElUpload,界面空白
原因:Element Plus 组件按需引入配置错误。vite.config.ts中AutoImport插件未正确注册ElUpload。
解决:打开vite.config.ts,检查AutoImport配置项:
// 确保 plugins 数组包含此项 AutoImport({ imports: ['vue', 'vue-router', 'element-plus'], // ↓ 必须显式添加,否则 ElUpload 不会被识别 resolvers: [ElementPlusResolver({ importStyle: 'sass' })], })4.5 现象:处方PDF中中文显示为方块,诊断栏全是“□□□”
原因:iText 7 渲染PDF时未指定中文字体路径,系统默认字体不支持中文。
解决:在PrescriptionPdfGenerator.java的createPrescriptionPdf()方法中,定位字体加载代码:
// 修改前(使用默认字体) Font font = PdfFontFactory.createFont(); // 修改后(指定 NotoSansCJK SC 字体,源码已自带 fonts/NotoSansCJKsc-Regular.otf) String fontPath = "fonts/NotoSansCJKsc-Regular.otf"; Font font = PdfFontFactory.createFont(new FileInputStream(fontPath), PdfEncodings.IDENTITY_H);注意:字体文件
NotoSansCJKsc-Regular.otf已放在src/main/resources/fonts/目录下,无需额外下载。
5. 处方PDF生成进阶:从静态模板到动态水印与防伪码
PDF 不只是文档交付终点,更是医疗责任追溯的起点。源码的PdfGenerator模块预留了三个生产级扩展点,我通常会在客户验收前一周集中补全——它们不改变主流程,但能让系统真正跨过合规门槛。
5.1 动态添加医生电子签名水印
处方PDF需体现“谁在何时开具”。源码默认使用文字水印,但真实场景需叠加医生手写签名图。修改PrescriptionPdfGenerator.addWatermark()方法:
private void addWatermark(PdfDocument pdfDoc, String doctorName, LocalDateTime issueTime) { PdfPage page = pdfDoc.getFirstPage(); PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), page); // 1. 添加半透明文字水印(原逻辑) canvas.saveState(); canvas.setFillColor(ColorConstants.LIGHT_GRAY); canvas.beginText(); canvas.setFontAndSize(font, 60); canvas.setTextMatrix(AffineTransform.getRotateInstance(Math.toRadians(-30))); canvas.showText("处方-" + doctorName); canvas.endText(); canvas.restoreState(); // 2. 叠加医生签名图片(新增) String signaturePath = "signatures/" + doctorName.replace("医生", "") + ".png"; try (InputStream is = getClass().getClassLoader().getResourceAsStream(signaturePath)) { if (is != null) { ImageData imageData = ImageDataFactory.create(is); canvas.addImage(imageData, 400, 750, 120, 40, true); // x,y,width,height } } catch (Exception e) { log.warn("签名图未找到,跳过: {}", signaturePath); } }参数说明:签名图需按
signatures/张三.png命名,存于src/main/resources/。尺寸建议 120×40px,PNG 透明背景。若图不存在,自动降级为文字水印,保障流程不中断。
5.2 内置处方防伪二维码:绑定处方号与时间戳
每张处方PDF右下角生成一个 QR Code,扫码可跳转至医院官方验证页(需客户自行部署)。核心逻辑在PrescriptionPdfGenerator.generateQrCode():
private Image generateQrCode(String prescriptionId, LocalDateTime issueTime) { // 生成防伪字符串:处方号 + 时间戳(秒级) + SHA256盐值 String salt = "medplatform-2024"; // 生产环境应从配置中心读取 String raw = prescriptionId + "|" + issueTime.truncatedTo(ChronoUnit.SECONDS) + "|" + salt; String hash = DigestUtils.sha256Hex(raw); // 使用 ZXing 生成 QR Code BitMatrix matrix = new MultiFormatWriter().encode( "https://verify.hospital.com/check?rx=" + prescriptionId + "&sig=" + hash, BarcodeFormat.QR_CODE, 200, 200 ); ByteArrayOutputStream os = new ByteArrayOutputStream(); MatrixToImageWriter.writeToStream(matrix, "PNG", os); return new Image(ImageDataFactory.create(os.toByteArray())); }安全提示:
salt值必须在生产环境替换为随机密钥,并通过application-prod.yml加密配置注入,禁止硬编码。
5.3 PDF元数据注入:让Adobe Acrobat显示真实签发信息
PDF 文件属性(右键→属性→描述)默认为空。通过PdfDocument.getCatalog().getPdfObject()注入元数据:
pdfDoc.getCatalog().getPdfObject().put( PdfName.Author, new PdfString("互联网医院处方系统 v2.3.1") ); pdfDoc.getCatalog().getPdfObject().put( PdfName.Creator, new PdfString("iText 7.2.5 / Java 11") ); pdfDoc.getCatalog().getPdfObject().put( PdfName.Keywords, new PdfString("电子处方,在线问诊," + department) );验证方法:生成PDF后,用 Adobe Acrobat Pro 打开 → 文件→属性→描述,确认字段已填充。这是等保2.0三级要求的“文档来源可追溯”落地点。
从那以后我每次交付医疗类系统,都会强制走一遍这三步:签名水印路径检查、防伪码扫码验证、PDF元数据审查。不是因为客户提了需求,而是某次三方审计时,对方工程师用 Adobe 的“属性检查器”翻出我们漏掉的Author字段,当场叫停了签字流程。希望帮到你。
本文还有配套的精品资源,点击获取