结对编程

学号: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()
`
运算结果
image
image
image
image

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

posted @ 2026-04-23 12:58  格斯o  阅读(3)  评论(0)    收藏  举报