基于Python+Django的医院药物管理系统设计与优化

📅 2026/8/12 12:59:29
基于Python+Django的医院药物管理系统设计与优化
1. 项目背景与需求分析医院药物管理系统是医疗机构日常运营中不可或缺的核心业务系统。传统药物管理系统往往存在以下几个痛点数据孤岛问题药房库存、医嘱系统、财务系统相互独立数据无法实时同步操作效率低下人工盘点耗时长易出错平均每次全库盘点需要4-6小时缺乏智能预警近效期药品、短缺药品无法提前预警据统计约15%的药品浪费源于过期追溯困难当出现用药问题时难以快速定位问题环节基于PythonDjango的新型系统设计目标实现药品全生命周期管理采购→入库→存储→处方→发药→回收建立实时库存监控机制库存准确率要求≥99.9%开发智能预警模块效期、库存、配伍禁忌支持多维度数据分析用药趋势、医师处方习惯等关键指标三甲医院日均处理处方量约2000-5000张系统需在1秒内完成单次药品库存状态查询2. 技术选型与架构设计2.1 核心框架选择Django优势分析ORM高效开发药品主数据模型包含50字段Django Model可快速定义复杂关系Admin后台开箱即用药品基础信息管理可直接使用Django Admin二次开发安全机制完善内置CSRF、XSS防护符合医疗系统安全要求缓存集成利用Redis缓存药品目录QPS提升300%# 典型药品模型定义示例 class Medicine(models.Model): drug_code models.CharField(max_length20, uniqueTrue) # 药品编码 name models.CharField(max_length100) # 通用名 specification models.CharField(max_length50) # 规格 manufacturer models.ForeignKey(Manufacturer, on_deletemodels.PROTECT) category models.CharField(max_length20, choicesCATEGORY_CHOICES) # 处方/非处方 unit models.CharField(max_length10) # 单位 price models.DecimalField(max_digits8, decimal_places2) # 单价 stock models.IntegerField(default0) # 当前库存 alert_level models.IntegerField(default100) # 库存预警线 expiry_alert_days models.IntegerField(default30) # 效期预警天数 class Meta: indexes [ models.Index(fields[drug_code]), models.Index(fields[category]), ]2.2 数据库设计要点MySQL优化策略分表设计将药品动态数据库存、价格与静态数据基础信息分离索引优化为高频查询字段药品编码、分类建立组合索引事务处理药品出入库采用事务操作确保数据一致性-- 关键表结构示例 CREATE TABLE medicine_transaction ( id bigint(20) NOT NULL AUTO_INCREMENT, medicine_id int(11) NOT NULL, batch_no varchar(50) NOT NULL COMMENT 批次号, transaction_type tinyint(4) NOT NULL COMMENT 1-入库 2-出库, quantity int(11) NOT NULL, remain_quantity int(11) NOT NULL COMMENT 批次剩余量, expiry_date date NOT NULL, operator_id int(11) NOT NULL, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_medicine_batch (medicine_id,batch_no), KEY idx_expiry (expiry_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;2.3 系统架构设计B/S架构实现方案表示层HTML5 Bootstrap Vue.js 业务层Django REST Framework 数据层MySQL Redis缓存 消息队列Celery处理异步任务如库存预警高并发解决方案药品目录使用Redis缓存减少数据库访问库存变更采用乐观锁机制报表查询使用Django的annotate进行聚合计算3. 核心功能模块实现3.1 药品全流程追踪批次管理实现逻辑采购入库时记录批次号、生产日期、效期出库时按照近效期先出原则自动分配批次系统每日凌晨自动扫描近效期药品# 批次分配算法示例 def allocate_batch(medicine_id, required_qty): batches MedicineBatch.objects.filter( medicine_idmedicine_id, remain_quantity__gt0 ).order_by(expiry_date) allocated [] remaining_qty required_qty for batch in batches: if remaining_qty 0: break allocate_qty min(batch.remain_quantity, remaining_qty) allocated.append({ batch_id: batch.id, quantity: allocate_qty }) batch.remain_quantity - allocate_qty batch.save() remaining_qty - allocate_qty if remaining_qty 0: raise InsufficientStockError() return allocated3.2 智能预警系统预警规则配置库存预警当库存量低于安全库存时触发效期预警药品效期30天时提醒滞销预警6个月无出库记录的药品# 预警任务Celery实现 app.task def check_inventory_alerts(): # 库存预警 low_stock Medicine.objects.filter( stock__ltF(alert_level) ).values(id, name, stock, alert_level) # 效期预警 expiry_alert MedicineBatch.objects.filter( expiry_date__lttimezone.now() timedelta(days30), remain_quantity__gt0 ).select_related(medicine) # 触发预警通知 if low_stock.exists() or expiry_alert.exists(): send_alert_notification( low_stocklist(low_stock), expiry_alertlist(expiry_alert) )3.3 处方审核模块业务规则配伍禁忌检查使用药品相互作用数据库剂量合理性验证医保限制药品审核class PrescriptionValidator: def __init__(self, prescription_items): self.items prescription_items def validate(self): errors [] # 检查配伍禁忌 drug_ids [item.medicine_id for item in self.items] conflicts DrugInteraction.objects.filter( Q(drug_a__indrug_ids) Q(drug_b__indrug_ids) ) for conflict in conflicts: errors.append(f配伍禁忌{conflict.drug_a.name}与{conflict.drug_b.name}存在{conflict.interaction_type}) # 检查单次剂量 for item in self.items: if item.dosage item.medicine.max_single_dose: errors.append(f超量用药{item.medicine.name}单次最大剂量为{item.medicine.max_single_dose}{item.medicine.unit}) return errors4. 性能优化实战经验4.1 数据库查询优化典型问题 药品目录页加载缓慢3s包含分类筛选、搜索等功能优化方案使用select_related/prefetch_related减少查询次数添加数据库索引高频访问数据缓存# 优化前后对比 # 原始查询产生N1问题 medicines Medicine.objects.filter(categoryantibiotic) for med in medicines: print(med.manufacturer.name) # 每次循环都查询manufacturer # 优化后查询 medicines Medicine.objects.select_related(manufacturer)\ .filter(categoryantibiotic)\ .only(name, specification, manufacturer__name)4.2 并发控制方案药品出库时的库存竞争问题解决方案乐观锁实现transaction.atomic def deduct_stock(medicine_id, quantity): medicine Medicine.objects.select_for_update().get(pkmedicine_id) if medicine.stock quantity: medicine.stock - quantity medicine.save() return True return FalseRedis原子操作def redis_deduct_stock(medicine_id, quantity): lua_script local current tonumber(redis.call(GET, KEYS[1])) if current tonumber(ARGV[1]) then return redis.call(DECRBY, KEYS[1], ARGV[1]) else return -1 end result redis_client.eval( lua_script, 1, fmedicine:stock:{medicine_id}, str(quantity) ) return result ! -14.3 缓存策略设计多级缓存方案药品基础信息Redis缓存过期时间1小时库存数据本地内存缓存有效期30秒药品图片CDN缓存class MedicineService: classmethod def get_medicine(cls, medicine_id): cache_key fmedicine:{medicine_id} # 先查本地缓存 medicine local_cache.get(cache_key) if medicine: return medicine # 再查Redis medicine redis_client.get(cache_key) if medicine: local_cache.set(cache_key, medicine, 30) return medicine # 最后查数据库 medicine Medicine.objects.get(pkmedicine_id) redis_client.setex(cache_key, 3600, medicine) local_cache.set(cache_key, medicine, 30) return medicine5. 安全与合规实现5.1 医疗数据安全关键措施数据传输加密全站HTTPS敏感数据脱敏医师姓名、患者信息显示时脱敏处理操作日志审计记录所有药品变更操作# 审计日志中间件示例 class AuditLogMiddleware: def __init__(self, get_response): self.get_response get_response def __call__(self, request): response self.get_response(request) if request.method in (POST, PUT, DELETE): OperationLog.objects.create( userrequest.user, pathrequest.path, methodrequest.method, paramsjson.dumps(getattr(request, request.method, {})), iprequest.META.get(REMOTE_ADDR) ) return response5.2 权限控制方案基于Django内置权限系统的扩展角色定义药房管理员药品基础数据维护采购员药品入库操作医师处方开具护士处方执行细粒度权限控制permission_required(medicine.change_medicine_stock) def update_stock(request): # 库存修改逻辑 pass数据权限过滤class MedicineListView(ListView): def get_queryset(self): qs super().get_queryset() if not self.request.user.has_perm(medicine.view_all): return qs.filter( departmentself.request.user.department ) return qs6. 部署与运维实践6.1 生产环境部署推荐架构Web服务器Nginx Gunicorn数据库MySQL主从复制缓存Redis哨兵模式监控Prometheus Grafana# Gunicorn启动脚本示例 gunicorn \ --bind 0.0.0.0:8000 \ --workers 4 \ --threads 2 \ --timeout 120 \ --access-logfile - \ --error-logfile - \ --capture-output \ medicine_system.wsgi6.2 关键运维指标监控重点数据库连接池使用率警戒线80%API响应时间P99500ms库存同步延迟1s预警任务执行时长# Prometheus自定义指标示例 from prometheus_client import Gauge MEDICINE_STOCK_GAUGE Gauge( medicine_stock_level, Current medicine stock level, [medicine_id] ) # 在库存变更时更新指标 def update_stock(medicine_id, delta): # 更新数据库... MEDICINE_STOCK_GAUGE.labels(medicine_id).set(new_stock)6.3 灾备方案设计数据库每日全量备份 binlog增量备份药品主数据异地备份快速恢复流程优先恢复药品基础数据然后恢复最近3天交易数据最后同步最新库存实际运维中发现药品批次信息是最关键的恢复数据需确保至少保留两个备份副本7. 项目演进与扩展7.1 移动端集成混合开发方案微信小程序提供患者用药提醒功能护士端APP扫码快速核对药品采用Django REST Framework提供统一API# 小程序API示例 class MedicineReminderViewSet(viewsets.ModelViewSet): queryset MedicineReminder.objects.all() serializer_class MedicineReminderSerializer def get_queryset(self): return super().get_queryset().filter( patientself.request.user.patient ) action(detailTrue, methods[post]) def confirm(self, request, pkNone): reminder self.get_object() reminder.confirm() return Response({status: confirmed})7.2 数据分析扩展基于Pandas的用药分析药品消耗趋势分析处方合理性分析供应商绩效评估def analyze_prescription_pattern(start_date, end_date): queryset Prescription.objects.filter( created_at__range(start_date, end_date) ).values(doctor, medicine).annotate( totalCount(id), avg_dosageAvg(dosage) ) df pd.DataFrame.from_records(queryset) pivot pd.pivot_table( df, valuestotal, indexdoctor, columnsmedicine, fill_value0 ) # 生成处方热力图 plt.figure(figsize(12, 8)) sns.heatmap(pivot, cmapYlGnBu) plt.title(Doctor Prescription Pattern) return plt7.3 智能补货预测基于历史数据的库存预测模型使用ARIMA算法预测药品需求考虑季节性因素如感冒药冬季需求增加自动生成采购建议from statsmodels.tsa.arima.model import ARIMA def forecast_demand(medicine_id, periods7): history MedicineTransaction.objects.filter( medicine_idmedicine_id, transaction_type2 # 出库 ).annotate( dateTruncDate(created_at) ).values(date).annotate( totalSum(quantity) ).order_by(date) series pd.Series( [x[total] for x in history], indexpd.to_datetime([x[date] for x in history]) ) model ARIMA(series, order(1,1,1)) model_fit model.fit() forecast model_fit.forecast(stepsperiods) return forecast8. 项目经验总结在实际部署过程中有几个关键经验值得分享药品编码统一问题初期遇到不同供应商使用不同编码体系最终采用国药准字H8位数字作为主键建立编码映射表处理历史数据效期管理陷阱发现部分药品批次存在见包装的效期标注方式解决方案强制要求入库时录入具体年月高并发场景验证压力测试发现库存扣减存在超卖现象最终采用RedisLua脚本实现原子操作医嘱系统对接与HIS系统对接时遇到数据格式不一致开发中间件进行数据转换和清洗# 实际项目中最有价值的工具函数 def convert_medical_code(source_code, source_system): 不同编码体系转换工具 :param source_code: 原始编码 :param source_system: 来源系统 :return: 标准编码 mapping { HIS: {A123: H12345678}, ERP: {B456: H87654321} } return mapping.get(source_system, {}).get(source_code, source_code)对于计划开发类似系统的团队建议重点关注药品主数据标准的早期确立效期管理的严格流程设计库存变更的原子性保证与现有医疗系统的对接方案