1. 项目概述:基于Vue与Python的在线考试系统
去年为某职业培训机构开发在线考试系统时,我深刻体会到传统考试方式的痛点:组卷耗时、阅卷压力大、数据分析困难。这套基于Vue+Django的解决方案最终将组卷时间从2小时压缩到10分钟,自动阅卷准确率达98%。下面分享从架构设计到具体实现的完整过程。
系统采用前后端分离架构,前端使用Vue 3组合式API开发响应式界面,后端选用Django REST framework构建高可用API服务。特别在组卷算法和防作弊机制上做了深度优化,支持200人同时在线考试时服务器负载保持在30%以下。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 前端技术栈解析
选择Vue 3而非React的主要考虑:
- 组合式API更适合复杂考试逻辑的状态管理
- TypeScript支持完善,减少题型配置时的类型错误
- 体积更小(生产环境打包后仅20KB核心代码)
实际开发中采用的技术组合:
bash复制npm create vue@latest
# 选择配置:TypeScript + Pinia + Router + ESLint
关键依赖版本控制(避免兼容性问题):
json复制"dependencies": {
"vue": "^3.3.0",
"element-plus": "^2.3.0",
"axios": "^1.3.0",
"vue-router": "^4.2.0"
}
踩坑提示:Element Plus的按需导入需要额外配置unplugin-vue-components,否则打包体积会增大300%
2.2 后端框架对比决策
Django vs Flask的选型对比表:
| 特性 | Django | Flask |
|---|---|---|
| 开发速度 | 快速(自带Admin) | 需手动搭建 |
| 性能 | 中等(QPS 2000) | 更高(QPS 3500) |
| ORM | 内置强大 | 需SQLAlchemy |
| 适用场景 | 需要快速成型的管理系统 | 高并发API服务 |
最终选择Django的核心原因:
- 自带Admin后台:5分钟即可搭建试题管理界面
- DRF序列化器:自动处理试题数据的JSON转换
- MVT架构:天然支持组卷策略的模式设计
3. 核心模块实现细节
3.1 智能组卷算法实现
组卷策略采用权重分配+随机筛选的混合算法:
python复制# exams/utils.py
def generate_paper(strategy):
questions = []
# 按知识点权重筛选
for point in strategy['points']:
qs = Question.objects.filter(
knowledge_point=point['id'],
difficulty__lte=point['max_difficulty']
).order_by('?')[:point['count']]
questions.extend(qs)
# 补全数量差异
if len(questions) < strategy['total']:
remaining = strategy['total'] - len(questions)
qs = Question.objects.exclude(
id__in=[q.id for q in questions]
).order_by('?')[:remaining]
questions.extend(qs)
return questions
参数说明:
strategy: 包含知识点分布、难度系数、题目数量的JSON配置order_by('?'): Django的随机排序,确保每次组卷不同
3.2 实时防作弊方案
前端监控代码示例:
javascript复制// src/utils/examMonitor.js
let blurCount = 0;
window.addEventListener('blur', () => {
if (document.hidden) {
blurCount++;
ElNotification.warning(`已检测到离开考试页面${blurCount}次`);
if (blurCount > 3) {
submitPaper('force_submit');
}
}
});
// 禁止右键和F12
document.addEventListener('contextmenu', e => e.preventDefault());
document.addEventListener('keydown', e => {
if (e.key === 'F12') e.preventDefault();
});
后端配合措施:
- 题目乱序API:
/api/questions?random=true&seed=用户ID - 答案提交频率限制:RateLimit 10次/分钟
- 行为日志分析:记录异常操作时间点
4. 性能优化实战记录
4.1 数据库查询优化
原始查询(N+1问题):
python复制# 错误示例
papers = Paper.objects.all()
for p in papers:
print(p.questions.all()) # 每次循环都查询数据库
优化方案:
python复制# 使用select_related和prefetch_related
papers = Paper.objects.prefetch_related(
Prefetch('questions',
queryset=Question.objects.select_related('knowledge_point'))
).all()
效果对比:
- 试卷列表页查询时间从1200ms → 200ms
- 内存占用减少40%
4.2 前端懒加载策略
题型组件动态加载配置:
javascript复制// router/index.js
{
path: '/exam/:id',
component: () => import('../views/ExamView.vue'),
children: [
{
path: 'single',
component: () => import('../components/questions/SingleChoice.vue')
},
// 其他题型...
]
}
配合Webpack分块:
javascript复制// vite.config.js
build: {
rollupOptions: {
output: {
manualChunks: {
editor: ['@wangeditor/editor'],
chart: ['echarts']
}
}
}
}
5. 部署与运维要点
5.1 生产环境配置
Nginx关键配置:
nginx复制location /api {
proxy_pass http://backend;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 300s;
proxy_read_timeout 300s; # 考试长连接需要
}
location / {
root /var/www/exam-frontend;
try_files $uri $uri/ /index.html;
expires 1d; # 静态资源缓存
}
Django安全设置:
python复制# settings.py
SECURE_HSTS_SECONDS = 31536000
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = 'Lax'
5.2 高并发应对方案
压力测试结果(JMeter模拟):
- 500并发时:平均响应时间<1.5s
- 故障点:MySQL连接池耗尽
最终解决方案:
- 增加数据库连接池:
python复制'OPTIONS': { 'pool_size': 50, 'max_overflow': 100 } - 引入Redis缓存:
python复制CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', } } }
6. 典型问题排查手册
6.1 跨域问题深度解决
除了常规CORS配置,特别注意:
- 开发环境代理配置(vite.config.js):
javascript复制server: {
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
- 生产环境Nginx特殊头设置:
nginx复制add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-[Agent](https://taotoken.net?utm_source=general),X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
6.2 文件上传内存溢出
症状:上传10MB以上的试题图片导致服务器崩溃
解决方案:
- 前端限制:
html复制<input type="file" accept="image/*" @change="checkFile" />
<script>
const checkFile = (e) => {
if (e.target.files[0].size > 5 * 1024 * 1024) {
alert('文件需小于5MB');
e.target.value = '';
}
}
</script>
- 后端流式处理:
python复制# Django view
def upload_image(request):
with tempfile.NamedTemporaryFile() as tmp:
for chunk in request.FILES['image'].chunks():
tmp.write(chunk)
# 处理文件...
7. 扩展功能开发建议
7.1 语音题型的实现
Web Audio API录音方案:
javascript复制// 录音组件
const startRecording = async () => {
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = e => {
chunks.push(e.data);
};
mediaRecorder.start();
};
// Django后端处理
class AudioQuestionViewSet(ModelViewSet):
parser_classes = [FileUploadParser]
@action(detail=False, methods=['POST'])
def upload(self, request):
audio_file = request.FILES['audio']
# 保存到云存储或本地
7.2 智能阅卷增强
使用NLP处理简答题:
python复制# 安装:pip install transformers
from transformers import pipeline
grader = pipeline('text-classification',
model='uer/roberta-base-finetuned-dianping-chinese')
def score_answer(answer, standard):
result = grader(f"{standard}[SEP]{answer}")
return result['score'] * 10 # 转换为10分制
效果评估:
- 中文简答题评分准确率:82%
- 需配合关键词匹配作为fallback
这套系统经过三个版本的迭代,目前已在5家教育机构稳定运行。最大的收获是:技术方案必须服务于业务场景,比如最初追求完美的算法反而导致组卷速度下降,后来改用"80%准确率+人工调整"的模式更实用。
