1. 项目概述:基于Python与Vue的药品购买系统开发
药品购买系统作为医疗健康领域的典型应用,需要兼顾严格的业务合规性和良好的用户体验。采用Python+Django/Flask作为后端、Vue.js作为前端的技术组合,既能满足复杂业务逻辑的实现,又能提供现代化的交互界面。这个技术栈的选择背后有几个关键考量:
首先,Python在数据处理和快速原型开发方面具有显著优势,Django的全功能特性适合构建包含药品管理、订单处理、用户权限等复杂模块的系统,而Flask的轻量级特性则便于快速实现API服务。Vue.js的响应式特性和组件化开发模式,特别适合构建需要频繁交互的药品展示与购物流程界面。
在实际开发环境中,PyCharm作为专业的Python IDE,提供了完善的代码提示、调试和数据库工具链支持,能显著提升开发效率。而VSCode配合Vue插件则是前端开发的利器,两者结合可以构建完整的开发工作流。
2. 技术栈选型与配置指南
2.1 Python环境搭建与关键依赖
推荐使用Python 3.8+版本,这个版本在性能优化和异步支持方面都有显著改进。通过pyenv或conda管理多版本Python环境是专业开发者的常见做法:
bash复制# 使用conda创建隔离环境
conda create -n pharmacy python=3.8
conda activate pharmacy
# 核心依赖安装
pip install django==4.2 flask==2.3
对于药品系统特别重要的依赖包括:
django-allauth:用于用户认证和第三方登录集成django-filter:实现药品的复杂筛选功能reportlab:生成药品购买收据和处方单django-crispy-forms:快速构建表单界面
注意:药品系统涉及敏感数据,务必使用最新安全版本的框架,并定期更新依赖包。我曾遇到过因使用旧版Django导致SQL注入漏洞的案例,更新后问题立即解决。
2.2 Vue.js环境配置与优化
现代Vue开发推荐使用Vite作为构建工具,它能显著提升开发服务器的启动速度:
bash复制npm create vite@latest pharmacy-frontend --template vue
cd pharmacy-frontend
npm install vue-router@4 pinia@2 axios
关键配置优化点:
- 在vite.config.js中配置代理,解决开发环境跨域问题
- 使用
unplugin-auto-import自动导入Vue API,减少样板代码 - 为药品图片等静态资源配置CDN路径
实际项目中,我推荐安装以下Vue开发者工具:
- Vue Devtools:调试组件状态和事件
- Volar:Vue的VSCode语言支持插件
- ESLint + Prettier:保持代码风格一致
2.3 数据库选型与Django ORM配置
药品系统的数据模型需要特别关注以下几个方面:
- 药品信息的标准化(遵循国家药品编码规则)
- 库存变化的原子性操作
- 处方数据的完整性约束
Django的模型定义示例:
python复制class Medicine(models.Model):
NATIONAL_CODE_LENGTH = 10
name = models.CharField(max_length=100)
national_code = models.CharField(
max_length=NATIONAL_CODE_LENGTH,
unique=True,
validators=[RegexValidator(r'^\d{10}$')]
)
specification = models.CharField(max_length=200)
manufacturer = models.ForeignKey('Manufacturer', on_delete=models.PROTECT)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField(default=0)
requires_prescription = models.BooleanField(default=False)
class Meta:
indexes = [
models.Index(fields=['name']),
models.Index(fields=['national_code']),
]
对于高并发的药品查询操作,建议:
- 使用
select_related和prefetch_related优化关联查询 - 对热门药品数据添加缓存装饰器
- 使用Django的
F()表达式处理库存的原子更新
3. 核心功能模块实现
3.1 药品目录与搜索系统
药品搜索需要支持多种维度:
- 通用名/商品名模糊匹配
- 适应症关键词检索
- 价格区间筛选
- 处方/非处方药分类
Vue前端实现示例:
javascript复制// 在Pinia store中定义搜索逻辑
export const useMedicineStore = defineStore('medicine', {
actions: {
async searchMedicines(params) {
const { keywords, minPrice, maxPrice, requiresRx } = params
try {
const response = await api.get('/medicines/', {
params: {
search: keywords,
price_gt: minPrice,
price_lt: maxPrice,
prescription: requiresRx
}
})
this.searchResults = response.data
} catch (error) {
this.handleApiError(error)
}
}
}
})
Django后端对应的视图优化技巧:
python复制from django_filters.rest_framework import DjangoFilterBackend
class MedicineFilter(django_filters.FilterSet):
price_gt = django_filters.NumberFilter(field_name='price', lookup_expr='gt')
price_lt = django_filters.NumberFilter(field_name='price', lookup_expr='lt')
class Meta:
model = Medicine
fields = ['requires_prescription']
class MedicineListView(generics.ListAPIView):
queryset = Medicine.objects.select_related('manufacturer')
serializer_class = MedicineSerializer
filter_backends = [filters.SearchFilter, DjangoFilterBackend]
search_fields = ['name', 'specification', 'manufacturer__name']
filterset_class = MedicineFilter
def get_queryset(self):
queryset = super().get_queryset()
# 添加自定义排序逻辑
if 'popular' in self.request.query_params:
return queryset.order_by('-sales_volume')
return queryset
3.2 购物车与处方验证系统
药品购物车需要特殊处理:
- 处方药必须关联有效的电子处方
- 库存实时校验防止超卖
- 医保结算信息采集
Flask实现示例:
python复制@app.route('/api/cart/checkout', methods=['POST'])
@login_required
def checkout():
cart_items = request.json.get('items')
prescription_id = request.json.get('prescription_id')
# 验证处方有效性
if any(item['requires_prescription'] for item in cart_items):
if not validate_prescription(prescription_id, current_user.id):
abort(400, description="无效处方")
# 使用数据库事务处理订单
try:
with db.session.begin():
order = create_order(current_user.id, cart_items)
reduce_inventory(cart_items)
if prescription_id:
mark_prescription_used(prescription_id)
return jsonify(order.to_dict())
except InventoryShortage as e:
abort(409, description=str(e))
前端需要处理的特殊场景:
- 处方药添加购物车时的额外验证
- 库存变化的实时通知
- 医保个人账户支付流程
3.3 订单处理与物流跟踪
药品订单的特殊要求:
- 敏感药品需要特殊包装
- 冷链药品的温度记录
- 处方药的签收验证
Django Admin的定制示例:
python复制@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ('order_number', 'user', 'status', 'total_amount')
list_filter = ('status', 'created_at')
actions = ['export_prescription_orders']
def export_prescription_orders(self, request, queryset):
# 实现处方订单导出逻辑
pass
def get_queryset(self, request):
qs = super().get_queryset(request)
if not request.user.is_superuser:
return qs.filter(pharmacy=request.user.pharmacy)
return qs
4. 部署与运维实践
4.1 云服务器部署方案
药品系统的部署需要考虑:
- 医疗数据合规性要求
- 高可用性保障
- 敏感数据加密
使用Nginx+Gunicorn部署Django的配置要点:
nginx复制# 药品API服务配置
upstream pharmacy_api {
server 127.0.0.1:8000;
keepalive 32;
}
server {
listen 443 ssl;
server_name api.pharmacy.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://pharmacy_api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# 静态药品图片资源
location /media/ {
alias /var/www/pharmacy/media/;
expires 30d;
add_header Cache-Control "public";
}
}
4.2 安全防护措施
医疗系统必须实施的安全措施:
- 数据传输全程HTTPS加密
- 用户密码加盐哈希存储
- 敏感操作日志审计
- 定期漏洞扫描
Django的安全配置示例:
python复制# settings.py
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000 # 1 year
# 密码哈希配置
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
]
# 审计日志配置
LOGGING = {
'version': 1,
'handlers': {
'audit_file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/pharmacy/audit.log',
'formatter': 'audit',
},
},
'loggers': {
'audit': {
'handlers': ['audit_file'],
'level': 'INFO',
'propagate': False,
},
},
}
4.3 性能监控与优化
药品系统性能关键指标:
- 药品搜索响应时间
- 订单提交成功率
- 高并发下的库存准确性
使用Prometheus+Grafana监控方案:
- 安装Django Prometheus:
bash复制pip install django-prometheus
- 配置中间件:
python复制MIDDLEWARE = [
'django_prometheus.middleware.PrometheusBeforeMiddleware',
# ...其他中间件...
'django_prometheus.middleware.PrometheusAfterMiddleware',
]
INSTALLED_APPS += ['django_prometheus']
- 自定义业务指标:
python复制from prometheus_client import Counter
MEDICINE_SEARCH_REQUESTS = Counter(
'pharmacy_medicine_search_total',
'Total medicine search requests',
['search_type']
)
class MedicineSearchView(APIView):
def get(self, request):
search_type = request.query_params.get('type', 'simple')
MEDICINE_SEARCH_REQUESTS.labels(search_type=search_type).inc()
# ...搜索逻辑...
5. 开发经验与避坑指南
5.1 药品数据标准化处理
在整合药品数据时,最常见的坑是不同来源的药品规格表述不一致。我们建立了规格解析器:
python复制import re
def parse_specification(spec):
"""
解析药品规格字符串,返回标准化字典
示例输入:"24片/盒,每片0.5g"
输出:{'unit': '片', 'per_unit': '0.5g', 'total': 24, 'package': '盒'}
"""
patterns = [
r'(?P<total>\d+)(?P<unit>[片粒支])\s*/\s*(?P<package>[盒瓶袋])',
r'每(?P<unit>[片粒支])(?P<per_unit>[\d.]+[m]?g)',
]
result = {}
for pattern in patterns:
match = re.search(pattern, spec)
if match:
result.update(match.groupdict())
return result
这个处理函数帮助我们统一了来自12个供应商的药品数据格式,后续的库存管理和价格计算都基于标准化数据。
5.2 库存并发控制方案
药品超卖是电商系统的常见问题,我们最终采用了三种防护措施:
- 数据库层面使用SELECT FOR UPDATE锁:
python复制with transaction.atomic():
medicine = Medicine.objects.select_for_update().get(pk=medicine_id)
if medicine.stock >= quantity:
medicine.stock -= quantity
medicine.save()
- Redis分布式锁防止集群环境下的竞争:
python复制from redis import Redis
from contextlib import contextmanager
@contextmanager
def redis_lock(lock_name, timeout=10):
redis = Redis()
try:
acquired = redis.set(lock_name, '1', nx=True, ex=timeout)
if not acquired:
raise InventoryShortage("操作过于频繁,请稍后再试")
yield
finally:
redis.delete(lock_name)
- 最终一致性检查的异步任务:
python复制@app.task
def verify_inventory_consistency():
orders = Order.objects.filter(status='PAID')
for order in orders:
actual = get_actual_inventory(order.medicine_id)
if actual < 0:
notify_admin_inventory_discrepancy(order)
5.3 处方图片识别优化
处理用户上传的处方照片时,我们经历了从简单裁剪到智能识别的演进:
-
初始方案:直接存储原图
- 问题:图片体积大,加载慢
-
改进方案:使用Pillow压缩
python复制from PIL import Image
def compress_prescription(image_path):
with Image.open(image_path) as img:
img.save(image_path, quality=70, optimize=True)
- 当前方案:OpenCV识别关键区域
python复制import cv2
import numpy as np
def extract_prescription_area(image_path):
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 边缘检测和轮廓查找逻辑...
return processed_image
最终我们的处方图片体积减少了80%,同时通过边界增强提高了OCR识别准确率。
