38 KiB
ตารางสิทธิ์การใช้งานระบบ E-Learning (Permissions Matrix)
รายการสิทธิ์ทั้งหมด (Permission List)
1. การจัดการผู้ใช้งาน (User Management)
| Permission Code | ชื่อสิทธิ์ | คำอธิบาย |
|---|---|---|
user.register |
ลงทะเบียนผู้ใช้ใหม่ | สามารถสร้างบัญชีผู้ใช้ใหม่ในระบบ |
user.login |
เข้าสู่ระบบ | สามารถเข้าสู่ระบบด้วยอีเมล/รหัสผ่าน |
user.profile.view |
ดูโปรไฟล์ตนเอง | สามารถดูข้อมูลโปรไฟล์ของตนเอง |
user.profile.edit |
แก้ไขโปรไฟล์ตนเอง | สามารถแก้ไขข้อมูลส่วนตัวของตนเอง |
user.password.reset |
รีเซ็ตรหัสผ่าน | สามารถรีเซ็ตรหัสผ่านผ่านอีเมล |
user.list.view |
ดูรายการผู้ใช้ทั้งหมด | สามารถดูรายชื่อผู้ใช้ทั้งหมดในระบบ |
user.manage |
จัดการผู้ใช้งาน | สามารถสร้าง แก้ไข ลบบัญชีผู้ใช้ |
user.role.assign |
กำหนดบทบาทผู้ใช้ | สามารถกำหนดและเปลี่ยนบทบาทของผู้ใช้ |
2. การจัดการหลักสูตร (Course Management)
| Permission Code | ชื่อสิทธิ์ | คำอธิบาย |
|---|---|---|
course.list.view |
ดูรายการหลักสูตร | สามารถดูรายการหลักสูตรที่เผยแพร่ |
course.detail.view |
ดูรายละเอียดหลักสูตร | สามารถดูรายละเอียดของหลักสูตร |
course.search |
ค้นหาหลักสูตร | สามารถค้นหาหลักสูตรในระบบ |
course.enroll |
ลงทะเบียนเรียน | สามารถลงทะเบียนเรียนหลักสูตร |
course.create |
สร้างหลักสูตร | สามารถสร้างหลักสูตรใหม่ |
course.edit |
แก้ไขหลักสูตร | สามารถแก้ไขรายละเอียดหลักสูตร |
course.delete |
ลบหลักสูตร | สามารถลบหลักสูตร |
course.status.manage |
จัดการสถานะหลักสูตร | สามารถเปลี่ยนสถานะเผยแพร่/ซ่อนหลักสูตร |
course.category.manage |
จัดการหมวดหมู่หลักสูตร | สามารถกำหนดหมวดหมู่หลักสูตร |
3. การจัดการเนื้อหาบทเรียน (Content Management)
| Permission Code | ชื่อสิทธิ์ | คำอธิบาย |
|---|---|---|
content.view |
ดูเนื้อหาบทเรียน | สามารถเข้าถึงและดูเนื้อหาบทเรียน |
content.video.play |
เล่นวิดีโอ | สามารถเล่นวิดีโอการเรียนรู้ |
content.document.view |
ดูเอกสาร | สามารถดูเอกสาร PDF |
content.document.download |
ดาวน์โหลดเอกสาร | สามารถดาวน์โหลดเอกสารประกอบ |
content.progress.track |
ติดตามความคืบหน้า | ระบบบันทึกความคืบหน้าการเรียน |
content.chapter.create |
สร้าง Chapter | สามารถสร้าง Chapter ในหลักสูตร |
content.lesson.create |
สร้าง Lesson | สามารถสร้าง Lesson ในหลักสูตร |
content.chapter.edit |
แก้ไข Chapter | สามารถแก้ไข Chapter |
content.lesson.edit |
แก้ไข Lesson | สามารถแก้ไข Lesson |
content.upload |
อัปโหลดเนื้อหา | สามารถอัปโหลดวิดีโอ, PDF, ไฟล์ประกอบ |
content.order.manage |
จัดเรียงลำดับบทเรียน | สามารถจัดเรียงลำดับ Chapter และ Lesson |
content.access.manage |
กำหนดเงื่อนไขการเข้าถึง | สามารถกำหนดเงื่อนไขการเข้าถึงบทเรียน |
4. การจัดการแบบทดสอบ (Quiz & Assessment)
| Permission Code | ชื่อสิทธิ์ | คำอธิบาย |
|---|---|---|
quiz.take |
ทำแบบทดสอบ | สามารถทำแบบทดสอบออนไลน์ |
quiz.result.view |
ดูผลคะแนนตนเอง | สามารถดูผลคะแนนและประวัติการทำแบบทดสอบของตนเอง |
quiz.create |
สร้างแบบทดสอบ | สามารถสร้างแบบทดสอบใหม่ |
quiz.edit |
แก้ไขแบบทดสอบ | สามารถแก้ไขแบบทดสอบ |
quiz.delete |
ลบแบบทดสอบ | สามารถลบแบบทดสอบ |
quiz.settings.manage |
กำหนดการตั้งค่าแบบทดสอบ | สามารถกำหนดเวลา คะแนน เกณฑ์ผ่าน |
quiz.results.view.all |
ดูผลคะแนนผู้เรียนทั้งหมด | สามารถดูผลคะแนนและสถิติของผู้เรียนทั้งหมด |
5. การรายงานและประเมินผล (Reporting)
| Permission Code | ชื่อสิทธิ์ | คำอธิบาย |
|---|---|---|
report.progress.view.own |
ดูรายงานความคืบหน้าตนเอง | สามารถดูรายงานความคืบหน้าการเรียนของตนเอง |
report.score.view.own |
ดูรายงานคะแนนตนเอง | สามารถดูรายงานคะแนนแบบทดสอบของตนเอง |
report.certificate.view |
ดูใบประกาศนียบัตร | สามารถดูและดาวน์โหลดใบประกาศนียบัตร |
report.students.view |
ดูรายงานผู้เรียน | สามารถดูรายงานผู้เรียนในหลักสูตร |
report.progress.view.all |
ดูรายงานความคืบหน้าผู้เรียนทั้งหมด | สามารถดูความคืบหน้าการเรียนของผู้เรียนทั้งหมด |
report.export |
ส่งออกรายงาน | สามารถส่งออกรายงานเป็น Excel หรือ PDF |
report.dashboard.view |
ดู Dashboard | สามารถดู Dashboard ภาพรวมของระบบ |
report.statistics.view |
ดูสถิติระบบ | สามารถดูสถิติการใช้งานระบบโดยรวม |
6. การจัดการประกาศ (Announcements)
| Permission Code | ชื่อสิทธิ์ | คำอธิบาย |
|---|---|---|
announcement.view |
ดูประกาศ | สามารถดูประกาศภายในหลักสูตร |
announcement.create |
สร้างประกาศ | สามารถโพสต์ประกาศใหม่ |
announcement.edit |
แก้ไขประกาศ | สามารถแก้ไขประกาศ |
announcement.delete |
ลบประกาศ | สามารถลบประกาศ |
ตารางสิทธิ์ตามบทบาท (Role-Based Permissions Matrix)
สัญลักษณ์
- ✅ = มีสิทธิ์
- ❌ = ไม่มีสิทธิ์
- 🔒 = มีสิทธิ์เฉพาะข้อมูลของตนเอง
1. การจัดการผู้ใช้งาน (User Management)
| สิทธิ์ | Student | Instructor | Admin |
|---|---|---|---|
user.register |
✅ | ✅ | ✅ |
user.login |
✅ | ✅ | ✅ |
user.profile.view |
🔒 | 🔒 | 🔒 |
user.profile.edit |
🔒 | 🔒 | 🔒 |
user.password.reset |
✅ | ✅ | ✅ |
user.list.view |
❌ | ❌ | ✅ |
user.manage |
❌ | ❌ | ✅ |
user.role.assign |
❌ | ❌ | ✅ |
2. การจัดการหลักสูตร (Course Management)
| สิทธิ์ | Student | Instructor | Admin |
|---|---|---|---|
course.list.view |
✅ | ✅ | ✅ |
course.detail.view |
✅ | ✅ | ✅ |
course.search |
✅ | ✅ | ✅ |
course.enroll |
✅ | ❌ | ❌ |
course.create |
❌ | ✅ | ❌ |
course.edit |
❌ | ✅ | ❌ |
course.delete |
❌ | ✅ | ❌ |
course.status.manage |
❌ | ✅ | ❌ |
course.category.manage |
❌ | ✅ | ❌ |
3. การจัดการเนื้อหาบทเรียน (Content Management)
| สิทธิ์ | Student | Instructor | Admin |
|---|---|---|---|
content.view |
✅ | ✅ | ❌ |
content.video.play |
✅ | ✅ | ❌ |
content.document.view |
✅ | ✅ | ❌ |
content.document.download |
✅ | ✅ | ❌ |
content.progress.track |
✅ | ❌ | ❌ |
content.chapter.create |
❌ | ✅ | ❌ |
content.lesson.create |
❌ | ✅ | ❌ |
content.chapter.edit |
❌ | ✅ | ❌ |
content.lesson.edit |
❌ | ✅ | ❌ |
content.upload |
❌ | ✅ | ❌ |
content.order.manage |
❌ | ✅ | ❌ |
content.access.manage |
❌ | ✅ | ❌ |
4. การจัดการแบบทดสอบ (Quiz & Assessment)
| สิทธิ์ | Student | Instructor | Admin |
|---|---|---|---|
quiz.take |
✅ | ❌ | ❌ |
quiz.result.view |
🔒 | ❌ | ❌ |
quiz.create |
❌ | ✅ | ❌ |
quiz.edit |
❌ | ✅ | ❌ |
quiz.delete |
❌ | ✅ | ❌ |
quiz.settings.manage |
❌ | ✅ | ❌ |
quiz.results.view.all |
❌ | ✅ | ❌ |
5. การรายงานและประเมินผล (Reporting)
| สิทธิ์ | Student | Instructor | Admin |
|---|---|---|---|
report.progress.view.own |
✅ | ❌ | ❌ |
report.score.view.own |
✅ | ❌ | ❌ |
report.certificate.view |
✅ | ❌ | ❌ |
report.students.view |
❌ | ✅ | ❌ |
report.progress.view.all |
❌ | ✅ | ❌ |
report.export |
❌ | ✅ | ❌ |
report.dashboard.view |
❌ | ❌ | ✅ |
report.statistics.view |
❌ | ❌ | ✅ |
6. การจัดการประกาศ (Announcements)
| สิทธิ์ | Student | Instructor | Admin |
|---|---|---|---|
announcement.view |
✅ | ✅ | ❌ |
announcement.create |
❌ | ✅ | ❌ |
announcement.edit |
❌ | ✅ | ❌ |
announcement.delete |
❌ | ✅ | ❌ |
สรุปจำนวนสิทธิ์ตามบทบาท
| บทบาท | จำนวนสิทธิ์ทั้งหมด | สิทธิ์เต็ม | สิทธิ์เฉพาะตนเอง | ไม่มีสิทธิ์ |
|---|---|---|---|---|
| Student | 52 | 21 | 5 | 26 |
| Instructor | 52 | 30 | 3 | 19 |
| Admin | 52 | 9 | 3 | 40 |
หมายเหตุสำคัญ
1. การควบคุมสิทธิ์ระดับข้อมูล (Data-Level Access Control)
Student
- สามารถดูและแก้ไขเฉพาะโปรไฟล์ของตนเอง
- สามารถดูผลคะแนนและความคืบหน้าเฉพาะของตนเอง
- ไม่สามารถเข้าถึงข้อมูลของผู้เรียนคนอื่น
Instructor
- สามารถจัดการเฉพาะหลักสูตรที่ตนเองสร้างหรือได้รับมอบหมาย
- สามารถดูข้อมูลผู้เรียนเฉพาะในหลักสูตรของตนเอง
- ไม่สามารถเข้าถึงข้อมูลระดับระบบโดยรวม
Admin
- มีสิทธิ์เข้าถึงข้อมูลผู้ใช้ทั้งหมด
- มีสิทธิ์ดูสถิติและรายงานระดับระบบ
- ไม่มีสิทธิ์จัดการเนื้อหาการสอนโดยตรง (เป็นหน้าที่ของ Instructor)
2. การนำไปใช้งาน (Implementation Guidelines)
Backend API
// ตัวอย่างการตรวจสอบสิทธิ์
const checkPermission = (user, permission) => {
const rolePermissions = {
STUDENT: ['user.login', 'course.enroll', 'content.view', ...],
INSTRUCTOR: ['user.login', 'course.create', 'content.upload', ...],
ADMIN: ['user.manage', 'user.role.assign', 'report.dashboard.view', ...]
};
return rolePermissions[user.role]?.includes(permission);
};
Frontend UI
// ตัวอย่างการซ่อน/แสดง UI ตามสิทธิ์
const canCreateCourse = hasPermission('course.create'); // true สำหรับ Instructor
const canManageUsers = hasPermission('user.manage'); // true สำหรับ Admin
3. ความปลอดภัย (Security Considerations)
Important
- การตรวจสอบสิทธิ์ต้องทำทั้งที่ Frontend และ Backend
- Frontend ใช้สำหรับ UX (ซ่อน/แสดง UI)
- Backend ใช้สำหรับ Security (ป้องกันการเข้าถึงที่ไม่ได้รับอนุญาต)
Warning
- ห้ามพึ่งพาการตรวจสอบสิทธิ์ที่ Frontend เพียงอย่างเดียว
- ต้องมีการตรวจสอบสิทธิ์ที่ API Layer ทุกครั้ง
4. กรณีพิเศษและ Edge Cases ที่ต้องจัดการ
Caution
กรณีพิเศษเหล่านี้ต้องได้รับการจัดการอย่างถูกต้องเพื่อป้องกันช่องโหว่ด้านความปลอดภัยและปัญหาการใช้งาน
4.1 การเปลี่ยนแปลงบทบาทผู้ใช้โดย Admin (Admin Role Management)
สถานการณ์: Admin เปลี่ยนบทบาทผู้ใช้ (เช่น ปรับสถานะบัญชี, แก้ไขข้อมูลผิดพลาด)
Note
ในระบบนี้ ไม่มีการเปลี่ยนบทบาทระหว่าง Student และ Instructor ในการใช้งานปกติ
- Student ลงทะเบียนเป็น Student ตั้งแต่แรก
- Instructor สมัครหรือได้รับเชิญเป็น Instructor ตั้งแต่แรก
- การเปลี่ยนบทบาทเกิดขึ้นเฉพาะกรณีพิเศษที่ Admin จัดการ (เช่น แก้ไขข้อมูลผิดพลาด)
ปัญหาที่อาจเกิด (กรณีพิเศษที่ Admin ต้องเปลี่ยนบทบาท):
- Session/Token เก่าอาจยังมีสิทธิ์เดิมอยู่
- ข้อมูลที่เกี่ยวข้องกับบทบาทเดิม (เช่น หลักสูตรที่สร้าง, การลงทะเบียน)
วิธีจัดการ:
// Admin เปลี่ยนบทบาทผู้ใช้ (กรณีพิเศษเท่านั้น)
async function adminChangeUserRole(userId, newRole, adminId, reason) {
const user = await getUser(userId);
const oldRole = user.role;
// บันทึก audit log
await logRoleChange(userId, oldRole, newRole, adminId, reason);
// 1. อัปเดตบทบาทในฐานข้อมูล
await updateUserRole(userId, newRole);
// 2. Invalidate all active sessions/tokens
await invalidateUserSessions(userId);
// 3. แจ้งเตือนผู้ใช้ทางอีเมล
await notifyUserRoleChange(userId, oldRole, newRole, reason);
// 4. บังคับให้ Login ใหม่
return {
success: true,
requireRelogin: true,
message: 'เปลี่ยนบทบาทเรียบร้อย ผู้ใช้จะต้อง Login ใหม่'
};
}
4.2 การเข้าถึงหลักสูตรที่ถูกซ่อน/ลบ (Hidden/Deleted Course Access)
สถานการณ์: Student ลงทะเบียนเรียนหลักสูตรแล้ว แต่ Instructor เปลี่ยนสถานะเป็น "ซ่อน" หรือลบหลักสูตร
ปัญหาที่อาจเกิด:
- Student ที่ลงทะเบียนไว้แล้วควรเข้าถึงได้หรือไม่?
- ความคืบหน้าการเรียนจะเกิดอะไร?
- ใบประกาศนียบัตรที่ได้รับไปแล้วยังใช้ได้หรือไม่?
วิธีจัดการ:
// ตรวจสอบการเข้าถึงหลักสูตร
async function canAccessCourse(userId, courseId) {
const course = await getCourse(courseId);
const enrollment = await getEnrollment(userId, courseId);
// กรณีหลักสูตรถูกลบ
if (course.isDeleted) {
// อนุญาตให้ดูเฉพาะ read-only mode (ดูเนื้อหาเก่าได้ แต่ไม่สามารถทำแบบทดสอบ)
return enrollment ? { access: 'read-only', reason: 'course_deleted' } : false;
}
// กรณีหลักสูตรถูกซ่อน
if (course.status === 'HIDDEN') {
// Student ที่ลงทะเบียนไว้แล้วยังเข้าถึงได้ปกติ
if (enrollment) return { access: 'full' };
// Student ใหม่ไม่สามารถลงทะเบียนได้
return false;
}
return { access: 'full' };
}
นโยบายที่แนะนำ:
- หลักสูตรที่ถูก ซ่อน: Student ที่ลงทะเบียนแล้วยังเข้าถึงได้ปกติ
- หลักสูตรที่ถูก ลบ: เปลี่ยนเป็น Read-Only Mode (ดูเนื้อหาได้ แต่ไม่สามารถทำแบบทดสอบหรือรับใบประกาศนียบัตรใหม่)
4.3 การลงทะเบียนซ้ำ (Duplicate Enrollment)
สถานการณ์: Student พยายามลงทะเบียนหลักสูตรที่ลงทะเบียนไว้แล้ว
ปัญหาที่อาจเกิด:
- ข้อมูลความคืบหน้าเดิมจะเป็นอย่างไร?
- ควรรีเซ็ตความคืบหน้าหรือไม่?
วิธีจัดการ:
async function enrollCourse(userId, courseId) {
const existingEnrollment = await getEnrollment(userId, courseId);
if (existingEnrollment) {
// ถ้าลงทะเบียนไว้แล้ว ให้ redirect ไปที่หลักสูตร
return {
success: false,
message: 'คุณได้ลงทะเบียนหลักสูตรนี้ไว้แล้ว',
redirectTo: `/courses/${courseId}`
};
}
// สร้างการลงทะเบียนใหม่
return await createEnrollment(userId, courseId);
}
4.4 การเข้าถึงเนื้อหาที่ยังไม่ถึงลำดับ (Sequential Content Access)
สถานการณ์: Student พยายามเข้าถึง Lesson 5 โดยที่ยังไม่ได้เรียน Lesson 1-4
ปัญหาที่อาจเกิด:
- Student อาจใช้ Direct URL เพื่อข้ามลำดับ
- ระบบต้องบังคับให้เรียนตามลำดับ
วิธีจัดการ:
async function canAccessLesson(userId, lessonId) {
const lesson = await getLesson(lessonId);
const course = await getCourse(lesson.courseId);
// ถ้าหลักสูตรไม่บังคับลำดับ
if (!course.requireSequentialAccess) {
return true;
}
// ตรวจสอบว่าเรียน Lesson ก่อนหน้าครบหรือไม่
const previousLessons = await getPreviousLessons(lessonId);
const completedLessons = await getCompletedLessons(userId, lesson.courseId);
const allPreviousCompleted = previousLessons.every(prev =>
completedLessons.includes(prev.id)
);
if (!allPreviousCompleted) {
return {
access: false,
reason: 'กรุณาเรียนบทเรียนก่อนหน้าให้เสร็จก่อน',
nextAvailableLesson: findNextAvailableLesson(userId, lesson.courseId)
};
}
return { access: true };
}
4.5 การทำแบบทดสอบหลายครั้ง (Multiple Quiz Attempts)
สถานการณ์: Student ทำแบบทดสอบไม่ผ่านและต้องการทำใหม่
ปัญหาที่อาจเกิด:
- จำกัดจำนวนครั้งในการทำหรือไม่?
- คะแนนที่บันทึกควรเป็นครั้งแรก ครั้งสุดท้าย หรือคะแนนสูงสุด?
- ต้องรอระยะเวลาก่อนทำใหม่หรือไม่?
วิธีจัดการ:
async function canTakeQuiz(userId, quizId) {
const quiz = await getQuiz(quizId);
const attempts = await getQuizAttempts(userId, quizId);
// ตรวจสอบจำนวนครั้งที่ทำได้
if (quiz.maxAttempts && attempts.length >= quiz.maxAttempts) {
return {
canTake: false,
reason: `คุณทำแบบทดสอบครบ ${quiz.maxAttempts} ครั้งแล้ว`
};
}
// ตรวจสอบระยะเวลารอระหว่างการทำ (cooldown)
if (quiz.cooldownMinutes && attempts.length > 0) {
const lastAttempt = attempts[attempts.length - 1];
const minutesSinceLastAttempt =
(Date.now() - lastAttempt.completedAt) / 1000 / 60;
if (minutesSinceLastAttempt < quiz.cooldownMinutes) {
const remainingMinutes = Math.ceil(
quiz.cooldownMinutes - minutesSinceLastAttempt
);
return {
canTake: false,
reason: `กรุณารออีก ${remainingMinutes} นาทีก่อนทำใหม่`
};
}
}
return { canTake: true };
}
// การบันทึกคะแนน
async function recordQuizScore(userId, quizId, score) {
const quiz = await getQuiz(quizId);
// กำหนดว่าจะเก็บคะแนนแบบไหน
const scorePolicy = quiz.scorePolicy || 'HIGHEST'; // HIGHEST, LATEST, FIRST, AVERAGE
await saveQuizAttempt(userId, quizId, score, scorePolicy);
}
นโยบายที่แนะนำ:
- จำนวนครั้ง: กำหนดได้ต่อแบบทดสอบ (เช่น 3 ครั้ง, ไม่จำกัด)
- คะแนนที่บันทึก: ใช้คะแนนสูงสุด (HIGHEST) เป็นค่าเริ่มต้น
- Cooldown: กำหนดได้ต่อแบบทดสอบ (เช่น รอ 30 นาที)
4.6 Instructor แก้ไขหลักสูตรขณะที่ Student กำลังเรียน
สถานการณ์: Instructor แก้ไขเนื้อหา/ลบ Lesson ขณะที่ Student กำลังดูอยู่
ปัญหาที่อาจเกิด:
- Student อาจเห็นข้อผิดพลาด 404 Not Found
- ความคืบหน้าอาจไม่ถูกต้อง
- Video ที่กำลังเล่นอาจหยุดทำงาน
วิธีจัดการ:
// Soft Delete แทน Hard Delete
async function deleteLesson(lessonId, instructorId) {
const activeViewers = await getActiveLessonViewers(lessonId);
if (activeViewers.length > 0) {
// ไม่ลบทันที แต่ทำ Soft Delete
await markLessonAsDeleted(lessonId);
// แจ้งเตือน Instructor
return {
success: true,
warning: `มีผู้เรียน ${activeViewers.length} คนกำลังดูบทเรียนนี้อยู่ ระบบจะซ่อนบทเรียนนี้และลบถาวรในอีก 24 ชั่วโมง`
};
}
// ถ้าไม่มีคนดู ลบได้เลย
await hardDeleteLesson(lessonId);
return { success: true };
}
// ตรวจสอบการเข้าถึง Lesson
async function accessLesson(userId, lessonId) {
const lesson = await getLesson(lessonId);
if (lesson.isDeleted) {
// ถ้า Student เข้าถึงก่อนที่จะถูกลบ ให้ดูต่อได้
const wasAccessedBefore = await hasAccessedBefore(userId, lessonId);
if (wasAccessedBefore) {
return {
access: true,
warning: 'บทเรียนนี้จะถูกลบในเร็วๆ นี้'
};
}
return { access: false, reason: 'บทเรียนนี้ถูกลบแล้ว' };
}
return { access: true };
}
4.7 Admin ลบบัญชี Instructor ที่มีหลักสูตรอยู่
สถานการณ์: Admin ลบบัญชี Instructor ที่เป็นเจ้าของหลักสูตรหลายรายการ
ปัญหาที่อาจเกิด:
- หลักสูตรจะกลายเป็น "ไม่มีเจ้าของ"
- Student ที่ลงทะเบียนไว้จะเข้าถึงไม่ได้
- ข้อมูลรายงานจะสูญหาย
วิธีจัดการ:
async function deleteInstructor(instructorId, adminId) {
const courses = await getCoursesByInstructor(instructorId);
if (courses.length > 0) {
// ห้ามลบถ้ายังมีหลักสูตร
return {
success: false,
error: 'ไม่สามารถลบ Instructor ที่มีหลักสูตรอยู่',
suggestion: 'กรุณาโอนหลักสูตรให้ Instructor คนอื่นก่อน',
courses: courses.map(c => ({ id: c.id, title: c.title }))
};
}
// Soft Delete แทน Hard Delete
await deactivateUser(instructorId);
return {
success: true,
message: 'ระงับการใช้งานบัญชีเรียบร้อย'
};
}
// ฟังก์ชันโอนหลักสูตร
async function transferCourses(fromInstructorId, toInstructorId, adminId) {
const courses = await getCoursesByInstructor(fromInstructorId);
for (const course of courses) {
await updateCourseOwner(course.id, toInstructorId);
await logOwnershipTransfer(course.id, fromInstructorId, toInstructorId, adminId);
}
return { success: true, transferredCount: courses.length };
}
นโยบายที่แนะนำ:
- ใช้ Soft Delete (ระงับการใช้งาน) แทน Hard Delete
- บังคับให้โอนหลักสูตรก่อนลบบัญชี
- เก็บ Audit Log ของการโอนหลักสูตร
4.8 การเข้าถึงข้อมูลข้ามหลักสูตร (Cross-Course Data Access)
สถานการณ์: Instructor A พยายามดูรายงานผู้เรียนในหลักสูตรของ Instructor B
ปัญหาที่อาจเกิด:
- Instructor อาจใช้ API โดยตรงเพื่อเข้าถึงข้อมูลหลักสูตรอื่น
- การ Bypass ผ่าน Direct URL
วิธีจัดการ:
// Middleware ตรวจสอบความเป็นเจ้าของ
async function checkCourseOwnership(req, res, next) {
const { courseId } = req.params;
const { userId, role } = req.user;
// Admin สามารถเข้าถึงได้ทุกหลักสูตร (เฉพาะดู ไม่ใช่แก้ไข)
if (role === 'ADMIN') {
req.accessLevel = 'read-only';
return next();
}
// Instructor ต้องเป็นเจ้าของหลักสูตร
if (role === 'INSTRUCTOR') {
const course = await getCourse(courseId);
if (course.instructorId !== userId) {
return res.status(403).json({
error: 'คุณไม่มีสิทธิ์เข้าถึงหลักสูตรนี้'
});
}
req.accessLevel = 'full';
return next();
}
// Student ต้องลงทะเบียนหลักสูตรนี้
if (role === 'STUDENT') {
const enrollment = await getEnrollment(userId, courseId);
if (!enrollment) {
return res.status(403).json({
error: 'คุณต้องลงทะเบียนหลักสูตรนี้ก่อน'
});
}
req.accessLevel = 'student';
return next();
}
return res.status(403).json({ error: 'ไม่มีสิทธิ์เข้าถึง' });
}
// ใช้งาน
app.get('/api/courses/:courseId/students',
authenticate,
checkCourseOwnership,
getStudentList
);
4.9 Student จบหลักสูตรแล้วแต่ Instructor เพิ่มเนื้อหาใหม่
สถานการณ์: Student ทำครบทุก Lesson และได้ใบประกาศนียบัตรแล้ว แต่ Instructor เพิ่ม Lesson ใหม่
ปัญหาที่อาจเกิด:
- ความคืบหน้าจะกลับไปไม่ครบ 100%
- ใบประกาศนียบัตรยังใช้ได้หรือไม่?
- ต้องเรียนเนื้อหาใหม่หรือไม่?
วิธีจัดการ:
async function calculateProgress(userId, courseId) {
const enrollment = await getEnrollment(userId, courseId);
const allLessons = await getCourseLessons(courseId);
const completedLessons = await getCompletedLessons(userId, courseId);
// แยกเนื้อหาเก่าและใหม่
const lessonsAtEnrollment = allLessons.filter(
lesson => lesson.createdAt <= enrollment.enrolledAt
);
const newLessons = allLessons.filter(
lesson => lesson.createdAt > enrollment.enrolledAt
);
// คำนวณความคืบหน้าจากเนื้อหาตอนลงทะเบียน
const originalProgress =
(completedLessons.length / lessonsAtEnrollment.length) * 100;
// ถ้าจบหลักสูตรแล้ว (100% ของเนื้อหาเดิม)
if (originalProgress === 100 && enrollment.certificateIssued) {
return {
progress: 100,
status: 'COMPLETED',
certificate: enrollment.certificateId,
newContentAvailable: newLessons.length > 0,
message: newLessons.length > 0
? `มีเนื้อหาใหม่ ${newLessons.length} บทเรียน (ไม่บังคับ)`
: null
};
}
// ถ้ายังไม่จบ คำนวณจากเนื้อหาทั้งหมด
const totalProgress = (completedLessons.length / allLessons.length) * 100;
return {
progress: totalProgress,
status: 'IN_PROGRESS',
remainingLessons: allLessons.length - completedLessons.length
};
}
นโยบายที่แนะนำ:
- ใบประกาศนียบัตรที่ออกแล้ว ยังคงใช้ได้
- เนื้อหาใหม่ถือเป็น เนื้อหาเสริม (ไม่บังคับ)
- แสดงป้าย "มีเนื้อหาใหม่" ให้ Student ที่จบแล้วทราบ
4.10 ระบบอยู่ในช่วง Maintenance
สถานการณ์: ระบบต้องปิดปรับปรุงชั่วคราว
ปัญหาที่อาจเกิด:
- Student ที่กำลังทำแบบทดสอบจะเกิดอะไร?
- ความคืบหน้าจะสูญหายหรือไม่?
วิธีจัดการ:
// Maintenance Mode Middleware
async function maintenanceMode(req, res, next) {
const isMaintenanceMode = await getSystemSetting('maintenance_mode');
if (!isMaintenanceMode) {
return next();
}
const { role } = req.user || {};
// อนุญาตให้ Admin เข้าถึงได้
if (role === 'ADMIN') {
return next();
}
// บันทึกข้อมูลที่กำลังทำอยู่ก่อน
if (req.method === 'POST' || req.method === 'PUT') {
await savePendingRequest(req);
}
return res.status(503).json({
error: 'ระบบอยู่ในช่วงปรับปรุง',
message: 'ขออภัยในความไม่สะดวก ระบบจะกลับมาให้บริการเร็วๆ นี้',
estimatedTime: await getMaintenanceEndTime(),
savedData: req.method !== 'GET' // แจ้งว่าบันทึกข้อมูลไว้แล้ว
});
}
// Auto-save สำหรับแบบทดสอบ
async function autoSaveQuizProgress(userId, quizId, answers) {
// บันทึกทุก 30 วินาที
await saveQuizDraft(userId, quizId, answers, Date.now());
}
// กู้คืนข้อมูลหลัง Maintenance
async function restoreQuizProgress(userId, quizId) {
const draft = await getQuizDraft(userId, quizId);
if (draft && Date.now() - draft.savedAt < 24 * 60 * 60 * 1000) {
return {
canRestore: true,
answers: draft.answers,
savedAt: draft.savedAt
};
}
return { canRestore: false };
}
นโยบายที่แนะนำ:
- แจ้งเตือนล่วงหน้า 24 ชั่วโมงก่อน Maintenance
- Auto-save ข้อมูลแบบทดสอบทุก 30 วินาที
- เก็บ Draft ไว้ 24 ชั่วโมงหลัง Maintenance
- อนุญาตให้ Admin เข้าถึงระบบได้ระหว่าง Maintenance
5. การขยายสิทธิ์ในอนาคต (Future Extensions)
ระบบสามารถขยายเพิ่มสิทธิ์ได้ เช่น:
course.moderate- สำหรับ Moderator ที่ช่วยตรวจสอบหลักสูตรcontent.review- สำหรับ Reviewer ที่ตรวจสอบเนื้อหาquiz.manual.grade- สำหรับการให้คะแนนแบบอัตนัยdiscussion.moderate- สำหรับการดูแลกระดานสนทนา (ถ้ามีในอนาคต)course.co-teach- สำหรับการสอนร่วมกันหลาย Instructorcontent.version.manage- สำหรับการจัดการเวอร์ชันของเนื้อหา