1. 核心需求分析与技术选型
中西医结合医院的档案管理需同时处理西医电子病历(EMR)和中医电子病历(TCM-EMR),两者在数据结构、术语体系上差异显著。一个合格的软件必须能统一管理这两类数据,并满足《电子病历系统功能规范(试行)》和《中医电子病历基本规范》的双重要求。
我们选择基于开源技术栈自建,核心原因在于可控性、成本与合规适配灵活性。技术栈如下:
- 后端框架:Django 4.2 (Python)。其强大的ORM、内置Admin后台和安全性,适合快速构建合规的数据模型。
- 数据库:PostgreSQL 15。对JSON字段的良好支持,便于存储中医辨证论治等非结构化或半结构化数据。
- 前端框架:Vue 3 + Element Plus。用于构建交互复杂的病历录入与检索界面。
- 全文检索:Elasticsearch 8.x。用于对病历中的症状、诊断(含中医证型)等文本进行高效模糊和术语检索。
- 容器化:Docker + Docker Compose。确保环境一致,简化部署。
2. 环境准备与项目初始化
以下操作在Ubuntu 22.04 LTS服务器上执行。请确保拥有sudo权限。
2.1 安装系统依赖与Docker
首先更新系统并安装必要工具:
```
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl git python3-pip
```
安装Docker和Docker Compose:
```
安装Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
请重新登录终端使组权限生效
安装Docker Compose插件
sudo apt install -y docker-compose-plugin
```
验证安装:docker --version 和 docker compose version。
2.2 拉取项目代码与配置
我们使用一个预配置好的开源医院档案管理模板项目作为基础:
```
git clone https://github.com/example-repo/his-emr-template.git hospital-archives
cd hospital-archives
```
项目结构如下:
backend/: Django后端代码
frontend/: Vue前端代码
docker-compose.yml: 服务编排文件
.env.example: 环境变量示例文件
复制环境变量文件并编辑:
```
cp .env.example .env
nano .env 或使用vim等其他编辑器
```
在打开的.env文件中,修改以下核心配置:
```
数据库配置
POSTGRES_DB=hospital_archive
POSTGRES_USER=archive_admin
POSTGRES_PASSWORD=YourStrongPassword123! 务必修改为强密码
POSTGRES_HOST=db
POSTGRES_PORT=5432
Django密钥,使用命令 `openssl rand -hex 32` 生成
DJANGO_SECRET_KEY=生成的64位十六进制字符串
Elasticsearch配置(留空为禁用,生产环境建议启用)
ELASTICSEARCH_HOSTS=http://es:9200
```
3. 数据库与核心数据模型配置
3.1 启动基础服务并初始化数据库
使用Docker Compose启动PostgreSQL和Elasticsearch:
```
docker compose up -d db es 启动数据库和搜索引擎
```
等待约30秒后,进入Django容器并执行数据库迁移:
```
docker compose run --rm backend python manage.py migrate
```
3.2 创建超级管理员账户
执行以下命令创建后台管理账户:
```
docker compose run --rm backend python manage.py createsuperuser
```
根据提示输入用户名、邮箱和密码。
3.3 配置中西医结合病历核心数据模型
关键步骤是扩展Django的Patient和MedicalRecord模型,以兼容中西医数据。打开backend/records/models.py,核心模型定义如下:
```
from django.db import models
from django.contrib.postgres.fields import JSONField
class Patient(models.Model):
"""患者主索引"""
patient_id = models.CharField(max_length=20, unique=True, verbose_name='病案号')
name = models.CharField(max_length=50, verbose_name='姓名')
id_number = models.CharField(max_length=18, unique=True, verbose_name='身份证号')
gender_choices = (('M', '男'), ('F', '女'), ('U', '未知'))
gender = models.CharField(max_length=1, choices=gender_choices, verbose_name='性别')
birth_date = models.DateField(verbose_name='出生日期')
... 其他基础字段
class MedicalRecord(models.Model):
"""电子病历主表"""
patient = models.ForeignKey(Patient, on_delete=models.CASCADE, related_name='records')
visit_id = models.CharField(max_length=30, unique=True, verbose_name='就诊流水号')
visit_type_choices = (('W', '西医门诊'), ('W_I', '西医住院'), ('T', '中医门诊'), ('T_I', '中医住院'), ('I', '中西医结合'))
visit_type = models.CharField(max_length=5, choices=visit_type_choices, verbose_name='就诊类型')
visit_time = models.DateTimeField(verbose_name='就诊时间')
西医诊断部分(结构化)
western_diagnosis = models.TextField(verbose_name='西医诊断', blank=True)
icd11_code = models.CharField(max_length=20, blank=True, verbose_name='ICD-11编码')
中医诊断部分(半结构化,使用JSONField存储)
tcm_data = JSONField(default=dict, verbose_name='中医四诊信息')
tcm_data 结构示例:
{
"syndrome": "肝肾阴虚证",
"tongue": "舌红少苔",
"pulse": "脉细数",
"tcm_diagnosis": "眩晕",
"tcm_therapy": "滋阴补肾,平肝潜阳",
"prescription": "杞菊地黄丸加减"
}
通用字段
chief_complaint = models.TextField(verbose_name='主诉')
present_illness = models.TextField(verbose_name='现病史')
physical_exam = models.TextField(verbose_name='体格检查', blank=True)
lab_results = models.TextField(verbose_name='实验室检查结果', blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
indexes = [
models.Index(fields=['visit_id']),
models.Index(fields=['patient', '-visit_time']),
]
```
保存文件后,生成并应用新的数据库迁移:
```
docker compose run --rm backend python manage.py makemigrations
docker compose run --rm backend python manage.py migrate
```
4. 病历数据录入与索引配置
4.1 配置Django Admin以支持中西医数据录入
打开backend/records/admin.py,注册模型并自定义表单:
```
from django.contrib import admin
from .models import Patient, MedicalRecord
import json
class MedicalRecordAdmin(admin.ModelAdmin):
list_display = ('visit_id', 'patient', 'visit_type', 'visit_time')
list_filter = ('visit_type',)
search_fields = ('patient__name', 'patient__patient_id', 'visit_id')
fieldsets = (
('基本信息', {
'fields': ('patient', 'visit_id', 'visit_type', 'visit_time')
}),
('主诉与病史', {
'fields': ('chief_complaint', 'present_illness', 'physical_exam')
}),
('西医诊断', {
'fields': ('western_diagnosis', 'icd11_code', 'lab_results'),
'classes': ('collapse',) 可折叠
}),
('中医四诊信息', {
'fields': ('tcm_data',),
'description': '请按JSON格式填写,键值对示例:{"syndrome": "风寒束表证", "tongue": "舌淡红,苔薄白"}'
}),
)
在保存前格式化JSON字段
def save_model(self, request, obj, form, change):
if isinstance(obj.tcm_data, str):
try:
obj.tcm_data = json.loads(obj.tcm_data)
except json.JSONDecodeError:
pass
super().save_model(request, obj, form, change)
admin.site.register(Patient)
admin.site.register(MedicalRecord, MedicalRecordAdmin)
```
4.2 配置Elasticsearch全文索引

首先确保Elasticsearch服务已启动。然后安装Django Elasticsearch DSL驱动:
编辑backend/requirements.txt,添加一行:django-elasticsearch-dsl==7.2.2。
在Django容器内重建并安装依赖:
```
docker compose build backend
docker compose up -d backend
```
创建backend/records/documents.py文件,定义索引:
```
from django_elasticsearch_dsl import Document, fields
from django_elasticsearch_dsl.registries import registry
from .models import MedicalRecord
@registry.register_document
class MedicalRecordDocument(Document):
patient_name = fields.TextField(attr='patient.name')
patient_id = fields.KeywordField(attr='patient.patient_id')
visit_type = fields.KeywordField()
western_diagnosis = fields.TextField()
将JSON字段中的关键信息映射为可检索字段
tcm_syndrome = fields.TextField()
tcm_diagnosis = fields.TextField()
chief_complaint = fields.TextField()
class Index:
name = 'medical_records'
settings = {'number_of_shards': 1, 'number_of_replicas': 0}
class Django:
model = MedicalRecord
fields = ['visit_id', 'present_illness']
def prepare_tcm_syndrome(self, instance):
return instance.tcm_data.get('syndrome', '')
def prepare_tcm_diagnosis(self, instance):
return instance.tcm_data.get('tcm_diagnosis', '')
def get_queryset(self):
return super().get_queryset().select_related('patient')
```
在Django的settings.py中添加配置:
```
ELASTICSEARCH_DSL = {
'default': {
'hosts': os.environ.get('ELASTICSEARCH_HOSTS', 'localhost:9200')
},
}
```
构建索引并将现有数据导入:
```
docker compose run --rm backend python manage.py search_index --rebuild
```
5. 前端查询界面部署与使用
5.1 构建并启动前端服务
进入前端目录,安装依赖并构建生产版本:
```
cd frontend
npm install 如果未安装Node.js,请先安装Node.js 18+
npm run build
cd ..
```
修改docker-compose.yml,确保前端服务配置正确:
```
frontend:
build: ./frontend
ports:
- "8080:80"
depends_on:
- backend
environment:
- VITE_API_BASE_URL=http://后端服务器IP或域名:8000/api
```
启动所有服务:
```
docker compose up -d
```
5.2 使用病历查询功能
访问 http://你的服务器IP:8080 打开前端应用。
基础查询:在首页搜索框,输入患者姓名、病案号或症状关键词(如“头晕”、“舌红”),系统将同时检索结构化字段和JSON字段中的中医信息。
高级筛选:点击“高级搜索”,可使用以下筛选器:
- 就诊类型:单选或多选西医门诊、中医住院等。
- 时间段:选择就诊日期范围。
- 诊断类型:可单独筛选“包含西医诊断”或“包含中医诊断”的病历。
病历详情查看:点击查询结果中的任一病历,页面将分栏展示:
左栏为西医诊断、实验室检查等;右栏专门展示中医四诊信息(舌象、脉象、证型、治法方药),并以结构化样式呈现。
6. 数据备份与日常维护
6.1 自动化数据库备份
在服务器上创建备份脚本/opt/backup_db.sh:
```
!/bin/bash
BACKUP_DIR="/var/backups/hospital_archive"
DATE=$(date +%Y%m%d_%H%M%S)
docker compose exec -T db pg_dump -U archive_admin hospital_archive > "$BACKUP_DIR/dump_$DATE.sql"
压缩并删除7天前的备份
find "$BACKUP_DIR" -name ".sql" -mtime +7 -delete
```
赋予执行权限并添加到crontab,每天凌晨2点执行:
```
chmod +x /opt/backup_db.sh
crontab -e
添加一行:0 2 /opt/backup_db.sh
```
6.2 日志与监控
查看服务日志:docker compose logs -f backend。
检查Elasticsearch健康状态:curl http://localhost:9200/_cluster/health?pretty。
6.3 系统升级
更新代码后,执行标准升级流程:
```
git pull origin main
docker compose build
docker compose run --rm backend python manage.py migrate
docker compose up -d
docker compose run --rm backend python manage.py search_index --rebuild
```