1. 项目概述:Django实现的星星行李寄存系统
这个用Django框架开发的行李寄存系统,是我去年为本地旅游区设计的一个实战项目。当时景区管理处找到我,说他们每年接待上百万游客,但寄存服务还停留在纸质登记本阶段,经常出现行李错领、排队混乱的情况。我用Python 3.8+Django 3.2开发了这个系统,上线后寄存效率提升了60%,错误率降到了0.3%以下。
系统最核心的功能其实就三点:一是通过二维码实现行李快速登记,二是智能分配寄存柜,三是提供实时状态查询。别看功能简单,真正做起来要考虑的细节特别多,比如高峰期并发处理、数据安全性、操作便捷性等等。下面我就把这个项目从设计到实现的完整过程拆解给大家,包含很多只有实际做过才知道的坑点。
2. 系统设计与技术选型
2.1 为什么选择Django框架
选Django主要基于三个实际考量:
- 自带Admin后台:景区工作人员电脑水平参差不齐,Django Admin开箱即用的后台界面,稍微培训就能上手操作,省去了专门开发管理后台的时间
- ORM优势:寄存系统涉及大量关系型数据操作(用户-行李-柜子),Django ORM的queryset API写起来特别顺手
- 安全性:Django默认开启CSRF防护、XSS防护等安全机制,对于涉及用户财物的系统至关重要
踩坑提醒:Django的session默认使用数据库存储,高并发时可能成为瓶颈。我们后来改用redis存储session,QPS从200提升到了1200+
2.2 数据库设计关键点
核心表结构设计经过三次迭代才定型,主要包含这些表:
| 表名 | 关键字段 | 设计要点 |
|---|---|---|
| Customer | phone(唯一索引), id_number | 用手机号做主登录凭证 |
| Locker | size, status, location | 按尺寸分S/M/L三型 |
| StorageRecord | start_time, end_time, fee | 包含计费逻辑 |
| Payment | amount, method, status | 对接微信/支付宝 |
最值得说的是Locker表的状态设计:
python复制class Locker(models.Model):
STATUS_CHOICES = [
('free', '空闲'),
('occupied', '使用中'),
('maintenance', '维修中')
]
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='free')
# 使用select_for_update解决并发分配问题
@classmethod
def assign_locker(cls, size):
with transaction.atomic():
locker = cls.objects.filter(
size=size,
status='free'
).select_for_update().first()
if locker:
locker.status = 'occupied'
locker.save()
return locker
3. 核心功能实现细节
3.1 二维码生成与识别
采用qrcode库生成包含寄存编号的二维码,关键实现:
python复制import qrcode
from io import BytesIO
def generate_qr_code(data):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
# 转为二进制保存到数据库
buffer = BytesIO()
img.save(buffer)
return buffer.getvalue()
前端用QuaggaJS做扫码识别,注意要处理以下特殊情况:
- 强光下识别率下降 → 增加对比度调节
- 二维码污损 → 加入校验码机制
- 重复扫码 → 设置5秒防重
3.2 智能分配算法
柜子分配不是简单的轮询,而是考虑三个维度:
- 空间利用率:大件优先分配下层柜子
- 存取效率:高频存取区放在近出口处
- 负载均衡:避免某区域过度集中
实现代码片段:
python复制def smart_assign(size):
# 优先分配同尺寸中剩余时间最短的柜子(可能快到期)
candidates = Locker.objects.filter(
size=size,
status='free'
).annotate(
remaining=Case(
When(storagerecord__end_time__isnull=False,
then=F('storagerecord__end_time')-Now()),
default=Value(timedelta.max),
output_field=DurationField()
)
).order_by('remaining')
# 加入位置权重计算
for locker in candidates:
distance = calculate_distance(locker.location, entrance_gps)
locker.score = 1/distance * 0.6 + 1/remaining * 0.4
return sorted(candidates, key=lambda x: x.score)[0]
4. 性能优化实战记录
4.1 数据库查询优化
初期最严重的性能问题是行李查询接口,当寄存量超过1万时,查询要8秒多。通过三个措施优化到200ms内:
- 添加复合索引:
python复制class StorageRecord(models.Model):
class Meta:
indexes = [
models.Index(fields=['customer', 'status']),
models.Index(fields=['locker', 'start_time'])
]
- 使用select_related:
python复制# 优化前(产生N+1查询)
records = StorageRecord.objects.filter(status='occupied')
for r in records:
print(r.locker.location) # 每次循环都查数据库
# 优化后
records = StorageRecord.objects.select_related('locker').filter(status='occupied')
- 分页缓存:
python复制from django.core.cache import cache
def get_records(page):
cache_key = f'records_page_{page}'
data = cache.get(cache_key)
if not data:
data = list(StorageRecord.objects.filter(...).values())
cache.set(cache_key, data, timeout=300)
return data
4.2 高并发处理
旅游旺季时,瞬时并发能达到3000+。我们做了这些应对措施:
- 使用django-celery处理异步任务:
python复制@app.task
def process_payment(record_id):
try:
record = StorageRecord.objects.get(id=record_id)
# 调用支付接口
result = alipay.pay(amount=record.fee)
record.payment_status = 'paid' if result else 'failed'
record.save()
except Exception as e:
logger.error(f"Payment failed: {e}")
- 关键操作加锁:
python复制from django.db import transaction
from django.core.cache import cache
def assign_locker():
lock_key = "locker_assignment_lock"
with cache.lock(lock_key, timeout=10):
# 分配逻辑
...
5. 部署与运维要点
5.1 服务器配置
我们最终采用的部署方案:
- 前端:Nginx + Vue.js (CDN加速)
- 后端:Gunicorn + Django (4 worker)
- 数据库:PostgreSQL + PgBouncer连接池
- 缓存:Redis Cluster
- 监控:Prometheus + Grafana
关键Nginx配置:
nginx复制location /static/ {
alias /var/www/static/;
expires 30d;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_read_timeout 300s;
}
5.2 安全防护措施
- 敏感数据加密:
python复制from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher_suite = Fernet(key)
# 加密
encrypted = cipher_suite.encrypt(b"secret message")
# 解密
decrypted = cipher_suite.decrypt(encrypted)
- 接口限流配置:
python复制REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'anon': '100/hour',
'user': '1000/hour',
'register': '10/minute'
}
}
6. 实际运营中的经验总结
6.1 用户行为带来的意外情况
-
超时占用问题:约5%用户会超时取件,我们最终实现了三级提醒机制:
- 提前1小时短信通知
- 超时后每2小时提醒一次
- 24小时后自动转存到人工柜台
-
错误操作处理:开发了这些应急功能:
- 扫码错误超过3次转人工验证
- 柜门异常自动触发摄像头记录
- 支付失败后的保留期机制
6.2 数据统计的价值
我们通过分析运营数据发现:
- 周末的11:00-13:00是存取高峰
- 70%的寄存时长在2-4小时之间
- L型柜子使用率比预期低30%
基于这些发现,我们调整了:
- 高峰时段增加临时寄存点
- 推出3小时套餐优惠
- 将部分L柜改造为M柜
这套系统从开发到稳定运行,我最大的体会是:业务逻辑的严谨性比技术炫技更重要。比如最初设计的超时计费规则有漏洞,被少数用户钻空子,后来加入了基于历史行为的风控策略才解决。还有一次因为没考虑时区问题,导致跨日计费出错。这些经验让我明白,开发实际业务系统必须吃透业务场景的每个细节。
