1. 项目背景与需求分析
高校选课系统作为教务管理的重要环节,直接影响着教学秩序和学生学习体验。传统的人工选课方式存在诸多痛点:选课高峰期教务处排长队、课程容量难以实时更新、退改选流程繁琐等。我曾参与过某高校教务系统升级项目,亲眼目睹学生为抢热门课程凌晨排队的情形,这促使我思考如何用技术手段解决这些问题。
Python+Vue选课系统正是针对这些痛点设计的解决方案。系统采用B/S架构,将用户分为三类角色:
- 管理员:负责基础数据维护(院系/专业/班级)、课程审核、选课开关控制
- 教师:管理所授课程的学生名单、录入成绩、查看选课统计
- 学生:在线选课、退课、查看课表和成绩
实际开发中发现,选课冲突检测和并发控制是系统最核心的难点。当多个学生同时选择热门课程时,必须确保数据一致性和系统响应速度。
2. 技术选型与架构设计
2.1 前后端分离架构
系统采用典型的前后端分离架构:
code复制前端:Vue.js 2.x + Element UI + Axios
后端:Python 3.7 + Django REST framework/Flask
数据库:MySQL 5.7
开发工具:PyCharm + Navicat
选择Vue而非React/Angular的考虑:
- 学习曲线平缓,适合高校技术栈
- 双向数据绑定简化表单处理
- Element UI提供丰富的教务系统组件(课表、分页表格等)
2.2 Django与Flask的取舍
两种Python框架各有适用场景:
| 特性 | Django | Flask |
|---|---|---|
| 适用规模 | 中大型系统 | 小型快速开发 |
| 内置功能 | ORM/Admin俱全 | 需自行组装 |
| 灵活性 | 较低 | 极高 |
| 性能 | 中等 | 较高 |
本系统最终选择Django的原因:
- 自带Admin后台,快速实现管理功能
- ORM简化数据库操作,避免SQL注入
- 完善的权限管理系统(django-guardian)
2.3 数据库设计要点
核心表关系设计:
python复制class Course(models.Model):
name = models.CharField(max_length=50)
capacity = models.IntegerField() # 课程容量
# 其他字段...
class Selection(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
select_time = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=10) # 已选/待审核/已退
class Meta:
unique_together = ('student', 'course') # 防止重复选课
关键设计决策:
- 使用复合唯一约束防止重复选课
- 添加status字段实现选课审核流程
- 为course.capacity字段建立索引加速查询
3. 核心功能实现
3.1 选课并发控制
高并发选课场景需要解决两个问题:
- 超卖问题:课程容量被超额占用
- 性能瓶颈:数据库压力过大
解决方案:
python复制# views.py
@transaction.atomic # 开启事务
def select_course(request):
course = Course.objects.select_for_update().get(id=course_id) # 加行锁
if course.current_num >= course.capacity:
return JsonResponse({'code': 400, 'msg': '课程已满'})
# 检查时间冲突
conflicts = Selection.objects.filter(
student=request.user,
course__schedule__overlap=course.schedule # 使用PostgreSQL的range字段
).exists()
if not conflicts:
Selection.objects.create(
student=request.user,
course=course,
status='pending'
)
course.current_num += 1
course.save()
优化措施:
- 使用select_for_update()锁定课程记录
- 采用PostgreSQL的range类型处理时间冲突
- 添加Redis缓存热门课程信息
3.2 权限控制系统
基于Django的权限方案:
python复制# permissions.py
class IsTeacher(BasePermission):
def has_permission(self, request, view):
return request.user.role == 'teacher'
# views.py
@api_view(['POST'])
@permission_classes([IsTeacher])
def input_grade(request):
# 教师录入成绩逻辑
特殊处理:
- 学生只能看到自己的选课记录(添加queryset过滤)
- 教师只能管理自己教授的课程(重写get_queryset)
- 管理员操作记录审计(使用django-auditlog)
3.3 课表可视化
前端实现技巧:
vue复制<template>
<el-timetable
:events="courses"
@select="handleSelect"
/>
</template>
<script>
export default {
data() {
return {
courses: [{
id: 1,
title: 'Python编程',
start: 'Mon 08:00',
end: 'Mon 10:00',
color: '#409EFF'
}]
}
},
methods: {
handleSelect(event) {
this.$axios.post('/api/select/', {course_id: event.id})
}
}
}
</script>
4. 性能优化实践
4.1 数据库查询优化
常见性能陷阱及解决方案:
- N+1查询问题:
python复制# 错误示例(每次循环都查询数据库)
for selection in Selection.objects.all():
print(selection.course.name)
# 正确做法(使用select_related)
Selection.objects.select_related('course').all()
- 分页优化:
python复制# 不要使用count()获取总数(大数据量表很慢)
paginator = Paginator(queryset, 20)
page = paginator.page(1)
return Response({
'results': page.object_list,
'has_next': page.has_next()
})
4.2 缓存策略
三级缓存架构:
- 热点数据:Redis缓存课程余量
- 静态资源:CDN加速前端文件
- 浏览器缓存:ETag协商缓存
配置示例:
python复制CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
# 装饰器缓存视图
@cache_page(60 * 15)
def course_list(request):
...
5. 安全防护措施
5.1 常见攻击防护
- SQL注入:
- 始终使用ORM或参数化查询
- 禁止拼接SQL语句
- XSS攻击:
- 前端使用vue-sanitize过滤输入
- 后端设置Django的SECURE_BROWSER_XSS_FILTER=True
- CSRF防护:
- 确保Django的CsrfViewMiddleware启用
- 前端Axios配置withCredentials
5.2 敏感数据保护
- 密码存储:
python复制# 使用make_password加密
from django.contrib.auth.hashers import make_password
user.password = make_password('123456')
- 日志脱敏:
python复制LOGGING = {
'filters': {
'mask_password': {
'()': 'util.log.PasswordMaskFilter'
}
}
}
6. 部署实践
6.1 生产环境配置
推荐部署方案:
code复制前端:Nginx + Vue静态文件
后端:Django + Gunicorn + Nginx
数据库:MySQL主从复制
监控:Prometheus + Grafana
Docker部署示例:
dockerfile复制# backend/Dockerfile
FROM python:3.7
RUN pip install gunicorn
COPY requirements.txt .
RUN pip install -r requirements.txt
CMD ["gunicorn", "-w 4", "-b :8000", "core.wsgi"]
6.2 压力测试
使用Locust模拟选课高峰:
python复制from locust import HttpUser, task
class SelectionUser(HttpUser):
@task
def select_course(self):
self.client.post("/api/select/", json={
"course_id": 1
}, headers={"Authorization": "Bearer xxx"})
测试结果优化:
- 4核8G服务器可支持2000+并发
- 平均响应时间<1.5秒
- 数据库连接池大小建议20-30
7. 项目演进方向
- 智能推荐系统:
- 基于协同过滤算法推荐课程
- 考虑先修课程关联关系
- 微信小程序端:
- 使用uni-app跨平台开发
- 集成消息推送功能
- 微服务改造:
- 将选课、成绩等模块拆分为独立服务
- 使用Kubernetes管理容器
在项目开发过程中,最大的教训是要提前设计好数据一致性方案。我们曾遇到过选课数据异常的情况,最终通过引入数据库事务和分布式锁解决。建议在类似系统中,尽早考虑以下问题:
- 如何保证选课操作的原子性
- 如何处理分布式环境下的并发控制
- 如何设计数据补偿机制