165 lines
10 KiB
SQL
165 lines
10 KiB
SQL
-- =============================================
|
||
-- Education 模块 — 交卷提交与成绩报告 DDL
|
||
-- Ticket #8: 交卷 CAS、保护性答案快照、评分与报告
|
||
-- Migration: 004
|
||
-- Prerequisites: 003-education-answer-idempotency.sql
|
||
-- =============================================
|
||
|
||
-- =============================================
|
||
-- Preconditions
|
||
-- =============================================
|
||
-- Operator is expected to verify:
|
||
-- SELECT COUNT(*) FROM information_schema.tables
|
||
-- WHERE table_schema = DATABASE()
|
||
-- AND table_name IN ('education_submit_idempotency',
|
||
-- 'education_practice_report',
|
||
-- 'education_practice_report_detail');
|
||
-- 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_session',
|
||
-- 'education_practice_question',
|
||
-- 'education_answer_idempotency');
|
||
-- Result MUST be 3.
|
||
|
||
-- =============================================
|
||
-- PracticeQuestionDO: add protected answer snapshot columns
|
||
-- =============================================
|
||
-- Purpose: At session creation, snapshot correct_answer and explanation
|
||
-- from the full CatalogQuestionDTO. These fields are NEVER exposed
|
||
-- before submission (enforced by SafeQuestionRespVO allow-list and
|
||
-- PracticeQuestionRespVO which does not include them).
|
||
ALTER TABLE `education_practice_question`
|
||
ADD COLUMN `correct_answer` TEXT DEFAULT NULL COMMENT '正确答案快照(不可在交卷前暴露)',
|
||
ADD COLUMN `explanation` TEXT DEFAULT NULL COMMENT '解析快照(不可在交卷前暴露)';
|
||
|
||
-- =============================================
|
||
-- 交卷幂等表
|
||
-- =============================================
|
||
-- Purpose: Provide durable idempotency for submit-session commands.
|
||
-- Same (tenant, user, operation, idempotency_key) + same request_hash → replay original report.
|
||
-- Same key + different request_hash → conflict.
|
||
-- Concurrent same-key inserts resolved by unique constraint race handling.
|
||
--
|
||
-- Indexes:
|
||
-- uk_submit_idempotency — per-tenant, per-actor, per-operation uniqueness for idempotency key.
|
||
-- INSERT during submit. DuplicateKeyException catch for concurrent-create race resolution.
|
||
-- idx_submit_session — covers lookup by session for audit/debug.
|
||
CREATE TABLE `education_submit_idempotency` (
|
||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||
`tenant_id` BIGINT NOT NULL COMMENT '租户编号',
|
||
`user_id` BIGINT NOT NULL COMMENT '交卷用户编号',
|
||
`operation` VARCHAR(32) NOT NULL DEFAULT 'SUBMIT_SESSION'
|
||
COMMENT '操作类型:SUBMIT_SESSION',
|
||
`idempotency_key` VARCHAR(64) NOT NULL COMMENT '客户端幂等键(UUID)',
|
||
`request_hash` VARCHAR(64) NOT NULL COMMENT '请求载荷 SHA-256 哈希',
|
||
`session_id` BIGINT NOT NULL COMMENT '会话 ID',
|
||
`report_id` BIGINT DEFAULT NULL COMMENT '关联的报告 ID(成功时有值)',
|
||
`status` VARCHAR(20) NOT NULL DEFAULT 'ACCEPTED'
|
||
COMMENT '状态:ACCEPTED-已接受, CONFLICT-冲突',
|
||
`response_json` TEXT NOT NULL COMMENT '首次成功响应 JSON(用于重试重放)',
|
||
`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_submit_idempotency` (`tenant_id`, `user_id`, `operation`, `idempotency_key`),
|
||
KEY `idx_submit_session` (`tenant_id`, `session_id`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='教育-交卷幂等记录';
|
||
|
||
-- =============================================
|
||
-- 练习报告表(会话级)
|
||
-- =============================================
|
||
-- Purpose: Store the computed scoring result for a submitted session.
|
||
-- One report per session. Immutable after creation.
|
||
-- Question snapshots (stem, selectedAnswer, correctAnswer, explanation)
|
||
-- are stored in report_details so source question edits don't affect history.
|
||
--
|
||
-- Indexes:
|
||
-- uk_report_session — one report per session (unique).
|
||
-- idx_report_tenant_user — covers paginated history queries for current tenant+user.
|
||
-- idx_report_create_time — covers time-sorted listing.
|
||
CREATE TABLE `education_practice_report` (
|
||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||
`tenant_id` BIGINT NOT NULL COMMENT '租户编号',
|
||
`user_id` BIGINT NOT NULL COMMENT '用户编号',
|
||
`session_id` BIGINT NOT NULL COMMENT '会话 ID',
|
||
`question_count` INT NOT NULL COMMENT '题目总数',
|
||
`answered_count` INT NOT NULL DEFAULT 0 COMMENT '已答题数',
|
||
`unanswered_count` INT NOT NULL DEFAULT 0 COMMENT '未答题数',
|
||
`correct_count` INT NOT NULL DEFAULT 0 COMMENT '正确题数',
|
||
`incorrect_count` INT NOT NULL DEFAULT 0 COMMENT '错误题数',
|
||
`score` INT NOT NULL DEFAULT 0 COMMENT '得分(整数,满分 100 为基准)',
|
||
`status` VARCHAR(20) NOT NULL DEFAULT 'SUBMITTED'
|
||
COMMENT '报告状态:SUBMITTED',
|
||
`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_report_session` (`session_id`),
|
||
KEY `idx_report_tenant_user` (`tenant_id`, `user_id`),
|
||
KEY `idx_report_create_time` (`create_time`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='教育-练习报告';
|
||
|
||
-- =============================================
|
||
-- 练习报告明细表(逐题结果)
|
||
-- =============================================
|
||
-- Purpose: Store per-question scoring results at submission time.
|
||
-- Includes snapshot of stem, selectedAnswer, correctAnswer, and explanation
|
||
-- so that history is stable even if source questions are later edited.
|
||
--
|
||
-- Indexes:
|
||
-- uk_report_sequence — per-report uniqueness for question sequence.
|
||
-- idx_detail_session — covers lookup by session for report assembly.
|
||
CREATE TABLE `education_practice_report_detail` (
|
||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||
`tenant_id` BIGINT NOT NULL COMMENT '租户编号',
|
||
`user_id` BIGINT NOT NULL COMMENT '用户编号',
|
||
`report_id` BIGINT NOT NULL COMMENT '报告 ID',
|
||
`session_id` BIGINT NOT NULL COMMENT '会话 ID',
|
||
`question_id` VARCHAR(64) NOT NULL COMMENT '原始题目 ID',
|
||
`sequence` INT NOT NULL COMMENT '题目序号(1-based)',
|
||
`stem` TEXT NOT NULL COMMENT '题干快照',
|
||
`type` VARCHAR(32) NOT NULL COMMENT '题型快照',
|
||
`difficulty` VARCHAR(32) DEFAULT NULL COMMENT '难度快照',
|
||
`selected_answer` TEXT DEFAULT NULL COMMENT '学生已选答案',
|
||
`correct_answer` TEXT DEFAULT NULL COMMENT '正确答案快照',
|
||
`is_correct` BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否正确',
|
||
`explanation` TEXT DEFAULT NULL COMMENT '解析快照',
|
||
`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_report_sequence` (`report_id`, `sequence`),
|
||
KEY `idx_detail_session` (`session_id`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='教育-练习报告明细';
|
||
|
||
-- =============================================
|
||
-- Post-migration verification queries
|
||
-- =============================================
|
||
-- Verify new tables exist:
|
||
-- SHOW CREATE TABLE education_submit_idempotency;
|
||
-- SHOW CREATE TABLE education_practice_report;
|
||
-- SHOW CREATE TABLE education_practice_report_detail;
|
||
-- Verify columns added to question table:
|
||
-- SELECT COLUMN_NAME, DATA_TYPE, COLUMN_DEFAULT
|
||
-- FROM information_schema.COLUMNS
|
||
-- WHERE TABLE_SCHEMA = DATABASE()
|
||
-- AND TABLE_NAME = 'education_practice_question'
|
||
-- AND COLUMN_NAME IN ('correct_answer', 'explanation');
|
||
-- Verify unique keys are enforced:
|
||
-- SHOW INDEX FROM education_submit_idempotency WHERE Key_name = 'uk_submit_idempotency';
|
||
-- SHOW INDEX FROM education_practice_report WHERE Key_name = 'uk_report_session';
|
||
-- SHOW INDEX FROM education_practice_report_detail WHERE Key_name = 'uk_report_sequence';
|
||
-- Verify no orphan data (should be 0 after fresh migration):
|
||
-- SELECT COUNT(*) FROM education_submit_idempotency;
|
||
-- SELECT COUNT(*) FROM education_practice_report;
|
||
-- SELECT COUNT(*) FROM education_practice_report_detail;
|