Files
sena_db_api_layer/SUBJECT_CHAPTER_LESSON_GUIDE.md
silverpro89 53d97ba5db update
2026-01-20 20:29:07 +07:00

32 KiB

📚 Hướng Dẫn Tạo và Quản Lý Subject, Chapter, Lesson

Tổng Quan

Hệ thống nội dung được tổ chức theo mô hình phân cấp 3 tầng:

Subject (Môn học)
    ├── Chapter 1 (Chương 1)
    │   ├── Lesson 1 (Bài học 1)
    │   ├── Lesson 2 (Bài học 2)
    │   └── Lesson 3 (Bài học 3)
    ├── Chapter 2 (Chương 2)
    │   ├── Lesson 4 (Bài học 4)
    │   └── Lesson 5 (Bài học 5)
    └── Chapter 3 (Chương 3)
        └── ...

Mối quan hệ

  • Subject (1) → Chapter (nhiều): Một môn học có nhiều chương
  • Chapter (1) → Lesson (nhiều): Một chương có nhiều bài học
  • LessonGame (optional): Bài học có thể tham chiếu đến Game engine

📊 Cấu Trúc Dữ Liệu

1. Subject (Môn học)

Bảng: subjects
Mục đích: Quản lý các môn học/giáo trình

Trường Kiểu Mô tả
id UUID ID duy nhất
subject_code VARCHAR(50) Mã môn học (unique)
subject_name VARCHAR(255) Tên môn học (tiếng Việt)
subject_name_en VARCHAR(255) Tên tiếng Anh
description TEXT Mô tả chi tiết
is_active BOOLEAN Đang hoạt động
is_premium BOOLEAN Nội dung premium
is_training BOOLEAN Đào tạo nhân sự
is_public BOOLEAN Tự học công khai
required_role VARCHAR(50) Role yêu cầu
min_subscription_tier VARCHAR(50) Gói tối thiểu

Ví dụ:

{
  "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_training": false,
  "is_public": true,
  "required_role": "student",
  "min_subscription_tier": "basic"
}

2. Chapter (Chương học)

Bảng: chapters
Mục đích: Chia môn học thành các chương

Trường Kiểu Mô tả
id UUID ID duy nhất
subject_id UUID FK → subjects.id
chapter_number INTEGER Số thứ tự chương
chapter_title VARCHAR(255) Tiêu đề chương
chapter_description TEXT Mô tả nội dung
duration_minutes INTEGER Thời lượng ước tính
is_published BOOLEAN Đã xuất bản
display_order INTEGER Thứ tự hiển thị

Ví dụ:

{
  "subject_id": "abc-123-xyz",
  "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
}

3. Lesson (Bài học)

Bảng: lessons
Mục đích: Nội dung giảng dạy cụ thể

Trường Kiểu Mô tả
id UUID ID duy nhất
chapter_id UUID FK → chapters.id
lesson_number INTEGER Số thứ tự bài học
lesson_title VARCHAR(255) Tiêu đề bài học
lesson_type ENUM json_content hoặc url_content
lesson_description TEXT Mô tả bài học
content_json JSON Nội dung tương tác
content_url VARCHAR(500) Link video/PDF
content_type VARCHAR(50) youtube, pdf, audio
duration_minutes INTEGER Thời lượng
is_published BOOLEAN Đã xuất bản
is_free BOOLEAN Học thử miễn phí
display_order INTEGER Thứ tự hiển thị
thumbnail_url VARCHAR(500) Ảnh thumbnail

Ví dụ Lesson JSON Content:

{
  "chapter_id": "def-456-uvw",
  "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",
    "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]
      }
    ],
    "pass_score": 70
  },
  "duration_minutes": 15,
  "is_published": true,
  "is_free": true,
  "display_order": 1
}

Ví dụ Lesson URL Content:

{
  "chapter_id": "def-456-uvw",
  "lesson_number": 2,
  "lesson_title": "Video: Hướng dẫn đếm số",
  "lesson_type": "url_content",
  "content_url": "https://www.youtube.com/watch?v=abc123",
  "content_type": "youtube",
  "duration_minutes": 10,
  "is_published": true,
  "is_free": false,
  "display_order": 2
}

🚀 Hướng Dẫn Tạo Nội Dung

Quy trình 3 bước

Bước 1: Tạo Subject (Môn học)
   ↓
Bước 2: Tạo Chapter (Chương) - liên kết với Subject
   ↓
Bước 3: Tạo Lesson (Bài học) - liên kết với Chapter

📝 Bước 1: Tạo Subject (Môn học)

API Endpoint

POST /api/subjects
Content-Type: application/json
Authorization: Bearer <token>

Request Body

{
  "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, bao gồm số học cơ bản, phép cộng trừ, hình học đơn giản",
  "is_active": true,
  "is_premium": false,
  "is_training": false,
  "is_public": true,
  "required_role": "student",
  "min_subscription_tier": "basic"
}

Response (201 Created)

{
  "success": true,
  "message": "Subject created successfully",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "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...",
    "is_active": true,
    "is_premium": false,
    "is_training": false,
    "is_public": true,
    "required_role": "student",
    "min_subscription_tier": "basic",
    "created_at": "2026-01-20T10:00:00.000Z",
    "updated_at": "2026-01-20T10:00:00.000Z"
  }
}

Ví dụ với cURL

curl -X POST http://localhost:3000/api/subjects \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "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
  }'

Ví dụ với Node.js (Sequelize)

const { Subject } = require('./models');

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_training: false,
  is_public: true,
  required_role: 'student',
  min_subscription_tier: 'basic'
});

console.log('Subject ID:', mathSubject.id);
// Lưu ID này để tạo Chapter

Các Subject mẫu khác

Tiếng Việt lớp 1:

{
  "subject_code": "VIET_G1",
  "subject_name": "Tiếng Việt lớp 1",
  "subject_name_en": "Vietnamese Grade 1",
  "description": "Học chữ cái, đọc, viết và hiểu văn bản đơn giản",
  "is_active": true,
  "is_premium": false,
  "is_public": true
}

Tiếng Anh lớp 1:

{
  "subject_code": "ENG_G1",
  "subject_name": "Tiếng Anh lớp 1",
  "subject_name_en": "English Grade 1",
  "description": "Làm quen với bảng chữ cái, từ vựng và câu giao tiếp cơ bản",
  "is_active": true,
  "is_premium": true,
  "is_public": false,
  "min_subscription_tier": "premium"
}

📖 Bước 2: Tạo Chapter (Chương)

API Endpoint

POST /api/chapters
Content-Type: application/json
Authorization: Bearer <token>

Request Body

{
  "subject_id": "550e8400-e29b-41d4-a716-446655440000",
  "chapter_number": 1,
  "chapter_title": "Số và chữ số",
  "chapter_description": "Làm quen với các số từ 1 đến 10, học đếm và nhận biết chữ số",
  "duration_minutes": 180,
  "is_published": true,
  "display_order": 1
}

Response (201 Created)

{
  "success": true,
  "message": "Chapter created successfully",
  "data": {
    "id": "660e8400-e29b-41d4-a716-446655440001",
    "subject_id": "550e8400-e29b-41d4-a716-446655440000",
    "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,
    "created_at": "2026-01-20T10:05:00.000Z",
    "updated_at": "2026-01-20T10:05:00.000Z"
  }
}

Ví dụ với Node.js

const { Chapter } = require('./models');

// Sử dụng subject_id từ bước 1
const chapter1 = await Chapter.create({
  subject_id: mathSubject.id,  // ID từ bước 1
  chapter_number: 1,
  chapter_title: 'Số và chữ số',
  chapter_description: 'Làm quen với các số từ 1 đến 10, học đếm và nhận biết chữ số',
  duration_minutes: 180,
  is_published: true,
  display_order: 1
});

console.log('Chapter ID:', chapter1.id);

// Tạo Chapter 2
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ách cộng các số từ 1 đến 10',
  duration_minutes: 240,
  is_published: true,
  display_order: 2
});

// Tạo Chapter 3
const chapter3 = await Chapter.create({
  subject_id: mathSubject.id,
  chapter_number: 3,
  chapter_title: 'Phép trừ trong phạm vi 10',
  chapter_description: 'Học cách trừ các số từ 1 đến 10',
  duration_minutes: 240,
  is_published: false,  // Chưa xuất bản
  display_order: 3
});

Tạo nhiều Chapter cùng lúc

const chapters = await Chapter.bulkCreate([
  {
    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
  },
  {
    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
  },
  {
    subject_id: mathSubject.id,
    chapter_number: 3,
    chapter_title: 'Phép trừ trong phạm vi 10',
    chapter_description: 'Học trừ các số từ 1 đến 10',
    duration_minutes: 240,
    is_published: true,
    display_order: 3
  }
]);

console.log(`Created ${chapters.length} chapters`);

📚 Bước 3: Tạo Lesson (Bài học)

3.1. Lesson với JSON Content (Tương tác)

API Endpoint

POST /api/lessons
Content-Type: application/json
Authorization: Bearer <token>

Request Body

{
  "chapter_id": "660e8400-e29b-41d4-a716-446655440001",
  "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 tương tác",
  "content_json": {
    "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"
}

Response (201 Created)

{
  "success": true,
  "message": "Lesson created successfully",
  "data": {
    "id": "770e8400-e29b-41d4-a716-446655440002",
    "chapter_id": "660e8400-e29b-41d4-a716-446655440001",
    "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...",
    "content_json": { /* ... */ },
    "content_url": null,
    "content_type": null,
    "duration_minutes": 15,
    "is_published": true,
    "is_free": true,
    "display_order": 1,
    "thumbnail_url": "https://cdn.senaai.tech/thumbnails/lesson-counting.jpg",
    "created_at": "2026-01-20T10:10:00.000Z",
    "updated_at": "2026-01-20T10:10:00.000Z"
  }
}

Ví dụ với Node.js

const { Lesson } = require('./models');

const lesson1 = await Lesson.create({
  chapter_id: chapter1.id,  // ID từ bước 2
  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 tương tác',
  content_json: {
    type: 'counting_quiz',  // Khớp với Game.type
    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'
});

console.log('Lesson ID:', lesson1.id);

3.2. 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,
  thumbnail_url: 'https://img.youtube.com/vi/abc123xyz/maxresdefault.jpg'
});

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',
  lesson_description: 'Tài liệu PDF hướng dẫn nhận biết và viết các số',
  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 File

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',
  lesson_description: 'Học cách phát âm đúng các số từ 1 đến 10',
  content_url: 'https://cdn.senaai.tech/audio/numbers-pronunciation.mp3',
  content_type: 'audio',
  duration_minutes: 8,
  is_published: true,
  is_free: true,
  display_order: 4
});

3.3. Tạo nhiều Lesson cùng lúc

const lessons = await Lesson.bulkCreate([
  {
    chapter_id: chapter1.id,
    lesson_number: 1,
    lesson_title: 'Đếm từ 1 đến 5',
    lesson_type: 'json_content',
    content_json: {
      type: 'counting_quiz',
      questions: [/* ... */]
    },
    duration_minutes: 15,
    is_published: true,
    is_free: true,
    display_order: 1
  },
  {
    chapter_id: chapter1.id,
    lesson_number: 2,
    lesson_title: 'Đếm từ 6 đến 10',
    lesson_type: 'json_content',
    content_json: {
      type: 'counting_quiz',
      questions: [/* ... */]
    },
    duration_minutes: 15,
    is_published: true,
    is_free: true,
    display_order: 2
  },
  {
    chapter_id: chapter1.id,
    lesson_number: 3,
    lesson_title: 'Video: Hướng dẫn đếm số',
    lesson_type: 'url_content',
    content_url: 'https://www.youtube.com/watch?v=abc123',
    content_type: 'youtube',
    duration_minutes: 10,
    is_published: true,
    is_free: false,
    display_order: 3
  }
]);

console.log(`Created ${lessons.length} lessons`);

🔍 Truy Vấn và Quản Lý

Lấy danh sách Subject

GET /api/subjects

Response:

{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "subject_code": "MATH_G1",
      "subject_name": "Toán lớp 1",
      "is_active": true,
      "chapter_count": 3
    }
  ]
}

Lấy chi tiết Subject

GET /api/subjects/:id

Response:

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "subject_code": "MATH_G1",
    "subject_name": "Toán lớp 1",
    "description": "Chương trình Toán học lớp 1...",
    "chapters": [
      {
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "chapter_number": 1,
        "chapter_title": "Số và chữ số",
        "lesson_count": 5
      }
    ]
  }
}

Lấy danh sách Chapter của Subject

GET /api/subjects/:subject_id/chapters

Lấy danh sách Lesson của Chapter

GET /api/chapters/:chapter_id/lessons

Response:

{
  "success": true,
  "data": [
    {
      "id": "770e8400-e29b-41d4-a716-446655440002",
      "lesson_number": 1,
      "lesson_title": "Đếm từ 1 đến 5",
      "lesson_type": "json_content",
      "duration_minutes": 15,
      "is_free": true,
      "thumbnail_url": "https://cdn.senaai.tech/thumbnails/lesson-counting.jpg"
    },
    {
      "id": "770e8400-e29b-41d4-a716-446655440003",
      "lesson_number": 2,
      "lesson_title": "Video: Hướng dẫn đếm số",
      "lesson_type": "url_content",
      "duration_minutes": 10,
      "is_free": false
    }
  ]
}

Lấy chi tiết Lesson

GET /api/lessons/:id

Response:

{
  "success": true,
  "data": {
    "id": "770e8400-e29b-41d4-a716-446655440002",
    "chapter_id": "660e8400-e29b-41d4-a716-446655440001",
    "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...",
    "content_json": {
      "type": "counting_quiz",
      "questions": [/* ... */],
      "pass_score": 70
    },
    "duration_minutes": 15,
    "is_published": true,
    "is_free": true,
    "chapter": {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "chapter_title": "Số và chữ số",
      "subject": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "subject_name": "Toán lớp 1"
      }
    }
  }
}

✏️ Cập Nhật Nội Dung

Cập nhật Subject

PUT /api/subjects/:id
Content-Type: application/json

Request:

{
  "subject_name": "Toán lớp 1 - Chương trình nâng cao",
  "description": "Chương trình Toán học lớp 1 nâng cao với nhiều bài tập thực hành",
  "is_premium": true,
  "min_subscription_tier": "premium"
}

Cập nhật Chapter

PUT /api/chapters/:id
Content-Type: application/json

Request:

{
  "chapter_title": "Số và chữ số (cập nhật)",
  "chapter_description": "Làm quen với các số từ 1 đến 20",
  "duration_minutes": 240,
  "is_published": true
}

Cập nhật Lesson

PUT /api/lessons/:id
Content-Type: application/json

Request:

{
  "lesson_title": "Đếm từ 1 đến 10 (mở rộng)",
  "content_json": {
    "type": "counting_quiz",
    "questions": [
      /* Thêm câu hỏi mới */
    ],
    "pass_score": 80
  },
  "duration_minutes": 20,
  "is_free": false
}

🗑️ Xóa Nội Dung

Xóa Subject

DELETE /api/subjects/:id

Lưu ý: Xóa Subject sẽ xóa tất cả Chapter và Lesson liên quan (nếu có cascade)

Xóa Chapter

DELETE /api/chapters/:id

Lưu ý: Xóa Chapter sẽ xóa tất cả Lesson liên quan

Xóa Lesson

DELETE /api/lessons/:id

📊 Script Tạo Dữ Liệu Mẫu Đầy Đủ

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

async function seedSampleContent() {
  try {
    console.log('🌱 Seeding sample content...');

    // 1. TẠO SUBJECT: 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_training: false,
      is_public: true,
      required_role: 'student',
      min_subscription_tier: 'basic'
    });
    console.log('✅ Created Subject: Toán lớp 1');

    // 2. TẠO CHAPTER 1: Số và chữ số
    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
    });
    console.log('✅ Created Chapter 1: Số và chữ số');

    // 3. TẠO LESSON cho Chapter 1
    await Lesson.bulkCreate([
      {
        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',
          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]
            }
          ],
          pass_score: 70
        },
        duration_minutes: 15,
        is_published: true,
        is_free: true,
        display_order: 1
      },
      {
        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=sample123',
        content_type: 'youtube',
        duration_minutes: 10,
        is_published: true,
        is_free: false,
        display_order: 2
      },
      {
        chapter_id: chapter1.id,
        lesson_number: 3,
        lesson_title: 'Đếm từ 6 đến 10',
        lesson_type: 'json_content',
        lesson_description: 'Tiếp tục học đếm các số từ 6 đến 10',
        content_json: {
          type: 'counting_quiz',
          theme: 'animals',
          questions: [
            {
              id: 1,
              question: 'Có bao nhiêu con mèo?',
              image: 'https://cdn.senaai.tech/images/cats-7.png',
              correct_answer: 7,
              options: [6, 7, 8, 9]
            },
            {
              id: 2,
              question: 'Có bao nhiêu con chó?',
              image: 'https://cdn.senaai.tech/images/dogs-10.png',
              correct_answer: 10,
              options: [8, 9, 10, 11]
            }
          ],
          pass_score: 70
        },
        duration_minutes: 15,
        is_published: true,
        is_free: true,
        display_order: 3
      }
    ]);
    console.log('✅ Created 3 Lessons for Chapter 1');

    // 4. TẠO CHAPTER 2: Phép cộng
    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
    });
    console.log('✅ Created Chapter 2: Phép cộng');

    // 5. TẠO LESSON cho Chapter 2
    await Lesson.bulkCreate([
      {
        chapter_id: chapter2.id,
        lesson_number: 1,
        lesson_title: 'Phép cộng cơ bản',
        lesson_type: 'json_content',
        lesson_description: 'Học phép cộng 2 số trong phạm vi 5',
        content_json: {
          type: 'math_practice',
          operation: 'addition',
          range: { min: 1, max: 5 },
          question_count: 10,
          show_solution_steps: true
        },
        duration_minutes: 20,
        is_published: true,
        is_free: true,
        display_order: 1
      },
      {
        chapter_id: chapter2.id,
        lesson_number: 2,
        lesson_title: 'Thực hành phép cộng',
        lesson_type: 'json_content',
        lesson_description: 'Luyện tập phép cộng trong phạm vi 10',
        content_json: {
          type: 'math_practice',
          operation: 'addition',
          range: { min: 1, max: 10 },
          question_count: 20,
          show_solution_steps: true,
          allow_calculator: false
        },
        duration_minutes: 25,
        is_published: true,
        is_free: false,
        display_order: 2
      }
    ]);
    console.log('✅ Created 2 Lessons for Chapter 2');

    // 6. TẠO SUBJECT: Tiếng Việt lớp 1
    const vietnameseSubject = await Subject.create({
      subject_code: 'VIET_G1',
      subject_name: 'Tiếng Việt lớp 1',
      subject_name_en: 'Vietnamese Grade 1',
      description: 'Học chữ cái, đọc, viết và hiểu văn bản đơn giản',
      is_active: true,
      is_premium: false,
      is_public: true
    });
    console.log('✅ Created Subject: Tiếng Việt lớp 1');

    // 7. TẠO CHAPTER cho Tiếng Việt
    const vnChapter1 = await Chapter.create({
      subject_id: vietnameseSubject.id,
      chapter_number: 1,
      chapter_title: 'Bảng chữ cái tiếng Việt',
      chapter_description: 'Học 29 chữ cái tiếng Việt',
      duration_minutes: 300,
      is_published: true,
      display_order: 1
    });
    console.log('✅ Created Chapter: Bảng chữ cái tiếng Việt');

    // 8. TẠO LESSON cho Tiếng Việt
    await Lesson.create({
      chapter_id: vnChapter1.id,
      lesson_number: 1,
      lesson_title: 'Học chữ A, B, C',
      lesson_type: 'json_content',
      lesson_description: 'Làm quen với 3 chữ cái đầu tiên',
      content_json: {
        type: 'alphabet_learning',
        letters: ['A', 'B', 'C'],
        activities: ['recognize', 'write', 'pronounce']
      },
      duration_minutes: 30,
      is_published: true,
      is_free: true,
      display_order: 1
    });
    console.log('✅ Created Lesson: Học chữ A, B, C');

    console.log('\n🎉 Sample content seeded successfully!');
    console.log('\nSummary:');
    console.log('- Subjects: 2 (Toán lớp 1, Tiếng Việt lớp 1)');
    console.log('- Chapters: 3 (Số và chữ số, Phép cộng, Bảng chữ cái)');
    console.log('- Lessons: 6');

  } catch (error) {
    console.error('❌ Error seeding data:', error);
    throw error;
  }
}

// Chạy script
seedSampleContent()
  .then(() => {
    console.log('\n✅ Done!');
    process.exit(0);
  })
  .catch(err => {
    console.error('\n❌ Failed:', err);
    process.exit(1);
  });

Chạy script

node scripts/seed-sample-content.js

🎯 Workflow Thực Tế

Scenario 1: Thêm môn học mới hoàn chỉnh

// 1. Tạo Subject
const englishSubject = await Subject.create({
  subject_code: 'ENG_G1',
  subject_name: 'Tiếng Anh lớp 1',
  is_active: true,
  is_premium: true,
  is_public: false
});

// 2. Tạo nhiều Chapter
const chapters = await Chapter.bulkCreate([
  {
    subject_id: englishSubject.id,
    chapter_number: 1,
    chapter_title: 'Alphabet',
    is_published: true,
    display_order: 1
  },
  {
    subject_id: englishSubject.id,
    chapter_number: 2,
    chapter_title: 'Greetings',
    is_published: true,
    display_order: 2
  }
]);

// 3. Tạo Lesson cho mỗi Chapter
for (const chapter of chapters) {
  await Lesson.create({
    chapter_id: chapter.id,
    lesson_number: 1,
    lesson_title: `${chapter.chapter_title} - Lesson 1`,
    lesson_type: 'json_content',
    content_json: { /* ... */ },
    is_published: true,
    display_order: 1
  });
}

Scenario 2: Sao chép cấu trúc từ môn khác

// Sao chép cấu trúc từ Toán lớp 1 sang Toán lớp 2
const mathG1 = await Subject.findOne({ 
  where: { subject_code: 'MATH_G1' },
  include: [{ 
    model: Chapter, 
    include: [Lesson] 
  }]
});

const mathG2 = await Subject.create({
  subject_code: 'MATH_G2',
  subject_name: 'Toán lớp 2',
  // ... các trường khác
});

// Copy chapters và lessons
for (const chapter of mathG1.Chapters) {
  const newChapter = await Chapter.create({
    subject_id: mathG2.id,
    chapter_number: chapter.chapter_number,
    chapter_title: chapter.chapter_title,
    // ... các trường khác
  });

  for (const lesson of chapter.Lessons) {
    await Lesson.create({
      chapter_id: newChapter.id,
      lesson_number: lesson.lesson_number,
      lesson_title: lesson.lesson_title,
      // ... các trường khác
    });
  }
}

Checklist Tạo Nội Dung

Khi tạo Subject:

  • subject_code là duy nhất (unique)
  • subject_name rõ ràng, dễ hiểu
  • Chọn đúng is_premium, is_public, is_training
  • Thiết lập required_rolemin_subscription_tier phù hợp

Khi tạo Chapter:

  • Liên kết đúng subject_id
  • chapter_number theo thứ tự 1, 2, 3...
  • display_order phản ánh thứ tự hiển thị
  • duration_minutes hợp lý với số lượng lesson
  • Đặt is_published = true khi sẵn sàng

Khi tạo Lesson:

  • Liên kết đúng chapter_id
  • Chọn đúng lesson_type: json_content hoặc url_content
  • Nếu json_content: content_json.type phải khớp với Game.type
  • Nếu url_content: Kiểm tra URL hợp lệ
  • lesson_numberdisplay_order đúng thứ tự
  • Thiết lập is_free cho bài học thử miễn phí
  • Thêm thumbnail_url để hiển thị đẹp

🚨 Lỗi Thường Gặp

1. Lỗi Foreign Key

Error: Cannot create chapter: subject_id does not exist

Giải pháp: Kiểm tra subject_id có tồn tại trong bảng subjects

2. Lỗi Unique Constraint

Error: subject_code must be unique

Giải pháp: Chọn subject_code khác hoặc kiểm tra Subject đã tồn tại

3. Lỗi JSON Content Type

Warning: No game found for type: xxx

Giải pháp: Tạo Game với type khớp với content_json.type

4. Lỗi Validation

Error: lesson_type must be either 'json_content' or 'url_content'

Giải pháp: Kiểm tra giá trị lesson_type hợp lệ


📚 Tài Liệu Liên Quan

  • CONTENT_MANAGEMENT_GUIDE.md - Hướng dẫn tổng thể chi tiết
  • API Documentation - Swagger UI
  • models/Subject.js - Model Subject
  • models/Chapter.js - Model Chapter
  • models/Lesson.js - Model Lesson
  • controllers/lessonController.js - Lesson Controller
  • scripts/seed-sample-content.js - Script tạo dữ liệu mẫu

💡 Tips & Best Practices

  1. Luôn tạo theo thứ tự: Subject → Chapter → Lesson
  2. Sử dụng display_order: Kiểm soát thứ tự hiển thị độc lập với số thứ tự
  3. Draft trước, publish sau: Tạo với is_published: false, kiểm tra kỹ rồi mới publish
  4. Consistent naming: Đặt tên theo quy ước rõ ràng (MATH_G1, VIET_G2)
  5. Free content: Luôn có một số bài học miễn phí để học viên trải nghiệm
  6. Thumbnail: Thêm thumbnail cho lesson để tăng trải nghiệm UI
  7. Metadata: Sử dụng description để mô tả đầy đủ nội dung
  8. Testing: Test kỹ từng lesson trước khi publish

Chúc bạn tạo nội dung thành công! 🎉