2026-01-08 03:58:29 +00:00
|
|
|
# Agent Skills - E-Learning Backend Development
|
|
|
|
|
|
|
|
|
|
> คู่มือสำหรับ AI Agent ในการพัฒนา Backend ของระบบ E-Learning Platform
|
2026-01-12 03:36:54 +00:00
|
|
|
> **Tech Stack**: Node.js + Express + Prisma + PostgreSQL + MinIO (S3)
|
2026-01-08 03:58:29 +00:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🎯 Overview
|
|
|
|
|
|
|
|
|
|
ระบบ E-Learning Platform เป็น REST API ที่รองรับ:
|
|
|
|
|
- **3 บทบาทหลัก**: Admin, Instructor, Student
|
|
|
|
|
- **107+ endpoints** ครอบคลุมการจัดการคอร์ส, บทเรียน, แบบทดสอบ, ประกาศ, รายงาน
|
|
|
|
|
- **Multi-language support**: Thai (th) และ English (en)
|
|
|
|
|
- **File management**: Video streaming, attachments, certificates
|
|
|
|
|
- **Real-time features**: Video progress tracking, quiz attempts
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📚 Core Concepts
|
|
|
|
|
|
|
|
|
|
### 1. Multi-Language Data Structure
|
|
|
|
|
ข้อมูลที่แสดงผลต้องรองรับ 2 ภาษา:
|
|
|
|
|
```javascript
|
|
|
|
|
{
|
|
|
|
|
"title": {
|
|
|
|
|
"th": "Python สำหรับผู้เริ่มต้น",
|
|
|
|
|
"en": "Python for Beginners"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. Role-Based Access Control (RBAC)
|
|
|
|
|
```javascript
|
|
|
|
|
// 3 บทบาทหลัก
|
|
|
|
|
- ADMIN: จัดการระบบทั้งหมด
|
|
|
|
|
- INSTRUCTOR: สร้างและจัดการคอร์สของตนเอง
|
|
|
|
|
- STUDENT: ลงทะเบียนและเรียนคอร์ส
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3. Course Hierarchy
|
|
|
|
|
```
|
|
|
|
|
Course
|
|
|
|
|
└─ Chapters (บท)
|
|
|
|
|
└─ Lessons (บทเรียน)
|
|
|
|
|
├─ Video (optional)
|
|
|
|
|
├─ Attachments (0-10 files)
|
|
|
|
|
└─ Quiz (optional)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4. Lesson Prerequisites
|
|
|
|
|
- Sequential lessons: ต้องเรียนตามลำดับ
|
|
|
|
|
- Prerequisite lessons: ระบุบทเรียนที่ต้องจบก่อน
|
|
|
|
|
- Quiz requirements: ต้องผ่านแบบทดสอบก่อน
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🔐 Authentication & Authorization
|
|
|
|
|
|
|
|
|
|
### JWT Token Structure
|
|
|
|
|
```javascript
|
|
|
|
|
{
|
|
|
|
|
userId: 123,
|
|
|
|
|
username: "john_doe",
|
|
|
|
|
email: "john@example.com",
|
|
|
|
|
role: "STUDENT",
|
|
|
|
|
iat: 1234567890,
|
|
|
|
|
exp: 1234654290
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Login Methods
|
|
|
|
|
- **Username + Password**
|
|
|
|
|
- **Email + Password**
|
|
|
|
|
|
|
|
|
|
### Key Endpoints
|
|
|
|
|
```
|
|
|
|
|
POST /api/auth/register
|
|
|
|
|
POST /api/auth/login
|
|
|
|
|
POST /api/auth/logout
|
|
|
|
|
POST /api/auth/refresh
|
|
|
|
|
POST /api/auth/password/reset-request
|
|
|
|
|
POST /api/auth/password/reset
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 👥 User Management
|
|
|
|
|
|
|
|
|
|
### User Profile Structure
|
|
|
|
|
```javascript
|
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
username: "john_doe",
|
|
|
|
|
email: "john@example.com",
|
|
|
|
|
role: { code: "STUDENT", name: {...} },
|
|
|
|
|
profile: {
|
|
|
|
|
prefix: "Mr.",
|
|
|
|
|
first_name: "John",
|
|
|
|
|
last_name: "Doe",
|
|
|
|
|
phone: "0812345678",
|
|
|
|
|
avatar_url: "https://..."
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Key Endpoints
|
|
|
|
|
```
|
|
|
|
|
GET /api/users/me
|
|
|
|
|
GET /api/users/me/profile
|
|
|
|
|
PUT /api/users/me/profile
|
|
|
|
|
PUT /api/users/me/password
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📚 Course Management
|
|
|
|
|
|
|
|
|
|
### Course Status Flow
|
|
|
|
|
```
|
|
|
|
|
DRAFT → PENDING → APPROVED/REJECTED → PUBLISHED
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Course Structure
|
|
|
|
|
```javascript
|
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
title: { th: "...", en: "..." },
|
|
|
|
|
description: { th: "...", en: "..." },
|
|
|
|
|
thumbnail: "https://...",
|
|
|
|
|
price: 990,
|
|
|
|
|
is_free: false,
|
|
|
|
|
have_certificate: true,
|
|
|
|
|
status: "APPROVED",
|
|
|
|
|
category_id: 1,
|
|
|
|
|
instructors: [
|
|
|
|
|
{ user_id: 5, is_primary: true },
|
|
|
|
|
{ user_id: 10, is_primary: false }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Multi-Instructor Support
|
|
|
|
|
- **Primary Instructor**: สามารถจัดการทุกอย่างในคอร์ส
|
|
|
|
|
- **Co-Instructors**: ช่วยสอนและจัดการเนื้อหา
|
|
|
|
|
- ห้ามลบ instructor คนสุดท้าย
|
|
|
|
|
|
|
|
|
|
### Key Endpoints
|
|
|
|
|
|
|
|
|
|
**Public:**
|
|
|
|
|
```
|
|
|
|
|
GET /api/courses
|
|
|
|
|
GET /api/courses/:courseId
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Student:**
|
|
|
|
|
```
|
|
|
|
|
POST /api/students/courses/:courseId/enroll
|
|
|
|
|
GET /api/students/courses
|
|
|
|
|
GET /api/students/courses/:courseId/learn
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Instructor:**
|
|
|
|
|
```
|
|
|
|
|
POST /api/instructor/courses
|
|
|
|
|
GET /api/instructor/courses
|
|
|
|
|
PUT /api/instructor/courses/:courseId
|
|
|
|
|
DELETE /api/instructor/courses/:courseId
|
|
|
|
|
POST /api/instructor/courses/:courseId/submit
|
|
|
|
|
POST /api/instructor/courses/:courseId/clone
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Instructors Management:**
|
|
|
|
|
```
|
|
|
|
|
GET /api/instructor/courses/:courseId/instructors
|
|
|
|
|
POST /api/instructor/courses/:courseId/instructors
|
|
|
|
|
DELETE /api/instructor/courses/:courseId/instructors/:userId
|
|
|
|
|
PUT /api/instructor/courses/:courseId/instructors/:userId/primary
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📖 Chapters & Lessons
|
|
|
|
|
|
|
|
|
|
### Lesson Types
|
|
|
|
|
- **video**: วีดีโอบทเรียน
|
|
|
|
|
- **text**: เนื้อหาข้อความ
|
|
|
|
|
- **pdf**: เอกสาร PDF
|
|
|
|
|
- **quiz**: แบบทดสอบ
|
|
|
|
|
|
|
|
|
|
### Create Lesson (One Request)
|
|
|
|
|
สามารถสร้าง lesson พร้อม video และ attachments ในครั้งเดียว:
|
|
|
|
|
|
|
|
|
|
```http
|
|
|
|
|
POST /api/instructor/courses/:courseId/chapters/:chapterId/lessons
|
|
|
|
|
Content-Type: multipart/form-data
|
|
|
|
|
|
|
|
|
|
title_th: "บทที่ 1"
|
|
|
|
|
title_en: "Lesson 1"
|
|
|
|
|
type: "video"
|
|
|
|
|
video: <file>
|
|
|
|
|
attachments[]: <file1>
|
|
|
|
|
attachments[]: <file2>
|
|
|
|
|
descriptions[0][th]: "สไลด์"
|
|
|
|
|
descriptions[0][en]: "Slides"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Lesson Prerequisites
|
|
|
|
|
```javascript
|
|
|
|
|
{
|
|
|
|
|
is_sequential: true, // ต้องเรียนตามลำดับ
|
|
|
|
|
prerequisite_lesson_ids: [1, 2], // ต้องจบบทเรียนนี้ก่อน
|
|
|
|
|
require_pass_quiz: true // ต้องผ่านแบบทดสอบ
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Key Endpoints
|
|
|
|
|
|
|
|
|
|
**Chapters:**
|
|
|
|
|
```
|
|
|
|
|
POST /api/instructor/courses/:courseId/chapters
|
|
|
|
|
PUT /api/instructor/courses/:courseId/chapters/:chapterId
|
|
|
|
|
DELETE /api/instructor/courses/:courseId/chapters/:chapterId
|
|
|
|
|
PUT /api/instructor/courses/:courseId/chapters/reorder
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Lessons:**
|
|
|
|
|
```
|
|
|
|
|
POST /api/instructor/courses/:courseId/chapters/:chapterId/lessons
|
|
|
|
|
PUT /api/instructor/courses/:courseId/lessons/:lessonId
|
|
|
|
|
DELETE /api/instructor/courses/:courseId/lessons/:lessonId
|
|
|
|
|
PUT /api/instructor/courses/:courseId/lessons/reorder
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Prerequisites:**
|
|
|
|
|
```
|
|
|
|
|
GET /api/instructor/courses/:courseId/lessons/:lessonId/prerequisites
|
|
|
|
|
POST /api/instructor/courses/:courseId/lessons/:lessonId/prerequisites
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📎 Attachments
|
|
|
|
|
|
|
|
|
|
### File Limits
|
|
|
|
|
- **Max file size**: 100 MB per file
|
|
|
|
|
- **Max attachments**: 10 files per lesson
|
|
|
|
|
- **Total size**: 500 MB per lesson
|
|
|
|
|
|
|
|
|
|
### Allowed File Types
|
|
|
|
|
- **Documents**: PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX
|
|
|
|
|
- **Archives**: ZIP, RAR, 7Z
|
|
|
|
|
- **Images**: JPG, PNG, GIF
|
|
|
|
|
- **Text**: TXT, CSV
|
|
|
|
|
|
|
|
|
|
### Key Endpoints
|
|
|
|
|
```
|
|
|
|
|
POST /api/instructor/courses/:courseId/lessons/:lessonId/attachments
|
|
|
|
|
GET /api/instructor/courses/:courseId/lessons/:lessonId/attachments
|
|
|
|
|
PUT /api/instructor/courses/:courseId/lessons/:lessonId/attachments/:attachmentId
|
|
|
|
|
DELETE /api/instructor/courses/:courseId/lessons/:lessonId/attachments/:attachmentId
|
|
|
|
|
PUT /api/instructor/courses/:courseId/lessons/:lessonId/attachments/reorder
|
|
|
|
|
GET /api/students/lessons/:lessonId/attachments/:attachmentId/download
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📹 Video Progress Tracking
|
|
|
|
|
|
|
|
|
|
### Auto-Save Progress
|
|
|
|
|
- บันทึกทุก **5 วินาที** ขณะดูวีดีโอ
|
|
|
|
|
- Auto-complete เมื่อดู **≥ 90%**
|
|
|
|
|
- Resume จากตำแหน่งล่าสุด
|
|
|
|
|
|
|
|
|
|
### Data Structure
|
|
|
|
|
```javascript
|
|
|
|
|
{
|
|
|
|
|
lesson_id: 5,
|
|
|
|
|
video_progress_seconds: 450,
|
|
|
|
|
video_duration_seconds: 900,
|
|
|
|
|
video_progress_percentage: 50.00,
|
|
|
|
|
is_completed: false,
|
|
|
|
|
last_watched_at: "2024-12-24T14:00:00Z"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Key Endpoints
|
|
|
|
|
```
|
|
|
|
|
POST /api/students/lessons/:lessonId/progress
|
|
|
|
|
GET /api/students/lessons/:lessonId/progress
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🎯 Quizzes & Assessments
|
|
|
|
|
|
|
|
|
|
### Quiz Configuration
|
|
|
|
|
```javascript
|
|
|
|
|
{
|
|
|
|
|
title: { th: "...", en: "..." },
|
|
|
|
|
passing_score: 70, // คะแนนผ่าน (%)
|
|
|
|
|
time_limit: 30, // นาที
|
|
|
|
|
max_attempts: 3, // จำนวนครั้งที่ทำได้
|
|
|
|
|
cooldown_minutes: 60, // ระยะเวลารอระหว่างครั้ง
|
|
|
|
|
score_policy: "HIGHEST", // HIGHEST, LATEST, FIRST, AVERAGE
|
|
|
|
|
shuffle_questions: true,
|
|
|
|
|
shuffle_choices: true,
|
|
|
|
|
show_answers_after_completion: true
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Question Types
|
|
|
|
|
- **multiple_choice**: เลือกตอบ (1 คำตอบ)
|
|
|
|
|
- **true_false**: จริง/เท็จ
|
|
|
|
|
- **multiple_select**: เลือกหลายคำตอบ
|
|
|
|
|
|
|
|
|
|
### Key Endpoints
|
|
|
|
|
|
|
|
|
|
**Instructor:**
|
|
|
|
|
```
|
|
|
|
|
POST /api/instructor/courses/:courseId/lessons/:lessonId/quiz
|
|
|
|
|
PUT /api/instructor/quizzes/:quizId
|
|
|
|
|
DELETE /api/instructor/quizzes/:quizId
|
|
|
|
|
POST /api/instructor/quizzes/:quizId/questions
|
|
|
|
|
PUT /api/instructor/quizzes/:quizId/questions/:questionId
|
|
|
|
|
DELETE /api/instructor/quizzes/:quizId/questions/:questionId
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Student:**
|
|
|
|
|
```
|
|
|
|
|
GET /api/students/quizzes/:quizId
|
|
|
|
|
POST /api/students/quizzes/:quizId/submit
|
|
|
|
|
GET /api/students/quizzes/:quizId/attempts
|
|
|
|
|
GET /api/students/quizzes/:quizId/attempts/:attemptId
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📢 Announcements
|
|
|
|
|
|
|
|
|
|
### Announcement Structure
|
|
|
|
|
```javascript
|
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
course_id: 1,
|
|
|
|
|
title: { th: "...", en: "..." },
|
|
|
|
|
content: { th: "...", en: "..." },
|
|
|
|
|
is_pinned: true,
|
|
|
|
|
attachments: [...],
|
|
|
|
|
created_at: "2024-12-23T10:00:00Z"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Key Endpoints
|
|
|
|
|
|
|
|
|
|
**Student:**
|
|
|
|
|
```
|
|
|
|
|
GET /api/students/courses/:courseId/announcements
|
|
|
|
|
GET /api/students/courses/:courseId/announcements/:announcementId
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Instructor:**
|
|
|
|
|
```
|
|
|
|
|
GET /api/instructor/courses/:courseId/announcements
|
|
|
|
|
POST /api/instructor/courses/:courseId/announcements
|
|
|
|
|
PUT /api/instructor/courses/:courseId/announcements/:announcementId
|
|
|
|
|
DELETE /api/instructor/courses/:courseId/announcements/:announcementId
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📊 Progress & Certificates
|
|
|
|
|
|
|
|
|
|
### Course Progress Calculation
|
|
|
|
|
```javascript
|
|
|
|
|
progress_percentage = (completed_lessons / total_lessons) * 100
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Certificate Issuance
|
|
|
|
|
- ออกอัตโนมัติเมื่อจบคอร์ส 100%
|
|
|
|
|
- เฉพาะคอร์สที่ `have_certificate = true`
|
|
|
|
|
- เก็บไฟล์ PDF ใน S3
|
|
|
|
|
|
|
|
|
|
### Key Endpoints
|
|
|
|
|
```
|
|
|
|
|
GET /api/students/progress
|
|
|
|
|
GET /api/students/courses/:courseId/progress
|
|
|
|
|
GET /api/students/courses/:courseId/certificate
|
|
|
|
|
GET /api/students/certificates
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🔄 Course Cloning
|
|
|
|
|
|
|
|
|
|
### What Gets Cloned
|
|
|
|
|
✅ **Copied:**
|
|
|
|
|
- Course info (title, description, price)
|
|
|
|
|
- All chapters and lessons
|
|
|
|
|
- All quizzes and questions
|
|
|
|
|
- All attachments (files copied to new location)
|
|
|
|
|
- Lesson prerequisites
|
|
|
|
|
- Sort orders
|
|
|
|
|
|
|
|
|
|
❌ **NOT Copied:**
|
|
|
|
|
- Enrollments
|
|
|
|
|
- Student progress
|
|
|
|
|
- Reviews/ratings
|
|
|
|
|
- Approval status (new course = DRAFT)
|
|
|
|
|
|
|
|
|
|
### Key Endpoint
|
|
|
|
|
```
|
|
|
|
|
POST /api/instructor/courses/:courseId/clone
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🔒 Access Control & Permissions
|
|
|
|
|
|
|
|
|
|
### Lesson Access Check
|
|
|
|
|
```
|
|
|
|
|
GET /api/students/courses/:courseId/lessons/:lessonId/access-check
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Response (Locked):**
|
|
|
|
|
```javascript
|
|
|
|
|
{
|
|
|
|
|
can_access: false,
|
|
|
|
|
reason: "incomplete_prerequisites",
|
|
|
|
|
required_lessons: [1, 2],
|
|
|
|
|
missing_lessons: [2],
|
|
|
|
|
next_available_lesson: { id: 2, title: "..." }
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Ownership Validation
|
|
|
|
|
```javascript
|
|
|
|
|
// Middleware ตรวจสอบ
|
|
|
|
|
if (role === 'INSTRUCTOR' && course.instructorId !== userId) {
|
|
|
|
|
return 403;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📈 Reports & Analytics
|
|
|
|
|
|
|
|
|
|
### Student Reports
|
|
|
|
|
```
|
|
|
|
|
GET /api/students/progress
|
|
|
|
|
GET /api/students/courses/:courseId/progress
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Instructor Reports
|
|
|
|
|
```
|
|
|
|
|
GET /api/instructor/courses/:courseId/statistics
|
|
|
|
|
GET /api/instructor/courses/:courseId/students
|
|
|
|
|
GET /api/instructor/courses/:courseId/students/export
|
|
|
|
|
GET /api/instructor/dashboard
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Admin Dashboard
|
|
|
|
|
```
|
|
|
|
|
GET /api/admin/statistics
|
|
|
|
|
GET /api/admin/reports/revenue
|
|
|
|
|
GET /api/admin/reports/users
|
|
|
|
|
GET /api/admin/reports/courses
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ⚠️ Edge Cases & Best Practices
|
|
|
|
|
|
|
|
|
|
### 1. Soft Delete > Hard Delete
|
|
|
|
|
```javascript
|
|
|
|
|
// ใช้ flag แทนการลบจริง
|
|
|
|
|
{ isDeleted: true, deletedAt: "..." }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. Concurrent Video Progress
|
|
|
|
|
```javascript
|
|
|
|
|
// ใช้ last_watched_at ตัดสินว่า session ไหนล่าสุด
|
|
|
|
|
if (newProgress.last_watched_at > currentProgress.last_watched_at) {
|
|
|
|
|
updateProgress(newProgress);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3. Quiz Attempt Validation
|
|
|
|
|
```javascript
|
|
|
|
|
// ตรวจสอบก่อนให้ทำแบบทดสอบ
|
|
|
|
|
if (attempts >= maxAttempts) return error;
|
|
|
|
|
if (lastAttempt + cooldown > now) return error;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4. Lesson Lock Check
|
|
|
|
|
```javascript
|
|
|
|
|
// ตรวจสอบที่ Backend เสมอ
|
|
|
|
|
if (!allPreviousCompleted) {
|
|
|
|
|
return { access: false, nextAvailableLesson };
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5. Multi-Instructor Constraints
|
|
|
|
|
```javascript
|
|
|
|
|
// ห้ามลบ instructor คนสุดท้าย
|
|
|
|
|
if (instructors.length === 1) {
|
|
|
|
|
return error("Cannot remove last instructor");
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🗄️ Database Schema (Prisma)
|
|
|
|
|
|
|
|
|
|
### Key Models
|
|
|
|
|
```prisma
|
|
|
|
|
model User {
|
|
|
|
|
id Int @id @default(autoincrement())
|
|
|
|
|
username String @unique
|
|
|
|
|
email String @unique
|
|
|
|
|
password String
|
|
|
|
|
role Role @relation(...)
|
|
|
|
|
profile Profile?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model Course {
|
|
|
|
|
id Int @id @default(autoincrement())
|
|
|
|
|
title Json // { th: "", en: "" }
|
|
|
|
|
description Json
|
|
|
|
|
price Decimal
|
|
|
|
|
is_free Boolean
|
|
|
|
|
have_certificate Boolean
|
|
|
|
|
status CourseStatus
|
|
|
|
|
chapters Chapter[]
|
|
|
|
|
instructors CourseInstructor[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model Lesson {
|
|
|
|
|
id Int @id @default(autoincrement())
|
|
|
|
|
title Json
|
|
|
|
|
content Json
|
|
|
|
|
type LessonType
|
|
|
|
|
video_url String?
|
|
|
|
|
is_sequential Boolean
|
|
|
|
|
prerequisite_lessons LessonPrerequisite[]
|
|
|
|
|
attachments Attachment[]
|
|
|
|
|
quiz Quiz?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model Quiz {
|
|
|
|
|
id Int @id @default(autoincrement())
|
|
|
|
|
title Json
|
|
|
|
|
passing_score Int
|
|
|
|
|
time_limit Int?
|
|
|
|
|
max_attempts Int?
|
|
|
|
|
cooldown_minutes Int?
|
|
|
|
|
score_policy ScorePolicy
|
|
|
|
|
show_answers_after_completion Boolean
|
|
|
|
|
questions Question[]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🔧 Technical Implementation
|
|
|
|
|
|
|
|
|
|
### File Upload (MinIO/S3)
|
|
|
|
|
```javascript
|
|
|
|
|
// Video upload
|
|
|
|
|
const videoPath = `courses/${courseId}/lessons/${lessonId}/video.mp4`;
|
|
|
|
|
await s3.upload(videoPath, videoFile);
|
|
|
|
|
|
|
|
|
|
// Attachment upload
|
|
|
|
|
const attachmentPath = `lessons/${lessonId}/attachments/${filename}`;
|
|
|
|
|
await s3.upload(attachmentPath, file);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Video Progress Auto-Save
|
|
|
|
|
```javascript
|
|
|
|
|
// Frontend: Save ทุก 5 วินาที
|
|
|
|
|
setInterval(() => {
|
|
|
|
|
if (!video.paused) {
|
|
|
|
|
saveProgress(video.currentTime, video.duration);
|
|
|
|
|
}
|
|
|
|
|
}, 5000);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### JWT Middleware
|
|
|
|
|
```javascript
|
|
|
|
|
const authMiddleware = async (req, res, next) => {
|
|
|
|
|
const token = req.headers.authorization?.split(' ')[1];
|
|
|
|
|
const decoded = jwt.verify(token, JWT_SECRET);
|
|
|
|
|
req.user = await prisma.user.findUnique({ where: { id: decoded.userId } });
|
|
|
|
|
next();
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Role-Based Middleware
|
|
|
|
|
```javascript
|
|
|
|
|
const requireRole = (roles) => (req, res, next) => {
|
|
|
|
|
if (!roles.includes(req.user.role.code)) {
|
|
|
|
|
return res.status(403).json({ error: "Forbidden" });
|
|
|
|
|
}
|
|
|
|
|
next();
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📋 Error Codes
|
|
|
|
|
|
|
|
|
|
| Code | Description |
|
|
|
|
|
|------|-------------|
|
|
|
|
|
| `INVALID_CREDENTIALS` | อีเมล/รหัสผ่านไม่ถูกต้อง |
|
|
|
|
|
| `ALREADY_ENROLLED` | ลงทะเบียนซ้ำ |
|
|
|
|
|
| `LESSON_LOCKED` | บทเรียนถูกล็อค |
|
|
|
|
|
| `INCOMPLETE_PREREQUISITES` | ยังไม่จบบทเรียนที่ต้องเรียนก่อน |
|
|
|
|
|
| `QUIZ_NOT_PASSED` | ยังไม่ผ่านแบบทดสอบ |
|
|
|
|
|
| `MAX_ATTEMPTS_EXCEEDED` | ทำแบบทดสอบเกินจำนวนครั้ง |
|
|
|
|
|
| `COOLDOWN_ACTIVE` | ต้องรอก่อนทำแบบทดสอบอีกครั้ง |
|
|
|
|
|
| `FILE_TOO_LARGE` | ไฟล์ใหญ่เกินไป |
|
|
|
|
|
| `INVALID_FILE_TYPE` | ประเภทไฟล์ไม่รองรับ |
|
|
|
|
|
| `COURSE_NOT_FOUND` | ไม่พบคอร์ส |
|
|
|
|
|
| `UNAUTHORIZED` | ไม่มีสิทธิ์เข้าถึง |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🎯 Development Checklist
|
|
|
|
|
|
|
|
|
|
### สำหรับทุก Endpoint
|
|
|
|
|
- [ ] Validate input data (Joi/Zod)
|
|
|
|
|
- [ ] Check authentication (JWT)
|
|
|
|
|
- [ ] Check authorization (Role/Ownership)
|
|
|
|
|
- [ ] Handle errors properly
|
|
|
|
|
- [ ] Return consistent response format
|
|
|
|
|
- [ ] Log important actions
|
|
|
|
|
- [ ] Add rate limiting
|
|
|
|
|
|
|
|
|
|
### สำหรับ File Upload
|
|
|
|
|
- [ ] Validate file type
|
|
|
|
|
- [ ] Validate file size
|
|
|
|
|
- [ ] Generate unique filename
|
|
|
|
|
- [ ] Upload to S3/MinIO
|
|
|
|
|
- [ ] Save metadata to database
|
|
|
|
|
- [ ] Handle upload errors
|
|
|
|
|
|
|
|
|
|
### สำหรับ Multi-Language
|
|
|
|
|
- [ ] Accept `lang` query parameter
|
|
|
|
|
- [ ] Return appropriate language
|
|
|
|
|
- [ ] Fallback to default language
|
|
|
|
|
- [ ] Validate both languages exist
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📚 Related Documentation
|
|
|
|
|
|
|
|
|
|
- [API Endpoints Reference](../docs/api-docs/api_endpoints.md)
|
|
|
|
|
- [API Usage Examples](../docs/api-docs/api_usage_examples.md)
|
|
|
|
|
- [Edge Cases Quick Reference](../docs/api-docs/edge_cases_quick_reference.md)
|
|
|
|
|
- [Development Setup](../docs/development_setup.md)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🚀 Quick Start Commands
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Install dependencies
|
|
|
|
|
npm install
|
|
|
|
|
|
|
|
|
|
# Setup database
|
|
|
|
|
npx prisma migrate dev
|
|
|
|
|
|
|
|
|
|
# Seed database
|
|
|
|
|
npx prisma db seed
|
|
|
|
|
|
|
|
|
|
# Start development server
|
|
|
|
|
npm run dev
|
|
|
|
|
|
|
|
|
|
# Run tests
|
|
|
|
|
npm test
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
**Last Updated**: 2026-01-07
|
|
|
|
|
**Version**: 1.0.0
|