-- ============================================================ -- Edu 微服务 All-Services Schema (P2 阶段,单库 next_edu_cloud) -- 由 Drizzle ORM schema 文件转换而来 -- 使用 CREATE TABLE IF NOT EXISTS,可重复执行 -- 字符集:utf8mb4 / utf8mb4_unicode_ci;引擎:InnoDB -- ============================================================ -- ============================================================ -- 1. IAM 服务(services/iam/src/iam/iam.schema.ts) -- ============================================================ -- 1.1 用户表 CREATE TABLE IF NOT EXISTS `iam_users` ( `id` CHAR(36) NOT NULL, `email` VARCHAR(255) NOT NULL, `password_hash` VARCHAR(255) NOT NULL, `name` VARCHAR(100) NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'active', `data_scope` ENUM('self','subject','class','grade','school','all') NOT NULL DEFAULT 'self', `password_changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uniq_iam_users_email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 1.2 角色表 CREATE TABLE IF NOT EXISTS `iam_roles` ( `id` CHAR(36) NOT NULL, `name` VARCHAR(50) NOT NULL, `description` VARCHAR(255) NULL, `role_type` ENUM('system','organization','temporary') NOT NULL DEFAULT 'system', `level` INT NOT NULL DEFAULT 0, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uniq_iam_roles_name` (`name`), INDEX `idx_iam_roles_type` (`role_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 1.3 用户-角色关联表 CREATE TABLE IF NOT EXISTS `iam_user_roles` ( `user_id` CHAR(36) NOT NULL, `role_id` CHAR(36) NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX `idx_iam_user_roles_pk` (`user_id`, `role_id`), INDEX `idx_iam_user_roles_role` (`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 1.4 权限表 CREATE TABLE IF NOT EXISTS `iam_permissions` ( `id` CHAR(36) NOT NULL, `name` VARCHAR(100) NOT NULL, `resource` VARCHAR(50) NOT NULL, `action` VARCHAR(50) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uniq_iam_permissions_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 1.5 角色-权限关联表 CREATE TABLE IF NOT EXISTS `iam_role_permissions` ( `role_id` CHAR(36) NOT NULL, `permission_id` CHAR(36) NOT NULL, INDEX `idx_iam_role_permissions_pk` (`role_id`, `permission_id`), INDEX `idx_iam_role_permissions_perm` (`permission_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 1.6 刷新令牌表 CREATE TABLE IF NOT EXISTS `iam_refresh_tokens` ( `id` CHAR(36) NOT NULL, `user_id` CHAR(36) NOT NULL, `token_hash` VARCHAR(255) NOT NULL, `jti` VARCHAR(36) NULL, `expires_at` TIMESTAMP NOT NULL, `revoked_at` TIMESTAMP NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_iam_refresh_tokens_user` (`user_id`), INDEX `idx_iam_refresh_tokens_jti` (`jti`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 1.7 视口配置表(4 层模型) CREATE TABLE IF NOT EXISTS `iam_role_viewports` ( `id` CHAR(36) NOT NULL, `role_id` CHAR(36) NOT NULL, `viewport_key` VARCHAR(50) NOT NULL, `label` VARCHAR(100) NOT NULL, `route` VARCHAR(200) NOT NULL, `icon` VARCHAR(50) NULL, `sort_order` VARCHAR(10) NOT NULL DEFAULT '0', `required_permission` VARCHAR(100) NULL, `level` ENUM('admin','teacher','student','parent') NOT NULL DEFAULT 'teacher', `component_config` TEXT NULL, PRIMARY KEY (`id`), INDEX `idx_iam_role_viewports_role` (`role_id`), INDEX `idx_iam_role_viewports_level` (`level`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 1.8 学生-家长关系表 CREATE TABLE IF NOT EXISTS `iam_student_guardians` ( `id` CHAR(36) NOT NULL, `student_id` CHAR(36) NOT NULL, `guardian_id` CHAR(36) NOT NULL, `relation` VARCHAR(20) NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uniq_student_guardian` (`student_id`, `guardian_id`), INDEX `idx_iam_student_guardians_guardian` (`guardian_id`), INDEX `idx_iam_student_guardians_student` (`student_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 1.9 审计日志表 CREATE TABLE IF NOT EXISTS `iam_user_audit_log` ( `id` CHAR(36) NOT NULL, `actor_user_id` CHAR(36) NOT NULL, `action` VARCHAR(50) NOT NULL, `resource_type` VARCHAR(50) NOT NULL, `resource_id` VARCHAR(36) NOT NULL, `before_state` TEXT NULL, `after_state` TEXT NULL, `ip` VARCHAR(45) NULL, `user_agent` VARCHAR(255) NULL, `trace_id` VARCHAR(64) NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_iam_audit_actor` (`actor_user_id`), INDEX `idx_iam_audit_resource` (`resource_type`, `resource_id`), INDEX `idx_iam_audit_created` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 1.10 密码历史表 CREATE TABLE IF NOT EXISTS `iam_password_history` ( `id` CHAR(36) NOT NULL, `user_id` CHAR(36) NOT NULL, `password_hash` VARCHAR(255) NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_iam_password_history_user` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ============================================================ -- 2. Classes 服务(services/classes/src/classes/classes.schema.ts) -- 班级表(已在 01-init.sql 中创建,此处保留以保完整性,IF NOT EXISTS 安全) -- ============================================================ CREATE TABLE IF NOT EXISTS `classes` ( `id` CHAR(36) NOT NULL, `name` VARCHAR(100) NOT NULL, `grade_id` CHAR(36) NOT NULL, `head_teacher_id` CHAR(36) NULL, `description` TEXT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ============================================================ -- 3. Core-EDU 服务(services/core-edu/src/**/*.schema.ts) -- ============================================================ -- 3.1 教师关联表(IAM 事件消费幂等) CREATE TABLE IF NOT EXISTS `core_edu_teacher_associations` ( `id` CHAR(36) NOT NULL, `teacher_id` CHAR(36) NOT NULL, `class_id` CHAR(36) NOT NULL, `subject_id` CHAR(36) NULL, `school_id` CHAR(36) NOT NULL, `source` VARCHAR(20) NOT NULL DEFAULT 'iam_event', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uniq_teacher_class_subject` (`teacher_id`, `class_id`, `subject_id`), INDEX `idx_teacher_assoc_teacher_id` (`teacher_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.2 考试主表 CREATE TABLE IF NOT EXISTS `core_edu_exams` ( `id` CHAR(36) NOT NULL, `class_id` CHAR(36) NOT NULL, `subject_id` CHAR(36) NOT NULL, `title` VARCHAR(200) NOT NULL, `description` TEXT NULL, `exam_date` DATETIME NOT NULL, `duration` INT NOT NULL, `total_score` DECIMAL(6,2) NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'draft', `status_changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `status_changed_by` CHAR(36) NULL, `school_id` CHAR(36) NOT NULL, `created_by` CHAR(36) NOT NULL, `archived_at` DATETIME NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_exams_class_status` (`class_id`, `status`), INDEX `idx_exams_school_date` (`school_id`, `exam_date`), INDEX `idx_exams_created_by` (`created_by`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.3 考试题目关联表 CREATE TABLE IF NOT EXISTS `core_edu_exam_questions` ( `id` CHAR(36) NOT NULL, `exam_id` CHAR(36) NOT NULL, `question_id` CHAR(36) NOT NULL, `order` INT NOT NULL, `score` DECIMAL(6,2) NOT NULL, `question_type` VARCHAR(30) NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_exam_questions_exam_id` (`exam_id`), INDEX `idx_exam_questions_question_id` (`question_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.4 考试提交表 CREATE TABLE IF NOT EXISTS `core_edu_exam_submissions` ( `id` CHAR(36) NOT NULL, `exam_id` CHAR(36) NOT NULL, `student_id` CHAR(36) NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'not_submitted', `submitted_at` DATETIME NULL, `graded_at` DATETIME NULL, `graded_by` CHAR(36) NULL, `total_score` DECIMAL(6,2) NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uniq_exam_student` (`exam_id`, `student_id`), INDEX `idx_exam_submissions_exam_id` (`exam_id`), INDEX `idx_exam_submissions_student_id` (`student_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.5 作业主表 CREATE TABLE IF NOT EXISTS `core_edu_homework` ( `id` CHAR(36) NOT NULL, `class_id` CHAR(36) NOT NULL, `subject_id` CHAR(36) NOT NULL, `title` VARCHAR(200) NOT NULL, `description` TEXT NULL, `due_date` DATETIME NOT NULL, `grace_period` INT NOT NULL DEFAULT 300, `status` VARCHAR(20) NOT NULL DEFAULT 'assigned', `school_id` CHAR(36) NOT NULL, `created_by` CHAR(36) NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_homework_class_status` (`class_id`, `status`), INDEX `idx_homework_created_by` (`created_by`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.6 作业提交表 CREATE TABLE IF NOT EXISTS `core_edu_homework_submissions` ( `id` CHAR(36) NOT NULL, `homework_id` CHAR(36) NOT NULL, `student_id` CHAR(36) NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'not_submitted', `submitted_at` DATETIME NULL, `graded_at` DATETIME NULL, `graded_by` CHAR(36) NULL, `total_score` DECIMAL(6,2) NULL, `feedback` TEXT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uniq_hw_student` (`homework_id`, `student_id`), INDEX `idx_hw_submissions_homework_id` (`homework_id`), INDEX `idx_hw_submissions_student_id` (`student_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.7 作业答题表 CREATE TABLE IF NOT EXISTS `core_edu_homework_answers` ( `id` CHAR(36) NOT NULL, `submission_id` CHAR(36) NOT NULL, `question_id` CHAR(36) NOT NULL, `answer` TEXT NULL, `score` DECIMAL(6,2) NULL, `teacher_comment` TEXT NULL, `is_correct` TINYINT(1) NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_hw_answers_submission_id` (`submission_id`), INDEX `idx_hw_answers_question_id` (`question_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.8 成绩主表 CREATE TABLE IF NOT EXISTS `core_edu_grades` ( `id` CHAR(36) NOT NULL, `student_id` CHAR(36) NOT NULL, `exam_id` CHAR(36) NULL, `homework_id` CHAR(36) NULL, `score` DECIMAL(6,2) NOT NULL, `total_score` DECIMAL(6,2) NOT NULL, `feedback` TEXT NULL, `graded_by` CHAR(36) NOT NULL, `school_id` CHAR(36) NOT NULL, `idempotency_key` VARCHAR(128) NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uniq_student_exam` (`student_id`, `exam_id`), UNIQUE KEY `uniq_student_hw` (`student_id`, `homework_id`), UNIQUE KEY `uniq_idempotency` (`idempotency_key`), INDEX `idx_grades_student_id` (`student_id`), INDEX `idx_grades_exam_id` (`exam_id`), INDEX `idx_grades_homework_id` (`homework_id`), INDEX `idx_grades_graded_by` (`graded_by`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.9 成绩公式表 CREATE TABLE IF NOT EXISTS `core_edu_grade_formulas` ( `id` CHAR(36) NOT NULL, `scope` VARCHAR(20) NOT NULL, `scope_id` CHAR(36) NOT NULL, `formula_type` VARCHAR(20) NOT NULL, `weights` JSON NULL, `custom_expression` TEXT NULL, `effective_from` DATETIME NOT NULL, `effective_to` DATETIME NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_grade_formulas_scope` (`scope`, `scope_id`), INDEX `idx_grade_formulas_effective` (`effective_from`, `effective_to`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.10 考勤记录表 CREATE TABLE IF NOT EXISTS `core_edu_attendance` ( `id` CHAR(36) NOT NULL, `schedule_id` CHAR(36) NOT NULL, `student_id` CHAR(36) NOT NULL, `status` VARCHAR(20) NOT NULL, `remark` TEXT NULL, `recorded_by` CHAR(36) NOT NULL, `school_id` CHAR(36) NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uniq_sched_student` (`schedule_id`, `student_id`), INDEX `idx_attendance_student_id` (`student_id`), INDEX `idx_attendance_schedule_id` (`schedule_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.11 课程表 CREATE TABLE IF NOT EXISTS `core_edu_courses` ( `id` CHAR(36) NOT NULL, `class_id` CHAR(36) NOT NULL, `subject_id` CHAR(36) NOT NULL, `teacher_id` CHAR(36) NOT NULL, `school_id` CHAR(36) NOT NULL, `name` VARCHAR(200) NOT NULL, `start_date` DATE NOT NULL, `end_date` DATE NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_courses_class_id` (`class_id`), INDEX `idx_courses_teacher_id` (`teacher_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.12 课次表 CREATE TABLE IF NOT EXISTS `core_edu_lessons` ( `id` CHAR(36) NOT NULL, `course_id` CHAR(36) NOT NULL, `title` VARCHAR(200) NOT NULL, `order` INT NOT NULL, `knowledge_point_ids` JSON NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_lessons_course_id` (`course_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.13 排课表 CREATE TABLE IF NOT EXISTS `core_edu_schedules` ( `id` CHAR(36) NOT NULL, `course_id` CHAR(36) NOT NULL, `lesson_id` CHAR(36) NULL, `teacher_id` CHAR(36) NOT NULL, `class_id` CHAR(36) NOT NULL, `room_id` CHAR(36) NULL, `start_time` DATETIME NOT NULL, `end_time` DATETIME NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'scheduled', `school_id` CHAR(36) NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_schedules_teacher_time` (`teacher_id`, `start_time`, `end_time`), INDEX `idx_schedules_class_time` (`class_id`, `start_time`, `end_time`), INDEX `idx_schedules_course_id` (`course_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 3.14 Core-EDU Outbox 表(事务性发件箱) CREATE TABLE IF NOT EXISTS `core_edu_outbox` ( `id` CHAR(36) NOT NULL, `event_id` CHAR(36) NOT NULL, `aggregate_id` CHAR(36) NOT NULL, `aggregate_type` VARCHAR(50) NOT NULL, `event_type` VARCHAR(100) NOT NULL, `occurred_at` DATETIME NOT NULL, `payload` TEXT NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'pending', `retry_count` BIGINT NOT NULL DEFAULT 0, `next_retry_at` DATETIME NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `processed_at` TIMESTAMP NULL, PRIMARY KEY (`id`), UNIQUE KEY `uniq_event_id` (`event_id`), INDEX `idx_outbox_status_retry` (`status`, `next_retry_at`), INDEX `idx_outbox_aggregate` (`aggregate_type`, `aggregate_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ============================================================ -- 4. Content 服务(services/content/src/**/*.schema.ts) -- ============================================================ -- 4.1 教材表 CREATE TABLE IF NOT EXISTS `content_textbooks` ( `id` VARCHAR(32) NOT NULL, `title` VARCHAR(255) NOT NULL, `subject_id` VARCHAR(32) NOT NULL, `grade_id` VARCHAR(32) NOT NULL, `version` VARCHAR(32) NOT NULL DEFAULT '1.0', `status` VARCHAR(32) NOT NULL DEFAULT 'draft', `tenant_id` VARCHAR(32) NULL, `metadata` JSON NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_textbooks_subject_grade` (`subject_id`, `grade_id`), INDEX `idx_textbooks_status` (`status`), INDEX `idx_textbooks_tenant` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 4.2 章节表 CREATE TABLE IF NOT EXISTS `content_chapters` ( `id` VARCHAR(32) NOT NULL, `textbook_id` VARCHAR(32) NOT NULL, `title` VARCHAR(255) NOT NULL, `order_num` INT NOT NULL DEFAULT 0, `parent_id` VARCHAR(32) NULL, `status` VARCHAR(32) NOT NULL DEFAULT 'draft', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_chapters_textbook_order` (`textbook_id`, `order_num`), INDEX `idx_chapters_parent` (`parent_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 4.3 知识点表 CREATE TABLE IF NOT EXISTS `content_knowledge_points` ( `id` VARCHAR(32) NOT NULL, `chapter_id` VARCHAR(32) NOT NULL, `title` VARCHAR(255) NOT NULL, `description` TEXT NULL, `difficulty` TINYINT NOT NULL DEFAULT 3, `metadata` JSON NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_kp_chapter` (`chapter_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 4.4 题目表 CREATE TABLE IF NOT EXISTS `content_questions` ( `id` VARCHAR(32) NOT NULL, `knowledge_point_id` VARCHAR(32) NOT NULL, `type` VARCHAR(32) NOT NULL, `content` TEXT NOT NULL, `options` JSON NULL, `answer` TEXT NOT NULL, `explanation` TEXT NULL, `difficulty` TINYINT NOT NULL DEFAULT 3, `status` VARCHAR(32) NOT NULL DEFAULT 'draft', `source` VARCHAR(32) NOT NULL DEFAULT 'manual', `created_by` VARCHAR(32) NOT NULL, `metadata` JSON NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_questions_kp` (`knowledge_point_id`), INDEX `idx_questions_type_difficulty` (`type`, `difficulty`), INDEX `idx_questions_status` (`status`), INDEX `idx_questions_source` (`source`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 4.5 Content Outbox 表 CREATE TABLE IF NOT EXISTS `content_outbox_events` ( `id` VARCHAR(32) NOT NULL, `aggregate_type` VARCHAR(64) NOT NULL, `aggregate_id` VARCHAR(32) NOT NULL, `event_type` VARCHAR(100) NOT NULL, `topic` VARCHAR(128) NOT NULL, `payload` TEXT NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'pending', `retry_count` INT NOT NULL DEFAULT 0, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `published_at` TIMESTAMP NULL, `next_retry_at` TIMESTAMP NULL, `last_error` TEXT NULL, PRIMARY KEY (`id`), INDEX `idx_outbox_status_retry` (`status`, `next_retry_at`), INDEX `idx_outbox_aggregate` (`aggregate_type`, `aggregate_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- ============================================================ -- 5. MSG 服务(services/msg/src/**/*.schema.ts) -- ============================================================ -- 5.1 通知主表 CREATE TABLE IF NOT EXISTS `msg_notifications` ( `id` VARCHAR(32) NOT NULL, `user_id` VARCHAR(32) NOT NULL, `type` VARCHAR(32) NOT NULL, `title` VARCHAR(255) NOT NULL, `content` TEXT NOT NULL, `channel` VARCHAR(32) NOT NULL, `is_read` TINYINT(1) NOT NULL DEFAULT 0, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `status` VARCHAR(32) NOT NULL DEFAULT 'pending', `metadata` JSON NULL, `related_entity_type` VARCHAR(64) NULL, `related_entity_id` VARCHAR(32) NULL, `group_id` VARCHAR(32) NULL, `sender_id` VARCHAR(32) NULL, `template_id` VARCHAR(32) NULL, `event_id` VARCHAR(64) NULL, `read_at` TIMESTAMP NULL, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 5.2 用户偏好表 CREATE TABLE IF NOT EXISTS `msg_notification_preferences` ( `id` VARCHAR(32) NOT NULL, `user_id` VARCHAR(32) NOT NULL, `type` VARCHAR(32) NOT NULL, `channels` JSON NOT NULL, `frequency_limit` INT NULL, `quiet_hours_start` VARCHAR(8) NULL, `quiet_hours_end` VARCHAR(8) NULL, `quiet_hours_timezone` VARCHAR(64) NULL DEFAULT 'Asia/Shanghai', `enabled` TINYINT(1) NOT NULL DEFAULT 1, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 5.3 通知模板表 CREATE TABLE IF NOT EXISTS `msg_notification_templates` ( `id` VARCHAR(32) NOT NULL, `code` VARCHAR(64) NOT NULL, `type` VARCHAR(32) NOT NULL, `title_template` VARCHAR(255) NOT NULL, `content_template` TEXT NOT NULL, `default_channels` JSON NOT NULL, `variables` JSON NOT NULL, `locale` VARCHAR(16) NOT NULL DEFAULT 'zh-CN', `status` VARCHAR(32) NOT NULL DEFAULT 'draft', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 5.4 投递记录表 CREATE TABLE IF NOT EXISTS `msg_notification_deliveries` ( `id` VARCHAR(32) NOT NULL, `notification_id` VARCHAR(32) NOT NULL, `channel` VARCHAR(32) NOT NULL, `status` VARCHAR(32) NOT NULL DEFAULT 'pending', `external_id` VARCHAR(128) NULL, `attempt_count` INT NOT NULL DEFAULT 0, `max_retry` INT NOT NULL DEFAULT 3, `last_error` TEXT NULL, `next_retry_at` TIMESTAMP NULL, `delivered_at` TIMESTAMP NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 5.5 MSG Outbox 表 CREATE TABLE IF NOT EXISTS `msg_outbox_events` ( `event_id` VARCHAR(64) NOT NULL, `aggregate_type` VARCHAR(64) NOT NULL, `aggregate_id` VARCHAR(32) NOT NULL, `event_type` VARCHAR(64) NOT NULL, `topic` VARCHAR(128) NOT NULL, `payload` JSON NOT NULL, `status` VARCHAR(16) NOT NULL DEFAULT 'pending', `retry_count` INT NOT NULL DEFAULT 0, `max_retry_count` INT NOT NULL DEFAULT 5, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `published_at` TIMESTAMP NULL, `next_retry_at` TIMESTAMP NULL, `last_error` TEXT NULL, `metadata` JSON NULL, PRIMARY KEY (`event_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- 5.6 已处理事件表(消费幂等) CREATE TABLE IF NOT EXISTS `processed_events` ( `event_id` VARCHAR(64) NOT NULL, `topic` VARCHAR(128) NOT NULL, `processed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`event_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;