Flask+SQLite+OpenCV高校考试系统实战部署指南
简介本资源是一套完整的智慧校园考试系统毕业设计项目基于Python开发面向计算机专业本科生、初阶开发者及小型技术团队解决在线考试场景下的题库管理、试卷生成、考生监考与成绩分析等核心需求。压缩包共6386个文件体量46.85MB包含1791个Python源码文件实现业务逻辑与Web后端、1113个po/mo本地化翻译文件支持多语言考试界面、141个HTML模板及111个JavaScript脚本构建前端交互另有CSS、SVG、PNG等资源支撑UI渲染以及xlsx、json等格式的配置与测试数据。已有236人学习下载资源附带完整开发文档说明涵盖系统架构图、模块功能说明、数据库设计与部署指南代码结构清晰、注释规范适合作为毕设参考模板、Python全栈开发学习范例或轻量级考试系统二次开发基础。1. 这不是又一个“学生管理系统”而是一套能真实跑在教务处服务器上的考试闭环工具很多计算机专业同学拿到“智慧校园考试系统”这个毕业设计选题时第一反应是不就是个带登录的 CRUD 网站加个试卷生成、在线答题、自动阅卷——听起来很全但部署到学院机房一试就崩并发 50 人卡顿、监考端无法实时抓屏、成绩导出 Excel 格式错乱、教师端批量导入题库失败……问题不在功能少而在没把“考试”这件事当生产级业务来设计。这个.zip包里的 Python 源代码核心价值不是“有登录注册”而是用Flask SQLite OpenCV pdfplumber四件套在无云服务、无中间件、仅靠一台 Windows Server 或 Ubuntu 物理机的前提下实现考场本地化部署、离线题库加载、摄像头行为识别监考、结构化试卷解析与多维度成绩归档。它面向的是真实高校场景机房网络不稳定、教师不熟悉命令行、考试时间窗口固定且不可重试。所以本文不讲 Django REST API 设计只拆解如何让这套代码在你导师办公室那台老 Dell 电脑上从解压到监考上线不超过 20 分钟。2. 用 Flask SQLite 构建轻量但可靠的考试服务骨架2.1 为什么不用 Django 或 FastAPI——毕业设计场景下的技术选型逻辑毕业设计系统不是 SaaS 产品首要约束是可交付性和零依赖部署。Django 自带 admin 后台虽好但manage.py migrate在 Windows 教务处电脑上常因权限/路径问题失败FastAPI 的异步模型对监考端实时视频流反而增加复杂度。而 Flask 的app.run(host0.0.0.0, port5000, debugFalse)一行启动配合 SQLite 单文件数据库整个系统可压缩进一个目录双击start.batWindows或./start.shLinux即运行。查看源码根目录下requirements.txt关键依赖仅 7 行Flask2.3.3 Werkzeug2.3.7 Jinja23.1.2 pdfplumber0.10.2 opencv-python4.8.1.78 Pillow10.0.1 openpyxl3.1.2提示pdfplumber用于解析教师上传的 PDF 试卷非扫描版opencv-python用于监考端调用 USB 摄像头做简单动作检测如长时间低头、离开座位openpyxl替代xlrd处理新版 Excel 成绩模板——这三者是区别于普通“学生信息管理”的硬性能力点。2.2 数据库设计直击考试业务本质三张表撑起全流程打开database.dbSQLite 文件用 DB Browser for SQLite 打开可见仅 3 张表但覆盖全部考试生命周期表名字段关键作用说明examsid,title,start_time,duration_min,status(draft/active/closed)考试主实体status控制前端按钮显隐如“开始考试”仅在draft时可见questionsid,exam_id,content,options,answer,score,type(single/multi/text)题目表options存 JSON 字符串[A. xxx, B. yyy]避免多对一关联表复杂度submissionsid,student_id,exam_id,answers,submit_time,score,ip_address提交记录answers存 JSON{q1:A,q2:[A,C],q3:答案文本}支持单选/多选/简答混合注意submissions.answers不拆成子表是因为毕业设计需快速验证逻辑。真实项目中若需统计每题正确率再按exam_idquestion_id建索引即可此处CREATE INDEX idx_exam_q ON submissions(exam_id, answers);已在init_db.py中预置。2.3 启动服务前必须做的三件事端口、静态资源、管理员账号解压后进入目录执行前务必检查修改config.py中的监听地址默认HOST 127.0.0.1只允许本机访问。教务处需多台监考机连接必须改为HOST 0.0.0.0 # 允许局域网内所有设备访问 PORT 5000 # 若 5000 被占用改为此处任意空闲端口如 8080确认static/目录结构完整static/css/,static/js/,static/uploads/必须存在。其中uploads/是教师上传 PDF 试卷的存放目录首次运行前需手动创建并赋予权限# Linux/macOS mkdir -p static/uploads chmod 755 static/uploads:: Windows mkdir static\uploads初始化管理员账号仅首次运行运行python init_admin.py源码中已提供输入用户名密码后账号写入database.db的users表该表仅存管理员无学生注册功能。后续登录页/login即可使用。3. 实现监考端实时行为识别OpenCV 调用本地摄像头的最小可行方案3.1 不用 YOLO用 Haar Cascade 做轻量级姿态检测毕业设计无需高精度人体关键点识别。源码中monitor/camera_monitor.py采用 OpenCV 内置的haarcascade_frontalface_default.xml已内置在static/haarcascades/通过人脸持续丢失时长判断考生异常import cv2 import time def detect_absence(video_source0, timeout_sec15): cap cv2.VideoCapture(video_source) face_cascade cv2.CascadeClassifier(static/haarcascades/haarcascade_frontalface_default.xml) last_face_time time.time() while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale(gray, 1.1, 4) if len(faces) 0: last_face_time time.time() # 重置计时器 # 绘制人脸框调试用 for (x, y, w, h) in faces: cv2.rectangle(frame, (x, y), (xw, yh), (0, 255, 0), 2) else: # 无脸状态持续超时触发告警 if time.time() - last_face_time timeout_sec: print(f[ALERT] 考生 {get_student_id()} 画面丢失超 {timeout_sec} 秒) send_alert_to_teacher() # 调用 Flask 接口推送消息 break cv2.imshow(Monitor, frame) if cv2.waitKey(1) 0xFF ord(q): # 按 q 退出 break cap.release() cv2.destroyAllWindows()逻辑说明timeout_sec15是可调参数对应监考规则“考生离座不得超过 15 秒”。send_alert_to_teacher()实际调用requests.post(http://localhost:5000/api/alert, json{...})由 Flask 后端存入alerts表并推送到教师端 WebSocket。此方案 CPU 占用低于 15%远低于 YOLOv5 的 60%适配老旧监考机。3.2 教师端监考看板WebSocket 实现实时状态同步教师登录后访问/monitor页面加载monitor.js建立 WebSocket 连接// monitor.js const ws new WebSocket(ws://${window.location.host}/ws/monitor); ws.onmessage function(event) { const data JSON.parse(event.data); if (data.type absence_alert) { // 弹窗提示 页面高亮考生区域 document.getElementById(alert-box).innerHTML div classalert⚠️ ${data.student_name} 离座超时/div; document.querySelector([data-student${data.student_id}]).style.border 3px solid red; } }; // 发送心跳维持连接 setInterval(() ws.send(JSON.stringify({type: heartbeat})), 30000);后端app.py中启用 Flask-SocketIO已在requirements.txt列出from flask_socketio import SocketIO socketio SocketIO(app, cors_allowed_origins*) # 允许跨域监考机可能不同域名 socketio.on(connect) def handle_connect(): emit(status, {msg: 监考连接成功}) def broadcast_alert(student_id, student_name): socketio.emit(alert, { type: absence_alert, student_id: student_id, student_name: student_name, timestamp: datetime.now().isoformat() })参数说明cors_allowed_origins*解决监考机浏览器同源策略限制emit()发送事件名必须与前端onmessage中data.type严格一致broadcast_alert()被camera_monitor.py中的send_alert_to_teacher()调用形成闭环。3.3 关键避坑摄像头权限与多实例冲突在 Windows 教务处电脑上常见问题摄像头被其他程序占用cv2.VideoCapture(0)返回False。解决方案在camera_monitor.py开头添加设备探测def find_working_camera(): for i in range(10): # 尝试前 10 个设备号 cap cv2.VideoCapture(i) if cap.isOpened(): cap.release() return i return None多监考机同时连接导致端口冲突教师端/monitor页面默认连接ws://localhost:5000/ws/monitor若监考机 IP 非localhost需在页面 JS 中动态获取const wsUrl ws://${window.location.hostname}:5000/ws/monitor;4. 试卷解析与成绩归档pdfplumber 解析 PDF openpyxl 导出结构化数据4.1 教师上传 PDF 试卷的格式要求与自动校验系统不支持扫描版 PDF需 OCR仅处理文字可复制的 PDF如 Word 导出、LaTeX 编译生成。上传后upload_handler.py执行三重校验文件头校验读取前 4 字节是否为%PDF页数限制pdfplumber.open(file).pages长度 ≤ 20防大文件阻塞题目结构识别正则匹配^\d\.\s题号点或^[A-Z]\.\s选项import pdfplumber def parse_pdf_questions(pdf_path): questions [] with pdfplumber.open(pdf_path) as pdf: for page in pdf.pages: text page.extract_text() if not text: continue # 按题号分割例1. 什么是Python blocks re.split(r\n(?\d\.\s), text) for block in blocks: if not block.strip(): continue # 提取题干、选项、答案标识如 (A) q_match re.match(r^(\d\.\s)(.?)(?\n[A-Z]\.\s|\n$), block, re.DOTALL) if q_match: content q_match.group(2).strip() options re.findall(r\n([A-Z])\.\s(.?)(?\n[A-Z]\.\s|\n$), block) # 答案逻辑查找 (A) 或 答案A answer_match re.search(r答案[:]\s*([A-Z]), block) or re.search(r\(([A-Z])\), block) answer answer_match.group(1) if answer_match else None questions.append({ content: content, options: [f{o[0]}. {o[1]} for o in options], answer: answer, type: single if answer else text }) return questions注意re.DOTALL让.匹配换行符确保题干跨行也能捕获options提取时保留字母前缀A. xxx避免后续渲染错位answer字段为空时设为text类型进入人工批阅队列。4.2 成绩导出 Excel 的字段映射与样式控制教师点击“导出成绩”触发export_results.py生成符合教务处要求的 Excel列名来源字段样式要求学号submissions.student_id加粗居中姓名students.name关联查表居中总分submissions.score数值保留 1 位小数客观题得分submissions.score_obj计算字段蓝色字体主观题得分submissions.score_subj计算字段红色字体提交时间submissions.submit_timeYYYY-MM-DD HH:MM:SS格式from openpyxl import Workbook from openpyxl.styles import Font, Alignment, PatternFill def export_to_excel(submission_list, filename): wb Workbook() ws wb.active ws.title 考试成绩 # 表头 headers [学号, 姓名, 总分, 客观题得分, 主观题得分, 提交时间] for col, header in enumerate(headers, 1): cell ws.cell(row1, columncol, valueheader) cell.font Font(boldTrue) cell.alignment Alignment(horizontalcenter) # 数据行 for row, sub in enumerate(submission_list, 2): ws.cell(rowrow, column1, valuesub[student_id]).font Font(boldTrue) ws.cell(rowrow, column2, valuesub[name]).alignment Alignment(horizontalcenter) ws.cell(rowrow, column3, valuefloat(sub[score])).number_format 0.0 ws.cell(rowrow, column4, valuefloat(sub[score_obj])).font Font(color0000FF) ws.cell(rowrow, column5, valuefloat(sub[score_subj])).font Font(colorFF0000) ws.cell(rowrow, column6, valuesub[submit_time]).number_format yyyy-mm-dd hh:mm:ss wb.save(filename)提示number_format控制数字显示格式避免 Excel 自动转科学计数法Font(color0000FF)使用十六进制颜色码比blue更稳定导出文件名含时间戳f成绩_{datetime.now().strftime(%Y%m%d_%H%M%S)}.xlsx防止覆盖。5. 毕业设计答辩必答三问从代码到部署的实战应答技巧5.1 “为什么用 SQLite 而不用 MySQL”——聚焦场景拒绝技术炫技答辩时老师常问此问题。标准回答不是罗列 SQLite 优点而是绑定具体约束“本系统定位为院系级离线考试工具部署环境是教务处一台 Windows Server 2012 物理机无专职运维人员。MySQL 需额外安装服务、配置 root 密码、开放 3306 端口而 SQLite 仅需一个.db文件备份时直接复制文件即可。我们测试过 200 人并发提交SQLite 的 WAL 模式已在init_db.py中启用PRAGMA journal_modeWAL;写入延迟稳定在 80ms 内满足考试场景‘提交即存’要求。”附实测数据在test_concurrent_submit.py中模拟 100 线程提交平均响应时间 76ms错误率 0%。5.2 “监考摄像头识别准确率多少”——用可验证指标代替模糊表述不要说“基本准确”要给出可复现的测试方法“我们在 3 种典型环境测试① 教室日光灯下亮度 300lux识别率 92%② 临窗侧光亮度 800lux识别率 85%③ 晚自习台灯直射亮度 150lux识别率 78%。测试方法录制 10 分钟考生视频人工标注‘离座’时刻点对比系统告警时间戳误差 ≤3 秒记为正确。代码中timeout_sec参数可由教师根据考场光线调节这是比‘准确率’更实用的设计。”提供test_accuracy.py脚本输入视频路径和标注 CSVtimestamp,action自动计算 TP/TN/FP/FN。5.3 “如何保证考试过程防作弊”——分层防御不依赖单一技术系统未采用人脸识别需大量样本训练而是组合策略层级措施技术实现前端禁用右键、F12、截图快捷键document.addEventListener(contextmenu, e e.preventDefault())window.addEventListener(keydown, e {if(e.keyF12) e.preventDefault()})网络防多开浏览器提交时校验session.get(student_id)与request.headers.get(User-Agent)组合唯一性重复提交返回 403服务端防答案泄露试卷 JSON 通过json.dumps(questions, separators(,, :))压缩传输禁用浏览器 DevTools 查看原始数据关键技巧在templates/student/exam.html中题目内容用{{ question|safe }}渲染但question.content已在后端做过 HTML 转义html.escape()既防 XSS 又保格式。此细节常被忽略却是答辩时体现安全意识的加分项。本文还有配套的精品资源点击获取