
1. 项目概述1.1 为什么社区诊所需要一套电子病历系统做这个系统之前我在几家社区卫生服务中心和私人诊所转过一圈发现大部分中小型诊所的病历管理还停留在纸质病历本 Excel 登记表的阶段。患者每次来复诊医生要翻箱倒柜找之前的病历本药房盘点靠手抄月底统计慢病随访数据护士要对着纸质档案熬夜加班。这哪是信息化的时代简直是手工作坊。所以当我接到Pythondjango的社区诊所居民电子病历管理系统这个项目需求时第一反应不是急着写代码而是先想清楚一个问题社区诊所和三级医院的病历系统到底差在哪三甲医院有专门的电子病历厂商投入几十上百万有独立的机房和IT团队。但社区诊所预算有限、人手有限、IT基础薄弱需要的是一套轻量、易维护、能跑在一台普通服务器上甚至一台高性能PC上的系统。这也决定了技术选型必须走开源路线——Python Django 就是非常合适的一对。1.2 这套系统能做什么简单说这套系统覆盖了社区诊所日常诊疗的核心业务链路居民建档 - 门诊挂号 - 医生接诊 - 电子病历录入 - 开具处方 - 药品扣减 - 收费结算 - 复诊随访。围绕这条链路我把系统拆成了几个核心模块居民健康档案管理、门诊电子病历、处方与收费管理、药品库存管理、统计报表。基本把诊所除了财务总账之外的信息流都管起来了。适合谁来参考这套系统如果你的诊所或基层医疗机构也想做信息化改造或者你是Django学习者想找一个贴近真实业务场景的完整项目来练手这篇文章都值得看完。我会把从数据库设计、核心业务逻辑到部署上线的整个思路和踩坑过程都拆开讲清楚。2. 整体设计与技术选型思路2.1 技术栈选型背后的考量先说选型。社区诊所的电子病历系统我最终选了 Python 3.10 Django 4.2 MySQL 8.0 Bootstrap 5 这套组合跑在 Linux 服务器上用 Nginx Gunicorn 部署。这套组合在社区医疗这种场景下很稳原因有三。第一Django 自带的 Admin 后台太适合这种业务了。病历系统里有很多基础数据维护工作——药品目录、ICD-10诊断编码、医生排班表这些数据的特点是量大、字段固定、操作简单。直接用 Django Admin 就能快速搭出管理界面省掉一大半 CRUD 页面的开发时间。我实际开发中基础数据的维护页面几乎没有额外写代码全靠 Admin 的 list_display、search_fields、list_filter 这几个属性配置。第二ORM 带来的开发效率提升明显。社区诊所的业务表将近二十张表之间关系复杂——患者和病历是一对多病历和处方是一对多处方和药品是多对多。如果写原生 SQL 或者用 MyBatis 那种半自动框架光维护关联查询就能累死人。Django 的 ORM 配合 select_related 和 prefetch_related几行代码就能把复杂关联查询搞定。第三社区诊所没有专职运维。Django 的迁移机制makemigrations/migrate能把数据库表结构的变更管起来部署时一条命令同步表结构。这点在后续维护中非常省心不用每次改完模型还手动去数据库执行 SQL。2.2 系统模块怎么拆整个系统我按业务域拆成了 6 个 Django Appaccounts用户认证和权限管理负责医生、护士、药房人员、管理员的登录和角色控制residents居民健康档案管理患者的基础信息、既往史、过敏史outpatient门诊核心模块包含挂号、电子病历、诊断记录prescription处方与收费包含处方明细、收费结算pharmacy药品库存管理包含药品目录、入库、出库、库存预警reports统计报表包含门诊量、病种分布、药品消耗等统计这六个 App 彼此依赖关系清晰residents和accounts是基础层outpatient依赖这两个prescription依赖outpatientpharmacy独立但被prescription调用reports是所有模块的数据汇总层。这样拆的好处是每个 App 内部高内聚App 之间低耦合后续如果要增加住院管理或者检验检查模块不会影响现有代码。2.3 为什么不用Java或PHP我也考虑过其他方案。Java 系虽然企业级框架成熟但社区诊所这个体量用 Spring Boot 有点杀鸡用牛刀——项目初始化成本高、依赖管理复杂、部署环境要求高一个社区诊所很难配一个懂 Java 的运维。PHP 系开发快但是后期的数据模型维护比较痛苦特别是病历这种强结构化的数据用 PHP 的弱类型处理容易出问题。Python 在这两者之间取了个平衡开发周期短从零到跑通核心流程我用了三周数据模型清晰部署也不算复杂。对于十来个医生护士内部使用、日门诊量在一两百人次的诊所来说完全够用。3. 数据库设计与核心模型3.1 实体关系梳理数据库设计是整个系统的地基这块如果设计不合理后面写业务代码时会不断返工。我梳理了社区诊所的核心业务后确定了几个关键实体和关系居民居民健康档案这是系统的根所有业务都围绕居民展开就诊记录居民每次来诊所的诊疗过程病历就诊记录的详细内容包括主诉、现病史、既往史、体格检查、诊断、处理意见处方医生开的药方一条处方对应多次就诊一条处方包含多个药品收费记录处方的结算记录药品诊所的药品库存核心关系是居民 1 - N 就诊记录 1 - 1 病历 1 - N 处方 1 - N 收费。3.2 核心表结构设计在 Django 里我这样定义核心模型from django.db import models from django.contrib.auth.models import User class Resident(models.Model): 居民健康档案 id_card models.CharField(身份证号, max_length18, uniqueTrue) name models.CharField(姓名, max_length50) gender models.CharField(性别, max_length2, choices((M, 男), (F, 女))) birth_date models.DateField(出生日期, nullTrue, blankTrue) phone models.CharField(联系电话, max_length20, blankTrue) address models.CharField(家庭住址, max_length200, blankTrue) allergy_history models.TextField(过敏史, blankTrue) past_history models.TextField(既往史, blankTrue) created_at models.DateTimeField(建档时间, auto_now_addTrue) class Meta: db_table resident verbose_name 居民健康档案 indexes [ models.Index(fields[name]), models.Index(fields[id_card]), ] def __str__(self): return f{self.name}({self.id_card}) class Visit(models.Model): 就诊记录 resident models.ForeignKey(Resident, on_deletemodels.CASCADE, verbose_name居民) visit_time models.DateTimeField(就诊时间, auto_now_addTrue) doctor models.ForeignKey(User, on_deletemodels.PROTECT, verbose_name接诊医生) visit_type models.CharField(就诊类型, max_length20, choices((general, 普通门诊), (follow, 复诊), (emergency, 急诊))) class Meta: db_table visit verbose_name 就诊记录 class MedicalRecord(models.Model): 电子病历 visit models.OneToOneField(Visit, on_deletemodels.CASCADE, verbose_name就诊) chief_complaint models.TextField(主诉) present_illness models.TextField(现病史, blankTrue) past_history models.TextField(既往史, blankTrue) physical_exam models.TextField(体格检查, blankTrue) diagnosis models.TextField(诊断, blankTrue) treatment_plan models.TextField(处理意见, blankTrue) class Meta: db_table medical_record verbose_name 电子病历 class Drug(models.Model): 药品目录 drug_code models.CharField(药品编码, max_length30, uniqueTrue) name models.CharField(药品名称, max_length100) spec models.CharField(规格, max_length100) unit models.CharField(单位, max_length10, default盒) unit_price models.DecimalField(单价, max_digits10, decimal_places2) stock_count models.IntegerField(库存数量, default0) stock_warning models.IntegerField(库存预警值, default20) class Meta: db_table drug verbose_name 药品 class Prescription(models.Model): 处方 visit models.ForeignKey(Visit, on_deletemodels.CASCADE, verbose_name就诊) drugs models.ManyToManyField(Drug, throughPrescriptionItem, verbose_name药品) total_amount models.DecimalField(总金额, max_digits10, decimal_places2, default0) is_paid models.BooleanField(是否已结算, defaultFalse) class Meta: db_table prescription verbose_name 处方 class PrescriptionItem(models.Model): 处方明细 prescription models.ForeignKey(Prescription, on_deletemodels.CASCADE, verbose_name处方) drug models.ForeignKey(Drug, on_deletemodels.PROTECT, verbose_name药品) quantity models.IntegerField(数量, default1) amount models.DecimalField(金额, max_digits10, decimal_places2) class Meta: db_table prescription_item verbose_name 处方明细3.3 数据库设计的几个细节有几个设计细节是我实际踩过坑才调整出来的先说两个。第一金额字段一律用DecimalField而不是FloatField。药品单价、处方金额这种涉及钱的数据用 Float 会因为二进制浮点表示误差出现 0.1 0.2 ! 0.3 的尴尬情况哪怕只是差几分钱在收费环节也会引发纠纷。DecimalField 配合decimal_places2能精确到分这是做医疗系统的基本素养。第二用户模型直接复用 Django 自带的User不要自己造轮子建用户表。Django 的 User 已经实现了密码哈希、session 认证、权限标记够用且安全。我通过user.profile单向关联的方式给医生扩展了工号和科室字段这样既保留了 Django 认证体系的完整性又能存业务需要的附加字段。第三外键删除策略要谨慎。看上面代码里Visit 对 Resident 是CASCADE——居民档案没了就诊记录跟着删。这是因为社区诊所的场景是建了档案但可能长期不来如果保留孤儿数据反而没有意义。但 Prescription 对 Doctor 我用的是PROTECT医生如果已经开过处方就不能直接删除用户必须先把处方的关联处理掉。这是为了防止医疗数据的追溯链断裂。4. 核心业务功能实现4.1 居民健康档案——系统的基础数据源居民电子档案是社区诊所的最大特色和三甲医院来一诊看一诊不同社区诊所服务的是周边常驻居民需要建立连续的健康档案。所以我把Resident作为整个系统的根实体。在视图中建档操作我加了身份证号的唯一性校验。居民来建档时先通过身份证号检索如果已存在就转入复诊流程否则新建档案。这块的细节在residents/views.py中def resident_create(request): if request.method POST: form ResidentForm(request.POST) if form.is_valid(): id_card form.cleaned_data[id_card] if Resident.objects.filter(id_cardid_card).exists(): messages.warning(request, f身份证号 {id_card} 已存在档案请直接选择复诊) return redirect(outpatient:visit_create, resident_idold_resident.id) form.save() messages.success(request, 居民健康档案创建成功) return redirect(residents:detail, pkform.instance.pk) else: form ResidentForm() return render(request, residents/resident_form.html, {form: form})这里前端再配合一个联动输入身份证号后按回车如果id_number在数据库中存在自动带出姓名、出生日期、性别等基本信息。这个功能不复杂但能省掉医生重复录入的时间实测每天能省下半小时左右的录入量。另一件值得做的事是首页面我把近期就诊居民、今日挂号人数、库存预警药品数量几个关键指标用卡片的形式放到了工作台首页医生打开系统第一眼就能看到今天的工作节奏而不是被一堆菜单淹没。借助 django-taggit 或直接写一个简单的模板 context processor就能方便地把这些统计信息注入到所有模板页面避免每个视图重复查询。4.2 门诊接诊与电子病历录入门诊接诊是使用频率最高、也是交互要求最严格的页面。医生点的路径是选择或搜索居民 - 创建就诊记录 - 进入病历编辑页 - 填写主诉、现病史、体格检查 - 录入诊断 - 开具处方 - 提交保存。这里的核心难点是病历编辑页的数据提交关系。一次新增就诊会同时写入三张表Visit、MedicalRecord以及通过处方中间表关联的PrescriptionItem。如果分三步提交中途医生关闭浏览器会导致数据不一致——有 Visit 没 MedicalRecord或者有处方没收费。我用 Django 的Transaction.atomic()把这几个操作包裹在同一个数据库事务里要么全部成功要么全部回滚。from django.db import transaction transaction.atomic def visit_create(request): if request.method POST: form VisitForm(request.POST) if form.is_valid(): visit form.save(commitFalse) visit.doctor request.user visit.save() record_form MedicalRecordForm(request.POST) if record_form.is_valid(): record record_form.save(commitFalse) record.visit visit record.save() # 处理处方明细 drug_ids request.POST.getlist(drug_id) quantities request.POST.getlist(quantity) total_amount Decimal(0.00) for drug_id, quantity in zip(drug_ids, quantities): drug Drug.objects.select_for_update().get(iddrug_id) if drug.stock_count int(quantity): raise ValueError(f药品 {drug.name} 库存不足) item PrescriptionItem.objects.create( prescriptionprescription, drugdrug, quantityint(quantity), amountdrug.unit_price * int(quantity) ) drug.stock_count - int(quantity) drug.save() total_amount item.amount prescription.total_amount total_amount prescription.save()这里有个细节扣减药品库存时我用select_for_update()做了行锁。社区诊所虽然并发量不大但药剂师同时发药和医生同时开方是可能发生的行锁能防住超卖的情况。在数据库事务的隔离级别下这种乐观中的悲观锁反而最省心不依赖应用层代码的严谨性。4.3 药品库存管理与预警药品库存这块我设计了一套简单但实用的机制。药品表里维护了一个stock_count当前库存数和一个stock_warning预警阈值。每次处方提交时扣减库存每次药品入库时增加库存。当stock_count stock_warning时系统在药品列表页和首页同时显示预警标识提醒药房人员补货。药品入库的页面比较常规就是一个表单录入药品编码、名称、规格、单价和入库数量。但这里我特意加了一个近效期先出的提醒功能——药品入库时要求填写生产日期和有效期处方自动扣减库存时优先扣减最早到期的批次也就是医药行业常说的 FEFOFirst Expiry, First Out。在社区诊所如果只按总库存扣减很容易出现存储器里堆着临期药却还在开新批次药的情况。实现上我给药品加了ExpiryBatch模型每次入库生成一个批次记录扣减时def deduct_stock(drug, quantity): 按效期优先扣减库存 batches ExpiryBatch.objects.filter( drugdrug, remaining_quantity__gt0 ).order_by(expiry_date) remaining quantity for batch in batches: if remaining 0: break deduct min(batch.remaining_quantity, remaining) batch.remaining_quantity - deduct batch.save() remaining - deduct这个逻辑看起来简单但它避开了一个很大的管理麻烦诊所不需要人工去判断先开哪批药系统直接按效期排列。药房人员只需要定期按系统导出的近效期清单去调整货架即可。4.4 统计报表与慢病管理社区诊所的特殊之处在于承担了公共卫生职能——慢病随访、老年人健康管理、预防接种这些都要上报。如果靠人工从纸质档案里去统计工作量巨大。我在reports这个 App 里实现了几个常用报表门诊量日报/周报/月报按医生、科室维度分组统计疾病谱统计按诊断关键词聚合看在某个时间段内哪些病种占比最高方便诊所做药品和耗材的采购计划慢病患者清单筛选出诊断包含高血压、糖尿病等关键词的患者列表按随访日期排序慢病患者的筛查逻辑说起来很简单就是诊断字段的模糊匹配chronic_keywords [高血压, 糖尿病, 冠心病, 脑卒中] q_objects models.Q() for kw in chronic_keywords: q_objects | models.Q(medicalrecord__diagnosis__icontainskw) patients Resident.objects.filter( q_objects ).distinct().order_by(id_card)但真正难处理的是去重和随访周期计算。同一个患者可能多次就诊、多次被写进慢病清单我的策略是取最近一次诊断作为当前状态同时计算距上次随访的时间间隔——超过三个月没来随访的标记为待随访这样护士就能按优先级打电话邀约。这一个小功能直接提升了慢病管理工作的效率和准确度。5. 实操过程与部署上线5.1 本地开发环境快速搭建如果你照着这个项目自己动手先把本地开发环境搭好。我用的是虚拟环境方式Django 版本固定为 4.2 LTS。LTSLong Term Support版本意味着官方会持续修复安全漏洞至少三年对于医疗这种对系统稳定性要求高的场景选 LTS 是最稳妥的。python -m venv clinic_env source clinic_env/bin/activate # Windows 上执行 clinic_env\Scripts\activate pip install django4.2 mysqlclient python-dotenv django-admin startproject clinic_system .项目初始化后我习惯先建好目录结构python manage.py startapp accounts python manage.py startapp residents python manage.py startapp outpatient python manage.py startapp prescription python manage.py startapp pharmacy python manage.py startapp reports然后进settings.py把 App 注册进去配置数据库连接。我强烈建议配置信息不要把数据库密码直接写死在代码里而是通过环境变量读取配合python-dotenv使用。虽然这只是个内部系统但安全习惯要建立起来。数据库我用 MySQL 8.0。Django 连 MySQL 必须装mysqlclient它依赖系统里的 MySQL 开发库。在 Ubuntu 上要先装libmysqlclient-dev在 macOS 上要用brew install mysql-client后设置环境变量不然pip install mysqlclient会直接编译报错——这个坑无数新手踩过。# settings.py 核心配置节选 DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: os.environ.get(DB_NAME, clinic), USER: os.environ.get(DB_USER, clinic_user), PASSWORD: os.environ.get(DB_PASSWORD, ), HOST: os.environ.get(DB_HOST, 127.0.0.1), PORT: os.environ.get(DB_PORT, 3306), OPTIONS: {charset: utf8mb4}, } }注意这里一定要配置charset: utf8mb4如果缺了这一项默认使用的是 MySQL 的 latin1 字符集会导致中文乱码。这是社区诊所系统开发中最容易遇到的低级但又麻烦的问题之一。5.2 核心配置时区、语言与静态文件中国区使用需要做两件事。第一在settings.py里修改时区LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai USE_I18N True USE_TZ True这里有个细节注意即使设置了TIME_ZONE Asia/Shanghai在USE_TZ True的情况下Django 存储到数据库的DateTimeField仍然是 UTC 时间渲染到页面上会自动转成本地时间。如果应用里用了datetime.now()去获取当前时间把它存到DateTimeField里到了数据库层实际保存的会是 8 小时的错乱时间。所以我统一用 Django 的timezone.now()替代 Python 原生的datetime.now()。第二处理静态文件。CSS 和 JavaScript 文件我在本地用 Bootstrap 5 的 CDN 是最快的但考虑到社区诊所的内网环境经常访问不了外网 CDN我还是把 Bootstrap 下载到本地static/目录用 Django 的{% static %}模板标签引用。这样断网环境下系统也能正常显示初始化体验完全不同。5.3 生产环境部署流程社区诊所的部署环境一般有两种一种是有公网 IP 的云服务器另一种是内网的 PC 机当作服务器。但不管是哪种部署流程基本一致步骤操作说明1配置 Python 环境和系统依赖安装 Python 3.10、MySQL、Nginx、Gunicorn2同步数据库python manage.py migrate3创建管理员账号python manage.py createsuperuser4收集静态文件python manage.py collectstatic5启动 Gunicorngunicorn clinic_system.wsgi:application -b 0.0.0.0:80006配置 Nginx 反向代理将 Nginx 的 80 端口转发到本机的 8000 端口Gunicorn 的启动我建议用一个 systemd 服务脚本这样一来诊所重启电脑或者服务异常退出时系统能自动恢复。如果你觉得配置 systemd 太麻烦至少也用nohup的方式跑不然 SSH 一断开 Django 进程就掉了。# /etc/systemd/system/clinic.service [Unit] DescriptionClinic Django Application Afternetwork.target [Service] Userclinic Groupclinic WorkingDirectory/srv/clinic_system ExecStart/srv/clinic_env/bin/gunicorn clinic_system.wsgi:application -b 127.0.0.1:8000 Restartalways EnvironmentFile/srv/clinic_system/.env [Install] WantedBymulti-user.targetNginx 配置的核心是反向代理和静态文件托管。上面collectstatic会把所有静态文件集中到staticfiles/目录Nginx 直接托管这个目录不需要经过 Django 处理性能和稳定性都好很多。server { listen 80; server_name clinic.example.com; location /static/ { alias /srv/clinic_system/staticfiles/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }5.4 备份策略医疗数据是无价的诊所可以接受系统宕机半天但绝对不能接受病历数据丢失。我给部署现场定的备份方案是每天凌晨自动备份数据库保留最近 30 天的备份文件。用系统自带的 cron 配合 mysqldump 就能实现# 每天凌晨 2 点备份 0 2 * * * mysqldump -u clinic_user -p密码 clinic /srv/backups/clinic_$(date \%Y\%m\%d).sql # 删除 30 天前的旧备份 0 3 * * * find /srv/backups -name clinic_*.sql -mtime 30 -delete实测这套方案在诊所的单点故障场景下非常有效。有一次他们的服务器硬盘故障换了新硬盘后我把最新的备份文件拉回来用一条mysql clinic clinic_20240915.sql命令恢复了全部数据损失为零。如果没有备份后果不堪设想。6. 常见问题与排查技巧实录6.1 中文显示乱码这个问题的原因和解决方案在上面已经提到了主要是数据库连接字符集配置不正确。但我再补充一个容易遗漏的点MySQL 建库时也要把默认字符集设为 utf8mb4。如果你是在系统已经建好之后才加上OPTIONS: charset那已经建好的表还是 latin1 编码后来存进去的中文还是会乱。正确的做法是CREATE DATABASE clinic CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;如果你已经在乱码边缘挣扎了可以先把数据导出来重新建库再导入。Django 的dumpdata命令导出的是 JSON 格式天然带 Unicode 转义不会因为字符集问题损坏数据。6.2 时区导致的时间错位有一个很经典的坑医生录入病历的时间显示正常但统计报表按天分组时某天的记录数量总是比前台页面上看起来少几条。这是因为USE_TZ True的情况下数据按 UTC 时区存储而按天分组查询用了本地时区的日期边界。解决办法是在进行日期分组查询时显式转换时区from django.db.models.functions import TruncDate from django.db.models import Count from django.utils import timezone daily_visits Visit.objects.annotate( dayTruncDate(visit_time, tzinfotimezone.get_current_timezone()) ).values(day).annotate(countCount(id)).order_by(day)这个tzinfo参数是我排查了好久才发现的关键。不加它TruncDate 按照 UTC 的零点分组和北京时间的零点差了 8 小时自然会导致统计错位。这也是新手做这类系统最容易忽视的地方。6.3 表单提交中文字段丢失有一次用户反馈在病历页面输入很长一段既往史文本点保存后页面刷新那段字就没影了。检查发现是 HTML 表单里没有指定accept-charsetUTF-8。虽然绝大多数现代浏览器默认使用 UTF-8 编码提交表单但一些企业内网定制版浏览器会出现编码不一致的问题。在模板的form标签上显式加上form methodpost accept-charsetUTF-8另外还要检查 MySQL 连接串的字符集以及 Django 模板渲染的响应头里是否声明了Content-Type: text/html; charsetutf-8。Django 默认就是这个一般不会出问题但如果你手动设置了HttpResponse容易漏掉这个 Content-Type。6.4 慢查询拖垮页面系统上线三个月后医生反馈打开患者列表越来越慢。我查了下 MySQL 慢查询日志发现慢的语句都是SELECT * FROM visit WHERE resident_id xxx ORDER BY visit_time DESC这类查询。原因很典型关联查询没走索引。解决方法是把常用查询条件都建上索引。上面模型定义里我给Resident的id_card建了唯一索引给Visit的resident_id加了db_indexTrue然后给MedicalRecord的visit_id这个外键加了默认的索引。查询性能从平均 800ms 降到了 50ms 以内。给读者一个建议每次新增一个查询条件或者调优后打开 Django 的django-debug-toolbar看它直接输出的 SQL 语句是否用了索引。这个调试工具能把 Django ORM 生成的 SQL 直接在页面底部显示出来升级排查效率非常明显。7. 权限设计与安全加固7.1 基于角色的访问控制社区诊所的角色很简单医生、护士、药师、管理员。我用 Django 自带的User.groups来做角色分配然后用login_required和permission_required装饰器控制视图访问。比如医生可以创建病历护士只能查看和管理居民档案药师只能操作药品入库和发药不能看诊断信息。在 Django 的默认权限之外我给核心操作加了额外的判断。比如创建病历的视图需要medical.add_medicalrecord权限没有权限的用户直接访问 URL 会被重定向到登录页并提示权限不足。这个在社区诊所虽然操作不频繁但严谨做权限控制是医疗系统的合规底线。另外我重写了LoginView加上了验证码。诊所里大家共用电脑的现象很普遍没有验证码容易被反复尝试弱口令。Django 的django-simple-captcha三行代码接进来效果立竿见影。7.2 数据脱敏与操作留痕医疗数据有个特性即使内部使用也要防止敏感信息过度暴露。在系统里我做了两个处理。第一个是居民列表默认隐藏部分身份证号只在详情页完整显示。这样当护士站在药房电脑前帮患者查药时荧幕上不会直接弹出一排完整的身份证号。第二个是变化留痕。用 Django 的django-auditlog这个第三方库把Resident、MedicalRecord、Prescription这几个核心模型的所有新增、修改、删除操作都记录下来包括操作人、操作时间和变化前后的值。一旦出现医疗纠纷或者用药差错可以直接查审计日志定位到具体操作者。社区诊所虽然规模不大但这层保护对医生和患者都有意义。7.3 安全配置项Django 的生产部署安全配置有几个必须打开的项目我在settings.py里这样配置DEBUG False ALLOWED_HOSTS [clinic.example.com, 192.168.1.10] # 诊所实际域名或IP SECURE_CONTENT_TYPE_NOSNIFF True SECURE_REFERRER_POLICY same-origin CSRF_COOKIE_SECURE True SESSION_COOKIE_SECURE TrueCSRF_COOKIE_SECURE 和 SESSION_COOKIE_SECURE 要求站点必须走 HTTPS 才能传递 cookie。如果诊所没有域名和 HTTPS 证书内网直接 HTTP 访问时这两个选项改成 False否则登录后 cookie 存不下来页面会一直跳回登录页这也是我实际部署时踩过的一个坑——内网没有 HTTPS 却强行配置了 Secure cookie结果所有人登录不进去。8. 效果评估与实际使用心得8.1 系统运行带来的改变系统在合作诊所稳定运行了大半年后几个核心数据的变化非常明显。病历录入方面纸质时代医生每天平均要写 20 多份带手写文字的病历经常写到手酸。现在用系统模板录入一份简单的上呼吸道感染病历只要两分钟就能完成而且不再有字迹难看再看不清的问题。诊断和处方自动归档后续复诊时点开上次记录就能看到完整历史。库存周转方面药房管理人员每月的盘点时间从一天缩短到两小时。因为处方提交时库存自动扣减实时盘点和系统数据基本一致月底只需确认差异即可。慢病管理方面护士原本每个月要花两三天翻纸质档案筛查待随访患者现在系统自动合成待随访清单按列表拨电话即可还能记录每通电话的随访结果。随访覆盖率在一个季度内提升了将近三成。8.2 系统还有哪些可以扩展的方向这个项目正式版的基础上我留了一些扩展余地后续可以考虑接上这几个方向微信预约挂号给社区居民提供线上预约渠道减少了诊室门口排队拥挤的痛点检查检验管理接入诊所的化验设备和影像设备让报告直接上传到系统替代手工粘贴报告的模式家庭医生签约功能按社区划分责任医生团队将慢病管理和健康宣教标准化到每个家庭医生的工作台数据上报接口部分地区的公共卫生管理平台开放数据上报接口系统可以直接对接免去重复填报社区医疗信息化是一个长期项目我这个系统的定位是好用不贵。想投入真金白银上大品牌 HIS 的诊所很多但基层医疗结构的预算和运维能力摆在那里一套耐用的开源系统反而是最务实的解决方案。9. 写在最后的一点小建议开发这套系统过程中我最大的体会是做医疗类项目技术本身从来不是最难的部分真正的难点在于对业务流程的理解和对数据敏感性的一丝不苟。社区居民群体跨度大老年患者居多医生往往需要在很短时间内完成信息录入。所以系统设计时我一直坚持快和稳这两个字——页面加载要快、录入步骤要少、数据要稳、备份要勤。最后分享一个刚刚提过的细节社区诊所的网络环境比不了大医院如果诊所内部没有稳定的外网连接所有前端资源一定要本地化。这个系统从第一行代码开始我就坚持所有依赖库和前端资源都打进项目包就算诊所搬到了没有宽带的临时场所拔出电源到新电脑上一条命令就能恢复运行。这也许是最土的建议但却是这套系统能在基层真正存活下来的核心原因之一。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。