资讯动态

Spring Boot+Vue中医诊所病历系统开发实战:复诊与处方设计

发布时间:2026/10/13 3:37:36 来源:尧图企业网站定制
帮朋友打理的一家中医诊所做病历系统之前我本来以为这就是个普通的后台管理项目——无非患者表、病历表、处方表写几个增删改查页面完事。真正开始梳理流程我才意识到中医诊所的病历处理逻辑跟普通诊所差得不是一点半点。这篇以我实现的“springboot中医诊所病历系统”为蓝本把从需求梳理、技术选型、数据库设计到上线踩坑的完整过程复述一遍。如果你正在规划类似的项目或者想看看中医诊疗场景怎么落地成一套可用的系统这篇应该对你有用。先交代一下背景那家诊所不大诊室加上药房一共四五个房间纸质病历堆了一柜子复诊时找旧档案基本靠记忆方剂也一直是手写。他们之前试过一套通用门诊系统用了不到两周就弃了——界面倒是齐全挂号、收费、药房、检验无所不包可真到接诊环节老中医想要的“上一诊舌苔是什么”“上次开了什么方这周怎么加减”这种逻辑一套都没有。所以最终决定从零写一套贴合中医诊所场景的病历系统技术栈定为 Spring Boot 2.7 MyBatis-Plus MySQL 8.0前端用 Vue 3 Element Plus。1. 为什么中医诊所需要一套自己的病历系统1.1 中医病历与普通门诊病历的差异做过医疗类系统的开发者都知道一套病历系统的核心不是代码而是病历的数据模型。西医病历的主线是“症状-检查-诊断-用药”路径相对固定诊断结果往往能落到标准编码上。但中医病历的主线完全不同医生关注的是望闻问切——面色、舌苔、脉象、饮食二便这些描述性信息最终落点是“证型”和“治则”再根据证型开出对应的方剂。这两者的差异直接决定了系统设计对比维度中医诊所病历普通门诊病历记录重点四诊信息、舌苔脉象、辨证分型主诉、体征、检查结果、明确诊断处方形式复方可加减每味药有剂量、用法、先煎后下多为固定药品加固定用法复诊频率每周或每月复诊需前后对比调整方剂按疗程或按需复诊历史记录价值前后舌脉、方药变化的对比极为关键检查指标变化是主要线索拿我实现时最头疼的一个点举例远近、厚薄、腻燥这些词在舌苔描述里是模糊且动态变化的你不能像西医那样用“正常/异常”两个字段来存。它需要把每次就诊时的舌苔、脉象原文原样记录下来还要在复诊时把上次的记绿带到新病历旁边方便医生对比。这就是中医病案的“时间线”特性记录本身的意义远大于单次诊断的意义。1.2 通用HIS系统落地中小诊所的尴尬市面上大厂的门诊HIS系统功能确实强大但它们大多是为大型医院设计的科室、床位、检验、医嘱、收费这些模块对一个两三个诊室的中医诊所来说属于典型的“过度设计”。部署成本高、培训成本高最关键的是录入流程跟中医的接诊习惯不匹配医生需要填大量的无关字段病历录入效率反而比手写还慢。中小诊所真正需要的是一套轻量、贴合就诊场景的系统患者来了建档记录主诉记舌苔脉象辨证选方打印病历收费完事。不需要挂号和分诊排队但需要非常快的复诊带出、方剂模板、剂量默认值这些“顺手”的功能。这也是我最后的经验做这类系统功能宁可少流程一定要顺。1.3 技术选型Spring Boot Vue 这套组合的理由选 Spring Boot 而不是别的框架原因很实际第一Java 生态在医疗相关项目里的接受度很高稳定性和安全性都有保障后续如果要对接医保、卫健平台Java 体系的资料最齐全踩坑概率最低。第二Spring Boot MyBatis-Plus 这套组合在中小型管理系统里开发效率非常高大部分 CRUD 可以直接用 ServiceImpl 和 Mapper 完成省掉了大量重复代码。第三前后端分离用 Vue 3 Element Plus组件生态成熟表格、弹窗、表单校验这些门诊系统高频组件拿来即用。如果单看性能这套技术栈一定不是最优解但考虑到开发效率、维护成本、招人难度它是最稳妥的。上线后诊所访问量很小一天几十个患者吞吐量完全不是瓶颈系统的核心难点反而全在业务逻辑和数据处理上。2. 业务流程梳理先搞清楚谁在用、怎么用2.1 诊所里的四种角色与一条完整就诊链路动手写代码之前我先花了两天泡在诊所里观察他们接诊的完整流程。这一步不建议跳过去大多数“做出来没人用”的系统问题都出在流程没理清。这个系统里实际有四种角色前台负责患者建档、预约登记、复诊提醒。医生核心用户负责接诊、录入四诊、辨证开方、打印病历。药房按方抓药核对处方录入库存消耗虽然第一版没做库存但处方查询是必需的。管理者查看每日就诊统计、收入流水、病种分布。对应的就诊链路是患者到店前台建档或确认老患者身份系统带出既往病历列表医生接诊后新建一条病历先录入主诉和现病史再记录舌苔脉象选择证型和治则接下来开处方从常用方模板里选择或者逐味加药打印病历患者去药房取药药房通过处方号调出当次处方。这个链路里最核心的交互发生在“医生接诊”到“打印病历”这一段。我后来做前端时特意把这段设计成单页三块布局左边是患者信息中间是四诊录入右边是处方明细。医生可以眼睛不离开屏幕完成一次接诊。2.2 功能模块划分档案、四诊、辨证、处方、复诊按实际业务需求我把系统划分成几个大模块而不是像通用系统那样按科室分。患者档案模块管基本信息姓名、性别、出生日期、手机号、住址、过敏史。这里特别注意保存过敏史和体质信息中医开方时问诊经常会涉及。病历模块是主体每条诊疗记录包含主诉、现病史、既往史、望诊、舌象、脉象、证型、治则、并发症及注意事项。我额外加了一个“医嘱”字段比如忌口、煎药方法这些在实际门诊里非常常用但通用系统往往没有合适的位置。处方模块包含处方主表和处方明细表。主表记录处方名、剂数、用法备注、总价明细表记录每味药、剂量、单位、煎法先煎、后下、包煎、用法。这里每一味药必须一行不允许把整张方子当一个长字符串塞进去——虽然医生输入时确实经常一大段话但数据层拆开才能做剂量校验、配伍排查和后续的用药统计。复诊管理模块是这家诊所最看重的功能。老患者再次来诊前台用手机号直接带出档案医生新建病历的时候一键带出上一次的主诉、舌苔、脉象和处方然后在此基础上修改。这一步做顺畅了系统推广基本就成功了一半。2.3 复诊场景为什么必须“一键带出历史”我观察到一个细节老中医复诊看患者时通常会翻上次的病历嘴里念叨“上次舌苔还黄腻呢这次好多了”然后拿起笔在旧方子上改两三味药。如果系统让他重新把整个方子敲一遍他一定拒绝。所以这个“一键带出历史”功能不只是方便而是系统可用的前提。具体实现逻辑后面会讲这里先强调数据层面的设计每次复诊必须生成一条新的病历记录旧数据原样保留不允许覆盖修改。新记录会自动复制上次的主诉、舌苔、脉象、证型、治则处方则带出上次明细医生在此基础上加减一切痕迹都会被记录下来。3. 数据库设计与建模病历和方剂怎么存才科学3.1 核心表关系与建表SQL数据库设计是这个项目里最值得反复推敲的部分。我最终定的核心表是五张外加字典表、用户表、操作日志表。先看患者表CREATE TABLE patient ( id BIGINT AUTO_INCREMENT PRIMARY KEY, patient_code VARCHAR(32) NOT NULL COMMENT 档案编号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT COMMENT 性别 1男 0女, birthday DATE COMMENT 出生日期, phone VARCHAR(20) COMMENT 手机号, address VARCHAR(200) COMMENT 住址, allergic_history TEXT COMMENT 过敏史, constitution VARCHAR(50) COMMENT 中医体质, deleted TINYINT DEFAULT 0 COMMENT 逻辑删除, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_phone_deleted (phone, deleted) ) COMMENT 患者档案表;病历主表CREATE TABLE medical_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, record_no VARCHAR(32) NOT NULL COMMENT 病历编号, patient_id BIGINT NOT NULL COMMENT 患者ID, visit_date DATETIME NOT NULL COMMENT 就诊时间, complaint TEXT COMMENT 主诉, present_illness TEXT COMMENT 现病史, past_history TEXT COMMENT 既往史, tongue TEXT COMMENT 舌象, pulse TEXT COMMENT 脉象, syndrome_type VARCHAR(100) COMMENT 证型, treatment_principle VARCHAR(100) COMMENT 治则, doctor_advice TEXT COMMENT 医嘱, doctor_id BIGINT COMMENT 接诊医生ID, status TINYINT DEFAULT 0 COMMENT 状态 0草稿 1完成, deleted TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_patient_visit (patient_id, visit_date) ) COMMENT 病历主表;处方主表和明细表CREATE TABLE prescription ( id BIGINT AUTO_INCREMENT PRIMARY KEY, record_id BIGINT NOT NULL COMMENT 病历ID, prescription_name VARCHAR(100) COMMENT 方名, dosage_text VARCHAR(200) COMMENT 服法, total_copies INT DEFAULT 1 COMMENT 剂数, fee_amount DECIMAL(10,2) COMMENT 费用, note TEXT COMMENT 备注, deleted TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 处方主表; CREATE TABLE prescription_item ( id BIGINT AUTO_INCREMENT PRIMARY KEY, prescription_id BIGINT NOT NULL COMMENT 处方ID, herb_name VARCHAR(50) NOT NULL COMMENT 药名, dosage DECIMAL(10,2) NOT NULL COMMENT 剂量, unit VARCHAR(10) NOT NULL COMMENT 单位, decoction_method VARCHAR(20) COMMENT 煎法, usage_text VARCHAR(50) COMMENT 用法, sort_order INT COMMENT 排序 ) COMMENT 处方明细表;表关系一句话概括患者1对多病历病历1对1主处方主处方1对多明细。没有在病历表里直接挂“辨证”“舌脉”的子表而是用字段平铺因为一次就诊的四诊信息在临床上是聚合输出不是一对多关系没必要拆表增加复杂度。3.2 舌苔、脉象等非结构化信息的存储方案这个点是开发过程中反复取舍过的。项目初期我一度把所有诊断描述塞进一个 JSON 字段想着反正查询不多、存着灵活。但做完统计功能我就发现不行管理者问“这个月黄腻苔的患者有多少”如果证型和舌象全在 JSON 里SQL 根本写不出来。最终的方案是“关键字段结构化 原文文本冗余”。舌象、脉象原文用 TEXT 保存用于打印和展示同时从原文里抽出必要的结构化维度比如舌色、舌苔厚薄、脉象类型用有限字段存下来方便统计。证型和治则则直接用字典表的主键关联完全结构化。这套方案不完美——它要求录入时多做一步选择而不是放开一个纯文本框。但考虑到中医诊所的规模和数据量这是统计需求与录入效率之间最均衡的选择。真要做到完全自由录入又能智能统计那是 NLP 的活儿了不适合一个小诊所项目。3.3 字典表中医术语标准化的隐藏价值做中医类系统字典表的设计直接决定系统好不好用。患者可能会说“我有点拉肚子”但病历里记的应该是“便溏”还是“泄泻”不同医生对同一个证型的表述也可能不一样——有人写“肝郁脾虚”有人写“肝旺脾弱”。我建了一套三层的字典结构字典分类表、字典条目表、业务关联表。证型、治则、单位、煎法、剂型、脉象、舌象这些都走字典。好处有三个第一录入变成点选不需要打字。第二统计口径统一不同医生的病历可以横向比较。第三后续做智能辅助时数据都规整到固定集合里才有进一步分析的基础。当然纯字典也会限制老中医的个性化表达所以我在所有字典选择框旁边都保留了一个“其他自定义”入口录进去的内容会落到附属字段既保留个性又不破坏主数据结构。3.4 病历版本控制为什么不允许原地修改这个问题是我跟诊所负责人反复确认过的。他说了一句很关键的话“病历是医疗文书改没改过、什么时候改的得能查。”所以业务上有严格约束完成状态的病历不允许直接修改医生只能新建一条复诊记录确实需要更正时走专门的“病历更正”操作原记录保留新记录关联更正原因。落到底层就是在一张病历主表里用 status 字段区分草稿和完成草稿可以随意改完成以后所有变更都通过新增记录来体现。关键表都加了操作日志记录是谁、何时、改动了哪个字段。这套机制虽然简单但对诊所来说已经足够——他们不需要完整的电子病历审计系统但需要一个能被信任的记录过程。4. 后端实现核心要点复诊逻辑、处方校验、打印4.1 复诊带出的Service实现复诊带出是医生端最常用的功能代码不复杂但思路要清晰基于患者 ID 查最近一次病历把主诉、现病史、舌苔、脉象、证型、治则原样带到新病历对象里处方明细则是单独查询并复制。public MedicalRecord createFollowUpRecord(Long patientId, Long doctorId) { MedicalRecord latest medicalRecordMapper.selectLatestByPatientId(patientId); if (latest null) { return null; } MedicalRecord newRecord new MedicalRecord(); newRecord.setPatientId(patientId); newRecord.setDoctorId(doctorId); newRecord.setVisitDate(new Date()); newRecord.setComplaint(latest.getComplaint()); newRecord.setPresentIllness(latest.getPresentIllness()); newRecord.setTongue(latest.getTongue()); newRecord.setPulse(latest.getPulse()); newRecord.setSyndromeType(latest.getSyndromeType()); newRecord.setTreatmentPrinciple(latest.getTreatmentPrinciple()); newRecord.setStatus(0); medicalRecordMapper.insert(newRecord); // 复制最近一次处方明细 Prescription oldPrescription prescriptionMapper.selectByRecordId(latest.getId()); if (oldPrescription ! null) { Prescription newPrescription new Prescription(); newPrescription.setRecordId(newRecord.getId()); newPrescription.setPrescriptionName(oldPrescription.getPrescriptionName()); newPrescription.setTotalCopies(oldPrescription.getTotalCopies()); prescriptionMapper.insert(newPrescription); ListPrescriptionItem items prescriptionItemMapper.selectList( new LambdaQueryWrapperPrescriptionItem() .eq(PrescriptionItem::getPrescriptionId, oldPrescription.getId())); for (PrescriptionItem item : items) { item.setId(null); item.setPrescriptionId(newPrescription.getId()); prescriptionItemMapper.insert(item); } } return newRecord; }这里有个细节复制处方是把每味药生成新的明细记录而不是直接复用旧记录的 ID。这样医生在界面上加减药、改剂量时完全不会影响上次的处方数据两者只是内容相同物理上互相独立。4.2 处方剂量与配伍禁忌校验中医处方虽然自由度高但有些底线不能破。我做了一个不算复杂的校验机制在提交处方时触发。剂量校验依赖中药字典里的参考剂量区间比如某味药常规用量是 3~10 克。录入时医生输入 50 克系统会弹一个清晰提示“参考剂量范围 3-10 克确认使用”医生可以选择无视继续但这个提示能拦下绝大多数手误。配伍禁忌我维护了一张关系表存“有争议”的药对组合。提交时遍历处方全部明细两两比对命中就给警示。这不算医学判断只是数据层面的安全网真正的辨证责任还是在医生身上。实现核心就是两层循环for (int i 0; i items.size(); i) { for (int j i 1; j items.size(); j) { String herbA items.get(i).getHerbName(); String herbB items.get(j).getHerbName(); if (compatibilityMapper.checkConflict(herbA, herbB) 0) { result.addWarning(发现配伍禁忌 herbA 与 herbB); } } }剂量校验和配伍提示对诊所来说是个意外惊喜。他们以前全靠人脑记偶尔配药时才发现某两味药不宜同用现在系统提前提示药房和医生对系统的信任度明显增加了。4.3 病历打印与PDF导出的两种思路病历打印是这个系统里被反复打磨的功能之一。一开始我尝试用后端 Java 生成 PDF引入 PDF 库做一个 HTML 模板写了一套样式结果发现调样式的时间远大于写功能的时间。后来改用更简单的方案前端打印。具体做法是页面里放一个打印按钮点击时把病历区域单独提取出来注入一段全局样式后调用 window.print()。浏览器打印时可以选择保存为 PDF也可以直接走打印机。医生看到的效果就是所见即所得不需要额外服务也不用处理字体嵌入问题。唯一要处理的是打印样式里把不需要的导航栏、按钮、操作栏全部隐藏只留病历正文。这个方案在 Windows 和 macOS 上都工作正常诊所的打印机也能正常输出。如果你要对接第三方电子病历平台后端生成 PDF 这条路早晚要走但第一版上线阶段前端打印是最快、最不折腾的方案。5. 前端交互设计让医生愿意用的关键是“少打字”5.1 常用方剂模板与整方引用前端做得再花哨医生不买账就等于零。我总结出的核心体验原则是让医生少敲键盘、少翻菜单。常用方剂模板就是这个原则的集中体现。我在系统里维护了一批基础方四君子汤、四物汤、柴胡疏肝散、六味地黄丸这类通用方以及每个医生自己积累的私房方。选一个模板整个处方的明细就自动填充到右侧明细区医生只需要针对这次就诊做加减调整。模板不只是供医生用的它实际上是诊所诊疗经验的数字化沉淀。我专门做了“模板来源”标识区分公共模板和医生个人模板。后续这些数据还可以分析每个医生的用药偏好做成统计报表管理者能一目了然。5.2 拼音码检索与剂量默认值录入处方明细时我做了两个效率优化。第一个是拼音首字母检索。中药字典表里有一个 pinyin_code 字段比如陈皮是 cp、炙甘草是 zg。医生输入 cp 回车下拉框直接定位到陈皮不需要翻网页或打全名。这个改动让老医生的接受度明显上升因为他们不需要记住某个药在界面上的哪个位置只要会拼写就一定能找到。第二个是剂量默认值。中药字典里除了参考剂量区间还存了一个门诊常用的默认剂量比如黄芪默认 15 克。加药时默认值直接填好医生觉得不对再改。大多数情况下默认值就是对的这个细节为每个处方节省了好几秒一天几十张方子省下的时间积累起来很可观。5.3 平板适配与打印样式诊室空间不大我给医生配的是平板加上一个蓝牙键盘。所以前端界面在设计时做了触控适配所有按钮都足够大表单输入框在点选时能弹出放大处方明细表格在小平板上需要横向滚动但整体可操作。打印样式是最后专门调的一环。病历纸有固定的版式我用一套单独的 CSS 控制打印输出包括字体大小、行距、诊疗栏的排列。第一次试打印时发现正文把患者隐私信息全打了后来加了权限控制只在打印时显示二代身份信息通讯地址默认省略除非医生勾选包含。这些都是很细的点但决定了系统能不能真正替代手写病历。6. 上线前后踩过的坑和排查过程6.1 病历日期早八小时的时区问题上线第二天诊所反馈患者当天的病历时间显示成了前一天晚上。排查链路是这样的先看前端传参确认页面上显示的日期正确再看后端接口接收到的日期值发现接口层就进了八小时的偏差继续查 Jackson 对日期类型的序列化配置以及 MySQL 连接参数。最终问题出在 JDBC 连接串上。数据库服务器时间没问题但连接参数里没显式指定时区默认走了 UTC而本机是东八区所以所有时间都偏移了八小时。解决方式很简单在连接串里加serverTimezoneAsia/ShanghaiuseLegacyDatetimeCodefalse这个坑很经典做 Java 项目的开发者基本都遇到过。想少踩坑就在项目初始化阶段把所有日期字段统一为标准时间格式传输数据库连接串显式指定时区前端提交一律带时区信息。6.2 逻辑删除让唯一索引失效患者表设计时我对手机号加了唯一索引防止重复建档。但当时用的是“逻辑删除”方案删除记录时把 deleted 字段置为 1数据还留在表里。问题来了如果删除了一条手机号为 138xxxx 的患者记录再次新增同号患者时唯一索引仍然会命中那条已删除记录导致插入失败。排查过程是先看报错信息发现是唯一索引冲突把 SQL 拿出来在命令行单独执行确认数据存在但逻辑删除然后查 MyBatis-Plus 的配置发现逻辑删除只影响查询语句数据库层面的唯一索引完全不受影响。解决方案是构造联合唯一索引把 deleted 字段一起加进去ALTER TABLE patient ADD UNIQUE KEY uk_phone_deleted (phone, deleted);这样第一次删除后 deleted1再次插入同号码时新记录 deleted0不会冲突。如果同号删除两次deleted 一直递增即可。注意唯一索引里不能使用 NULL否则唯一性会失效所以 deleted 字段要默认 0不要用 NULL 表示未删除。6.3 双击提交导致重复建档开发测试阶段没太在意让诊所实际用了两天就出问题了前台录入一个新患者时因为点了两下“保存”按钮患者表里出现了两条几乎一样的记录。这类问题的本质是前端没有防重复提交后端也没有做幂等处理。我的修复分两步前端在保存按钮点击后立即置灰并加 loading 状态后端在患者插入前做一次存在性校验手机号加 base 字段唯一索引兜底。双保险之后这个问题再没出现过。如果你的系统后续要面向公网环境建议再做一层基于请求唯一 Key 的幂等处理前端每次打开表单生成一个 traceId提交时携带后端用 Redis 记录 traceId重复提交直接拒掉。小诊所单机部署可以先用前两种方案成本最低。6.4 医生抵触系统的过渡方案技术问题都好解决最难的是让医生真的用起来。上线第一周某位老医生基本不碰电脑还是习惯拿起处方笺直接手写写完让护士拍照归档。我一度以为项目要失败了。后来换了个思路不要求他一步到位全流程电子化而是让护士在医生口述的同时在系统里同步录入医生只负责看一眼打印出来的病历确认无误后签字。这样医生的工作量没有增加系统数据却完整保留了下来。磨合了两周后这位医生发现复诊带出确实方便开始自己在触屏上翻看上次的舌脉记录到最后基本实现了独立录入。这个顿悟很关键工具再好也要尊重使用者的习惯和节奏。推广期宁可增加一部分人的工作量先让数据完整跑起来再慢慢让所有人尝到甜头。7. 给打算做类似项目的开发者几条实在建议如果你也准备做中医诊所或者其他垂直行业的病历系统下面这几点是我这次做完之后切切实实的体会。第一开工之前先去现场蹲几天流程把用户真实的工作场景拍下来、写下来。你会发现很多你以为的设计点根本不存在而真正让他们崩溃的痛点往往写在项目的功能清单之外。第二技术选型保守一点。这类系统通常没有太高的并发要求用成熟稳定的技术栈比追逐新框架重要。Spring Boot MyBatis-Plus MySQL 这套组合够用而且好招人遇到问题网上资料也多。第三数据库设计要兼顾“录入效率”和“统计能力”。看病历和方剂怎么存不要一开始就想着把所有内容塞进 JSON后期统计会把你折腾死。结构化字段 原文冗余是性价比最高的方案。第四复诊带出和处方模板这两个功能一定要做到极致。它们是诊所用户使用频率最高的功能也是系统能否推广开来的分水岭。第五权限和审计不要做太重。小诊所就十几个人一套简单的用户角色加操作日志就足够了。做成复杂的 RBAC 模型反而让维护成本变高管理者也未必看得懂。这套系统上线运行到现在诊所的纸质档案柜已经不再增加新册子复诊时找记录的时间从几分钟缩短到几十秒药房抓药也少了看错手写方的情况。对我来说最有成就感的不是代码跑得多稳而是那位一开始抵触系统的老医生某天下班时说了一句“这个确实比翻本子方便”。对做业务系统的人来说这句话大概就是最值钱的回报了。

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

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

免费获取报价 →
↑