fix: 修复集成测试中发现的全部 bug — 15 服务端到端验证通过

修复涵盖 6 大类问题:

1. api-gateway
   - 路径前缀剥离 /api 而非 /api/v1,保留下游 /v1/ controller 前缀
   - JWKS URL 默认值修复
   - publicPaths 白名单对齐 /v1/iam/*

2. iam
   - iam.module.ts exports 补充 PermissionCacheService 和 IamRepository
   - main.ts resolveProtoPath() 多路径探测 proto 文件

3. core-edu
   - app.module.ts AuthMiddleware 全局注册

4. BFF 层 GraphQL 端点(teacher-bff / parent-bff / student-bff)
   - teacher-bff: mock dataScope OWN→SELF 对齐 GraphQL enum;WHATWG Request header .get() 提取
   - parent-bff: handleNodeRequestAndResponse 不存在 → 直接 yoga(req,res);WHATWG Request header .get() 提取
   - student-bff: auth.resolver 移除 ActionState 信封返回扁平对象;WHATWG Request header .get() 提取

5. ai
   - Kafka 事务降级 + 10s 超时
   - gRPC 拦截器降级
   - dev mode 禁用事务模式

6. 前端 + 共享包
   - teacher-portal: MF 插件条件实例化 + transpilePackages + extensionAlias
   - ui-components: error-boundary.tsx 添加 use client
   - ui-tokens: tailwind-theme.css 移除 @layer base
   - shared-ts: 导出从源码改为 dist 编译产物;OutboxModule global:true

7. infra
   - .gitignore 补充 keys/ *.pem *.key secrets/ 排除规则
   - infra/init-sql/02-all-services-schema.sql 36 张表 DDL

验证结果:
- TS typecheck: 19 个 workspace 项目全部通过
- Go vet + Ruff: 通过
- 15 服务全部启动成功
- 3 个 BFF GraphQL 端点 + 4 个前端页面全部 200
- Gateway → iam → core-edu 端到端链路验证通过

AI identity: trae-main(集成测试修复会话)
This commit is contained in:
SpecialX
2026-07-11 01:41:46 +08:00
parent c6362f4b04
commit 61d824924a
35 changed files with 1020 additions and 192 deletions

View File

@@ -0,0 +1,609 @@
-- ============================================================
-- 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;