1113 lines
37 KiB
Markdown
1113 lines
37 KiB
Markdown
# 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 ขึ้นมาเอง ให้ยืนยันจากโค้ดจริงก่อนทุกครั้ง
|
|
|
|
---
|
|
|
|
# ปัญหาปัจจุบัน
|
|
|
|
ในหน้าบทเรียนออนไลน์ของเจ้าหน้าที่ เมื่อบทเรียนถูกผู้ตรวจสอบส่งกลับมาแก้ไข รายการมีสถานะประเภท:
|
|
|
|
```text
|
|
returned
|
|
rejected
|
|
revision_required
|
|
changes_requested
|
|
```
|
|
|
|
หรือสถานะจริงที่ระบบใช้อยู่
|
|
|
|
เจ้าหน้าที่ซึ่งเป็นเจ้าของหรือผู้สร้างบทเรียนควรสามารถแก้ไขงานของตนเองและส่งตรวจสอบใหม่ได้ แต่ปัจจุบันระบบกลับตรวจสอบสิทธิ์ด้วย Permission:
|
|
|
|
```text
|
|
online_lesson:revision
|
|
```
|
|
|
|
ทำให้เจ้าหน้าที่ทั่วไปที่ไม่มี Permission ดังกล่าว:
|
|
|
|
- ไม่เห็นปุ่มแก้ไข
|
|
- ไม่สามารถเข้าหน้าแก้ไข
|
|
- หรือเปิดหน้าแก้ไขได้แต่ไม่สามารถบันทึก
|
|
- ไม่สามารถส่งตรวจสอบใหม่
|
|
- ถูกปฏิเสธจาก Frontend หรือ Backend Authorization
|
|
|
|
จากการวิเคราะห์เบื้องต้น ปัญหาไม่น่าเกิดจากตัว Form โดยตรง แต่เกิดจากการตีความ Permission ผิดหน้าที่
|
|
|
|
---
|
|
|
|
# หลักการ Permission ที่ถูกต้อง
|
|
|
|
ให้แยกความหมายของ Permission อย่างชัดเจนดังนี้
|
|
|
|
## `online_lesson:revision`
|
|
|
|
Permission นี้ควรเป็นสิทธิ์ของฝั่งผู้ตรวจสอบหรือผู้อนุมัติ สำหรับการดำเนินการ:
|
|
|
|
```text
|
|
ส่งบทเรียนกลับให้เจ้าหน้าที่แก้ไข
|
|
```
|
|
|
|
ตัวอย่าง Action:
|
|
|
|
```text
|
|
pending_review → returned
|
|
```
|
|
|
|
หรือ Transition จริงของระบบ
|
|
|
|
Permission นี้ไม่ควรถูกใช้เป็นเงื่อนไขหลักในการอนุญาตให้เจ้าของบทเรียนแก้ไขรายการที่ถูกส่งกลับ
|
|
|
|
---
|
|
|
|
## `online_lesson:edit_draft`
|
|
|
|
Permission นี้ควรเป็นสิทธิ์ของฝั่งผู้สร้างหรือเจ้าหน้าที่ สำหรับการ:
|
|
|
|
- แก้ไขบทเรียนฉบับร่างของตนเอง
|
|
- แก้ไขบทเรียนของตนเองที่ถูกส่งกลับ
|
|
- บันทึกการแก้ไข
|
|
- ส่งตรวจสอบใหม่
|
|
|
|
ตัวอย่างสถานะที่ควรแก้ไขได้:
|
|
|
|
```text
|
|
draft
|
|
returned
|
|
```
|
|
|
|
หรือสถานะจริงของระบบ เช่น:
|
|
|
|
```text
|
|
draft
|
|
revision_required
|
|
rejected
|
|
```
|
|
|
|
ต้องตรวจสอบ Workflow จริงก่อนกำหนดรายการสถานะ
|
|
|
|
---
|
|
|
|
# เป้าหมาย
|
|
|
|
ปรับ Authorization Logic ให้เจ้าของหรือผู้สร้างบทเรียนที่ถูกส่งกลับสามารถแก้ไขงานของตนเองได้ด้วย Permission ฝั่งผู้สร้าง เช่น:
|
|
|
|
```text
|
|
online_lesson:edit_draft
|
|
```
|
|
|
|
โดยยังคงให้:
|
|
|
|
```text
|
|
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 ทั้งหมดต่อไปนี้:
|
|
|
|
```text
|
|
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 ที่เกี่ยวข้อง เช่น:
|
|
|
|
```text
|
|
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 ต่อไปนี้ถูกประกาศไว้ที่ใด:
|
|
|
|
```text
|
|
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` ถูกใช้ในตำแหน่งใดบ้าง และมีจุดใดนำไปใช้ผิดวัตถุประสงค์ เช่น:
|
|
|
|
```ts
|
|
canEdit =
|
|
hasPermission("online_lesson:revision") && lesson.status === "returned";
|
|
```
|
|
|
|
หรือ:
|
|
|
|
```ts
|
|
requirePermission("online_lesson:revision");
|
|
```
|
|
|
|
ภายใน Update API
|
|
|
|
ให้จัดทำรายการ Usage ทั้งหมดก่อนแก้ไข
|
|
|
|
---
|
|
|
|
# Phase 2: ตรวจสอบสถานะ Workflow จริง
|
|
|
|
ตรวจสอบ Enum หรือ Constant ของสถานะบทเรียนออนไลน์จาก Source of Truth จริง เช่น:
|
|
|
|
```ts
|
|
draft;
|
|
pending_review;
|
|
returned;
|
|
approved;
|
|
rejected;
|
|
scheduled;
|
|
published;
|
|
archived;
|
|
```
|
|
|
|
ต้องแยกให้ชัดเจนว่า:
|
|
|
|
- สถานะใดหมายถึง “ส่งกลับให้แก้ไข”
|
|
- สถานะใดหมายถึง “ปฏิเสธถาวร”
|
|
- สถานะใดอนุญาตให้ส่งใหม่
|
|
- สถานะใดไม่อนุญาตให้แก้ไขอีก
|
|
|
|
ห้ามเหมารวม `returned` และ `rejected` ว่าแก้ไขได้เหมือนกันโดยอัตโนมัติ
|
|
|
|
หาก Workflow ปัจจุบันกำหนดว่า:
|
|
|
|
```text
|
|
returned = แก้ไขและส่งใหม่ได้
|
|
rejected = ปิดรายการ ไม่สามารถส่งใหม่ได้
|
|
```
|
|
|
|
ให้รองรับเฉพาะ `returned`
|
|
|
|
หากระบบใช้ `rejected` ในความหมายว่า “ส่งกลับให้แก้ไข” จริง ให้ยึดตาม Business Rule ปัจจุบัน แต่ควรบันทึกข้อสังเกตไว้ในรายงาน
|
|
|
|
---
|
|
|
|
# Phase 3: สร้างหรือปรับกฎกลางสำหรับการแก้ไข
|
|
|
|
ควรมี Authorization Helper กลางสำหรับตรวจสอบสิทธิ์การแก้ไข เช่น:
|
|
|
|
```ts
|
|
canEditOnlineLesson({
|
|
actor,
|
|
lesson,
|
|
});
|
|
```
|
|
|
|
หรือใช้ Function เดิมที่มีอยู่แล้ว
|
|
|
|
แนวคิดที่ควรได้:
|
|
|
|
```ts
|
|
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 จริงของระบบ
|
|
|
|
หากระบบมีสิทธิ์ระดับจัดการ เช่น:
|
|
|
|
```text
|
|
online_lesson:manage
|
|
online_lesson:edit_all
|
|
```
|
|
|
|
ให้รองรับตาม Business Rule เดิม เช่น:
|
|
|
|
```ts
|
|
const canManageAll = hasPermission("online_lesson:manage");
|
|
```
|
|
|
|
แต่ห้ามเพิ่มสิทธิ์ใหม่โดยไม่จำเป็น
|
|
|
|
---
|
|
|
|
# หลักการ Ownership
|
|
|
|
ผู้สร้างหรือเจ้าของบทเรียนควรแก้ไขได้เฉพาะบทเรียนของตนเอง เว้นแต่มี Permission ระดับจัดการทั้งหมด
|
|
|
|
ต้องตรวจสอบ Field ที่ใช้ระบุเจ้าของจริง เช่น:
|
|
|
|
```ts
|
|
createdBy;
|
|
created_by;
|
|
ownerId;
|
|
owner_id;
|
|
authorId;
|
|
submittedBy;
|
|
employeeId;
|
|
```
|
|
|
|
ห้ามตรวจ Ownership ด้วยชื่อหรืออีเมล หากระบบมี ID ที่ชัดเจน
|
|
|
|
ตัวอย่างแนวคิด:
|
|
|
|
```ts
|
|
const isOwner = lesson.createdByUserId === session.user.id;
|
|
```
|
|
|
|
หรือ:
|
|
|
|
```ts
|
|
const isOwner = lesson.createdByEmployeeId === currentEmployee.id;
|
|
```
|
|
|
|
ให้ใช้ Identifier เดียวกับระบบปัจจุบัน
|
|
|
|
---
|
|
|
|
# จุดที่ต้องแก้ไขให้สอดคล้องกัน
|
|
|
|
## 1. Action Menu และปุ่มแก้ไข
|
|
|
|
ตรวจสอบเงื่อนไขการแสดงปุ่มแก้ไข
|
|
|
|
ตัวอย่างปัญหา:
|
|
|
|
```ts
|
|
const canEdit =
|
|
lesson.status === "draft" || hasPermission("online_lesson:revision");
|
|
```
|
|
|
|
หรือ:
|
|
|
|
```ts
|
|
const canEditReturned = hasPermission("online_lesson:revision");
|
|
```
|
|
|
|
ให้เปลี่ยนไปใช้ Helper กลาง เช่น:
|
|
|
|
```ts
|
|
const canEdit = canEditOnlineLesson({
|
|
actor,
|
|
lesson,
|
|
});
|
|
```
|
|
|
|
ปุ่มแก้ไขต้องแสดงเมื่อ:
|
|
|
|
- เป็นเจ้าของบทเรียน
|
|
- มี `online_lesson:edit_draft`
|
|
- สถานะอยู่ในรายการที่แก้ไขได้
|
|
|
|
---
|
|
|
|
## 2. Edit Page Guard
|
|
|
|
ตรวจสอบ Guard ของหน้าแก้ไข เช่น:
|
|
|
|
```ts
|
|
if (
|
|
lesson.status !== "draft" &&
|
|
!hasPermission("online_lesson:revision")
|
|
) {
|
|
redirect(...);
|
|
}
|
|
```
|
|
|
|
ให้ปรับเป็นกฎเดียวกับ Action Menu
|
|
|
|
ตัวอย่าง:
|
|
|
|
```ts
|
|
if (!canEditOnlineLesson({ actor, lesson })) {
|
|
forbidden();
|
|
}
|
|
```
|
|
|
|
ห้ามให้ Frontend แสดงปุ่มได้ แต่ Route Guard ปฏิเสธ
|
|
|
|
---
|
|
|
|
## 3. Update API หรือ Server Action
|
|
|
|
Backend ต้องตรวจสอบกฎเดียวกันเสมอ
|
|
|
|
ห้ามอาศัยเฉพาะการซ่อนปุ่มฝั่ง Frontend
|
|
|
|
ตรวจสอบ Logic เช่น:
|
|
|
|
```ts
|
|
requirePermission("online_lesson:revision");
|
|
```
|
|
|
|
ภายใน Update API สำหรับสถานะ Returned
|
|
|
|
ให้เปลี่ยนเป็น:
|
|
|
|
```ts
|
|
requirePermission("online_lesson:edit_draft");
|
|
```
|
|
|
|
ร่วมกับ:
|
|
|
|
```ts
|
|
requireOwnership(...)
|
|
```
|
|
|
|
และ Status Check
|
|
|
|
ตัวอย่างแนวคิด:
|
|
|
|
```ts
|
|
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
|
|
|
|
ตัวอย่างปัญหา:
|
|
|
|
```ts
|
|
const isReadOnly = lesson.status !== "draft";
|
|
```
|
|
|
|
ควรเปลี่ยนเป็น:
|
|
|
|
```ts
|
|
const isReadOnly = !canEdit;
|
|
```
|
|
|
|
หาก Form รับ Prop เช่น:
|
|
|
|
```tsx
|
|
<OnlineLessonForm readOnly={...} />
|
|
```
|
|
|
|
ให้ส่งค่าจาก Authorization Helper กลาง
|
|
|
|
---
|
|
|
|
## 5. ปุ่มบันทึกฉบับร่าง
|
|
|
|
เมื่อบทเรียนสถานะ Returned ถูกแก้ไขแล้วกดบันทึกฉบับร่าง ต้องกำหนด Transition ตาม Workflow เดิม
|
|
|
|
แนวทางที่แนะนำ:
|
|
|
|
```text
|
|
returned → save draft → returned
|
|
```
|
|
|
|
เหตุผลคือยังเป็นงานที่ถูกส่งกลับและยังไม่ส่งเข้าตรวจสอบใหม่
|
|
|
|
แต่หากระบบปัจจุบันกำหนดว่า:
|
|
|
|
```text
|
|
returned → save draft → draft
|
|
```
|
|
|
|
ให้ยึด Business Rule ที่ผ่านการอนุมัติแล้ว
|
|
|
|
ห้ามเปลี่ยน Status โดยไม่ตรวจสอบ Workflow จริง
|
|
|
|
---
|
|
|
|
## 6. ปุ่มส่งตรวจสอบใหม่
|
|
|
|
เมื่อเจ้าหน้าที่แก้ไขเสร็จและส่งตรวจสอบอีกครั้ง ควรเกิด Transition เช่น:
|
|
|
|
```text
|
|
returned → pending_review
|
|
```
|
|
|
|
ต้องใช้ Permission ฝั่งผู้สร้าง เช่น:
|
|
|
|
```text
|
|
online_lesson:submit
|
|
```
|
|
|
|
หรือ Permission จริงของระบบ
|
|
|
|
ห้ามใช้:
|
|
|
|
```text
|
|
online_lesson:revision
|
|
```
|
|
|
|
สำหรับการส่งตรวจสอบใหม่
|
|
|
|
---
|
|
|
|
## 7. Action “ส่งกลับให้แก้ไข”
|
|
|
|
ฝั่งผู้ตรวจสอบต้องยังใช้ Permission:
|
|
|
|
```text
|
|
online_lesson:revision
|
|
```
|
|
|
|
สำหรับ Transition เช่น:
|
|
|
|
```text
|
|
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 ต้องประกอบด้วย:
|
|
|
|
```text
|
|
Permission
|
|
AND Ownership
|
|
AND Current Status
|
|
AND Allowed Transition
|
|
```
|
|
|
|
ตัวอย่าง:
|
|
|
|
```ts
|
|
const canEdit =
|
|
hasPermission("online_lesson:edit_draft") && isOwner && isEditableStatus;
|
|
```
|
|
|
|
ตัวอย่างการส่งกลับ:
|
|
|
|
```ts
|
|
const canReturnForRevision =
|
|
hasPermission("online_lesson:revision") && lesson.status === "pending_review";
|
|
```
|
|
|
|
---
|
|
|
|
# ตรวจสอบการใช้ Permission ที่ผิดทั้งหมด
|
|
|
|
ค้นหาทั้ง Repository ด้วยคำ:
|
|
|
|
```text
|
|
online_lesson:revision
|
|
```
|
|
|
|
ตรวจสอบทุกตำแหน่งว่าเป็นการใช้งานสำหรับ:
|
|
|
|
```text
|
|
ผู้ตรวจสอบส่งกลับให้แก้ไข
|
|
```
|
|
|
|
จริงหรือไม่
|
|
|
|
ตำแหน่งที่อาจผิด:
|
|
|
|
- ปุ่มแก้ไข 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 อธิบายคลุมเครือ เช่น:
|
|
|
|
```text
|
|
แก้ไขบทเรียน
|
|
```
|
|
|
|
ให้ปรับคำอธิบายให้ชัดเจน เช่น:
|
|
|
|
```text
|
|
ส่งบทเรียนกลับให้ผู้สร้างแก้ไข
|
|
```
|
|
|
|
โดยไม่เปลี่ยน Permission Key หากไม่จำเป็น
|
|
|
|
---
|
|
|
|
# การจัดการ Rejected
|
|
|
|
ต้องตรวจสอบให้ชัดเจนว่าสถานะ `rejected` หมายถึงอะไร
|
|
|
|
## กรณี Rejected คือปฏิเสธถาวร
|
|
|
|
ห้ามให้เจ้าของแก้ไขหรือส่งใหม่โดยอัตโนมัติ
|
|
|
|
พฤติกรรม:
|
|
|
|
```text
|
|
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 ที่เกี่ยวข้อง เช่น:
|
|
|
|
```text
|
|
online-lessons
|
|
online-lesson-detail
|
|
online-lesson-counts
|
|
review-queue
|
|
notifications
|
|
```
|
|
|
|
ห้ามใช้:
|
|
|
|
```ts
|
|
window.location.reload();
|
|
```
|
|
|
|
ห้ามใช้ `setTimeout()` เพื่อแก้ปัญหา Refresh
|
|
|
|
---
|
|
|
|
# Error Message
|
|
|
|
ปรับข้อความ Error ให้ตรงกับกฎจริง
|
|
|
|
ตัวอย่างข้อความที่ควรหลีกเลี่ยง:
|
|
|
|
```text
|
|
สามารถแก้ไขได้เฉพาะสถานะฉบับร่างเท่านั้น
|
|
```
|
|
|
|
หาก Returned แก้ไขได้แล้ว
|
|
|
|
ควรเป็น:
|
|
|
|
```text
|
|
สามารถแก้ไขได้เฉพาะบทเรียนฉบับร่างหรือบทเรียนที่ถูกส่งกลับให้แก้ไข
|
|
```
|
|
|
|
หรือใช้ข้อความตามมาตรฐานระบบ
|
|
|
|
กรณีไม่ใช่เจ้าของ:
|
|
|
|
```text
|
|
คุณไม่มีสิทธิ์แก้ไขบทเรียนนี้
|
|
```
|
|
|
|
กรณีสถานะไม่อนุญาต:
|
|
|
|
```text
|
|
ไม่สามารถแก้ไขบทเรียนในสถานะปัจจุบันได้
|
|
```
|
|
|
|
ไม่ควรเปิดเผยรายละเอียด Permission ภายในเกินจำเป็นต่อผู้ใช้ทั่วไป
|
|
|
|
---
|
|
|
|
# Audit Log
|
|
|
|
ตรวจสอบว่าการแก้ไข Returned และการส่งตรวจสอบใหม่บันทึก Audit Log ตามมาตรฐานเดิม เช่น:
|
|
|
|
```text
|
|
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 ต้องแก้ไม่ได้
|
|
|
|
ทดสอบสถานะ:
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
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` เช่น:
|
|
|
|
```bash
|
|
pnpm typecheck
|
|
pnpm lint
|
|
pnpm test
|
|
pnpm build
|
|
```
|
|
|
|
ห้ามแก้ Type Error ด้วย:
|
|
|
|
```ts
|
|
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 ใช้ทำอะไร โดยเฉพาะ:
|
|
|
|
```text
|
|
online_lesson:edit_draft
|
|
online_lesson:revision
|
|
online_lesson:submit
|
|
```
|
|
|
|
## 3. สถานะที่แก้ไขได้
|
|
|
|
ระบุสถานะจริง เช่น:
|
|
|
|
```text
|
|
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 ของตนเองได้จริง
|