Files
alla-tms/plans/online-lesson-returned-rejected-permission-logic-plan.md
2026-07-16 09:53:14 +07:00

37 KiB

Prompt: ตรวจสอบและแก้ไข Permission Logic สำหรับบทเรียนออนไลน์สถานะ Returned/Rejected

บทบาท

ให้คุณทำหน้าที่เป็น Senior Full-Stack Developer, Authorization Reviewer และ Code Auditor ตรวจสอบระบบสิทธิ์และ Workflow ของโมดูลบทเรียนออนไลน์ก่อนดำเนินการแก้ไข

ต้องตรวจสอบ Source Code, Permission Definitions, Route Guards, Action Menu, Form Submission, API Authorization และ Workflow Transition ที่ระบบใช้อยู่จริง

ห้ามคาดเดาชื่อสถานะ Permission Route หรือ Function ขึ้นมาเอง ให้ยืนยันจากโค้ดจริงก่อนทุกครั้ง


ปัญหาปัจจุบัน

ในหน้าบทเรียนออนไลน์ของเจ้าหน้าที่ เมื่อบทเรียนถูกผู้ตรวจสอบส่งกลับมาแก้ไข รายการมีสถานะประเภท:

returned
rejected
revision_required
changes_requested

หรือสถานะจริงที่ระบบใช้อยู่

เจ้าหน้าที่ซึ่งเป็นเจ้าของหรือผู้สร้างบทเรียนควรสามารถแก้ไขงานของตนเองและส่งตรวจสอบใหม่ได้ แต่ปัจจุบันระบบกลับตรวจสอบสิทธิ์ด้วย Permission:

online_lesson:revision

ทำให้เจ้าหน้าที่ทั่วไปที่ไม่มี Permission ดังกล่าว:

  • ไม่เห็นปุ่มแก้ไข
  • ไม่สามารถเข้าหน้าแก้ไข
  • หรือเปิดหน้าแก้ไขได้แต่ไม่สามารถบันทึก
  • ไม่สามารถส่งตรวจสอบใหม่
  • ถูกปฏิเสธจาก Frontend หรือ Backend Authorization

จากการวิเคราะห์เบื้องต้น ปัญหาไม่น่าเกิดจากตัว Form โดยตรง แต่เกิดจากการตีความ Permission ผิดหน้าที่


หลักการ Permission ที่ถูกต้อง

ให้แยกความหมายของ Permission อย่างชัดเจนดังนี้

online_lesson:revision

Permission นี้ควรเป็นสิทธิ์ของฝั่งผู้ตรวจสอบหรือผู้อนุมัติ สำหรับการดำเนินการ:

ส่งบทเรียนกลับให้เจ้าหน้าที่แก้ไข

ตัวอย่าง Action:

pending_review → returned

หรือ Transition จริงของระบบ

Permission นี้ไม่ควรถูกใช้เป็นเงื่อนไขหลักในการอนุญาตให้เจ้าของบทเรียนแก้ไขรายการที่ถูกส่งกลับ


online_lesson:edit_draft

Permission นี้ควรเป็นสิทธิ์ของฝั่งผู้สร้างหรือเจ้าหน้าที่ สำหรับการ:

  • แก้ไขบทเรียนฉบับร่างของตนเอง
  • แก้ไขบทเรียนของตนเองที่ถูกส่งกลับ
  • บันทึกการแก้ไข
  • ส่งตรวจสอบใหม่

ตัวอย่างสถานะที่ควรแก้ไขได้:

draft
returned

หรือสถานะจริงของระบบ เช่น:

draft
revision_required
rejected

ต้องตรวจสอบ Workflow จริงก่อนกำหนดรายการสถานะ


เป้าหมาย

ปรับ Authorization Logic ให้เจ้าของหรือผู้สร้างบทเรียนที่ถูกส่งกลับสามารถแก้ไขงานของตนเองได้ด้วย Permission ฝั่งผู้สร้าง เช่น:

online_lesson:edit_draft

โดยยังคงให้:

online_lesson:revision

เป็น Permission สำหรับผู้ตรวจสอบในการส่งบทเรียนกลับให้แก้ไขเท่านั้น

หลังแก้ไขแล้วต้องได้พฤติกรรมดังนี้:

  1. เจ้าหน้าที่ที่มี online_lesson:edit_draft สามารถแก้ไขบทเรียนของตนเองในสถานะ Draft
  2. เจ้าหน้าที่ที่มี online_lesson:edit_draft สามารถแก้ไขบทเรียนของตนเองในสถานะ Returned
  3. เจ้าหน้าที่ไม่จำเป็นต้องมี online_lesson:revision เพื่อแก้ไขงานที่ถูกส่งกลับ
  4. ผู้ตรวจสอบยังต้องมี online_lesson:revision เพื่อกดส่งกลับ
  5. ผู้ใช้งานต้องไม่สามารถแก้ไขบทเรียนของบุคคลอื่น เว้นแต่มี Permission ระดับจัดการที่ระบบกำหนดไว้
  6. บทเรียนสถานะ Pending, Approved, Published หรือสถานะล็อกอื่น ต้องยังไม่สามารถแก้ไขได้ตาม Business Rule เดิม
  7. Frontend และ Backend ต้องใช้กฎเดียวกัน

ขอบเขตการตรวจสอบ

ตรวจสอบ Flow ทั้งหมดต่อไปนี้:

Permission Definitions
→ Role / Permission Template
→ Navigation / Action Visibility
→ Action Menu
→ Edit Button
→ Edit Page Guard
→ Form Initialization
→ Update API / Server Action
→ Workflow Service
→ Status Transition
→ Ownership Check
→ Audit Log
→ Resubmission

ค้นหาไฟล์หรือ Function ที่เกี่ยวข้อง เช่น:

permissions.ts
permission-constants.ts
online-lesson-permissions.ts
authorization.ts
requirePermission.ts
canEditOnlineLesson.ts
isOnlineLessonEditable.ts
OnlineLessonActionMenu
OnlineLessonCellAction
OnlineLessonEditPage
OnlineLessonForm
updateOnlineLesson
submitOnlineLessonForReview
returnOnlineLessonForRevision

ชื่อจริงอาจแตกต่าง ให้ตรวจสอบจาก Repository


Phase 1: ตรวจสอบ Permission Definition

ตรวจสอบว่า Permission ต่อไปนี้ถูกประกาศไว้ที่ใด:

online_lesson:create
online_lesson:edit_draft
online_lesson:submit
online_lesson:review
online_lesson:approve
online_lesson:revision
online_lesson:publish
online_lesson:manage

ให้ยืนยันความหมายจริงจาก:

  • Permission Seed
  • Permission Template
  • Permission Description
  • Role Mapping
  • UI Permission Matrix
  • Authorization Helper
  • Existing Tests
  • Documentation

ตรวจสอบว่า online_lesson:revision ถูกใช้ในตำแหน่งใดบ้าง และมีจุดใดนำไปใช้ผิดวัตถุประสงค์ เช่น:

canEdit =
  hasPermission("online_lesson:revision") && lesson.status === "returned";

หรือ:

requirePermission("online_lesson:revision");

ภายใน Update API

ให้จัดทำรายการ Usage ทั้งหมดก่อนแก้ไข


Phase 2: ตรวจสอบสถานะ Workflow จริง

ตรวจสอบ Enum หรือ Constant ของสถานะบทเรียนออนไลน์จาก Source of Truth จริง เช่น:

draft;
pending_review;
returned;
approved;
rejected;
scheduled;
published;
archived;

ต้องแยกให้ชัดเจนว่า:

  • สถานะใดหมายถึง “ส่งกลับให้แก้ไข”
  • สถานะใดหมายถึง “ปฏิเสธถาวร”
  • สถานะใดอนุญาตให้ส่งใหม่
  • สถานะใดไม่อนุญาตให้แก้ไขอีก

ห้ามเหมารวม returned และ rejected ว่าแก้ไขได้เหมือนกันโดยอัตโนมัติ

หาก Workflow ปัจจุบันกำหนดว่า:

returned = แก้ไขและส่งใหม่ได้
rejected = ปิดรายการ ไม่สามารถส่งใหม่ได้

ให้รองรับเฉพาะ returned

หากระบบใช้ rejected ในความหมายว่า “ส่งกลับให้แก้ไข” จริง ให้ยึดตาม Business Rule ปัจจุบัน แต่ควรบันทึกข้อสังเกตไว้ในรายงาน


Phase 3: สร้างหรือปรับกฎกลางสำหรับการแก้ไข

ควรมี Authorization Helper กลางสำหรับตรวจสอบสิทธิ์การแก้ไข เช่น:

canEditOnlineLesson({
  actor,
  lesson,
});

หรือใช้ Function เดิมที่มีอยู่แล้ว

แนวคิดที่ควรได้:

type CanEditOnlineLessonInput = {
  actorUserId: string;
  actorEmployeeId?: string;
  permissions: string[];
  lesson: {
    createdBy: string;
    status: OnlineLessonStatus;
  };
};

export function canEditOnlineLesson(input: CanEditOnlineLessonInput): boolean {
  const isOwner = input.lesson.createdBy === input.actorUserId;

  const hasEditDraftPermission = input.permissions.includes(
    "online_lesson:edit_draft",
  );

  const isEditableStatus = [
    OnlineLessonStatus.DRAFT,
    OnlineLessonStatus.RETURNED,
  ].includes(input.lesson.status);

  return isOwner && hasEditDraftPermission && isEditableStatus;
}

ตัวอย่างนี้เป็นเพียงแนวคิด ต้องใช้ Field และ Type จริงของระบบ

หากระบบมีสิทธิ์ระดับจัดการ เช่น:

online_lesson:manage
online_lesson:edit_all

ให้รองรับตาม Business Rule เดิม เช่น:

const canManageAll = hasPermission("online_lesson:manage");

แต่ห้ามเพิ่มสิทธิ์ใหม่โดยไม่จำเป็น


หลักการ Ownership

ผู้สร้างหรือเจ้าของบทเรียนควรแก้ไขได้เฉพาะบทเรียนของตนเอง เว้นแต่มี Permission ระดับจัดการทั้งหมด

ต้องตรวจสอบ Field ที่ใช้ระบุเจ้าของจริง เช่น:

createdBy;
created_by;
ownerId;
owner_id;
authorId;
submittedBy;
employeeId;

ห้ามตรวจ Ownership ด้วยชื่อหรืออีเมล หากระบบมี ID ที่ชัดเจน

ตัวอย่างแนวคิด:

const isOwner = lesson.createdByUserId === session.user.id;

หรือ:

const isOwner = lesson.createdByEmployeeId === currentEmployee.id;

ให้ใช้ Identifier เดียวกับระบบปัจจุบัน


จุดที่ต้องแก้ไขให้สอดคล้องกัน

1. Action Menu และปุ่มแก้ไข

ตรวจสอบเงื่อนไขการแสดงปุ่มแก้ไข

ตัวอย่างปัญหา:

const canEdit =
  lesson.status === "draft" || hasPermission("online_lesson:revision");

หรือ:

const canEditReturned = hasPermission("online_lesson:revision");

ให้เปลี่ยนไปใช้ Helper กลาง เช่น:

const canEdit = canEditOnlineLesson({
  actor,
  lesson,
});

ปุ่มแก้ไขต้องแสดงเมื่อ:

  • เป็นเจ้าของบทเรียน
  • มี online_lesson:edit_draft
  • สถานะอยู่ในรายการที่แก้ไขได้

2. Edit Page Guard

ตรวจสอบ Guard ของหน้าแก้ไข เช่น:

if (
  lesson.status !== "draft" &&
  !hasPermission("online_lesson:revision")
) {
  redirect(...);
}

ให้ปรับเป็นกฎเดียวกับ Action Menu

ตัวอย่าง:

if (!canEditOnlineLesson({ actor, lesson })) {
  forbidden();
}

ห้ามให้ Frontend แสดงปุ่มได้ แต่ Route Guard ปฏิเสธ


3. Update API หรือ Server Action

Backend ต้องตรวจสอบกฎเดียวกันเสมอ

ห้ามอาศัยเฉพาะการซ่อนปุ่มฝั่ง Frontend

ตรวจสอบ Logic เช่น:

requirePermission("online_lesson:revision");

ภายใน Update API สำหรับสถานะ Returned

ให้เปลี่ยนเป็น:

requirePermission("online_lesson:edit_draft");

ร่วมกับ:

requireOwnership(...)

และ Status Check

ตัวอย่างแนวคิด:

const lesson = await findOnlineLessonById(id);

if (!lesson) {
  throw new NotFoundError();
}

const allowed = canEditOnlineLesson({
  actor,
  lesson,
});

if (!allowed) {
  throw new ForbiddenError("คุณไม่มีสิทธิ์แก้ไขบทเรียนนี้");
}

4. Form Initialization

ตรวจสอบว่า Form ไม่ได้ Disable Field ทั้งหมดเมื่อสถานะไม่ใช่ Draft

ตัวอย่างปัญหา:

const isReadOnly = lesson.status !== "draft";

ควรเปลี่ยนเป็น:

const isReadOnly = !canEdit;

หาก Form รับ Prop เช่น:

<OnlineLessonForm readOnly={...} />

ให้ส่งค่าจาก Authorization Helper กลาง


5. ปุ่มบันทึกฉบับร่าง

เมื่อบทเรียนสถานะ Returned ถูกแก้ไขแล้วกดบันทึกฉบับร่าง ต้องกำหนด Transition ตาม Workflow เดิม

แนวทางที่แนะนำ:

returned → save draft → returned

เหตุผลคือยังเป็นงานที่ถูกส่งกลับและยังไม่ส่งเข้าตรวจสอบใหม่

แต่หากระบบปัจจุบันกำหนดว่า:

returned → save draft → draft

ให้ยึด Business Rule ที่ผ่านการอนุมัติแล้ว

ห้ามเปลี่ยน Status โดยไม่ตรวจสอบ Workflow จริง


6. ปุ่มส่งตรวจสอบใหม่

เมื่อเจ้าหน้าที่แก้ไขเสร็จและส่งตรวจสอบอีกครั้ง ควรเกิด Transition เช่น:

returned → pending_review

ต้องใช้ Permission ฝั่งผู้สร้าง เช่น:

online_lesson:submit

หรือ Permission จริงของระบบ

ห้ามใช้:

online_lesson:revision

สำหรับการส่งตรวจสอบใหม่


7. Action “ส่งกลับให้แก้ไข”

ฝั่งผู้ตรวจสอบต้องยังใช้ Permission:

online_lesson:revision

สำหรับ Transition เช่น:

pending_review → returned

ต้องตรวจสอบ:

  • ผู้ตรวจสอบมี Permission
  • สถานะปัจจุบันถูกต้อง
  • มีเหตุผลหรือ Comment ตาม Validation เดิม
  • บันทึกผู้ส่งกลับ
  • บันทึกวันที่
  • Audit Log
  • Notification ไปยังเจ้าของบทเรียน

การแก้ครั้งนี้ห้ามลดระดับความปลอดภัยของ Action ฝั่งผู้ตรวจสอบ


Permission Matrix ที่คาดหวัง

ตรวจสอบและปรับให้ได้พฤติกรรมตามแนวคิดนี้ โดยใช้ชื่อ Permission จริงของระบบ

Action เจ้าของบทเรียน ผู้ตรวจสอบ Permission หลัก
สร้างบทเรียน ได้ ตามสิทธิ์ online_lesson:create
แก้ไข Draft ของตนเอง ได้ ไม่เกี่ยวข้อง online_lesson:edit_draft
แก้ไข Returned ของตนเอง ได้ ไม่เกี่ยวข้อง online_lesson:edit_draft
ส่งตรวจสอบ ได้ ไม่เกี่ยวข้อง online_lesson:submit
ตรวจสอบบทเรียน ไม่ได้ ได้ online_lesson:review
ส่งกลับให้แก้ไข ไม่ได้ ได้ online_lesson:revision
อนุมัติ ไม่ได้ ได้ online_lesson:approve
เผยแพร่ ตาม Workflow ตามสิทธิ์ online_lesson:publish

ตารางนี้เป็นแนวทาง ต้องยึด Permission จริงในระบบ


แยก Permission กับ Status Transition

ห้ามตรวจเพียง Permission อย่างเดียว

การอนุญาต Action ต้องประกอบด้วย:

Permission
AND Ownership
AND Current Status
AND Allowed Transition

ตัวอย่าง:

const canEdit =
  hasPermission("online_lesson:edit_draft") && isOwner && isEditableStatus;

ตัวอย่างการส่งกลับ:

const canReturnForRevision =
  hasPermission("online_lesson:revision") && lesson.status === "pending_review";

ตรวจสอบการใช้ Permission ที่ผิดทั้งหมด

ค้นหาทั้ง Repository ด้วยคำ:

online_lesson:revision

ตรวจสอบทุกตำแหน่งว่าเป็นการใช้งานสำหรับ:

ผู้ตรวจสอบส่งกลับให้แก้ไข

จริงหรือไม่

ตำแหน่งที่อาจผิด:

  • ปุ่มแก้ไข Returned
  • Edit Page Guard
  • Update API
  • Form Read-only
  • Save Draft Action
  • Resubmit Action
  • Navigation
  • Permission Matrix Description
  • Automated Tests
  • Seed Data
  • UI Description

หากพบว่า Permission Description อธิบายคลุมเครือ เช่น:

แก้ไขบทเรียน

ให้ปรับคำอธิบายให้ชัดเจน เช่น:

ส่งบทเรียนกลับให้ผู้สร้างแก้ไข

โดยไม่เปลี่ยน Permission Key หากไม่จำเป็น


การจัดการ Rejected

ต้องตรวจสอบให้ชัดเจนว่าสถานะ rejected หมายถึงอะไร

กรณี Rejected คือปฏิเสธถาวร

ห้ามให้เจ้าของแก้ไขหรือส่งใหม่โดยอัตโนมัติ

พฤติกรรม:

rejected → locked

อาจต้องสร้างรายการใหม่แทน ตาม Business Rule

กรณี Rejected ถูกใช้แทน Returned

ให้พิจารณารองรับการแก้ไข แต่ต้องรายงานว่า Naming ของสถานะอาจไม่สอดคล้องกับความหมายทางธุรกิจ

ห้ามเปลี่ยนชื่อ Enum หรือทำ Database Migration ภายในงานนี้ เว้นแต่ได้รับคำสั่งเพิ่มเติม


Cache และ UI Refresh

หลังแก้ไขหรือส่งตรวจสอบใหม่ ต้องอัปเดตหน้ารายการทันที

ตรวจสอบ Data Fetching Pattern จริง เช่น:

  • TanStack Query
  • SWR
  • Server Component
  • Server Action

หลัง Mutation สำเร็จ ต้อง Invalidate หรือ Revalidate Query ที่เกี่ยวข้อง เช่น:

online-lessons
online-lesson-detail
online-lesson-counts
review-queue
notifications

ห้ามใช้:

window.location.reload();

ห้ามใช้ setTimeout() เพื่อแก้ปัญหา Refresh


Error Message

ปรับข้อความ Error ให้ตรงกับกฎจริง

ตัวอย่างข้อความที่ควรหลีกเลี่ยง:

สามารถแก้ไขได้เฉพาะสถานะฉบับร่างเท่านั้น

หาก Returned แก้ไขได้แล้ว

ควรเป็น:

สามารถแก้ไขได้เฉพาะบทเรียนฉบับร่างหรือบทเรียนที่ถูกส่งกลับให้แก้ไข

หรือใช้ข้อความตามมาตรฐานระบบ

กรณีไม่ใช่เจ้าของ:

คุณไม่มีสิทธิ์แก้ไขบทเรียนนี้

กรณีสถานะไม่อนุญาต:

ไม่สามารถแก้ไขบทเรียนในสถานะปัจจุบันได้

ไม่ควรเปิดเผยรายละเอียด Permission ภายในเกินจำเป็นต่อผู้ใช้ทั่วไป


Audit Log

ตรวจสอบว่าการแก้ไข Returned และการส่งตรวจสอบใหม่บันทึก Audit Log ตามมาตรฐานเดิม เช่น:

online_lesson.updated
online_lesson.resubmitted

ควรบันทึก:

  • Lesson ID
  • ผู้ดำเนินการ
  • สถานะก่อนหน้า
  • สถานะหลัง
  • วันที่เวลา
  • Field ที่แก้ไข หากระบบรองรับ
  • Revision Number หากมี

ห้ามลบประวัติการส่งกลับหรือเหตุผลเดิม


Notification

เมื่อเจ้าหน้าที่ส่งบทเรียนที่แก้ไขแล้วเข้าตรวจสอบใหม่ ให้ตรวจสอบ Notification ตาม Workflow เดิม เช่น:

  • แจ้งผู้ตรวจสอบ
  • แจ้ง HRD Manager
  • อัปเดต In-app Notification
  • Email หากระบบรองรับ

ห้ามส่ง Notification ซ้ำจากการกดปุ่มหลายครั้ง


Test Cases

Test Case 1: เจ้าของแก้ไข Draft

  1. Login เป็นเจ้าหน้าที่ที่มี online_lesson:edit_draft
  2. เปิดบทเรียน Draft ที่ตนเองสร้าง
  3. ต้องเห็นปุ่มแก้ไข
  4. เข้าหน้าแก้ไขได้
  5. บันทึกได้สำเร็จ

Test Case 2: เจ้าของแก้ไข Returned

  1. ผู้ตรวจสอบส่งบทเรียนกลับ
  2. Login เป็นเจ้าของบทเรียน
  3. เจ้าของไม่มี online_lesson:revision
  4. เจ้าของมี online_lesson:edit_draft
  5. ต้องเห็นปุ่มแก้ไข
  6. เข้าหน้าแก้ไขได้
  7. บันทึกได้
  8. ไม่ได้รับ Forbidden Error

Test Case 3: เจ้าของไม่มี Edit Draft Permission

  1. Login เป็นเจ้าของบทเรียน
  2. ไม่มี online_lesson:edit_draft
  3. บทเรียนอยู่สถานะ Returned
  4. ต้องไม่เห็นปุ่มแก้ไข
  5. เข้าหน้า Edit โดย URL ต้องถูกปฏิเสธ
  6. เรียก Update API โดยตรงต้องได้ Forbidden

Test Case 4: ผู้ใช้พยายามแก้บทเรียนคนอื่น

  1. Login เป็นเจ้าหน้าที่ A
  2. มี online_lesson:edit_draft
  3. เปิดบทเรียน Returned ของเจ้าหน้าที่ B
  4. ต้องไม่เห็นปุ่มแก้ไข
  5. Route Guard ต้องปฏิเสธ
  6. API ต้องปฏิเสธ

Test Case 5: ผู้ตรวจสอบส่งกลับ

  1. Login เป็นผู้ตรวจสอบ
  2. มี online_lesson:revision
  3. เปิดบทเรียน Pending Review
  4. สามารถส่งกลับให้แก้ไขได้
  5. สถานะเปลี่ยนเป็น Returned
  6. เจ้าของได้รับ Notification

Test Case 6: เจ้าหน้าที่ไม่มี Revision แต่แก้ Returned ได้

  1. เจ้าหน้าที่มี:

    • online_lesson:edit_draft
    • ไม่มี online_lesson:revision
  2. บทเรียนเป็นของตนเอง

  3. สถานะ Returned

  4. ต้องแก้ไขและบันทึกได้

Test Case นี้เป็น Acceptance Test หลักของงาน


Test Case 7: ส่งตรวจสอบใหม่

  1. เจ้าของแก้ไข Returned
  2. กดส่งตรวจสอบใหม่
  3. ใช้ Permission ฝั่ง Submit
  4. สถานะเปลี่ยนเป็น Pending Review
  5. รายการออกจาก Returned Filter
  6. ไปปรากฏใน Pending Review
  7. ผู้ตรวจสอบได้รับ Notification

Test Case 8: Pending Review ต้องแก้ไม่ได้

  1. เจ้าหน้าที่เป็นเจ้าของบทเรียน
  2. มี online_lesson:edit_draft
  3. สถานะ Pending Review
  4. ต้องไม่เห็นปุ่มแก้ไข
  5. Route และ API ต้องปฏิเสธ

Test Case 9: Approved/Published ต้องแก้ไม่ได้

ทดสอบสถานะ:

approved
published
scheduled
archived

หรือสถานะจริงของระบบ

เจ้าของที่มี online_lesson:edit_draft ต้องไม่สามารถแก้ไขได้ เว้นแต่มี Business Rule หรือ Permission ระดับจัดการที่รองรับอยู่แล้ว


Test Case 10: Rejected

ตรวจสอบตามความหมายจริง:

  • หาก Rejected ถาวร ต้องแก้ไม่ได้
  • หาก Rejected หมายถึงส่งกลับ ต้องแก้ได้ตามกฎเดียวกับ Returned

ให้รายงานผลชัดเจน


Test Case 11: Direct API Access

ทดสอบเรียก API โดยไม่ผ่าน UI:

  • เจ้าของ + Edit Draft + Returned → สำเร็จ
  • เจ้าของ + ไม่มี Edit Draft → Forbidden
  • ไม่ใช่เจ้าของ + Edit Draft → Forbidden
  • Revision อย่างเดียวแต่ไม่ใช่ผู้สร้าง → ต้องไม่สามารถใช้ Update API ของเจ้าของได้ เว้นแต่มี Manage Permission

Test Case 12: Permission Regression

ตรวจสอบว่า:

  • ผู้ตรวจสอบยังส่งกลับได้
  • เจ้าหน้าที่ยังส่งตรวจสอบได้
  • ผู้อนุมัติยังอนุมัติได้
  • ผู้เผยแพร่ยังเผยแพร่ได้
  • ไม่มี Permission ใดได้รับสิทธิ์เกินเดิม

Automated Tests

หาก Repository มี Test Framework ให้เพิ่มหรือปรับปรุง Test ตาม Pattern เดิม

อย่างน้อยต้องครอบคลุม Authorization Matrix:

owner + edit_draft + draft = allow
owner + edit_draft + returned = allow
owner + edit_draft + pending = deny
owner + revision + returned แต่ไม่มี edit_draft = deny edit
reviewer + revision + pending = allow return
non-owner + edit_draft + returned = deny
manage permission + supported status = follow existing rule

เพิ่ม Test สำหรับ:

  • Action visibility
  • Route Guard
  • Update API
  • Resubmit Action
  • Workflow Transition
  • Permission Description หากมี Snapshot หรือ Seed Test

Validation ก่อนส่งงาน

รันคำสั่งจริงจาก package.json เช่น:

pnpm typecheck
pnpm lint
pnpm test
pnpm build

ห้ามแก้ Type Error ด้วย:

any
@ts-ignore
@ts-expect-error
eslint-disable

เว้นแต่มีเหตุผลจำเป็นและอธิบายในรายงาน


Acceptance Criteria

งานถือว่าเสร็จเมื่อ:

  • เจ้าของบทเรียน Returned สามารถแก้ไขได้ด้วย online_lesson:edit_draft
  • เจ้าของไม่จำเป็นต้องมี online_lesson:revision
  • online_lesson:revision ใช้สำหรับ Action ส่งกลับของผู้ตรวจสอบ
  • Action Menu แสดงปุ่มถูกต้อง
  • Edit Page Guard ใช้กฎเดียวกัน
  • Form ไม่ถูก Read-only ผิดพลาด
  • Update API ใช้กฎเดียวกัน
  • Ownership ถูกตรวจสอบ
  • Draft และ Returned แก้ไขได้ตามกฎ
  • Pending, Approved และ Published ยังถูกล็อก
  • Rejected ทำงานตาม Business Rule จริง
  • สามารถบันทึกฉบับร่างหลังถูกส่งกลับ
  • สามารถส่งตรวจสอบใหม่
  • Cache และหน้ารายการอัปเดตทันที
  • Audit Log ถูกต้อง
  • Notification ไม่ซ้ำ
  • Permission ฝั่งผู้ตรวจสอบไม่ถูกลดระดับ
  • ไม่มีการเปิดให้แก้ไขบทเรียนของคนอื่น
  • Type Check ผ่าน
  • Lint ผ่าน
  • Test ผ่าน
  • Build ผ่าน

รูปแบบรายงานผลหลังแก้ไข

หลังดำเนินการเสร็จ ให้รายงานตามหัวข้อต่อไปนี้

1. สาเหตุของปัญหา

ระบุจุดที่ใช้ online_lesson:revision ผิดหน้าที่ พร้อม Path และ Function ที่เกี่ยวข้อง

2. ความหมาย Permission หลังตรวจสอบ

สรุปว่าแต่ละ Permission ใช้ทำอะไร โดยเฉพาะ:

online_lesson:edit_draft
online_lesson:revision
online_lesson:submit

3. สถานะที่แก้ไขได้

ระบุสถานะจริง เช่น:

draft
returned

และอธิบาย Rejected ว่ามีความหมายอย่างไร

4. Ownership Rule

ระบุ Field ที่ใช้ตรวจสอบเจ้าของบทเรียน

5. ไฟล์ที่แก้ไข

ระบุ Path และหน้าที่ของแต่ละไฟล์

6. Authorization Helper

อธิบาย Helper กลางที่สร้างหรือปรับปรุง และตำแหน่งที่นำไปใช้

7. Frontend Changes

อธิบาย:

  • Action Menu
  • Edit Button
  • Form Read-only
  • Error Message
  • Cache Refresh

8. Backend Changes

อธิบาย:

  • API Authorization
  • Status Check
  • Ownership Check
  • Workflow Transition

9. Permission Matrix

สรุปผลลัพธ์ของแต่ละ Role หรือ Permission Combination

10. ผลการทดสอบ

รายงาน:

  • Manual Test
  • Authorization Test
  • Automated Test
  • Type Check
  • Lint
  • Build

11. Regression Check

ยืนยันว่า:

  • ผู้ตรวจสอบยังส่งกลับได้
  • เจ้าหน้าที่แก้เฉพาะงานตนเอง
  • Pending และ Published ยังล็อก
  • Permission อื่นไม่เปลี่ยน
  • หน้า List และ Action Menu อัปเดตถูกต้อง

12. ประเด็นคงค้าง

หากพบว่า Naming ของ rejected หรือ Permission Description ยังคลุมเครือ ให้รายงานแยก ห้ามทำ Migration หรือเปลี่ยน Permission Key โดยไม่ได้รับคำสั่ง


ข้อกำหนดสุดท้าย

  • ตรวจสอบโค้ดจริงก่อนแก้ไข
  • แยกสิทธิ์ “แก้ไขงานของผู้สร้าง” ออกจากสิทธิ์ “ส่งกลับของผู้ตรวจสอบ”
  • ใช้ online_lesson:edit_draft สำหรับเจ้าของบทเรียน Draft/Returned
  • ใช้ online_lesson:revision สำหรับผู้ตรวจสอบส่งกลับ
  • ต้องตรวจ Ownership ทุกครั้ง
  • Frontend และ Backend ต้องใช้กฎเดียวกัน
  • ห้ามแก้เฉพาะการซ่อนหรือแสดงปุ่ม
  • ห้ามอาศัย Frontend Authorization เพียงอย่างเดียว
  • ห้ามให้ revision กลายเป็นสิทธิ์แก้ไขบทเรียนของบุคคลอื่น
  • ห้ามเพิ่ม Permission ใหม่หากของเดิมรองรับอยู่แล้ว
  • ห้ามเปลี่ยน Database Schema หรือ Permission Key โดยไม่จำเป็น
  • ห้ามใช้ window.location.reload() เพื่อแก้ UI
  • ต้องมี Test ครอบคลุม Permission Matrix
  • ต้องยืนยันว่าเจ้าหน้าที่ที่ไม่มี online_lesson:revision แต่มี online_lesson:edit_draft สามารถแก้ไขบทเรียน Returned ของตนเองได้จริง