1. 项目概述:基于Python与Vue的演唱会票务系统
去年帮本地音乐节开发票务系统时,我深刻体会到传统售票平台的痛点:黄牛脚本抢票导致普通用户根本买不到票、座位图加载缓慢引发用户流失、支付超时后库存释放不同步造成超卖。这套采用Python+Django+Vue.js的解决方案,最终实现了每秒3000+订单的稳定处理,移动端购票转化率提升47%。
系统采用前后端分离架构,后端使用Django REST framework构建API服务,前端基于Vue3+TypeScript开发。这种组合既能利用Python快速开发业务逻辑的优势,又能通过Vue的响应式特性打造流畅的用户体验。特别在座位选择场景,我们通过Canvas动态渲染实现了演唱会场的3D视角选座,这是传统jQuery方案难以实现的。
2. 技术架构设计
2.1 后端技术栈选型
为什么选择Django而非Flask?在票务系统这种业务逻辑复杂的场景,Django自带的Admin后台、ORM系统和认证模块能节省30%以上的开发时间。我们特别重写了Django的缓存机制:
python复制# 自定义票务缓存装饰器
def ticket_lock(func):
@wraps(func)
def wrapper(session_id, *args, **kwargs):
cache_key = f"lock_{session_id}"
with cache.lock(cache_key, timeout=10):
if cache.get(cache_key):
raise ConcurrentModificationError
cache.set(cache_key, 1, 10)
try:
return func(session_id, *args, **kwargs)
finally:
cache.delete(cache_key)
return wrapper
数据库采用PostgreSQL+Redis组合:
- PostgreSQL的SKIP LOCKED特性完美解决高并发下的座位冲突
- Redis除了常规缓存,还用于:
- 分布式锁(防超卖)
- 异步任务队列(Celery broker)
- 实时在线人数统计(HyperLogLog)
2.2 前端技术方案
Vue3的组合式API让复杂状态管理变得清晰。下面是核心的购物车状态管理:
typescript复制// useCartStore.ts
export const useCartStore = defineStore('cart', () => {
const items = ref<CartItem[]>([])
const total = computed(() => items.value.reduce((sum, item) => sum + item.price, 0))
const addItem = (seat: Seat) => {
if (items.value.some(i => i.seatId === seat.id)) return
items.value.push({
seatId: seat.id,
price: seat.price,
section: seat.section
})
localStorage.setSync('cart', items.value)
}
return { items, total, addItem }
})
性能优化关键点:
- 使用Virtual List渲染演出列表(1万+数据仍流畅)
- Web Worker处理座位图的碰撞检测
- 支付倒计时使用WebSocket实时同步
3. 核心功能实现
3.1 高并发座位处理
票务系统最关键的库存管理采用预扣库存模式:
python复制# 座位预留逻辑
def reserve_seats(show_id, seat_ids, user_id):
with transaction.atomic():
# SKIP LOCKED跳过已被锁定的记录
seats = Seat.objects.select_for_update(
skip_locked=True
).filter(
show_id=show_id,
id__in=seat_ids,
status=SeatStatus.AVAILABLE
)
if len(seats) != len(seat_ids):
raise SeatNotAvailable
expiration = timezone.now() + timedelta(minutes=15)
for seat in seats:
seat.status = SeatStatus.RESERVED
seat.reserved_until = expiration
seat.save()
Order.objects.create(
user_id=user_id,
show_id=show_id,
status=OrderStatus.PENDING,
expires_at=expiration
)
重要提示:必须配合数据库事务隔离级别设为REPEATABLE READ,避免幻读导致超卖
3.2 支付系统集成
支付宝/微信支付对接有三个关键陷阱:
- 支付结果异步通知要做签名验证
- 需要自己维护本地交易状态
- 掉单后要有对账机制
我们的解决方案:
python复制@api_view(['POST'])
def payment_callback(request):
# 验证签名
if not verify_alipay_signature(request.data):
return Response(status=400)
order = Order.objects.get(out_trade_no=request.data['out_trade_no'])
if order.status != OrderStatus.PAID:
with transaction.atomic():
order.status = OrderStatus.PAID
order.paid_at = timezone.now()
order.save()
# 关联座位状态变更
Seat.objects.filter(
show=order.show,
id__in=order.seat_ids
).update(status=SeatStatus.SOLD)
return Response({'status': 'success'})
4. 性能优化实战
4.1 缓存策略设计
采用多级缓存架构:
- 热点数据(如热门演出信息)→ Redis
- 静态资源 → CDN
- 数据库查询 → Django缓存框架+手动缓存
python复制# 演出详情页缓存示例
@cache_page(60 * 15)
@cache_control(public=True, max_age=3600)
def show_detail(request, show_id):
show = cache.get_or_set(
f'show_{show_id}',
lambda: get_object_or_404(Show, pk=show_id),
60 * 10
)
return render(request, 'show_detail.html', {'show': show})
4.2 数据库优化
关键索引设计:
sql复制CREATE INDEX idx_show_seat_status ON seats (show_id, status) WHERE status IN ('available', 'reserved');
CREATE INDEX idx_order_user ON orders (user_id, created_at);
5. 安全防护方案
5.1 防自动化攻击
- 图形验证码:在登录/注册环节使用
- 行为验证:购票环节引入滑动拼图验证
- 频率限制:使用django-ratelimit
python复制from ratelimit.decorators import ratelimit
@ratelimit(key='ip', rate='5/m', block=True)
def add_to_cart(request):
# 购物车逻辑
5.2 数据安全
- 敏感字段加密:用户手机号等使用django-fernet-fields
- 日志脱敏:自定义日志过滤器
- SQL注入防护:坚持使用ORM或参数化查询
6. 部署架构
生产环境推荐配置:
code复制前端服务:Nginx + Vue静态资源(Docker容器)
后端服务:Gunicorn + Django(K8s Pod)
数据库:PostgreSQL主从集群(AWS RDS)
缓存:Redis Cluster(ElastiCache)
监控:Prometheus + Grafana(全链路监控)
Docker-compose开发环境示例:
yaml复制version: '3'
services:
redis:
image: redis:alpine
ports: ["6379:6379"]
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: mysecretpassword
volumes:
- pgdata:/var/lib/postgresql/data
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- db
- redis
7. 踩坑实录
-
座位状态同步问题
初期直接更新数据库导致超卖,后来引入Redis分布式锁+数据库乐观锁双保险 -
支付回调丢失
未处理网络抖动导致掉单,增加每日对账任务:python复制@periodic_task(run_every=crontab(minute=0, hour=3)) def reconcile_orders(): unpaid = Order.objects.filter( status=OrderStatus.PENDING, expires_at__lt=timezone.now() ) for order in unpaid: release_seats(order) -
移动端适配
Vant组件库在iOS上有样式问题,最终通过postcss-px-to-viewport方案解决
这套系统经过三次大型音乐节实战检验,峰值QPS达到4200的情况下保持稳定运行。关键经验是:在高并发场景下,宁可牺牲少量一致性也要保证可用性,比如我们的座位状态更新采用最终一致性而非强一致性。