数据设计
数据库:SQLite 单文件库(quiz.db),启用 journal_mode=WAL、synchronous=NORMAL、busy_timeout=30000ms。
本页目录
5.1 数据模型总览
- 数据库:SQLite 单文件库(
quiz.db),启用journal_mode=WAL、synchronous=NORMAL、busy_timeout=30000ms。 - 规模:26 张表、57 个索引(2026-09-24 实测;其中
sqlite_sequence为 SQLite 内部表)。 - 隔离键:
school_id(多租户,1= 默认学校,0= 平台公共库)、room_code(班级现场隔离)。 - 时间字段统一使用
REAL(Unix 秒)或INTEGER(Unix 秒)。
表清单与行数(2026-09-24 快照)
| 表名 | 行数 | 用途 |
|---|---|---|
schools |
4 | 学校(租户)主表 |
school_applies |
2 | 学校开通申请与审核 |
users |
525 | 统一账号表(超管/校管/教师/学生) |
login_sessions |
13 | 一账号多设备登录会话 |
verify_codes |
0 | 短信验证码(预留表,未启用) |
qb_questions |
917 | 题库主表(全部题型,含材料大题与子题) |
qb_categories |
22 | 题库分类 |
qb_tags |
10 | 题库标签 |
qb_question_tags |
784 | 题目-标签关联 |
qb_papers |
27 | 试卷(组卷结果) |
qb_paper_items |
881 | 试卷题目明细(顺序与分值) |
questions |
551 | 实时答题题表(按房间隔离,含 qb_src 指向题库) |
players |
65 | 实时答题选手(房间内唯一) |
answers |
20 | 实时答题作答留痕 |
random_deal |
546 | 随机刷题的个人发题顺序 |
rooms |
2 | 房间(房间码为主键) |
sessions |
27 | 场次(实时场 / 整卷场) |
session_users |
155 | 场次成绩汇总(一人一场一行) |
attempt_items |
3,250 | 作答明细(一人一题一行) |
mistake_status |
2 | 错题本状态(重做/掌握标记) |
excel_submission |
38 | 表格操作题作答留痕(整卷/练习) |
excel_live_sub |
1 | 表格操作题作答(实时场) |
import_batches |
28 | 批量导入批次(账号/题目) |
status |
59 | 运行态键值表(当前题、模式、题池等) |
_b |
10 | 历史遗留临时表(诊断残留,可清理) |
sqlite_sequence |
17 | SQLite 内部自增序列 |
5.2 数据表定义
5.2.1 账号与学校域
schools(学校/租户主表)
CREATE TABLE schools (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE, -- 学校名称(全局唯一)
code TEXT NOT NULL UNIQUE, -- 学校代码(全局唯一,用于学校码登录/展示)
status TEXT NOT NULL DEFAULT 'active', -- active 启用 | disabled 停用
note TEXT DEFAULT '',
created_by INTEGER, created_at REAL);
-- 索引:idx_schools_status(status)
school_applies(学校开通申请):字段含 school_name、联系人(contact_name/phone/email)、status(pending|approved|rejected)、审核信息(review_note/review_by/review_at)、school_id。索引 idx_applies_status。
users(统一账号表)
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
school_id INTEGER NOT NULL DEFAULT 1,
username TEXT NOT NULL, -- 学号/用户名(校内唯一)
display_name TEXT NOT NULL, -- 姓名/显示名
phone TEXT UNIQUE, email TEXT UNIQUE,
password_hash TEXT NOT NULL, -- scrypt 哈希
role TEXT NOT NULL DEFAULT 'student', -- superadmin|admin|teacher|student
status TEXT NOT NULL DEFAULT 'normal', -- normal|disabled
source TEXT NOT NULL DEFAULT 'import', -- 账号来源
batch_id INTEGER, -- 所属导入批次
remark TEXT DEFAULT '',
session_token TEXT, -- 兼容保留(新会话在 login_sessions)
fail_count INTEGER DEFAULT 0, lock_until REAL,
claimed_at REAL, last_login_at REAL,
created_at REAL NOT NULL, updated_at REAL,
profile_confirmed_at REAL, -- 首次登录资料确认时间
department TEXT DEFAULT '', created_by INTEGER,
UNIQUE(school_id, username));
-- 索引:idx_users_status / idx_users_batch / idx_users_school / idx_users_token(部分索引)
login_sessions(多设备会话):token(随机串,主键)、user_id、created_at、last_seen;索引 idx_ls_user、idx_ls_created。读取顺序:本表优先,回退 users.session_token(兼容旧数据)。
verify_codes(预留):短信验证码表,当前 0 行、无接口引用,属预留设计。
5.2.2 题库与组卷域
qb_questions(题库主表)
CREATE TABLE qb_questions(
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL, -- single|multi|judge|blank|material|code|excel_wps
parent_id INTEGER, -- 材料大题 → 子题
category_id INTEGER,
stem TEXT NOT NULL DEFAULT '', -- 题干(富文本)
options TEXT, -- [{"k":"A","t":"..."}]
answer TEXT, -- single "A" / multi ["A","C"] / judge "T|F" / code {lang,text} / blank 答案组
analysis TEXT DEFAULT '', -- 解析
created INTEGER, updated INTEGER,
difficulty TEXT NOT NULL DEFAULT 'medium', -- easy|medium|hard
points REAL NOT NULL DEFAULT 10, -- 默认分值
school_id INTEGER NOT NULL DEFAULT 1,
visibility INTEGER NOT NULL DEFAULT 0, -- 0 私有 | 1 公开(平台公共用 school_id=0)
template_file_url TEXT, json_standard_url TEXT, judge_js_url TEXT, -- 表格题三件套
oj_lang TEXT, oj_testcases TEXT, oj_limit INTEGER NOT NULL DEFAULT 5, -- 编程题配置
source_uid TEXT, -- 题库包稳定 ID
local_edited INTEGER NOT NULL DEFAULT 0, -- 本地已修改标记(题库更新不静默覆盖)
ord INTEGER NOT NULL DEFAULT 0); -- 子题序号
-- 索引:idx_qbq_parent / idx_qbq_school / idx_qb_questions_source_uid(部分唯一)
qb_categories / qb_tags:均有 school_id + visibility + source_uid 与 UNIQUE(school_id, name);qb_question_tags 为复合主键关联表 PRIMARY KEY(qid, tid)。
qb_papers / qb_paper_items
CREATE TABLE qb_papers(
id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, note TEXT DEFAULT '',
created INTEGER, code TEXT, -- 6 位试卷/考试房间码
school_id INTEGER NOT NULL DEFAULT 1, visibility INTEGER NOT NULL DEFAULT 0,
source_uid TEXT);
CREATE TABLE qb_paper_items(
id INTEGER PRIMARY KEY AUTOINCREMENT, paper_id INTEGER NOT NULL, qid INTEGER NOT NULL,
ord INTEGER NOT NULL DEFAULT 0, -- 卷内顺序
points REAL DEFAULT 10); -- 题位分值(折算权重)
5.2.3 实时答题域
questions(实时题表):字段与题库对应(question/optA..optD/options/answer/analysis),另含 room_code(房间隔离)、qb_src(来源题库题 ID)、parent_id(材料大题子题)、表格题三件套与编程题配置。
players(选手):name + room 唯一(UNIQUE(room,name)),含 score、user_id、display、last_seen、presence(on|away|left)、away_since;索引 idx_players_room、idx_players_user。
answers(实时作答):player_id、qid、sel、free(1=补答)、编程题结果(oj_pass/oj_total/oj_add/oj_detail)。索引 idx_answers_player、idx_answers_qid。
random_deal(随机刷题发题表):player_id + qid 唯一,ord 为该生个人题序。
status(运行态键值表):key 主键、val 文本。实时链路使用 current_q(当前题)、running、mode、queue(题池)、idx、q_start、q_limit、reveal、reveal_ana、buzz_owner/buzz_deadline/buzz_wrong(抢答)、round_start、portal 等;另含房间级覆盖键(room:<房间码>:q_limit 等)与全局开关 allow_register。
5.2.4 场次、房间与成绩域
rooms:code(6 位房间码,主键)、title、creator_id、note、school_id、created_at。
sessions(场次)
CREATE TABLE sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
mode TEXT NOT NULL, -- live_sequence|live_random|live_buzz|exam
paper_id INTEGER,
quiz_plan TEXT NOT NULL DEFAULT '[]', -- [{qn_id,qn_type,points,ord}]
duration INTEGER DEFAULT 0, -- 限时(秒)
reveal_policy TEXT NOT NULL DEFAULT 'after_submit', -- inline|submit|scheduled|never
pass_line REAL DEFAULT 60, -- 达标线
started_at REAL, ended_at REAL,
status TEXT NOT NULL DEFAULT 'open', -- open|ended|aborted
revealed INTEGER DEFAULT 0, reveal_at REAL,-- 是否已公布 / 定时公布时间
creator TEXT DEFAULT '',
join_mode TEXT DEFAULT 'fixed', -- fixed|assign|choose(多卷模式)
paper_plans TEXT DEFAULT '[]', -- 多卷快照
code TEXT, -- 考试码
room_code TEXT DEFAULT '', note TEXT DEFAULT '',
shuffle INTEGER DEFAULT 0, -- 整卷题序乱序
school_id INTEGER NOT NULL DEFAULT 0,
kind TEXT DEFAULT '', -- 如 mistake_redo(错题重做)
owner_id INTEGER);
-- 索引:idx_sessions_status / idx_sessions_school / idx_sessions_code(部分唯一)
session_users(成绩汇总)
CREATE TABLE session_users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id INTEGER NOT NULL, user_id INTEGER NOT NULL,
score REAL DEFAULT 0, max_score REAL DEFAULT 0,
correct_cnt INTEGER DEFAULT 0, answered_cnt INTEGER DEFAULT 0,
used_secs INTEGER, submit_at REAL, paper_id INTEGER DEFAULT 0,
UNIQUE(session_id, user_id));
-- 索引:idx_su_sess_score(session_id, score) —— 场控排行榜轮询使用
attempt_items(作答明细)
CREATE TABLE attempt_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id INTEGER NOT NULL, user_id INTEGER NOT NULL,
qn_type TEXT NOT NULL DEFAULT 'qb', -- qb|legacy
qn_id INTEGER NOT NULL,
sel TEXT, -- 学生作答
is_correct INTEGER, -- 是否正确
points_got REAL DEFAULT 0, -- 得分
answered_at REAL, latency INTEGER, -- 作答时间与耗时
excel_sub_id INTEGER, -- 表格题作答关联
free INTEGER DEFAULT 0, -- 是否补答
oj_detail TEXT, -- 编程题用例明细
UNIQUE(session_id, user_id, qn_type, qn_id));
-- 索引:idx_att_session / idx_att_user / idx_att_qn / idx_att_sess_qn
mistake_status(错题本):user_id + qkey(形如 qb:123)唯一,记录 redo_cnt、redo_ok、last_ok、last_at、mastered;只存状态,不复制题目正文,错题集由 attempt_items.is_correct=0 关联得出。
5.2.5 表格操作题域
excel_submission(整卷/练习)
CREATE TABLE excel_submission (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id INTEGER NOT NULL, user_id INTEGER NOT NULL, qn_id INTEGER NOT NULL,
qb_id INTEGER, file_path TEXT, file_name TEXT, file_size INTEGER DEFAULT 0,
score REAL DEFAULT 0, score_max REAL DEFAULT 0,
passed INTEGER DEFAULT 0, failed INTEGER DEFAULT 0, unknown INTEGER DEFAULT 0,
total_rules INTEGER DEFAULT 0,
detail TEXT, -- 逐步骤明细(仅排查用)
subs TEXT, -- 合并小题计分结果(计分与展示以此为准)
created_at REAL, updated_at REAL,
UNIQUE(session_id, user_id, qn_id));
-- 索引:idx_exsub_session / idx_exsub_user / idx_exsub_qn
excel_live_sub(实时场):键为 UNIQUE(room_code, player_id, qid),含 score_added(实际计入选手总分的增量)与同样的 subs 结构。
5.2.6 导入与其它
import_batches:filename、total_rows/ok_rows/fail_rows、status(pending|done|cancelled)、raw(待确认行 JSON)、kind(users|questions)、school_id、processed/ins_rows/upd_rows(分块进度与新增/更新计数)、created_by(批次创建人,防止跨校下载含明文初始口令的清单)。
_b:历史诊断残留表(单列 id,10 行),无业务引用,可在交付前清理。
5.3 关键字段取值范围(数据字典补充)
| 表.字段 | 取值 |
|---|---|
users.role |
superadmin(平台超管)/ admin(学校管理员)/ teacher(教师)/ student(学生) |
users.status |
normal / disabled |
qb_questions.type |
single 单选 / multi 多选 / judge 判断 / blank 填空 / material 材料大题 / code 编程题 / excel_wps 表格操作题 |
qb_questions.difficulty |
easy / medium / hard |
qb_questions.visibility |
0 本校私有 / 1 公开 |
sessions.mode |
live_sequence / live_random / live_buzz / exam |
sessions.status |
open / ended / aborted |
sessions.reveal_policy |
inline 答后立现 / submit 交卷后可见 / scheduled 定时发布 / never 仅报告可见 |
sessions.join_mode |
fixed 全班同卷 / assign 按账号分配 / choose 学生自选 |
players.presence |
on 在线 / away 切换标签页 / left 已离开 |
attempt_items.qn_type |
qb(题库题)/ legacy(实时题表题) |
import_batches.kind |
users(账号批次)/ questions(题目批次) |