结对编程
学号:2452627 2452726
算法设计思路
一、整体项目架构设计
采用三层架构 + 前后端分离轻量化设计,基于 Flask + PyWebview + SQLite 开发,职责完全解耦:
1.启动入口层(main.py):依托 pywebview 将 Web 页面打包为桌面客户端,统一程序窗口大小、标题、运行入口,隐藏命令行,实现软件化体验。
2.路由控制层(app.py):Flask 核心路由,仅负责接收前端请求、参数校验、调用数据库层接口、返回页面 / JSON 数据,不写数据库操作与复杂业务逻辑。
3.数据持久层(database.py):集中封装全部 SQLite 数据库操作,包含题库、考生信息、考试记录、作答数据、成绩数据的增删改查,统一数据读写规范。
4.前端展示层(templates):4 个 HTML 页面分工承载管理员题库管理、考生信息录入、在线答题、成绩报告展示,搭配动画、倒计时、进度条、错题解析等交互效果。
二、核心业务算法设计
(1)题库管理算法
区分单选、多选、判断三类题型,封装动态选项构建逻辑;数据库独立存储题干、选项集合、标准答案、解析、题型、分值,管理员端通过表单动态新增 / 编辑选项,提交后同步写入数据库,支持全量查询、条件删除、信息修改。
(2)限时答题与实时保存算法
前端计时器:根据选择时长初始化倒计时,实时秒级刷新,最后 60 秒开启文字红色闪烁告警;
作答持久化:考生每完成一题作答,前端即时向后端提交答案,调用数据库接口实时存入作答表,防止意外退出数据丢失;
进度统计:统计已答题目 / 总题目数量,动态渲染进度条,直观展示答题进度。
(3)自动判分核心算法
单选题:考生答案与标准答案精确字符串匹配,一致得分,否则不得分;
多选题:将考生答案、标准答案分别分割、排序后再比对,忽略选项顺序,实现无序匹配;
判断题:采用文字内容精准匹配,严格校验正误结果;
百分制核算:按单题分值累加总分,结合总分自动划分优秀、良好、及格、不及格评级。
(4)成绩报告渲染算法
分数可视化:圆形进度动画绑定最终得分,直观展示成绩;
错题处理:自动筛选错误题目,默认展开解析模块,并排展示考生作答内容 + 标准答案;
数据联动:读取本次考试记录、作答数据、题库解析,一键整合渲染完整报告。
三、整体运行流程
程序由 main.py 启动 → 唤起 pywebview 桌面窗口加载 Flask 服务 → 路由分发跳转管理员 / 考生首页 → 管理员完成题库维护 或 考生填写信息、选择考试时长 → 加载题库开始限时答题 → 每题答案实时入库 → 提交试卷后触发自动判分 → 生成带动画、错题解析、等级评定的成绩报告 → 同步保存完整考试记录至数据库。
程序代码
main.py
`import threading
import time
import webview
from database import db
from app import app
def run_flask():
db.init()
app.run(port=5000, debug=False, use_reloader=False)
def main():
flask_thread = threading.Thread(target=run_flask, daemon=True)
flask_thread.start()
time.sleep(1.2)
webview.create_window(
title="简易在线考试系统",
url="http://127.0.0.1:5000",
width=1280,
height=800,
min_size=(900, 600),
resizable=True,
)
webview.start()
if name == "main":
main()
`
app.py
`from flask import Flask, render_template, request, jsonify, session
from database import db
import time
─────────────────────────────────────────
初始化
─────────────────────────────────────────
app = Flask(name)
app.secret_key = "exam_secret_2024"
─────────────────────────────────────────
页面路由
─────────────────────────────────────────
@app.route("/")
def index():
return render_template("index.html")
@app.route("/admin")
def admin():
return render_template("admin.html")
@app.route("/exam")
def exam():
return render_template("exam.html")
@app.route("/result/int:record_id")
def result(record_id):
return render_template("result.html", record_id=record_id)
─────────────────────────────────────────
API - 题目管理(管理员用)
─────────────────────────────────────────
@app.route("/api/questions", methods=["GET"])
def get_questions():
questions = db.get_all_questions()
return jsonify({"success": True, "data": questions})
@app.route("/api/questions", methods=["POST"])
def add_question():
data = request.json
required = ["type", "content", "options", "answer"]
if not all(k in data for k in required):
return jsonify({"success": False, "msg": "参数不完整"})
qid = db.add_question(data["type"], data["content"], data["options"], data["answer"])
return jsonify({"success": True, "id": qid})
@app.route("/api/questions/int:qid", methods=["PUT"])
def update_question(qid):
data = request.json
db.update_question(qid, data["type"], data["content"], data["options"], data["answer"])
return jsonify({"success": True})
@app.route("/api/questions/int:qid", methods=["DELETE"])
def delete_question(qid):
db.delete_question(qid)
return jsonify({"success": True})
─────────────────────────────────────────
API - 考试(考生用)
─────────────────────────────────────────
@app.route("/api/exam/start", methods=["POST"])
def start_exam():
data = request.json
student_name = data.get("name", "匿名考生")
questions = db.get_all_questions()
if not questions:
return jsonify({"success": False, "msg": "题库为空,请先添加题目"})
record_id = db.create_record(student_name)
questions_safe = [
{"id": q["id"], "type": q["type"], "content": q["content"], "options": q["options"]}
for q in questions
]
return jsonify({"success": True, "record_id": record_id, "questions": questions_safe})
@app.route("/api/exam/save", methods=["POST"])
def save_answer():
data = request.json
db.save_answer(data["record_id"], data["question_id"], data["answer"])
return jsonify({"success": True})
@app.route("/api/exam/submit", methods=["POST"])
def submit_exam():
data = request.json
record_id = data["record_id"]
result = db.calculate_score(record_id)
return jsonify({"success": True, "record_id": record_id, **result})
─────────────────────────────────────────
API - 成绩报告
─────────────────────────────────────────
@app.route("/api/result/int:record_id")
def get_result(record_id):
result = db.get_result(record_id)
return jsonify({"success": True, "data": result})
@app.route("/api/records")
def get_records():
records = db.get_all_records()
return jsonify({"success": True, "data": records})
─────────────────────────────────────────
启动
─────────────────────────────────────────
if name == "main":
db.init()
app.run(port=5000, debug=False)
`
datebase.py
`import sqlite3
import json
import os
DB_PATH = os.path.join(os.path.dirname(file), "exam.db")
def _connect():
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
class Database:
# ─────────────────────────────────────────
# 初始化 - 建表
# ─────────────────────────────────────────
def init(self):
with _connect() as conn:
conn.executescript("""
CREATE TABLE IF NOT EXISTS questions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL,
content TEXT NOT NULL,
options TEXT NOT NULL,
answer TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
student_name TEXT NOT NULL,
score REAL DEFAULT NULL,
total INTEGER DEFAULT NULL,
created_at TEXT DEFAULT (datetime('now','localtime')),
finished_at TEXT DEFAULT NULL
);
CREATE TABLE IF NOT EXISTS answers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
record_id INTEGER NOT NULL,
question_id INTEGER NOT NULL,
answer TEXT NOT NULL DEFAULT '',
is_correct INTEGER DEFAULT NULL
);
""")
print(f"[DB] 数据库已就绪: {DB_PATH}")
# ─────────────────────────────────────────
# 题目管理
# ─────────────────────────────────────────
def get_all_questions(self):
with _connect() as conn:
rows = conn.execute("SELECT * FROM questions ORDER BY id").fetchall()
return [self._format_question(r) for r in rows]
def add_question(self, type_, content, options, answer):
with _connect() as conn:
cur = conn.execute(
"INSERT INTO questions (type, content, options, answer) VALUES (?,?,?,?)",
(type_, content, json.dumps(options, ensure_ascii=False), answer)
)
return cur.lastrowid
def update_question(self, qid, type_, content, options, answer):
with _connect() as conn:
conn.execute(
"UPDATE questions SET type=?, content=?, options=?, answer=? WHERE id=?",
(type_, content, json.dumps(options, ensure_ascii=False), answer, qid)
)
def delete_question(self, qid):
with _connect() as conn:
conn.execute("DELETE FROM questions WHERE id=?", (qid,))
# ─────────────────────────────────────────
# 考试流程
# ─────────────────────────────────────────
def create_record(self, student_name):
with _connect() as conn:
cur = conn.execute(
"INSERT INTO records (student_name) VALUES (?)", (student_name,)
)
return cur.lastrowid
def save_answer(self, record_id, question_id, answer):
with _connect() as conn:
exists = conn.execute(
"SELECT id FROM answers WHERE record_id=? AND question_id=?",
(record_id, question_id)
).fetchone()
if exists:
conn.execute(
"UPDATE answers SET answer=? WHERE record_id=? AND question_id=?",
(answer, record_id, question_id)
)
else:
conn.execute(
"INSERT INTO answers (record_id, question_id, answer) VALUES (?,?,?)",
(record_id, question_id, answer)
)
def calculate_score(self, record_id):
with _connect() as conn:
answers = conn.execute(
"SELECT * FROM answers WHERE record_id=?", (record_id,)
).fetchall()
questions = {q["id"]: q for q in conn.execute("SELECT * FROM questions").fetchall()}
correct_count = 0
total = len(questions)
for ans in answers:
qid = ans["question_id"]
q = questions.get(qid)
if not q:
continue
student_ans = ans["answer"].strip().upper()
correct_ans = q["answer"].strip().upper()
# 多选题:排序后比较
if q["type"] == "multi":
student_sorted = ",".join(sorted(student_ans.split(",")))
correct_sorted = ",".join(sorted(correct_ans.split(",")))
is_correct = 1 if student_sorted == correct_sorted else 0
else:
is_correct = 1 if student_ans == correct_ans else 0
correct_count += is_correct
conn.execute(
"UPDATE answers SET is_correct=? WHERE record_id=? AND question_id=?",
(is_correct, record_id, qid)
)
score = round(correct_count / total * 100, 1) if total > 0 else 0
conn.execute(
"UPDATE records SET score=?, total=?, finished_at=datetime('now','localtime') WHERE id=?",
(score, total, record_id)
)
return {"score": score, "correct": correct_count, "total": total}
# ─────────────────────────────────────────
# 成绩报告
# ─────────────────────────────────────────
def get_result(self, record_id):
with _connect() as conn:
record = conn.execute(
"SELECT * FROM records WHERE id=?", (record_id,)
).fetchone()
answers = conn.execute(
"SELECT a.*, q.type, q.content, q.options, q.answer as correct_answer "
"FROM answers a JOIN questions q ON a.question_id=q.id "
"WHERE a.record_id=?", (record_id,)
).fetchall()
detail = []
for a in answers:
detail.append({
"question_id": a["question_id"],
"type": a["type"],
"content": a["content"],
"options": json.loads(a["options"]),
"correct_answer": a["correct_answer"],
"student_answer": a["answer"],
"is_correct": a["is_correct"],
})
return {
"student_name": record["student_name"],
"score": record["score"],
"total": record["total"],
"created_at": record["created_at"],
"finished_at": record["finished_at"],
"detail": detail,
}
def get_all_records(self):
with _connect() as conn:
rows = conn.execute(
"SELECT * FROM records ORDER BY id DESC"
).fetchall()
return [dict(r) for r in rows]
# ─────────────────────────────────────────
# 内部工具
# ─────────────────────────────────────────
def _format_question(self, row):
return {
"id": row["id"],
"type": row["type"],
"content": row["content"],
"options": json.loads(row["options"]),
"answer": row["answer"],
}
db = Database()
`
运算结果




体会心得
在本次结对编程中我们分工明确、互相配合,合作过程中,两人互相交流思路、排查 bug,及时发现并修正代码漏洞,让项目开发效率大幅提升。
在协作中,我熟悉了前后端交互、数据库调用与桌面端打包的完整流程。遇到功能难点时,通过互相讨论完善了判分逻辑、倒计时、实时存库等核心细节。
同时也认识到自身代码规范和细节处理的不足,学会了借鉴同伴的思路优化程序。本次合作提升了团队协作能力与问题解决能力,也巩固了 Python、Flask 与数据库的实操基础,收获良多。

浙公网安备 33010602011771号