帮朋友打理的一家中医诊所做病历系统之前,我本来以为这就是个普通的后台管理项目——无非患者表、病历表、处方表,写几个增删改查页面,完事。真正开始梳理流程我才意识到,中医诊所的病历处理逻辑跟普通诊所差得不是一点半点。这篇以我实现的“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); List<PrescriptionItem> items = prescriptionItemMapper.selectList( new LambdaQueryWrapper<PrescriptionItem>() .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,而本机是东八区,所以所有时间都偏移了八小时。解决方式很简单,在连接串里加:
serverTimezone=Asia/Shanghai&useLegacyDatetimeCode=false这个坑很经典,做 Java 项目的开发者基本都遇到过。想少踩坑,就在项目初始化阶段把所有日期字段统一为标准时间格式传输,数据库连接串显式指定时区,前端提交一律带时区信息。
6.2 逻辑删除让唯一索引失效
患者表设计时我对手机号加了唯一索引,防止重复建档。但当时用的是“逻辑删除”方案,删除记录时把 deleted 字段置为 1,数据还留在表里。问题来了:如果删除了一条手机号为 138xxxx 的患者记录,再次新增同号患者时,唯一索引仍然会命中那条已删除记录,导致插入失败。
排查过程是:先看报错信息,发现是唯一索引冲突;把 SQL 拿出来在命令行单独执行,确认数据存在但逻辑删除;然后查 MyBatis-Plus 的配置,发现逻辑删除只影响查询语句,数据库层面的唯一索引完全不受影响。
解决方案是构造联合唯一索引,把 deleted 字段一起加进去:
ALTER TABLE patient ADD UNIQUE KEY uk_phone_deleted (phone, deleted);这样第一次删除后 deleted=1,再次插入同号码时新记录 deleted=0,不会冲突。如果同号删除两次,deleted 一直递增即可。注意唯一索引里不能使用 NULL,否则唯一性会失效,所以 deleted 字段要默认 0,不要用 NULL 表示未删除。
6.3 双击提交导致重复建档
开发测试阶段没太在意,让诊所实际用了两天就出问题了:前台录入一个新患者时,因为点了两下“保存”按钮,患者表里出现了两条几乎一样的记录。
这类问题的本质是前端没有防重复提交,后端也没有做幂等处理。我的修复分两步:前端在保存按钮点击后立即置灰,并加 loading 状态;后端在患者插入前做一次存在性校验,手机号加 base 字段唯一索引兜底。双保险之后,这个问题再没出现过。
如果你的系统后续要面向公网环境,建议再做一层基于请求唯一 Key 的幂等处理,前端每次打开表单生成一个 traceId,提交时携带,后端用 Redis 记录 traceId,重复提交直接拒掉。小诊所单机部署可以先用前两种方案,成本最低。
6.4 医生抵触系统的过渡方案
技术问题都好解决,最难的是让医生真的用起来。上线第一周,某位老医生基本不碰电脑,还是习惯拿起处方笺直接手写,写完让护士拍照归档。我一度以为项目要失败了。
后来换了个思路:不要求他一步到位全流程电子化,而是让护士在医生口述的同时在系统里同步录入,医生只负责看一眼打印出来的病历,确认无误后签字。这样医生的工作量没有增加,系统数据却完整保留了下来。磨合了两周后,这位医生发现复诊带出确实方便,开始自己在触屏上翻看上次的舌脉记录,到最后基本实现了独立录入。
这个顿悟很关键:工具再好,也要尊重使用者的习惯和节奏。推广期宁可增加一部分人的工作量,先让数据完整跑起来,再慢慢让所有人尝到甜头。
7. 给打算做类似项目的开发者几条实在建议
如果你也准备做中医诊所或者其他垂直行业的病历系统,下面这几点是我这次做完之后切切实实的体会。
第一,开工之前先去现场蹲几天流程,把用户真实的工作场景拍下来、写下来。你会发现很多你以为的设计点根本不存在,而真正让他们崩溃的痛点,往往写在项目的功能清单之外。
第二,技术选型保守一点。这类系统通常没有太高的并发要求,用成熟稳定的技术栈比追逐新框架重要。Spring Boot + MyBatis-Plus + MySQL 这套组合,够用而且好招人,遇到问题网上资料也多。
第三,数据库设计要兼顾“录入效率”和“统计能力”。看病历和方剂怎么存,不要一开始就想着把所有内容塞进 JSON,后期统计会把你折腾死。结构化字段 + 原文冗余是性价比最高的方案。
第四,复诊带出和处方模板这两个功能一定要做到极致。它们是诊所用户使用频率最高的功能,也是系统能否推广开来的分水岭。
第五,权限和审计不要做太重。小诊所就十几个人,一套简单的用户角色加操作日志就足够了。做成复杂的 RBAC 模型反而让维护成本变高,管理者也未必看得懂。
这套系统上线运行到现在,诊所的纸质档案柜已经不再增加新册子,复诊时找记录的时间从几分钟缩短到几十秒,药房抓药也少了看错手写方的情况。对我来说,最有成就感的不是代码跑得多稳,而是那位一开始抵触系统的老医生,某天下班时说了一句“这个确实比翻本子方便”。对做业务系统的人来说,这句话大概就是最值钱的回报了。