1. 项目背景与需求分析
校园体育器材管理一直是后勤工作中的痛点。传统的手工登记方式效率低下,器材丢失率高,使用情况难以统计。我在某高校信息化部门工作时,曾亲眼目睹体育老师每周要花3小时手工核对器材出入库记录,学生借用流程更是繁琐到需要跑三个部门盖章。
这个Python开发的校园体育器材租赁管理系统,正是为了解决以下核心痛点:
- 流程电子化:将纸质登记转为线上预约,减少90%的人工操作
- 状态实时监控:通过扫码即可查看器材在位状态和使用记录
- 智能调度:根据课程安排和社团活动自动分配热门器材
- 数据分析:生成器材使用率报表指导采购决策
2. 技术选型与架构设计
2.1 为什么选择Django+Flask组合
这个项目采用了Django作为主框架,Flask负责API服务的混合架构,主要基于以下考虑:
-
Django优势:
- 自带Admin后台,30分钟就能搭建完整的数据管理界面
- ORM支持多数据库切换,我们测试过MySQL/PostgreSQL/SQLite
- 完善的Auth权限系统,开箱即用的用户分组功能
-
Flask的灵活性:
- 轻量级路由更适合微信小程序API开发
- 可以单独部署到高并发场景(如租赁高峰期)
- 与Django共享Redis缓存,Session数据互通
实际部署时,我们使用Nginx做负载均衡:
- /admin 路由指向Django服务
- /api 路由指向Flask服务
- 静态文件统一由Nginx处理
2.2 数据库设计要点
器材管理系统的ER图有这几个关键设计:
python复制class Equipment(models.Model):
# 使用Django的ChoiceField实现状态机
STATUS_CHOICES = [
('available', '可借用'),
('rented', '已出租'),
('maintenance', '维修中')
]
qr_code = models.CharField(max_length=100, unique=True) # 二维码标识
current_user = models.ForeignKey(User, null=True) # 当前使用者
class RentalOrder(models.Model):
# 使用复合索引提升查询效率
class Meta:
indexes = [
models.Index(fields=['user', 'status']),
models.Index(fields=['equipment', 'start_time'])
]
3. 核心功能实现细节
3.1 二维码全生命周期管理
我们为每件器材生成唯一二维码,包含三个版本:
- 静态二维码:存储基础信息(器材ID、名称)
- 动态加密二维码:包含时效性的JWT令牌(防篡改)
- 小程序码:微信专属格式,扫码直接跳转小程序
python复制# Flask中生成动态二维码的示例
@app.route('/generate_qr')
@jwt_required()
def generate_qr():
equipment_id = request.args.get('id')
payload = {
'exp': datetime.utcnow() + timedelta(hours=2),
'equipment_id': equipment_id,
'admin_id': get_jwt_identity()
}
token = jwt.encode(payload, current_app.config['SECRET_KEY'])
url = f"{current_app.config['QR_BASE_URL']}?token={token}"
return qrcode.make(url).get_image()
3.2 租赁状态机实现
器材流转涉及复杂状态变更,我们使用Python的transitions库实现:
python复制from transitions import Machine
class RentalState:
states = ['pending', 'approved', 'rejected', 'active', 'overdue', 'completed']
def __init__(self):
self.machine = Machine(
model=self,
states=self.states,
initial='pending'
)
# 定义状态转换规则
self.machine.add_transition(
trigger='approve',
source='pending',
dest='approved'
)
self.machine.add_transition(
trigger='reject',
source='pending',
dest='rejected'
)
4. 性能优化实践
4.1 缓存策略设计
针对高并发场景(如开学季集中借器材),我们采用三级缓存:
- 热点数据:Redis缓存器材实时状态(TTL 5秒)
- 列表数据:Memcached缓存分页查询结果(TTL 1分钟)
- 静态数据:Nginx缓存基础信息(TTL 1小时)
python复制# Django中使用缓存的示例
from django.core.cache import cache
def get_equipment_list():
key = "equipment_list_page_1"
result = cache.get(key)
if not result:
result = list(Equipment.objects.all()[:20].values())
cache.set(key, result, 60) # 60秒过期
return result
4.2 数据库查询优化
通过Django的select_related和prefetch_related减少查询次数:
python复制# 错误做法:产生N+1查询问题
orders = RentalOrder.objects.filter(status='active')
for order in orders:
print(order.equipment.name) # 每次循环都查询数据库
# 正确做法:一次性预加载关联数据
orders = RentalOrder.objects.select_related('equipment') \
.filter(status='active')
5. 安全防护方案
5.1 JWT安全增强措施
标准JWT实现存在安全隐患,我们增加了以下防护:
- 双Token机制:AccessToken(30分钟过期) + RefreshToken(7天过期)
- IP绑定:Token生成时记录用户IP,使用时校验
- 黑名单:注销的Token加入Redis黑名单
python复制# Flask中增强的JWT验证
@app.route('/protected')
@jwt_required()
def protected():
current_user = get_jwt_identity()
# 检查Token是否在黑名单
jti = get_jwt()['jti']
if redis.get(f"token_blacklist:{jti}"):
return {"msg": "Token revoked"}, 401
return {"user": current_user}
5.2 防刷单策略
针对可能的恶意刷单行为,系统实现了:
- 滑动窗口限流:同一用户5分钟内最多发起3次租赁
- 设备指纹识别:记录浏览器特征防止多账号作弊
- 行为验证码:关键操作需完成拼图验证
6. 部署与监控
6.1 Docker化部署
使用docker-compose编排服务:
yaml复制version: '3'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
django:
build: ./backend
environment:
- REDIS_URL=redis://redis:6379/0
depends_on:
- redis
flask:
build: ./api
ports:
- "5000:5000"
6.2 监控指标采集
使用Prometheus+Grafana监控关键指标:
- 器材平均借用时长
- 各时段并发租赁数
- 接口响应时间P99值
- 数据库查询耗时
7. 踩坑经验分享
7.1 微信小程序登录对接
微信官方文档有几个隐藏坑点:
- session_key有效期:实际是24小时(文档未明确说明)
- 用户信息解密:需要先获取手机号才能解密用户信息
- 二维码生成限制:单个小程序每日上限10万次
解决方案是使用服务端缓存session_key,并实现降级方案。
7.2 Django Admin定制陷阱
直接重写ModelAdmin的save_model会导致权限系统失效。正确做法是:
python复制class EquipmentAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if not change: # 新建记录时自动设置创建人
obj.created_by = request.user
super().save_model(request, obj, form, change) # 必须调用父类方法
8. 扩展方向建议
- 智能调度算法:根据历史数据预测器材需求高峰
- 损坏检测AI:用户归还时自动拍照检测器材状况
- 信用积分系统:建立用户租赁信用档案
- 物联网集成:通过RFID自动识别器材出入库
这个项目让我深刻体会到,好的管理系统不是功能越多越好,而是要精准解决用户的真实痛点。我们在第二版迭代时砍掉了50%的"看起来很酷"的功能,专注优化核心租赁流程,反而获得了更高的用户满意度