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
- Lesson → Game (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_codelà duy nhất (unique)subject_namerõ ràng, dễ hiểu- Chọn đúng
is_premium,is_public,is_training - Thiết lập
required_rolevàmin_subscription_tierphù hợp
Khi tạo Chapter:
- Liên kết đúng
subject_id chapter_numbertheo thứ tự 1, 2, 3...display_orderphản ánh thứ tự hiển thịduration_minuteshợp lý với số lượng lesson- Đặt
is_published = truekhi sẵn sàng
Khi tạo Lesson:
- Liên kết đúng
chapter_id - Chọn đúng
lesson_type:json_contenthoặcurl_content - Nếu
json_content:content_json.typephải khớp vớiGame.type - Nếu
url_content: Kiểm tra URL hợp lệ lesson_numbervàdisplay_orderđúng thứ tự- Thiết lập
is_freecho 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 Subjectmodels/Chapter.js- Model Chaptermodels/Lesson.js- Model Lessoncontrollers/lessonController.js- Lesson Controllerscripts/seed-sample-content.js- Script tạo dữ liệu mẫu
💡 Tips & Best Practices
- Luôn tạo theo thứ tự: Subject → Chapter → Lesson
- Sử dụng
display_order: Kiểm soát thứ tự hiển thị độc lập với số thứ tự - Draft trước, publish sau: Tạo với
is_published: false, kiểm tra kỹ rồi mới publish - Consistent naming: Đặt tên theo quy ước rõ ràng (
MATH_G1,VIET_G2) - Free content: Luôn có một số bài học miễn phí để học viên trải nghiệm
- Thumbnail: Thêm thumbnail cho lesson để tăng trải nghiệm UI
- Metadata: Sử dụng
descriptionđể mô tả đầy đủ nội dung - Testing: Test kỹ từng lesson trước khi publish
Chúc bạn tạo nội dung thành công! 🎉