1. 医院门诊挂号系统架构设计
在开发医院门诊挂号系统时,技术选型直接影响系统的稳定性和可维护性。我们选择了Python生态中的Django作为主要框架,同时保留Flask作为备选方案。这种选择基于以下几个关键考量:
Django的全栈特性特别适合医疗类应用开发,它内置的ORM、Admin后台和认证系统可以快速搭建起一个功能完善的门诊系统。比如,使用Django的models.py定义数据模型时,几行代码就能建立起医生、患者和预约之间的复杂关系。而Flask的轻量级特性则适合需要高度定制化的微服务场景,比如单独部署的支付模块或消息通知服务。
微信小程序作为前端入口,通过HTTPS与后端API通信。我们采用RESTful API设计风格,所有接口都遵循统一的JSON数据格式。考虑到医疗数据的敏感性,每个API请求都要求携带JWT令牌进行身份验证。
数据库方面,PostgreSQL是我们的首选,它的JSON字段类型特别适合存储医生的动态排班信息。MySQL作为备选方案,在中小型医院场景下也能良好运行。我们为所有核心表都设计了适当的索引,比如在预约表的doctor_id和time_slot字段上建立联合索引,大幅提升查询效率。
2. 核心数据模型设计
2.1 用户与医生模型
用户模型采用Django内置的AbstractUser进行扩展,增加了患者专属字段如医保卡号、过敏史等。医生模型则关联到科室,并包含职称、专长等专业信息。特别重要的是医生的排班信息,我们使用JSONField来存储灵活的时间安排:
python复制class Doctor(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.ForeignKey('Department', on_delete=models.PROTECT)
title = models.CharField(max_length=50) # 主任医师/副主任医师等
specialty = models.TextField() # 专业特长
schedule = models.JSONField(default=dict) # 示例: {"周一": ["09:00-12:00", "14:00-17:00"]}
2.2 预约模型设计
预约模型是整个系统的核心,它需要处理各种状态流转和业务规则:
python复制class Appointment(models.Model):
STATUS_CHOICES = [
('pending', '待支付'),
('confirmed', '已确认'),
('completed', '已完成'),
('cancelled', '已取消'),
]
patient = models.ForeignKey(User, on_delete=models.CASCADE)
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
time_slot = models.DateTimeField()
status = models.CharField(max_length=20, choices=STATUS_CHOICES)
symptoms = models.TextField(blank=True) # 患者症状描述
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
indexes = [
models.Index(fields=['doctor', 'time_slot']),
models.Index(fields=['status', 'time_slot']),
]
3. 微信小程序集成开发
3.1 微信登录实现
微信小程序登录流程需要前后端配合完成。前端调用wx.login()获取临时code,后端通过微信接口服务换取openid和session_key:
python复制# Django示例代码
from rest_framework.views import APIView
import requests
class WeChatLoginView(APIView):
def post(self, request):
code = request.data.get('code')
if not code:
return Response({'error': 'Missing code'}, status=400)
# 向微信服务器发送请求
url = f"https://api.weixin.qq.com/sns/jscode2session?appid={APPID}&secret={SECRET}&js_code={code}&grant_type=authorization_code"
response = requests.get(url)
data = response.json()
# 处理微信返回结果
openid = data.get('openid')
if not openid:
return Response({'error': 'WeChat login failed'}, status=401)
# 创建或更新用户
user, created = User.objects.get_or_create(
wechat_openid=openid,
defaults={'username': f"wxuser_{openid[-6:]}"}
)
# 生成JWT令牌
token = generate_jwt_token(user)
return Response({'token': token, 'user_id': user.id})
3.2 预约接口设计
预约接口需要考虑时间冲突校验、号源库存控制等业务逻辑:
python复制class AppointmentViewSet(viewsets.ModelViewSet):
queryset = Appointment.objects.all()
permission_classes = [IsAuthenticated]
def create(self, request):
doctor_id = request.data.get('doctor_id')
time_slot = request.data.get('time_slot')
# 检查时间格式
try:
appointment_time = parse_datetime(time_slot)
except ValueError:
return Response({'error': 'Invalid time format'}, status=400)
# 检查医生是否存在
try:
doctor = Doctor.objects.get(id=doctor_id)
except Doctor.DoesNotExist:
return Response({'error': 'Doctor not found'}, status=404)
# 检查时间冲突
if Appointment.objects.filter(
doctor=doctor,
time_slot=appointment_time,
status__in=['pending', 'confirmed']
).exists():
return Response({'error': 'Time slot already taken'}, status=409)
# 创建预约记录
appointment = Appointment.objects.create(
patient=request.user,
doctor=doctor,
time_slot=appointment_time,
status='pending'
)
# 发送微信模板消息
send_template_message(request.user.wechat_openid, appointment)
return Response(AppointmentSerializer(appointment).data, status=201)
4. 预约业务逻辑实现
4.1 分时段预约控制
医院门诊通常需要将一天划分为多个时段进行预约管理。我们设计了TimeSlot模型来管理可预约时段:
python复制class TimeSlot(models.Model):
department = models.ForeignKey('Department', on_delete=models.CASCADE)
start_time = models.TimeField()
end_time = models.TimeField()
max_appointments = models.PositiveIntegerField(default=10)
is_active = models.BooleanField(default=True)
@property
def available_slots(self):
taken = Appointment.objects.filter(
time_slot__date=date.today(),
time_slot__time__gte=self.start_time,
time_slot__time__lt=self.end_time,
status__in=['pending', 'confirmed']
).count()
return max(0, self.max_appointments - taken)
4.2 号源库存管理
为了防止超卖,我们使用Redis实现分布式锁和库存缓存:
python复制import redis
from django.core.cache import cache
r = redis.Redis(host='localhost', port=6379, db=0)
def reserve_slot(doctor_id, time_slot):
lock_key = f"lock:{doctor_id}:{time_slot}"
inventory_key = f"inventory:{doctor_id}:{time_slot}"
# 获取分布式锁
with r.lock(lock_key, timeout=10):
remaining = r.get(inventory_key)
if remaining and int(remaining) <= 0:
return False
# 扣减库存
r.decr(inventory_key)
return True
def init_inventory():
# 每天凌晨初始化号源
for slot in TimeSlot.objects.all():
for doctor in slot.department.doctors.all():
key = f"inventory:{doctor.id}:{slot.id}"
r.set(key, slot.max_appointments)
5. 系统安全与性能优化
5.1 安全防护措施
医疗系统对安全性要求极高,我们实施了多层防护:
- 数据传输安全:全站HTTPS,敏感字段如症状描述在传输前进行AES加密
- 认证授权:JWT令牌包含用户角色和权限,每个API都进行细粒度权限检查
- 防刷机制:预约接口实施限流(如1分钟最多5次请求)
- 数据脱敏:日志中的患者信息自动进行掩码处理
python复制# Django中间件示例
class SecurityMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# 请求前处理
if not request.path.startswith('/admin/'):
if not request.is_secure():
return HttpResponseForbidden("HTTPS required")
response = self.get_response(request)
# 响应头安全设置
response['X-Content-Type-Options'] = 'nosniff'
response['X-Frame-Options'] = 'DENY'
response['Content-Security-Policy'] = "default-src 'self'"
return response
5.2 性能优化策略
-
数据库优化:
- 使用select_related和prefetch_related减少查询次数
- 对高频查询建立适当索引
- 配置数据库连接池
-
缓存策略:
- 医生排班信息缓存1小时
- 热门科室的号源信息缓存5分钟
- 使用Redis做分布式会话存储
-
异步处理:
- 使用Celery处理耗时操作如发送短信、生成报表
- 预约确认邮件通过消息队列异步发送
python复制# 异步任务示例
@app.task
def send_confirmation_email(appointment_id):
appointment = Appointment.objects.get(id=appointment_id)
subject = f"预约确认: {appointment.doctor.name}医生"
message = render_to_string('email/confirmation.html', {
'appointment': appointment
})
send_mail(subject, message, 'noreply@hospital.com', [appointment.patient.email])
6. 系统部署与监控
6.1 生产环境部署
我们采用Docker容器化部署方案,便于扩展和维护:
dockerfile复制# Django Dockerfile示例
FROM python:3.9
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "hospital.wsgi:application"]
使用docker-compose编排多个服务:
yaml复制version: '3'
services:
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:6
web:
build: .
command: bash -c "python manage.py migrate && gunicorn --bind 0.0.0.0:8000 --workers 4 hospital.wsgi:application"
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- db
- redis
volumes:
postgres_data:
6.2 监控与告警
完善的监控系统是保障医疗系统稳定运行的关键:
- 应用性能监控:使用Prometheus收集接口响应时间、错误率等指标
- 业务指标监控:跟踪每日预约量、取消率等关键业务指标
- 日志收集:使用ELK(Elasticsearch+Logstash+Kibana)集中管理日志
- 异常告警:配置微信/邮件告警规则,如5分钟内错误率超过1%触发告警
python复制# Prometheus监控示例
from prometheus_client import start_http_server, Counter
APPOINTMENT_CREATED = Counter(
'appointment_created_total',
'Total number of appointments created',
['department']
)
class AppointmentViewSet(viewsets.ModelViewSet):
def create(self, request):
# ...原有逻辑...
APPOINTMENT_CREATED.labels(
department=appointment.doctor.department.name
).inc()
return response
7. 扩展功能实现
7.1 智能推荐医生
基于患者的病史和预约记录,实现医生推荐功能:
python复制from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
def recommend_doctors(patient):
# 获取患者历史症状描述
symptoms = Appointment.objects.filter(
patient=patient
).values_list('symptoms', flat=True)
if not symptoms:
return Doctor.objects.order_by('?')[:3]
# 医生专长向量化
doctors = Doctor.objects.all()
doctor_specialties = [d.specialty for d in doctors]
# 计算相似度
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(doctor_specialties + [' '.join(symptoms)])
similarity = cosine_similarity(X[-1], X[:-1])[0]
# 返回相似度最高的3位医生
top_indices = similarity.argsort()[-3:][::-1]
return [doctors[i] for i in top_indices]
7.2 消息通知系统
集成多种通知渠道,确保患者及时接收重要信息:
python复制class NotificationService:
@classmethod
def send_appointment_reminder(cls, appointment):
# 微信模板消息
if appointment.patient.wechat_openid:
send_wechat_template(
openid=appointment.patient.wechat_openid,
template_id='APPOINTMENT_REMINDER',
data={
'time': {'value': appointment.time_slot.strftime('%Y-%m-%d %H:%M')},
'doctor': {'value': appointment.doctor.name},
'location': {'value': '3楼内科诊室'}
}
)
# 短信提醒
if appointment.patient.phone:
send_sms(
phone=appointment.patient.phone,
message=f"【医院】您预约的{appointment.doctor.name}医生将于{appointment.time_slot.strftime('%m月%d日%H:%M')}就诊,请提前10分钟到达"
)
8. 测试与质量保障
8.1 自动化测试策略
医疗系统必须经过严格测试,我们建立了多层次的测试体系:
- 单元测试:覆盖核心业务逻辑如预约冲突检测
- 接口测试:验证API的输入输出和错误处理
- 集成测试:测试各模块协同工作情况
- 性能测试:模拟高峰时段的并发预约场景
python复制# 单元测试示例
class AppointmentTestCase(TestCase):
def setUp(self):
self.doctor = Doctor.objects.create(name="张医生")
self.patient = User.objects.create(username="testuser")
self.time_slot = timezone.now() + timedelta(days=1)
def test_create_appointment(self):
appointment = Appointment.objects.create(
patient=self.patient,
doctor=self.doctor,
time_slot=self.time_slot,
status='pending'
)
self.assertEqual(appointment.status, 'pending')
def test_time_conflict(self):
Appointment.objects.create(
patient=self.patient,
doctor=self.doctor,
time_slot=self.time_slot,
status='confirmed'
)
with self.assertRaises(ValidationError):
Appointment.objects.create(
patient=self.patient,
doctor=self.doctor,
time_slot=self.time_slot,
status='pending'
)
8.2 持续集成部署
使用GitHub Actions实现CI/CD流程,确保每次代码提交都经过自动化测试:
yaml复制# .github/workflows/django.yml
name: Django CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:13
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres
run: |
python manage.py test
9. 实际部署经验分享
在真实医院环境中部署时,有几个关键点需要注意:
- 数据迁移:从旧系统迁移数据时,建议先在测试环境验证,特别注意时间格式的转换
- 培训计划:为医护人员提供详细的操作培训,特别是排班管理和号源调整功能
- 应急预案:准备手动处理预约的备用方案,防止系统故障影响门诊运行
- 灰度发布:先在小范围科室试用,收集反馈后再全院推广
提示:在正式上线前,务必进行充分的压力测试。我们曾遇到过一个案例:某医院系统在早高峰时段因并发预约请求过多导致数据库连接耗尽。解决方案是引入连接池和读写分离,同时优化预约接口的响应速度。
10. 系统维护与升级建议
门诊挂号系统上线后,持续的维护和迭代同样重要:
- 定期备份:患者数据每日全量备份,保留最近30天的备份
- 性能监控:持续关注关键接口的响应时间,及时发现性能瓶颈
- 功能迭代:根据医院需求逐步增加新功能,如:
- 检查报告查询
- 在线问诊
- 药品配送
- 安全更新:及时更新框架和依赖库的安全补丁
python复制# 数据库备份管理示例
from django.core.management.commands.dumpdata import Command as DumpData
class BackupService:
@classmethod
def daily_backup(cls):
filename = f"backup_{date.today().isoformat()}.json"
with open(f"/backups/{filename}", 'w') as f:
DumpData().run(stdout=f)
# 清理30天前的备份
for old_file in Path('/backups').glob('*.json'):
if (date.today() - date.fromisoformat(old_file.stem[7:])).days > 30:
old_file.unlink()