1. 项目概述:Django电影购票系统开发实录
去年接手的一个影院管理系统项目让我对Django框架的实战应用有了全新认识。这个基于Python的电影购票系统不仅需要处理常规的CRUD操作,还要应对高并发选座、支付对账、排片冲突检测等业务场景的特殊需求。下面我将从技术选型到功能实现的完整过程进行拆解,并分享在开发过程中积累的实战经验。
2. 系统架构设计
2.1 技术栈选型考量
选择Django作为主要框架基于以下关键因素:
- 自带Admin后台适合快速构建管理系统
- ORM层对复杂业务关系的处理能力
- 完善的用户认证和权限控制机制
- 丰富的第三方插件生态
数据库选用MySQL 8.0而非SQLite的原因:
- 需要处理影院、影厅、场次的多级关联查询
- 事务操作要求(如座位锁定)
- 后期可能的分库分表需求
前端采用Bootstrap+jQuery组合而非Vue/React:
- 后台管理系统对SPA需求不强
- 需要快速实现响应式布局
- 与Django模板引擎配合更直接
2.2 核心数据模型设计
python复制class Cinema(models.Model):
name = models.CharField(max_length=100)
address = models.TextField()
contact = models.CharField(max_length=20)
class Hall(models.Model):
cinema = models.ForeignKey(Cinema, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
seat_layout = models.JSONField() # 存储座位矩阵
class Movie(models.Model):
title = models.CharField(max_length=200)
duration = models.IntegerField() # 分钟为单位
cover = models.ImageField(upload_to='covers/')
class Screening(models.Model):
movie = models.ForeignKey(Movie, on_delete=models.CASCADE)
hall = models.ForeignKey(Hall, on_delete=models.CASCADE)
start_time = models.DateTimeField()
end_time = models.DateTimeField()
price = models.DecimalField(max_digits=6, decimal_places=2)
关键设计点:使用JSONField存储影厅座位布局,便于动态生成选座界面
3. 核心功能实现
3.1 选座锁定机制
采用Redis+数据库事务的双重保障:
- 用户选择座位时,先向Redis写入锁定记录(设置15分钟过期)
- 在数据库事务中检查座位可用性
- 创建订单后删除Redis锁
python复制def lock_seats(request):
with transaction.atomic():
screening = Screening.objects.select_for_update().get(pk=request.POST['screening_id'])
seats = json.loads(request.POST['seats'])
# 检查座位是否可用
occupied = OrderItem.objects.filter(
screening=screening,
seat_row__in=[s[0] for s in seats],
seat_col__in=[s[1] for s in seats]
).exists()
if not occupied:
# 创建订单逻辑
...
return JsonResponse({'status': 'success'})
return JsonResponse({'status': 'seat_taken'}, status=400)
3.2 排片冲突检测
在管理员添加场次时自动检测:
- 同一影厅的时间段不能重叠
- 影片间隔至少15分钟用于清洁
- 特殊时段(如凌晨)不排片
python复制class ScreeningForm(forms.ModelForm):
def clean(self):
data = super().clean()
start = data['start_time']
end = data['end_time']
conflicts = Screening.objects.filter(
hall=data['hall'],
start_time__lt=end,
end_time__gt=start
).exclude(pk=self.instance.pk if self.instance else None)
if conflicts.exists():
raise forms.ValidationError("该时段已有排片冲突")
return data
4. 支付系统集成
4.1 支付流程设计
- 前端生成订单后获取支付参数
- 跳转至支付网关完成支付
- 异步通知处理订单状态
- 定时任务补偿对账
4.2 支付状态机实现
python复制class Order(models.Model):
PAYMENT_STATUS = (
('pending', '待支付'),
('paid', '已支付'),
('expired', '已过期'),
('refunded', '已退款')
)
status = models.CharField(max_length=10, choices=PAYMENT_STATUS, default='pending')
payment_time = models.DateTimeField(null=True)
expire_time = models.DateTimeField()
def can_pay(self):
return self.status == 'pending' and timezone.now() < self.expire_time
@transition(field=status, source='pending', target='paid')
def mark_paid(self):
self.payment_time = timezone.now()
5. 性能优化实践
5.1 查询优化方案
- 使用
select_related和prefetch_related减少查询次数 - 对热门场次数据添加缓存
- 分页查询使用
Paginator类
5.2 缓存策略设计
python复制from django.core.cache import cache
def get_movie_list():
key = 'hot_movies'
movies = cache.get(key)
if not movies:
movies = Movie.objects.filter(is_hot=True)[:10]
cache.set(key, movies, timeout=3600)
return movies
6. 安全防护措施
6.1 常见攻击防护
- CSRF防护:启用Django中间件
- XSS防护:模板自动转义
- SQL注入:使用ORM参数化查询
- 暴力破解:django-axes插件限制登录尝试
6.2 敏感数据保护
python复制from django.db import models
from django_cryptography.fields import encrypt
class PaymentInfo(models.Model):
card_number = encrypt(models.CharField(max_length=20))
expiry_date = encrypt(models.CharField(max_length=5))
7. 部署上线经验
7.1 生产环境配置
- 使用Gunicorn+Nginx组合
- 配置静态文件收集
- 设置合理的数据库连接池
- 日志分割和监控
7.2 典型部署问题
- 静态文件404:检查
STATIC_ROOT和Nginx配置 - 数据库连接耗尽:优化
CONN_MAX_AGE - 时区不一致:统一设置为UTC
- 内存泄漏:使用
django-debug-toolbar排查
8. 源码结构说明
项目采用标准Django项目结构:
code复制movie_tickets/
├── apps/
│ ├── cinema/
│ ├── booking/
│ └── payment/
├── static/
├── templates/
├── manage.py
└── requirements.txt
关键功能模块划分:
- cinema:影院、影厅、影片管理
- booking:场次查询、选座下单
- payment:支付对接和订单处理
9. 扩展开发建议
- 增加会员积分系统
- 实现退改签功能
- 接入第三方票务平台
- 开发小程序端入口
- 加入推荐算法模块
在实现这个系统的过程中,最大的收获是对Django ORM高级用法的掌握。特别是使用select_for_update()处理并发选座时,需要特别注意事务的隔离级别设置。另一个经验是支付系统的异步通知处理一定要做好幂等设计,我们曾经因为重复通知处理导致订单状态异常。