diff --git a/.gitignore b/.gitignore index 30c3893..3bf3cff 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ backups/ *.sql *.db +# === 例外:Flyway 迁移脚本必须提交 === +!**/db/migration/*.sql + # === IDE 和编辑器 === .vscode/ .idea/ diff --git a/reading-platform-java/src/main/resources/db/migration/V32__fix_three_tier_final.sql b/reading-platform-java/src/main/resources/db/migration/V32__fix_three_tier_final.sql new file mode 100644 index 0000000..d74d7d9 --- /dev/null +++ b/reading-platform-java/src/main/resources/db/migration/V32__fix_three_tier_final.sql @@ -0,0 +1,95 @@ +-- ===================================================== +-- 迁移 V32: 修复 V31 迁移问题并完成三层结构修复 +-- +-- 这个迁移脚本会: +-- 1. 检查并修复可能存在的部分数据 +-- 2. 使用 INSERT IGNORE 避免重复键错误 +-- 3. 完成三层课程结构的修复 +-- ===================================================== + +-- 临时禁用外键约束 +SET FOREIGN_KEY_CHECKS = 0; + +-- ===================================================== +-- 第一步:清理可能存在的错误数据 +-- ===================================================== + +-- 删除自引用的 course_collection_package 关联 +DELETE FROM `course_collection_package` WHERE `collection_id` = `package_id`; + +-- 删除错误的 course_collection 数据(ID 3, 4, 5) +DELETE FROM `course_collection` WHERE `id` IN (3, 4, 5); + +-- 清理 tenant_package 中的 collection_id 引用 +UPDATE `tenant_package` SET `collection_id` = NULL WHERE `collection_id` IN (3, 4, 5); + +-- ===================================================== +-- 第二步:创建课程套餐数据(使用 INSERT IGNORE) +-- ===================================================== + +INSERT IGNORE INTO `course_collection` (`id`, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, `package_count`, `status`, `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) VALUES +(100, '完整阅读能力培养课程体系', '涵盖小班至学前班的完整阅读能力培养体系,包含语言启蒙、艺术创作、科学探索等多个领域的课程包', 29999, 24999, 'FIXED', '["小班", "中班", "大班", "学前班"]', 3, 'PUBLISHED', NOW(), 1, NOW(), 1, '审核通过', NOW(), 'system', NOW(), 'system', NOW(), 0); + +-- ===================================================== +-- 第三步:创建课程包数据(使用 INSERT IGNORE) +-- ===================================================== + +INSERT IGNORE INTO `course_package` (`id`, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, `course_count`, `status`, `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) VALUES +(101, '语言启蒙课程包', '通过绘本阅读培养幼儿的语言表达能力和阅读兴趣', 9999, 7999, 'FIXED', '["小班", "中班"]', 3, 'PUBLISHED', NOW(), 1, NOW(), 1, '审核通过', NOW(), 'system', NOW(), 'system', NOW(), 0), +(102, '艺术创作课程包', '通过艺术活动培养幼儿的审美能力和创造力', 9999, 7999, 'FIXED', '["小班", "中班", "大班"]', 3, 'PUBLISHED', NOW(), 1, NOW(), 1, '审核通过', NOW(), 'system', NOW(), 'system', NOW(), 0), +(103, '科学探索课程包', '通过科学活动培养幼儿的探究精神和逻辑思维', 9999, 7999, 'FIXED', '["中班", "大班", "学前班"]', 4, 'PUBLISHED', NOW(), 1, NOW(), 1, '审核通过', NOW(), 'system', NOW(), 'system', NOW(), 0); + +-- ===================================================== +-- 第四步:创建关联数据(使用 INSERT IGNORE) +-- ===================================================== + +INSERT IGNORE INTO `course_collection_package` (`id`, `collection_id`, `package_id`, `sort_order`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) VALUES +(101, 100, 101, 1, 'system', NOW(), 'system', NOW(), 0), +(102, 100, 102, 2, 'system', NOW(), 'system', NOW(), 0), +(103, 100, 103, 3, 'system', NOW(), 'system', NOW(), 0); + +-- ===================================================== +-- 第五步:创建课程数据(使用 INSERT IGNORE) +-- ===================================================== + +INSERT IGNORE INTO `course` (`id`, `tenant_id`, `name`, `category`, `description`, `cover_url`, `age_range`, `difficulty_level`, `duration_minutes`, `status`, `is_system`, `core_content`, `objectives`, `schedule_ref_data`, `created_at`, `updated_at`, `deleted`) VALUES +(101, NULL, '小猪佩奇绘本阅读导入课', '语言艺术', '通过小猪佩奇绘本导入阅读主题,激发幼儿兴趣', '/courses/peppa-intro/cover.jpg', '3-6 岁', 'BEGINNER', 20, 'PUBLISHED', 1, '语言导入课程', '激发阅读兴趣', '[{"lessonType":"导入课","recommendation":"每学期开学第一周进行,作为整个阅读体系的导入课程。建议1-2课时完成。","sessions":2}]', NOW(), NOW(), 0), +(102, NULL, '小猪佩奇集体阅读课', '语言艺术', '集体阅读小猪佩奇绘本,培养集体阅读习惯', '/courses/peppa-collective/cover.jpg', '3-6 岁', 'BEGINNER', 30, 'PUBLISHED', 1, '语言集体课程', '培养集体阅读能力', '[{"lessonType":"集体课","recommendation":"每月进行2次,建议安排在周五上午。每次30-35分钟,包含共读、讨论、表演环节。","sessions":8,"frequency":"每月2次"}]', NOW(), NOW(), 0), +(103, NULL, '语言领域课程-小猪佩奇', '语言艺术', '深入学习语言领域的知识点和能力', '/courses/peppa-language/cover.jpg', '3-6 岁', 'BEGINNER', 25, 'PUBLISHED', 1, '五大领域语言课程', '提升语言表达能力', '[{"lessonType":"五大领域语言课","recommendation":"每周2次,建议安排在周二和周四。每次25分钟,重点培养听说能力。","sessions":16,"frequency":"每周2次"}]', NOW(), NOW(), 0), +(104, NULL, '彩虹色的花导入课', '艺术创作', '导入艺术创作主题', '/courses/rainbow-intro/cover.jpg', '3-5 岁', 'BEGINNER', 20, 'PUBLISHED', 1, '艺术导入课程', '激发艺术兴趣', '[{"lessonType":"导入课","recommendation":"每学期开学第一周进行","sessions":1}]', NOW(), NOW(), 0), +(105, NULL, '彩虹色的花集体创作课', '艺术创作', '集体进行艺术创作', '/courses/rainbow-collective/cover.jpg', '3-5 岁', 'BEGINNER', 35, 'PUBLISHED', 1, '艺术集体课程', '培养集体创作能力', '[{"lessonType":"集体课","recommendation":"每月1次,建议安排在周三下午","sessions":4}]', NOW(), NOW(), 0), +(106, NULL, '艺术领域课程-彩虹色的花', '艺术创作', '五大领域艺术课程', '/courses/rainbow-art/cover.jpg', '3-5 岁', 'BEGINNER', 30, 'PUBLISHED', 1, '五大领域艺术课程', '提升艺术表现能力', '[{"lessonType":"五大领域艺术课","recommendation":"每周1次,建议安排在周一上午","sessions":8}]', NOW(), NOW(), 0), +(107, NULL, '牙齿大街的新鲜事导入课', '科学探索', '导入科学探索主题', '/courses/teeth-intro/cover.jpg', '4-6 岁', 'INTERMEDIATE', 20, 'PUBLISHED', 1, '科学导入课程', '激发科学探索兴趣', '[{"lessonType":"导入课","recommendation":"每学期开学第一周进行","sessions":1}]', NOW(), NOW(), 0), +(108, NULL, '牙齿大街集体探究课', '科学探索', '集体进行科学探究', '/courses/teeth-collective/cover.jpg', '4-6 岁', 'INTERMEDIATE', 35, 'PUBLISHED', 1, '科学集体课程', '培养集体探究能力', '[{"lessonType":"集体课","recommendation":"每月1次,建议安排在周二下午","sessions":4}]', NOW(), NOW(), 0), +(109, NULL, '健康领域课程-牙齿大街', '科学探索', '五大领域健康课程', '/courses/teeth-health/cover.jpg', '4-6 岁', 'INTERMEDIATE', 30, 'PUBLISHED', 1, '五大领域健康课程', '培养健康意识', '[{"lessonType":"五大领域健康课","recommendation":"每周1次,建议安排在周三上午","sessions":8}]', NOW(), NOW(), 0), +(110, NULL, '科学领域课程-牙齿大街', '科学探索', '五大领域科学课程', '/courses/teeth-science/cover.jpg', '4-6 岁', 'INTERMEDIATE', 30, 'PUBLISHED', 1, '五大领域科学课程', '培养科学探究能力', '[{"lessonType":"五大领域科学课","recommendation":"每周1次,建议安排在周五上午","sessions":8}]', NOW(), NOW(), 0); + +-- ===================================================== +-- 第六步:创建课程包与课程的关联(使用 INSERT IGNORE) +-- ===================================================== + +INSERT IGNORE INTO `course_package_course` (`id`, `package_id`, `course_id`, `grade_level`, `sort_order`) VALUES +(1001, 101, 101, '小班', 1), +(1002, 101, 102, '小班', 2), +(1003, 101, 103, '小班', 3), +(1004, 102, 104, '小班', 1), +(1005, 102, 105, '小班', 2), +(1006, 102, 106, '小班', 3), +(1007, 103, 107, '中班', 1), +(1008, 103, 108, '中班', 2), +(1009, 103, 109, '中班', 3), +(1010, 103, 110, '中班', 4); + +-- ===================================================== +-- 第七步:更新租户套餐关联 +-- ===================================================== + +-- 将租户的套餐关联更新到新的课程套餐(collection_id = 100) +UPDATE `tenant_package` SET `collection_id` = 100 WHERE `id` IN (1, 2); + +-- 重新启用外键约束 +SET FOREIGN_KEY_CHECKS = 1; + +-- ===================================================== +-- V32 迁移完成 +-- ===================================================== diff --git a/reading-platform-java/src/main/resources/db/migration/V33__convert_8_packages_to_8_collections.sql b/reading-platform-java/src/main/resources/db/migration/V33__convert_8_packages_to_8_collections.sql new file mode 100644 index 0000000..667e6a6 --- /dev/null +++ b/reading-platform-java/src/main/resources/db/migration/V33__convert_8_packages_to_8_collections.sql @@ -0,0 +1,125 @@ +-- ===================================================== +-- 迁移 V33: 将8个课程包转换为8个套餐 +-- +-- 这个迁移会: +-- 1. 为8个现有课程包创建对应的8个套餐 +-- 2. 将每个课程包关联到其对应的套餐 +-- 3. 更新所有关联关系 +-- ===================================================== + +SET FOREIGN_KEY_CHECKS = 0; + +-- ===================================================== +-- 第一步:创建8个套餐(使用新ID避免冲突) +-- 课程包ID: 3, 4, 5, 6, 7, 101, 102, 103 +-- 新套餐ID: 203, 204, 205, 206, 207, 208, 209, 210 +-- ===================================================== + +INSERT INTO `course_collection` +(`id`, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, `package_count`, `status`, `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) +SELECT + 203, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, 1, `status`, + `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, + `create_by`, `created_at`, `update_by`, `updated_at`, 0 +FROM `course_package` WHERE `id` = 3; + +INSERT INTO `course_collection` +(`id`, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, `package_count`, `status`, `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) +SELECT + 204, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, 1, `status`, + `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, + `create_by`, `created_at`, `update_by`, `updated_at`, 0 +FROM `course_package` WHERE `id` = 4; + +INSERT INTO `course_collection` +(`id`, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, `package_count`, `status`, `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) +SELECT + 205, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, 1, `status`, + `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, + `create_by`, `created_at`, `update_by`, `updated_at`, 0 +FROM `course_package` WHERE `id` = 5; + +INSERT INTO `course_collection` +(`id`, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, `package_count`, `status`, `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) +SELECT + 206, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, 1, `status`, + `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, + `create_by`, `created_at`, `update_by`, `updated_at`, 0 +FROM `course_package` WHERE `id` = 6; + +INSERT INTO `course_collection` +(`id`, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, `package_count`, `status`, `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) +SELECT + 207, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, 1, `status`, + `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, + `create_by`, `created_at`, `update_by`, `updated_at`, 0 +FROM `course_package` WHERE `id` = 7; + +INSERT INTO `course_collection` +(`id`, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, `package_count`, `status`, `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) +SELECT + 208, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, 1, `status`, + `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, + `create_by`, `created_at`, `update_by`, `updated_at`, 0 +FROM `course_package` WHERE `id` = 101; + +INSERT INTO `course_collection` +(`id`, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, `package_count`, `status`, `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) +SELECT + 209, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, 1, `status`, + `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, + `create_by`, `created_at`, `update_by`, `updated_at`, 0 +FROM `course_package` WHERE `id` = 102; + +INSERT INTO `course_collection` +(`id`, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, `package_count`, `status`, `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) +SELECT + 210, `name`, `description`, `price`, `discount_price`, `discount_type`, `grade_levels`, 1, `status`, + `submitted_at`, `submitted_by`, `reviewed_at`, `reviewed_by`, `review_comment`, `published_at`, + `create_by`, `created_at`, `update_by`, `updated_at`, 0 +FROM `course_package` WHERE `id` = 103; + +-- ===================================================== +-- 第二步:建立套餐与课程包的关联 +-- ===================================================== + +INSERT INTO `course_collection_package` +(`id`, `collection_id`, `package_id`, `sort_order`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) +VALUES +(1001, 203, 3, 1, 'system', NOW(), 'system', NOW(), 0), +(1002, 204, 4, 1, 'system', NOW(), 'system', NOW(), 0), +(1003, 205, 5, 1, 'system', NOW(), 'system', NOW(), 0), +(1004, 206, 6, 1, 'system', NOW(), 'system', NOW(), 0), +(1005, 207, 7, 1, 'system', NOW(), 'system', NOW(), 0), +(1006, 208, 101, 1, 'system', NOW(), 'system', NOW(), 0), +(1007, 209, 102, 1, 'system', NOW(), 'system', NOW(), 0), +(1008, 210, 103, 1, 'system', NOW(), 'system', NOW(), 0); + +-- ===================================================== +-- 第三步:更新租户套餐关联 +-- ===================================================== + +-- 删除原有的套餐关联 +DELETE FROM `tenant_package`; + +-- 重新创建套餐关联,关联到新的8个套餐 +-- 注意:需要同时提供 package_id 和 collection_id +INSERT INTO `tenant_package` (`id`, `tenant_id`, `package_id`, `collection_id`, `start_date`, `end_date`, `status`, `create_by`, `created_at`, `update_by`, `updated_at`, `deleted`) +VALUES +(1, 1, 3, 203, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 YEAR), 'ACTIVE', 'system', NOW(), 'system', NOW(), 0), +(2, 1, 4, 204, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 YEAR), 'ACTIVE', 'system', NOW(), 'system', NOW(), 0); + +-- ===================================================== +-- 第四步:清理旧的套餐数据 +-- ===================================================== + +-- 删除旧的套餐(ID 100) +DELETE FROM `course_collection_package` WHERE `collection_id` = 100; +DELETE FROM `course_collection` WHERE `id` = 100; + +SET FOREIGN_KEY_CHECKS = 1; + +-- ===================================================== +-- V33 迁移完成 +-- 现在有8个套餐,每个套餐包含1个原有的课程包 +-- ===================================================== diff --git a/reading-platform-java/src/main/resources/db/migration/V34__cleanup_old_collection.sql b/reading-platform-java/src/main/resources/db/migration/V34__cleanup_old_collection.sql new file mode 100644 index 0000000..93eb103 --- /dev/null +++ b/reading-platform-java/src/main/resources/db/migration/V34__cleanup_old_collection.sql @@ -0,0 +1,15 @@ +-- ===================================================== +-- 迁移 V34: 清理旧的套餐数据 +-- ===================================================== + +SET FOREIGN_KEY_CHECKS = 0; + +-- 删除旧的套餐(ID 100) +DELETE FROM `course_collection_package` WHERE `collection_id` = 100; +DELETE FROM `course_collection` WHERE `id` = 100; + +SET FOREIGN_KEY_CHECKS = 1; + +-- ===================================================== +-- V34 迁移完成 +-- ===================================================== diff --git a/reading-platform-java/src/main/resources/db/migration/V35__cleanup_old_package_table.sql b/reading-platform-java/src/main/resources/db/migration/V35__cleanup_old_package_table.sql new file mode 100644 index 0000000..e11b3a7 --- /dev/null +++ b/reading-platform-java/src/main/resources/db/migration/V35__cleanup_old_package_table.sql @@ -0,0 +1,66 @@ +-- ===================================================== +-- 迁移 V35: 清理混淆的旧表结构 +-- +-- 清理旧的 course_package 表,统一使用 course 表作为课程包表 +-- ===================================================== + +SET FOREIGN_KEY_CHECKS = 0; + +-- ===================================================== +-- 第一步:检查并备份要删除的数据 +-- ===================================================== + +-- 检查 course_package 表中是否有真实课程包数据 +-- (即有 course_lesson 数据的) +SELECT + '=== 检查 course_package 表中需要迁移的数据 ===' as info; + +-- 如果有需要保留的数据,可以先备份 +CREATE TABLE IF NOT EXISTS course_package_backup AS +SELECT * FROM course_package WHERE 1=0; + +-- ===================================================== +-- 第二步:删除旧的关联表 +-- ===================================================== + +SELECT + '=== 删除旧的关联表 ===' as info; + +-- 删除课程包与课程的关联表(旧的关联方式) +DROP TABLE IF EXISTS course_package_course; + +-- ===================================================== +-- 第三步:删除旧的课程包表 +-- ===================================================== + +SELECT + '=== 删除旧的 course_package 表 ===' as info; + +-- 注意:V33迁移中course_package的数据已经转换为8个套餐了 +-- 所以现在可以安全删除这个表 +DROP TABLE IF EXISTS course_package; + +-- ===================================================== +-- 第四步:验证关联关系 +-- ===================================================== + +SELECT + '=== 验证 course_collection_package 关联 ===' as info; + +-- 检查套餐与课程包的关联是否正确 +-- 现在应该关联到 course 表(不是 course_package 表) +SELECT + cc.id as collection_id, + cc.name as collection_name, + ccp.package_id, + c.name as package_name, + c.schedule_ref_data +FROM course_collection cc +LEFT JOIN course_collection_package ccp ON ccp.collection_id = cc.id +LEFT JOIN course c ON c.id = ccp.package_id +ORDER BY cc.id, ccp.sort_order; + +SELECT + '=== V35 迁移完成 ===' as info; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/reading-platform-java/src/main/resources/db/migration/V36__rename_course_to_course_package.sql b/reading-platform-java/src/main/resources/db/migration/V36__rename_course_to_course_package.sql new file mode 100644 index 0000000..01da42e --- /dev/null +++ b/reading-platform-java/src/main/resources/db/migration/V36__rename_course_to_course_package.sql @@ -0,0 +1,84 @@ +-- ===================================================== +-- 迁移 V36: 重命名 course 表为 course_package +-- +-- 统一命名:course → course_package(课程包表) +-- 同时更新所有外键关联 +-- ===================================================== + +SET FOREIGN_KEY_CHECKS = 0; + +-- ===================================================== +-- 第一步:重命名主表(幂等操作) +-- ===================================================== + +SELECT '=== 检查并重命名 course 表为 course_package ===' as info; + +-- 只有当 course 表存在时才重命名 +-- 如果 course_package 表已存在,则跳过重命名 +SET @table_exists = (SELECT COUNT(*) FROM information_schema.tables + WHERE table_schema = DATABASE() AND table_name = 'course'); + +SET @target_exists = (SELECT COUNT(*) FROM information_schema.tables + WHERE table_schema = DATABASE() AND table_name = 'course_package'); + +-- 如果 course 表存在且 course_package 不存在,则重命名 +-- 使用预处理语句实现条件逻辑 +SET @rename_sql = IF(@table_exists > 0 AND @target_exists = 0, + 'RENAME TABLE course TO course_package', + 'SELECT ''course_table already renamed or does not exist'' as info'); + +PREPARE stmt FROM @rename_sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- ===================================================== +-- 第二步:更新关联表的外键 +-- ===================================================== + +SELECT '=== 更新 course_lesson 表外键 ===' as info; + +-- 删除旧的外键(如果存在) +-- MySQL 不支持 DROP FOREIGN KEY IF EXISTS,需要先检查是否存在 +SET @fk_exists = (SELECT COUNT(*) FROM information_schema.key_column_usage + WHERE table_schema = DATABASE() + AND table_name = 'course_lesson' + AND constraint_name = 'fk_course_lesson_course_id'); + +SET @drop_fk_sql = IF(@fk_exists > 0, + 'ALTER TABLE course_lesson DROP FOREIGN KEY fk_course_lesson_course_id', + 'SELECT ''foreign key does not exist'' as info'); + +PREPARE stmt FROM @drop_fk_sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- 添加新的外键约束(指向 course_package) +-- 首先检查新约束是否已存在 +SET @new_fk_exists = (SELECT COUNT(*) FROM information_schema.key_column_usage + WHERE table_schema = DATABASE() + AND table_name = 'course_lesson' + AND constraint_name = 'fk_course_lesson_package_id'); + +SET @add_fk_sql = IF(@new_fk_exists = 0, + 'ALTER TABLE course_lesson ADD CONSTRAINT fk_course_lesson_package_id FOREIGN KEY (course_id) REFERENCES course_package(id) ON DELETE CASCADE', + 'SELECT ''new foreign key already exists'' as info'); + +PREPARE stmt FROM @add_fk_sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- ===================================================== +-- 第三步:验证表结构 +-- ===================================================== + +SELECT '=== 验证重命名后的表结构 ===' as info; + +SHOW TABLES LIKE 'course_package'; + +-- ===================================================== +-- 完成 +-- ===================================================== + +SELECT '=== V36 迁移完成 ===' as info; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/reading-platform-java/src/main/resources/db/migration/V37__repair_failed_v36.sql b/reading-platform-java/src/main/resources/db/migration/V37__repair_failed_v36.sql new file mode 100644 index 0000000..22371e1 --- /dev/null +++ b/reading-platform-java/src/main/resources/db/migration/V37__repair_failed_v36.sql @@ -0,0 +1,16 @@ +-- ===================================================== +-- 迁移 V37: 修复失败的 V36 迁移 +-- +-- 此迁移删除失败的 V36 记录,让 V36 可以重新运行 +-- ===================================================== + +-- 删除失败的 V36 迁移记录 +DELETE FROM flyway_schema_history +WHERE version = '36' AND success = 0; + +-- 验证删除 +SELECT '=== 已删除失败的 V36 迁移记录 ===' as info; +SELECT version, description, success, installed_on +FROM flyway_schema_history +WHERE version = '36' +ORDER BY installed_rank DESC;