feat: Add comprehensive API usage examples for v3, ERD, data dictionary, and course cloning documentation.

This commit is contained in:
JakkrapartXD 2026-01-07 10:42:39 +07:00
parent 6c4d10963e
commit a1f7ee057d
9 changed files with 1661 additions and 10 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

BIN
docs/.DS_Store vendored

Binary file not shown.

BIN
docs/api-docs/.DS_Store vendored Normal file

Binary file not shown.

BIN
docs/api-docs/ER/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,507 @@
// E-Learning Platform - Enhanced ERD v3.0
// Major improvements:
// 1. Separated roles table with display values
// 2. Separated user_profiles table
// 3. Added created_by/updated_by audit trails
// 4. Made updated_at/updated_by nullable
// 5. Used ENUM for status fields
// 6. Optimized audit trail placement
// 7. Added announcement status and multi-instructor support
// ============================================
// CORE TABLES
// ============================================
Table roles {
id int [pk, increment]
code varchar [unique, not null, note: 'ADMIN | INSTRUCTOR | STUDENT']
name jsonb [not null, note: '{"th": "ผู้ดูแลระบบ", "en": "Administrator"}']
description jsonb [note: 'role description']
created_at datetime [not null, default: `now()`]
indexes {
code [unique]
}
}
Table users {
id int [pk, increment]
username varchar [unique, not null]
email varchar [unique, not null]
password varchar [not null]
role_id int [not null, ref: > roles.id]
email_verified_at datetime
created_at datetime [not null, default: `now()`]
updated_at datetime
indexes {
username [unique]
email [unique]
role_id
}
}
Table user_profiles {
id int [pk, increment]
user_id int [unique, not null, ref: > users.id]
prefix varchar [note: 'Mr. | Mrs. | Ms. | Dr. etc.']
first_name varchar [not null]
last_name varchar [not null]
phone varchar
avatar_url varchar
created_at datetime [not null, default: `now()`]
updated_at datetime
updated_by int [ref: > users.id]
indexes {
user_id [unique]
}
}
Table categories {
id int [pk, increment]
name jsonb [not null, note: 'multi-language category name']
slug varchar [unique, not null]
description jsonb
icon varchar [note: 'icon identifier']
sort_order int [not null, default: 0]
is_active boolean [not null, default: true]
created_at datetime [not null, default: `now()`]
created_by int [not null, ref: > users.id]
updated_at datetime
updated_by int [ref: > users.id]
indexes {
slug [unique]
is_active
sort_order
}
}
// ============================================
// COURSE STRUCTURE
// ============================================
Table courses {
id int [pk, increment]
category_id int [ref: > categories.id]
title jsonb [not null, note: '{"th": "...", "en": "..."}']
slug varchar [unique, not null]
description jsonb
thumbnail_url varchar
price decimal [not null, default: 0, note: 'must be >= 0']
is_free boolean [not null, default: false]
have_certificate boolean [not null, default: false, note: 'issue certificate upon completion']
status varchar [not null, default: 'DRAFT', note: 'ENUM: DRAFT | PENDING | APPROVED | REJECTED | ARCHIVED']
approved_by int [ref: > users.id, note: 'admin user id']
approved_at datetime
rejection_reason varchar
created_at datetime [not null, default: `now()`]
created_by int [not null, ref: > users.id, note: 'primary instructor']
updated_at datetime
updated_by int [ref: > users.id]
indexes {
category_id
slug [unique]
status
created_by
(status, is_free)
(category_id, status)
}
}
Table course_instructors {
id int [pk, increment]
course_id int [not null, ref: > courses.id]
user_id int [not null, ref: > users.id]
is_primary boolean [not null, default: false, note: 'primary instructor']
joined_at datetime [not null, default: `now()`]
indexes {
(course_id, user_id) [unique]
course_id
user_id
is_primary
}
}
Table chapters {
id int [pk, increment]
course_id int [not null, ref: > courses.id]
title jsonb [not null, note: 'multi-language']
description jsonb
sort_order int [not null, default: 0, note: 'must be >= 0']
is_published boolean [not null, default: false]
created_at datetime [not null, default: `now()`]
updated_at datetime
Note: 'No created_by/updated_by - use course audit trail'
indexes {
course_id
(course_id, sort_order)
}
}
Table lessons {
id int [pk, increment]
chapter_id int [not null, ref: > chapters.id]
title jsonb [not null, note: 'multi-language']
content jsonb [note: 'multi-language lesson content']
type varchar [not null, note: 'ENUM: VIDEO QUIZ']
duration_minutes int [note: 'estimated duration']
sort_order int [not null, default: 0, note: 'must be >= 0']
is_sequential boolean [not null, default: true]
prerequisite_lesson_ids jsonb [note: 'array of lesson IDs [1, 2, 3]']
require_pass_quiz boolean [default: false]
is_published boolean [not null, default: false]
created_at datetime [not null, default: `now()`]
updated_at datetime
Note: 'No created_by/updated_by - use course audit trail'
indexes {
chapter_id
(chapter_id, sort_order)
type
}
}
// ============================================
// QUIZ SYSTEM
// ============================================
Table quizzes {
id int [pk, increment]
lesson_id int [not null, ref: > lessons.id]
title jsonb [not null, note: 'multi-language']
description jsonb
passing_score int [not null, default: 60, note: 'must be 0-100']
time_limit int [note: 'in minutes, must be > 0 if set']
shuffle_questions boolean [not null, default: false]
shuffle_choices boolean [not null, default: false]
show_answers_after_completion boolean [not null, default: true, note: 'show correct answers after quiz completion']
created_at datetime [not null, default: `now()`]
created_by int [not null, ref: > users.id]
updated_at datetime
updated_by int [ref: > users.id]
indexes {
lesson_id
}
}
Table questions {
id int [pk, increment]
quiz_id int [not null, ref: > quizzes.id]
question jsonb [not null, note: 'multi-language']
explanation jsonb [note: 'answer explanation']
question_type varchar [not null, default: 'MULTIPLE_CHOICE', note: 'ENUM: MULTIPLE_CHOICE | TRUE_FALSE | SHORT_ANSWER']
score int [not null, default: 1, note: 'must be > 0']
sort_order int [not null, default: 0]
created_at datetime [not null, default: `now()`]
updated_at datetime
Note: 'No created_by/updated_by - saved with quiz'
indexes {
quiz_id
(quiz_id, sort_order)
}
}
Table choices {
id int [pk, increment]
question_id int [not null, ref: > questions.id]
text jsonb [not null, note: 'multi-language']
is_correct boolean [not null, default: false]
sort_order int [not null, default: 0]
indexes {
question_id
}
}
// ============================================
// STUDENT PROGRESS
// ============================================
Table enrollments {
id int [pk, increment]
user_id int [not null, ref: > users.id]
course_id int [not null, ref: > courses.id]
status varchar [not null, default: 'ENROLLED', note: 'ENUM: ENROLLED | IN_PROGRESS | COMPLETED | DROPPED']
progress_percentage int [not null, default: 0, note: '0-100']
enrolled_at datetime [not null, default: `now()`]
started_at datetime [note: 'first lesson access']
completed_at datetime
last_accessed_at datetime
Note: 'No created_by/updated_by - system managed'
indexes {
(user_id, course_id) [unique, name: 'unique_enrollment']
user_id
course_id
status
last_accessed_at
}
}
Table certificates {
id int [pk, increment]
user_id int [not null, ref: > users.id]
course_id int [not null, ref: > courses.id]
enrollment_id int [unique, not null, ref: > enrollments.id]
file_path varchar [not null, note: 'S3 path to certificate PDF']
issued_at datetime [not null, default: `now()`]
indexes {
user_id
course_id
enrollment_id [unique]
(user_id, course_id)
}
}
Table lesson_progress {
id int [pk, increment]
user_id int [not null, ref: > users.id]
lesson_id int [not null, ref: > lessons.id]
is_completed boolean [not null, default: false]
completed_at datetime
video_progress_seconds int [default: 0]
video_duration_seconds int
video_progress_percentage decimal(5,2)
last_watched_at datetime
created_at datetime [not null, default: `now()`]
updated_at datetime
Note: 'No created_by/updated_by - student action only'
indexes {
(user_id, lesson_id) [unique]
user_id
lesson_id
last_watched_at
}
}
Table quiz_attempts {
id int [pk, increment]
user_id int [not null, ref: > users.id]
quiz_id int [not null, ref: > quizzes.id]
score int [not null, default: 0, note: '0-100']
total_questions int [not null]
correct_answers int [not null, default: 0]
is_passed boolean [not null, default: false]
attempt_number int [not null, default: 1]
answers jsonb [note: 'student answers for review']
started_at datetime [not null, default: `now()`]
completed_at datetime
Note: 'No updated_at - attempts are immutable after completion'
indexes {
user_id
quiz_id
(user_id, quiz_id)
(user_id, quiz_id, attempt_number)
}
}
// ============================================
// COMMUNICATION
// ============================================
Table announcements {
id int [pk, increment]
course_id int [not null, ref: > courses.id]
title jsonb [not null, note: 'multi-language']
content jsonb [not null, note: 'multi-language']
status varchar [not null, default: 'DRAFT', note: 'ENUM: DRAFT | PUBLISHED | ARCHIVED']
is_pinned boolean [not null, default: false]
published_at datetime [note: 'scheduled publish date']
created_at datetime [not null, default: `now()`]
created_by int [not null, ref: > users.id]
updated_at datetime
updated_by int [ref: > users.id]
indexes {
course_id
created_by
status
(course_id, status, is_pinned, published_at)
}
}
Table announcement_attachments {
id int [pk, increment]
announcement_id int [not null, ref: > announcements.id]
file_name varchar [not null]
file_path varchar [not null, note: 'S3 key']
file_size int [not null, note: 'bytes']
mime_type varchar [not null]
created_at datetime [not null, default: `now()`]
Note: 'No updated_at - attachments are immutable'
indexes {
announcement_id
}
}
Table lesson_attachments {
id int [pk, increment]
lesson_id int [not null, ref: > lessons.id]
file_name varchar [not null]
file_path varchar [not null, note: 'S3 key']
file_size int [not null, note: 'bytes']
mime_type varchar [not null]
description jsonb [note: 'multi-language']
sort_order int [not null, default: 0]
created_at datetime [not null, default: `now()`]
Note: 'No updated_at - use lesson audit trail'
indexes {
lesson_id
(lesson_id, sort_order)
}
}
// ============================================
// PAYMENT SYSTEM (Future)
// ============================================
Table orders {
id int [pk, increment]
user_id int [not null, ref: > users.id]
total_amount decimal [not null, default: 0]
status varchar [not null, default: 'PENDING', note: 'ENUM: PENDING | PAID | CANCELLED | REFUNDED']
created_at datetime [not null, default: `now()`]
updated_at datetime
indexes {
user_id
status
(user_id, status)
}
}
Table order_items {
id int [pk, increment]
order_id int [not null, ref: > orders.id]
course_id int [not null, ref: > courses.id]
price decimal [not null]
created_at datetime [not null, default: `now()`]
indexes {
order_id
course_id
}
}
Table payments {
id int [pk, increment]
order_id int [not null, ref: > orders.id]
provider varchar [not null, note: 'stripe | paypal | promptpay']
transaction_id varchar [unique]
amount decimal [not null]
status varchar [not null, default: 'PENDING', note: 'ENUM: PENDING | SUCCESS | FAILED']
paid_at datetime
created_at datetime [not null, default: `now()`]
updated_at datetime
indexes {
order_id
transaction_id [unique]
status
}
}
Table instructor_balances {
id int [pk, increment]
instructor_id int [not null, unique, ref: > users.id]
available_amount decimal [not null, default: 0]
withdrawn_amount decimal [not null, default: 0]
created_at datetime [not null, default: `now()`]
updated_at datetime
indexes {
instructor_id [unique]
}
}
Table withdrawal_requests {
id int [pk, increment]
instructor_id int [not null, ref: > users.id]
amount decimal [not null]
status varchar [not null, default: 'PENDING', note: 'ENUM: PENDING | APPROVED | REJECTED | PAID']
approved_by int [ref: > users.id]
approved_at datetime
rejected_reason varchar
created_at datetime [not null, default: `now()`]
updated_at datetime
updated_by int [ref: > users.id]
indexes {
instructor_id
status
(instructor_id, status)
}
}
// ============================================
// NOTES & BEST PRACTICES
// ============================================
// 1. ENUM Status Values:
// - courses: DRAFT | PENDING | APPROVED | REJECTED | ARCHIVED
// - enrollments: ENROLLED | IN_PROGRESS | COMPLETED | DROPPED
// - announcements: DRAFT | PUBLISHED | ARCHIVED
// - orders: PENDING | PAID | CANCELLED | REFUNDED
// - payments: PENDING | SUCCESS | FAILED
// - withdrawals: PENDING | APPROVED | REJECTED | PAID
// - score_policy: HIGHEST | LATEST | FIRST | AVERAGE
// - lesson_type: VIDEO | PDF | TEXT | QUIZ
// - question_type: MULTIPLE_CHOICE | TRUE_FALSE | SHORT_ANSWER
// 2. Audit Trail Strategy:
// - Full audit (created_by, updated_by): courses, quizzes, categories, announcements
// - Partial audit (created_by only): user_profiles
// - No audit: student actions (enrollments, progress, attempts)
// - No audit: child records saved with parent (questions, choices, chapters, lessons)
// 3. Updated Fields:
// - updated_at is NULL on creation, only set when actually updated
// - updated_by is NULL on creation, only set when updated by someone
// 4. Multi-Instructor Support:
// - course_instructors table allows multiple instructors per course
// - is_primary flag identifies main instructor
// - created_by in courses table is the original creator
// 5. Role System:
// - roles table stores code (ADMIN) and display names ({"th": "ผู้ดูแลระบบ"})
// - Allows easy translation and role management
// - users.role_id references roles.id
// 6. User Profiles:
// - Separated from users table for cleaner authentication
// - Contains display information (name, bio, avatar)
// - prefix field for Thai/international name prefixes
// 7. Announcement Status:
// - DRAFT: not visible to students
// - PUBLISHED: visible to enrolled students
// - ARCHIVED: hidden but kept for history
// 8. Foreign Key Actions:
// - users.role_id: RESTRICT (don't delete roles in use)
// - courses.created_by: RESTRICT (don't delete instructors with courses)
// - enrollments: CASCADE (delete with user/course)
// - announcements: CASCADE (delete with course)
// - quiz_attempts: CASCADE (delete with user)

View file

@ -0,0 +1,205 @@
table_name,column_name,data_type,size,is_nullable,is_primary_key,is_foreign_key,description
roles,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
roles,code,VARCHAR,50,NO,NO,NO,รหัสบทบาท (ADMIN | INSTRUCTOR | STUDENT)
roles,name,JSONB,,NO,NO,NO,ชื่อบทบาทหลายภาษา
roles,description,JSONB,,YES,NO,NO,คำอธิบายบทบาท
roles,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
users,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
users,username,VARCHAR,100,NO,NO,NO,ชื่อผู้ใช้สำหรับเข้าสู่ระบบ (ไม่ซ้ำ)
users,email,VARCHAR,255,NO,NO,NO,อีเมล (ไม่ซ้ำ)
users,password,VARCHAR,255,NO,NO,NO,รหัสผ่านที่เข้ารหัสแล้ว
users,role_id,INTEGER,10,NO,NO,YES,อ้างอิงบทบาทผู้ใช้
users,email_verified_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่ยืนยันอีเมล
users,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
users,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
user_profiles,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
user_profiles,user_id,INTEGER,10,NO,NO,YES,อ้างอิงผู้ใช้ (ไม่ซ้ำ)
user_profiles,prefix,VARCHAR,20,YES,NO,NO,"คำนำหน้าชื่อ enum(นาย, นาง, นางสาว, ดร.)"
user_profiles,first_name,VARCHAR,100,NO,NO,NO,ชื่อจริง
user_profiles,last_name,VARCHAR,100,NO,NO,NO,นามสกุล
user_profiles,phone,VARCHAR,20,YES,NO,NO,เบอร์โทรศัพท์
user_profiles,avatar_url,VARCHAR,500,YES,NO,NO,URL รูปโปรไฟล์
user_profiles,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
user_profiles,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
user_profiles,updated_by,INTEGER,10,YES,NO,YES,ผู้ใช้ที่แก้ไขล่าสุด
categories,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
categories,name,JSONB,,NO,NO,NO,ชื่อหมวดหมู่หลายภาษา
categories,slug,VARCHAR,100,NO,NO,NO,ตัวระบุสำหรับ URL (ไม่ซ้ำ)
categories,description,JSONB,,YES,NO,NO,คำอธิบายหลายภาษา
categories,icon,VARCHAR,100,YES,NO,NO,ตัวระบุไอคอน
categories,sort_order,INTEGER,10,NO,NO,NO,ลำดับการแสดงผล
categories,is_active,BOOLEAN,,NO,NO,NO,สถานะเปิดใช้งาน
categories,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
categories,created_by,INTEGER,10,NO,NO,YES,ผู้สร้าง
categories,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
categories,updated_by,INTEGER,10,YES,NO,YES,ผู้แก้ไขล่าสุด
courses,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
courses,category_id,INTEGER,10,YES,NO,YES,อ้างอิงหมวดหมู่
courses,title,JSONB,,NO,NO,NO,ชื่อคอร์สหลายภาษา
courses,slug,VARCHAR,200,NO,NO,NO,ตัวระบุสำหรับ URL (ไม่ซ้ำ)
courses,description,JSONB,,YES,NO,NO,คำอธิบายคอร์สหลายภาษา
courses,thumbnail_url,VARCHAR,500,YES,NO,NO,URL รูปปกคอร์ส
courses,price,"DECIMAL(10,2)",,NO,NO,NO,ราคาคอร์ส (ต้อง >= 0)
courses,is_free,BOOLEAN,,NO,NO,NO,คอร์สฟรี
courses,have_certificate,BOOLEAN,,NO,NO,NO,ออกใบประกาศนียบัตรเมื่อจบ
courses,status,VARCHAR,20,NO,NO,NO,"สถานะ enum(DRAFT, PENDING, APPROVED, REJECTED, ARCHIVED)"
courses,approved_by,INTEGER,10,YES,NO,YES,ผู้ดูแลที่อนุมัติ
courses,approved_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่อนุมัติ
courses,rejection_reason,TEXT,,YES,NO,NO,เหตุผลที่ปฏิเสธ
courses,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
courses,created_by,INTEGER,10,NO,NO,YES,ผู้สอนหลัก
courses,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
courses,updated_by,INTEGER,10,YES,NO,YES,ผู้แก้ไขล่าสุด
course_instructors,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
course_instructors,course_id,INTEGER,10,NO,NO,YES,อ้างอิงคอร์ส
course_instructors,user_id,INTEGER,10,NO,NO,YES,อ้างอิงผู้สอน
course_instructors,is_primary,BOOLEAN,,NO,NO,NO,ผู้สอนหลัก
course_instructors,joined_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่เข้าร่วม
chapters,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
chapters,course_id,INTEGER,10,NO,NO,YES,อ้างอิงคอร์ส
chapters,title,JSONB,,NO,NO,NO,ชื่อบทหลายภาษา
chapters,description,JSONB,,YES,NO,NO,คำอธิบายบทหลายภาษา
chapters,sort_order,INTEGER,10,NO,NO,NO,ลำดับการแสดงผล (ต้อง >= 0)
chapters,is_published,BOOLEAN,,NO,NO,NO,สถานะเผยแพร่
chapters,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
chapters,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
lessons,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
lessons,chapter_id,INTEGER,10,NO,NO,YES,อ้างอิงบท
lessons,title,JSONB,,NO,NO,NO,ชื่อบทเรียนหลายภาษา
lessons,content,JSONB,,YES,NO,NO,เนื้อหาบทเรียนหลายภาษา
lessons,type,VARCHAR,20,NO,NO,NO,"ประเภทบทเรียน (VIDEO, QUIZ)"
lessons,duration_minutes,INTEGER,10,YES,NO,NO,ระยะเวลาโดยประมาณ (นาที)
lessons,sort_order,INTEGER,10,NO,NO,NO,ลำดับการแสดงผล (ต้อง >= 0)
lessons,is_sequential,BOOLEAN,,NO,NO,NO,ต้องเรียนตามลำดับ
lessons,prerequisite_lesson_ids,JSONB,,YES,NO,NO,รายการ ID บทเรียนที่ต้องเรียนก่อน
lessons,require_pass_quiz,BOOLEAN,,YES,NO,NO,ต้องผ่านแบบทดสอบก่อนดำเนินการต่อ
lessons,is_published,BOOLEAN,,NO,NO,NO,สถานะเผยแพร่
lessons,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
lessons,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
quizzes,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
quizzes,lesson_id,INTEGER,10,NO,NO,YES,อ้างอิงบทเรียน
quizzes,title,JSONB,,NO,NO,NO,ชื่อแบบทดสอบหลายภาษา
quizzes,description,JSONB,,YES,NO,NO,คำอธิบายแบบทดสอบหลายภาษา
quizzes,passing_score,INTEGER,10,NO,NO,NO,คะแนนผ่าน (0-100)
quizzes,time_limit,INTEGER,10,YES,NO,NO,เวลาจำกัด (นาที ต้อง > 0 ถ้ากำหนด)
quizzes,shuffle_questions,BOOLEAN,,NO,NO,NO,สุ่มลำดับคำถาม
quizzes,shuffle_choices,BOOLEAN,,NO,NO,NO,สุ่มลำดับตัวเลือก
quizzes,show_answers_after_completion,BOOLEAN,,NO,NO,NO,แสดงเฉลยหลังทำเสร็จ
quizzes,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
quizzes,created_by,INTEGER,10,NO,NO,YES,ผู้สร้าง
quizzes,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
quizzes,updated_by,INTEGER,10,YES,NO,YES,ผู้แก้ไขล่าสุด
questions,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
questions,quiz_id,INTEGER,10,NO,NO,YES,อ้างอิงแบบทดสอบ
questions,question,JSONB,,NO,NO,NO,ข้อความคำถามหลายภาษา
questions,explanation,JSONB,,YES,NO,NO,คำอธิบายเฉลยหลายภาษา
questions,question_type,VARCHAR,20,NO,NO,NO,"ประเภทคำถาม enum(MULTIPLE_CHOICE, TRUE_FALSE, SHORT_ANSWER)"
questions,score,INTEGER,10,NO,NO,NO,คะแนนสำหรับคำตอบที่ถูก (ต้อง > 0)
questions,sort_order,INTEGER,10,NO,NO,NO,ลำดับการแสดงผล
questions,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
questions,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
choices,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
choices,question_id,INTEGER,10,NO,NO,YES,อ้างอิงคำถาม
choices,text,JSONB,,NO,NO,NO,ข้อความตัวเลือกหลายภาษา
choices,is_correct,BOOLEAN,,NO,NO,NO,คำตอบที่ถูกต้อง
choices,sort_order,INTEGER,10,NO,NO,NO,ลำดับการแสดงผล
enrollments,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
enrollments,user_id,INTEGER,10,NO,NO,YES,อ้างอิงนักเรียน
enrollments,course_id,INTEGER,10,NO,NO,YES,อ้างอิงคอร์ส
enrollments,status,VARCHAR,20,NO,NO,NO,"สถานะ enum(ENROLLED, IN_PROGRESS, COMPLETED, DROPPED)"
enrollments,progress_percentage,INTEGER,10,NO,NO,NO,เปอร์เซ็นต์ความคืบหน้า (0-100)
enrollments,enrolled_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่ลงทะเบียน
enrollments,started_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่เข้าเรียนครั้งแรก
enrollments,completed_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่เรียนจบ
enrollments,last_accessed_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่เข้าใช้ล่าสุด
certificates,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
certificates,user_id,INTEGER,10,NO,NO,YES,อ้างอิงนักเรียน
certificates,course_id,INTEGER,10,NO,NO,YES,อ้างอิงคอร์ส
certificates,enrollment_id,INTEGER,10,NO,NO,YES,อ้างอิงการลงทะเบียน (ไม่ซ้ำ)
certificates,file_path,VARCHAR,500,NO,NO,NO,เส้นทาง S3 ไฟล์ PDF ใบประกาศนียบัตร
certificates,issued_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่ออกใบประกาศนียบัตร
lesson_progress,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
lesson_progress,user_id,INTEGER,10,NO,NO,YES,อ้างอิงนักเรียน
lesson_progress,lesson_id,INTEGER,10,NO,NO,YES,อ้างอิงบทเรียน
lesson_progress,is_completed,BOOLEAN,,NO,NO,NO,สถานะเรียนจบ
lesson_progress,completed_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่เรียนจบ
lesson_progress,video_progress_seconds,INTEGER,10,YES,NO,NO,ความคืบหน้าวีดีโอ (วินาที)
lesson_progress,video_duration_seconds,INTEGER,10,YES,NO,NO,ความยาววีดีโอทั้งหมด (วินาที)
lesson_progress,video_progress_percentage,"DECIMAL(5,2)",,YES,NO,NO,เปอร์เซ็นต์ความคืบหน้าวีดีโอ
lesson_progress,last_watched_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่ดูล่าสุด
lesson_progress,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
lesson_progress,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
quiz_attempts,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
quiz_attempts,user_id,INTEGER,10,NO,NO,YES,อ้างอิงนักเรียน
quiz_attempts,quiz_id,INTEGER,10,NO,NO,YES,อ้างอิงแบบทดสอบ
quiz_attempts,score,INTEGER,10,NO,NO,NO,คะแนนที่ได้ (0-100)
quiz_attempts,total_questions,INTEGER,10,NO,NO,NO,จำนวนคำถามทั้งหมด
quiz_attempts,correct_answers,INTEGER,10,NO,NO,NO,จำนวนคำตอบที่ถูก
quiz_attempts,is_passed,BOOLEAN,,NO,NO,NO,สถานะผ่าน
quiz_attempts,attempt_number,INTEGER,10,NO,NO,NO,ครั้งที่ทำ
quiz_attempts,answers,JSONB,,YES,NO,NO,คำตอบของนักเรียนสำหรับตรวจสอบ
quiz_attempts,started_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่เริ่มทำ
quiz_attempts,completed_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่ทำเสร็จ
announcements,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
announcements,course_id,INTEGER,10,NO,NO,YES,อ้างอิงคอร์ส
announcements,title,JSONB,,NO,NO,NO,ชื่อประกาศหลายภาษา
announcements,content,JSONB,,NO,NO,NO,เนื้อหาประกาศหลายภาษา
announcements,status,VARCHAR,20,NO,NO,NO,"สถานะ enum(DRAFT, PUBLISHED, ARCHIVED)"
announcements,is_pinned,BOOLEAN,,NO,NO,NO,ปักหมุด
announcements,published_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่กำหนดเผยแพร่
announcements,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
announcements,created_by,INTEGER,10,NO,NO,YES,ผู้สร้าง
announcements,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
announcements,updated_by,INTEGER,10,YES,NO,YES,ผู้แก้ไขล่าสุด
announcement_attachments,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
announcement_attachments,announcement_id,INTEGER,10,NO,NO,YES,อ้างอิงประกาศ
announcement_attachments,file_name,VARCHAR,255,NO,NO,NO,ชื่อไฟล์
announcement_attachments,file_path,VARCHAR,500,NO,NO,NO,เส้นทาง/คีย์ S3
announcement_attachments,file_size,INTEGER,10,NO,NO,NO,ขนาดไฟล์ (ไบต์)
announcement_attachments,mime_type,VARCHAR,100,NO,NO,NO,ประเภท MIME
announcement_attachments,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
lesson_attachments,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
lesson_attachments,lesson_id,INTEGER,10,NO,NO,YES,อ้างอิงบทเรียน
lesson_attachments,file_name,VARCHAR,255,NO,NO,NO,ชื่อไฟล์
lesson_attachments,file_path,VARCHAR,500,NO,NO,NO,เส้นทาง/คีย์ S3
lesson_attachments,file_size,INTEGER,10,NO,NO,NO,ขนาดไฟล์ (ไบต์)
lesson_attachments,mime_type,VARCHAR,100,NO,NO,NO,ประเภท MIME
lesson_attachments,description,JSONB,,YES,NO,NO,คำอธิบายไฟล์หลายภาษา
lesson_attachments,sort_order,INTEGER,10,NO,NO,NO,ลำดับการแสดงผล
lesson_attachments,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
orders,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
orders,user_id,INTEGER,10,NO,NO,YES,อ้างอิงลูกค้า
orders,total_amount,"DECIMAL(10,2)",,NO,NO,NO,ยอดรวมคำสั่งซื้อ
orders,status,VARCHAR,20,NO,NO,NO,"สถานะ enum(PENDING, PAID, CANCELLED, REFUNDED)"
orders,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
orders,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
order_items,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
order_items,order_id,INTEGER,10,NO,NO,YES,อ้างอิงคำสั่งซื้อ
order_items,course_id,INTEGER,10,NO,NO,YES,อ้างอิงคอร์ส
order_items,price,"DECIMAL(10,2)",,NO,NO,NO,ราคาคอร์ส ณ เวลาที่ซื้อ
order_items,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
payments,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
payments,order_id,INTEGER,10,NO,NO,YES,อ้างอิงคำสั่งซื้อ
payments,provider,VARCHAR,50,NO,NO,NO,"ผู้ให้บริการชำระเงิน enum(stripe, paypal, promptpay)"
payments,transaction_id,VARCHAR,100,YES,NO,NO,รหัสธุรกรรมจากผู้ให้บริการ (ไม่ซ้ำ)
payments,amount,"DECIMAL(10,2)",,NO,NO,NO,จำนวนเงินที่ชำระ
payments,status,VARCHAR,20,NO,NO,NO,"สถานะ enum(PENDING, SUCCESS, FAILED)"
payments,paid_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่ชำระเงิน
payments,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
payments,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
instructor_balances,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
instructor_balances,instructor_id,INTEGER,10,NO,NO,YES,อ้างอิงผู้สอน (ไม่ซ้ำ)
instructor_balances,available_amount,"DECIMAL(10,2)",,NO,NO,NO,ยอดเงินคงเหลือ
instructor_balances,withdrawn_amount,"DECIMAL(10,2)",,NO,NO,NO,ยอดเงินที่ถอนแล้วทั้งหมด
instructor_balances,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
instructor_balances,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
withdrawal_requests,id,INTEGER,10,NO,YES,NO,คีย์หลัก เพิ่มค่าอัตโนมัติ
withdrawal_requests,instructor_id,INTEGER,10,NO,NO,YES,อ้างอิงผู้สอน
withdrawal_requests,amount,"DECIMAL(10,2)",,NO,NO,NO,จำนวนเงินที่ขอถอน
withdrawal_requests,status,VARCHAR,20,NO,NO,NO,"สถานะ enum(PENDING, APPROVED, REJECTED, PAID)"
withdrawal_requests,approved_by,INTEGER,10,YES,NO,YES,ผู้ดูแลที่อนุมัติ
withdrawal_requests,approved_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่อนุมัติ
withdrawal_requests,rejected_reason,TEXT,,YES,NO,NO,เหตุผลที่ปฏิเสธ
withdrawal_requests,created_at,TIMESTAMP,,NO,NO,NO,วันเวลาที่สร้าง
withdrawal_requests,updated_at,TIMESTAMP,,YES,NO,NO,วันเวลาที่แก้ไขล่าสุด
withdrawal_requests,updated_by,INTEGER,10,YES,NO,YES,ผู้แก้ไขล่าสุด
1 table_name column_name data_type size is_nullable is_primary_key is_foreign_key description
2 roles id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
3 roles code VARCHAR 50 NO NO NO รหัสบทบาท (ADMIN | INSTRUCTOR | STUDENT)
4 roles name JSONB NO NO NO ชื่อบทบาทหลายภาษา
5 roles description JSONB YES NO NO คำอธิบายบทบาท
6 roles created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
7 users id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
8 users username VARCHAR 100 NO NO NO ชื่อผู้ใช้สำหรับเข้าสู่ระบบ (ไม่ซ้ำ)
9 users email VARCHAR 255 NO NO NO อีเมล (ไม่ซ้ำ)
10 users password VARCHAR 255 NO NO NO รหัสผ่านที่เข้ารหัสแล้ว
11 users role_id INTEGER 10 NO NO YES อ้างอิงบทบาทผู้ใช้
12 users email_verified_at TIMESTAMP YES NO NO วันเวลาที่ยืนยันอีเมล
13 users created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
14 users updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
15 user_profiles id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
16 user_profiles user_id INTEGER 10 NO NO YES อ้างอิงผู้ใช้ (ไม่ซ้ำ)
17 user_profiles prefix VARCHAR 20 YES NO NO คำนำหน้าชื่อ enum(นาย, นาง, นางสาว, ดร.)
18 user_profiles first_name VARCHAR 100 NO NO NO ชื่อจริง
19 user_profiles last_name VARCHAR 100 NO NO NO นามสกุล
20 user_profiles phone VARCHAR 20 YES NO NO เบอร์โทรศัพท์
21 user_profiles avatar_url VARCHAR 500 YES NO NO URL รูปโปรไฟล์
22 user_profiles created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
23 user_profiles updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
24 user_profiles updated_by INTEGER 10 YES NO YES ผู้ใช้ที่แก้ไขล่าสุด
25 categories id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
26 categories name JSONB NO NO NO ชื่อหมวดหมู่หลายภาษา
27 categories slug VARCHAR 100 NO NO NO ตัวระบุสำหรับ URL (ไม่ซ้ำ)
28 categories description JSONB YES NO NO คำอธิบายหลายภาษา
29 categories icon VARCHAR 100 YES NO NO ตัวระบุไอคอน
30 categories sort_order INTEGER 10 NO NO NO ลำดับการแสดงผล
31 categories is_active BOOLEAN NO NO NO สถานะเปิดใช้งาน
32 categories created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
33 categories created_by INTEGER 10 NO NO YES ผู้สร้าง
34 categories updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
35 categories updated_by INTEGER 10 YES NO YES ผู้แก้ไขล่าสุด
36 courses id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
37 courses category_id INTEGER 10 YES NO YES อ้างอิงหมวดหมู่
38 courses title JSONB NO NO NO ชื่อคอร์สหลายภาษา
39 courses slug VARCHAR 200 NO NO NO ตัวระบุสำหรับ URL (ไม่ซ้ำ)
40 courses description JSONB YES NO NO คำอธิบายคอร์สหลายภาษา
41 courses thumbnail_url VARCHAR 500 YES NO NO URL รูปปกคอร์ส
42 courses price DECIMAL(10,2) NO NO NO ราคาคอร์ส (ต้อง >= 0)
43 courses is_free BOOLEAN NO NO NO คอร์สฟรี
44 courses have_certificate BOOLEAN NO NO NO ออกใบประกาศนียบัตรเมื่อจบ
45 courses status VARCHAR 20 NO NO NO สถานะ enum(DRAFT, PENDING, APPROVED, REJECTED, ARCHIVED)
46 courses approved_by INTEGER 10 YES NO YES ผู้ดูแลที่อนุมัติ
47 courses approved_at TIMESTAMP YES NO NO วันเวลาที่อนุมัติ
48 courses rejection_reason TEXT YES NO NO เหตุผลที่ปฏิเสธ
49 courses created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
50 courses created_by INTEGER 10 NO NO YES ผู้สอนหลัก
51 courses updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
52 courses updated_by INTEGER 10 YES NO YES ผู้แก้ไขล่าสุด
53 course_instructors id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
54 course_instructors course_id INTEGER 10 NO NO YES อ้างอิงคอร์ส
55 course_instructors user_id INTEGER 10 NO NO YES อ้างอิงผู้สอน
56 course_instructors is_primary BOOLEAN NO NO NO ผู้สอนหลัก
57 course_instructors joined_at TIMESTAMP NO NO NO วันเวลาที่เข้าร่วม
58 chapters id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
59 chapters course_id INTEGER 10 NO NO YES อ้างอิงคอร์ส
60 chapters title JSONB NO NO NO ชื่อบทหลายภาษา
61 chapters description JSONB YES NO NO คำอธิบายบทหลายภาษา
62 chapters sort_order INTEGER 10 NO NO NO ลำดับการแสดงผล (ต้อง >= 0)
63 chapters is_published BOOLEAN NO NO NO สถานะเผยแพร่
64 chapters created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
65 chapters updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
66 lessons id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
67 lessons chapter_id INTEGER 10 NO NO YES อ้างอิงบท
68 lessons title JSONB NO NO NO ชื่อบทเรียนหลายภาษา
69 lessons content JSONB YES NO NO เนื้อหาบทเรียนหลายภาษา
70 lessons type VARCHAR 20 NO NO NO ประเภทบทเรียน (VIDEO, QUIZ)
71 lessons duration_minutes INTEGER 10 YES NO NO ระยะเวลาโดยประมาณ (นาที)
72 lessons sort_order INTEGER 10 NO NO NO ลำดับการแสดงผล (ต้อง >= 0)
73 lessons is_sequential BOOLEAN NO NO NO ต้องเรียนตามลำดับ
74 lessons prerequisite_lesson_ids JSONB YES NO NO รายการ ID บทเรียนที่ต้องเรียนก่อน
75 lessons require_pass_quiz BOOLEAN YES NO NO ต้องผ่านแบบทดสอบก่อนดำเนินการต่อ
76 lessons is_published BOOLEAN NO NO NO สถานะเผยแพร่
77 lessons created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
78 lessons updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
79 quizzes id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
80 quizzes lesson_id INTEGER 10 NO NO YES อ้างอิงบทเรียน
81 quizzes title JSONB NO NO NO ชื่อแบบทดสอบหลายภาษา
82 quizzes description JSONB YES NO NO คำอธิบายแบบทดสอบหลายภาษา
83 quizzes passing_score INTEGER 10 NO NO NO คะแนนผ่าน (0-100)
84 quizzes time_limit INTEGER 10 YES NO NO เวลาจำกัด (นาที ต้อง > 0 ถ้ากำหนด)
85 quizzes shuffle_questions BOOLEAN NO NO NO สุ่มลำดับคำถาม
86 quizzes shuffle_choices BOOLEAN NO NO NO สุ่มลำดับตัวเลือก
87 quizzes show_answers_after_completion BOOLEAN NO NO NO แสดงเฉลยหลังทำเสร็จ
88 quizzes created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
89 quizzes created_by INTEGER 10 NO NO YES ผู้สร้าง
90 quizzes updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
91 quizzes updated_by INTEGER 10 YES NO YES ผู้แก้ไขล่าสุด
92 questions id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
93 questions quiz_id INTEGER 10 NO NO YES อ้างอิงแบบทดสอบ
94 questions question JSONB NO NO NO ข้อความคำถามหลายภาษา
95 questions explanation JSONB YES NO NO คำอธิบายเฉลยหลายภาษา
96 questions question_type VARCHAR 20 NO NO NO ประเภทคำถาม enum(MULTIPLE_CHOICE, TRUE_FALSE, SHORT_ANSWER)
97 questions score INTEGER 10 NO NO NO คะแนนสำหรับคำตอบที่ถูก (ต้อง > 0)
98 questions sort_order INTEGER 10 NO NO NO ลำดับการแสดงผล
99 questions created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
100 questions updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
101 choices id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
102 choices question_id INTEGER 10 NO NO YES อ้างอิงคำถาม
103 choices text JSONB NO NO NO ข้อความตัวเลือกหลายภาษา
104 choices is_correct BOOLEAN NO NO NO คำตอบที่ถูกต้อง
105 choices sort_order INTEGER 10 NO NO NO ลำดับการแสดงผล
106 enrollments id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
107 enrollments user_id INTEGER 10 NO NO YES อ้างอิงนักเรียน
108 enrollments course_id INTEGER 10 NO NO YES อ้างอิงคอร์ส
109 enrollments status VARCHAR 20 NO NO NO สถานะ enum(ENROLLED, IN_PROGRESS, COMPLETED, DROPPED)
110 enrollments progress_percentage INTEGER 10 NO NO NO เปอร์เซ็นต์ความคืบหน้า (0-100)
111 enrollments enrolled_at TIMESTAMP NO NO NO วันเวลาที่ลงทะเบียน
112 enrollments started_at TIMESTAMP YES NO NO วันเวลาที่เข้าเรียนครั้งแรก
113 enrollments completed_at TIMESTAMP YES NO NO วันเวลาที่เรียนจบ
114 enrollments last_accessed_at TIMESTAMP YES NO NO วันเวลาที่เข้าใช้ล่าสุด
115 certificates id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
116 certificates user_id INTEGER 10 NO NO YES อ้างอิงนักเรียน
117 certificates course_id INTEGER 10 NO NO YES อ้างอิงคอร์ส
118 certificates enrollment_id INTEGER 10 NO NO YES อ้างอิงการลงทะเบียน (ไม่ซ้ำ)
119 certificates file_path VARCHAR 500 NO NO NO เส้นทาง S3 ไฟล์ PDF ใบประกาศนียบัตร
120 certificates issued_at TIMESTAMP NO NO NO วันเวลาที่ออกใบประกาศนียบัตร
121 lesson_progress id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
122 lesson_progress user_id INTEGER 10 NO NO YES อ้างอิงนักเรียน
123 lesson_progress lesson_id INTEGER 10 NO NO YES อ้างอิงบทเรียน
124 lesson_progress is_completed BOOLEAN NO NO NO สถานะเรียนจบ
125 lesson_progress completed_at TIMESTAMP YES NO NO วันเวลาที่เรียนจบ
126 lesson_progress video_progress_seconds INTEGER 10 YES NO NO ความคืบหน้าวีดีโอ (วินาที)
127 lesson_progress video_duration_seconds INTEGER 10 YES NO NO ความยาววีดีโอทั้งหมด (วินาที)
128 lesson_progress video_progress_percentage DECIMAL(5,2) YES NO NO เปอร์เซ็นต์ความคืบหน้าวีดีโอ
129 lesson_progress last_watched_at TIMESTAMP YES NO NO วันเวลาที่ดูล่าสุด
130 lesson_progress created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
131 lesson_progress updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
132 quiz_attempts id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
133 quiz_attempts user_id INTEGER 10 NO NO YES อ้างอิงนักเรียน
134 quiz_attempts quiz_id INTEGER 10 NO NO YES อ้างอิงแบบทดสอบ
135 quiz_attempts score INTEGER 10 NO NO NO คะแนนที่ได้ (0-100)
136 quiz_attempts total_questions INTEGER 10 NO NO NO จำนวนคำถามทั้งหมด
137 quiz_attempts correct_answers INTEGER 10 NO NO NO จำนวนคำตอบที่ถูก
138 quiz_attempts is_passed BOOLEAN NO NO NO สถานะผ่าน
139 quiz_attempts attempt_number INTEGER 10 NO NO NO ครั้งที่ทำ
140 quiz_attempts answers JSONB YES NO NO คำตอบของนักเรียนสำหรับตรวจสอบ
141 quiz_attempts started_at TIMESTAMP NO NO NO วันเวลาที่เริ่มทำ
142 quiz_attempts completed_at TIMESTAMP YES NO NO วันเวลาที่ทำเสร็จ
143 announcements id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
144 announcements course_id INTEGER 10 NO NO YES อ้างอิงคอร์ส
145 announcements title JSONB NO NO NO ชื่อประกาศหลายภาษา
146 announcements content JSONB NO NO NO เนื้อหาประกาศหลายภาษา
147 announcements status VARCHAR 20 NO NO NO สถานะ enum(DRAFT, PUBLISHED, ARCHIVED)
148 announcements is_pinned BOOLEAN NO NO NO ปักหมุด
149 announcements published_at TIMESTAMP YES NO NO วันเวลาที่กำหนดเผยแพร่
150 announcements created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
151 announcements created_by INTEGER 10 NO NO YES ผู้สร้าง
152 announcements updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
153 announcements updated_by INTEGER 10 YES NO YES ผู้แก้ไขล่าสุด
154 announcement_attachments id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
155 announcement_attachments announcement_id INTEGER 10 NO NO YES อ้างอิงประกาศ
156 announcement_attachments file_name VARCHAR 255 NO NO NO ชื่อไฟล์
157 announcement_attachments file_path VARCHAR 500 NO NO NO เส้นทาง/คีย์ S3
158 announcement_attachments file_size INTEGER 10 NO NO NO ขนาดไฟล์ (ไบต์)
159 announcement_attachments mime_type VARCHAR 100 NO NO NO ประเภท MIME
160 announcement_attachments created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
161 lesson_attachments id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
162 lesson_attachments lesson_id INTEGER 10 NO NO YES อ้างอิงบทเรียน
163 lesson_attachments file_name VARCHAR 255 NO NO NO ชื่อไฟล์
164 lesson_attachments file_path VARCHAR 500 NO NO NO เส้นทาง/คีย์ S3
165 lesson_attachments file_size INTEGER 10 NO NO NO ขนาดไฟล์ (ไบต์)
166 lesson_attachments mime_type VARCHAR 100 NO NO NO ประเภท MIME
167 lesson_attachments description JSONB YES NO NO คำอธิบายไฟล์หลายภาษา
168 lesson_attachments sort_order INTEGER 10 NO NO NO ลำดับการแสดงผล
169 lesson_attachments created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
170 orders id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
171 orders user_id INTEGER 10 NO NO YES อ้างอิงลูกค้า
172 orders total_amount DECIMAL(10,2) NO NO NO ยอดรวมคำสั่งซื้อ
173 orders status VARCHAR 20 NO NO NO สถานะ enum(PENDING, PAID, CANCELLED, REFUNDED)
174 orders created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
175 orders updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
176 order_items id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
177 order_items order_id INTEGER 10 NO NO YES อ้างอิงคำสั่งซื้อ
178 order_items course_id INTEGER 10 NO NO YES อ้างอิงคอร์ส
179 order_items price DECIMAL(10,2) NO NO NO ราคาคอร์ส ณ เวลาที่ซื้อ
180 order_items created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
181 payments id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
182 payments order_id INTEGER 10 NO NO YES อ้างอิงคำสั่งซื้อ
183 payments provider VARCHAR 50 NO NO NO ผู้ให้บริการชำระเงิน enum(stripe, paypal, promptpay)
184 payments transaction_id VARCHAR 100 YES NO NO รหัสธุรกรรมจากผู้ให้บริการ (ไม่ซ้ำ)
185 payments amount DECIMAL(10,2) NO NO NO จำนวนเงินที่ชำระ
186 payments status VARCHAR 20 NO NO NO สถานะ enum(PENDING, SUCCESS, FAILED)
187 payments paid_at TIMESTAMP YES NO NO วันเวลาที่ชำระเงิน
188 payments created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
189 payments updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
190 instructor_balances id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
191 instructor_balances instructor_id INTEGER 10 NO NO YES อ้างอิงผู้สอน (ไม่ซ้ำ)
192 instructor_balances available_amount DECIMAL(10,2) NO NO NO ยอดเงินคงเหลือ
193 instructor_balances withdrawn_amount DECIMAL(10,2) NO NO NO ยอดเงินที่ถอนแล้วทั้งหมด
194 instructor_balances created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
195 instructor_balances updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
196 withdrawal_requests id INTEGER 10 NO YES NO คีย์หลัก เพิ่มค่าอัตโนมัติ
197 withdrawal_requests instructor_id INTEGER 10 NO NO YES อ้างอิงผู้สอน
198 withdrawal_requests amount DECIMAL(10,2) NO NO NO จำนวนเงินที่ขอถอน
199 withdrawal_requests status VARCHAR 20 NO NO NO สถานะ enum(PENDING, APPROVED, REJECTED, PAID)
200 withdrawal_requests approved_by INTEGER 10 YES NO YES ผู้ดูแลที่อนุมัติ
201 withdrawal_requests approved_at TIMESTAMP YES NO NO วันเวลาที่อนุมัติ
202 withdrawal_requests rejected_reason TEXT YES NO NO เหตุผลที่ปฏิเสธ
203 withdrawal_requests created_at TIMESTAMP NO NO NO วันเวลาที่สร้าง
204 withdrawal_requests updated_at TIMESTAMP YES NO NO วันเวลาที่แก้ไขล่าสุด
205 withdrawal_requests updated_by INTEGER 10 YES NO YES ผู้แก้ไขล่าสุด

View file

@ -0,0 +1,360 @@
# Course Cloning API
## 📋 Overview
API endpoint for cloning an entire course including all content, lessons, quizzes, and attachments.
---
## 🔧 Clone Course
### Endpoint
```http
POST /api/instructor/courses/:courseId/clone
Authorization: Bearer <instructor-token>
Content-Type: application/json
```
### Request Body
```json
{
"new_title": {
"th": "Python สำหรับผู้เริ่มต้น (ปรับปรุง 2026)",
"en": "Python for Beginners (Updated 2026)"
},
"new_slug": "python-beginners-2026",
"copy_settings": {
"copy_chapters": true,
"copy_lessons": true,
"copy_quizzes": true,
"copy_attachments": true,
"copy_announcements": false
}
}
```
### Response 201
```json
{
"new_course_id": 25,
"cloned_from_course_id": 1,
"status": "DRAFT",
"message": "Course cloned successfully",
"cloned_content": {
"chapters": 10,
"lessons": 45,
"quizzes": 10,
"questions": 150,
"attachments": 30,
"total_duration_minutes": 1200
},
"next_steps": [
"Edit content as needed",
"Update videos if necessary",
"Submit for approval when ready"
]
}
```
---
## 📝 What Gets Cloned
### ✅ Copied:
- Course info (title, description, price, thumbnail)
- All chapters
- All lessons (including video references)
- All quizzes and questions
- All attachments (files copied to new location)
- Course settings (is_free, have_certificate)
- Lesson prerequisites
- Sort orders
### ❌ NOT Copied:
- Enrollments
- Student progress
- Reviews/ratings
- Announcements (optional)
- Approval status (new course = DRAFT)
- Statistics
---
## 🎯 Use Cases
### 1. Update Videos
```
1. Clone course
2. Replace videos in new course
3. Submit for approval
4. Publish
```
### 2. Add Lessons
```
1. Clone course
2. Add new lessons/chapters
3. Submit for approval
4. Publish
```
### 3. Major Restructure
```
1. Clone course
2. Reorganize chapters/lessons
3. Update content
4. Submit for approval
5. Publish
```
---
## 🔒 Permissions
**Who can clone**:
- ✅ Course owner (created_by)
- ✅ Primary instructor (is_primary = true)
- ✅ Admin
**Restrictions**:
- ❌ Cannot clone archived courses
- ❌ Cannot clone rejected courses
- ✅ Can clone approved/published courses
---
## 📊 Clone Process
### Backend Process:
```javascript
async function cloneCourse(originalCourseId, newData) {
// 1. Create new course
const newCourse = await createCourse({
...originalCourse,
title: newData.new_title,
slug: newData.new_slug,
status: 'DRAFT',
created_by: currentUser.id
});
// 2. Clone chapters
for (const chapter of originalChapters) {
const newChapter = await createChapter({
...chapter,
course_id: newCourse.id
});
// 3. Clone lessons
for (const lesson of chapter.lessons) {
const newLesson = await createLesson({
...lesson,
chapter_id: newChapter.id
});
// 4. Clone attachments (copy files)
for (const attachment of lesson.attachments) {
await copyFileToS3(attachment.file_path, newPath);
await createAttachment({
...attachment,
lesson_id: newLesson.id,
file_path: newPath
});
}
// 5. Clone quizzes
if (lesson.quiz) {
const newQuiz = await createQuiz({
...lesson.quiz,
lesson_id: newLesson.id
});
// 6. Clone questions
for (const question of lesson.quiz.questions) {
const newQuestion = await createQuestion({
...question,
quiz_id: newQuiz.id
});
// 7. Clone choices
for (const choice of question.choices) {
await createChoice({
...choice,
question_id: newQuestion.id
});
}
}
}
}
}
return newCourse;
}
```
---
## ⚠️ Error Handling
### Error: Course Not Found
```json
{
"error": {
"code": "COURSE_NOT_FOUND",
"message": "Course not found or you don't have permission"
}
}
```
### Error: Slug Already Exists
```json
{
"error": {
"code": "SLUG_EXISTS",
"message": "Course slug already exists. Please use a different slug.",
"suggestion": "python-beginners-2026-v2"
}
}
```
### Error: Clone Failed
```json
{
"error": {
"code": "CLONE_FAILED",
"message": "Failed to clone course. Please try again.",
"details": "Error copying attachment: file.pdf"
}
}
```
---
## 🎨 Frontend Integration
### Clone Button
```javascript
async function cloneCourse(courseId) {
const response = await fetch(`/api/instructor/courses/${courseId}/clone`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
new_title: {
th: `${originalTitle.th} (คัดลอก)`,
en: `${originalTitle.en} (Copy)`
},
new_slug: `${originalSlug}-copy-${Date.now()}`
})
});
const data = await response.json();
if (response.ok) {
// Redirect to edit new course
window.location.href = `/instructor/courses/${data.new_course_id}/edit`;
}
}
```
---
## 📈 Performance Considerations
### Large Courses:
- Use background job for cloning
- Show progress indicator
- Send notification when complete
```http
POST /api/instructor/courses/:courseId/clone
{
"async": true
}
Response 202:
{
"job_id": "clone-job-123",
"status": "PROCESSING",
"message": "Clone in progress. You will be notified when complete.",
"estimated_time_minutes": 5
}
```
### Check Job Status:
```http
GET /api/instructor/clone-jobs/:jobId
Response:
{
"job_id": "clone-job-123",
"status": "COMPLETED",
"new_course_id": 25,
"progress": {
"chapters": "10/10",
"lessons": "45/45",
"quizzes": "10/10"
}
}
```
---
## 🔗 Related Endpoints
### Get Clone History
```http
GET /api/instructor/courses/:courseId/clones
```
**Response**:
```json
{
"original_course_id": 1,
"clones": [
{
"id": 25,
"title": "Python for Beginners (Updated 2026)",
"status": "PUBLISHED",
"cloned_at": "2026-01-06T15:00:00Z",
"cloned_by": "Prof. John Smith"
},
{
"id": 30,
"title": "Python for Beginners (Advanced)",
"status": "DRAFT",
"cloned_at": "2026-01-05T10:00:00Z"
}
]
}
```
---
## ✅ Summary
**Endpoint**: `POST /instructor/courses/:courseId/clone`
**Copies**:
- ✅ All chapters, lessons, quizzes
- ✅ All attachments (files copied)
- ✅ All settings and configurations
**Does NOT copy**:
- ❌ Enrollments or student data
- ❌ Reviews or ratings
- ❌ Approval status
**Result**: New DRAFT course ready for editing
---
## 🚀 Next Steps
After cloning:
1. Edit new course as needed
2. Update videos if necessary
3. Add/remove lessons
4. Submit for approval
5. Publish when approved

View file

@ -18,8 +18,8 @@ Authorization: Bearer <token>
### Authentication
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/auth/register` | - | Register new user |
| POST | `/auth/login` | - | Login |
| POST | `/auth/register` | - | Register new user (username + email) |
| POST | `/auth/login` | - | Login (username OR email) |
| POST | `/auth/logout` | ✅ | Logout |
| POST | `/auth/refresh` | ✅ | Refresh token |
| POST | `/auth/password/reset-request` | - | Request password reset |
@ -28,8 +28,9 @@ Authorization: Bearer <token>
### User Profile
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| GET | `/users/me` | ✅ | Get current user profile |
| PUT | `/users/me` | ✅ | Update profile |
| GET | `/users/me` | ✅ | Get current user info |
| GET | `/users/me/profile` | ✅ | Get user profile details |
| PUT | `/users/me/profile` | ✅ | Update profile (name, phone, avatar) |
| PUT | `/users/me/password` | ✅ | Change password |
### User Management (Admin)
@ -40,6 +41,12 @@ Authorization: Bearer <token>
| PUT | `/admin/users/:userId/role` | 🔒 Admin | Update user role |
| DELETE | `/admin/users/:userId` | 🔒 Admin | Deactivate user |
### Role Management (Admin)
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| GET | `/admin/roles` | 🔒 Admin | List all roles |
| GET | `/admin/roles/:roleId` | 🔒 Admin | Get role details |
---
## 2. Categories
@ -83,6 +90,15 @@ Authorization: Bearer <token>
| PUT | `/instructor/courses/:courseId` | 👨‍🏫 Instructor | Update course |
| DELETE | `/instructor/courses/:courseId` | 👨‍🏫 Instructor | Delete course |
| POST | `/instructor/courses/:courseId/submit` | 👨‍🏫 Instructor | Submit for approval |
| POST | `/instructor/courses/:courseId/clone` | 👨‍🏫 Instructor | Clone course (copy all content) |
### Course Instructors
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| GET | `/instructor/courses/:courseId/instructors` | 👨‍🏫 Instructor | List course instructors |
| POST | `/instructor/courses/:courseId/instructors` | 👨‍🏫 Instructor | Add instructor to course |
| DELETE | `/instructor/courses/:courseId/instructors/:userId` | 👨‍🏫 Instructor | Remove instructor |
| PUT | `/instructor/courses/:courseId/instructors/:userId/primary` | 👨‍🏫 Instructor | Set as primary instructor |
---
@ -166,8 +182,8 @@ Authorization: Bearer <token>
|--------|----------|------|-------------|
| GET | `/students/progress` | 👨‍🎓 Student | Get my overall progress |
| GET | `/students/courses/:courseId/progress` | 👨‍🎓 Student | Get course progress |
| GET | `/students/courses/:courseId/certificate` | 👨‍🎓 Student | Get certificate |
| GET | `/students/certificates` | 👨‍🎓 Student | Get all certificates |
| GET | `/students/courses/:courseId/certificate` | 👨‍🎓 Student | Get certificate (PDF file_path) |
| GET | `/students/certificates` | 👨‍🎓 Student | Get all my certificates |
### Instructor Reports
| Method | Endpoint | Auth | Description |
@ -311,15 +327,15 @@ Authorization: Bearer <token>
---
## Total Endpoints: **96+**
## Total Endpoints: **107+**
- Authentication: 6
- User Management: 4
- User Management: 6 (+2 profile, +2 roles)
- Categories: 5
- Courses: 14 (+2)
- Courses: 19 (+4 instructors, +1 clone)
- Chapters & Lessons: 16
- Quizzes: 10
- Announcements: 7 (+1)
- Announcements: 7
- Reports: 11
- Orders & Payments: 7 (อนาคต)
- Earnings & Withdrawals: 8 (อนาคต)

View file

@ -0,0 +1,563 @@
# API Usage Examples for ERD v3
## 🔐 Authentication
### Register with Username
```http
POST /api/auth/register
Content-Type: application/json
{
"username": "john_doe",
"email": "john@example.com",
"password": "SecurePass123!",
"first_name": "John",
"last_name": "Doe",
"prefix": "Mr."
}
```
**Response 201**:
```json
{
"user": {
"id": 1,
"username": "john_doe",
"email": "john@example.com",
"role": {
"id": 3,
"code": "STUDENT",
"name": {
"th": "นักเรียน",
"en": "Student"
}
},
"profile": {
"prefix": "Mr.",
"first_name": "John",
"last_name": "Doe"
}
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
---
### Login with Username
```http
POST /api/auth/login
Content-Type: application/json
{
"username": "john_doe",
"password": "SecurePass123!"
}
```
**Response 200**:
```json
{
"user": {
"id": 1,
"username": "john_doe",
"email": "john@example.com",
"role": {
"code": "STUDENT",
"name": {"th": "นักเรียน", "en": "Student"}
}
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
---
### Login with Email
```http
POST /api/auth/login
Content-Type: application/json
{
"email": "john@example.com",
"password": "SecurePass123!"
}
```
**Response**: Same as username login
---
## 👤 User Profile
### Get User Info
```http
GET /api/users/me
Authorization: Bearer <token>
```
**Response 200**:
```json
{
"id": 1,
"username": "john_doe",
"email": "john@example.com",
"email_verified_at": "2026-01-01T10:00:00Z",
"role": {
"id": 3,
"code": "STUDENT",
"name": {"th": "นักเรียน", "en": "Student"}
},
"created_at": "2026-01-01T10:00:00Z"
}
```
---
### Get User Profile Details
```http
GET /api/users/me/profile
Authorization: Bearer <token>
```
**Response 200**:
```json
{
"id": 1,
"user_id": 1,
"prefix": "Mr.",
"first_name": "John",
"last_name": "Doe",
"phone": "0812345678",
"avatar_url": "https://s3.../avatars/john.jpg",
"created_at": "2026-01-01T10:00:00Z",
"updated_at": "2026-01-05T15:30:00Z"
}
```
---
### Update Profile
```http
PUT /api/users/me/profile
Authorization: Bearer <token>
Content-Type: application/json
{
"prefix": "Dr.",
"first_name": "John",
"last_name": "Doe",
"phone": "0898765432",
"avatar_url": "https://s3.../avatars/john-new.jpg"
}
```
**Response 200**:
```json
{
"id": 1,
"prefix": "Dr.",
"first_name": "John",
"last_name": "Doe",
"phone": "0898765432",
"avatar_url": "https://s3.../avatars/john-new.jpg",
"updated_at": "2026-01-06T11:00:00Z",
"updated_by": 1
}
```
---
## 🎓 Multi-Instructor Support
### List Course Instructors
```http
GET /api/instructor/courses/1/instructors
Authorization: Bearer <token>
```
**Response 200**:
```json
{
"instructors": [
{
"id": 1,
"user_id": 5,
"username": "prof_smith",
"name": "Prof. John Smith",
"avatar_url": "https://s3.../avatars/smith.jpg",
"is_primary": true,
"joined_at": "2026-01-01T10:00:00Z"
},
{
"id": 2,
"user_id": 10,
"username": "dr_jane",
"name": "Dr. Jane Doe",
"avatar_url": "https://s3.../avatars/jane.jpg",
"is_primary": false,
"joined_at": "2026-01-05T14:00:00Z"
}
]
}
```
---
### Add Instructor to Course
```http
POST /api/instructor/courses/1/instructors
Authorization: Bearer <token>
Content-Type: application/json
{
"user_id": 15,
"is_primary": false
}
```
**Response 201**:
```json
{
"id": 3,
"course_id": 1,
"user_id": 15,
"username": "ta_mike",
"name": "Mike Johnson",
"is_primary": false,
"joined_at": "2026-01-06T11:00:00Z"
}
```
---
### Set Primary Instructor
```http
PUT /api/instructor/courses/1/instructors/10/primary
Authorization: Bearer <token>
```
**Response 200**:
```json
{
"message": "Primary instructor updated",
"instructors": [
{
"user_id": 5,
"is_primary": false
},
{
"user_id": 10,
"is_primary": true
}
]
}
```
---
### Remove Instructor
```http
DELETE /api/instructor/courses/1/instructors/15
Authorization: Bearer <token>
```
**Response 200**:
```json
{
"message": "Instructor removed successfully"
}
```
**Error 400** (Last instructor):
```json
{
"error": {
"code": "LAST_INSTRUCTOR",
"message": "Cannot remove the last instructor from course"
}
}
```
---
## 🎓 Certificates
### Get My Certificates
```http
GET /api/students/certificates
Authorization: Bearer <token>
```
**Response 200**:
```json
{
"certificates": [
{
"id": 1,
"course_id": 1,
"course_title": "Python for Beginners",
"completion_date": "2026-01-05",
"file_path": "https://s3.../certificates/cert-user1-course1.pdf",
"issued_at": "2026-01-05T16:00:00Z"
},
{
"id": 2,
"course_id": 3,
"course_title": "Web Development Bootcamp",
"completion_date": "2025-12-20",
"file_path": "https://s3.../certificates/cert-user1-course3.pdf",
"issued_at": "2025-12-20T10:00:00Z"
}
]
}
```
---
### Get Course Certificate
```http
GET /api/students/courses/1/certificate
Authorization: Bearer <token>
```
**Response 200**:
```json
{
"id": 1,
"course_id": 1,
"course_title": "Python for Beginners",
"student_name": "John Doe",
"completion_date": "2026-01-05",
"file_path": "https://s3.../certificates/cert-user1-course1.pdf",
"issued_at": "2026-01-05T16:00:00Z"
}
```
**Error 404** (Not completed):
```json
{
"error": {
"code": "CERTIFICATE_NOT_FOUND",
"message": "Certificate not available. Complete the course first."
}
}
```
**Error 400** (Course doesn't issue certificates):
```json
{
"error": {
"code": "NO_CERTIFICATE",
"message": "This course does not issue certificates"
}
}
```
---
## 🔒 Role Management (Admin)
### List All Roles
```http
GET /api/admin/roles
Authorization: Bearer <admin-token>
```
**Response 200**:
```json
{
"roles": [
{
"id": 1,
"code": "ADMIN",
"name": {
"th": "ผู้ดูแลระบบ",
"en": "Administrator"
},
"description": {
"th": "มีสิทธิ์เต็มในการจัดการระบบ",
"en": "Full system access"
}
},
{
"id": 2,
"code": "INSTRUCTOR",
"name": {
"th": "ผู้สอน",
"en": "Instructor"
},
"description": {
"th": "สามารถสร้างและจัดการคอร์สเรียน",
"en": "Can create and manage courses"
}
},
{
"id": 3,
"code": "STUDENT",
"name": {
"th": "นักเรียน",
"en": "Student"
},
"description": {
"th": "สามารถลงทะเบียนและเรียนคอร์ส",
"en": "Can enroll and learn courses"
}
}
]
}
```
---
### Get Role Details
```http
GET /api/admin/roles/2
Authorization: Bearer <admin-token>
```
**Response 200**:
```json
{
"id": 2,
"code": "INSTRUCTOR",
"name": {
"th": "ผู้สอน",
"en": "Instructor"
},
"description": {
"th": "สามารถสร้างและจัดการคอร์สเรียน",
"en": "Can create and manage courses"
},
"user_count": 45,
"created_at": "2026-01-01T00:00:00Z"
}
```
---
## 📚 Course with Certificate
### Get Course Details (with have_certificate)
```http
GET /api/courses/1
```
**Response 200**:
```json
{
"id": 1,
"title": {
"th": "Python สำหรับผู้เริ่มต้น",
"en": "Python for Beginners"
},
"description": {...},
"price": 990,
"is_free": false,
"have_certificate": true,
"instructors": [
{
"user_id": 5,
"name": "Prof. John Smith",
"is_primary": true
},
{
"user_id": 10,
"name": "Dr. Jane Doe",
"is_primary": false
}
],
"total_lessons": 50,
"total_duration": 1200,
"status": "APPROVED"
}
```
---
## 🎯 Quiz with show_answers_after_completion
### Get Quiz
```http
GET /api/students/quizzes/1
Authorization: Bearer <token>
```
**Response 200**:
```json
{
"id": 1,
"title": {"th": "แบบทดสอบท้ายบท", "en": "Chapter Quiz"},
"description": {...},
"passing_score": 60,
"time_limit": 30,
"shuffle_questions": true,
"shuffle_choices": true,
"show_answers_after_completion": true,
"questions": [...]
}
```
---
### Submit Quiz (with answers shown)
```http
POST /api/students/quizzes/1/submit
Authorization: Bearer <token>
Content-Type: application/json
{
"answers": [
{"question_id": 1, "choice_id": 3},
{"question_id": 2, "choice_id": 7}
]
}
```
**Response 200** (show_answers_after_completion = true):
```json
{
"attempt_id": 1,
"score": 75,
"total_questions": 10,
"correct_answers": 8,
"is_passed": true,
"answers": [
{
"question_id": 1,
"question": {"th": "Python คืออะไร?"},
"your_answer": 3,
"correct_answer": 3,
"is_correct": true,
"explanation": {"th": "Python เป็นภาษาโปรแกรมมิ่ง..."}
},
{
"question_id": 2,
"question": {"th": "ตัวแปรใน Python..."},
"your_answer": 7,
"correct_answer": 8,
"is_correct": false,
"explanation": {"th": "ตัวแปรใน Python ไม่ต้องประกาศชนิดข้อมูล"}
}
]
}
```
**Response 200** (show_answers_after_completion = false):
```json
{
"attempt_id": 1,
"score": 75,
"total_questions": 10,
"correct_answers": 8,
"is_passed": true
}
```
---
## 🔗 Related Documentation
- [API Endpoints Reference](./api_endpoints.md)
- [ERD v3 Schema](./ER/ERD_v3_improved.txt)
- [Edge Cases](./edge_cases_quick_reference.md)