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:
609
infra/init-sql/02-all-services-schema.sql
Normal file
609
infra/init-sql/02-all-services-schema.sql
Normal 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;
|
||||
Reference in New Issue
Block a user