1. teacher-portal 登录 JSON 解析错误修复
- 根因:next.config.js rewrites /api/auth/* → gateway /api/auth/*,
但 gateway 只在 /api/v1 路由组下注册代理,/api/auth/* 返回 404 纯文本
- 修复:rewrites 映射 /api/auth/* → /api/v1/iam/*(对齐 iam controller @Controller("v1/iam"))
2. msg / data-ana 端口冲突修复
- 根因:msg env.ts PORT 默认 3006,与 data-ana(config.py http_port=3006)冲突
- 修复:msg PORT 默认 3006→3007(对齐端口分配表 msg=3007, data-ana=3006)
3. Prometheus classes-service 3001 DOWN 修复
- 根因:classes 已合并入 core-edu(P3 裁决 C1),但 prometheus.yml 仍有 classes-service scrape 配置
- 修复:从 prometheus.yml 删除 classes-service job
4. iam outbox 表 aggregate_id 列长度修复
- 根因:iam_outbox.aggregate_id VARCHAR(32),但 UUID 是 36 字符,导致 "Data too long" 错误
- 修复:DDL 中 VARCHAR(32) → VARCHAR(36);ALTER TABLE 修复现有数据库
5. 补充未搭建的监控基础设施
- docker-compose.yml 新增 Alertmanager(9093)/ Loki(3100)/ Promtail(observability profile)
- prometheus.yml alertmanager target 更新为 edu-alertmanager:9093
验证:
- TS typecheck 19 项目全部通过
- 登录链路 teacher-portal → gateway → iam 验证通过
- Prometheus targets 不再有 classes-service
- 3 个新基础设施容器(alertmanager/loki/promtail)启动成功
AI identity: trae-main(基础设施修复会话)
610 lines
24 KiB
SQL
610 lines
24 KiB
SQL
-- ============================================================
|
||
-- 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(36) 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(36) 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;
|