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

1252 lines
32 KiB
Markdown

# 📚 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ụ**:
```json
{
"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ụ**:
```json
{
"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**:
```json
{
"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**:
```json
{
"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
```json
{
"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)
```json
{
"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
```bash
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)
```javascript
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**:
```json
{
"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**:
```json
{
"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
```json
{
"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)
```json
{
"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
```javascript
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
```javascript
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
```json
{
"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)
```json
{
"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
```javascript
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
```javascript
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
```javascript
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
```javascript
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
```javascript
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**:
```json
{
"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**:
```json
{
"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**:
```json
{
"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**:
```json
{
"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**:
```json
{
"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**:
```json
{
"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**:
```json
{
"lesson_title": "Đếm từ 1 đến 10 (mở rộng)",
"content_json": {
"type": "counting_quiz",
"questions": [
/* Thêm câu hi mi */
],
"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 Đủ
```javascript
// 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
```bash
node scripts/seed-sample-content.js
```
---
## 🎯 Workflow Thực Tế
### Scenario 1: Thêm môn học mới hoàn chỉnh
```javascript
// 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
```javascript
// 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_role``min_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_number``display_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](./CONTENT_MANAGEMENT_GUIDE.md) - Hướng dẫn tổng thể chi tiết
- [API Documentation](http://localhost:3000/api-docs) - 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! 🎉**