Files
sena_db_api_layer/CONTENT_MANAGEMENT_GUIDE.md
silverpro89 97e2e8402e update
2026-01-19 20:32:23 +07:00

19 KiB

📚 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

{
  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

{
  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

{
  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

{
  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)

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)

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)

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)

// 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

// 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

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

// 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  type khớp

// Load game và truyền content vào
<iframe 
  src={game.url}
  data-content={JSON.stringify(lesson.content_json)}
/>

// 5b. Nếu lesson_type = 'url_content'
if (lesson.content_type === 'youtube') {
  <YouTubePlayer url={lesson.content_url} />
} else if (lesson.content_type === 'pdf') {
  <PDFViewer url={lesson.content_url} />
} else if (lesson.content_type === 'audio') {
  <AudioPlayer url={lesson.content_url} />
}

Game Engine Integration

// Trong game engine (ví dụ: counting-game/index.html)
window.addEventListener('message', (event) => {
  if (event.data.type === 'LOAD_CONTENT') {
    const content = event.data.content;  // Lesson.content_json
    
    // Render game với nội dung từ lesson
    renderGame({
      questions: content.questions,
      theme: content.theme,
      instructions: content.instructions,
      passScore: content.pass_score
    });
  }
});

// Khi học viên hoàn thành
window.parent.postMessage({
  type: 'GAME_COMPLETE',
  score: 85,
  time_spent: 300,
  answers: [...]
}, '*');

📋 API Endpoints Cần Thiết

Subject Routes (Đã có: routes/subjectRoutes.js)

GET    /api/subjects              - Danh sách môn học
GET    /api/subjects/:id          - Chi tiết môn học
POST   /api/subjects              - Tạo môn học mới
PUT    /api/subjects/:id          - Cập nhật môn học
DELETE /api/subjects/:id          - Xóa môn học

Chapter Routes (Đã có: routes/chapterRoutes.js)

GET    /api/chapters              - Danh sách chương
GET    /api/chapters/:id          - Chi tiết chương
GET    /api/subjects/:id/chapters - Chương của môn học
POST   /api/chapters              - Tạo chương mới
PUT    /api/chapters/:id          - Cập nhật chương
DELETE /api/chapters/:id          - Xóa chương

Lesson Routes (CẦN TẠO)

GET    /api/lessons               - Danh sách bài học
GET    /api/lessons/:id           - Chi tiết bài học
GET    /api/chapters/:id/lessons  - Bài học của chương
POST   /api/lessons               - Tạo bài học mới
PUT    /api/lessons/:id           - Cập nhật bài học
DELETE /api/lessons/:id           - Xóa bài học
POST   /api/lessons/:id/complete  - Đánh dấu hoàn thành

Game Routes (Đã có: routes/gameRoutes.js)

GET    /api/games                 - Danh sách game
GET    /api/games/:id             - Chi tiết game
GET    /api/games?type=xxx        - Lọc theo type
POST   /api/games                 - Tạo game mới
PUT    /api/games/:id             - Cập nhật game
DELETE /api/games/:id             - Xóa game
POST   /api/games/:id/play        - Tăng play_count
POST   /api/games/:id/rate        - Đánh giá game

Checklist Triển Khai

Bước 1: Tạo Lesson Routes & Controller

  • Tạo routes/lessonRoutes.js
  • Tạo controllers/lessonController.js
  • Đăng ký route trong app.js

Bước 2: Tạo Sample Data

  • Tạo script scripts/seed-sample-content.js
  • Tạo Subject mẫu (Toán, Tiếng Việt, Tiếng Anh)
  • Tạo Chapter mẫu (3-5 chương mỗi môn)
  • Tạo Lesson mẫu (5-10 bài mỗi chương)
  • Tạo Game template mẫu (3-5 game)

Bước 3: Update Relations

// Trong models/index.js
Subject.hasMany(Chapter, { foreignKey: 'subject_id' });
Chapter.belongsTo(Subject, { foreignKey: 'subject_id' });

Chapter.hasMany(Lesson, { foreignKey: 'chapter_id' });
Lesson.belongsTo(Chapter, { foreignKey: 'chapter_id' });

Bước 4: Thêm Swagger Documentation

  • Thêm Swagger cho Lesson routes
  • Cập nhật schemas trong config/swagger.js

Bước 5: Test

  • Test tạo Subject → Chapter → Lesson
  • Test query lessons theo chapter
  • Test match Game với Lesson.content_json.type
  • Test các loại content khác nhau

💡 Best Practices

1. Lesson Type Naming Convention

// Đặt tên theo format: <category>_<action>
'counting_quiz'           // Đếm số - Quiz
'multiple_choice_quiz'    // Trắc nghiệm
'math_practice'           // Luyện toán
'word_puzzle'             // Ghép chữ
'reading_comprehension'   // Đọc hiểu
'listening_exercise'      // Nghe
'speaking_practice'       // Luyện nói
'writing_assignment'      // Bài tập viết
'project_based'           // Dự án

2. Content JSON Structure

{
  type: 'xxx',              // REQUIRED: Phải khớp với Game.type
  version: '1.0',           // Version của schema
  metadata: {               // Optional: Thông tin thêm
    author: 'Nguyễn Văn A',
    created_at: '2026-01-19',
    tags: ['counting', 'basic', 'grade1']
  },
  config: {},               // Cấu hình riêng của lesson
  data: {}                  // Dữ liệu chính (questions, tasks, etc.)
}

3. Game Config Structure

{
  engine: 'phaser3',        // Game engine sử dụng
  version: '1.0.0',
  features: [],             // Các tính năng: sound, animation, etc.
  controls: [],             // touch, mouse, keyboard
  responsive: true,
  settings: {
    max_time: 300,
    hints_allowed: true,
    retry_count: 3
  }
}

4. Error Handling

// Khi student học lesson
if (lesson.lesson_type === 'json_content') {
  const lessonType = lesson.content_json.type;
  const game = await Game.findOne({ 
    where: { 
      type: lessonType,
      is_active: true 
    } 
  });
  
  if (!game) {
    // Fallback: Hiển thị content JSON dạng text/list
    console.warn(`No game found for type: ${lessonType}`);
    return renderStaticContent(lesson.content_json);
  }
  
  return renderGameWithContent(game, lesson.content_json);
}

🚀 Quick Start Script

Chạy script này để tạo sample data:

// scripts/seed-sample-content.js
const { Subject, Chapter, Lesson, Game } = require('../models');

async function seedSampleContent() {
  // 1. Tạo Subject
  const math = await Subject.create({
    subject_code: 'MATH_G1',
    subject_name: 'Toán lớp 1',
    is_active: true,
    is_public: true
  });
  
  // 2. Tạo Chapter
  const chapter = await Chapter.create({
    subject_id: math.id,
    chapter_number: 1,
    chapter_title: 'Số và chữ số',
    is_published: true
  });
  
  // 3. Tạo Lesson
  const lesson = await Lesson.create({
    chapter_id: chapter.id,
    lesson_number: 1,
    lesson_title: 'Đếm từ 1 đến 5',
    lesson_type: 'json_content',
    content_json: {
      type: 'counting_quiz',
      questions: [
        {
          question: 'Có bao nhiêu quả táo?',
          image: '/images/apples-3.png',
          answer: 3,
          options: [2, 3, 4, 5]
        }
      ]
    },
    is_published: true,
    is_free: true
  });
  
  // 4. Tạo Game
  const game = await Game.create({
    title: 'Trò chơi đếm số',
    url: 'https://games.senaai.tech/counting/',
    type: 'counting_quiz',
    is_active: true
  });
  
  console.log('✅ Sample content created successfully!');
}

seedSampleContent();

Chạy: node scripts/seed-sample-content.js


📞 Support

Nếu cần hỗ trợ thêm:

  1. Tạo Lesson Routes & Controller
  2. Tạo script seed data mẫu
  3. Cập nhật Swagger documentation
  4. Tạo frontend component để render lesson

Hãy cho tôi biết bạn muốn tôi implement phần nào! 🚀