From 97e2e8402e42c1f7fadac0b82fe240b0702b0e06 Mon Sep 17 00:00:00 2001 From: silverpro89 Date: Mon, 19 Jan 2026 20:32:23 +0700 Subject: [PATCH] update --- CONTENT_MANAGEMENT_GUIDE.md | 735 +++ HYBRID_ROLE_ARCHITECTURE.md | 278 + SWAGGER_GUIDE.md | 228 + app.js | 39 +- config/config.json | 2 +- config/swagger.js | 201 + controllers/authController.js | 188 +- models/UserProfile.js | 5 + package-lock.json | 7258 +++++++++++++++++++++++++ package.json | 32 +- scripts/add-primary-role-info.js | 59 + scripts/populate-primary-role-info.js | 115 + services/roleHelperService.js | 266 + yarn.lock | 1395 ++--- 14 files changed, 10115 insertions(+), 686 deletions(-) create mode 100644 CONTENT_MANAGEMENT_GUIDE.md create mode 100644 HYBRID_ROLE_ARCHITECTURE.md create mode 100644 SWAGGER_GUIDE.md create mode 100644 config/swagger.js create mode 100644 package-lock.json create mode 100644 scripts/add-primary-role-info.js create mode 100644 scripts/populate-primary-role-info.js create mode 100644 services/roleHelperService.js diff --git a/CONTENT_MANAGEMENT_GUIDE.md b/CONTENT_MANAGEMENT_GUIDE.md new file mode 100644 index 0000000..46ac00b --- /dev/null +++ b/CONTENT_MANAGEMENT_GUIDE.md @@ -0,0 +1,735 @@ +# 📚 Hướng Dẫn Quản Lý Nội Dung Giảng Dạy và Game + +## Tổng Quan Kiến Trúc + +Hệ thống nội dung của bạn được xây dựng theo mô hình phân cấp: + +``` +Subject (Môn học/Giáo trình) + └─ Chapter (Chương) + └─ Lesson (Bài học) + ├─ JSON Content → Game Engine render + └─ URL Content → Video/PDF player +``` + +--- + +## 📊 Cấu Trúc Models + +### 1. Subject (Môn học) +**File**: `models/Subject.js` +**Table**: `subjects` + +```javascript +{ + id: UUID, + subject_code: 'MATH_G1', // Mã môn học (unique) + subject_name: 'Toán lớp 1', // Tên tiếng Việt + subject_name_en: 'Math Grade 1', // Tên tiếng Anh + description: TEXT, // Mô tả + is_active: true, // Đang hoạt động + is_premium: false, // Nội dung premium + is_training: false, // Đào tạo nhân sự + is_public: false, // Tự học công khai + required_role: 'student', // Role yêu cầu + min_subscription_tier: 'basic' // Gói tối thiểu +} +``` + +### 2. Chapter (Chương học) +**File**: `models/Chapter.js` +**Table**: `chapters` + +```javascript +{ + id: UUID, + subject_id: UUID, // FK → subjects + chapter_number: 1, // Số thứ tự chương + chapter_title: 'Số và chữ số', // Tiêu đề + chapter_description: TEXT, // Mô tả + duration_minutes: 180, // Thời lượng (phút) + is_published: true, // Đã xuất bản + display_order: 1 // Thứ tự hiển thị +} +``` + +### 3. Lesson (Bài học) +**File**: `models/Lesson.js` +**Table**: `lessons` + +```javascript +{ + id: UUID, + chapter_id: UUID, // FK → chapters + lesson_number: 1, // Số thứ tự bài học + lesson_title: 'Đếm từ 1 đến 5', // Tiêu đề + lesson_type: 'json_content', // hoặc 'url_content' + lesson_description: TEXT, + + // Dạng 1: JSON Content (tương tác) + content_json: { + type: 'counting_quiz', // PHẢI khớp với Game.type + questions: [...], + instructions: '...', + pass_score: 70 + }, + + // Dạng 2: URL Content (video/PDF/link) + content_url: 'https://youtube.com/...', + content_type: 'youtube', // video, audio, pdf, external_link + + duration_minutes: 15, + is_published: true, + is_free: true, // Học thử miễn phí + display_order: 1, + thumbnail_url: '...' +} +``` + +### 4. Game (Game Engine/Template) +**File**: `models/Game.js` +**Table**: `games` + +```javascript +{ + id: UUID, + title: 'Trò chơi đếm số', + description: TEXT, + url: 'https://games.senaai.tech/counting-game/', // HTML5/Unity WebGL + thumbnail: '...', + type: 'counting_quiz', // PHẢI khớp với Lesson.content_json.type + config: { + engine: 'phaser3', + features: ['sound', 'animation'], + controls: ['touch', 'mouse'], + max_time: 300 + }, + is_active: true, + is_premium: false, + min_grade: 1, // Cấp lớp tối thiểu + max_grade: 3, // Cấp lớp tối đa + difficulty_level: 'easy', // easy, medium, hard + play_count: 0, + rating: 4.5 +} +``` + +--- + +## 🎯 Cách 1: Thêm Nội Dung Giảng Dạy + +### Bước 1: Tạo Subject (Môn học) + +```javascript +const { Subject, Chapter, Lesson } = require('./models'); + +// Tạo môn Toán lớp 1 +const mathSubject = await Subject.create({ + subject_code: 'MATH_G1', + subject_name: 'Toán lớp 1', + subject_name_en: 'Math Grade 1', + description: 'Chương trình Toán học lớp 1 theo SGK mới', + is_active: true, + is_premium: false, + is_public: true, + min_subscription_tier: 'basic' +}); +``` + +### Bước 2: Tạo Chapter (Chương) + +```javascript +const chapter1 = await Chapter.create({ + subject_id: mathSubject.id, + chapter_number: 1, + chapter_title: 'Số và chữ số', + chapter_description: 'Làm quen với các số từ 1 đến 10', + duration_minutes: 180, + is_published: true, + display_order: 1 +}); + +const chapter2 = await Chapter.create({ + subject_id: mathSubject.id, + chapter_number: 2, + chapter_title: 'Phép cộng trong phạm vi 10', + chapter_description: 'Học cộng các số từ 1 đến 10', + duration_minutes: 240, + is_published: true, + display_order: 2 +}); +``` + +### Bước 3a: Tạo Lesson với JSON Content (Tương tác) + +```javascript +const lesson1 = await Lesson.create({ + chapter_id: chapter1.id, + lesson_number: 1, + lesson_title: 'Đếm từ 1 đến 5', + lesson_type: 'json_content', + lesson_description: 'Học đếm các số từ 1 đến 5 qua trò chơi', + content_json: { + type: 'counting_quiz', // Khớp với Game có type='counting_quiz' + theme: 'fruits', + questions: [ + { + id: 1, + question: 'Có bao nhiêu quả táo?', + image: 'https://cdn.senaai.tech/images/apples-3.png', + correct_answer: 3, + options: [2, 3, 4, 5] + }, + { + id: 2, + question: 'Có bao nhiêu quả cam?', + image: 'https://cdn.senaai.tech/images/oranges-5.png', + correct_answer: 5, + options: [3, 4, 5, 6] + } + ], + instructions: 'Nhìn vào hình và đếm số lượng vật, sau đó chọn đáp án đúng', + pass_score: 70, + max_attempts: 3, + show_hints: true + }, + duration_minutes: 15, + is_published: true, + is_free: true, + display_order: 1, + thumbnail_url: 'https://cdn.senaai.tech/thumbnails/lesson-counting.jpg' +}); +``` + +### Bước 3b: Tạo Lesson với URL Content (Video/PDF) + +```javascript +// Video YouTube +const lesson2 = await Lesson.create({ + chapter_id: chapter1.id, + lesson_number: 2, + lesson_title: 'Video: Hướng dẫn đếm số', + lesson_type: 'url_content', + lesson_description: 'Video hướng dẫn cách đếm số từ 1 đến 10', + content_url: 'https://www.youtube.com/watch?v=abc123xyz', + content_type: 'youtube', + duration_minutes: 10, + is_published: true, + is_free: false, + display_order: 2 +}); + +// PDF Document +const lesson3 = await Lesson.create({ + chapter_id: chapter1.id, + lesson_number: 3, + lesson_title: 'Tài liệu: Bảng số từ 1-10', + lesson_type: 'url_content', + content_url: 'https://cdn.senaai.tech/docs/number-chart-1-10.pdf', + content_type: 'pdf', + duration_minutes: 5, + is_published: true, + is_free: true, + display_order: 3 +}); + +// Audio +const lesson4 = await Lesson.create({ + chapter_id: chapter1.id, + lesson_number: 4, + lesson_title: 'Nghe: Phát âm các số', + lesson_type: 'url_content', + content_url: 'https://cdn.senaai.tech/audio/numbers-pronunciation.mp3', + content_type: 'audio', + duration_minutes: 8, + is_published: true, + display_order: 4 +}); +``` + +### Bước 3c: Các loại Content JSON khác + +```javascript +// Quiz trắc nghiệm +const quizLesson = await Lesson.create({ + chapter_id: chapter2.id, + lesson_number: 1, + lesson_title: 'Bài kiểm tra: Phép cộng', + lesson_type: 'json_content', + content_json: { + type: 'multiple_choice_quiz', + time_limit: 600, // 10 phút + questions: [ + { + id: 1, + question: '2 + 3 = ?', + options: ['4', '5', '6', '7'], + correct_answer: '5', + explanation: 'Hai cộng ba bằng năm' + }, + { + id: 2, + question: '4 + 5 = ?', + options: ['7', '8', '9', '10'], + correct_answer: '9' + } + ], + pass_score: 80 + }, + duration_minutes: 10, + is_published: true +}); + +// Bài tập tương tác +const interactiveLesson = await Lesson.create({ + chapter_id: chapter2.id, + lesson_number: 2, + lesson_title: 'Thực hành: Giải toán cộng', + lesson_type: 'json_content', + content_json: { + type: 'math_practice', + difficulty: 'easy', + operations: ['addition'], + range: { min: 1, max: 10 }, + question_count: 20, + show_solution_steps: true, + allow_calculator: false + }, + duration_minutes: 20, + is_published: true +}); + +// Assignment (Bài tập về nhà) +const assignmentLesson = await Lesson.create({ + chapter_id: chapter2.id, + lesson_number: 3, + lesson_title: 'Bài tập về nhà: Tuần 1', + lesson_type: 'json_content', + content_json: { + type: 'assignment', + deadline_days: 7, + tasks: [ + { + task_id: 1, + title: 'Làm bài tập SGK trang 15', + description: 'Hoàn thành các bài từ 1 đến 5', + points: 10 + }, + { + task_id: 2, + title: 'Vẽ 5 quả táo và đếm', + description: 'Vẽ hình và viết số', + points: 5, + requires_upload: true + } + ], + total_points: 15, + submission_type: 'photo' // photo, pdf, text + }, + duration_minutes: 30, + is_published: true +}); +``` + +--- + +## 🎮 Cách 2: Thêm Game Engine + +### Tạo Game Template + +```javascript +const { Game } = require('./models'); + +// Game 1: Đếm số +const countingGame = await Game.create({ + title: 'Trò chơi đếm số', + description: 'Game tương tác giúp trẻ học đếm các vật thể', + url: 'https://games.senaai.tech/counting-game/index.html', + thumbnail: 'https://cdn.senaai.tech/game-thumbs/counting.jpg', + type: 'counting_quiz', // Khớp với Lesson.content_json.type + config: { + engine: 'phaser3', + version: '1.0.0', + features: ['sound', 'animation', 'reward_stars'], + controls: ['touch', 'mouse', 'keyboard'], + responsive: true, + max_time_per_question: 60, + hints_enabled: true, + save_progress: true + }, + is_active: true, + is_premium: false, + min_grade: 1, + max_grade: 2, + difficulty_level: 'easy', + play_count: 0, + rating: 0, + display_order: 1 +}); + +// Game 2: Quiz trắc nghiệm +const quizGame = await Game.create({ + title: 'Trả lời nhanh', + description: 'Trò chơi trắc nghiệm với giới hạn thời gian', + url: 'https://games.senaai.tech/quiz-game/index.html', + thumbnail: 'https://cdn.senaai.tech/game-thumbs/quiz.jpg', + type: 'multiple_choice_quiz', + config: { + engine: 'react', + features: ['timer', 'leaderboard', 'achievements'], + sound_effects: true, + show_correct_answer: true, + retry_allowed: true + }, + is_active: true, + is_premium: false, + min_grade: 1, + max_grade: 12, + difficulty_level: 'medium', + display_order: 2 +}); + +// Game 3: Thực hành Toán +const mathPracticeGame = await Game.create({ + title: 'Luyện tập Toán', + description: 'Game luyện toán với nhiều cấp độ khó', + url: 'https://games.senaai.tech/math-practice/index.html', + thumbnail: 'https://cdn.senaai.tech/game-thumbs/math.jpg', + type: 'math_practice', + config: { + engine: 'unity_webgl', + features: ['adaptive_difficulty', 'step_by_step_solution', 'scratch_pad'], + controls: ['touch', 'mouse'], + performance_tracking: true + }, + is_active: true, + is_premium: true, + min_grade: 1, + max_grade: 6, + difficulty_level: 'medium', + display_order: 3 +}); + +// Game 4: Word Puzzle +const wordPuzzleGame = await Game.create({ + title: 'Ghép chữ', + description: 'Trò chơi ghép chữ và học từ vựng', + url: 'https://games.senaai.tech/word-puzzle/index.html', + thumbnail: 'https://cdn.senaai.tech/game-thumbs/word.jpg', + type: 'word_puzzle', + config: { + engine: 'phaser3', + features: ['drag_drop', 'text_to_speech', 'pronunciation'], + languages: ['vi', 'en'], + difficulty_levels: ['easy', 'medium', 'hard'] + }, + is_active: true, + is_premium: false, + min_grade: 1, + max_grade: 5, + difficulty_level: 'easy', + display_order: 4 +}); +``` + +--- + +## 🔄 Luồng Hoạt Động + +### Frontend Flow + +```javascript +// 1. Học viên chọn môn học +GET /api/subjects/:subject_id +→ Trả về thông tin Subject + +// 2. Xem danh sách chương +GET /api/subjects/:subject_id/chapters +→ Trả về danh sách Chapter + +// 3. Xem danh sách bài học trong chương +GET /api/chapters/:chapter_id/lessons +→ Trả về danh sách Lesson + +// 4. Học một bài cụ thể +GET /api/lessons/:lesson_id +→ Trả về chi tiết Lesson + +// 5a. Nếu lesson_type = 'json_content' +const lessonType = lesson.content_json.type; // vd: 'counting_quiz' + +// Tìm game engine phù hợp +GET /api/games?type=counting_quiz +→ Trả về Game có type khớp + +// Load game và truyền content vào +