# -*- coding: utf-8 -*- """Part A: cover, abstract, TOC, chapter 1, chapter 2.""" from docx.shared import Pt, Cm, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK from docx_helpers import (init_doc, set_cn_font, make_table, para, rich, bullet, bullet_rich, heading, caption, note, quote, link_para, toc, add_hyperlink, GREY, BLACK) from docx.oxml.ns import qn from docx.oxml import OxmlElement GH = "https://github.com/" def cover(doc): doc.add_paragraph() p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER r = p.add_run("在线考试系统技术文案(方案 B)配套材料") set_cn_font(r, "楷体", "Times New Roman", 12, False, GREY) doc.add_paragraph() p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER p.paragraph_format.space_after = Pt(6) r = p.add_run("在线考试系统开源方案调研") set_cn_font(r, "黑体", "Times New Roman", 26, True, BLACK) p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER p.paragraph_format.space_after = Pt(18) r = p.add_run("与论文写作参考") set_cn_font(r, "黑体", "Times New Roman", 26, True, BLACK) p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER p.paragraph_format.space_after = Pt(26) r = p.add_run("——面向「多约束组卷、断网续考答案一致性、作弊识别」三个研究点的 GitHub 生态梳理") set_cn_font(r, "楷体", "Times New Roman", 12, False, GREY) rows = [("对应方案", "《在线考试系统中的组卷优化、答案一致性与作弊识别方法研究》(方案 B:Spring Boot 模块化单体)"), ("调研对象", "GitHub 公开仓库(含同类考试系统、组卷求解器、离线同步引擎、在线监考项目、测评平台与标准)"), ("数据口径", "GitHub REST API 实时查询;星标数与最后提交时间以 2026-11-09 复核值为准"), ("文档性质", "文献与开源现状综述素材;可直接裁剪进论文第 1 章、第 2 章、第 4 章与第 5 章"), ("统计规模", "候选仓库 60 余个,逐仓核验 29 个,重点精读 6 个")] t = doc.add_table(rows=0, cols=2) t.autofit = False for k, v in rows: row = t.add_row() c0, c1 = row.cells c0.width = Cm(3.0) c1.width = Cm(12.2) c0.text = "" c1.text = "" pa0 = c0.paragraphs[0] pa0.paragraph_format.space_after = Pt(3) r0 = pa0.add_run(k) set_cn_font(r0, "黑体", "Times New Roman", 10, True, BLACK) pa1 = c1.paragraphs[0] pa1.paragraph_format.space_after = Pt(3) r1 = pa1.add_run(v) set_cn_font(r1, "宋体", "Times New Roman", 10) doc.add_paragraph() p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER r = p.add_run("2026 年 11 月") set_cn_font(r, "宋体", "Times New Roman", 12) def abstract(doc): doc.add_page_break() p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER p.paragraph_format.space_after = Pt(14) r = p.add_run("摘 要") set_cn_font(r, "黑体", "Times New Roman", 16, True, BLACK) para(doc, "本文档是对 GitHub 开源生态中与在线考试系统相关项目的系统性梳理,服务于本科毕业论文《在线考试系统中的组卷优化、答案一致性与作弊识别方法研究》(方案 B:Spring Boot 模块化单体)的开题论证、相关工作撰写与技术选型。调研覆盖 60 余个候选仓库,逐仓核验 29 个,重点精读 6 个,按「同类系统—组卷求解—离线同步—作弊识别—平台与标准」五条线索组织,并给出每个项目与三个研究点的对应关系。", first_indent=True) para(doc, "调研得到三点关键结论。第一,国内可用的同类系统已相当成熟:学之思(xzs-mysql,3604 星)与「砚考」(exam-platform,886 星)分别在业务闭环完整度与工程现代化程度(Spring Boot 3.5 + JDK 17/21 + Vue 3)上处于领先,但两者的组卷都停留在「按题型、难度、分值随机抽题」的规则筛选层面,不具备约束满足求解能力;砚考的防作弊以 13 类行为的计数与阈值强制交卷为主,未建立风险分模型。第二,针对断网续考的检索在所考察的 topic 范围内未发现任何以离线答题、断网续答或冲突合并为核心的开源考试系统,现有实现普遍止步于「逐题自动保存 + 断点续答」,客户端与服务端之间的版本冲突没有公开的通用方案,本研究的唯一索引幂等写入、last_seq 增量同步与交卷令牌三项设计因此具备可论证的空白性。第三,三个研究点在开源生态中呈现明显的不均衡:作弊识别方向项目数量最多但几乎全部采用摄像头视觉 AI 路线,与本研究「不做深度模型、验证轻量行为信号有效性」的定位差异显著;组卷方向可复用的仅是通用求解器(or-tools 的 CP-SAT、Timefold 的局部搜索),没有开箱可用的考试组卷引擎。", first_indent=True) para(doc, "基于以上结论,文档给出了分研究点的引用建议、可作为实验基线的实现清单、许可证风险提示,以及标准与学术检索的补充方向。总体判断是:本研究的技术栈选择已被成熟项目验证,无需自证可行性;而三个研究点在开源生态中的空白与不均衡,恰好构成本文创新性论证的事实基础。", first_indent=True) p = doc.add_paragraph() p.paragraph_format.space_before = Pt(14) r = p.add_run("关键词:") set_cn_font(r, "黑体", "Times New Roman", 10.5, True, BLACK) r2 = p.add_run("在线考试系统;开源调研;多约束组卷;断网续考;最终一致性;作弊识别;模块化单体") set_cn_font(r2, "宋体", "Times New Roman", 10.5) def toc_page(doc): doc.add_page_break() p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER p.paragraph_format.space_after = Pt(12) r = p.add_run("目 录") set_cn_font(r, "黑体", "Times New Roman", 16, True, BLACK) toc(doc) doc.add_page_break() def chapter1(doc): heading(doc, "第一章 调研概述", 1) heading(doc, "1.1 调研背景", 2) para(doc, "在线考试系统在高校课程考核、职业资格认证、企业内训测评等场景中已成为基础设施级应用。与一般的业务信息系统相比,它天然叠加了三类硬约束:试卷必须同时满足知识点覆盖、难度分布、题型配比与总分等多重约束,属于典型的组合优化问题;考试过程对网络连续性高度敏感,一次断网就可能造成答案丢失,后果等同于教学事故;考场缺乏现场监考,只能依靠行为信号推断作弊意图,而误判又会产生申诉与复核成本。") para(doc, "本研究采用的方案 B 将系统定位为 Spring Boot 3 模块化单体(modular monolith),围绕上述三个问题分别提出方法,并用对比实验与故障注入验证其有效性。在正式展开设计之前,需要回答一个前置问题:这些工作是否已经被开源社区做过?如果已有成熟实现,本研究的价值应转向工程整合与场景适配;如果存在明显空白,则应把空白本身写进创新性论证。本文档即为回答这一问题而编写。") heading(doc, "1.2 调研目的与范围", 2) para(doc, "本次调研设定三个目的。") bullet_rich(doc, [("摸清实现边界。", True), ("确认同类系统在组卷、断网续考、作弊识别三方面究竟做到了哪一步,避免在论文中重复叙述已被普遍实现的功能。", False)]) bullet_rich(doc, [("判断创新性。", True), ("研究点的价值取决于其稀缺程度。若某个问题在开源生态中存在大量成熟方案,则应重新表述研究角度;若普遍缺失,则该缺失可作为创新性的事实依据。", False)]) bullet_rich(doc, [("寻找可复用资产。", True), ("包括可直接引用的求解器、同步引擎、定时任务锁、测试工具,以及可作为实验对照基线的现成实现。", False)]) para(doc, "调研范围限定为 GitHub 上的公开仓库,检索截止时间为 2026 年 11 月 5 日,数据于 2026 年 11 月 9 日逐仓复核。Gitee 镜像、商业产品(如超星学习通、雨课堂)仅作为功能对照,不纳入代码级分析;学术论文的检索策略由方案 B 第十一章文献策略另行规定,本文档只在必要处给出衔接建议。") heading(doc, "1.3 检索方法与数据口径", 2) para(doc, "检索分三条路径并行:以名称关键词匹配同类系统,以 topic 标签聚合垂直领域,以技术关键词定位可复用的通用组件。主要检索式与命中情况如表 1-1 所示。") caption(doc, "表 1-1 检索式与命中情况") make_table(doc, ["检索路径", "检索式", "命中规模", "用途"], [["名称匹配", "online exam in:name / exam system in:name", "数十至数百", "定位同类系统"], ["topic 聚合", "topic:online-exam", "119 个仓库", "垂直领域全景"], ["topic 聚合", "topic:exam", "1744 个仓库", "含大量非考试业务噪声"], ["topic 聚合", "topic:proctoring", "155 个仓库", "在线监考方向"], ["技术定位", "automatic paper generation / constraint solver", "少量", "组卷算法基线"], ["技术定位", "offline first sync / conflict resolution", "少量", "断网续考参照实现"]], [2.8, 6.2, 3.0, 3.4]) note(doc, "说明:GitHub 搜索 API 对匿名请求限速 10 次/分钟,且单次响应存在长度上限,本文档的数据通过逐仓调用仓库详情接口获得;星标数与最后提交时间均以查询当日为准,引用时须注明查询日期,避免评审质疑数据时效。") heading(doc, "1.4 主要结论", 2) para(doc, "调研结论可归纳为五点,其中第三点最关键,因为它同时影响了论文的选题表述与章节重心。") rich(doc, [("结论一:同类系统成熟,但成熟的是「业务流程」,不是「方法」。", True)]) para(doc, "学之思与砚考已经覆盖了题库管理、组卷、考试、判分、统计的完整闭环,界面与工程结构均达到可用水平。但它们解决的是「功能有没有」,而不是「方法好不好」:组卷仍是规则筛选,续考仍是逐题保存,防作弊仍是行为计数。这恰恰说明本研究的三个研究点没有被同类系统覆盖。") rich(doc, [("结论二:组卷方向缺少开箱可用的实现,通用求解器是唯一可靠的可复用资产。", True)]) para(doc, "在检索到的自动组卷项目中,绝大多数采用遗传算法或随机回溯的简化实现,且多为 SSM 时代的老技术栈,缺少对约束强度、退化曲线的系统性度量。工业级的约束求解能力集中在 or-tools 与 Timefold 这类通用求解器中,需要自行建模。这意味着本研究的三求解器对比实验既有基线可依,又不至于与既有工作重复。") rich(doc, [("结论三:断网续考是本轮调研中最明确的空白。", True)]) para(doc, "在所考察的 topic:online-exam(119 个仓库)、topic:exam(1744 个仓库)、topic:proctoring(155 个仓库)范围内,没有任何一个开源考试系统以离线答题、断网续答或客户端—服务端冲突合并为核心特性。商业系统的宣传口径也普遍停留在「逐题自动保存 + 断点续答」,即假设网络短暂中断后重新拉取即可,未处理同一题在两端被并发修改的情形。这一空白构成本研究问题二最有力的立论依据。") rich(doc, [("结论四:作弊识别项目数量最多,但路线高度同质。", True)]) para(doc, "开源监考项目几乎全部采用摄像头视觉方案(人脸、视线、手机检测、多人检测),依赖 OpenCV、dlib 或 YOLO 系列模型;另有少数走客户端锁机路线(Safe Exam Browser 生态)。与本研究「规则引擎 + 风险分 + 人工复核、不做深度模型」的定位相比,二者不是同一路线的优劣,而是成本结构的不同:视觉方案需要摄像头、算力与隐私合规成本,规则方案则以浏览器事件与日志分析为主。论文中应把这一差异写成方法选择的理由,而不是技术先进性的比较。") rich(doc, [("结论五:技术栈选择无需自证,架构选择需要论证。", True)]) para(doc, "Spring Boot 3 + Vue 3 + MyBatis-Plus 的组合在砚考等项目中已有完整落地,因此第 2 章的技术选型部分可以直接引用既有实践,不必展开论证;真正需要论证的是「为什么是模块化单体而不是微服务」,而砚考恰好提供了微服务形态的完整样本,可作为第 4 章的正面对照材料。") heading(doc, "1.5 文档结构", 2) para(doc, "第二章梳理同类在线考试系统,给出逐项能力对照,并说明可供本研究借鉴的设计;第三章按三个研究点分别整理可复用的开源资产,并给出研究空白的判定依据;第四章汇总平台、标准与学术检索方向;第五章给出与论文各章节对应的引用建议、实验基线与写作要点;附录 A 为完整仓库清单,附录 B 为按主题整理的检索记录。") def chapter2(doc): heading(doc, "第二章 同类在线考试系统调研", 1) heading(doc, "2.1 总体格局", 2) para(doc, "按技术栈与定位,可将检索到的同类系统分为三类。第一类是国产 Java 全栈系统,以 Spring Boot + Vue 前后端分离为主,代表是学之思系列与砚考,功能覆盖最完整、社区最活跃,也是本研究最直接的参照对象。第二类是轻量毕设型系统,代码量小、结构简单,适合快速理解业务流程,但工程规范性不足。第三类是以 Moodle、Open edX、TAO 为代表的国际测评平台,功能极其完备但体量庞大,更适合作为标准与概念参照,而非代码参照。") para(doc, "从活跃度看,学之思两个仓库在 2026 年 11 月仍有提交,砚考同日也有推送,说明国产考试系统生态仍在演进;而多数学术型自动组卷项目已停止维护(如 cppcpp/exam-system 最后提交为 2022 年 12 月),其价值主要在算法思路而非工程质量。") heading(doc, "2.2 重点系统清单", 2) para(doc, "表 2-1 列出逐仓核验后与本研究相关性最高的八个系统。星标数为 2026-11-09 的复核值,许可证一栏为空表示仓库未声明许可证,此类项目只能参考设计,不能复制代码。") caption(doc, "表 2-1 同类在线考试系统清单(星标与提交时间为 2026-11-09 复核)") make_table(doc, ["项目(点击进入仓库)", "技术栈", "星标 / 许可", "覆盖范围", "对本研究的参考价值"], [ [("mindskip/xzs-mysql", GH + "mindskip/xzs-mysql"), "Java + Vue,Web 与微信小程序", "3604 / AGPL-3.0", "题库、组卷、考试、判分、统计全流程", "业务闭环与表结构设计的首选参照;断点续答与随机组卷逻辑可直接对照"], [("mindskip/xzs", GH + "mindskip/xzs"), "Java + Vue(SQL Server 版)", "3914 / AGPL-3.0", "同 xzs-mysql,文档与前端更完整", "前端工程组织与部署脚本"], [("Alanosy/exam-platform", GH + "Alanosy/exam-platform"), "Spring Boot 3.5 + JDK17/21 + Vue3 + TS + Element Plus;微服务(RuoYi-Cloud-Plus 2.6.2)", "886 / MIT", "出题、组卷、考试、监考、阅卷、统计、发证、错题本闭环,9 种题型", "与本方案技术栈逐项对应;13 类防作弊计数、服务端强制交卷、交卷幂等可直接对照;其微服务形态可作第 4 章架构取舍的反例"], [("lsgwr/spring-boot-online-exam", GH + "lsgwr/spring-boot-online-exam"), "Spring Boot + Vue", "1692 / MIT", "题库、考试、成绩,含在线预览", "代码量适中、许可证宽松,适合作为实现级参考"], [("YXJ2018/SpringBoot-Vue-OnlineExam", GH + "YXJ2018/SpringBoot-Vue-OnlineExam"), "Spring Boot + Vue", "2279 / 无", "经典前后端分离考试系统", "模块划分与论文章节结构易被评审接受;无许可证,仅参考"], [("shuzhao11/quiz", GH + "shuzhao11/quiz"), "Spring Boot + Thymeleaf", "393 / 无", "毕设定位的在线测试系统", "论文结构参照,技术深度不足"], [("cppcpp/exam-system", GH + "cppcpp/exam-system"), "SSM(Java)+ 遗传算法", "87 / 无", "南京晓庄学院自动组卷,遗传算法生成试卷", "问题一最贴合的基线实现;技术栈老旧,仅借鉴算法与约束表达"], [("xingxingzaixian/django-vue3.2-online-exam", GH + "xingxingzaixian/django-vue3.2-online-exam"), "Django + DRF + Vue3 + Element Plus", "184 / MIT", "在线考试,开发中", "Vue3 + Element Plus 的前端交互参考(题库、答题页)"]], [3.5, 3.6, 2.2, 3.1, 3.0], link_col=0) note(doc, "注:wulivictor/ExamOnline(739 星,答题/考试小程序)虽未列入上表,但其切屏与退后台处理对移动端答题场景有参考价值;xzs 系列的 AGPL-3.0 许可具有传染性,仅可借鉴设计,不可复制代码进入以其他许可证发布的仓库。") heading(doc, "2.3 逐项能力对照", 2) para(doc, "为把「已有 vs 未有」说清楚,表 2-2 将四个代表性系统与本研究方案逐项对齐。该表可直接改造为论文第 1 章国内外研究现状中的对照表,也可作为第 4 章设计取舍的论据。") caption(doc, "表 2-2 代表性系统与本研究方案的能力对照") make_table(doc, ["能力维度", "学之思 xzs-mysql", "砚考 exam-platform", "lsgwr 版", "本研究(方案 B)"], [["组卷方式", "按题型/难度/分值随机抽题,规则可配置", "手工选题 + 规则随机抽题,候选不足时提示", "随机抽题", "约束三元组建模,三求解器(回溯/贪心/遗传)"], ["约束满足能力", "规则筛选,无求解保证", "规则筛选,无求解保证", "无", "带成功率与退化曲线度量"], ["断点续答", "支持(逐题自动保存)", "支持(中途退出可续答)", "部分支持", "支持"], ["客户端离线缓存", "无(依赖实时提交)", "无", "无", "IndexedDB 本地草稿 + last_seq 游标"], ["答案冲突合并", "无", "无", "无", "版本号比较,服务端拒绝过期版本"], ["交卷幂等", "未见显式设计", "支持(重复交卷幂等)", "无", "唯一索引 (session_id, submit_token)"], ["防作弊信号", "切屏、复制等基础检测", "13 类行为计数,达阈值服务端强制交卷", "切屏检测", "5 类信号 → 加权风险分 → 分级复核"], ["风险分级模型", "无", "无(仅阈值强制交卷)", "无", "连续风险分 + 人工复核队列"], ["主观题阅卷", "人工阅卷", "三级人工阅卷,改分留痕;双评已预留", "人工阅卷", "双评 + 仲裁,与判分链路一致性绑定"], ["成绩一致性保障", "同步判分", "汇总表 10 分钟定时重算兜底", "同步判分", "outbox + 定时对账,故障注入验证"], ["架构形态", "单体(前后端分离)", "微服务(Nacos + Dubbo,10 个服务)", "单体", "模块化单体,显式模块边界"], ["许可证", "AGPL-3.0", "MIT", "MIT", "—"]], [2.6, 3.4, 3.6, 2.0, 3.8]) note(doc, "注:本表中「无」指该仓库公开代码与文档中未见对应实现,不等同于项目缺陷;砚考的部分能力(双评、AI 阅卷)在其 README 中标注为「已预留接口、尚未接入」。") heading(doc, "2.4 可借鉴的设计与明确的差距", 2) heading(doc, "2.4.1 可直接借鉴的设计", 3) bullet_rich(doc, [("表结构草案。", True), ("学之思的题库、试卷、考试、答卷四组表与本研究的数据模型高度同构,可作为 question、paper、exam、exam_session、answer_item 的字段起点,减少建模范式上的返工。", False)]) bullet_rich(doc, [("交卷幂等的落地方式。", True), ("砚考以「前端主动提交 + 后端定时兜底」双保险处理倒计时归零场景,与本研究 (session_id, submit_token) 方案可以互相印证:前者保证「一定会交」,后者保证「只生效一次」,二者正交互补。", False)]) bullet_rich(doc, [("统计表的预计算 + 定时重算。", True), ("砚考将考试统计落到预计算汇总表,并以每 10 分钟定时重算兜底。这一结构与本研究第 3.4 节「交卷—判分—成绩」的 outbox + 对账思路一致,可作为该支撑研究点的工程依据。", False)]) bullet_rich(doc, [("前端三端同工程的组织方式。", True), ("砚考与学生端、教师端、管理端的路由拆分方式,对应本研究 4.2 节的三端功能划分。", False)]) heading(doc, "2.4.2 需要在本研究中补足的差距", 3) bullet_rich(doc, [("组卷从「筛选」升级为「求解」。", True), ("既有系统的组卷本质是对候选集按条件过滤后随机抽取,约束一多就会出现抽不满或凑不齐总分的情况,且没有任何成功率度量。本研究把它表述为约束满足问题并给出三档强度下的退化曲线,这是方法层面的增量。", False)]) bullet_rich(doc, [("续考从「保存」升级为「一致性协议」。", True), ("既有实现的断点续答建立在「网络恢复后以服务端为准」的隐含假设上,未定义同一题在客户端与服务端被并发修改时的裁决规则。本研究引入客户端版本号、服务端唯一索引与增量同步三者配合,形成可证明的收敛协议。", False)]) bullet_rich(doc, [("防作弊从「计数」升级为「风险度量」。", True), ("砚考的 13 类行为计数达到上限即强制交卷,是二值判定:在阈值以下完全无害,越过阈值直接终止。本研究的加权风险分允许中间状态进入人工复核,兼顾召回率与误报成本,并可用单信号 AUC 说明各信号的贡献。", False)]) bullet_rich(doc, [("架构从「微服务堆叠」回归「模块化单体」。", True), ("砚考用 Nacos + Dubbo 拆分 10 个服务,对一个课程设计规模的系统而言,分布式基础设施带来的运维与调试成本高于收益。本研究以模块边界纪律替代物理拆分,并用同一边界说明未来演进路径,这一点在第 4 章需要正面论证。", False)])