基于Django与Flask的滑雪场雪具租赁系统开发实践

1. 项目概述:滑雪场雪具租赁系统的技术实现

滑雪场雪具租赁系统是一个典型的B/S架构业务管理系统,主要解决滑雪场在雪具租赁业务中面临的手工登记效率低、库存管理混乱、财务统计困难等问题。这个系统需要实现会员管理、雪具库存管理、租赁订单处理、费用结算等核心功能模块。

作为Python全栈开发者,我们选择Django作为主要开发框架,Flask作为辅助微服务框架。这种技术组合既能利用Django强大的ORM和Admin后台快速搭建核心业务系统,又能通过Flask的灵活性实现特定的API接口和定制功能。

提示:在实际滑雪场运营中,雪具租赁高峰期往往集中在上午9-11点,系统需要具备高并发处理能力,这是技术选型的重要考量因素。

2. 系统架构设计

2.1 技术栈选型分析

我们采用分层架构设计,主要技术组件包括:

  • 前端层:Bootstrap + jQuery实现响应式界面
  • 业务逻辑层:Django作为主框架,处理核心业务流程
  • 数据访问层:Django ORM + PostgreSQL
  • API服务层:Flask实现特定微服务
  • 缓存层:Redis用于热点数据缓存

选择Django的主要原因:

  1. 自带Admin后台,可快速搭建管理界面
  2. 完善的ORM支持,简化数据库操作
  3. 内置用户认证系统,节省开发时间
  4. 模板引擎易于前端开发人员协作

引入Flask的考虑因素:

  1. 某些特殊接口需要更灵活的请求处理
  2. 部分统计功能需要定制化的响应格式
  3. 未来可能对接第三方服务的扩展性

2.2 数据库设计要点

雪具租赁系统的核心数据模型包括:

class Equipment(models.Model): CATEGORY_CHOICES = [ ('SKI', '滑雪板'), ('BOOT', '滑雪靴'), ('POLE', '滑雪杖'), ('CLOTHES', '滑雪服'), ('GOGGLE', '雪镜'), ] name = models.CharField(max_length=100) category = models.CharField(max_length=20, choices=CATEGORY_CHOICES) size = models.CharField(max_length=50) status = models.CharField(max_length=20) # 可用、租赁中、维修中 purchase_date = models.DateField() last_maintenance = models.DateField() price_per_hour = models.DecimalField(max_digits=6, decimal_places=2) class Customer(models.Model): name = models.CharField(max_length=100) phone = models.CharField(max_length=20) id_number = models.CharField(max_length=30) # 身份证/护照号 membership_level = models.IntegerField(default=1) registration_date = models.DateTimeField(auto_now_add=True) class RentalOrder(models.Model): customer = models.ForeignKey(Customer, on_delete=models.PROTECT) equipment = models.ForeignKey(Equipment, on_delete=models.PROTECT) start_time = models.DateTimeField() end_time = models.DateTimeField(null=True, blank=True) actual_return_time = models.DateTimeField(null=True, blank=True) deposit = models.DecimalField(max_digits=8, decimal_places=2) total_cost = models.DecimalField(max_digits=8, decimal_places=2, null=True) status = models.CharField(max_length=20) # 进行中、已完成、已取消

注意:在实际部署时,Equipment表的status字段需要建立索引,因为这是高频查询条件。

3. 核心功能实现细节

3.1 租赁业务流程实现

租赁业务的核心流程包括:客户登记、雪具选择、押金支付、租赁开始、租赁结束、费用结算。我们使用Django的类视图实现这一流程:

class RentalProcessView(LoginRequiredMixin, View): def get(self, request): # 显示可用雪具列表 available_equipments = Equipment.objects.filter(status='可用') return render(request, 'rental/select_equipment.html', {'equipments': available_equipments}) def post(self, request): # 处理租赁请求 equipment_id = request.POST.get('equipment_id') customer_id = request.POST.get('customer_id') deposit = Decimal(request.POST.get('deposit')) try: with transaction.atomic(): equipment = Equipment.objects.select_for_update().get( pk=equipment_id, status='可用') customer = Customer.objects.get(pk=customer_id) # 创建租赁订单 order = RentalOrder.objects.create( customer=customer, equipment=equipment, start_time=timezone.now(), deposit=deposit, status='进行中' ) # 更新雪具状态 equipment.status = '租赁中' equipment.save() # 记录操作日志 LogEntry.objects.log_action( user_id=request.user.id, content_type_id=ContentType.objects.get_for_model(order).pk, object_id=order.id, object_repr=str(order), action_flag=ADDITION, change_message='创建租赁订单' ) return JsonResponse({'status': 'success', 'order_id': order.id}) except Exception as e: return JsonResponse({'status': 'error', 'message': str(e)})

3.2 库存管理模块

雪具库存管理需要特别关注以下几点:

  1. 雪具的维护周期管理
  2. 不同类别雪具的库存预警
  3. 雪具使用频率统计

我们实现了一个自动维护提醒功能:

from django.db.models import F, ExpressionWrapper, fields from django.db.models.functions import Now class MaintenanceCheckView(View): def get(self, request): # 找出超过30天未维护的雪具 threshold = timezone.now() - timedelta(days=30) equipments_needing_maintenance = Equipment.objects.annotate( days_since_maintenance=ExpressionWrapper( Now() - F('last_maintenance'), output_field=fields.DurationField() ) ).filter( days_since_maintenance__gt=timedelta(days=30) ).exclude(status='维修中') return render(request, 'equipment/maintenance_list.html', {'equipments': equipments_needing_maintenance})

3.3 费用计算服务

由于费用计算规则可能频繁变更,我们使用Flask实现了一个独立的计算微服务:

from flask import Flask, request, jsonify from datetime import datetime app = Flask(__name__) @app.route('/calculate', methods=['POST']) def calculate_cost(): data = request.json start_time = datetime.fromisoformat(data['start_time']) end_time = datetime.fromisoformat(data['end_time']) price_per_hour = float(data['price_per_hour']) duration_hours = (end_time - start_time).total_seconds() / 3600 # 不足1小时按1小时计算 duration_hours = max(1, math.ceil(duration_hours)) # 会员折扣规则 discount = 1.0 if data.get('membership_level') == 2: discount = 0.9 # 银卡会员9折 elif data.get('membership_level') == 3: discount = 0.8 # 金卡会员8折 total_cost = duration_hours * price_per_hour * discount return jsonify({ 'original_cost': duration_hours * price_per_hour, 'discount_rate': discount, 'total_cost': round(total_cost, 2), 'duration_hours': duration_hours }) if __name__ == '__main__': app.run(port=5001)

4. 性能优化与高并发处理

4.1 缓存策略实现

滑雪场在节假日会出现租赁高峰,我们采用多级缓存策略:

  1. Redis缓存热点数据:雪具列表、价格信息等
  2. Django缓存框架:缓存常用查询结果
  3. 数据库连接池:使用django-db-geventpool优化数据库连接

配置示例:

CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', 'MAX_ENTRIES': 1000, 'CULL_FREQUENCY': 10, } } } # 使用缓存装饰器 from django.views.decorators.cache import cache_page @cache_page(60 * 15) # 缓存15分钟 def equipment_list(request): equipments = Equipment.objects.filter(status='可用').select_related('category') return render(request, 'equipment/list.html', {'equipments': equipments})

4.2 异步任务处理

对于耗时的操作如报表生成、数据导出等,我们使用Celery实现异步处理:

from celery import shared_task from django.core.mail import send_mail @shared_task def generate_daily_report(): from_date = timezone.now() - timedelta(days=1) report_data = RentalOrder.objects.filter( start_time__gte=from_date ).aggregate( total_orders=Count('id'), total_revenue=Sum('total_cost'), avg_duration=Avg(F('end_time') - F('start_time')) ) # 生成PDF报表 pdf_file = generate_pdf_report(report_data) # 发送邮件 send_mail( '每日租赁报表', '请查收附件中的每日报表', 'noreply@skiresort.com', ['manager@skiresort.com'], attachments=[('daily_report.pdf', pdf_file, 'application/pdf')] )

5. 安全防护措施

5.1 支付安全实现

租赁系统涉及押金支付和费用结算,我们采取以下安全措施:

  1. 使用HTTPS加密所有通信
  2. 支付接口实现防重放攻击机制
  3. 敏感数据加密存储
  4. 实现完善的日志审计

支付签名验证示例:

import hmac from hashlib import sha256 def verify_payment_signature(data, secret_key): received_sign = data.pop('signature') # 按参数名排序后拼接 param_str = '&'.join(f'{k}={v}' for k,v in sorted(data.items())) expected_sign = hmac.new( secret_key.encode(), param_str.encode(), sha256 ).hexdigest() return hmac.compare_digest(received_sign, expected_sign)

5.2 权限控制设计

系统采用RBAC权限模型,主要角色包括:

  • 管理员:全系统权限
  • 前台员工:租赁业务操作
  • 仓库管理员:雪具维护管理
  • 财务人员:报表查看

权限控制实现:

from django.contrib.auth.mixins import PermissionRequiredMixin class FinancialReportView(PermissionRequiredMixin, View): permission_required = 'rental.view_financial_report' def get(self, request): # 财务报表查询逻辑 ...

6. 部署架构与运维

6.1 生产环境部署方案

我们采用Docker容器化部署,架构如下:

前端Nginx → Gunicorn(Django) → PostgreSQL ↓ Flask微服务 ↓ Redis

Docker-compose配置示例:

version: '3' services: web: build: . command: gunicorn ski_rental.wsgi:application --bind 0.0.0.0:8000 volumes: - .:/code ports: - "8000:8000" depends_on: - redis - db - calc_service calc_service: build: ./calc_service command: python app.py ports: - "5001:5001" db: image: postgres:13 environment: POSTGRES_PASSWORD: example volumes: - postgres_data:/var/lib/postgresql/data redis: image: redis:6 ports: - "6379:6379" volumes: - redis_data:/data volumes: postgres_data: redis_data:

6.2 监控与日志

我们使用Prometheus + Grafana实现系统监控,关键监控指标包括:

  1. 接口响应时间
  2. 数据库查询性能
  3. 系统负载
  4. 错误率

日志配置示例:

LOGGING = { 'version': 1, 'handlers': { 'file': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', 'filename': '/var/log/ski_rental/debug.log', 'maxBytes': 1024*1024*5, # 5MB 'backupCount': 5, }, 'console': { 'level': 'INFO', 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django': { 'handlers': ['file', 'console'], 'level': 'INFO', }, 'rental': { 'handlers': ['file'], 'level': 'DEBUG', }, }, }

7. 实际运营中的经验总结

在系统上线后,我们积累了一些宝贵经验:

  1. 雪具条码管理:为每件雪具配备防水条码标签,大幅提高出入库效率
  2. 租赁套餐设计:推出2小时、4小时、全天等套餐,减少结算争议
  3. 高峰期预案:在节假日提前增加服务器资源,优化数据库索引
  4. 移动端适配:开发简化版移动界面,方便工作人员在雪场现场处理业务

一个实用的雪具状态检查脚本:

def check_equipment_status(): """检查是否有雪具超期未归还""" overdue_rentals = RentalOrder.objects.filter( status='进行中', end_time__lt=timezone.now() ).select_related('equipment', 'customer') for rental in overdue_rentals: # 发送提醒通知 send_overdue_notification(rental) # 记录违约信息 CustomerPenalty.objects.create( customer=rental.customer, rental_order=rental, penalty_amount=calculate_penalty(rental), reason='超期未归还' )