102 lines
6.6 KiB
SQL
102 lines
6.6 KiB
SQL
-- =============================================
|
||
-- Education 模块 — 练习会话与题目快照 DDL
|
||
-- Ticket #6: 练习会话创建、题目快照、恢复与状态机
|
||
-- Migration: 002
|
||
-- Prerequisites: 000-education-schema.sql (database creation)
|
||
-- 001-education-tenant-seed.sql (tenant seed data)
|
||
-- =============================================
|
||
|
||
-- =============================================
|
||
-- Preconditions
|
||
-- =============================================
|
||
-- This migration MUST fail if either table already exists (no IF NOT EXISTS).
|
||
-- Operator is expected to verify:
|
||
-- SELECT COUNT(*) FROM information_schema.tables
|
||
-- WHERE table_schema = DATABASE()
|
||
-- AND table_name IN ('education_practice_session', 'education_practice_question');
|
||
-- Result MUST be 0 before executing this migration.
|
||
|
||
-- =============================================
|
||
-- 练习会话表
|
||
-- =============================================
|
||
-- Indexes:
|
||
-- uk_tenant_client_session — per-tenant uniqueness for clientSessionId idempotency.
|
||
-- Used by: selectByTenantAndClientSessionId (idempotent create check),
|
||
-- DuplicateKeyException catch for concurrent-create race resolution.
|
||
-- idx_tenant_user_status — covers getCurrentSession (latest ACTIVE by tenant+user)
|
||
-- and ownership queries. Column order: (tenant_id, user_id, status) so the
|
||
-- index supports both filtering by tenant+user and tenant+user+status.
|
||
-- Lock impact: INSERT acquires next-key lock on uk_tenant_client_session unique key;
|
||
-- concurrent inserts with same (tenant_id, client_session_id) serialize naturally.
|
||
-- No additional table-level locks required.
|
||
CREATE TABLE `education_practice_session` (
|
||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '会话主键',
|
||
`tenant_id` BIGINT NOT NULL COMMENT '租户编号',
|
||
`user_id` BIGINT NOT NULL COMMENT 'Member 用户编号',
|
||
`client_session_id` VARCHAR(36) NOT NULL COMMENT '客户端生成的会话标识(UUID),用于幂等创建',
|
||
`status` VARCHAR(20) NOT NULL DEFAULT 'ACTIVE'
|
||
COMMENT '会话状态:ACTIVE-进行中, SUBMITTED-已提交, EXPIRED-已过期, CANCELLED-已取消',
|
||
`question_count` INT NOT NULL DEFAULT 0 COMMENT '题目总数',
|
||
`collection_id` VARCHAR(64) DEFAULT NULL COMMENT '源题集 ID',
|
||
`node_id` VARCHAR(64) DEFAULT NULL COMMENT '源目录节点 ID',
|
||
`type` VARCHAR(32) DEFAULT NULL COMMENT '筛选题型',
|
||
`difficulty` VARCHAR(32) DEFAULT NULL COMMENT '筛选难度',
|
||
`version` INT NOT NULL DEFAULT 0 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_tenant_client_session` (`tenant_id`, `client_session_id`),
|
||
KEY `idx_tenant_user_status` (`tenant_id`, `user_id`, `status`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='教育-练习会话';
|
||
|
||
-- =============================================
|
||
-- 练习会话题目快照表
|
||
-- =============================================
|
||
-- Indexes:
|
||
-- uk_session_sequence — per-session uniqueness for question sequence numbers.
|
||
-- Used by: insertBatch to ensure no duplicate sequences within a session.
|
||
-- idx_session_id — covers selectBySessionIdOrderBySequence (load all questions
|
||
-- for a session, ordered by sequence). Also used by cascade delete lookups.
|
||
-- Lock impact: INSERT acquires gap locks within session_id range on uk_session_sequence;
|
||
-- concurrent inserts into different sessions are independent.
|
||
-- Options column: JSON data type stores only label, content, order — never isCorrect.
|
||
-- Application layer (optionsToSafeJson) strips correctness before storage.
|
||
CREATE TABLE `education_practice_question` (
|
||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||
`tenant_id` BIGINT NOT NULL COMMENT '租户编号',
|
||
`session_id` BIGINT NOT NULL COMMENT '会话 ID',
|
||
`sequence` INT NOT NULL COMMENT '题目序号(1-based,服务端固定)',
|
||
`question_id` VARCHAR(64) NOT NULL COMMENT '原始题目 ID',
|
||
`content_version` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '快照时的题目内容版本',
|
||
`stem` TEXT NOT NULL COMMENT '题干快照',
|
||
`type` VARCHAR(32) NOT NULL COMMENT '题型快照',
|
||
`difficulty` VARCHAR(32) DEFAULT NULL COMMENT '难度快照',
|
||
`options` JSON NOT NULL COMMENT '选项快照 JSON(不含 isCorrect)',
|
||
`selected_answer` TEXT DEFAULT NULL COMMENT '学生已选答案',
|
||
`is_answered` BIT(1) NOT NULL DEFAULT b'0' 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_session_sequence` (`session_id`, `sequence`),
|
||
KEY `idx_session_id` (`session_id`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='教育-练习会话题目快照';
|
||
|
||
-- =============================================
|
||
-- Post-migration verification queries
|
||
-- =============================================
|
||
-- Verify tables exist with correct structure:
|
||
-- SHOW CREATE TABLE education_practice_session;
|
||
-- SHOW CREATE TABLE education_practice_question;
|
||
-- Verify unique keys are enforced:
|
||
-- SHOW INDEX FROM education_practice_session WHERE Key_name = 'uk_tenant_client_session';
|
||
-- SHOW INDEX FROM education_practice_question WHERE Key_name = 'uk_session_sequence';
|
||
-- Verify no orphan data (should be 0 after fresh migration):
|
||
-- SELECT COUNT(*) FROM education_practice_session;
|
||
-- SELECT COUNT(*) FROM education_practice_question;
|