调研报告 · 题库分类体系

开源题库系统数据结构调研:
TAO × 学之思 × Spring Boot Online Exam

调研日期:2026-08-14。方法:shallow clone 三个仓库(及 TAO 相关扩展),直接阅读 SQL 建表语句与 Java/PHP 源码,核对实体、字段与判分逻辑。所有结论均来自源码,非文档转述。

〇、三家系统定位速览

表 1三家开源系统的定位与题库地位速览1
系统仓库技术栈定位许可证题库在系统中的地位
TAO github.com/oat-sa(tao-core + extension-tao-item / -itemqti / -test / -testqti / -delivery / -outcome) PHP + RDF 本体(generis),QTI/LTI 标准 商业级开源测评平台(卢森堡大学发起,OAT 维护) AGPL/GPL 系 核心:题库(Item Bank)+ 试卷(Test)+ 投放(Delivery)+ 结果(Outcome)四段式
学之思 github.com/mindskip/xzs Java SpringBoot + Vue + 微信小程序 在线考试系统(教育机构/企业) AGPL-3.0 子模块:题库管理 → 组卷 → 考试 → 判分 → 错题本
Spring Boot Online Exam github.com/lsgwr/spring-boot-online-exam Java SpringBoot + Vue 通用在线考试系统(演示型) 无明确开源协议(README 声明开源)2 子模块:题库 → 固定卷考试 → 记录

一句话:TAO 是「测评数据模型」的行业天花板;学之思是「国产考试系统」里工程最完整的参考;sboe 是 2019 年的教学演示项目,结构简单、反例也多。

    本节注释

  1. 仓库信息与技术栈摘自各仓库 2026-08-14 shallow clone 时的快照;下文以「sboe」简称 Spring Boot Online Exam。
  2. 该仓库未附带标准 LICENSE 文件,仅在 README 中声明开源,商用前需另行确认授权边界。

一、题目分类体系对比

1.1 学之思:4 个标量字段,题型即分类

t_subject(学科,自带 level 年级 1-12)+ t_question 上的 4 个字段:

表 2学之思 t_question 的分类相关字段
字段取值语义
subject_id学科 id(语文/数学/英语…)学科维度
grade_level1-12年级
question_type1 单选 / 2 多选 / 3 判断 / 4 填空 / 5 简答(Java 枚举 QuestionTypeEnum 写死在代码里)作答形式,兼当「题型」
difficult难度档难度

1.2 sboe:三张字典表,题型只有 3 种

question_category(学科字典:天文/数学/物理/生物/地理/化学/英语/历史/人文/生活)、question_level(难度 high/middle/low)、question_type(只有 single/multi/judge 三种,没有填空和简答)。题目通过 3 个外键引用。

1.3 TAO:没有内置分类体系,分类=类树+属性,自由度最高但零约束

1.4 三家 vs 你的分面体系

表 3分面分类维度对照1
你的维度学之思sboeTAO
A 技能领域(L/R/W/T/S/K)✗(需自建属性)
B 作答形式(b_form)部分:question_type(且与题型混在一起)部分:question_type(3 种)结构化:QTI interaction 类型(20+ 种,见 3.1)
C 材料体裁✗(无材料实体,见 3.2)
D 输入模态(TEXT/AUDIO/VIDEO/IMAGE)✗(图片可塞富文本)✗(图片 base64 塞题干 HTML)部分:媒体作为 item 附件,但非独立实体
题型(受控词表)与 question_type 合一与 question_type 合一无题型概念(题型=interaction 组合的自由度)
评分方式(MACHINE/HUMAN/AI/HYBRID)隐式:判分逻辑按枚举分支隐式:只有机器可判题型显式:responseDeclaration + responseProcessing(见 3.1)

核心结论:三家开源系统里没有任何一家实现了「材料/题组/题原子/计分点」四级实体,也没有「正交分面分类」——你的设计在开源参照物里是独一份,参考价值主要来自局部做法。

    本节注释

  1. 表 3 中「✗」表示在建表 SQL、实体类与本体文件中均未找到对应字段或实体;「部分」表示有相近机制但不完整。

二、数据结构逐家拆解

2.1 学之思(xzs-postgresql.sql)

实体关系1

t_subject(学科+年级)
  └─ t_question(题型/学科/分值(千分制)/年级/难度/正确答案文本)
        ├─ info_text_content_id → t_text_content(题干富文本:题面/填空/解析/答案全在这里)
  t_exam_paper(固定卷/时段卷)
        ├─ frame_text_content_id → t_text_content(试卷结构:JSON,见下)
  t_exam_paper_answer(一次作答的卷面快照:paper_name/paper_type/subject_id/system_score/user_score/question_correct/question_count/do_time/status)
  t_exam_paper_question_customer_answer(逐题作答快照)
        ├─ question_id / question_type / subject_id / customer_score / question_score
        ├─ question_text_content_id(题干快照)/ text_content_id(填空简答的学生作答文本)
        └─ answer / do_right / item_order
  t_task_exam + t_task_exam_customer_answer(任务组卷 + 任务作答)

要点:

2.2 sboe(doc/sql/exam.sql)

question_category(学科字典) question_level(难度字典) question_type(题型字典)
question(题干=longtext HTML(含 base64 图片)、question_option_ids 用"-"拼接、question_answer_option_ids 正确选项"-"拼接)
  └─ question_option(独立选项表:内容 + 解析)
exam(试卷:exam_question_ids 全部题号"-"拼接 + 按题型分列 radio/check/judge 题号 + 各题型单题分值列 + 时限/有效期)
exam_record(answer_option_ids varchar(4096):questionId@True/False_选项id串 用 $ 连接的一条大字符串)
exam_record_level(优秀/良好/一般/及格/不及格)

要点与反例:

2.3 TAO(RDF 本体 + QTI XML)

数据模型是「资源-属性」三元组,核心四段:

Item(题库,class 树自由分类)
  └─ ItemContent:QTI XML 文件(itemBody + interactions + responseDeclaration + responseProcessing)
Test(试卷库,class 树)
  └─ TestContent:QTI assessmentTest XML
        ├─ testPart → assessmentSection(可嵌套)→ assessmentItemRef(引用 Item)
        └─ itemSessionControl(最大尝试次数/是否可跳过/何时显示反馈)+ outcomeProcessing(分数规则)
Delivery(投放:把 Test 分发给 TestTaker 的会话)
Outcome / Results(逐题结果变量流:每个 interaction 的响应值 + 分数)

三系统「题目 → 试卷 → 作答记录」实体关系对照

按各仓库建表 SQL 与本体文件归纳 · 仅保留主干实体

学之思 · 四级链(快照冗余) 题目 t_question 试卷 t_exam_paper 卷面作答快照 t_exam_paper_answer 逐题作答快照 customer_answer 题干/解析/答案混存富文本;作答时题干、分值、答案全量冗余复制 sboe · 字典表 + 拼接串 题目 question 试卷(固定列) exam 作答记录 exam_record 题号 / 选项 / 作答全部以 - $ @ 拼接成 varchar 大字符串(反例) TAO · 四段式(Item / Test 彻底分离) Item 题库 QTI item XML Test 试卷库 assessmentTest XML Delivery 投放 分发给 TestTaker Outcome 结果 逐题结果变量流 assessmentItemRef 引用 Item 与 Test 互为独立资源库:一题多卷复用,题改版走 revision;红色虚线为引用关系而非包含关系
图 1三系统实体关系对照。学之思为「题目→试卷→作答快照」链;sboe 以拼接字符串维系关系;TAO 四段式中 Item 与 Test 彻底分离(红色标出),Test 仅经 assessmentItemRef 引用 Item。

    本节注释

  1. 实体关系代码块仅保留与题库—试卷—作答相关的主干表,用户、消息、日志等周边表从略。
  2. 20+ 种交互类的完整清单见第五节资料来源所列 extension-tao-itemqti 的 model/qti/interaction/ 目录。

三、对本平台设计的参考价值

对照四级实体 + 分面分类 + scoring 映射逐条评估。

3.1 值得抄的

  1. TAO:答案结构声明(responseDeclaration 的 baseType/cardinality)→ 压缩进你的 blank 层。你的 blank(计分点)目前存「占位信息 + 参考答案」。建议增加一个「作答形态」描述(如 blank_kind: choice_single / choice_multi / text_exact / text_fuzzy / ordering / matching / gap…),等价于 QTI 的 baseType×cardinality。好处:b_form → scoring 的 mapping_rule 有了机器可读的输入,部分推导可自动化(choice_single + 精确匹配 → MACHINE;text_fuzzy / essay → HUMAN/AI),这正是 TAO 里 responseDeclaration 干的事。
  2. TAO:mapping 部分给分。responseDeclaration 支持「选项→分值」映射表(选 B 给 0.5 分)。你的 mapping_rule 目前是「(exam, paper, qtype, b_form) → scoring 默认值」,建议加上部分给分规则的字段,多选/匹配题的半对给分是真实刚需。
  3. TAO:Item 与 Test 分离 + 题可多卷复用。你的 item 层天然支持(item_group 可挂多卷),无需改动;但 TAO 证明了「题 = 独立可导入导出的资源文件(QTI)」这条路,建议你的 item 层设计一个序列化格式(哪怕先不接 QTI),未来与外系统互通有标准可循。
  4. 学之思:作答全快照(t_exam_paper_question_customer_answer)。作答时把题干、分值、答案、学生作答全部冗余复制。对应你的设计:item 允许改版/停用,但历史作答必须保留出题时的原样——在你的 item 上加 version,并在作答记录里存 item 快照(或 version_id),别只存 item_id 引用。
  5. 学之思:组卷模板 JSON(frame_text_content)。「题型 × 题数 × 分值」的组卷规则与具体题目解耦。你的平台如果要组卷,建议做成结构化的「组卷模板表」(比塞 text_content 更规范,sboe 的按题型固定列则是明确的反例)。
  6. sboe:选项独立表。单选/多选的选项应该是独立实体(可加解析、可排序、可复用),学之思把选项塞进题干富文本是劣势。你的 blank 层如果面对 choice 类题型,可以借鉴「选项实体化」的粒度。
  7. TAO:QTI/LTI 标准互通。TAO、QST、Moodle 都支持 QTI 导入导出。你的题库如果未来要接外部系统(或导数据给出版社合作方),QTI 是事实标准——至少把 QTI 的「题型-答案声明-判分规则」三段式思想吸收进数据结构,而不是自造一个无法互通的格式。

3.2 要避的坑

  1. sboe:一切 ID/答案用 -/$ 拼接的 varchar 大字符串——无法查询、统计、版本化。你的 blank 参考答案务必结构化(JSON 或独立表)。
  2. sboe:试卷按题型固定列——新增题型要改表。你的平台题型是受控词表可配置,试卷结构必须动态。
  3. sboe:base64 图片直接塞题干 HTML——数据膨胀、无法去重复用。你的 material 层 + 媒体引用 URL/对象存储是正解。
  4. 学之思:判分逻辑放在提交端(customer_score 由前端报文给出,服务端只校验)——可作弊、不可审计。你的评分方式(MACHINE/HUMAN/AI/HYBRID)应该由服务端规则引擎执行,映射表 + 复核队列的方向是对的。
  5. 学之思:题型枚举写死在 Java 代码里,新增题型要改代码 + 前端 + 判分——你定的「新题型走配置化新增字典值流程」正是它的补集。
  6. TAO:零约束分类——类树随便建、属性随便挂,数据质量靠流程纪律。你的「必填单值分面 + 受控词表 + 冲突复核队列」是工程上更可控的选择,不要因为 TAO 自由就放松约束。
  7. 三家共同:没有材料实体,听力/阅读材料靠复制——这是你四级模型的最大差异化,也是主要复杂度来源(引用/版本/权限)。工程建议:material_id 允许为空,单题内嵌材料也放行,两级并存,避免「必须建材料才能录题」的强约束吓退内容团队。

四级实体模型对照:本平台 vs 三家开源系统

按建表 SQL 与本体文件核对 · 虚线空框 = 该层实体缺失

材料 · 题组层 题目 · 计分层 本平台 material → item_group 材料可空 · 题组可退化为一题一组 item → blank 题目原子 → 计分点(作答形态) 学之思 ✗ 无材料 / 题组实体 t_question 材料塞题干富文本,多题共享靠复制 sboe ✗ 无材料 / 题组实体 question 图片 base64 直接塞题干 HTML TAO ✗ 无材料实体 Item(QTI XML) 媒体仅作单题附件,n 题共用 = n 份附件
图 2本平台「material → item_group → item → blank」四级模型与三家对照:三家均缺材料层(虚线空框),听力/阅读材料只能靠复制或单题附件,这正是四级模型的最大差异化所在。

3.3 一个重要认知校正

三家都没有「材料」「题组」概念,但都活得很好——说明不做材料层系统也能跑;你要做的材料/题组层,价值在「多模态材料复用 + 大规模题目共享 + 统计口径」,收益在规模上来之后才体现。所以分级落地是对的:第一阶段 item 单飞(material_id 可空、item_group 可退化为一题一组),第二阶段再强化材料共享。

四、结论清单(可直接进设计评审的条目)

  1. blank 层增加「作答形态」字段(对齐 QTI baseType×cardinality),作为 scoring 推导的机器可读输入。
  2. scoring 映射表增加「部分给分」能力(对齐 TAO mapping)。
  3. item 增加 version;作答记录存 item 快照(对齐学之思 customer_answer 快照)。
  4. 组卷模板独立结构化(对齐学之思 frame JSON,但入库为表)。
  5. choice 类题目的选项独立实体化(对齐 sboe question_option,但用正规外键)。
  6. item 层预留序列化格式(长期对接 QTI)。
  7. 材料层可选(material_id 可空 + 内嵌路径放行),避免强约束。
  8. 判分规则放服务端规则引擎,前端只提交原始作答(避开学之思提交端判分的坑)。
  9. 题型/分面全部字典表化,试卷结构动态生成(避开 sboe 固定列)。
  10. 评分管线(HUMAN/AI/HYBRID)自研:三家均无开放题评分,QTI 的「答题结构标准化 + 评分器可插拔」架构值得照抄思路。

五、资料来源(本次实际阅读的文件)


—— 全文完 ——