feat(education): project and review wrong questions
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
-- =============================================
|
||||
-- Education 模块 — 错题本 DDL Rollback
|
||||
-- Migration: 005
|
||||
-- =============================================
|
||||
-- IMPORTANT: This is a documentation-only rollback.
|
||||
-- No DROP/ALTER/DELETE statements are executed. The wrong_question
|
||||
-- table is provenance-safe: it only accumulates data and mastering
|
||||
-- is a status flag. Dropping these tables would lose student error
|
||||
-- history with no recovery path.
|
||||
--
|
||||
-- What this migration created:
|
||||
-- - education_wrong_question (new table)
|
||||
-- - education_wrong_question_idempotency (new table)
|
||||
-- - education_practice_report_detail.options (new column)
|
||||
-- - education_practice_session.review_fingerprint (new column)
|
||||
--
|
||||
-- Manual rollback requires:
|
||||
-- 1. Verified database backup before rollback
|
||||
-- 2. Operator approval (DBA sign-off)
|
||||
-- 3. Provenance of all wrong-question records preserved (exported)
|
||||
-- 4. Soft-delete via deleted = b'1' before any hard drop
|
||||
--
|
||||
-- These tables are NOT deleted by this script. Wrong history is
|
||||
-- retained; if deletion is required by external policy, consult
|
||||
-- the DBA for a verified rollback procedure.
|
||||
154
sql/mysql/education/005-education-wrong-question.sql
Normal file
154
sql/mysql/education/005-education-wrong-question.sql
Normal file
@@ -0,0 +1,154 @@
|
||||
-- =============================================
|
||||
-- Education 模块 — 错题本 DDL
|
||||
-- Ticket #9: 错题自动收集、复习练习创建
|
||||
-- Migration: 005
|
||||
-- Prerequisites: 004-education-submit-report.sql (report + detail tables)
|
||||
-- =============================================
|
||||
|
||||
-- =============================================
|
||||
-- Preconditions
|
||||
-- =============================================
|
||||
-- Operator is expected to verify:
|
||||
-- SELECT COUNT(*) FROM information_schema.tables
|
||||
-- WHERE table_schema = DATABASE()
|
||||
-- AND table_name IN ('education_wrong_question',
|
||||
-- 'education_wrong_question_idempotency');
|
||||
-- Result MUST be 0 before executing this migration.
|
||||
--
|
||||
-- Verify prerequisite tables exist:
|
||||
-- SELECT COUNT(*) FROM information_schema.tables
|
||||
-- WHERE table_schema = DATABASE()
|
||||
-- AND table_name IN ('education_practice_report',
|
||||
-- 'education_practice_report_detail');
|
||||
-- Result MUST be 2.
|
||||
|
||||
-- =============================================
|
||||
-- PracticeReportDetailDO: add options snapshot column
|
||||
-- =============================================
|
||||
-- PracticeReportDetailDO: add content_version + options snapshot columns
|
||||
-- =============================================
|
||||
-- Purpose: At submit time, snapshot question content version and options
|
||||
-- (without isCorrect) so wrong-question book and review sessions have
|
||||
-- stable display data. The options are already stripped of isCorrect
|
||||
-- by the submit flow.
|
||||
ALTER TABLE `education_practice_report_detail`
|
||||
ADD COLUMN `content_version` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '题目内容版本快照',
|
||||
ADD COLUMN `options` TEXT DEFAULT NULL COMMENT '选项快照 JSON(不含 isCorrect)';
|
||||
|
||||
-- =============================================
|
||||
-- PracticeSessionDO: add review fingerprint column
|
||||
-- =============================================
|
||||
-- Purpose: Persist the canonical fingerprint of wrong-question IDs used
|
||||
-- to create a review session. On idempotent replay, the fingerprint
|
||||
-- is compared: same tenant+clientSessionId+sameUser+sortedIDs match
|
||||
-- returns the existing session; different ID set returns
|
||||
-- SESSION_IDEMPOTENCY_MISMATCH.
|
||||
ALTER TABLE `education_practice_session`
|
||||
ADD COLUMN `review_fingerprint` VARCHAR(64) DEFAULT NULL COMMENT '复习会话题目指纹(SHA-256 of sorted unique wrongQuestionIds)';
|
||||
-- 错题表
|
||||
-- =============================================
|
||||
-- Purpose: Persistent wrong-question book per student.
|
||||
-- Each (tenant, user, question) is a unique entry.
|
||||
-- Repeated wrong answers on the SAME question increment wrong_count
|
||||
-- and update last_wrong_time. The idempotency guard table ensures
|
||||
-- each (tenant, user, question, report) can upsert at most once.
|
||||
--
|
||||
-- master_status values: 'PENDING' (default) | 'MASTERED'
|
||||
-- Marking mastered retains the full history and count; it does NOT
|
||||
-- delete or archive the record. Students can optionally un-master.
|
||||
--
|
||||
-- Snapshot fields (stem, type, difficulty, options, content_version):
|
||||
-- populated from the latest report detail that touched this question.
|
||||
-- These are for listing/detail display without joining report details.
|
||||
--
|
||||
-- latest_correct_answer, latest_explanation:
|
||||
-- also from the latest report detail; available for detail display
|
||||
-- post-submit (not exposed in review session creation pre-submit).
|
||||
--
|
||||
-- Indexes:
|
||||
-- uk_tenant_user_question — per-tenant, per-user, per-question uniqueness.
|
||||
-- INSERT ... ON DUPLICATE KEY UPDATE is the primary write path.
|
||||
-- idx_tenant_user_status — covers filtered list queries (page with status filter).
|
||||
-- idx_tenant_user_last_wrong — covers time-sorted listing.
|
||||
CREATE TABLE `education_wrong_question` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`tenant_id` BIGINT NOT NULL COMMENT '租户编号',
|
||||
`user_id` BIGINT NOT NULL COMMENT '学生用户编号',
|
||||
`question_id` VARCHAR(64) NOT NULL COMMENT '原始题目 ID',
|
||||
-- snapshot fields for listing / detail (from latest report detail)
|
||||
`stem` TEXT NOT NULL COMMENT '题干快照(最新)',
|
||||
`type` VARCHAR(32) NOT NULL COMMENT '题型快照',
|
||||
`difficulty` VARCHAR(32) DEFAULT NULL COMMENT '难度快照',
|
||||
`options` JSON NOT NULL COMMENT '选项快照 JSON(不含 isCorrect)',
|
||||
`content_version` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '题目内容版本',
|
||||
`latest_correct_answer` TEXT DEFAULT NULL COMMENT '正确答案快照(最新,供详情展示)',
|
||||
`latest_explanation` TEXT DEFAULT NULL COMMENT '解析快照(最新,供详情展示)',
|
||||
-- timing & count
|
||||
`first_wrong_time` DATETIME NOT NULL COMMENT '首次错误时间',
|
||||
`last_wrong_time` DATETIME NOT NULL COMMENT '最近错误时间',
|
||||
`wrong_count` INT NOT NULL DEFAULT 1 COMMENT '累计错误次数',
|
||||
-- mastery
|
||||
`master_status` VARCHAR(20) NOT NULL DEFAULT 'PENDING'
|
||||
COMMENT '掌握状态:PENDING-待掌握, MASTERED-已掌握',
|
||||
`mastered_time` DATETIME DEFAULT NULL COMMENT '标记掌握时间',
|
||||
-- provenance
|
||||
`last_report_id` BIGINT DEFAULT NULL COMMENT '最近关联的报告 ID',
|
||||
`last_session_id` BIGINT DEFAULT NULL COMMENT '最近关联的会话 ID',
|
||||
-- audit
|
||||
`creator` VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updater` VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tenant_user_question` (`tenant_id`, `user_id`, `question_id`),
|
||||
KEY `idx_tenant_user_status` (`tenant_id`, `user_id`, `master_status`),
|
||||
KEY `idx_tenant_user_last_wrong` (`tenant_id`, `user_id`, `last_wrong_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='教育-错题本';
|
||||
|
||||
-- =============================================
|
||||
-- 错题流水幂等表
|
||||
-- =============================================
|
||||
-- Purpose: Ensure each (tenant, user, question, report) upserts the
|
||||
-- wrong-question book exactly once. The submitSession transaction
|
||||
-- INSERT IGNOREs into this table BEFORE the wrong question upsert;
|
||||
-- a duplicate means this report already contributed to the count.
|
||||
-- This guards against:
|
||||
-- - Replayed submit (idempotent resubmit) double-counting
|
||||
-- - Concurrent submit races where both threads evaluate the
|
||||
-- same report details
|
||||
--
|
||||
-- Indexes:
|
||||
-- uk_tenant_user_question_report — per (tenant, user, question, report) uniqueness.
|
||||
-- INSERT IGNORE provides the idempotency guard BEFORE upserting.
|
||||
-- wrong_question_id is filled after upsert for audit purposes.
|
||||
-- idx_report — fast lookup by report for audit/debug.
|
||||
CREATE TABLE `education_wrong_question_idempotency` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`tenant_id` BIGINT NOT NULL COMMENT '租户编号',
|
||||
`user_id` BIGINT NOT NULL COMMENT '学生用户编号',
|
||||
`wrong_question_id` BIGINT DEFAULT NULL COMMENT '错题记录 ID(upsert 后填充)',
|
||||
`report_id` BIGINT NOT NULL COMMENT '报告 ID',
|
||||
`question_id` VARCHAR(64) NOT NULL COMMENT '题目 ID',
|
||||
`creator` VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updater` VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tenant_user_question_report` (`tenant_id`, `user_id`, `question_id`, `report_id`),
|
||||
KEY `idx_report` (`report_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='教育-错题流水幂等';
|
||||
|
||||
-- =============================================
|
||||
-- Post-migration verification queries
|
||||
-- =============================================
|
||||
-- Verify new tables exist:
|
||||
-- SHOW CREATE TABLE education_wrong_question;
|
||||
-- SHOW CREATE TABLE education_wrong_question_idempotency;
|
||||
-- Verify unique keys are enforced:
|
||||
-- SHOW INDEX FROM education_wrong_question WHERE Key_name = 'uk_tenant_user_question';
|
||||
-- SHOW INDEX FROM education_wrong_question_idempotency WHERE Key_name = 'uk_tenant_user_question_report';
|
||||
-- Verify no orphan data (should be 0 after fresh migration):
|
||||
-- SELECT COUNT(*) FROM education_wrong_question;
|
||||
-- SELECT COUNT(*) FROM education_wrong_question_idempotency;
|
||||
Reference in New Issue
Block a user