feat(db): 新增Kingbase数据库V2迁移与回滚脚本
This commit is contained in:
1 parent
277c9e210e
commit
c791e23f3e
17 files changed
+3386
No files matched your search
@@ -0,0 +1,435 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移前置检查: 000_preflight.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 严格只读核验当前数据库完全符合 Phase 0.5 精确结构骨架与数据基线
|
||||
-- 设计依据: rollback/000_phase05_exact_schema.sql 与 13_live_database_snapshot.md
|
||||
-- 验收机制:
|
||||
-- 1. 纯只读探测,零写入,零结构修改
|
||||
-- 2. 基于预期元数据集合 (VALUES CTE) 进行全集合差异断言,杜绝数量对冲假象
|
||||
-- 3. 精确校验: 14旧业务表PK + 24旧业务FK + 7组业务UNIQUE + 6个identity主键 + 9个触发器
|
||||
-- 4. 任何 missing / unexpected / definition mismatch 立即抛出 EXCEPTION 中止后续迁移
|
||||
-- ==============================================================================
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
v_schema TEXT;
|
||||
v_cnt INT;
|
||||
v_diff_text TEXT := '';
|
||||
r RECORD;
|
||||
BEGIN
|
||||
-- ==========================================================================
|
||||
-- 1. 会话 Schema 校验 (必须显式为 public)
|
||||
-- ==========================================================================
|
||||
SELECT current_schema() INTO v_schema;
|
||||
IF v_schema <> 'public' THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] 当前会话 schema 为 "%",必须显式为 "public"', v_schema;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 2. 14 张 Phase 0.5 历史基线表双向集合差异检查 (精确存在且仅存在这 14 张 train_* 表)
|
||||
-- ==========================================================================
|
||||
v_diff_text := '';
|
||||
FOR r IN (
|
||||
WITH expected_tables(table_name) AS (
|
||||
VALUES
|
||||
('train_teacher'), ('train_student'), ('train_course'), ('train_class_plan'),
|
||||
('train_dormitory_info'), ('train_dorm_auth'), ('train_classroom_info'), ('train_training_site'),
|
||||
('train_class_report'), ('train_electronic_board'), ('train_student_checkin'), ('train_score_info'),
|
||||
('train_study_duration'), ('train_student_portrait')
|
||||
),
|
||||
actual_tables AS (
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name LIKE 'train_%'
|
||||
)
|
||||
SELECT 'MISSING_TABLE' AS diff_type, e.table_name
|
||||
FROM expected_tables e
|
||||
LEFT JOIN actual_tables a ON e.table_name = a.table_name
|
||||
WHERE a.table_name IS NULL
|
||||
UNION ALL
|
||||
SELECT 'UNEXPECTED_TABLE' AS diff_type, a.table_name
|
||||
FROM actual_tables a
|
||||
LEFT JOIN expected_tables e ON a.table_name = e.table_name
|
||||
WHERE e.table_name IS NULL
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.diff_type || ': ' || r.table_name || '; ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] Phase 0.5 历史基线表集合全等校验失败: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 3. 检查 8 张 V2 新增表均不得存在 (杜绝半迁移脏环境)
|
||||
-- ==========================================================================
|
||||
v_diff_text := '';
|
||||
FOR r IN (
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name IN (
|
||||
'train_resource', 'train_course_resource', 'train_plan_course', 'train_enrollment',
|
||||
'train_lesson', 'train_accommodation', 'train_score_item', 'train_student_score'
|
||||
)
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.table_name || ', ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] 检测到已存在 V2 新表,当前非纯净 Phase 0.5 基线: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 4. 数据行数基线: train_teacher 精确 1 行,其余 13 张旧表精确 0 行
|
||||
-- ==========================================================================
|
||||
SELECT count(*) INTO v_cnt FROM train_teacher;
|
||||
IF v_cnt <> 1 THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] train_teacher 行数期望为 1,实际为 %', v_cnt;
|
||||
END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_student;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_student 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_course;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_course 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_class_plan;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_class_plan 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_dormitory_info;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_dormitory_info 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_dorm_auth;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_dorm_auth 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_classroom_info;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_classroom_info 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_training_site;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_training_site 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_class_report;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_class_report 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_electronic_board;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_electronic_board 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_student_checkin;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_student_checkin 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_score_info;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_score_info 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_study_duration;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_study_duration 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_student_portrait;
|
||||
IF v_cnt <> 0 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] train_student_portrait 非空,包含 % 行', v_cnt; END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 5. 14 个 Phase 0.5 旧业务主键 (PK) 精确骨架校验
|
||||
-- ==========================================================================
|
||||
v_diff_text := '';
|
||||
FOR r IN (
|
||||
WITH expected_pk(table_name, constraint_name, column_name) AS (
|
||||
VALUES
|
||||
('train_teacher', 'pk_train_teacher', 't_id'),
|
||||
('train_student', 'pk_train_student', 's_id'),
|
||||
('train_course', 'pk_train_course', 'c_id'),
|
||||
('train_class_plan', 'pk_train_class_plan', 'plan_id'),
|
||||
('train_dormitory_info', 'pk_train_dormitory_info', 'dorm_id'),
|
||||
('train_dorm_auth', 'pk_train_dorm_auth', 'auth_id'),
|
||||
('train_classroom_info', 'pk_train_classroom_info', 'class_room_id'),
|
||||
('train_training_site', 'pk_train_training_site', 'site_id'),
|
||||
('train_class_report', 'pk_train_class_report', 'report_id'),
|
||||
('train_electronic_board', 'pk_train_electronic_board', 'board_id'),
|
||||
('train_student_checkin', 'pk_train_student_checkin', 'checkin_id'),
|
||||
('train_score_info', 'pk_train_score_info', 'score_id'),
|
||||
('train_study_duration', 'pk_train_study_duration', 'dur_id'),
|
||||
('train_student_portrait', 'pk_train_student_portrait', 'portrait_id')
|
||||
),
|
||||
actual_pk AS (
|
||||
SELECT tc.table_name, tc.constraint_name, kcu.column_name
|
||||
FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.key_column_usage kcu
|
||||
ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
|
||||
WHERE tc.constraint_type = 'PRIMARY KEY'
|
||||
AND tc.table_schema = 'public'
|
||||
AND tc.table_name LIKE 'train_%'
|
||||
)
|
||||
SELECT 'MISSING_PK' AS diff_type, e.table_name, e.constraint_name, e.column_name
|
||||
FROM expected_pk e
|
||||
LEFT JOIN actual_pk a
|
||||
ON e.table_name = a.table_name AND e.constraint_name = a.constraint_name AND e.column_name = a.column_name
|
||||
WHERE a.table_name IS NULL
|
||||
UNION ALL
|
||||
SELECT 'UNEXPECTED_PK' AS diff_type, a.table_name, a.constraint_name, a.column_name
|
||||
FROM actual_pk a
|
||||
LEFT JOIN expected_pk e
|
||||
ON a.table_name = e.table_name AND a.constraint_name = e.constraint_name AND a.column_name = e.column_name
|
||||
WHERE e.table_name IS NULL
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.diff_type || ': ' || r.table_name || '.' || r.constraint_name || '(' || r.column_name || '); ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] Phase 0.5 主键 (PK) 骨架不匹配: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 6. 6 个 Phase 0.5 Identity 自增主键精确校验
|
||||
-- ==========================================================================
|
||||
v_diff_text := '';
|
||||
FOR r IN (
|
||||
WITH expected_identity(table_name, column_name) AS (
|
||||
VALUES
|
||||
('train_dorm_auth', 'auth_id'),
|
||||
('train_class_report', 'report_id'),
|
||||
('train_student_checkin', 'checkin_id'),
|
||||
('train_score_info', 'score_id'),
|
||||
('train_study_duration', 'dur_id'),
|
||||
('train_student_portrait', 'portrait_id')
|
||||
),
|
||||
actual_identity AS (
|
||||
SELECT table_name, column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name LIKE 'train_%'
|
||||
AND is_identity = 'YES'
|
||||
)
|
||||
SELECT 'MISSING_IDENTITY' AS diff_type, e.table_name, e.column_name
|
||||
FROM expected_identity e
|
||||
LEFT JOIN actual_identity a
|
||||
ON e.table_name = a.table_name AND e.column_name = a.column_name
|
||||
WHERE a.table_name IS NULL
|
||||
UNION ALL
|
||||
SELECT 'UNEXPECTED_IDENTITY' AS diff_type, a.table_name, a.column_name
|
||||
FROM actual_identity a
|
||||
LEFT JOIN expected_identity e
|
||||
ON a.table_name = e.table_name AND a.column_name = e.column_name
|
||||
WHERE e.table_name IS NULL
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.diff_type || ': ' || r.table_name || '.' || r.column_name || '; ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] Phase 0.5 Identity 属性不匹配: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 7. 7 组 Phase 0.5 历史业务唯一约束 (UNIQUE) 骨架精确校验
|
||||
-- ==========================================================================
|
||||
v_diff_text := '';
|
||||
FOR r IN (
|
||||
WITH expected_unique(table_name, constraint_name, column_list) AS (
|
||||
VALUES
|
||||
('train_dormitory_info', 'uq_dorm_building_room', 'dorm_building, dorm_room'),
|
||||
('train_dorm_auth', 'uq_dormauth_stu_plan', 'student_id, plan_id'),
|
||||
('train_class_report', 'uq_classreport_plan_stu', 'plan_id, student_id'),
|
||||
('train_electronic_board', 'uq_board_sn', 'board_sn'),
|
||||
('train_student_checkin', 'uq_checkin_lesson_stu', 'plan_id, c_id, student_id, lesson_start_time'),
|
||||
('train_score_info', 'uq_scoreinfo_plan_c_stu', 'plan_id, c_id, student_id'),
|
||||
('train_student_portrait', 'uq_portrait_stu_plan', 'student_id, plan_id')
|
||||
),
|
||||
actual_unique AS (
|
||||
SELECT
|
||||
tc.table_name,
|
||||
tc.constraint_name,
|
||||
string_agg(kcu.column_name, ', ' ORDER BY kcu.ordinal_position) AS column_list
|
||||
FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.key_column_usage kcu
|
||||
ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
|
||||
WHERE tc.constraint_type = 'UNIQUE'
|
||||
AND tc.table_schema = 'public'
|
||||
AND tc.table_name LIKE 'train_%'
|
||||
GROUP BY tc.table_name, tc.constraint_name
|
||||
)
|
||||
SELECT 'MISSING_UNIQUE' AS diff_type, e.table_name, e.constraint_name, e.column_list
|
||||
FROM expected_unique e
|
||||
LEFT JOIN actual_unique a
|
||||
ON e.table_name = a.table_name AND e.constraint_name = a.constraint_name AND e.column_list = a.column_list
|
||||
WHERE a.table_name IS NULL
|
||||
UNION ALL
|
||||
SELECT 'UNEXPECTED_UNIQUE' AS diff_type, a.table_name, a.constraint_name, a.column_list
|
||||
FROM actual_unique a
|
||||
LEFT JOIN expected_unique e
|
||||
ON a.table_name = e.table_name AND a.constraint_name = e.constraint_name AND a.column_list = e.column_list
|
||||
WHERE e.table_name IS NULL
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.diff_type || ': ' || r.table_name || '.' || r.constraint_name || '(' || r.column_list || '); ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] Phase 0.5 唯一约束 (UNIQUE) 骨架不匹配: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 8. 24 组 Phase 0.5 历史业务外键约束 (FK) 骨架精确校验 (名称+所属表+源列+引用表+引用列)
|
||||
-- ==========================================================================
|
||||
v_diff_text := '';
|
||||
FOR r IN (
|
||||
WITH expected_fk(table_name, constraint_name, column_name, foreign_table_name, foreign_column_name) AS (
|
||||
VALUES
|
||||
('train_class_plan', 'fk_classplan_course', 'c_id', 'train_course', 'c_id'),
|
||||
('train_class_plan', 'fk_classplan_teacher', 't_id', 'train_teacher', 't_id'),
|
||||
('train_dorm_auth', 'fk_dormauth_stu', 'student_id', 'train_student', 's_id'),
|
||||
('train_dorm_auth', 'fk_dormauth_plan', 'plan_id', 'train_class_plan', 'plan_id'),
|
||||
('train_dorm_auth', 'fk_dormauth_dorm', 'dorm_id', 'train_dormitory_info', 'dorm_id'),
|
||||
('train_class_report', 'fk_classreport_plan', 'plan_id', 'train_class_plan', 'plan_id'),
|
||||
('train_class_report', 'fk_classreport_stu', 'student_id', 'train_student', 's_id'),
|
||||
('train_class_report', 'fk_classreport_dorm', 'alloc_dorm_id', 'train_dormitory_info', 'dorm_id'),
|
||||
('train_class_report', 'fk_classreport_classroom', 'alloc_classroom_id', 'train_classroom_info', 'class_room_id'),
|
||||
('train_class_report', 'fk_classreport_site', 'alloc_site_id', 'train_training_site', 'site_id'),
|
||||
('train_electronic_board', 'fk_board_classroom', 'bind_classroom_id', 'train_classroom_info', 'class_room_id'),
|
||||
('train_electronic_board', 'fk_board_plan', 'bind_plan_id', 'train_class_plan', 'plan_id'),
|
||||
('train_student_checkin', 'fk_checkin_plan', 'plan_id', 'train_class_plan', 'plan_id'),
|
||||
('train_student_checkin', 'fk_checkin_course', 'c_id', 'train_course', 'c_id'),
|
||||
('train_student_checkin', 'fk_checkin_stu', 'student_id', 'train_student', 's_id'),
|
||||
('train_student_checkin', 'fk_checkin_board', 'board_id', 'train_electronic_board', 'board_id'),
|
||||
('train_score_info', 'fk_scoreinfo_plan', 'plan_id', 'train_class_plan', 'plan_id'),
|
||||
('train_score_info', 'fk_scoreinfo_course', 'c_id', 'train_course', 'c_id'),
|
||||
('train_score_info', 'fk_scoreinfo_stu', 'student_id', 'train_student', 's_id'),
|
||||
('train_study_duration', 'fk_studydur_stu', 'student_id', 'train_student', 's_id'),
|
||||
('train_study_duration', 'fk_studydur_plan', 'plan_id', 'train_class_plan', 'plan_id'),
|
||||
('train_study_duration', 'fk_studydur_course', 'c_id', 'train_course', 'c_id'),
|
||||
('train_student_portrait', 'fk_portrait_stu', 'student_id', 'train_student', 's_id'),
|
||||
('train_student_portrait', 'fk_portrait_plan', 'plan_id', 'train_class_plan', 'plan_id')
|
||||
),
|
||||
actual_fk AS (
|
||||
SELECT
|
||||
tc.table_name,
|
||||
tc.constraint_name,
|
||||
kcu.column_name,
|
||||
ccu.table_name AS foreign_table_name,
|
||||
ccu.column_name AS foreign_column_name
|
||||
FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.key_column_usage kcu
|
||||
ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
|
||||
JOIN information_schema.constraint_column_usage ccu
|
||||
ON tc.constraint_name = ccu.constraint_name AND tc.table_schema = ccu.table_schema
|
||||
WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_schema = 'public'
|
||||
AND tc.table_name LIKE 'train_%'
|
||||
)
|
||||
SELECT 'MISSING_FK' AS diff_type, e.table_name, e.constraint_name, e.column_name, e.foreign_table_name, e.foreign_column_name
|
||||
FROM expected_fk e
|
||||
LEFT JOIN actual_fk a
|
||||
ON e.table_name = a.table_name
|
||||
AND e.constraint_name = a.constraint_name
|
||||
AND e.column_name = a.column_name
|
||||
AND e.foreign_table_name = a.foreign_table_name
|
||||
AND e.foreign_column_name = a.foreign_column_name
|
||||
WHERE a.table_name IS NULL
|
||||
UNION ALL
|
||||
SELECT 'UNEXPECTED_FK' AS diff_type, a.table_name, a.constraint_name, a.column_name, a.foreign_table_name, a.foreign_column_name
|
||||
FROM actual_fk a
|
||||
LEFT JOIN expected_fk e
|
||||
ON a.table_name = e.table_name
|
||||
AND a.constraint_name = e.constraint_name
|
||||
AND a.column_name = e.column_name
|
||||
AND a.foreign_table_name = e.foreign_table_name
|
||||
AND a.foreign_column_name = e.foreign_column_name
|
||||
WHERE e.table_name IS NULL
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.diff_type || ': ' || r.table_name || '.' || r.constraint_name || ' (' || r.column_name || ' -> ' || r.foreign_table_name || '.' || r.foreign_column_name || '); ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] Phase 0.5 外键约束 (FK) 骨架不匹配: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 9. 9 个 Phase 0.5 历史 update_time 触发器精确校验
|
||||
-- ==========================================================================
|
||||
v_diff_text := '';
|
||||
FOR r IN (
|
||||
WITH expected_triggers(table_name, trigger_name) AS (
|
||||
VALUES
|
||||
('train_teacher', 'trig_train_teacher_modify'),
|
||||
('train_student', 'trig_train_student_modify'),
|
||||
('train_course', 'trig_train_course_modify'),
|
||||
('train_classroom_info', 'trig_train_classroom_modify'),
|
||||
('train_training_site', 'trig_train_trainsite_modify'),
|
||||
('train_electronic_board', 'trig_train_board_modify'),
|
||||
('train_dormitory_info', 'trig_train_dorminfo_modify'),
|
||||
('train_class_plan', 'trig_train_classplan_modify'),
|
||||
('train_dorm_auth', 'trig_train_dormauth_modify')
|
||||
),
|
||||
actual_triggers AS (
|
||||
SELECT event_object_table AS table_name, trigger_name
|
||||
FROM information_schema.triggers
|
||||
WHERE trigger_schema = 'public'
|
||||
AND event_object_table LIKE 'train_%'
|
||||
)
|
||||
SELECT 'MISSING_TRIGGER' AS diff_type, e.table_name, e.trigger_name
|
||||
FROM expected_triggers e
|
||||
LEFT JOIN actual_triggers a
|
||||
ON e.table_name = a.table_name AND e.trigger_name = a.trigger_name
|
||||
WHERE a.table_name IS NULL
|
||||
UNION ALL
|
||||
SELECT 'UNEXPECTED_TRIGGER' AS diff_type, a.table_name, a.trigger_name
|
||||
FROM actual_triggers a
|
||||
LEFT JOIN expected_triggers e
|
||||
ON a.table_name = e.table_name AND a.trigger_name = e.trigger_name
|
||||
WHERE e.table_name IS NULL
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.diff_type || ': ' || r.table_name || '.' || r.trigger_name || '; ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] Phase 0.5 触发器骨架不匹配: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 10. 历史旧字段特征校验 (确认是旧模型结构,未提前被执行过 V2)
|
||||
-- ==========================================================================
|
||||
SELECT count(*) INTO v_cnt FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_class_plan' AND column_name IN ('c_id', 't_id');
|
||||
IF v_cnt <> 2 THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] train_class_plan 未包含旧 c_id 与 t_id 列,结构已被篡改';
|
||||
END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_student_checkin' AND column_name = 'lesson_start_time';
|
||||
IF v_cnt <> 1 THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] train_student_checkin 结构异常,未发现旧 lesson_start_time 列';
|
||||
END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_score_info'
|
||||
AND column_name IN ('course_score', 'train_score', 'exam_score');
|
||||
IF v_cnt <> 3 THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] train_score_info 结构异常,未包含旧成绩列';
|
||||
END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_class_report'
|
||||
AND column_name IN ('alloc_dorm_id', 'alloc_classroom_id', 'alloc_site_id');
|
||||
IF v_cnt <> 3 THEN
|
||||
RAISE EXCEPTION '>>> [Preflight-FATAL] train_class_report 结构异常,未包含旧预分配列';
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 11. public.sys_* 系统基础表零受损基线核验
|
||||
-- ==========================================================================
|
||||
SELECT count(*) INTO v_cnt FROM public.sys_user;
|
||||
IF v_cnt <> 1 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] 基线 sys_user 行数受损,当前为 %', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM public.sys_menu;
|
||||
IF v_cnt <> 106 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] 基线 sys_menu 行数受损,当前为 %', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM public.sys_dept;
|
||||
IF v_cnt <> 15 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] 基线 sys_dept 行数受损,当前为 %', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM public.sys_dict_type;
|
||||
IF v_cnt <> 44 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] 基线 sys_dict_type 行数受损,当前为 %', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM public.sys_dict_data;
|
||||
IF v_cnt <> 500 THEN RAISE EXCEPTION '>>> [Preflight-FATAL] 基线 sys_dict_data 行数受损,当前为 %', v_cnt; END IF;
|
||||
|
||||
RAISE NOTICE '==============================================================================';
|
||||
RAISE NOTICE '>>> [Preflight-PASS] Phase 0.5 精确骨架校验全部通过!';
|
||||
RAISE NOTICE '>>> 核验项: 14旧表 + 14 PK + 24 FK + 7 UNIQUE + 6 Identity + 9 Triggers + 行数基线';
|
||||
RAISE NOTICE '>>> 判定结果: 当前数据库处于纯净 Phase 0.5 历史基线状态,允许执行 V2 迁移。';
|
||||
RAISE NOTICE '==============================================================================';
|
||||
END $$;
|
||||
@@ -0,0 +1,38 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移安全门禁 A: 000_tx_capability_probe.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public, database_mode: mysql)
|
||||
-- 执行目标: 在正式执行迁移前,实证探测当前数据库实例是否支持 DDL 事务回滚能力
|
||||
-- 约束原则:
|
||||
-- 1. 本文件为【前置能力探针】,绝不读取或修改任何 train_* 业务表与 sys_* 系统表
|
||||
-- 2. 仅在临时对象 public.__railway_v2_tx_probe 上执行可逆测试
|
||||
-- 3. 若 ROLLBACK 后探针表仍旧残留,表明 DDL 隐式自动提交,立即强行熔断后续迁移
|
||||
-- ==============================================================================
|
||||
|
||||
-- Phase A: 启动显式事务,创建并修改探针临时表,随后主动 ROLLBACK
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE public.__railway_v2_tx_probe (
|
||||
id INTEGER
|
||||
);
|
||||
|
||||
ALTER TABLE public.__railway_v2_tx_probe
|
||||
ADD COLUMN test_col INTEGER;
|
||||
|
||||
ROLLBACK;
|
||||
|
||||
-- Phase B: 只读断言验证探针表是否被完整撤销
|
||||
DO $$
|
||||
DECLARE
|
||||
v_probe_exists INT;
|
||||
BEGIN
|
||||
SELECT count(*) INTO v_probe_exists
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name = '__railway_v2_tx_probe';
|
||||
|
||||
IF v_probe_exists > 0 THEN
|
||||
-- 发现探针表未被回滚,说明数据库引擎在执行 DDL 时发生了隐式提交
|
||||
RAISE EXCEPTION '>>> [Probe-FATAL] 当前 Kingbase 实例 DDL 不满足 V2 迁移事务假设 (DDL 在 ROLLBACK 后依然残留),禁止继续执行 001~010 迁移!';
|
||||
ELSE
|
||||
RAISE NOTICE '>>> [Probe-PASS] 数据库 DDL 事务原子性验证通过:Kingbase 实例支持 DDL 事务回滚,符合 V2 迁移安全前提。';
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,103 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移脚本: 001_backup_current_schema.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 备份当前 14 张培训旧表的数据与列结构快照
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 安全原则:
|
||||
-- 1. 禁止覆盖已有备份: 执行前检查若存在任意 bak_v1_* 表,立即抛出 EXCEPTION 强行阻断
|
||||
-- 2. 严禁使用 DROP TABLE IF EXISTS bak_v1_* 自动抹除已有备份
|
||||
-- 3. 严禁触碰任何 sys_* 系统表
|
||||
-- 4. 说明: CREATE TABLE AS SELECT 在 KingbaseES 中仅复制列名、类型与数据,
|
||||
-- 不承担完整 DDL 快照职责 (完整结构备份由 rollback/000_phase05_exact_schema.sql 承担)
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 步骤 1: 强防重与防覆盖检查 (杜绝二次执行覆盖原始 V1 快照)
|
||||
DO $$
|
||||
DECLARE
|
||||
v_bak_cnt INT;
|
||||
v_existing_baks TEXT := '';
|
||||
r RECORD;
|
||||
BEGIN
|
||||
FOR r IN (
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = current_schema() AND table_name LIKE 'bak_v1_%'
|
||||
) LOOP
|
||||
v_existing_baks := v_existing_baks || r.table_name || ', ';
|
||||
END LOOP;
|
||||
|
||||
IF v_existing_baks <> '' THEN
|
||||
RAISE EXCEPTION '>>> [001-FATAL] 检测到既有备份表已经存在: [%]。为防止意外覆盖真实 V1 历史数据,严禁重复执行 001 备份!如需重新备份,请由 DBA 手动确认后归档清理既有 bak_v1_* 表。', v_existing_baks;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 步骤 2: 备份具备真实数据的核心主数据表: train_teacher (当前环境精确 1 行)
|
||||
CREATE TABLE bak_v1_train_teacher AS
|
||||
SELECT * FROM train_teacher;
|
||||
COMMENT ON TABLE bak_v1_train_teacher IS 'Phase 0.5 历史备份: 教师主档 (含唯一真实数据)';
|
||||
|
||||
-- 步骤 3: 备份其余 13 张现有空表 (当前行数均为 0,备份数据与字段基线)
|
||||
CREATE TABLE bak_v1_train_student AS SELECT * FROM train_student;
|
||||
COMMENT ON TABLE bak_v1_train_student IS 'Phase 0.5 历史备份: 学员表快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_course AS SELECT * FROM train_course;
|
||||
COMMENT ON TABLE bak_v1_train_course IS 'Phase 0.5 历史备份: 课程表快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_class_plan AS SELECT * FROM train_class_plan;
|
||||
COMMENT ON TABLE bak_v1_train_class_plan IS 'Phase 0.5 历史备份: 开班计划快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_dormitory_info AS SELECT * FROM train_dormitory_info;
|
||||
COMMENT ON TABLE bak_v1_train_dormitory_info IS 'Phase 0.5 历史备份: 宿舍信息快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_dorm_auth AS SELECT * FROM train_dorm_auth;
|
||||
COMMENT ON TABLE bak_v1_train_dorm_auth IS 'Phase 0.5 历史备份: 宿舍门禁快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_classroom_info AS SELECT * FROM train_classroom_info;
|
||||
COMMENT ON TABLE bak_v1_train_classroom_info IS 'Phase 0.5 历史备份: 教室信息快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_training_site AS SELECT * FROM train_training_site;
|
||||
COMMENT ON TABLE bak_v1_train_training_site IS 'Phase 0.5 历史备份: 实训场地快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_class_report AS SELECT * FROM train_class_report;
|
||||
COMMENT ON TABLE bak_v1_train_class_report IS 'Phase 0.5 历史备份: 报到记录快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_electronic_board AS SELECT * FROM train_electronic_board;
|
||||
COMMENT ON TABLE bak_v1_train_electronic_board IS 'Phase 0.5 历史备份: 电子班牌快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_student_checkin AS SELECT * FROM train_student_checkin;
|
||||
COMMENT ON TABLE bak_v1_train_student_checkin IS 'Phase 0.5 历史备份: 考勤签到快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_score_info AS SELECT * FROM train_score_info;
|
||||
COMMENT ON TABLE bak_v1_train_score_info IS 'Phase 0.5 历史备份: 旧成绩表快照 (待淘汰)';
|
||||
|
||||
CREATE TABLE bak_v1_train_study_duration AS SELECT * FROM train_study_duration;
|
||||
COMMENT ON TABLE bak_v1_train_study_duration IS 'Phase 0.5 历史备份: 学习时长快照';
|
||||
|
||||
CREATE TABLE bak_v1_train_student_portrait AS SELECT * FROM train_student_portrait;
|
||||
COMMENT ON TABLE bak_v1_train_student_portrait IS 'Phase 0.5 历史备份: 学员画像快照';
|
||||
|
||||
-- 步骤 4: 校验备份完整性
|
||||
DO $$
|
||||
DECLARE
|
||||
v_teacher_cnt INT;
|
||||
v_bak_total INT;
|
||||
BEGIN
|
||||
SELECT count(*) INTO v_teacher_cnt FROM bak_v1_train_teacher;
|
||||
IF v_teacher_cnt <> 1 THEN
|
||||
RAISE EXCEPTION '>>> [001-FATAL] 备份校验失败: bak_v1_train_teacher 期望 1 条数据,实际为 % 条', v_teacher_cnt;
|
||||
END IF;
|
||||
|
||||
SELECT count(*) INTO v_bak_total
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = current_schema() AND table_name LIKE 'bak_v1_%';
|
||||
|
||||
IF v_bak_total <> 14 THEN
|
||||
RAISE EXCEPTION '>>> [001-FATAL] 备份表总数异常: 期望 14 张备份表,实际生成 % 张', v_bak_total;
|
||||
END IF;
|
||||
|
||||
RAISE NOTICE '>>> [001-PASS] 14 张表快照备份全部完成,教师唯一真实数据已安全封存。';
|
||||
END $$;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,213 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移脚本: 002_create_new_tables.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 创建数据库 V2 新增的 8 张核心培训业务表
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 设计依据: RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0 及 Round 1 审查要求
|
||||
-- 规范要求:
|
||||
-- 1. 主键为 bigint GENERATED BY DEFAULT AS IDENTITY 或 varchar(32)
|
||||
-- 2. 时间类型统一使用 timestamp,禁用 DATETIME / ON UPDATE CURRENT_TIMESTAMP
|
||||
-- 3. 状态与布尔统一使用 smallint
|
||||
-- 4. train_lesson.t_id 设为 NOT NULL
|
||||
-- 5. train_student_score.score 设为 NOT NULL (删除 DEFAULT 0) 并增加 score >= 0 CHECK
|
||||
-- 6. train_lesson 增加 venue_type 与教室/场地的单行互斥 CHECK
|
||||
-- 7. 字典语义严格对齐 V2 冻结稿
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 1. 统一教学资源主表
|
||||
CREATE TABLE train_resource (
|
||||
resource_id VARCHAR(32) NOT NULL,
|
||||
resource_name VARCHAR(150) NOT NULL,
|
||||
resource_type SMALLINT NOT NULL,
|
||||
resource_url TEXT NOT NULL,
|
||||
description TEXT,
|
||||
status SMALLINT NOT NULL DEFAULT 1,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
del_flag CHAR(1) NOT NULL DEFAULT '0',
|
||||
CONSTRAINT pk_train_resource PRIMARY KEY (resource_id)
|
||||
);
|
||||
COMMENT ON TABLE train_resource IS '统一教学资源表 (文档、视频、3D模型、VR资源、外部教学链接、其他)';
|
||||
COMMENT ON COLUMN train_resource.resource_id IS '资源唯一ID';
|
||||
COMMENT ON COLUMN train_resource.resource_name IS '资源名称';
|
||||
COMMENT ON COLUMN train_resource.resource_type IS '资源类型 (1:文档, 2:视频, 3:3D模型, 4:VR资源, 5:外部教学链接, 6:其他)';
|
||||
COMMENT ON COLUMN train_resource.resource_url IS '资源存储地址/在线链接';
|
||||
COMMENT ON COLUMN train_resource.description IS '资源描述/使用说明';
|
||||
COMMENT ON COLUMN train_resource.status IS '状态 (1:正常, 0:停用)';
|
||||
COMMENT ON COLUMN train_resource.del_flag IS '删除标志 (0:存在, 2:删除)';
|
||||
|
||||
-- 2. 课程与资源多对多关联表
|
||||
CREATE TABLE train_course_resource (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
c_id VARCHAR(32) NOT NULL,
|
||||
resource_id VARCHAR(32) NOT NULL,
|
||||
sort_no INTEGER DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT pk_train_course_resource PRIMARY KEY (id)
|
||||
);
|
||||
COMMENT ON TABLE train_course_resource IS '课程与教学资源关联表 (N:M)';
|
||||
COMMENT ON COLUMN train_course_resource.id IS '自增主键';
|
||||
COMMENT ON COLUMN train_course_resource.c_id IS '课程ID';
|
||||
COMMENT ON COLUMN train_course_resource.resource_id IS '资源ID';
|
||||
COMMENT ON COLUMN train_course_resource.sort_no IS '排序号';
|
||||
|
||||
-- 3. 培训班与课程关联表
|
||||
CREATE TABLE train_plan_course (
|
||||
plan_course_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
c_id VARCHAR(32) NOT NULL,
|
||||
sort_no INTEGER DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_plan_course PRIMARY KEY (plan_course_id)
|
||||
);
|
||||
COMMENT ON TABLE train_plan_course IS '培训班课程关联表 (一期培训包含哪些课程)';
|
||||
COMMENT ON COLUMN train_plan_course.plan_course_id IS '培训课程实例ID (核心ID之一)';
|
||||
COMMENT ON COLUMN train_plan_course.plan_id IS '培训班ID';
|
||||
COMMENT ON COLUMN train_plan_course.c_id IS '课程ID';
|
||||
COMMENT ON COLUMN train_plan_course.sort_no IS '排课先后顺序号';
|
||||
|
||||
-- 4. 参训学员登记表 (系统最核心关系: 谁参加哪个班)
|
||||
CREATE TABLE train_enrollment (
|
||||
enrollment_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
student_id VARCHAR(32) NOT NULL,
|
||||
source_type SMALLINT NOT NULL,
|
||||
enrollment_status SMALLINT NOT NULL DEFAULT 0,
|
||||
accommodation_required SMALLINT NOT NULL,
|
||||
join_time TIMESTAMP,
|
||||
complete_time TIMESTAMP,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_enrollment PRIMARY KEY (enrollment_id)
|
||||
);
|
||||
COMMENT ON TABLE train_enrollment IS '参训学员登记表 (学员参加培训班全生命周期主表)';
|
||||
COMMENT ON COLUMN train_enrollment.enrollment_id IS '参训档案ID (核心ID之一)';
|
||||
COMMENT ON COLUMN train_enrollment.plan_id IS '培训班ID';
|
||||
COMMENT ON COLUMN train_enrollment.student_id IS '学员长期主档ID引用';
|
||||
COMMENT ON COLUMN train_enrollment.source_type IS '参训名单来源,具体字典值由 train_enrollment_source 定义';
|
||||
COMMENT ON COLUMN train_enrollment.enrollment_status IS '参训状态 (0:待报到, 1:已报到, 2:请假, 3:放弃, 4:已完成)';
|
||||
COMMENT ON COLUMN train_enrollment.accommodation_required IS '是否需要住宿 (0:否, 1:是)';
|
||||
COMMENT ON COLUMN train_enrollment.join_time IS '入班报到时间';
|
||||
COMMENT ON COLUMN train_enrollment.complete_time IS '培训完成/结课时间';
|
||||
|
||||
-- 5. 教学活动排课表 (排课课次必要包含授课教师,且场地类型与教室/实训场地严格互斥)
|
||||
CREATE TABLE train_lesson (
|
||||
lesson_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
plan_course_id BIGINT NOT NULL,
|
||||
t_id VARCHAR(32) NOT NULL,
|
||||
start_time TIMESTAMP NOT NULL,
|
||||
end_time TIMESTAMP NOT NULL,
|
||||
venue_type SMALLINT NOT NULL,
|
||||
class_room_id VARCHAR(32),
|
||||
site_id VARCHAR(32),
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_lesson PRIMARY KEY (lesson_id),
|
||||
CONSTRAINT chk_lesson_venue CHECK (
|
||||
(venue_type = 1 AND class_room_id IS NOT NULL AND site_id IS NULL) OR
|
||||
(venue_type = 2 AND site_id IS NOT NULL AND class_room_id IS NULL)
|
||||
)
|
||||
);
|
||||
COMMENT ON TABLE train_lesson IS '课次教学活动表 (具体一次上课/实训安排)';
|
||||
COMMENT ON COLUMN train_lesson.lesson_id IS '课次ID (核心ID之一)';
|
||||
COMMENT ON COLUMN train_lesson.plan_course_id IS '所属培训课程实例ID';
|
||||
COMMENT ON COLUMN train_lesson.t_id IS '授课教师ID (课次必要组成)';
|
||||
COMMENT ON COLUMN train_lesson.start_time IS '课次开始时间';
|
||||
COMMENT ON COLUMN train_lesson.end_time IS '课次结束时间';
|
||||
COMMENT ON COLUMN train_lesson.venue_type IS '场地类型 (1:普通教室, 2:实训场地)';
|
||||
COMMENT ON COLUMN train_lesson.class_room_id IS '普通教室ID (当场地类型为教室时必填且场地为空)';
|
||||
COMMENT ON COLUMN train_lesson.site_id IS '实训场地ID (当场地类型为实训场地时必填且教室为空)';
|
||||
|
||||
-- 6. 学员住宿安排表 (房间必填 + 床位可选)
|
||||
CREATE TABLE train_accommodation (
|
||||
accommodation_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
enrollment_id BIGINT NOT NULL,
|
||||
dorm_id VARCHAR(32) NOT NULL,
|
||||
bed_no VARCHAR(20),
|
||||
assigned_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
accommodation_status SMALLINT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_accommodation PRIMARY KEY (accommodation_id)
|
||||
);
|
||||
COMMENT ON TABLE train_accommodation IS '学员住宿安排表 (本次参训住宿分配)';
|
||||
COMMENT ON COLUMN train_accommodation.accommodation_id IS '住宿安排ID';
|
||||
COMMENT ON COLUMN train_accommodation.enrollment_id IS '参训档案ID';
|
||||
COMMENT ON COLUMN train_accommodation.dorm_id IS '分配宿舍房间ID';
|
||||
COMMENT ON COLUMN train_accommodation.bed_no IS '分配床位号 (可选)';
|
||||
COMMENT ON COLUMN train_accommodation.assigned_time IS '分配时间';
|
||||
COMMENT ON COLUMN train_accommodation.accommodation_status IS '住宿状态 (0:预分配, 1:已入住, 2:已退宿)';
|
||||
|
||||
-- 7. 培训班考核成绩项配置表 (用户自由配置分值与权重)
|
||||
CREATE TABLE train_score_item (
|
||||
score_item_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
plan_course_id BIGINT,
|
||||
item_name VARCHAR(100) NOT NULL,
|
||||
item_type SMALLINT NOT NULL DEFAULT 1,
|
||||
weight NUMERIC(5,2) NOT NULL DEFAULT 0.00,
|
||||
full_score NUMERIC(5,2) NOT NULL DEFAULT 100.00,
|
||||
sort_no INTEGER DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_score_item PRIMARY KEY (score_item_id)
|
||||
);
|
||||
COMMENT ON TABLE train_score_item IS '考核成绩项配置表 (某培训班的成绩构成规则)';
|
||||
COMMENT ON COLUMN train_score_item.score_item_id IS '成绩项ID';
|
||||
COMMENT ON COLUMN train_score_item.plan_id IS '培训班ID';
|
||||
COMMENT ON COLUMN train_score_item.plan_course_id IS '关联培训课程ID (为空表示全班综合项)';
|
||||
COMMENT ON COLUMN train_score_item.item_name IS '考核项目名称';
|
||||
COMMENT ON COLUMN train_score_item.item_type IS '项目类型 (1:理论考试, 2:实操演练, 3:出勤考核, 4:综合评价)';
|
||||
COMMENT ON COLUMN train_score_item.weight IS '所占综合权重百分比 (0~100)';
|
||||
COMMENT ON COLUMN train_score_item.full_score IS '满分设定 (默认 100)';
|
||||
COMMENT ON COLUMN train_score_item.sort_no IS '展示顺序';
|
||||
|
||||
-- 8. 学员单项得分明细表 (参训学员在某考核项上的实际成绩,无默认0,有行即有分且非负)
|
||||
CREATE TABLE train_student_score (
|
||||
score_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
enrollment_id BIGINT NOT NULL,
|
||||
score_item_id BIGINT NOT NULL,
|
||||
score NUMERIC(7,2) NOT NULL,
|
||||
source_type SMALLINT NOT NULL,
|
||||
source_system VARCHAR(100),
|
||||
source_event_id VARCHAR(100),
|
||||
import_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_student_score PRIMARY KEY (score_id),
|
||||
CONSTRAINT chk_student_score_nonneg CHECK (score >= 0.00)
|
||||
);
|
||||
COMMENT ON TABLE train_student_score IS '学员成绩明细表 (某学员某成绩项得分,未录入成绩不生成记录)';
|
||||
COMMENT ON COLUMN train_student_score.score_id IS '成绩明细ID';
|
||||
COMMENT ON COLUMN train_student_score.enrollment_id IS '参训档案ID';
|
||||
COMMENT ON COLUMN train_student_score.score_item_id IS '关联成绩项ID';
|
||||
COMMENT ON COLUMN train_student_score.score IS '实际得分 (非负,未录成绩不建行,0为真实0分)';
|
||||
COMMENT ON COLUMN train_student_score.source_type IS '录入来源类型 (1:人工录入, 2:理论考试系统, 3:实操设备, 4:外部导入)';
|
||||
COMMENT ON COLUMN train_student_score.source_system IS '来源外部系统名称';
|
||||
COMMENT ON COLUMN train_student_score.source_event_id IS '来源外部事件/单号ID (用于幂等)';
|
||||
COMMENT ON COLUMN train_student_score.import_time IS '成绩写入/同步时间';
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,251 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移脚本: 003_alter_master_tables.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 调整 8 张主数据表结构至 V2 规范模型
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 设计依据: RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0 及 Round 1 审查要求
|
||||
-- 包含表:
|
||||
-- 1. train_teacher (保留1行数据,先加新列,旧列待008迁移并校验后设NOT NULL并删除旧列)
|
||||
-- 2. train_student (0行,删除高校学籍旧列,增加企业主档与加密列,规范source_type语义)
|
||||
-- 3. train_course (0行,删除旧附件字段,增加课程编码与审计列)
|
||||
-- 4. train_classroom_info (0行,对齐公共审计字段)
|
||||
-- 5. train_training_site (0行,对齐公共审计字段)
|
||||
-- 6. train_electronic_board (0行,解绑计划ID,board_name 必填无默认空串)
|
||||
-- 7. train_dormitory_info (0行,重构为容量与性别属性,删除预测默认值,增加 capacity > 0 CHECK)
|
||||
-- 8. train_class_plan (0行,解绑单课程c_id与单教师t_id,升级为多课程架构)
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ==============================================================================
|
||||
-- 步骤 1: 移除主数据表上绑定的旧 update_time 触发器
|
||||
-- ==============================================================================
|
||||
DROP TRIGGER IF EXISTS trig_train_teacher_modify ON train_teacher;
|
||||
DROP TRIGGER IF EXISTS trig_train_student_modify ON train_student;
|
||||
DROP TRIGGER IF EXISTS trig_train_course_modify ON train_course;
|
||||
DROP TRIGGER IF EXISTS trig_train_classroom_modify ON train_classroom_info;
|
||||
DROP TRIGGER IF EXISTS trig_train_trainsite_modify ON train_training_site;
|
||||
DROP TRIGGER IF EXISTS trig_train_board_modify ON train_electronic_board;
|
||||
DROP TRIGGER IF EXISTS trig_train_dorminfo_modify ON train_dormitory_info;
|
||||
DROP TRIGGER IF EXISTS trig_train_classplan_modify ON train_class_plan;
|
||||
|
||||
-- ==============================================================================
|
||||
-- 步骤 2: 显式解除阻塞主数据列删除或变更的旧外键约束 (不使用 CASCADE)
|
||||
-- ==============================================================================
|
||||
-- train_class_plan 上的旧外键 (指向 train_course 和 train_teacher)
|
||||
ALTER TABLE train_class_plan DROP CONSTRAINT IF EXISTS fk_classplan_course;
|
||||
ALTER TABLE train_class_plan DROP CONSTRAINT IF EXISTS fk_classplan_teacher;
|
||||
|
||||
-- train_electronic_board 上的旧外键 (指向 train_class_plan)
|
||||
ALTER TABLE train_electronic_board DROP CONSTRAINT IF EXISTS fk_board_plan;
|
||||
|
||||
-- ==============================================================================
|
||||
-- 步骤 3: 调整 train_teacher (有 1 行真实数据,暂不删除旧列,留待 008 迁移数据)
|
||||
-- ==============================================================================
|
||||
ALTER TABLE train_teacher
|
||||
ADD COLUMN user_id BIGINT,
|
||||
ADD COLUMN org_name VARCHAR(100),
|
||||
ADD COLUMN job_title VARCHAR(50),
|
||||
ADD COLUMN status SMALLINT DEFAULT 1,
|
||||
ADD COLUMN create_by VARCHAR(64),
|
||||
ADD COLUMN update_by VARCHAR(64),
|
||||
ADD COLUMN del_flag CHAR(1) DEFAULT '0';
|
||||
|
||||
ALTER TABLE train_teacher
|
||||
ALTER COLUMN t_name TYPE VARCHAR(50),
|
||||
ALTER COLUMN t_gender TYPE CHAR(1),
|
||||
ALTER COLUMN t_email TYPE VARCHAR(100),
|
||||
ALTER COLUMN remark TYPE VARCHAR(500);
|
||||
|
||||
COMMENT ON COLUMN train_teacher.user_id IS 'sys_user登录账号逻辑关联ID';
|
||||
COMMENT ON COLUMN train_teacher.org_name IS '所属单位/部门名称 (由旧 t_department 迁移)';
|
||||
COMMENT ON COLUMN train_teacher.job_title IS '职务/职称 (由旧 t_title 迁移)';
|
||||
COMMENT ON COLUMN train_teacher.status IS '状态 (1:正常, 0:停用)';
|
||||
COMMENT ON COLUMN train_teacher.del_flag IS '删除标志 (0:存在, 2:删除)';
|
||||
|
||||
-- ==============================================================================
|
||||
-- 步骤 4: 调整 train_student (当前 0 行)
|
||||
-- ==============================================================================
|
||||
-- 4.1 淘汰高校模式旧字段与旧审计字段
|
||||
ALTER TABLE train_student
|
||||
DROP COLUMN IF EXISTS s_department,
|
||||
DROP COLUMN IF EXISTS s_major,
|
||||
DROP COLUMN IF EXISTS s_class,
|
||||
DROP COLUMN IF EXISTS create_user;
|
||||
|
||||
-- 4.2 增加企业培训新主档字段、加密字段与公共审计字段
|
||||
ALTER TABLE train_student
|
||||
ADD COLUMN user_id BIGINT,
|
||||
ADD COLUMN org_name VARCHAR(100),
|
||||
ADD COLUMN s_email VARCHAR(100),
|
||||
ADD COLUMN id_card_ciphertext TEXT,
|
||||
ADD COLUMN id_card_hash VARCHAR(128),
|
||||
ADD COLUMN id_card_last4 VARCHAR(4),
|
||||
ADD COLUMN face_ref VARCHAR(255),
|
||||
ADD COLUMN source_type SMALLINT DEFAULT 1 NOT NULL,
|
||||
ADD COLUMN status SMALLINT DEFAULT 1 NOT NULL,
|
||||
ADD COLUMN create_by VARCHAR(64),
|
||||
ADD COLUMN update_by VARCHAR(64),
|
||||
ADD COLUMN del_flag CHAR(1) DEFAULT '0' NOT NULL;
|
||||
|
||||
ALTER TABLE train_student
|
||||
ALTER COLUMN s_name TYPE VARCHAR(50),
|
||||
ALTER COLUMN s_gender TYPE CHAR(1),
|
||||
ALTER COLUMN remark TYPE VARCHAR(500);
|
||||
|
||||
COMMENT ON COLUMN train_student.user_id IS 'sys_user账号逻辑关联';
|
||||
COMMENT ON COLUMN train_student.org_name IS '所属单位/部门';
|
||||
COMMENT ON COLUMN train_student.id_card_ciphertext IS '身份证可逆加密密文';
|
||||
COMMENT ON COLUMN train_student.id_card_hash IS '身份证精确匹配Hash (防重)';
|
||||
COMMENT ON COLUMN train_student.id_card_last4 IS '身份证后四位展示';
|
||||
COMMENT ON COLUMN train_student.face_ref IS '人脸特征或图像凭证引用';
|
||||
COMMENT ON COLUMN train_student.source_type IS '人员主档进入系统的来源 (1:管理员录入, 2:Excel批量导入, 3:铁路内部业务平台同步)';
|
||||
COMMENT ON COLUMN train_student.status IS '主档状态 (1:正常, 0:停用)';
|
||||
COMMENT ON COLUMN train_student.del_flag IS '删除标志 (0:存在, 2:删除)';
|
||||
|
||||
-- ==============================================================================
|
||||
-- 步骤 5: 调整 train_course (当前 0 行)
|
||||
-- ==============================================================================
|
||||
-- 5.1 淘汰旧集中附件字段 (已解耦迁入 train_resource 与 train_course_resource)
|
||||
ALTER TABLE train_course
|
||||
DROP COLUMN IF EXISTS attach_doc,
|
||||
DROP COLUMN IF EXISTS attach_video,
|
||||
DROP COLUMN IF EXISTS attach_model_link,
|
||||
DROP COLUMN IF EXISTS model_resource_ids,
|
||||
DROP COLUMN IF EXISTS create_user;
|
||||
|
||||
-- 5.2 增加课程编码与标准审计字段
|
||||
ALTER TABLE train_course
|
||||
ADD COLUMN c_code VARCHAR(50),
|
||||
ADD COLUMN status SMALLINT DEFAULT 1 NOT NULL,
|
||||
ADD COLUMN create_by VARCHAR(64),
|
||||
ADD COLUMN update_by VARCHAR(64),
|
||||
ADD COLUMN del_flag CHAR(1) DEFAULT '0' NOT NULL;
|
||||
|
||||
ALTER TABLE train_course
|
||||
ALTER COLUMN remark TYPE VARCHAR(500);
|
||||
|
||||
COMMENT ON COLUMN train_course.c_code IS '课程统一业务编码';
|
||||
COMMENT ON COLUMN train_course.status IS '课程状态 (1:正常, 0:停用)';
|
||||
COMMENT ON COLUMN train_course.del_flag IS '删除标志 (0:存在, 2:删除)';
|
||||
|
||||
-- ==============================================================================
|
||||
-- 步骤 6: 调整 train_classroom_info (当前 0 行)
|
||||
-- ==============================================================================
|
||||
ALTER TABLE train_classroom_info
|
||||
DROP COLUMN IF EXISTS create_user;
|
||||
|
||||
ALTER TABLE train_classroom_info
|
||||
ADD COLUMN create_by VARCHAR(64),
|
||||
ADD COLUMN update_by VARCHAR(64),
|
||||
ADD COLUMN del_flag CHAR(1) DEFAULT '0' NOT NULL;
|
||||
|
||||
ALTER TABLE train_classroom_info
|
||||
ALTER COLUMN remark TYPE VARCHAR(500),
|
||||
ALTER COLUMN room_status SET NOT NULL;
|
||||
|
||||
COMMENT ON COLUMN train_classroom_info.del_flag IS '删除标志 (0:存在, 2:删除)';
|
||||
|
||||
-- ==============================================================================
|
||||
-- 步骤 7: 调整 train_training_site (当前 0 行)
|
||||
-- ==============================================================================
|
||||
ALTER TABLE train_training_site
|
||||
DROP COLUMN IF EXISTS create_user;
|
||||
|
||||
ALTER TABLE train_training_site
|
||||
ADD COLUMN create_by VARCHAR(64),
|
||||
ADD COLUMN update_by VARCHAR(64),
|
||||
ADD COLUMN del_flag CHAR(1) DEFAULT '0' NOT NULL;
|
||||
|
||||
ALTER TABLE train_training_site
|
||||
ALTER COLUMN remark TYPE VARCHAR(500),
|
||||
ALTER COLUMN site_status SET NOT NULL;
|
||||
|
||||
COMMENT ON COLUMN train_training_site.del_flag IS '删除标志 (0:存在, 2:删除)';
|
||||
|
||||
-- ==============================================================================
|
||||
-- 步骤 8: 调整 train_electronic_board (当前 0 行)
|
||||
-- ==============================================================================
|
||||
-- 8.1 移除计划绑定列与旧播放字段
|
||||
ALTER TABLE train_electronic_board
|
||||
DROP COLUMN IF EXISTS bind_plan_id,
|
||||
DROP COLUMN IF EXISTS board_content,
|
||||
DROP COLUMN IF EXISTS board_playmode,
|
||||
DROP COLUMN IF EXISTS create_user;
|
||||
|
||||
-- 8.2 增加设备名称 (必填,严禁空串默认)、API端点与审计字段
|
||||
ALTER TABLE train_electronic_board
|
||||
ADD COLUMN board_name VARCHAR(100) NOT NULL,
|
||||
ADD COLUMN api_endpoint VARCHAR(255),
|
||||
ADD COLUMN create_by VARCHAR(64),
|
||||
ADD COLUMN update_by VARCHAR(64),
|
||||
ADD COLUMN del_flag CHAR(1) DEFAULT '0' NOT NULL;
|
||||
|
||||
ALTER TABLE train_electronic_board
|
||||
ALTER COLUMN support_checkin_type TYPE VARCHAR(50),
|
||||
ALTER COLUMN remark TYPE VARCHAR(500),
|
||||
ALTER COLUMN board_status SET NOT NULL;
|
||||
|
||||
COMMENT ON COLUMN train_electronic_board.board_name IS '电子班牌名称 (必填)';
|
||||
COMMENT ON COLUMN train_electronic_board.api_endpoint IS '设备交互/推送接口地址';
|
||||
COMMENT ON COLUMN train_electronic_board.bind_classroom_id IS '绑定的物理教室ID';
|
||||
COMMENT ON COLUMN train_electronic_board.del_flag IS '删除标志 (0:存在, 2:删除)';
|
||||
|
||||
-- ==============================================================================
|
||||
-- 步骤 9: 调整 train_dormitory_info (当前 0 行)
|
||||
-- ==============================================================================
|
||||
-- 9.1 移除静态床位统计列
|
||||
ALTER TABLE train_dormitory_info
|
||||
DROP COLUMN IF EXISTS bed_total,
|
||||
DROP COLUMN IF EXISTS bed_used,
|
||||
DROP COLUMN IF EXISTS create_user;
|
||||
|
||||
-- 9.2 增加楼层、房间额定容量、适用性别 (杜绝业务默认值猜测,增加容量正数 CHECK)
|
||||
ALTER TABLE train_dormitory_info
|
||||
ADD COLUMN dorm_floor VARCHAR(20) NOT NULL,
|
||||
ADD COLUMN capacity INTEGER NOT NULL,
|
||||
ADD COLUMN gender_type SMALLINT DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN create_by VARCHAR(64),
|
||||
ADD COLUMN update_by VARCHAR(64),
|
||||
ADD COLUMN del_flag CHAR(1) DEFAULT '0' NOT NULL;
|
||||
|
||||
ALTER TABLE train_dormitory_info
|
||||
ALTER COLUMN dorm_building SET NOT NULL,
|
||||
ALTER COLUMN dorm_room SET NOT NULL,
|
||||
ALTER COLUMN remark TYPE VARCHAR(500),
|
||||
ALTER COLUMN dorm_status SET NOT NULL;
|
||||
|
||||
ALTER TABLE train_dormitory_info DROP CONSTRAINT IF EXISTS chk_dorm_capacity;
|
||||
ALTER TABLE train_dormitory_info ADD CONSTRAINT chk_dorm_capacity CHECK (capacity > 0);
|
||||
|
||||
COMMENT ON COLUMN train_dormitory_info.dorm_floor IS '所在楼层 (管理员真实录入)';
|
||||
COMMENT ON COLUMN train_dormitory_info.capacity IS '房间核定床位容量 (正整数,无默认值)';
|
||||
COMMENT ON COLUMN train_dormitory_info.gender_type IS '适用性别 (0:不限, 1:男, 2:女)';
|
||||
COMMENT ON COLUMN train_dormitory_info.del_flag IS '删除标志 (0:存在, 2:删除)';
|
||||
|
||||
-- ==============================================================================
|
||||
-- 步骤 10: 调整 train_class_plan (当前 0 行)
|
||||
-- ==============================================================================
|
||||
-- 10.1 彻底剥离旧单课 c_id 和旧单教师 t_id
|
||||
ALTER TABLE train_class_plan
|
||||
DROP COLUMN IF EXISTS c_id,
|
||||
DROP COLUMN IF EXISTS t_id,
|
||||
DROP COLUMN IF EXISTS create_user;
|
||||
|
||||
-- 10.2 增加报到截止时间与标准审计列
|
||||
ALTER TABLE train_class_plan
|
||||
ADD COLUMN report_deadline TIMESTAMP,
|
||||
ADD COLUMN create_by VARCHAR(64),
|
||||
ADD COLUMN update_by VARCHAR(64),
|
||||
ADD COLUMN del_flag CHAR(1) DEFAULT '0' NOT NULL;
|
||||
|
||||
ALTER TABLE train_class_plan
|
||||
ALTER COLUMN plan_name TYPE VARCHAR(150),
|
||||
ALTER COLUMN train_address TYPE VARCHAR(200),
|
||||
ALTER COLUMN remark TYPE VARCHAR(500),
|
||||
ALTER COLUMN plan_status SET NOT NULL;
|
||||
|
||||
COMMENT ON COLUMN train_class_plan.report_deadline IS '学员现场报到截止时间';
|
||||
COMMENT ON COLUMN train_class_plan.del_flag IS '删除标志 (0:存在, 2:删除)';
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,215 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移脚本: 004_rebuild_business_tables.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 重构 5 张需彻底解耦旧关系的空业务过程表
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 设计依据: RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0 及 Round 1 审查要求
|
||||
-- 规范要求:
|
||||
-- 1. 校验确认各表必须为 0 行方可重建
|
||||
-- 2. 显式解除旧依赖,杜绝无边界使用 CASCADE
|
||||
-- 3. 为 train_study_duration 增加时间有效性与单号/系统匹配 CHECK
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 步骤 1: 安全阻断校验 (确保各表确为 0 行,杜绝误删未备份数据)
|
||||
DO $$
|
||||
DECLARE
|
||||
v_cnt INT;
|
||||
BEGIN
|
||||
SELECT count(*) INTO v_cnt FROM train_dorm_auth;
|
||||
IF v_cnt > 0 THEN RAISE EXCEPTION '>>> [004-FATAL] train_dorm_auth 包含 % 行数据,禁止重建', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_class_report;
|
||||
IF v_cnt > 0 THEN RAISE EXCEPTION '>>> [004-FATAL] train_class_report 包含 % 行数据,禁止重建', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_student_checkin;
|
||||
IF v_cnt > 0 THEN RAISE EXCEPTION '>>> [004-FATAL] train_student_checkin 包含 % 行数据,禁止重建', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_study_duration;
|
||||
IF v_cnt > 0 THEN RAISE EXCEPTION '>>> [004-FATAL] train_study_duration 包含 % 行数据,禁止重建', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM train_student_portrait;
|
||||
IF v_cnt > 0 THEN RAISE EXCEPTION '>>> [004-FATAL] train_student_portrait 包含 % 行数据,禁止重建', v_cnt; END IF;
|
||||
END $$;
|
||||
|
||||
-- 步骤 2: 卸载旧触发器
|
||||
DROP TRIGGER IF EXISTS trig_train_dormauth_modify ON train_dorm_auth;
|
||||
|
||||
-- 步骤 3: 显式卸载已知旧外键约束 (杜绝使用 CASCADE 掩盖未预期依赖)
|
||||
ALTER TABLE train_dorm_auth DROP CONSTRAINT IF EXISTS fk_dormauth_stu;
|
||||
ALTER TABLE train_dorm_auth DROP CONSTRAINT IF EXISTS fk_dormauth_plan;
|
||||
ALTER TABLE train_dorm_auth DROP CONSTRAINT IF EXISTS fk_dormauth_dorm;
|
||||
|
||||
ALTER TABLE train_class_report DROP CONSTRAINT IF EXISTS fk_classreport_plan;
|
||||
ALTER TABLE train_class_report DROP CONSTRAINT IF EXISTS fk_classreport_stu;
|
||||
ALTER TABLE train_class_report DROP CONSTRAINT IF EXISTS fk_classreport_dorm;
|
||||
ALTER TABLE train_class_report DROP CONSTRAINT IF EXISTS fk_classreport_classroom;
|
||||
ALTER TABLE train_class_report DROP CONSTRAINT IF EXISTS fk_classreport_site;
|
||||
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS fk_checkin_plan;
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS fk_checkin_course;
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS fk_checkin_stu;
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS fk_checkin_board;
|
||||
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS fk_studydur_stu;
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS fk_studydur_plan;
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS fk_studydur_course;
|
||||
|
||||
ALTER TABLE train_student_portrait DROP CONSTRAINT IF EXISTS fk_portrait_stu;
|
||||
ALTER TABLE train_student_portrait DROP CONSTRAINT IF EXISTS fk_portrait_plan;
|
||||
|
||||
-- 步骤 4: 执行安全常规 DROP TABLE (无 CASCADE)
|
||||
DROP TABLE IF EXISTS train_dorm_auth;
|
||||
DROP TABLE IF EXISTS train_class_report;
|
||||
DROP TABLE IF EXISTS train_student_checkin;
|
||||
DROP TABLE IF EXISTS train_study_duration;
|
||||
DROP TABLE IF EXISTS train_student_portrait;
|
||||
|
||||
-- 步骤 5: 按照 V2 规范重建表结构
|
||||
|
||||
-- 5.1 门禁授权同步表
|
||||
CREATE TABLE train_dorm_auth (
|
||||
auth_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
accommodation_id BIGINT NOT NULL,
|
||||
auth_start TIMESTAMP,
|
||||
auth_end TIMESTAMP,
|
||||
face_sync_status SMALLINT NOT NULL DEFAULT 0,
|
||||
retry_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_error VARCHAR(500),
|
||||
external_auth_id VARCHAR(100),
|
||||
last_sync_time TIMESTAMP,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_dorm_auth PRIMARY KEY (auth_id)
|
||||
);
|
||||
COMMENT ON TABLE train_dorm_auth IS '宿舍门禁权限下发与同步表';
|
||||
COMMENT ON COLUMN train_dorm_auth.auth_id IS '门禁授权记录ID';
|
||||
COMMENT ON COLUMN train_dorm_auth.accommodation_id IS '住宿安排ID (一对一关联)';
|
||||
COMMENT ON COLUMN train_dorm_auth.auth_start IS '权限生效开始时间';
|
||||
COMMENT ON COLUMN train_dorm_auth.auth_end IS '权限失效结束时间';
|
||||
COMMENT ON COLUMN train_dorm_auth.face_sync_status IS '同步状态 (0:待下发, 1:已下发, 2:下发失败, 3:已注销)';
|
||||
COMMENT ON COLUMN train_dorm_auth.retry_count IS '同步失败重试次数';
|
||||
COMMENT ON COLUMN train_dorm_auth.last_error IS '最后一次下发失败错误信息';
|
||||
COMMENT ON COLUMN train_dorm_auth.external_auth_id IS '外部门禁硬件系统下发唯一授权凭证ID';
|
||||
COMMENT ON COLUMN train_dorm_auth.last_sync_time IS '最近一次同步请求时间';
|
||||
|
||||
-- 5.2 报到事件记录表
|
||||
CREATE TABLE train_class_report (
|
||||
report_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
enrollment_id BIGINT NOT NULL,
|
||||
report_way SMALLINT NOT NULL,
|
||||
report_device_sn VARCHAR(64),
|
||||
report_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
verify_result SMALLINT NOT NULL,
|
||||
verify_note VARCHAR(500),
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_class_report PRIMARY KEY (report_id)
|
||||
);
|
||||
COMMENT ON TABLE train_class_report IS '培训班学员现场报到事件表';
|
||||
COMMENT ON COLUMN train_class_report.report_id IS '报到流水ID';
|
||||
COMMENT ON COLUMN train_class_report.enrollment_id IS '参训档案ID (一对一关联)';
|
||||
COMMENT ON COLUMN train_class_report.report_way IS '报到核验方式 (1:人脸识别, 2:身份证刷卡, 3:二维码, 4:人工录入)';
|
||||
COMMENT ON COLUMN train_class_report.report_device_sn IS '核验设备硬件序列号';
|
||||
COMMENT ON COLUMN train_class_report.report_time IS '报到完成时间';
|
||||
COMMENT ON COLUMN train_class_report.verify_result IS '核验结果 (1:核验通过, 2:核验不匹配, 3:人工放行)';
|
||||
COMMENT ON COLUMN train_class_report.verify_note IS '核验说明/异常原因';
|
||||
|
||||
-- 5.3 考勤签到明细表
|
||||
CREATE TABLE train_student_checkin (
|
||||
checkin_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
enrollment_id BIGINT NOT NULL,
|
||||
lesson_id BIGINT NOT NULL,
|
||||
board_id VARCHAR(32),
|
||||
checkin_way SMALLINT NOT NULL,
|
||||
sign_in_time TIMESTAMP,
|
||||
sign_out_time TIMESTAMP,
|
||||
attendance_status SMALLINT NOT NULL DEFAULT 0,
|
||||
manual_flag SMALLINT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_student_checkin PRIMARY KEY (checkin_id)
|
||||
);
|
||||
COMMENT ON TABLE train_student_checkin IS '课次考勤打卡与最终考勤结果表';
|
||||
COMMENT ON COLUMN train_student_checkin.checkin_id IS '考勤记录ID';
|
||||
COMMENT ON COLUMN train_student_checkin.enrollment_id IS '参训档案ID';
|
||||
COMMENT ON COLUMN train_student_checkin.lesson_id IS '具体教学活动课次ID';
|
||||
COMMENT ON COLUMN train_student_checkin.board_id IS '打卡电子班牌ID';
|
||||
COMMENT ON COLUMN train_student_checkin.checkin_way IS '打卡方式 (1:人脸, 2:刷卡, 3:扫码, 4:教师代签)';
|
||||
COMMENT ON COLUMN train_student_checkin.sign_in_time IS '签到时间';
|
||||
COMMENT ON COLUMN train_student_checkin.sign_out_time IS '签退时间';
|
||||
COMMENT ON COLUMN train_student_checkin.attendance_status IS '考勤结论 (0:缺勤, 1:正常出勤, 2:迟到, 3:早退, 4:请假)';
|
||||
COMMENT ON COLUMN train_student_checkin.manual_flag IS '是否人工补录/修正 (0:自动, 1:人工补卡)';
|
||||
|
||||
-- 5.4 学习时长记录表 (自主/外部系统对接)
|
||||
CREATE TABLE train_study_duration (
|
||||
dur_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
enrollment_id BIGINT NOT NULL,
|
||||
plan_course_id BIGINT,
|
||||
resource_id VARCHAR(32),
|
||||
learning_content VARCHAR(200),
|
||||
source_system VARCHAR(100),
|
||||
source_event_id VARCHAR(100),
|
||||
start_time TIMESTAMP,
|
||||
end_time TIMESTAMP,
|
||||
study_seconds INTEGER NOT NULL DEFAULT 0,
|
||||
sync_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_study_duration PRIMARY KEY (dur_id),
|
||||
CONSTRAINT chk_study_seconds CHECK (study_seconds >= 0),
|
||||
CONSTRAINT chk_study_time CHECK (end_time IS NULL OR start_time IS NULL OR end_time >= start_time),
|
||||
CONSTRAINT chk_study_source_system CHECK (source_event_id IS NULL OR source_system IS NOT NULL)
|
||||
);
|
||||
COMMENT ON TABLE train_study_duration IS '学员自主学习与仿真实训时长上报表';
|
||||
COMMENT ON COLUMN train_study_duration.dur_id IS '学习时长记录ID';
|
||||
COMMENT ON COLUMN train_study_duration.enrollment_id IS '参训档案ID';
|
||||
COMMENT ON COLUMN train_study_duration.plan_course_id IS '关联培训课程ID (可选)';
|
||||
COMMENT ON COLUMN train_study_duration.resource_id IS '学习资源ID (可选)';
|
||||
COMMENT ON COLUMN train_study_duration.learning_content IS '学习主题/章节内容';
|
||||
COMMENT ON COLUMN train_study_duration.source_system IS '上报来源系统 (如 VR仿真系统, 在线学习端)';
|
||||
COMMENT ON COLUMN train_study_duration.source_event_id IS '来源系统上报事件ID (用于幂等防重)';
|
||||
COMMENT ON COLUMN train_study_duration.start_time IS '学习开始时间';
|
||||
COMMENT ON COLUMN train_study_duration.end_time IS '学习结束时间';
|
||||
COMMENT ON COLUMN train_study_duration.study_seconds IS '有效学习时长 (秒)';
|
||||
COMMENT ON COLUMN train_study_duration.sync_time IS '系统入库时间';
|
||||
|
||||
-- 5.5 学员参训综合画像表
|
||||
CREATE TABLE train_student_portrait (
|
||||
portrait_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
enrollment_id BIGINT NOT NULL,
|
||||
attendance_rate NUMERIC(5,2),
|
||||
theory_score NUMERIC(7,2),
|
||||
practical_score NUMERIC(7,2),
|
||||
total_study_hour NUMERIC(8,2),
|
||||
label_tag TEXT,
|
||||
portrait_content TEXT,
|
||||
generated_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
remark VARCHAR(500),
|
||||
CONSTRAINT pk_train_student_portrait PRIMARY KEY (portrait_id)
|
||||
);
|
||||
COMMENT ON TABLE train_student_portrait IS '学员参训全过程综合评估与画像表';
|
||||
COMMENT ON COLUMN train_student_portrait.portrait_id IS '画像记录ID';
|
||||
COMMENT ON COLUMN train_student_portrait.enrollment_id IS '参训档案ID (单期唯一画像)';
|
||||
COMMENT ON COLUMN train_student_portrait.attendance_rate IS '出勤率百分比 (0~100)';
|
||||
COMMENT ON COLUMN train_student_portrait.theory_score IS '理论考试综合分';
|
||||
COMMENT ON COLUMN train_student_portrait.practical_score IS '实操演练综合分';
|
||||
COMMENT ON COLUMN train_student_portrait.total_study_hour IS '累计在线与自主学习总学时';
|
||||
COMMENT ON COLUMN train_student_portrait.label_tag IS '特征标签集合 (JSON或逗号分隔文本)';
|
||||
COMMENT ON COLUMN train_student_portrait.portrait_content IS '综合评价文本/导师评语';
|
||||
COMMENT ON COLUMN train_student_portrait.generated_time IS '画像生成时间';
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,245 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移脚本: 005_constraints.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 集中创建外键约束 (FK)、唯一约束 (UNIQUE) 与单行一致性检查约束 (CHECK)
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 设计依据: RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0 及 Round 1 审查要求
|
||||
-- 核心原则:
|
||||
-- 1. 所有外键严格使用 ON DELETE NO ACTION,严禁使用 CASCADE
|
||||
-- 2. 唯一约束保护核心领域关系防重
|
||||
-- 3. 检查约束全面覆盖单行时间、场地互斥、分值非负、容量合法及系统单号联动逻辑
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ==============================================================================
|
||||
-- 一、唯一约束 (UNIQUE CONSTRAINTS - 共 13 组)
|
||||
-- ==============================================================================
|
||||
|
||||
-- 1. 学员身份证哈希唯一 (允许 NULL,防重)
|
||||
ALTER TABLE train_student DROP CONSTRAINT IF EXISTS uq_student_idcard_hash;
|
||||
ALTER TABLE train_student ADD CONSTRAINT uq_student_idcard_hash UNIQUE (id_card_hash);
|
||||
|
||||
-- 2. 课程编码唯一 (允许 NULL)
|
||||
ALTER TABLE train_course DROP CONSTRAINT IF EXISTS uq_course_code;
|
||||
ALTER TABLE train_course ADD CONSTRAINT uq_course_code UNIQUE (c_code);
|
||||
|
||||
-- 3. 电子班牌硬件序列号全局唯一
|
||||
ALTER TABLE train_electronic_board DROP CONSTRAINT IF EXISTS uq_board_sn;
|
||||
ALTER TABLE train_electronic_board ADD CONSTRAINT uq_board_sn UNIQUE (board_sn);
|
||||
|
||||
-- 4. 宿舍房间唯一 (楼栋 + 房号)
|
||||
ALTER TABLE train_dormitory_info DROP CONSTRAINT IF EXISTS uq_dorm_building_room;
|
||||
ALTER TABLE train_dormitory_info ADD CONSTRAINT uq_dorm_building_room UNIQUE (dorm_building, dorm_room);
|
||||
|
||||
-- 5. 课程与教学资源绑定唯一
|
||||
ALTER TABLE train_course_resource DROP CONSTRAINT IF EXISTS uq_course_resource;
|
||||
ALTER TABLE train_course_resource ADD CONSTRAINT uq_course_resource UNIQUE (c_id, resource_id);
|
||||
|
||||
-- 6. 培训班与课程关联唯一 (一个班不能重复加入同一门课)
|
||||
ALTER TABLE train_plan_course DROP CONSTRAINT IF EXISTS uq_plan_course;
|
||||
ALTER TABLE train_plan_course ADD CONSTRAINT uq_plan_course UNIQUE (plan_id, c_id);
|
||||
|
||||
-- 7. 参训学员档案唯一 (同一学员不能重复加入同一培训班)
|
||||
ALTER TABLE train_enrollment DROP CONSTRAINT IF EXISTS uq_enrollment;
|
||||
ALTER TABLE train_enrollment ADD CONSTRAINT uq_enrollment UNIQUE (plan_id, student_id);
|
||||
|
||||
-- 8. 住宿安排唯一 (一次参训保留一个有效住宿记录)
|
||||
ALTER TABLE train_accommodation DROP CONSTRAINT IF EXISTS uq_accommodation_enroll;
|
||||
ALTER TABLE train_accommodation ADD CONSTRAINT uq_accommodation_enroll UNIQUE (enrollment_id);
|
||||
|
||||
-- 9. 门禁授权唯一 (一次住宿安排对应一条门禁同步主记录)
|
||||
ALTER TABLE train_dorm_auth DROP CONSTRAINT IF EXISTS uq_dorm_auth_accomm;
|
||||
ALTER TABLE train_dorm_auth ADD CONSTRAINT uq_dorm_auth_accomm UNIQUE (accommodation_id);
|
||||
|
||||
-- 10. 报到记录唯一 (一次参训对应一次报到结果)
|
||||
ALTER TABLE train_class_report DROP CONSTRAINT IF EXISTS uq_class_report_enroll;
|
||||
ALTER TABLE train_class_report ADD CONSTRAINT uq_class_report_enroll UNIQUE (enrollment_id);
|
||||
|
||||
-- 11. 课次考勤打卡唯一 (一个学员在一堂课只有一条考勤主结果)
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS uq_student_checkin_enroll_lesson;
|
||||
ALTER TABLE train_student_checkin ADD CONSTRAINT uq_student_checkin_enroll_lesson UNIQUE (enrollment_id, lesson_id);
|
||||
|
||||
-- 12. 学员考核项成绩唯一 (一个学员在某考核项只有一条最终得分)
|
||||
ALTER TABLE train_student_score DROP CONSTRAINT IF EXISTS uq_student_score_enroll_item;
|
||||
ALTER TABLE train_student_score ADD CONSTRAINT uq_student_score_enroll_item UNIQUE (enrollment_id, score_item_id);
|
||||
|
||||
-- 13. 学员参训综合画像唯一 (一次参训生成一份画像)
|
||||
ALTER TABLE train_student_portrait DROP CONSTRAINT IF EXISTS uq_student_portrait_enroll;
|
||||
ALTER TABLE train_student_portrait ADD CONSTRAINT uq_student_portrait_enroll UNIQUE (enrollment_id);
|
||||
|
||||
|
||||
-- ==============================================================================
|
||||
-- 二、外键约束 (FOREIGN KEY CONSTRAINTS - 共 26 组,全部 ON DELETE NO ACTION)
|
||||
-- ==============================================================================
|
||||
|
||||
-- 1. train_course_resource (2 FK)
|
||||
ALTER TABLE train_course_resource DROP CONSTRAINT IF EXISTS fk_cr_course;
|
||||
ALTER TABLE train_course_resource ADD CONSTRAINT fk_cr_course
|
||||
FOREIGN KEY (c_id) REFERENCES train_course (c_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_course_resource DROP CONSTRAINT IF EXISTS fk_cr_resource;
|
||||
ALTER TABLE train_course_resource ADD CONSTRAINT fk_cr_resource
|
||||
FOREIGN KEY (resource_id) REFERENCES train_resource (resource_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 2. train_electronic_board (1 FK)
|
||||
ALTER TABLE train_electronic_board DROP CONSTRAINT IF EXISTS fk_board_classroom;
|
||||
ALTER TABLE train_electronic_board ADD CONSTRAINT fk_board_classroom
|
||||
FOREIGN KEY (bind_classroom_id) REFERENCES train_classroom_info (class_room_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 3. train_plan_course (2 FK)
|
||||
ALTER TABLE train_plan_course DROP CONSTRAINT IF EXISTS fk_plancourse_plan;
|
||||
ALTER TABLE train_plan_course ADD CONSTRAINT fk_plancourse_plan
|
||||
FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_plan_course DROP CONSTRAINT IF EXISTS fk_plancourse_course;
|
||||
ALTER TABLE train_plan_course ADD CONSTRAINT fk_plancourse_course
|
||||
FOREIGN KEY (c_id) REFERENCES train_course (c_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 4. train_enrollment (2 FK)
|
||||
ALTER TABLE train_enrollment DROP CONSTRAINT IF EXISTS fk_enrollment_plan;
|
||||
ALTER TABLE train_enrollment ADD CONSTRAINT fk_enrollment_plan
|
||||
FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_enrollment DROP CONSTRAINT IF EXISTS fk_enrollment_student;
|
||||
ALTER TABLE train_enrollment ADD CONSTRAINT fk_enrollment_student
|
||||
FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 5. train_lesson (4 FK)
|
||||
ALTER TABLE train_lesson DROP CONSTRAINT IF EXISTS fk_lesson_plancourse;
|
||||
ALTER TABLE train_lesson ADD CONSTRAINT fk_lesson_plancourse
|
||||
FOREIGN KEY (plan_course_id) REFERENCES train_plan_course (plan_course_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_lesson DROP CONSTRAINT IF EXISTS fk_lesson_teacher;
|
||||
ALTER TABLE train_lesson ADD CONSTRAINT fk_lesson_teacher
|
||||
FOREIGN KEY (t_id) REFERENCES train_teacher (t_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_lesson DROP CONSTRAINT IF EXISTS fk_lesson_classroom;
|
||||
ALTER TABLE train_lesson ADD CONSTRAINT fk_lesson_classroom
|
||||
FOREIGN KEY (class_room_id) REFERENCES train_classroom_info (class_room_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_lesson DROP CONSTRAINT IF EXISTS fk_lesson_site;
|
||||
ALTER TABLE train_lesson ADD CONSTRAINT fk_lesson_site
|
||||
FOREIGN KEY (site_id) REFERENCES train_training_site (site_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 6. train_accommodation (2 FK)
|
||||
ALTER TABLE train_accommodation DROP CONSTRAINT IF EXISTS fk_accomm_enrollment;
|
||||
ALTER TABLE train_accommodation ADD CONSTRAINT fk_accomm_enrollment
|
||||
FOREIGN KEY (enrollment_id) REFERENCES train_enrollment (enrollment_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_accommodation DROP CONSTRAINT IF EXISTS fk_accomm_dorm;
|
||||
ALTER TABLE train_accommodation ADD CONSTRAINT fk_accomm_dorm
|
||||
FOREIGN KEY (dorm_id) REFERENCES train_dormitory_info (dorm_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 7. train_dorm_auth (1 FK)
|
||||
ALTER TABLE train_dorm_auth DROP CONSTRAINT IF EXISTS fk_dormauth_accomm;
|
||||
ALTER TABLE train_dorm_auth ADD CONSTRAINT fk_dormauth_accomm
|
||||
FOREIGN KEY (accommodation_id) REFERENCES train_accommodation (accommodation_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 8. train_class_report (1 FK)
|
||||
ALTER TABLE train_class_report DROP CONSTRAINT IF EXISTS fk_report_enrollment;
|
||||
ALTER TABLE train_class_report ADD CONSTRAINT fk_report_enrollment
|
||||
FOREIGN KEY (enrollment_id) REFERENCES train_enrollment (enrollment_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 9. train_student_checkin (3 FK)
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS fk_checkin_enrollment;
|
||||
ALTER TABLE train_student_checkin ADD CONSTRAINT fk_checkin_enrollment
|
||||
FOREIGN KEY (enrollment_id) REFERENCES train_enrollment (enrollment_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS fk_checkin_lesson;
|
||||
ALTER TABLE train_student_checkin ADD CONSTRAINT fk_checkin_lesson
|
||||
FOREIGN KEY (lesson_id) REFERENCES train_lesson (lesson_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS fk_checkin_board;
|
||||
ALTER TABLE train_student_checkin ADD CONSTRAINT fk_checkin_board
|
||||
FOREIGN KEY (board_id) REFERENCES train_electronic_board (board_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 10. train_score_item (2 FK)
|
||||
ALTER TABLE train_score_item DROP CONSTRAINT IF EXISTS fk_scoreitem_plan;
|
||||
ALTER TABLE train_score_item ADD CONSTRAINT fk_scoreitem_plan
|
||||
FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_score_item DROP CONSTRAINT IF EXISTS fk_scoreitem_plancourse;
|
||||
ALTER TABLE train_score_item ADD CONSTRAINT fk_scoreitem_plancourse
|
||||
FOREIGN KEY (plan_course_id) REFERENCES train_plan_course (plan_course_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 11. train_student_score (2 FK)
|
||||
ALTER TABLE train_student_score DROP CONSTRAINT IF EXISTS fk_score_enrollment;
|
||||
ALTER TABLE train_student_score ADD CONSTRAINT fk_score_enrollment
|
||||
FOREIGN KEY (enrollment_id) REFERENCES train_enrollment (enrollment_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_student_score DROP CONSTRAINT IF EXISTS fk_score_item;
|
||||
ALTER TABLE train_student_score ADD CONSTRAINT fk_score_item
|
||||
FOREIGN KEY (score_item_id) REFERENCES train_score_item (score_item_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 12. train_study_duration (3 FK)
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS fk_study_enrollment;
|
||||
ALTER TABLE train_study_duration ADD CONSTRAINT fk_study_enrollment
|
||||
FOREIGN KEY (enrollment_id) REFERENCES train_enrollment (enrollment_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS fk_study_plancourse;
|
||||
ALTER TABLE train_study_duration ADD CONSTRAINT fk_study_plancourse
|
||||
FOREIGN KEY (plan_course_id) REFERENCES train_plan_course (plan_course_id) ON DELETE NO ACTION;
|
||||
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS fk_study_resource;
|
||||
ALTER TABLE train_study_duration ADD CONSTRAINT fk_study_resource
|
||||
FOREIGN KEY (resource_id) REFERENCES train_resource (resource_id) ON DELETE NO ACTION;
|
||||
|
||||
-- 13. train_student_portrait (1 FK)
|
||||
ALTER TABLE train_student_portrait DROP CONSTRAINT IF EXISTS fk_portrait_enrollment;
|
||||
ALTER TABLE train_student_portrait ADD CONSTRAINT fk_portrait_enrollment
|
||||
FOREIGN KEY (enrollment_id) REFERENCES train_enrollment (enrollment_id) ON DELETE NO ACTION;
|
||||
|
||||
|
||||
-- ==============================================================================
|
||||
-- 三、单行检查约束 (CHECK CONSTRAINTS)
|
||||
-- ==============================================================================
|
||||
|
||||
-- 1. 排课时间逻辑检查: 开始时间必须早于结束时间
|
||||
ALTER TABLE train_lesson DROP CONSTRAINT IF EXISTS chk_lesson_time;
|
||||
ALTER TABLE train_lesson ADD CONSTRAINT chk_lesson_time
|
||||
CHECK (start_time < end_time);
|
||||
|
||||
-- 2. 排课场地类型单行互斥约束
|
||||
ALTER TABLE train_lesson DROP CONSTRAINT IF EXISTS chk_lesson_venue;
|
||||
ALTER TABLE train_lesson ADD CONSTRAINT chk_lesson_venue CHECK (
|
||||
(venue_type = 1 AND class_room_id IS NOT NULL AND site_id IS NULL) OR
|
||||
(venue_type = 2 AND site_id IS NOT NULL AND class_room_id IS NULL)
|
||||
);
|
||||
|
||||
-- 3. 成绩项目单项权重范围: 0 ~ 100
|
||||
ALTER TABLE train_score_item DROP CONSTRAINT IF EXISTS chk_score_item_weight;
|
||||
ALTER TABLE train_score_item ADD CONSTRAINT chk_score_item_weight
|
||||
CHECK (weight >= 0.00 AND weight <= 100.00);
|
||||
|
||||
-- 4. 成绩项目满分设定: 必须大于 0
|
||||
ALTER TABLE train_score_item DROP CONSTRAINT IF EXISTS chk_score_item_full;
|
||||
ALTER TABLE train_score_item ADD CONSTRAINT chk_score_item_full
|
||||
CHECK (full_score > 0.00);
|
||||
|
||||
-- 5. 学员得分非负约束 (未录成绩不建行,录入则非负)
|
||||
ALTER TABLE train_student_score DROP CONSTRAINT IF EXISTS chk_student_score_nonneg;
|
||||
ALTER TABLE train_student_score ADD CONSTRAINT chk_student_score_nonneg
|
||||
CHECK (score >= 0.00);
|
||||
|
||||
-- 6. 学习时长非负检查
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS chk_study_seconds;
|
||||
ALTER TABLE train_study_duration ADD CONSTRAINT chk_study_seconds
|
||||
CHECK (study_seconds >= 0);
|
||||
|
||||
-- 7. 学习记录时间逻辑检查
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS chk_study_time;
|
||||
ALTER TABLE train_study_duration ADD CONSTRAINT chk_study_time
|
||||
CHECK (end_time IS NULL OR start_time IS NULL OR end_time >= start_time);
|
||||
|
||||
-- 8. 学习记录外部单号与来源系统一致性检查
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS chk_study_source_system;
|
||||
ALTER TABLE train_study_duration ADD CONSTRAINT chk_study_source_system
|
||||
CHECK (source_event_id IS NULL OR source_system IS NOT NULL);
|
||||
|
||||
-- 9. 宿舍核定容量正数检查
|
||||
ALTER TABLE train_dormitory_info DROP CONSTRAINT IF EXISTS chk_dorm_capacity;
|
||||
ALTER TABLE train_dormitory_info ADD CONSTRAINT chk_dorm_capacity
|
||||
CHECK (capacity > 0);
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,82 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移脚本: 006_indexes.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 创建业务核心查询索引、外键索引与部分唯一索引
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 设计依据: RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0 及 Round 1 审查要求
|
||||
-- 核心原则:
|
||||
-- 1. 主键与普通 UNIQUE 自动建立的唯一索引不重复创建
|
||||
-- 2. 为所有外键关联列建立独立索引,杜绝全表扫描与锁表
|
||||
-- 3. 排课多维冲突检测建立针对性复合时间索引
|
||||
-- 4. 学习记录来源事件使用双非空过滤的部分唯一索引防逃逸
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 1. train_teacher 教师表索引
|
||||
CREATE INDEX idx_teacher_user_id ON train_teacher (user_id);
|
||||
CREATE INDEX idx_teacher_name ON train_teacher (t_name);
|
||||
|
||||
-- 2. train_student 学员表索引
|
||||
CREATE INDEX idx_student_name ON train_student (s_name);
|
||||
CREATE INDEX idx_student_org ON train_student (org_name);
|
||||
CREATE INDEX idx_student_user_id ON train_student (user_id);
|
||||
|
||||
-- 3. train_course 课程表索引
|
||||
CREATE INDEX idx_course_name ON train_course (c_name);
|
||||
CREATE INDEX idx_course_status ON train_course (status);
|
||||
|
||||
-- 4. train_resource 教学资源表索引
|
||||
CREATE INDEX idx_resource_type ON train_resource (resource_type);
|
||||
CREATE INDEX idx_resource_status ON train_resource (status);
|
||||
|
||||
-- 5. train_course_resource 课程资源关联表索引
|
||||
CREATE INDEX idx_course_resource_course ON train_course_resource (c_id);
|
||||
CREATE INDEX idx_course_resource_resource ON train_course_resource (resource_id);
|
||||
|
||||
-- 6. train_electronic_board 电子班牌外键索引
|
||||
CREATE INDEX idx_board_classroom ON train_electronic_board (bind_classroom_id);
|
||||
|
||||
-- 7. train_plan_course 培训班课程表索引
|
||||
CREATE INDEX idx_plan_course_plan ON train_plan_course (plan_id);
|
||||
CREATE INDEX idx_plan_course_course ON train_plan_course (c_id);
|
||||
|
||||
-- 8. train_enrollment 参训学员表索引
|
||||
CREATE INDEX idx_enrollment_plan ON train_enrollment (plan_id);
|
||||
CREATE INDEX idx_enrollment_student ON train_enrollment (student_id);
|
||||
CREATE INDEX idx_enrollment_status ON train_enrollment (enrollment_status);
|
||||
|
||||
-- 9. train_lesson 排课表多维时间冲突检索复合索引
|
||||
CREATE INDEX idx_lesson_plan_course ON train_lesson (plan_course_id);
|
||||
CREATE INDEX idx_lesson_teacher_time ON train_lesson (t_id, start_time, end_time);
|
||||
CREATE INDEX idx_lesson_classroom_time ON train_lesson (class_room_id, start_time, end_time);
|
||||
CREATE INDEX idx_lesson_site_time ON train_lesson (site_id, start_time, end_time);
|
||||
|
||||
-- 10. train_accommodation 住宿安排表索引
|
||||
CREATE INDEX idx_accommodation_dorm ON train_accommodation (dorm_id);
|
||||
|
||||
-- 11. train_student_checkin 考勤打卡表索引
|
||||
CREATE INDEX idx_checkin_lesson ON train_student_checkin (lesson_id);
|
||||
CREATE INDEX idx_checkin_enrollment ON train_student_checkin (enrollment_id);
|
||||
CREATE INDEX idx_checkin_board ON train_student_checkin (board_id);
|
||||
|
||||
-- 12. train_score_item 成绩项配置表索引
|
||||
CREATE INDEX idx_score_item_plan ON train_score_item (plan_id);
|
||||
CREATE INDEX idx_score_item_plan_course ON train_score_item (plan_course_id);
|
||||
|
||||
-- 13. train_student_score 学员成绩明细表索引
|
||||
CREATE INDEX idx_student_score_enrollment ON train_student_score (enrollment_id);
|
||||
CREATE INDEX idx_student_score_item ON train_student_score (score_item_id);
|
||||
|
||||
-- 14. train_study_duration 学习时长表索引
|
||||
CREATE INDEX idx_study_enrollment ON train_study_duration (enrollment_id);
|
||||
CREATE INDEX idx_study_plan_course ON train_study_duration (plan_course_id);
|
||||
CREATE INDEX idx_study_resource ON train_study_duration (resource_id);
|
||||
CREATE INDEX idx_study_sync ON train_study_duration (sync_time);
|
||||
|
||||
-- 15. train_study_duration 幂等防重部分唯一索引 (必须 source_system 与 source_event_id 均非空方防重)
|
||||
CREATE UNIQUE INDEX uq_idx_study_source_event
|
||||
ON train_study_duration (source_system, source_event_id)
|
||||
WHERE source_system IS NOT NULL AND source_event_id IS NOT NULL;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,335 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 业务字典脚本: 007_dicts.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 准备 RailwaySystem 一期 16 组培训业务字典类型与字典数据
|
||||
-- 设计依据: RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0 及 Round 1 审查要求
|
||||
-- 核心警示:
|
||||
-- 1. 【本文件在当前数据库 V2 结构迁移阶段暂不执行!】留待后续 API/权限设计阶段统一接入。
|
||||
-- 2. 本文件不硬编码 dict_id / dict_code 数字主键,避免与已有 500+ 条系统字典数据冲突。
|
||||
-- 3. 采用 NOT EXISTS 幂等判断,保证即使重复执行也不会重复插入相同字典项。
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ==============================================================================
|
||||
-- 一、注册 16 组业务字典类型 (sys_dict_type)
|
||||
-- ==============================================================================
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '培训系统通用状态', 'train_common_status', '0', 'admin', CURRENT_TIMESTAMP, '通用启用/停用状态 (1:正常, 0:停用)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_common_status');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '开班计划状态', 'train_plan_status', '0', 'admin', CURRENT_TIMESTAMP, '培训班全生命周期状态 (0:未开始, 1:进行中, 2:已结束, 3:已取消)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_plan_status');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '学员参训状态', 'train_enrollment_status', '0', 'admin', CURRENT_TIMESTAMP, '学员参训全过程状态 (0:待报到, 1:已报到, 2:请假, 3:放弃, 4:已完成)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_enrollment_status');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '学员主档来源类型', 'train_student_source', '0', 'admin', CURRENT_TIMESTAMP, '人员主档进入系统的来源 (1:管理员录入, 2:Excel批量导入, 3:铁路内部业务平台同步)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_student_source');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '教学资源类型', 'train_resource_type', '0', 'admin', CURRENT_TIMESTAMP, '资源载体分类 (1:文档, 2:视频, 3:3D模型, 4:VR资源, 5:外部教学链接, 6:其他)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_resource_type');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '教室状态', 'train_room_status', '0', 'admin', CURRENT_TIMESTAMP, '教室使用状态 (1:可用, 0:维护中)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_room_status');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '宿舍房间状态', 'train_dorm_status', '0', 'admin', CURRENT_TIMESTAMP, '宿舍可用状态 (1:可用, 0:维修中)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_dorm_status');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '宿舍适用性别', 'train_gender_type', '0', 'admin', CURRENT_TIMESTAMP, '宿舍男女分配属性 (0:不限, 1:男, 2:女)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_gender_type');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '住宿安排状态', 'train_accommodation_status', '0', 'admin', CURRENT_TIMESTAMP, '住宿分配状态 (0:预分配, 1:已入住, 2:已退宿)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_accommodation_status');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '门禁授权同步状态', 'train_auth_sync_status', '0', 'admin', CURRENT_TIMESTAMP, '外部硬件权限下发状态 (0:待同步, 1:已下发, 2:下发失败, 3:已注销)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_auth_sync_status');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '报到核验方式', 'train_report_way', '0', 'admin', CURRENT_TIMESTAMP, '现场报到凭据方式 (1:人脸识别, 2:身份证刷卡, 3:二维码, 4:人工确认)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_report_way');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '报到核验结果', 'train_verify_result', '0', 'admin', CURRENT_TIMESTAMP, '报到比对判定结论 (1:通过, 2:失败, 3:人工放行)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_verify_result');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '考勤打卡方式', 'train_checkin_way', '0', 'admin', CURRENT_TIMESTAMP, '课次签到签退打卡媒介 (1:人脸识别, 2:刷卡, 3:扫码, 4:教师代签)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_checkin_way');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '课次考勤结论', 'train_attendance_status', '0', 'admin', CURRENT_TIMESTAMP, '考勤状态 (0:缺勤, 1:正常出勤, 2:迟到, 3:早退, 4:请假)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_attendance_status');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '成绩考核项类型', 'train_score_item_type', '0', 'admin', CURRENT_TIMESTAMP, '成绩组成项类型 (1:理论考试, 2:实操演练, 3:出勤考核, 4:综合评价)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_score_item_type');
|
||||
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
SELECT '成绩录入来源', 'train_score_source', '0', 'admin', CURRENT_TIMESTAMP, '成绩产生源头 (1:人工录入, 2:理论考试系统, 3:实操设备, 4:外部导入)'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'train_score_source');
|
||||
|
||||
|
||||
-- ==============================================================================
|
||||
-- 二、注册各字典项数据 (sys_dict_data)
|
||||
-- ==============================================================================
|
||||
|
||||
-- 1. train_common_status
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '正常', '1', 'train_common_status', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '正常启用'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_common_status' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '停用', '0', 'train_common_status', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '停用不可用'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_common_status' AND dict_value = '0');
|
||||
|
||||
-- 2. train_plan_status
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '未开始', '0', 'train_plan_status', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '班级已建立,未到开班时间'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_plan_status' AND dict_value = '0');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '进行中', '1', 'train_plan_status', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '培训班正在开展'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_plan_status' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '已结束', '2', 'train_plan_status', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '培训完成已结业'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_plan_status' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 4, '已取消', '3', 'train_plan_status', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '班级已撤销'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_plan_status' AND dict_value = '3');
|
||||
|
||||
-- 3. train_enrollment_status
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '待报到', '0', 'train_enrollment_status', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '已入班名单,待现场核验报到'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_enrollment_status' AND dict_value = '0');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '已报到', '1', 'train_enrollment_status', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '已到场核验完成'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_enrollment_status' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '请假', '2', 'train_enrollment_status', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '全程请假或特殊事假'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_enrollment_status' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 4, '放弃', '3', 'train_enrollment_status', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '未到或中途主动退训'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_enrollment_status' AND dict_value = '3');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 5, '已完成', '4', 'train_enrollment_status', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '参训合格并顺利结业'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_enrollment_status' AND dict_value = '4');
|
||||
|
||||
-- 4. train_student_source (人员主档来源)
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '管理员录入', '1', 'train_student_source', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '管理员单条或批量手工建立主档'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_student_source' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, 'Excel批量导入', '2', 'train_student_source', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, '线下花名册表格批量导入'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_student_source' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '铁路内部业务平台同步', '3', 'train_student_source', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '从国铁/路局内部HR或教务系统自动下发'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_student_source' AND dict_value = '3');
|
||||
|
||||
-- 5. train_resource_type (教学资源类型: 严格对齐 V2 冻结稿)
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '文档', '1', 'train_resource_type', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, 'PDF/Word/PPT等电子课件'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_resource_type' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '视频', '2', 'train_resource_type', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, 'MP4录播视频'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_resource_type' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '3D模型', '3', 'train_resource_type', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '3D数字化实物机车/工务模型'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_resource_type' AND dict_value = '3');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 4, 'VR资源', '4', 'train_resource_type', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '虚拟仿真头盔或交互式实训课件'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_resource_type' AND dict_value = '4');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 5, '外部教学链接', '5', 'train_resource_type', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '第三方外部实训/考试网址URL'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_resource_type' AND dict_value = '5');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 6, '其他', '6', 'train_resource_type', 'default', 'N', '0', 'admin', CURRENT_TIMESTAMP, '其他类型教学资料'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_resource_type' AND dict_value = '6');
|
||||
|
||||
-- 6. train_room_status
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '正常可用', '1', 'train_room_status', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '教室可正常排课'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_room_status' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '维护保养中', '0', 'train_room_status', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '检修维护,暂不可排课'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_room_status' AND dict_value = '0');
|
||||
|
||||
-- 7. train_dorm_status
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '正常可用', '1', 'train_dorm_status', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '房间正常分配'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_dorm_status' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '维修停用', '0', 'train_dorm_status', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '房间设施报修中'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_dorm_status' AND dict_value = '0');
|
||||
|
||||
-- 8. train_gender_type
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '不限', '0', 'train_gender_type', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '混合或不限'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_gender_type' AND dict_value = '0');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '男', '1', 'train_gender_type', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '男生宿舍'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_gender_type' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '女', '2', 'train_gender_type', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '女生宿舍'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_gender_type' AND dict_value = '2');
|
||||
|
||||
-- 9. train_accommodation_status
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '预分配', '0', 'train_accommodation_status', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '报到前系统预先分配'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_accommodation_status' AND dict_value = '0');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '已入住', '1', 'train_accommodation_status', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '报到确认并已入住'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_accommodation_status' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '已退宿', '2', 'train_accommodation_status', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '培训结束已离宿'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_accommodation_status' AND dict_value = '2');
|
||||
|
||||
-- 10. train_auth_sync_status
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '待下发', '0', 'train_auth_sync_status', 'info', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '未与外部门禁系统握手'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_auth_sync_status' AND dict_value = '0');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '已下发', '1', 'train_auth_sync_status', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '门禁硬件下发生效'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_auth_sync_status' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '下发失败', '2', 'train_auth_sync_status', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '网络超时或凭据错误'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_auth_sync_status' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 4, '已注销', '3', 'train_auth_sync_status', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '已退宿并回收门禁权限'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_auth_sync_status' AND dict_value = '3');
|
||||
|
||||
-- 11. train_report_way
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '人脸识别', '1', 'train_report_way', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '通过人脸比对报到'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_report_way' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '身份证刷卡', '2', 'train_report_way', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, '二代身份证读卡器'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_report_way' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '手机扫码', '3', 'train_report_way', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '扫班级签到二维码'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_report_way' AND dict_value = '3');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 4, '人工确认', '4', 'train_report_way', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '班主任后台手动报到'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_report_way' AND dict_value = '4');
|
||||
|
||||
-- 12. train_verify_result
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '通过', '1', 'train_verify_result', 'success', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '身份信息核验一致'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_verify_result' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '失败', '2', 'train_verify_result', 'danger', 'N', '0', 'admin', CURRENT_TIMESTAMP, '比对不符或未在名单'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_verify_result' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '人工放行', '3', 'train_verify_result', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '现场特殊人工核验放行'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_verify_result' AND dict_value = '3');
|
||||
|
||||
-- 13. train_checkin_way
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '班牌人脸识别', '1', 'train_checkin_way', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '教室门口电子班牌刷脸'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_checkin_way' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '班牌刷卡', '2', 'train_checkin_way', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, '学员卡刷班牌打卡'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_checkin_way' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '手机扫码打卡', '3', 'train_checkin_way', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '扫班牌动态二维码'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_checkin_way' AND dict_value = '3');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 4, '教师代签/补卡', '4', 'train_checkin_way', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '授课老师端人工代签'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_checkin_way' AND dict_value = '4');
|
||||
|
||||
-- 14. train_attendance_status
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '缺勤', '0', 'train_attendance_status', 'danger', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '课次无打卡记录'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_attendance_status' AND dict_value = '0');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '正常出勤', '1', 'train_attendance_status', 'primary', 'N', '0', 'admin', CURRENT_TIMESTAMP, '准时签到且准时签退'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_attendance_status' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '迟到', '2', 'train_attendance_status', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '超过上课设定宽限时间'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_attendance_status' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 4, '早退', '3', 'train_attendance_status', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '下课前提早离开'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_attendance_status' AND dict_value = '3');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 5, '请假', '4', 'train_attendance_status', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, '当堂课次有效请假'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_attendance_status' AND dict_value = '4');
|
||||
|
||||
-- 15. train_score_item_type
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '理论考试', '1', 'train_score_item_type', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '理论闭卷/开卷机考'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_score_item_type' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '实操演练', '2', 'train_score_item_type', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '现场设备实操/VR仿真测评'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_score_item_type' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '出勤考核', '3', 'train_score_item_type', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, '全期考勤率折算成绩'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_score_item_type' AND dict_value = '3');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 4, '综合评价', '4', 'train_score_item_type', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '教师日常考评或答辩'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_score_item_type' AND dict_value = '4');
|
||||
|
||||
-- 16. train_score_source
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 1, '人工录入', '1', 'train_score_source', 'primary', 'Y', '0', 'admin', CURRENT_TIMESTAMP, '管理员或教师手工登记'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_score_source' AND dict_value = '1');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 2, '理论机考对接', '2', 'train_score_source', 'info', 'N', '0', 'admin', CURRENT_TIMESTAMP, '从在线考试平台接口抓取'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_score_source' AND dict_value = '2');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 3, '实操设备回传', '3', 'train_score_source', 'warning', 'N', '0', 'admin', CURRENT_TIMESTAMP, '从VR/实训台硬件自动计算上报'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_score_source' AND dict_value = '3');
|
||||
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, list_class, is_default, status, create_by, create_time, remark)
|
||||
SELECT 4, 'Excel批量导入', '4', 'train_score_source', 'success', 'N', '0', 'admin', CURRENT_TIMESTAMP, '线下成绩表格批量导入'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'train_score_source' AND dict_value = '4');
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,57 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移脚本: 008_migrate_teacher.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 迁移并保留 train_teacher 唯一样本数据,完成旧列向新列平滑过渡并安全清理
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 设计依据: RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0 及 Round 1 审查要求
|
||||
-- 安全规范:
|
||||
-- 1. 严禁在脚本中出现真实教师姓名、电话、身份证、邮箱等明文数据!
|
||||
-- 2. 完全采用纯 DML 数据映射: t_department -> org_name, t_title -> job_title, create_user -> create_by
|
||||
-- 3. 执行后验证迁移完整性,再将 status 与 del_flag 设为 NOT NULL 并安全移除旧列
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 步骤 1: 数据平滑映射 (仅更新新字段为空的行,保证幂等)
|
||||
UPDATE train_teacher
|
||||
SET
|
||||
org_name = COALESCE(org_name, t_department),
|
||||
job_title = COALESCE(job_title, t_title),
|
||||
create_by = COALESCE(create_by, create_user),
|
||||
status = COALESCE(status, 1),
|
||||
del_flag = COALESCE(del_flag, '0')
|
||||
WHERE t_department IS NOT NULL OR t_title IS NOT NULL OR create_user IS NOT NULL;
|
||||
|
||||
-- 步骤 2: 校验迁移正确性 (数据行数精确为1,且新字段全部填充完成)
|
||||
DO $$
|
||||
DECLARE
|
||||
v_total_cnt INT;
|
||||
v_unmigrated_cnt INT;
|
||||
BEGIN
|
||||
SELECT count(*) INTO v_total_cnt FROM train_teacher;
|
||||
SELECT count(*) INTO v_unmigrated_cnt
|
||||
FROM train_teacher
|
||||
WHERE org_name IS NULL OR status IS NULL OR del_flag IS NULL;
|
||||
|
||||
IF v_total_cnt <> 1 THEN
|
||||
RAISE EXCEPTION '>>> [008-FATAL] 教师数据校验异常: 期望精确 1 条数据,实际检测到 % 条', v_total_cnt;
|
||||
END IF;
|
||||
|
||||
IF v_unmigrated_cnt > 0 THEN
|
||||
RAISE EXCEPTION '>>> [008-FATAL] 教师数据迁移失败: 存在 % 条记录的新主档字段尚未正确填充', v_unmigrated_cnt;
|
||||
END IF;
|
||||
|
||||
RAISE NOTICE '>>> [008-PASS] 教师唯一数据迁移核验通过,准备固化 NOT NULL 约束与清理旧列。';
|
||||
END $$;
|
||||
|
||||
-- 步骤 3: 固化新列 NOT NULL 约束
|
||||
ALTER TABLE train_teacher ALTER COLUMN status SET NOT NULL;
|
||||
ALTER TABLE train_teacher ALTER COLUMN del_flag SET NOT NULL;
|
||||
|
||||
-- 步骤 4: 安全删除已成功迁移的旧列 (杜绝 CASCADE)
|
||||
ALTER TABLE train_teacher
|
||||
DROP COLUMN IF EXISTS t_department,
|
||||
DROP COLUMN IF EXISTS t_title,
|
||||
DROP COLUMN IF EXISTS create_user;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,46 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移脚本: 009_drop_legacy_score.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 淘汰旧模型表 train_score_info (由 train_score_item + train_student_score 取代)
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 设计依据: RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0 及 Round 1 审查要求
|
||||
-- 安全规范:
|
||||
-- 1. 【强阻断要求】执行前必须严格断言 train_score_info 确为 0 行,非 0 立即异常中止!
|
||||
-- 2. 显式解除自身约束,使用普通 DROP TABLE (杜绝使用 CASCADE 掩盖未预期依赖)
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 步骤 1: 强安全阻断校验 (非 0 行立即阻断并中止事务)
|
||||
DO $$
|
||||
DECLARE
|
||||
v_table_exists INT;
|
||||
v_row_cnt INT;
|
||||
BEGIN
|
||||
SELECT count(*) INTO v_table_exists
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = current_schema() AND table_name = 'train_score_info';
|
||||
|
||||
IF v_table_exists > 0 THEN
|
||||
EXECUTE 'SELECT count(*) FROM train_score_info' INTO v_row_cnt;
|
||||
IF v_row_cnt > 0 THEN
|
||||
RAISE EXCEPTION '>>> [009-FATAL] train_score_info 当前包含 % 行真实数据!禁止执行 DROP 操作!', v_row_cnt;
|
||||
ELSE
|
||||
RAISE NOTICE '>>> [009-PASS] 安全探测通过: train_score_info 精确为 0 行,允许执行淘汰下线。';
|
||||
END IF;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 步骤 2: 卸载旧触发器 (若存在)
|
||||
DROP TRIGGER IF EXISTS trig_train_scoreinfo_modify ON train_score_info;
|
||||
|
||||
-- 步骤 3: 显式卸载已知历史约束
|
||||
ALTER TABLE train_score_info DROP CONSTRAINT IF EXISTS fk_scoreinfo_plan;
|
||||
ALTER TABLE train_score_info DROP CONSTRAINT IF EXISTS fk_scoreinfo_course;
|
||||
ALTER TABLE train_score_info DROP CONSTRAINT IF EXISTS fk_scoreinfo_stu;
|
||||
ALTER TABLE train_score_info DROP CONSTRAINT IF EXISTS uq_scoreinfo_plan_c_stu;
|
||||
|
||||
-- 步骤 4: 安全常规 DROP TABLE (无 CASCADE)
|
||||
DROP TABLE IF EXISTS train_score_info;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,500 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 迁移验收脚本: 010_verify.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 实施“定义级深度验收”,核验数据库 V2 迁移后对象存在性与真实结构定义
|
||||
-- 设计依据: RailwaySystem 数据库 V2 字段设计与 Kingbase DDL 规范 V1.0 及 Round 2 审查要求
|
||||
-- 核心原则:
|
||||
-- 1. 【失败即失败】所有验证逻辑均通过 PL/pgSQL DO 块强制断言,出现任何 FAIL 立即抛出 EXCEPTION
|
||||
-- 2. 【定义级验收】拒绝仅查名字,必须校验: 表名、列列表、目标外键与删除规则、CHECK定义、索引定义
|
||||
-- 3. 【默认值校验】强断言 accommodation_status 默认值为 0,强断言业务选择型字段无 DEFAULT
|
||||
-- 4. 【系统零破坏】显式核验 public.sys_* 系统表行数与基线完全吻合
|
||||
-- ==============================================================================
|
||||
|
||||
-- 步骤 1: 打印 21 张表状态清单 (供人工审计屏幕阅读)
|
||||
SELECT t.table_name,
|
||||
CASE WHEN c.table_name IS NOT NULL THEN 'EXISTS' ELSE 'MISSING' END AS table_status
|
||||
FROM (
|
||||
VALUES
|
||||
('train_teacher'), ('train_student'), ('train_course'), ('train_resource'),
|
||||
('train_course_resource'), ('train_classroom_info'), ('train_training_site'),
|
||||
('train_electronic_board'), ('train_dormitory_info'), ('train_class_plan'),
|
||||
('train_plan_course'), ('train_enrollment'), ('train_lesson'),
|
||||
('train_accommodation'), ('train_dorm_auth'), ('train_class_report'),
|
||||
('train_student_checkin'), ('train_score_item'), ('train_student_score'),
|
||||
('train_study_duration'), ('train_student_portrait')
|
||||
) AS t(table_name)
|
||||
LEFT JOIN information_schema.tables c
|
||||
ON c.table_schema = 'public' AND c.table_name = t.table_name
|
||||
ORDER BY t.table_name;
|
||||
|
||||
|
||||
-- 步骤 2: 核心自动化强断言块 (任何项不满足立即抛出 EXCEPTION 中止)
|
||||
DO $$
|
||||
DECLARE
|
||||
v_cnt INT;
|
||||
v_diff_text TEXT := '';
|
||||
r RECORD;
|
||||
BEGIN
|
||||
-- ==========================================================================
|
||||
-- 1. 逐名断言 21 张正式业务表完全就绪且无冗余 train_* 杂表
|
||||
-- ==========================================================================
|
||||
FOR r IN (
|
||||
WITH expected_tables(table_name) AS (
|
||||
VALUES
|
||||
('train_teacher'), ('train_student'), ('train_course'), ('train_resource'),
|
||||
('train_course_resource'), ('train_classroom_info'), ('train_training_site'),
|
||||
('train_electronic_board'), ('train_dormitory_info'), ('train_class_plan'),
|
||||
('train_plan_course'), ('train_enrollment'), ('train_lesson'),
|
||||
('train_accommodation'), ('train_dorm_auth'), ('train_class_report'),
|
||||
('train_student_checkin'), ('train_score_item'), ('train_student_score'),
|
||||
('train_study_duration'), ('train_student_portrait')
|
||||
),
|
||||
actual_tables AS (
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name LIKE 'train_%'
|
||||
AND table_name NOT LIKE 'bak_%'
|
||||
)
|
||||
SELECT 'MISSING_TABLE' AS diff_type, e.table_name
|
||||
FROM expected_tables e
|
||||
LEFT JOIN actual_tables a ON e.table_name = a.table_name
|
||||
WHERE a.table_name IS NULL
|
||||
UNION ALL
|
||||
SELECT 'UNEXPECTED_TABLE' AS diff_type, a.table_name
|
||||
FROM actual_tables a
|
||||
LEFT JOIN expected_tables e ON a.table_name = e.table_name
|
||||
WHERE e.table_name IS NULL
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.diff_type || ':' || r.table_name || '; ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] 21 张正式业务表集合不匹配: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- 确认旧淘汰表 train_score_info 彻底不存在
|
||||
SELECT count(*) INTO v_cnt FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name = 'train_score_info';
|
||||
IF v_cnt > 0 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] train_score_info 仍残留在 public schema 中,淘汰下线失败!';
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 2. 数据行数基线强断言 (教师精确 1 行,新字段全部填充,其余 20 表精确 0 行)
|
||||
-- ==========================================================================
|
||||
SELECT count(*) INTO v_cnt FROM train_teacher;
|
||||
IF v_cnt <> 1 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] train_teacher 行数期望为 1,实际为 % 行', v_cnt;
|
||||
END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt
|
||||
FROM train_teacher
|
||||
WHERE org_name IS NULL OR status IS NULL OR del_flag IS NULL;
|
||||
IF v_cnt > 0 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] train_teacher 记录中新主档字段存在非法 NULL 值';
|
||||
END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_teacher'
|
||||
AND column_name IN ('t_department', 't_title', 'create_user');
|
||||
IF v_cnt > 0 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] train_teacher 仍残留旧字段 (t_department/t_title/create_user)';
|
||||
END IF;
|
||||
|
||||
FOR r IN (
|
||||
SELECT unnest(ARRAY[
|
||||
'train_student', 'train_course', 'train_resource', 'train_course_resource',
|
||||
'train_classroom_info', 'train_training_site', 'train_electronic_board',
|
||||
'train_dormitory_info', 'train_class_plan', 'train_plan_course',
|
||||
'train_enrollment', 'train_lesson', 'train_accommodation',
|
||||
'train_dorm_auth', 'train_class_report', 'train_student_checkin',
|
||||
'train_score_item', 'train_student_score', 'train_study_duration',
|
||||
'train_student_portrait'
|
||||
]) AS tbl
|
||||
) LOOP
|
||||
EXECUTE 'SELECT count(*) FROM ' || r.tbl INTO v_cnt;
|
||||
IF v_cnt <> 0 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] 表 % 包含非预期数据,当前行数为 % (期望 0 行)', r.tbl, v_cnt;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 3. 13 组核心唯一约束 (UNIQUE) 定义级校验 (表名 + 约束名 + 组成列列表)
|
||||
-- ==========================================================================
|
||||
v_diff_text := '';
|
||||
FOR r IN (
|
||||
WITH expected_unique(table_name, constraint_name, column_list) AS (
|
||||
VALUES
|
||||
('train_student', 'uq_student_idcard_hash', 'id_card_hash'),
|
||||
('train_course', 'uq_course_code', 'c_code'),
|
||||
('train_electronic_board', 'uq_board_sn', 'board_sn'),
|
||||
('train_dormitory_info', 'uq_dorm_building_room', 'dorm_building, dorm_room'),
|
||||
('train_course_resource', 'uq_course_resource', 'c_id, resource_id'),
|
||||
('train_plan_course', 'uq_plan_course', 'plan_id, c_id'),
|
||||
('train_enrollment', 'uq_enrollment', 'plan_id, student_id'),
|
||||
('train_accommodation', 'uq_accommodation_enroll', 'enrollment_id'),
|
||||
('train_dorm_auth', 'uq_dorm_auth_accomm', 'accommodation_id'),
|
||||
('train_class_report', 'uq_class_report_enroll', 'enrollment_id'),
|
||||
('train_student_checkin', 'uq_student_checkin_enroll_lesson', 'enrollment_id, lesson_id'),
|
||||
('train_student_score', 'uq_student_score_enroll_item', 'enrollment_id, score_item_id'),
|
||||
('train_student_portrait', 'uq_student_portrait_enroll', 'enrollment_id')
|
||||
),
|
||||
actual_unique AS (
|
||||
SELECT
|
||||
tc.table_name,
|
||||
tc.constraint_name,
|
||||
string_agg(kcu.column_name, ', ' ORDER BY kcu.ordinal_position) AS column_list
|
||||
FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.key_column_usage kcu
|
||||
ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
|
||||
WHERE tc.constraint_type = 'UNIQUE'
|
||||
AND tc.table_schema = 'public'
|
||||
AND tc.table_name LIKE 'train_%'
|
||||
GROUP BY tc.table_name, tc.constraint_name
|
||||
)
|
||||
SELECT 'MISSING_UQ' AS diff_type, e.table_name, e.constraint_name, e.column_list
|
||||
FROM expected_unique e
|
||||
LEFT JOIN actual_unique a
|
||||
ON e.table_name = a.table_name
|
||||
AND e.constraint_name = a.constraint_name
|
||||
AND e.column_list = a.column_list
|
||||
WHERE a.table_name IS NULL
|
||||
UNION ALL
|
||||
SELECT 'UNEXPECTED_UQ' AS diff_type, a.table_name, a.constraint_name, a.column_list
|
||||
FROM actual_unique a
|
||||
LEFT JOIN expected_unique e
|
||||
ON a.table_name = e.table_name
|
||||
AND a.constraint_name = e.constraint_name
|
||||
AND a.column_list = e.column_list
|
||||
WHERE e.table_name IS NULL
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.diff_type || ': ' || r.table_name || '.' || r.constraint_name || '(' || r.column_list || '); ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] 13 组唯一约束 (UNIQUE) 定义不匹配: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 4. 26 组外键约束 (FK) 定义级校验 (表名 + 约束名 + 本地列 + 目标表 + 目标列 + 删除规则)
|
||||
-- ==========================================================================
|
||||
v_diff_text := '';
|
||||
FOR r IN (
|
||||
WITH expected_fk(table_name, constraint_name, column_name, foreign_table_name, foreign_column_name) AS (
|
||||
VALUES
|
||||
('train_course_resource', 'fk_cr_course', 'c_id', 'train_course', 'c_id'),
|
||||
('train_course_resource', 'fk_cr_resource', 'resource_id', 'train_resource', 'resource_id'),
|
||||
('train_electronic_board', 'fk_board_classroom', 'bind_classroom_id', 'train_classroom_info', 'class_room_id'),
|
||||
('train_plan_course', 'fk_plancourse_plan', 'plan_id', 'train_class_plan', 'plan_id'),
|
||||
('train_plan_course', 'fk_plancourse_course', 'c_id', 'train_course', 'c_id'),
|
||||
('train_enrollment', 'fk_enrollment_plan', 'plan_id', 'train_class_plan', 'plan_id'),
|
||||
('train_enrollment', 'fk_enrollment_student', 'student_id', 'train_student', 's_id'),
|
||||
('train_lesson', 'fk_lesson_plancourse', 'plan_course_id', 'train_plan_course', 'plan_course_id'),
|
||||
('train_lesson', 'fk_lesson_teacher', 't_id', 'train_teacher', 't_id'),
|
||||
('train_lesson', 'fk_lesson_classroom', 'class_room_id', 'train_classroom_info', 'class_room_id'),
|
||||
('train_lesson', 'fk_lesson_site', 'site_id', 'train_training_site', 'site_id'),
|
||||
('train_accommodation', 'fk_accomm_enrollment', 'enrollment_id', 'train_enrollment', 'enrollment_id'),
|
||||
('train_accommodation', 'fk_accomm_dorm', 'dorm_id', 'train_dormitory_info', 'dorm_id'),
|
||||
('train_dorm_auth', 'fk_dormauth_accomm', 'accommodation_id', 'train_accommodation', 'accommodation_id'),
|
||||
('train_class_report', 'fk_report_enrollment', 'enrollment_id', 'train_enrollment', 'enrollment_id'),
|
||||
('train_student_checkin', 'fk_checkin_enrollment', 'enrollment_id', 'train_enrollment', 'enrollment_id'),
|
||||
('train_student_checkin', 'fk_checkin_lesson', 'lesson_id', 'train_lesson', 'lesson_id'),
|
||||
('train_student_checkin', 'fk_checkin_board', 'board_id', 'train_electronic_board', 'board_id'),
|
||||
('train_score_item', 'fk_scoreitem_plan', 'plan_id', 'train_class_plan', 'plan_id'),
|
||||
('train_score_item', 'fk_scoreitem_plancourse', 'plan_course_id', 'train_plan_course', 'plan_course_id'),
|
||||
('train_student_score', 'fk_score_enrollment', 'enrollment_id', 'train_enrollment', 'enrollment_id'),
|
||||
('train_student_score', 'fk_score_item', 'score_item_id', 'train_score_item', 'score_item_id'),
|
||||
('train_study_duration', 'fk_study_enrollment', 'enrollment_id', 'train_enrollment', 'enrollment_id'),
|
||||
('train_study_duration', 'fk_study_plancourse', 'plan_course_id', 'train_plan_course', 'plan_course_id'),
|
||||
('train_study_duration', 'fk_study_resource', 'resource_id', 'train_resource', 'resource_id'),
|
||||
('train_student_portrait', 'fk_portrait_enrollment', 'enrollment_id', 'train_enrollment', 'enrollment_id')
|
||||
),
|
||||
actual_fk AS (
|
||||
SELECT
|
||||
tc.table_name,
|
||||
tc.constraint_name,
|
||||
kcu.column_name,
|
||||
ccu.table_name AS foreign_table_name,
|
||||
ccu.column_name AS foreign_column_name,
|
||||
rc.delete_rule
|
||||
FROM information_schema.table_constraints tc
|
||||
JOIN information_schema.key_column_usage kcu
|
||||
ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
|
||||
JOIN information_schema.constraint_column_usage ccu
|
||||
ON tc.constraint_name = ccu.constraint_name AND tc.table_schema = ccu.table_schema
|
||||
JOIN information_schema.referential_constraints rc
|
||||
ON tc.constraint_name = rc.constraint_name AND tc.table_schema = rc.constraint_schema
|
||||
WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_schema = 'public'
|
||||
AND tc.table_name LIKE 'train_%'
|
||||
)
|
||||
SELECT 'MISSING_FK' AS diff_type, e.table_name, e.constraint_name, e.column_name, e.foreign_table_name, e.foreign_column_name
|
||||
FROM expected_fk e
|
||||
LEFT JOIN actual_fk a
|
||||
ON e.table_name = a.table_name
|
||||
AND e.constraint_name = a.constraint_name
|
||||
AND e.column_name = a.column_name
|
||||
AND e.foreign_table_name = a.foreign_table_name
|
||||
AND e.foreign_column_name = a.foreign_column_name
|
||||
AND a.delete_rule = 'NO ACTION'
|
||||
WHERE a.table_name IS NULL
|
||||
UNION ALL
|
||||
SELECT 'UNEXPECTED_FK' AS diff_type, a.table_name, a.constraint_name, a.column_name, a.foreign_table_name, a.foreign_column_name
|
||||
FROM actual_fk a
|
||||
LEFT JOIN expected_fk e
|
||||
ON a.table_name = e.table_name
|
||||
AND a.constraint_name = e.constraint_name
|
||||
AND a.column_name = e.column_name
|
||||
AND a.foreign_table_name = e.foreign_table_name
|
||||
AND a.foreign_column_name = e.foreign_column_name
|
||||
WHERE e.table_name IS NULL
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.diff_type || ': ' || r.table_name || '.' || r.constraint_name || '; ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] 26 组外键约束 (FK) 定义或删除规则不匹配: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 5. 9 组单行检查约束 (CHECK) 定义逻辑校验 (逐名检查并核验包含预期核心表达式)
|
||||
-- ==========================================================================
|
||||
FOR r IN (
|
||||
SELECT
|
||||
cc.constraint_name,
|
||||
tc.table_name,
|
||||
cc.check_clause
|
||||
FROM information_schema.check_constraints cc
|
||||
JOIN information_schema.table_constraints tc
|
||||
ON cc.constraint_name = tc.constraint_name AND cc.constraint_schema = tc.constraint_schema
|
||||
WHERE tc.table_schema = 'public' AND tc.table_name LIKE 'train_%'
|
||||
) LOOP
|
||||
CASE r.constraint_name
|
||||
WHEN 'chk_lesson_time' THEN
|
||||
IF r.table_name <> 'train_lesson' OR r.check_clause NOT LIKE '%start_time < end_time%' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] chk_lesson_time 定义不合预期: %', r.check_clause;
|
||||
END IF;
|
||||
WHEN 'chk_lesson_venue' THEN
|
||||
IF r.table_name <> 'train_lesson' OR (r.check_clause NOT LIKE '%venue_type = 1%' OR r.check_clause NOT LIKE '%venue_type = 2%') THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] chk_lesson_venue 场地互斥定义不合预期: %', r.check_clause;
|
||||
END IF;
|
||||
WHEN 'chk_score_item_weight' THEN
|
||||
IF r.table_name <> 'train_score_item' OR (r.check_clause NOT LIKE '%weight >= 0%' OR r.check_clause NOT LIKE '%weight <= 100%') THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] chk_score_item_weight 权重范围定义不合预期: %', r.check_clause;
|
||||
END IF;
|
||||
WHEN 'chk_score_item_full' THEN
|
||||
IF r.table_name <> 'train_score_item' OR r.check_clause NOT LIKE '%full_score > 0%' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] chk_score_item_full 满分定义不合预期: %', r.check_clause;
|
||||
END IF;
|
||||
WHEN 'chk_student_score_nonneg' THEN
|
||||
IF r.table_name <> 'train_student_score' OR r.check_clause NOT LIKE '%score >= 0%' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] chk_student_score_nonneg 成绩非负定义不合预期: %', r.check_clause;
|
||||
END IF;
|
||||
WHEN 'chk_study_seconds' THEN
|
||||
IF r.table_name <> 'train_study_duration' OR r.check_clause NOT LIKE '%study_seconds >= 0%' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] chk_study_seconds 时长非负定义不合预期: %', r.check_clause;
|
||||
END IF;
|
||||
WHEN 'chk_study_time' THEN
|
||||
IF r.table_name <> 'train_study_duration' OR r.check_clause NOT LIKE '%end_time >= start_time%' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] chk_study_time 时间先后定义不合预期: %', r.check_clause;
|
||||
END IF;
|
||||
WHEN 'chk_study_source_system' THEN
|
||||
IF r.table_name <> 'train_study_duration' OR r.check_clause NOT LIKE '%source_system IS NOT NULL%' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] chk_study_source_system 来源系统一致性定义不合预期: %', r.check_clause;
|
||||
END IF;
|
||||
WHEN 'chk_dorm_capacity' THEN
|
||||
IF r.table_name <> 'train_dormitory_info' OR r.check_clause NOT LIKE '%capacity > 0%' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] chk_dorm_capacity 容量正数定义不合预期: %', r.check_clause;
|
||||
END IF;
|
||||
ELSE
|
||||
NULL; -- 忽略其他系统生成的非空检查
|
||||
END CASE;
|
||||
END LOOP;
|
||||
|
||||
-- 确认 9 个显式命名的 CHECK 约束一个不少
|
||||
SELECT count(*) INTO v_cnt
|
||||
FROM information_schema.table_constraints
|
||||
WHERE table_schema = 'public'
|
||||
AND constraint_type = 'CHECK'
|
||||
AND constraint_name IN (
|
||||
'chk_lesson_time', 'chk_lesson_venue', 'chk_score_item_weight',
|
||||
'chk_score_item_full', 'chk_student_score_nonneg', 'chk_study_seconds',
|
||||
'chk_study_time', 'chk_study_source_system', 'chk_dorm_capacity'
|
||||
);
|
||||
IF v_cnt <> 9 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] 9 个核心 CHECK 检查约束总数不匹配,实际发现 % 个', v_cnt;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 6. 12 张业务表 Identity 自增主键精确校验 (表名 + 指定列名 + is_identity = 'YES')
|
||||
-- ==========================================================================
|
||||
v_diff_text := '';
|
||||
FOR r IN (
|
||||
WITH expected_identity(table_name, column_name) AS (
|
||||
VALUES
|
||||
('train_course_resource', 'id'),
|
||||
('train_plan_course', 'plan_course_id'),
|
||||
('train_enrollment', 'enrollment_id'),
|
||||
('train_lesson', 'lesson_id'),
|
||||
('train_accommodation', 'accommodation_id'),
|
||||
('train_score_item', 'score_item_id'),
|
||||
('train_student_score', 'score_id'),
|
||||
('train_dorm_auth', 'auth_id'),
|
||||
('train_class_report', 'report_id'),
|
||||
('train_student_checkin', 'checkin_id'),
|
||||
('train_study_duration', 'dur_id'),
|
||||
('train_student_portrait', 'portrait_id')
|
||||
),
|
||||
actual_identity AS (
|
||||
SELECT table_name, column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name LIKE 'train_%'
|
||||
AND is_identity = 'YES'
|
||||
)
|
||||
SELECT 'MISSING_IDENTITY' AS diff_type, e.table_name, e.column_name
|
||||
FROM expected_identity e
|
||||
LEFT JOIN actual_identity a
|
||||
ON e.table_name = a.table_name AND e.column_name = a.column_name
|
||||
WHERE a.table_name IS NULL
|
||||
UNION ALL
|
||||
SELECT 'UNEXPECTED_IDENTITY' AS diff_type, a.table_name, a.column_name
|
||||
FROM actual_identity a
|
||||
LEFT JOIN expected_identity e
|
||||
ON a.table_name = e.table_name AND a.column_name = e.column_name
|
||||
WHERE e.table_name IS NULL
|
||||
) LOOP
|
||||
v_diff_text := v_diff_text || r.diff_type || ': ' || r.table_name || '.' || r.column_name || '; ';
|
||||
END LOOP;
|
||||
|
||||
IF v_diff_text <> '' THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] 12 张表 Identity 自增主键属性不匹配: %', v_diff_text;
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 7. 关键复合索引与部分唯一索引定义级校验
|
||||
-- ==========================================================================
|
||||
-- 7.1 uq_idx_study_source_event 必须为 UNIQUE 且具有双非空 WHERE 条件
|
||||
SELECT count(*) INTO v_cnt FROM pg_indexes
|
||||
WHERE schemaname = 'public'
|
||||
AND tablename = 'train_study_duration'
|
||||
AND indexname = 'uq_idx_study_source_event'
|
||||
AND indexdef LIKE '%UNIQUE INDEX%'
|
||||
AND indexdef LIKE '%(source_system, source_event_id)%'
|
||||
AND indexdef LIKE '%source_system IS NOT NULL%'
|
||||
AND indexdef LIKE '%source_event_id IS NOT NULL%';
|
||||
IF v_cnt <> 1 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] 部分唯一索引 uq_idx_study_source_event 定义不合要求';
|
||||
END IF;
|
||||
|
||||
-- 7.2 排课表三大时间检索复合索引
|
||||
SELECT count(*) INTO v_cnt FROM pg_indexes
|
||||
WHERE schemaname = 'public' AND tablename = 'train_lesson'
|
||||
AND indexname = 'idx_lesson_teacher_time' AND indexdef LIKE '%(t_id, start_time, end_time)%';
|
||||
IF v_cnt <> 1 THEN RAISE EXCEPTION '>>> [Verify-FATAL] 索引 idx_lesson_teacher_time 复合列定义不合要求'; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM pg_indexes
|
||||
WHERE schemaname = 'public' AND tablename = 'train_lesson'
|
||||
AND indexname = 'idx_lesson_classroom_time' AND indexdef LIKE '%(class_room_id, start_time, end_time)%';
|
||||
IF v_cnt <> 1 THEN RAISE EXCEPTION '>>> [Verify-FATAL] 索引 idx_lesson_classroom_time 复合列定义不合要求'; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM pg_indexes
|
||||
WHERE schemaname = 'public' AND tablename = 'train_lesson'
|
||||
AND indexname = 'idx_lesson_site_time' AND indexdef LIKE '%(site_id, start_time, end_time)%';
|
||||
IF v_cnt <> 1 THEN RAISE EXCEPTION '>>> [Verify-FATAL] 索引 idx_lesson_site_time 复合列定义不合要求'; END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 8. 默认值严格校验 (R2-01 住宿默认0预分配; R2-02 业务选择型字段严禁有DEFAULT)
|
||||
-- ==========================================================================
|
||||
-- 8.1 accommodation_status 默认值必须为 0 (预分配)
|
||||
SELECT count(*) INTO v_cnt
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_accommodation'
|
||||
AND column_name = 'accommodation_status' AND column_default LIKE '%0%';
|
||||
IF v_cnt <> 1 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] train_accommodation.accommodation_status 默认值必须为 0 (预分配)';
|
||||
END IF;
|
||||
|
||||
-- 8.2 业务选择型字段严禁存在默认值 (column_default 必须为 NULL)
|
||||
FOR r IN (
|
||||
SELECT table_name, column_name, column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND (
|
||||
(table_name = 'train_enrollment' AND column_name IN ('source_type', 'accommodation_required')) OR
|
||||
(table_name = 'train_lesson' AND column_name = 'venue_type') OR
|
||||
(table_name = 'train_student_score' AND column_name = 'source_type') OR
|
||||
(table_name = 'train_student_checkin' AND column_name = 'checkin_way') OR
|
||||
(table_name = 'train_class_report' AND column_name = 'verify_result')
|
||||
)
|
||||
) LOOP
|
||||
IF r.column_default IS NOT NULL THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] 业务选择型字段 %.% 非法配置了默认值: %,必须取消!', r.table_name, r.column_name, r.column_default;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 9. 核心领域架构解耦列特征强断言
|
||||
-- ==========================================================================
|
||||
-- train_class_plan 必须无 c_id 与 t_id
|
||||
SELECT count(*) INTO v_cnt FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_class_plan' AND column_name IN ('c_id', 't_id');
|
||||
IF v_cnt > 0 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] train_class_plan 仍包含旧单课或单教师外键列 c_id/t_id';
|
||||
END IF;
|
||||
|
||||
-- train_student_checkin 必须包含 enrollment_id 与 lesson_id,且无旧冗余列
|
||||
SELECT count(*) INTO v_cnt FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_student_checkin'
|
||||
AND column_name IN ('enrollment_id', 'lesson_id');
|
||||
IF v_cnt <> 2 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] train_student_checkin 未包含核心列 enrollment_id 与 lesson_id';
|
||||
END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_student_checkin'
|
||||
AND column_name IN ('plan_id', 'c_id', 'student_id', 'lesson_start_time', 'lesson_end_time');
|
||||
IF v_cnt > 0 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] train_student_checkin 仍残留历史冗余列';
|
||||
END IF;
|
||||
|
||||
-- train_class_report 必须无 alloc_* 旧预分配列
|
||||
SELECT count(*) INTO v_cnt FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_class_report'
|
||||
AND column_name IN ('alloc_dorm_id', 'alloc_classroom_id', 'alloc_site_id');
|
||||
IF v_cnt > 0 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] train_class_report 仍残留旧预分配列';
|
||||
END IF;
|
||||
|
||||
-- train_lesson.t_id 必须为 NOT NULL
|
||||
SELECT count(*) INTO v_cnt FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'train_lesson'
|
||||
AND column_name = 't_id' AND is_nullable = 'NO';
|
||||
IF v_cnt <> 1 THEN
|
||||
RAISE EXCEPTION '>>> [Verify-FATAL] train_lesson.t_id 必须为 NOT NULL';
|
||||
END IF;
|
||||
|
||||
-- ==========================================================================
|
||||
-- 10. public.sys_* 系统基线表行数与零受损核验
|
||||
-- ==========================================================================
|
||||
SELECT count(*) INTO v_cnt FROM public.sys_user;
|
||||
IF v_cnt <> 1 THEN RAISE EXCEPTION '>>> [Verify-FATAL] public.sys_user 行数受损,当前为 %', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM public.sys_menu;
|
||||
IF v_cnt <> 106 THEN RAISE EXCEPTION '>>> [Verify-FATAL] public.sys_menu 行数受损,当前为 %', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM public.sys_dept;
|
||||
IF v_cnt <> 15 THEN RAISE EXCEPTION '>>> [Verify-FATAL] public.sys_dept 行数受损,当前为 %', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM public.sys_dict_type;
|
||||
IF v_cnt <> 44 THEN RAISE EXCEPTION '>>> [Verify-FATAL] public.sys_dict_type 行数受损,当前为 %', v_cnt; END IF;
|
||||
|
||||
SELECT count(*) INTO v_cnt FROM public.sys_dict_data;
|
||||
IF v_cnt <> 500 THEN RAISE EXCEPTION '>>> [Verify-FATAL] public.sys_dict_data 行数受损,当前为 %', v_cnt; END IF;
|
||||
|
||||
RAISE NOTICE '==============================================================================';
|
||||
RAISE NOTICE '>>> [Verify-PASS] RailwaySystem 数据库 V2 定义级验收全量通过!';
|
||||
RAISE NOTICE '>>> 验收内容: 21表 + 13 UNIQUE(定义) + 26 FK(定义+NO ACTION) + 9 CHECK(表达式) + 12 Identity + 关键复合索引 + 默认值严格断言 + sys_*零破坏';
|
||||
RAISE NOTICE '==============================================================================';
|
||||
END $$;
|
||||
@@ -0,0 +1,356 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 回滚基线: 000_phase05_exact_schema.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 精确定义 Phase 0.5 历史基线 14 张培训业务表的完整物理 DDL
|
||||
-- 依据来源: codebase-audit 13_live_database_snapshot.md 真实元数据基线
|
||||
-- 包含内容:
|
||||
-- 1. 14 张表物理字段定义、NOT NULL、DEFAULT
|
||||
-- 2. 14 个 PRIMARY KEY (8 个 varchar(32), 6 个 bigint GENERATED BY DEFAULT AS IDENTITY)
|
||||
-- 3. 7 组历史业务 UNIQUE 约束
|
||||
-- 4. 24 组历史业务 FOREIGN KEY 约束
|
||||
-- 5. 9 个 update_time 触发器 (绑定 func_train_update_modifytime)
|
||||
-- 说明: 本文件为 Phase 0.5 原始结构标准,作为回滚操作的唯一结构依据,杜绝 CTAS 推断结构
|
||||
-- ==============================================================================
|
||||
|
||||
-- 1. train_teacher 教师主档
|
||||
CREATE TABLE train_teacher (
|
||||
t_id VARCHAR(32) NOT NULL,
|
||||
t_name VARCHAR(30) NOT NULL,
|
||||
t_gender CHAR(2),
|
||||
t_birth DATE,
|
||||
t_department VARCHAR(50) NOT NULL,
|
||||
t_title VARCHAR(30),
|
||||
t_phone VARCHAR(20),
|
||||
t_email VARCHAR(50),
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_teacher PRIMARY KEY (t_id)
|
||||
);
|
||||
|
||||
-- 2. train_student 学员表
|
||||
CREATE TABLE train_student (
|
||||
s_id VARCHAR(32) NOT NULL,
|
||||
s_name VARCHAR(30) NOT NULL,
|
||||
s_gender CHAR(2),
|
||||
s_birth DATE,
|
||||
s_department VARCHAR(50) NOT NULL,
|
||||
s_major VARCHAR(50) NOT NULL,
|
||||
s_class VARCHAR(30),
|
||||
s_phone VARCHAR(20),
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_student PRIMARY KEY (s_id)
|
||||
);
|
||||
|
||||
-- 3. train_course 课程表
|
||||
CREATE TABLE train_course (
|
||||
c_id VARCHAR(32) NOT NULL,
|
||||
c_name VARCHAR(100) NOT NULL,
|
||||
c_type VARCHAR(30),
|
||||
c_duration INTEGER,
|
||||
teaching_objective TEXT,
|
||||
course_content TEXT,
|
||||
attach_doc TEXT,
|
||||
attach_video TEXT,
|
||||
attach_model_link TEXT,
|
||||
model_resource_ids TEXT,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_course PRIMARY KEY (c_id)
|
||||
);
|
||||
|
||||
-- 4. train_class_plan 开班计划表
|
||||
CREATE TABLE train_class_plan (
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
plan_name VARCHAR(100) NOT NULL,
|
||||
c_id VARCHAR(32) NOT NULL,
|
||||
start_time TIMESTAMP NOT NULL,
|
||||
end_time TIMESTAMP NOT NULL,
|
||||
train_address VARCHAR(100),
|
||||
t_id VARCHAR(32) NOT NULL,
|
||||
recruit_num INTEGER,
|
||||
apply_condition TEXT,
|
||||
plan_status SMALLINT DEFAULT 0,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_class_plan PRIMARY KEY (plan_id)
|
||||
);
|
||||
|
||||
-- 5. train_dormitory_info 宿舍信息表
|
||||
CREATE TABLE train_dormitory_info (
|
||||
dorm_id VARCHAR(32) NOT NULL,
|
||||
dorm_building VARCHAR(50),
|
||||
dorm_room VARCHAR(30) NOT NULL,
|
||||
bed_total INTEGER,
|
||||
bed_used INTEGER DEFAULT 0,
|
||||
lock_device_sn VARCHAR(64),
|
||||
dorm_status SMALLINT DEFAULT 1,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_dormitory_info PRIMARY KEY (dorm_id),
|
||||
CONSTRAINT uq_dorm_building_room UNIQUE (dorm_building, dorm_room)
|
||||
);
|
||||
|
||||
-- 6. train_dorm_auth 宿舍门禁表
|
||||
CREATE TABLE train_dorm_auth (
|
||||
auth_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
student_id VARCHAR(32) NOT NULL,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
dorm_id VARCHAR(32) NOT NULL,
|
||||
bed_no VARCHAR(20),
|
||||
face_sync_status SMALLINT DEFAULT 0,
|
||||
auth_start TIMESTAMP,
|
||||
auth_end TIMESTAMP,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_dorm_auth PRIMARY KEY (auth_id),
|
||||
CONSTRAINT uq_dormauth_stu_plan UNIQUE (student_id, plan_id)
|
||||
);
|
||||
|
||||
-- 7. train_classroom_info 教室信息表
|
||||
CREATE TABLE train_classroom_info (
|
||||
class_room_id VARCHAR(32) NOT NULL,
|
||||
room_name VARCHAR(60) NOT NULL,
|
||||
room_location VARCHAR(100),
|
||||
capacity INTEGER,
|
||||
room_status SMALLINT DEFAULT 1,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_classroom_info PRIMARY KEY (class_room_id)
|
||||
);
|
||||
|
||||
-- 8. train_training_site 实训场地表
|
||||
CREATE TABLE train_training_site (
|
||||
site_id VARCHAR(32) NOT NULL,
|
||||
site_name VARCHAR(80) NOT NULL,
|
||||
site_location VARCHAR(100),
|
||||
capacity INTEGER,
|
||||
site_device TEXT,
|
||||
site_status SMALLINT DEFAULT 1,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_training_site PRIMARY KEY (site_id)
|
||||
);
|
||||
|
||||
-- 9. train_class_report 报到记录表
|
||||
CREATE TABLE train_class_report (
|
||||
report_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
student_id VARCHAR(32) NOT NULL,
|
||||
report_way SMALLINT,
|
||||
report_device_sn VARCHAR(64),
|
||||
report_time TIMESTAMP,
|
||||
report_status SMALLINT DEFAULT 0,
|
||||
alloc_dorm_id VARCHAR(32),
|
||||
alloc_classroom_id VARCHAR(32),
|
||||
alloc_site_id VARCHAR(32),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT pk_train_class_report PRIMARY KEY (report_id),
|
||||
CONSTRAINT uq_classreport_plan_stu UNIQUE (plan_id, student_id)
|
||||
);
|
||||
|
||||
-- 10. train_electronic_board 电子班牌表
|
||||
CREATE TABLE train_electronic_board (
|
||||
board_id VARCHAR(32) NOT NULL,
|
||||
board_sn VARCHAR(64) NOT NULL,
|
||||
bind_classroom_id VARCHAR(32),
|
||||
bind_plan_id VARCHAR(32),
|
||||
board_ip VARCHAR(50),
|
||||
board_content VARCHAR(500),
|
||||
board_playmode SMALLINT DEFAULT 1,
|
||||
support_checkin_type VARCHAR(30),
|
||||
board_status SMALLINT DEFAULT 1,
|
||||
last_heartbeat TIMESTAMP,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_electronic_board PRIMARY KEY (board_id),
|
||||
CONSTRAINT uq_board_sn UNIQUE (board_sn)
|
||||
);
|
||||
|
||||
-- 11. train_student_checkin 考勤签到表
|
||||
CREATE TABLE train_student_checkin (
|
||||
checkin_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
c_id VARCHAR(32) NOT NULL,
|
||||
student_id VARCHAR(32) NOT NULL,
|
||||
board_id VARCHAR(32),
|
||||
checkin_way SMALLINT,
|
||||
checkin_time TIMESTAMP,
|
||||
checkin_status SMALLINT,
|
||||
lesson_start_time TIMESTAMP,
|
||||
lesson_end_time TIMESTAMP,
|
||||
CONSTRAINT pk_train_student_checkin PRIMARY KEY (checkin_id),
|
||||
CONSTRAINT uq_checkin_lesson_stu UNIQUE (plan_id, c_id, student_id, lesson_start_time)
|
||||
);
|
||||
|
||||
-- 12. train_score_info 成绩信息表 (历史旧表)
|
||||
CREATE TABLE train_score_info (
|
||||
score_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
plan_id VARCHAR(32),
|
||||
c_id VARCHAR(32),
|
||||
student_id VARCHAR(32),
|
||||
course_score NUMERIC(5,2),
|
||||
train_score NUMERIC(5,2),
|
||||
exam_score NUMERIC(5,2),
|
||||
source_type SMALLINT,
|
||||
source_system VARCHAR(50),
|
||||
import_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT pk_train_score_info PRIMARY KEY (score_id),
|
||||
CONSTRAINT uq_scoreinfo_plan_c_stu UNIQUE (plan_id, c_id, student_id)
|
||||
);
|
||||
|
||||
-- 13. train_study_duration 学习时长表
|
||||
CREATE TABLE train_study_duration (
|
||||
dur_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
student_id VARCHAR(32),
|
||||
plan_id VARCHAR(32),
|
||||
c_id VARCHAR(32),
|
||||
device_type VARCHAR(50),
|
||||
start_time TIMESTAMP,
|
||||
end_time TIMESTAMP,
|
||||
study_seconds INTEGER,
|
||||
sync_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT pk_train_study_duration PRIMARY KEY (dur_id)
|
||||
);
|
||||
|
||||
-- 14. train_student_portrait 学员画像表
|
||||
CREATE TABLE train_student_portrait (
|
||||
portrait_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
student_id VARCHAR(32) NOT NULL,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
total_study_hour NUMERIC(8,2),
|
||||
avg_score NUMERIC(5,2),
|
||||
checkin_rate NUMERIC(5,2),
|
||||
label_tag TEXT,
|
||||
portrait_content TEXT,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT pk_train_student_portrait PRIMARY KEY (portrait_id),
|
||||
CONSTRAINT uq_portrait_stu_plan UNIQUE (student_id, plan_id)
|
||||
);
|
||||
|
||||
|
||||
-- ==============================================================================
|
||||
-- 历史 24 个外键约束 (FOREIGN KEY CONSTRAINTS - 全部 ON DELETE NO ACTION)
|
||||
-- ==============================================================================
|
||||
|
||||
-- train_class_plan (2 FK)
|
||||
ALTER TABLE train_class_plan ADD CONSTRAINT fk_classplan_course
|
||||
FOREIGN KEY (c_id) REFERENCES train_course (c_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_class_plan ADD CONSTRAINT fk_classplan_teacher
|
||||
FOREIGN KEY (t_id) REFERENCES train_teacher (t_id) ON DELETE NO ACTION;
|
||||
|
||||
-- train_dorm_auth (3 FK)
|
||||
ALTER TABLE train_dorm_auth ADD CONSTRAINT fk_dormauth_stu
|
||||
FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_dorm_auth ADD CONSTRAINT fk_dormauth_plan
|
||||
FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_dorm_auth ADD CONSTRAINT fk_dormauth_dorm
|
||||
FOREIGN KEY (dorm_id) REFERENCES train_dormitory_info (dorm_id) ON DELETE NO ACTION;
|
||||
|
||||
-- train_class_report (5 FK)
|
||||
ALTER TABLE train_class_report ADD CONSTRAINT fk_classreport_plan
|
||||
FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_class_report ADD CONSTRAINT fk_classreport_stu
|
||||
FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_class_report ADD CONSTRAINT fk_classreport_dorm
|
||||
FOREIGN KEY (alloc_dorm_id) REFERENCES train_dormitory_info (dorm_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_class_report ADD CONSTRAINT fk_classreport_classroom
|
||||
FOREIGN KEY (alloc_classroom_id) REFERENCES train_classroom_info (class_room_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_class_report ADD CONSTRAINT fk_classreport_site
|
||||
FOREIGN KEY (alloc_site_id) REFERENCES train_training_site (site_id) ON DELETE NO ACTION;
|
||||
|
||||
-- train_electronic_board (2 FK)
|
||||
ALTER TABLE train_electronic_board ADD CONSTRAINT fk_board_classroom
|
||||
FOREIGN KEY (bind_classroom_id) REFERENCES train_classroom_info (class_room_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_electronic_board ADD CONSTRAINT fk_board_plan
|
||||
FOREIGN KEY (bind_plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION;
|
||||
|
||||
-- train_student_checkin (4 FK)
|
||||
ALTER TABLE train_student_checkin ADD CONSTRAINT fk_checkin_plan
|
||||
FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_student_checkin ADD CONSTRAINT fk_checkin_course
|
||||
FOREIGN KEY (c_id) REFERENCES train_course (c_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_student_checkin ADD CONSTRAINT fk_checkin_stu
|
||||
FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_student_checkin ADD CONSTRAINT fk_checkin_board
|
||||
FOREIGN KEY (board_id) REFERENCES train_electronic_board (board_id) ON DELETE NO ACTION;
|
||||
|
||||
-- train_score_info (3 FK)
|
||||
ALTER TABLE train_score_info ADD CONSTRAINT fk_scoreinfo_plan
|
||||
FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_score_info ADD CONSTRAINT fk_scoreinfo_course
|
||||
FOREIGN KEY (c_id) REFERENCES train_course (c_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_score_info ADD CONSTRAINT fk_scoreinfo_stu
|
||||
FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION;
|
||||
|
||||
-- train_study_duration (3 FK)
|
||||
ALTER TABLE train_study_duration ADD CONSTRAINT fk_studydur_stu
|
||||
FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_study_duration ADD CONSTRAINT fk_studydur_plan
|
||||
FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_study_duration ADD CONSTRAINT fk_studydur_course
|
||||
FOREIGN KEY (c_id) REFERENCES train_course (c_id) ON DELETE NO ACTION;
|
||||
|
||||
-- train_student_portrait (2 FK)
|
||||
ALTER TABLE train_student_portrait ADD CONSTRAINT fk_portrait_stu
|
||||
FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION;
|
||||
ALTER TABLE train_student_portrait ADD CONSTRAINT fk_portrait_plan
|
||||
FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION;
|
||||
|
||||
|
||||
-- ==============================================================================
|
||||
-- 历史 9 个 update_time 触发器
|
||||
-- ==============================================================================
|
||||
CREATE OR REPLACE TRIGGER trig_train_teacher_modify
|
||||
BEFORE UPDATE ON train_teacher
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_student_modify
|
||||
BEFORE UPDATE ON train_student
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_course_modify
|
||||
BEFORE UPDATE ON train_course
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_classroom_modify
|
||||
BEFORE UPDATE ON train_classroom_info
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_trainsite_modify
|
||||
BEFORE UPDATE ON train_training_site
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_board_modify
|
||||
BEFORE UPDATE ON train_electronic_board
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_dorminfo_modify
|
||||
BEFORE UPDATE ON train_dormitory_info
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_classplan_modify
|
||||
BEFORE UPDATE ON train_class_plan
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_dormauth_modify
|
||||
BEFORE UPDATE ON train_dorm_auth
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
@@ -0,0 +1,45 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 回滚脚本: 001_rollback_new_tables.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 安全下线并删除数据库 V2 新增的 8 张培训业务表
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 说明: 先显式解除外键约束,然后按依赖反序执行常规 DROP TABLE (无 CASCADE)
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 步骤 1: 显式解除 V2 新增表之间的外键约束
|
||||
ALTER TABLE train_student_score DROP CONSTRAINT IF EXISTS fk_score_enrollment;
|
||||
ALTER TABLE train_student_score DROP CONSTRAINT IF EXISTS fk_score_item;
|
||||
|
||||
ALTER TABLE train_score_item DROP CONSTRAINT IF EXISTS fk_scoreitem_plan;
|
||||
ALTER TABLE train_score_item DROP CONSTRAINT IF EXISTS fk_scoreitem_plancourse;
|
||||
|
||||
ALTER TABLE train_lesson DROP CONSTRAINT IF EXISTS fk_lesson_plancourse;
|
||||
ALTER TABLE train_lesson DROP CONSTRAINT IF EXISTS fk_lesson_teacher;
|
||||
ALTER TABLE train_lesson DROP CONSTRAINT IF EXISTS fk_lesson_classroom;
|
||||
ALTER TABLE train_lesson DROP CONSTRAINT IF EXISTS fk_lesson_site;
|
||||
|
||||
ALTER TABLE train_accommodation DROP CONSTRAINT IF EXISTS fk_accomm_enrollment;
|
||||
ALTER TABLE train_accommodation DROP CONSTRAINT IF EXISTS fk_accomm_dorm;
|
||||
|
||||
ALTER TABLE train_enrollment DROP CONSTRAINT IF EXISTS fk_enrollment_plan;
|
||||
ALTER TABLE train_enrollment DROP CONSTRAINT IF EXISTS fk_enrollment_student;
|
||||
|
||||
ALTER TABLE train_plan_course DROP CONSTRAINT IF EXISTS fk_plancourse_plan;
|
||||
ALTER TABLE train_plan_course DROP CONSTRAINT IF EXISTS fk_plancourse_course;
|
||||
|
||||
ALTER TABLE train_course_resource DROP CONSTRAINT IF EXISTS fk_cr_course;
|
||||
ALTER TABLE train_course_resource DROP CONSTRAINT IF EXISTS fk_cr_resource;
|
||||
|
||||
-- 步骤 2: 按依赖拓扑反序执行常规 DROP TABLE
|
||||
DROP TABLE IF EXISTS train_student_score;
|
||||
DROP TABLE IF EXISTS train_score_item;
|
||||
DROP TABLE IF EXISTS train_lesson;
|
||||
DROP TABLE IF EXISTS train_accommodation;
|
||||
DROP TABLE IF EXISTS train_enrollment;
|
||||
DROP TABLE IF EXISTS train_plan_course;
|
||||
DROP TABLE IF EXISTS train_course_resource;
|
||||
DROP TABLE IF EXISTS train_resource;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,337 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 回滚脚本: 002_rollback_modified_tables.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 严格基于 000_phase05_exact_schema.sql 结构与 bak_v1_* 数据还原 13 张表
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 规范要求:
|
||||
-- 1. 彻底摒弃 CTAS 丢失约束/默认值/触发器的不可靠方案
|
||||
-- 2. 精确采用 Phase 0.5 原始 DDL 重建表结构
|
||||
-- 3. 从 bak_v1_* 恢复真实数据并严格校验行数
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 步骤 1: 显式解除既有 V2 外键约束与旧外键
|
||||
ALTER TABLE train_dorm_auth DROP CONSTRAINT IF EXISTS uq_dorm_auth_accomm;
|
||||
ALTER TABLE train_dorm_auth DROP CONSTRAINT IF EXISTS fk_dormauth_accomm;
|
||||
ALTER TABLE train_class_report DROP CONSTRAINT IF EXISTS uq_class_report_enroll;
|
||||
ALTER TABLE train_class_report DROP CONSTRAINT IF EXISTS fk_report_enrollment;
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS uq_student_checkin_enroll_lesson;
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS fk_checkin_enrollment;
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS fk_checkin_lesson;
|
||||
ALTER TABLE train_student_checkin DROP CONSTRAINT IF EXISTS fk_checkin_board;
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS fk_study_enrollment;
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS fk_study_plancourse;
|
||||
ALTER TABLE train_study_duration DROP CONSTRAINT IF EXISTS fk_study_resource;
|
||||
ALTER TABLE train_student_portrait DROP CONSTRAINT IF EXISTS uq_student_portrait_enroll;
|
||||
ALTER TABLE train_student_portrait DROP CONSTRAINT IF EXISTS fk_portrait_enrollment;
|
||||
ALTER TABLE train_electronic_board DROP CONSTRAINT IF EXISTS fk_board_classroom;
|
||||
|
||||
-- 步骤 2: 常规 DROP 13 张表
|
||||
DROP TABLE IF EXISTS train_student_portrait;
|
||||
DROP TABLE IF EXISTS train_study_duration;
|
||||
DROP TABLE IF EXISTS train_student_checkin;
|
||||
DROP TABLE IF EXISTS train_class_report;
|
||||
DROP TABLE IF EXISTS train_dorm_auth;
|
||||
DROP TABLE IF EXISTS train_electronic_board;
|
||||
DROP TABLE IF EXISTS train_dormitory_info;
|
||||
DROP TABLE IF EXISTS train_class_plan;
|
||||
DROP TABLE IF EXISTS train_training_site;
|
||||
DROP TABLE IF EXISTS train_classroom_info;
|
||||
DROP TABLE IF EXISTS train_course;
|
||||
DROP TABLE IF EXISTS train_student;
|
||||
DROP TABLE IF EXISTS train_teacher;
|
||||
|
||||
-- 步骤 3: 严格依据 Phase 0.5 原始 DDL 重建 13 张表物理结构
|
||||
|
||||
CREATE TABLE train_teacher (
|
||||
t_id VARCHAR(32) NOT NULL,
|
||||
t_name VARCHAR(30) NOT NULL,
|
||||
t_gender CHAR(2),
|
||||
t_birth DATE,
|
||||
t_department VARCHAR(50) NOT NULL,
|
||||
t_title VARCHAR(30),
|
||||
t_phone VARCHAR(20),
|
||||
t_email VARCHAR(50),
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_teacher PRIMARY KEY (t_id)
|
||||
);
|
||||
|
||||
CREATE TABLE train_student (
|
||||
s_id VARCHAR(32) NOT NULL,
|
||||
s_name VARCHAR(30) NOT NULL,
|
||||
s_gender CHAR(2),
|
||||
s_birth DATE,
|
||||
s_department VARCHAR(50) NOT NULL,
|
||||
s_major VARCHAR(50) NOT NULL,
|
||||
s_class VARCHAR(30),
|
||||
s_phone VARCHAR(20),
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_student PRIMARY KEY (s_id)
|
||||
);
|
||||
|
||||
CREATE TABLE train_course (
|
||||
c_id VARCHAR(32) NOT NULL,
|
||||
c_name VARCHAR(100) NOT NULL,
|
||||
c_type VARCHAR(30),
|
||||
c_duration INTEGER,
|
||||
teaching_objective TEXT,
|
||||
course_content TEXT,
|
||||
attach_doc TEXT,
|
||||
attach_video TEXT,
|
||||
attach_model_link TEXT,
|
||||
model_resource_ids TEXT,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_course PRIMARY KEY (c_id)
|
||||
);
|
||||
|
||||
CREATE TABLE train_classroom_info (
|
||||
class_room_id VARCHAR(32) NOT NULL,
|
||||
room_name VARCHAR(60) NOT NULL,
|
||||
room_location VARCHAR(100),
|
||||
capacity INTEGER,
|
||||
room_status SMALLINT DEFAULT 1,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_classroom_info PRIMARY KEY (class_room_id)
|
||||
);
|
||||
|
||||
CREATE TABLE train_training_site (
|
||||
site_id VARCHAR(32) NOT NULL,
|
||||
site_name VARCHAR(80) NOT NULL,
|
||||
site_location VARCHAR(100),
|
||||
capacity INTEGER,
|
||||
site_device TEXT,
|
||||
site_status SMALLINT DEFAULT 1,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_training_site PRIMARY KEY (site_id)
|
||||
);
|
||||
|
||||
CREATE TABLE train_class_plan (
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
plan_name VARCHAR(100) NOT NULL,
|
||||
c_id VARCHAR(32) NOT NULL,
|
||||
start_time TIMESTAMP NOT NULL,
|
||||
end_time TIMESTAMP NOT NULL,
|
||||
train_address VARCHAR(100),
|
||||
t_id VARCHAR(32) NOT NULL,
|
||||
recruit_num INTEGER,
|
||||
apply_condition TEXT,
|
||||
plan_status SMALLINT DEFAULT 0,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_class_plan PRIMARY KEY (plan_id),
|
||||
CONSTRAINT fk_classplan_course FOREIGN KEY (c_id) REFERENCES train_course (c_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_classplan_teacher FOREIGN KEY (t_id) REFERENCES train_teacher (t_id) ON DELETE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE train_dormitory_info (
|
||||
dorm_id VARCHAR(32) NOT NULL,
|
||||
dorm_building VARCHAR(50),
|
||||
dorm_room VARCHAR(30) NOT NULL,
|
||||
bed_total INTEGER,
|
||||
bed_used INTEGER DEFAULT 0,
|
||||
lock_device_sn VARCHAR(64),
|
||||
dorm_status SMALLINT DEFAULT 1,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_dormitory_info PRIMARY KEY (dorm_id),
|
||||
CONSTRAINT uq_dorm_building_room UNIQUE (dorm_building, dorm_room)
|
||||
);
|
||||
|
||||
CREATE TABLE train_electronic_board (
|
||||
board_id VARCHAR(32) NOT NULL,
|
||||
board_sn VARCHAR(64) NOT NULL,
|
||||
bind_classroom_id VARCHAR(32),
|
||||
bind_plan_id VARCHAR(32),
|
||||
board_ip VARCHAR(50),
|
||||
board_content VARCHAR(500),
|
||||
board_playmode SMALLINT DEFAULT 1,
|
||||
support_checkin_type VARCHAR(30),
|
||||
board_status SMALLINT DEFAULT 1,
|
||||
last_heartbeat TIMESTAMP,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_electronic_board PRIMARY KEY (board_id),
|
||||
CONSTRAINT uq_board_sn UNIQUE (board_sn),
|
||||
CONSTRAINT fk_board_classroom FOREIGN KEY (bind_classroom_id) REFERENCES train_classroom_info (class_room_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_board_plan FOREIGN KEY (bind_plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE train_dorm_auth (
|
||||
auth_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
student_id VARCHAR(32) NOT NULL,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
dorm_id VARCHAR(32) NOT NULL,
|
||||
bed_no VARCHAR(20),
|
||||
face_sync_status SMALLINT DEFAULT 0,
|
||||
auth_start TIMESTAMP,
|
||||
auth_end TIMESTAMP,
|
||||
create_user VARCHAR(32) NOT NULL,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
remark VARCHAR(200),
|
||||
CONSTRAINT pk_train_dorm_auth PRIMARY KEY (auth_id),
|
||||
CONSTRAINT uq_dormauth_stu_plan UNIQUE (student_id, plan_id),
|
||||
CONSTRAINT fk_dormauth_stu FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_dormauth_plan FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_dormauth_dorm FOREIGN KEY (dorm_id) REFERENCES train_dormitory_info (dorm_id) ON DELETE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE train_class_report (
|
||||
report_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
student_id VARCHAR(32) NOT NULL,
|
||||
report_way SMALLINT,
|
||||
report_device_sn VARCHAR(64),
|
||||
report_time TIMESTAMP,
|
||||
report_status SMALLINT DEFAULT 0,
|
||||
alloc_dorm_id VARCHAR(32),
|
||||
alloc_classroom_id VARCHAR(32),
|
||||
alloc_site_id VARCHAR(32),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT pk_train_class_report PRIMARY KEY (report_id),
|
||||
CONSTRAINT uq_classreport_plan_stu UNIQUE (plan_id, student_id),
|
||||
CONSTRAINT fk_classreport_plan FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_classreport_stu FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_classreport_dorm FOREIGN KEY (alloc_dorm_id) REFERENCES train_dormitory_info (dorm_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_classreport_classroom FOREIGN KEY (alloc_classroom_id) REFERENCES train_classroom_info (class_room_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_classreport_site FOREIGN KEY (alloc_site_id) REFERENCES train_training_site (site_id) ON DELETE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE train_student_checkin (
|
||||
checkin_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
c_id VARCHAR(32) NOT NULL,
|
||||
student_id VARCHAR(32) NOT NULL,
|
||||
board_id VARCHAR(32),
|
||||
checkin_way SMALLINT,
|
||||
checkin_time TIMESTAMP,
|
||||
checkin_status SMALLINT,
|
||||
lesson_start_time TIMESTAMP,
|
||||
lesson_end_time TIMESTAMP,
|
||||
CONSTRAINT pk_train_student_checkin PRIMARY KEY (checkin_id),
|
||||
CONSTRAINT uq_checkin_lesson_stu UNIQUE (plan_id, c_id, student_id, lesson_start_time),
|
||||
CONSTRAINT fk_checkin_plan FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_checkin_course FOREIGN KEY (c_id) REFERENCES train_course (c_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_checkin_stu FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_checkin_board FOREIGN KEY (board_id) REFERENCES train_electronic_board (board_id) ON DELETE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE train_study_duration (
|
||||
dur_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
student_id VARCHAR(32),
|
||||
plan_id VARCHAR(32),
|
||||
c_id VARCHAR(32),
|
||||
device_type VARCHAR(50),
|
||||
start_time TIMESTAMP,
|
||||
end_time TIMESTAMP,
|
||||
study_seconds INTEGER,
|
||||
sync_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT pk_train_study_duration PRIMARY KEY (dur_id),
|
||||
CONSTRAINT fk_studydur_stu FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_studydur_plan FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_studydur_course FOREIGN KEY (c_id) REFERENCES train_course (c_id) ON DELETE NO ACTION
|
||||
);
|
||||
|
||||
CREATE TABLE train_student_portrait (
|
||||
portrait_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
student_id VARCHAR(32) NOT NULL,
|
||||
plan_id VARCHAR(32) NOT NULL,
|
||||
total_study_hour NUMERIC(8,2),
|
||||
avg_score NUMERIC(5,2),
|
||||
checkin_rate NUMERIC(5,2),
|
||||
label_tag TEXT,
|
||||
portrait_content TEXT,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT pk_train_student_portrait PRIMARY KEY (portrait_id),
|
||||
CONSTRAINT uq_portrait_stu_plan UNIQUE (student_id, plan_id),
|
||||
CONSTRAINT fk_portrait_stu FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_portrait_plan FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION
|
||||
);
|
||||
|
||||
-- 步骤 4: 重建 9 个历史触发器
|
||||
CREATE OR REPLACE TRIGGER trig_train_teacher_modify
|
||||
BEFORE UPDATE ON train_teacher
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_student_modify
|
||||
BEFORE UPDATE ON train_student
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_course_modify
|
||||
BEFORE UPDATE ON train_course
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_classroom_modify
|
||||
BEFORE UPDATE ON train_classroom_info
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_trainsite_modify
|
||||
BEFORE UPDATE ON train_training_site
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_board_modify
|
||||
BEFORE UPDATE ON train_electronic_board
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_dorminfo_modify
|
||||
BEFORE UPDATE ON train_dormitory_info
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_classplan_modify
|
||||
BEFORE UPDATE ON train_class_plan
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
CREATE OR REPLACE TRIGGER trig_train_dormauth_modify
|
||||
BEFORE UPDATE ON train_dorm_auth
|
||||
FOR EACH ROW EXECUTE FUNCTION func_train_update_modifytime();
|
||||
|
||||
-- 步骤 5: 从 bak_v1_* 物理快照还原数据
|
||||
INSERT INTO train_teacher SELECT * FROM bak_v1_train_teacher;
|
||||
INSERT INTO train_student SELECT * FROM bak_v1_train_student;
|
||||
INSERT INTO train_course SELECT * FROM bak_v1_train_course;
|
||||
INSERT INTO train_classroom_info SELECT * FROM bak_v1_train_classroom_info;
|
||||
INSERT INTO train_training_site SELECT * FROM bak_v1_train_training_site;
|
||||
INSERT INTO train_class_plan SELECT * FROM bak_v1_train_class_plan;
|
||||
INSERT INTO train_dormitory_info SELECT * FROM bak_v1_train_dormitory_info;
|
||||
INSERT INTO train_electronic_board SELECT * FROM bak_v1_train_electronic_board;
|
||||
INSERT INTO train_dorm_auth SELECT * FROM bak_v1_train_dorm_auth;
|
||||
INSERT INTO train_class_report SELECT * FROM bak_v1_train_class_report;
|
||||
INSERT INTO train_student_checkin SELECT * FROM bak_v1_train_student_checkin;
|
||||
INSERT INTO train_study_duration SELECT * FROM bak_v1_train_study_duration;
|
||||
INSERT INTO train_student_portrait SELECT * FROM bak_v1_train_student_portrait;
|
||||
|
||||
-- 步骤 6: 校验教师唯一数据还原结果
|
||||
DO $$
|
||||
DECLARE
|
||||
v_cnt INT;
|
||||
BEGIN
|
||||
SELECT count(*) INTO v_cnt FROM train_teacher;
|
||||
IF v_cnt <> 1 THEN
|
||||
RAISE EXCEPTION '>>> [Rollback-FATAL] 教师数据还原失败: 期望 1 行,实际为 % 行', v_cnt;
|
||||
END IF;
|
||||
RAISE NOTICE '>>> [Rollback-PASS] 13 张表结构与教师数据已严格依据 Phase 0.5 标准 DDL 恢复完毕。';
|
||||
END $$;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,37 @@
|
||||
-- ==============================================================================
|
||||
-- RailwaySystem 数据库 V2 回滚脚本: 003_restore_legacy_score.sql
|
||||
-- 适用数据库: KingbaseES V009R003C018 (schema: public)
|
||||
-- 执行目标: 严格基于 000_phase05_exact_schema.sql 结构恢复旧表 train_score_info
|
||||
-- 事务边界: 全程在显式事务 BEGIN ... COMMIT 中执行
|
||||
-- 说明: 彻底摒弃 CTAS 伪造结构,严格恢复原字段、默认值、外键与唯一约束
|
||||
-- ==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
DROP TABLE IF EXISTS train_score_info;
|
||||
|
||||
-- 步骤 1: 依据 Phase 0.5 原始标准物理 DDL 重建表结构
|
||||
CREATE TABLE train_score_info (
|
||||
score_id BIGINT GENERATED BY DEFAULT AS IDENTITY,
|
||||
plan_id VARCHAR(32),
|
||||
c_id VARCHAR(32),
|
||||
student_id VARCHAR(32),
|
||||
course_score NUMERIC(5,2),
|
||||
train_score NUMERIC(5,2),
|
||||
exam_score NUMERIC(5,2),
|
||||
source_type SMALLINT,
|
||||
source_system VARCHAR(50),
|
||||
import_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT pk_train_score_info PRIMARY KEY (score_id),
|
||||
CONSTRAINT uq_scoreinfo_plan_c_stu UNIQUE (plan_id, c_id, student_id),
|
||||
CONSTRAINT fk_scoreinfo_plan FOREIGN KEY (plan_id) REFERENCES train_class_plan (plan_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_scoreinfo_course FOREIGN KEY (c_id) REFERENCES train_course (c_id) ON DELETE NO ACTION,
|
||||
CONSTRAINT fk_scoreinfo_stu FOREIGN KEY (student_id) REFERENCES train_student (s_id) ON DELETE NO ACTION
|
||||
);
|
||||
|
||||
COMMENT ON TABLE train_score_info IS 'Phase 0.5 历史成绩表 (应急回滚精准恢复)';
|
||||
|
||||
-- 步骤 2: 从 bak_v1_train_score_info 快照表恢复数据
|
||||
INSERT INTO train_score_info SELECT * FROM bak_v1_train_score_info;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,91 @@
|
||||
# RailwaySystem 数据库 V2 应急回滚指南 (Round 1 修订版)
|
||||
|
||||
> **技术基线**:KingbaseES V009R003C018 (`schema=public`)
|
||||
> **回滚目标**:安全撤销 V2 数据库结构变更,将数据模型与物理约束 100% 精确还原至 Phase 0.5 历史基线。
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心架构升级:解耦结构与数据
|
||||
|
||||
在 Round 1 审查中,彻底废除了使用 `CREATE TABLE ... AS SELECT * FROM bak_v1_*`(CTAS)推断历史结构的危险方案:
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Phase 0.5 精确物理结构 (表定义/列类型/PK/FK/UNIQUE/触发器) │
|
||||
│ 来源: rollback/000_phase05_exact_schema.sql │
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
│ (先精确重建物理表结构)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Phase 0.5 真实数据 (唯一教师行 + 各表数据) │
|
||||
│ 来源: 001 备份生成的 bak_v1_* 快照表 │
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
│ (再通过 INSERT INTO 导入数据)
|
||||
▼
|
||||
【Phase 0.5 绝对还原】
|
||||
```
|
||||
|
||||
- **`000_phase05_exact_schema.sql`**:作为唯一的物理结构基准,确保回滚后恢复所有 24 个旧外键、7 组旧业务唯一约束、NOT NULL、DEFAULT、IDENTITY 序列以及 9 个 `update_time` 触发器;
|
||||
- **`bak_v1_*` 快照表**:仅承担纯粹的“数据暂存”职责。
|
||||
|
||||
---
|
||||
|
||||
## 2. 回滚脚本执行顺序
|
||||
|
||||
若在测试迁移或灰度验证阶段发现重大异常,需按照以下严格顺序执行回滚流水线:
|
||||
|
||||
```text
|
||||
步骤 1: 执行 001_rollback_new_tables.sql
|
||||
-> 显式解除外键依赖,安全下线并 DROP 掉 V2 新增的 8 张业务表
|
||||
|
||||
步骤 2: 执行 002_rollback_modified_tables.sql
|
||||
-> 基于 000_phase05_exact_schema.sql 的精确结构重建 13 张表,并从 bak_v1_* 物理快照还原数据,严格断言教师数据为 1 行
|
||||
|
||||
步骤 3: 执行 003_restore_legacy_score.sql
|
||||
-> 基于 000_phase05_exact_schema.sql 的精确结构重建 train_score_info 及 3 个历史外键与唯一约束
|
||||
|
||||
步骤 4: 人工核验 Phase 0.5 状态
|
||||
-> 执行 000_preflight.sql 校验基线恢复,确认 14 张表、触发器与约束 100% 吻合
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 强前置依赖与数据来源声明
|
||||
|
||||
> [!CAUTION]
|
||||
> **回滚操作必须满足以下前置条件:**
|
||||
|
||||
1. **依赖 001 备份快照**:
|
||||
- 步骤 2 与步骤 3 的数据还原依赖迁移前由 `001_backup_current_schema.sql` 生成的 `bak_v1_*` 快照表。
|
||||
- 若 `bak_v1_*` 表意外被删,**严禁盲目执行数据还原语句**。
|
||||
2. **唯一教师数据的物理还原**:
|
||||
- 教师主档 `train_teacher` 的数据通过 `INSERT INTO train_teacher SELECT * FROM bak_v1_train_teacher;` 完整回灌。回滚脚本会严格校验恢复后的教师行数必须等于 1。
|
||||
3. **事务安全性**:
|
||||
- 每一个回滚 SQL 文件内部均由显式 `BEGIN ... COMMIT` 包裹,出现任何错误自动整单回滚,不产生中间半回滚状态。
|
||||
|
||||
---
|
||||
|
||||
## 4. 必须人工介入确认的场景
|
||||
|
||||
### 场景 A:V2 阶段已产生新业务数据
|
||||
如果系统在执行 V2 DDL 之后,业务端或测试端已经在 `train_enrollment`、`train_lesson`、`train_student_score` 等表中写入了新数据:
|
||||
- **直接执行回滚将导致这些新业务数据彻底物理清除!**
|
||||
- **必须人工介入**:先通过数据导出工具将 V2 新增业务数据导出为 CSV/JSON 归档,确认业务无异议后方可执行回滚。
|
||||
|
||||
### 场景 B:bak_v1_* 备份表意外缺失
|
||||
若 `bak_v1_*` 表不存在:
|
||||
- 必须暂停脚本执行;
|
||||
- 从数据库物理备份集(如 `sys_backup` / 逻辑转储快照)中恢复 14 张旧表数据。
|
||||
|
||||
---
|
||||
|
||||
## 5. 回滚后验收确认 Checklist
|
||||
|
||||
回滚完成后,在数据库客户端中直接运行 `000_preflight.sql` 即可自动化完成全量反向验收:
|
||||
|
||||
- [ ] 1. 运行 `000_preflight.sql` 成功输出 `[Preflight-PASS]`;
|
||||
- [ ] 2. 确认已无任何 V2 独有表(如 `train_plan_course` 等);
|
||||
- [ ] 3. 确认旧表 `train_score_info` 已恢复,且具备旧外键;
|
||||
- [ ] 4. 确认 `train_teacher` 精确包含 1 条数据,且旧字段 `t_department` 存在且非空;
|
||||
- [ ] 5. 确认 9 个旧触发器全部重新挂载生效;
|
||||
- [ ] 6. 确认系统表 `sys_*` 完好无损。
|
||||
Reference in new issue
Block a user