1. 项目背景与需求分析
药品管理系统是医疗机构和药店日常运营中不可或缺的核心业务系统。随着医药行业信息化建设的不断深入,传统的手工记录和Excel管理方式已经无法满足现代药品管理的需求。一个完善的药品管理系统需要解决以下几个核心痛点:
首先,药品信息管理混乱的问题。在传统管理模式下,药品的基本信息(如名称、规格、生产厂家、批号等)、库存数量、有效期等关键数据分散在不同表格中,难以实现统一管理和实时更新。我曾在一家社区医院见到药剂师需要同时翻看3个不同的Excel文件才能确认某药品的完整信息,效率极低且容易出错。
其次,库存管理缺乏预警机制。药品过期和短缺是医药机构面临的常见问题。没有系统化的预警功能,工作人员难以及时发现近效期药品或库存不足的品种。根据中国药学会的统计,约23%的医疗机构曾因库存管理不善导致药品过期报废。
第三,业务流程不规范。从采购入库到处方发药,涉及多个环节和岗位,手工操作容易出现差错。某三甲医院的调研数据显示,引入信息化系统后,发药差错率从0.15%降至0.02%。
针对这些痛点,本系统设计目标包括:
- 实现药品基础信息的数字化管理
- 建立完善的库存预警机制(低库存、近效期)
- 规范采购、入库、处方、发药等核心业务流程
- 提供多维度的数据统计和分析功能
2. 系统架构设计
2.1 技术选型与开发环境
本系统采用Python作为主要开发语言,具体技术栈如下:
后端技术:
- 语言:Python 3.8+
- Web框架:Django 3.2(选择理由:自带ORM、Admin后台、完善的文档和社区支持)
- 数据库:MySQL 5.7(关系型数据库适合处理药品的复杂关联数据)
- 缓存:Redis(用于高频访问的药品目录缓存)
前端技术:
- 基础框架:HTML5 + CSS3 + JavaScript
- UI库:Bootstrap 5(响应式设计适配不同设备)
- 图表库:ECharts(用于数据可视化展示)
开发工具:
- IDE:PyCharm Professional(对Django有很好的支持)
- 版本控制:Git + GitHub
- 接口测试:Postman
提示:Django框架的选择特别考虑了毕业设计的实际情况。相比Flask等微框架,Django自带的管理后台(Admin)可以快速构建基础CRUD功能,让学生把更多精力放在业务逻辑的实现上。
2.2 系统模块划分
系统采用经典的MVC架构,主要功能模块包括:
-
基础信息管理模块
- 药品信息管理(分类、规格、厂家等)
- 供应商管理
- 员工账号管理
-
库存管理模块
- 入库管理(采购入库、退货入库)
- 出库管理(处方发药、报损出库)
- 库存盘点
- 库存预警(低库存、近效期)
-
业务处理模块
- 处方管理
- 发药管理
- 退药处理
-
统计报表模块
- 药品销量统计
- 库存周转分析
- 效期预警报表
2.3 数据库设计
核心表结构设计如下(部分):
药品信息表(medicine)
sql复制CREATE TABLE `medicine` (
`id` int NOT NULL AUTO_INCREMENT,
`code` varchar(20) NOT NULL COMMENT '药品编码',
`name` varchar(100) NOT NULL COMMENT '通用名称',
`spec` varchar(50) NOT NULL COMMENT '规格',
`unit` varchar(10) NOT NULL COMMENT '单位',
`manufacturer` varchar(200) DEFAULT NULL COMMENT '生产厂家',
`category_id` int DEFAULT NULL COMMENT '分类ID',
`price` decimal(10,2) NOT NULL COMMENT '单价',
`stock` int NOT NULL DEFAULT '0' COMMENT '库存数量',
`warning_stock` int DEFAULT NULL COMMENT '库存预警值',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态(1正常 0停用)',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
库存流水表(stock_record)
sql复制CREATE TABLE `stock_record` (
`id` int NOT NULL AUTO_INCREMENT,
`medicine_id` int NOT NULL COMMENT '药品ID',
`batch_number` varchar(50) NOT NULL COMMENT '批号',
`production_date` date NOT NULL COMMENT '生产日期',
`expiry_date` date NOT NULL COMMENT '有效期至',
`quantity` int NOT NULL COMMENT '数量',
`type` tinyint NOT NULL COMMENT '类型(1入库 2出库)',
`related_id` int DEFAULT NULL COMMENT '关联业务ID',
`operator_id` int NOT NULL COMMENT '操作人',
`operation_time` datetime NOT NULL COMMENT '操作时间',
`remark` varchar(200) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `idx_medicine` (`medicine_id`),
KEY `idx_expiry` (`expiry_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现
3.1 药品信息管理
药品信息是系统的基础数据,我们采用Django的ModelForm来实现高效的CRUD操作。关键实现代码如下:
models.py
python复制from django.db import models
class MedicineCategory(models.Model):
name = models.CharField(max_length=50, verbose_name="分类名称")
parent = models.ForeignKey('self', null=True, blank=True,
on_delete=models.SET_NULL, verbose_name="父分类")
class Meta:
verbose_name = "药品分类"
verbose_name_plural = verbose_name
class Medicine(models.Model):
STATUS_CHOICES = (
(1, '正常'),
(0, '停用')
)
code = models.CharField(max_length=20, unique=True, verbose_name="药品编码")
name = models.CharField(max_length=100, verbose_name="通用名称")
spec = models.CharField(max_length=50, verbose_name="规格")
unit = models.CharField(max_length=10, verbose_name="单位")
manufacturer = models.CharField(max_length=200, blank=True, null=True, verbose_name="生产厂家")
category = models.ForeignKey(MedicineCategory, on_delete=models.SET_NULL,
null=True, verbose_name="分类")
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="单价")
stock = models.IntegerField(default=0, verbose_name="库存数量")
warning_stock = models.IntegerField(null=True, blank=True, verbose_name="库存预警值")
status = models.IntegerField(choices=STATUS_CHOICES, default=1, verbose_name="状态")
class Meta:
verbose_name = "药品信息"
verbose_name_plural = verbose_name
ordering = ['code']
def __str__(self):
return f"{self.name} {self.spec}"
admin.py
python复制from django.contrib import admin
from .models import Medicine, MedicineCategory
class MedicineAdmin(admin.ModelAdmin):
list_display = ('code', 'name', 'spec', 'unit', 'manufacturer', 'stock', 'status')
list_filter = ('category', 'status')
search_fields = ('code', 'name', 'manufacturer')
list_per_page = 20
admin.site.register(Medicine, MedicineAdmin)
admin.site.register(MedicineCategory)
注意:在实际项目中,药品编码应该遵循国家统一的编码规则(如医保编码),而不是简单的自增ID。这里为了简化示例使用了自定义编码字段。
3.2 库存预警功能
库存预警是药品管理的核心功能之一,主要包括低库存预警和近效期预警。我们通过Django的signals和定时任务来实现自动预警。
低库存预警实现:
python复制from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import send_mail
from .models import Medicine
@receiver(post_save, sender=Medicine)
def check_low_stock(sender, instance, **kwargs):
if instance.warning_stock and instance.stock < instance.warning_stock:
subject = f'药品低库存预警:{instance.name} {instance.spec}'
message = f'''药品编码:{instance.code}
药品名称:{instance.name} {instance.spec}
当前库存:{instance.stock}
预警阈值:{instance.warning_stock}
请及时补充库存!'''
send_mail(
subject,
message,
'system@example.com',
['pharmacy@example.com'],
fail_silently=False
)
近效期预警实现(使用Celery定时任务):
python复制from celery import shared_task
from django.utils import timezone
from datetime import timedelta
from .models import StockRecord
@shared_task
def check_expiry_medicine():
# 检查30天内将过期的药品
warning_date = timezone.now().date() + timedelta(days=30)
expiring = StockRecord.objects.filter(
expiry_date__lte=warning_date,
quantity__gt=0
).select_related('medicine').distinct('medicine_id')
if expiring.exists():
medicines = [f"{item.medicine.name} {item.medicine.spec} (批号:{item.batch_number}, 效期:{item.expiry_date})"
for item in expiring]
subject = '近效期药品预警通知'
message = '以下药品将在30天内过期:\n\n' + '\n'.join(medicines)
send_mail(
subject,
message,
'system@example.com',
['pharmacy@example.com'],
fail_silently=False
)
3.3 处方发药流程
处方发药是药品管理系统中最关键的业务流程,需要确保库存扣减的原子性和数据一致性。我们使用Django的transaction.atomic来保证事务完整性。
views.py
python复制from django.db import transaction
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import Medicine, StockRecord, Prescription
from .serializers import PrescriptionSerializer
class PrescriptionDispenseView(APIView):
def post(self, request, pk):
prescription = get_object_or_404(Prescription, pk=pk)
if prescription.status != 'approved':
return Response({'error': '处方未审核通过'}, status=400)
try:
with transaction.atomic():
# 锁定处方记录
locked_prescription = Prescription.objects.select_for_update().get(pk=pk)
if locked_prescription.status == 'dispensed':
return Response({'error': '处方已发药'}, status=400)
# 处理处方明细
for item in locked_prescription.items.all():
medicine = item.medicine
# 检查库存是否充足
if medicine.stock < item.quantity:
raise Exception(f'{medicine.name}库存不足')
# 扣减库存 (先进先出原则)
stock_records = StockRecord.objects.filter(
medicine=medicine,
quantity__gt=0
).order_by('expiry_date')
remaining = item.quantity
for record in stock_records:
if remaining <= 0:
break
deduct = min(remaining, record.quantity)
record.quantity -= deduct
record.save()
remaining -= deduct
# 记录出库流水
StockRecord.objects.create(
medicine=medicine,
batch_number=record.batch_number,
production_date=record.production_date,
expiry_date=record.expiry_date,
quantity=-deduct,
type=2, # 出库
related_id=locked_prescription.id,
operator_id=request.user.id,
operation_time=timezone.now()
)
if remaining > 0:
raise Exception(f'{medicine.name}库存不足')
# 更新药品总库存
medicine.stock -= item.quantity
medicine.save()
# 更新处方状态
locked_prescription.status = 'dispensed'
locked_prescription.dispense_time = timezone.now()
locked_prescription.dispenser = request.user
locked_prescription.save()
return Response({'success': True})
except Exception as e:
return Response({'error': str(e)}, status=400)
4. 系统部署与优化
4.1 生产环境部署
对于毕业设计项目,可以采用相对简单的部署方案:
-
服务器准备:
- 操作系统:Ubuntu 20.04 LTS
- Web服务器:Nginx(处理静态文件并反向代理到Django)
- WSGI服务器:Gunicorn
- 数据库:MySQL 5.7
-
部署步骤:
bash复制# 安装依赖
sudo apt update
sudo apt install python3-pip python3-dev libmysqlclient-dev nginx
# 创建虚拟环境
python3 -m venv venv
source venv/bin/activate
# 安装Python依赖
pip install -r requirements.txt
# 配置Gunicorn
gunicorn --bind 0.0.0.0:8000 project.wsgi:application
# Nginx配置示例
"""
server {
listen 80;
server_name your_domain.com;
location /static/ {
alias /path/to/your/static/files;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
"""
4.2 性能优化建议
-
数据库优化:
- 为常用查询字段添加索引(如药品编码、药品名称、效期等)
- 使用select_related和prefetch_related减少查询次数
- 对大数据量表考虑分表策略
-
缓存策略:
- 使用Redis缓存药品目录等高频访问但变化不频繁的数据
- 实现页面级缓存对静态内容较多的页面
-
前端优化:
- 使用Bootstrap的按需加载
- 实现前端分页减少单次请求数据量
- 对ECharts图表数据采用异步加载
4.3 安全注意事项
-
基础安全措施:
- 使用Django内置的CSRF防护
- 对所有用户输入进行验证和转义
- 实现严格的权限控制(Django的permission系统)
-
敏感数据保护:
- 数据库连接密码等敏感信息使用环境变量存储
- 用户密码必须加密存储(Django默认使用PBKDF2算法)
- 关键操作记录详细日志
-
API安全:
- 对REST API实施认证(如JWT)
- 对敏感接口实施频率限制
- 使用HTTPS加密传输
5. 毕业设计扩展建议
对于希望进一步提升项目水平的同学,可以考虑以下扩展方向:
-
条码/RFID支持:
- 集成条码扫描功能,实现药品快速入库和发药
- 使用Python的pyzbar库处理条码识别
-
移动端应用:
- 开发基于Flutter或React Native的移动端应用
- 实现库存盘点、药品查询等移动功能
-
数据分析扩展:
- 使用Pandas和Matplotlib进行药品销售分析
- 实现ABC分类分析指导库存管理
-
微服务重构:
- 将系统拆分为多个微服务(如库存服务、处方服务等)
- 使用Django REST framework构建API
- 引入消息队列处理异步任务
-
机器学习应用:
- 基于历史数据预测药品需求
- 实现智能补货建议功能
在实现这些扩展功能时,建议先完善核心功能并确保其稳定性。毕业设计答辩时,可以重点展示1-2个有特色的扩展功能,并详细说明技术实现方案。
