1. 项目概述:当宠物医疗遇上全栈开发
去年帮朋友改造他经营的社区宠物诊所时,我深刻体会到传统纸质挂号本和Excel病历管理的痛点。兽医在给雪纳瑞看皮肤病时,前台还在手写登记新来的布偶猫信息,诊疗室电脑里存着三年前绝育手术记录却找不到上周的驱虫记录。这正是我们决定用Python+Vue打造宠物医院系统的初衷——用技术解决宠物医疗场景中的信息孤岛问题。
这个系统要实现的核心功能非常明确:宠物主人能在线预约特定医生时段,前台可直观管理候诊队列,医生端支持病历结构化录入和历史记录调阅,同时整合常见疾病的诊断建议库。技术选型上,后端采用Python+Django REST framework处理复杂的业务逻辑和数据关系,前端用Vue3+Element Plus构建响应式管理界面,这种组合既能快速迭代开发,又能保证系统在200+日挂号量下的稳定运行。
2. 系统架构设计与技术选型
2.1 前后端分离架构解析
采用前后端分离架构绝非赶时髦,而是针对宠物医院特殊场景的必然选择。当兽医在诊疗室用iPad调取病历的同时,前台可能正在Windows电脑上办理新的挂号,而宠物主人则通过手机查询报告。传统的服务端渲染根本无法满足这种多终端适配需求。
我们的解决方案是:
-
后端API层:Python 3.10 + Django 4.1
- 选择Django而非Flask的关键考量是其内置的Admin系统,方便快速构建医院管理后台
- 使用DRF(Django REST framework)构建符合OpenAPI规范的接口
- 配置CORS中间件解决跨域问题
-
前端应用层:Vue 3.2 + TypeScript
- Composition API写法更适合复杂业务组件开发
- 采用Vite构建工具实现秒级热更新
- Element Plus组件库提供专业的表单和表格控件
2.2 数据库设计中的宠物医疗特性
宠物医疗数据相比人类医疗有显著差异,比如:
- 一个主人可能登记多只宠物
- 同种动物的不同品种(如英短 vs 布偶猫)易患疾病不同
- 需要记录宠物芯片ID等特殊标识
我们设计的核心实体关系如下:
python复制class Pet(models.Model):
PET_TYPE_CHOICES = [
('DOG', '犬科'),
('CAT', '猫科'),
('OTHER', '其他')
]
name = models.CharField(max_length=100)
pet_type = models.CharField(max_length=20, choices=PET_TYPE_CHOICES)
breed = models.CharField(max_length=100) # 品种
birth_date = models.DateField()
chip_id = models.CharField(max_length=50, unique=True, null=True)
owner = models.ForeignKey(Owner, on_delete=models.CASCADE)
class MedicalRecord(models.Model):
pet = models.ForeignKey(Pet, on_delete=models.CASCADE)
visit_date = models.DateTimeField(auto_now_add=True)
symptoms = models.TextField()
diagnosis = models.TextField()
treatment = models.TextField()
prescribed_medicines = models.ManyToManyField(Medicine)
attending_vet = models.ForeignKey(Vet, on_delete=models.SET_NULL, null=True)
特别注意:宠物年龄应该存储出生日期而非固定年龄值,因为动物年龄计算需要精确到月份
3. 核心功能模块实现细节
3.1 智能挂号排队系统
传统先到先得的排队方式在宠物急诊场景会造成资源错配。我们开发了基于优先级的动态排队算法:
python复制def calculate_priority(case):
base_score = 0
if case.is_emergency: # 紧急情况
base_score += 100
if case.pet.age < 1: # 幼宠优先
base_score += 30
if case.pet.pet_type == 'OTHER': # 异宠科医生少
base_score += 20
return base_score - case.waiting_time # 等待时间越长优先级微调
前端使用WebSocket实现实时排队更新:
javascript复制// Vue组件中
const socket = new WebSocket('wss://yourdomain.com/queue/')
socket.onmessage = (event) => {
this.queueList = JSON.parse(event.data).map(item => ({
...item,
estimatedTime: this.calculateETC(item.position)
}))
}
3.2 病历结构化录入设计
为提升兽医录入效率,我们将常见症状和诊断结果做成可搜索的标签系统:
vue复制<template>
<el-select
v-model="selectedSymptoms"
multiple
filterable
allow-create
@change="updateDiagnosisSuggestions"
>
<el-option
v-for="item in symptomOptions"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
</template>
<script>
// 症状选择后自动推荐可能诊断
function updateDiagnosisSuggestions() {
this.diagnosisOptions = getRelatedDiagnoses(
this.selectedSymptoms,
this.petType
)
}
</script>
4. 特殊场景处理与性能优化
4.1 高并发挂号场景应对
在早高峰时段(9:00-11:00),系统可能面临短时间内大量预约请求。我们采用以下策略:
-
数据库层面:
- 使用PostgreSQL而非SQLite,支持更高并发
- 为挂号表(Registration)添加分区,按日期分表
-
缓存策略:
python复制# settings.py CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } } } # views.py @cache_page(60 * 5) # 缓存5分钟 def get_available_slots(request, vet_id): # ...
4.2 医疗图片存储方案
宠物皮肤病等病例需要上传对比照片,我们采用:
- 前端使用vue-dropzone实现拖拽上传
- 后端使用Django-storages对接阿里云OSS
- 图片处理流程:
python复制from django.core.files.storage import default_storage from PIL import Image def process_pet_image(file): with default_storage.open(file.name, 'wb+') as destination: for chunk in file.chunks(): destination.write(chunk) # 生成缩略图 img = Image.open(file) img.thumbnail((500, 500)) thumb_name = f"thumbs/{file.name}" img.save(thumb_name) return file.name, thumb_name
5. 部署与运维实战经验
5.1 混合部署架构
考虑到宠物医院通常没有专业IT团队,我们设计了一键部署方案:
code复制production/
├── docker-compose.yml # 核心服务
├── nginx/
│ ├── nginx.conf # 负载均衡配置
│ └── ssl/ # HTTPS证书
└── backups/ # 自动备份脚本
关键Docker配置示例:
yaml复制services:
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- DJANGO_SETTINGS_MODULE=config.settings.production
depends_on:
- redis
- postgres
frontend:
build: ./frontend
ports:
- "8080:80"
volumes:
- ./frontend/dist:/usr/share/nginx/html
5.2 典型问题排查手册
-
挂号提交缓慢问题:
- 检查Redis连接池状态
- 验证PostgreSQL索引:
sql复制EXPLAIN ANALYZE SELECT * FROM registration WHERE visit_date > CURRENT_DATE;
-
图片上传失败:
- 检查OSS bucket权限
- 验证Nginx上传大小限制:
nginx复制client_max_body_size 20M;
-
WebSocket断开:
- 配置Nginx代理:
nginx复制location /ws/ { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
- 配置Nginx代理:
6. 扩展功能开发思路
6.1 对接智能硬件
-
体温数据自动录入:
python复制# serializers.py class TempDataSerializer(serializers.Serializer): device_id = serializers.CharField() temperature = serializers.FloatField() pet_chip_id = serializers.CharField() def create(self, validated_data): pet = Pet.objects.get(chip_id=validated_data['pet_chip_id']) return TempRecord.objects.create( pet=pet, value=validated_data['temperature'], device=validated_data['device_id'] ) -
用药提醒功能:
javascript复制// 使用Web Notification API function scheduleMedicationReminder(time, medicine) { Notification.requestPermission().then(perm => { if (perm === "granted") { new Notification(`给${petName}服用${medicine}`, { body: `剂量: ${dosage}`, icon: '/icons/pill.png' }) } }) }
6.2 数据分析模块
利用Pandas生成宠物健康报告:
python复制def generate_health_report(pet_id):
records = MedicalRecord.objects.filter(pet_id=pet_id)
df = pd.DataFrame.from_records(records.values())
# 常见症状分析
symptom_analysis = (
df['symptoms'].str.split(',')
.explode().str.strip()
.value_counts()
.head(5)
)
# 生成PDF报告
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
pdf_path = f"/reports/{pet_id}.pdf"
c = canvas.Canvas(pdf_path, pagesize=letter)
c.drawString(100, 750, f"{pet.name}健康报告")
# ...更多绘制逻辑
c.save()
return pdf_path
在开发过程中,最让我意外的是兽医们对"病历模板共享"功能的强烈需求。不同医生积累的诊疗经验通过系统沉淀为可复用的模板,新入职的年轻兽医可以快速学习常见病例的处理方法。这促使我们在医生端增加了"我的模板"和"科室模板"功能,通过简单的JSON结构存储问诊流程:
json复制{
"templateName": "犬类皮肤病常规检查",
"steps": [
{
"title": "基础检查",
"items": [
"体温测量",
"皮肤刮片采样",
"伍德氏灯检查"
]
},
{
"title": "问诊事项",
"questions": [
"瘙痒程度(1-10分)",
"出现症状时长",
"近期饮食变化"
]
}
]
}
这种设计既保留了结构化数据的优势,又给了医生足够的灵活性。技术实现上看似简单,却真正解决了临床工作中的痛点,这也提醒我们:在医疗系统开发中,业务场景的理解深度往往比技术炫技更重要。
