题王 QKing 局域网答题系统 · 软件说明书
第 5 章

数据设计

数据库: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(题目批次)