S3 — Activity Report & Course Registration (PRC) — Target Schema (Phase 2)¶
TARGET DESIGN — ไม่ใช่ schema ปัจจุบัน
ออกแบบใหม่ (greenfield) เพื่อ implement requirement จาก SRS S3 v2.0.0 (TOR 7.13) · ระบบนี้ยังไม่มี DB จริง — de-facto contract คือ demo ar_and_crs/fe/src/data.jsx (11 entity) · 📋 สิ่งที่ต่างจาก demo · DDL เต็ม: schema.sql · ERD · Data Dictionary · 🛠️ Backend Implementation · 🔄 วงจรธุรกิจหลัก (Business Core)
ทำไมต้องออกแบบใหม่ (greenfield)¶
S3 ไม่มีระบบ legacy ให้ migrate — demo ar_and_crs (React/Vite + NestJS) เก็บข้อมูลเป็น mock array
ใน data.jsx (11 entity) เท่านั้น งานนี้คือแปลง demo contract นั้นเป็น clean target schema ระดับ production:
- normalize array → junction —
PROJECTS.courses[]→ProjectCourse·MEMBERS.courses[]→Enrollment·TRAINERS.specialties[]→TrainerSpecialty - เพิ่ม FK + audit + workflow ที่ demo ไม่มี — FK referential integrity ครบทุก relation, audit cols (
CreatedAt/CreatedBy/UpdatedAt/UpdatedBy), workflow state (TrainerEditRequest,Registrationapproval,KnowledgeReportflag) - polymorphic
Attachment— 1 ตารางแนบไฟล์เดียว (OwnerEntityType/OwnerEntityId) ใช้ร่วมทุก entity แทนการแยกตารางแนบไฟล์ต่อ entity - thin-mirror auth —
AppUser/RoleAssignment/Roleเป็นเงา (mirror) ของ SSO กลาง ไม่เก็บรหัสผ่าน
S3-specific deviation จาก design_p2 D7 — PK = int IDENTITY (ไม่ใช่ GUID)
S8/S9 ใช้ uniqueidentifier (GUID) เป็น PK เพราะต้อง carry over จาก legacy · S3 เป็น greenfield ไม่มี GUID เดิมให้รักษา
จึงใช้ int IDENTITY(1,1) (running number) เป็น PK ทุกตาราง — เรียบง่ายและ index-friendly กว่า ; ตาราง log ปริมาณสูงใช้ bigint IDENTITY
Audit cols CreatedBy/UpdatedBy เป็น int FK → PRC.AppUser (ต่างจาก S8/S9 ที่เก็บเป็น nvarchar)
Role Hierarchy (SRS §3.1, 6 roles) — บริหารรวมศูนย์¶
ADMIN · OFFICER_KSS · OFFICER_OTHER · TRAINER · VIEWER · CITIZEN
| Role | สิทธิ์โดยสรุป |
|---|---|
| ADMIN | เห็นและจัดการทุกเมนู, อนุมัติคำขอ, จัดการ Role |
| OFFICER_KSS | บันทึก-แก้ไขหลักสูตร/โครงการ/งบประมาณ/ครุภัณฑ์, พิจารณาคำขอลงทะเบียน |
| OFFICER_OTHER | บันทึกรายงานเผยแพร่ฯ ของตนเอง, ร่างประวัติการสอนวิทยากร (รออนุมัติ) |
| TRAINER | ดูตารางหลักสูตรของตน, แก้ไขข้อมูลส่วนตัวเมื่อ ADMIN เปิดสิทธิ์ |
| VIEWER | ผู้บริหาร — Dashboard + รายงาน (read-only) |
| CITIZEN | Public Catalog + สมัครหลักสูตร + ตรวจสอบสถานะ + ดาวน์โหลดประกาศนียบัตร |
กำหนดสิทธิ์ที่ส่วนกลาง (SSO via JWT)
การยืนยันตัวตน + การมอบสิทธิ์อยู่ที่ SSO กลาง (ThaID / Keycloak federate กับ RLPD Active Directory)
S3 อ่านบทบาทจริงจาก JWT ขณะ runtime · AppUser + RoleAssignment ใน schema นี้เป็นเพียง local mirror
(auto-provision เมื่อ login ครั้งแรก) — ไม่มีคอลัมน์ password/lockout ; Role เป็น catalog 6 บทบาทไว้ map เมนูในแอป
ดู Backend Implementation §6
🔑 Requirement → Table Traceability (spine)¶
| REQ | TOR | สาระสำคัญ | ตารางที่รองรับ |
|---|---|---|---|
| REQ-PRC-001 | 7.13.1(1) | Dashboard real-time + Thailand heatmap + KPI | Province (new), Course (new), Registration (new), Certificate (new), Member (new), Enrollment (new) — aggregate queries |
| REQ-PRC-002 | 7.13.1(2) | สมาชิก CRUD + ค้นหา + soft-delete | Member (IsDeleted) (new), MemberType (new), Enrollment (new), Province (new), OrgUnit (new) |
| REQ-PRC-003 | 7.13.1(3) | หลักสูตร/โครงการ/วิทยากร + แนบไฟล์ + edit-request | Course (new), Project (new), ProjectCourse (new), Trainer (new), TrainerSpecialty (new), TrainerEditRequest (new), Attachment (new) |
| REQ-PRC-004 | 7.13.1(4) | Per-Head budget + 2-step delete | BudgetCalculation (new), Project (IsDeleted) (new), AuditLog (new) |
| REQ-PRC-005 | 7.13.1(5) | ครุภัณฑ์ 4 สถานะ / อายุ 5 ปี / audit โอนผู้รับผิดชอบ | Equipment (new), EquipmentOwnershipHistory (new) |
| REQ-PRC-006 | 7.13.1(6) | รายงานเผยแพร่ความรู้ + flag กลับให้แก้ไข | KnowledgeReport (new), KnowledgeType (new), OrgUnit (new), Province (new) |
| REQ-PRC-007 | 7.13.1(7) | Dashboard export + drill-down รายจังหวัด | (ไม่มี schema) อ่าน aggregate ของ REQ-001 ; export action → AuditLog (new) |
| REQ-PRC-008 | 7.13.2(1) | ลงทะเบียน + approval queue + draft/publish + prerequisite | Registration (new), Course (Status/IsDraft) (new), CoursePrerequisite (new), Enrollment (new), EmailNotification (new) |
| REQ-PRC-009 | 7.13.2(2) | Public catalog 2 หมวด (โครงการ / นักบริหารฯ) | Course (Category) (new), Batch (new), Trainer (new), Attachment (new) |
| REQ-PRC-010 | 7.13.2(3) | E-mail แจ้งผล + retry 3× + เช็คสถานะ | EmailNotification (new), EmailAttempt (new), Registration (new) |
| REQ-PRC-011 | 7.13.2(4) | ค้นหา + ส่งออก Excel/CSV (เจ้าหน้าที่) | (ไม่มี schema) อ่าน Registration / Member / Course / Enrollment |
| REQ-PRC-012 | Extra-HYB | Hybrid (Onsite + Online URL พร้อมกัน) | Course (Mode + OnsiteVenue + OnlineUrl) (new) |
| REQ-PRC-013 | Extra-LVL | ระดับหลักสูตร + Batch + ทำเนียบรุ่น (Alumni) | Course (Level) (new), Batch (new), Enrollment (new), Member (new), Certificate (new) |
| REQ-PRC-014 | Extra-WM | E-cert + ลายน้ำ "สำเนา" + audit การพิมพ์ | Certificate (new), CertificatePrintLog (new), AuditLog (new) |
| Auth | SRS §3.1 | SSO via JWT + RBAC 6 บทบาท | AppUser (mirror), RoleAssignment (mirror), Role (mirror) |
REQ ที่ไม่มีผลต่อ schema (จัดการที่ app/infra layer)
- REQ-PRC-007 — chart rendering + Dashboard export
.pdf/.png/.jpeg(≥ 1920×1080) = frontend/report engine ; เก็บได้แค่ export action ในAuditLog - REQ-PRC-011 — ส่งออก Excel / CSV สำหรับเจ้าหน้าที่ = generate ที่ app layer จาก query (ไม่มีตารางเฉพาะ)
- SSL/auth — TLS 1.2+, การยืนยันตัวตน + lockout policy อยู่ที่ central SSO (ThaID / Keycloak realm) ; S3 อ่านจาก JWT เท่านั้น
Table Inventory (28 ตาราง, schema PRC)¶
| กลุ่ม | ตาราง |
|---|---|
| Lookup | Province, MemberType, KnowledgeType, OrgUnit, Role |
| Identity/Auth (mirror) | AppUser (SSO profile, no password), RoleAssignment — role grants จาก SSO กลาง |
| Course Catalog | Course, ProjectCourse, CoursePrerequisite |
| Project/Budget | Project, BudgetCalculation |
| Trainer | Trainer, TrainerSpecialty, TrainerEditRequest |
| Member/Alumni | Member, Enrollment |
| Registration & Approval | Registration |
| Batch | Batch |
| Certificate | Certificate, CertificatePrintLog |
| Equipment | Equipment, EquipmentOwnershipHistory |
| Knowledge | KnowledgeReport |
| Notification/Email | EmailNotification, EmailAttempt |
| Audit | AuditLog |
| File | Attachment (single polymorphic) |
Demo → Target mapping (สรุป)¶
demo data.jsx มี 11 entity — ตารางที่เหลือใน 28 ตารางเป็น support tables / lookups / mirror auth ที่เพิ่มเข้ามาเพื่อ normalize + workflow + integrity
| Demo entity (data.jsx) | Target (PRC) | หมายเหตุ |
|---|---|---|
COURSES |
Course |
+ CourseCode UNIQUE, Category/Level/Mode/Status = CHECK, IsDraft, OnsiteVenue+OnlineUrl (Hybrid), HasCert, IsDeleted |
PROJECTS |
Project + ProjectCourse |
PROJECTS.courses[] → junction ProjectCourse (N:M) ; Project.IsDeleted = 2-step delete |
TRAINERS |
Trainer + TrainerSpecialty |
TRAINERS.specialties[] → junction TrainerSpecialty (1:N) |
MEMBERS |
Member + Enrollment |
MEMBERS.courses[] → junction Enrollment (Member↔Course↔Batch) ; Member.IsDeleted บังคับ (REQ-002) |
REGISTRATIONS |
Registration |
+ RegistrationNo UNIQUE, Status/Channel = CHECK, approval queue, EmailSent denorm |
CERTIFICATES |
Certificate |
+ CertificateNo UNIQUE, Score CHECK 0..100, Result PASS/FAIL, IsOriginalIssued, ReprintCount |
BATCHES |
Batch |
+ UNIQUE(CourseId, BatchNo), YearBE (int พ.ศ. label), CompletedCount denorm |
EQUIPMENT |
Equipment |
+ EquipmentCode UNIQUE, Status 4 ค่า = CHECK, AcquiredAt → คำนวณอายุ 5 ปี runtime |
KNOWLEDGE_REPORTS |
KnowledgeReport |
+ KnowledgeTypeId FK, Status PENDING/APPROVED/NEEDS_FIX, FlagNote, SubmittedBy/FlaggedBy |
EMAIL_LOG |
EmailNotification |
+ Status PENDING/SENT/FAILED/BOUNCED, RetryCount CHECK 0..3, NextRetryAt (backoff) |
AUDIT_LOG |
AuditLog |
append-only, denormalized ActorName/ActorRole, EntityType/EntityId = varchar |
Support tables / mirror auth ที่เพิ่มใหม่ (ไม่มีใน demo):
| Target (PRC) | เพิ่มมาเพื่อ |
|---|---|
BudgetCalculation |
สแนปช็อต Per-Head (user-confirm, ไม่ auto-save) — REQ-004 |
CoursePrerequisite |
เงื่อนไขหลักสูตรก่อนสมัคร (self-ref course→course) — REQ-008 (ยืนยันโดย กสส.) |
TrainerEditRequest |
คำขอแก้ประวัติวิทยากร (ร่าง → ADMIN อนุมัติ) — REQ-003 |
CertificatePrintLog |
append-only บันทึกการพิมพ์ทุกครั้ง (ORIGINAL/COPY + watermark) — REQ-014 |
EquipmentOwnershipHistory |
append-only การเปลี่ยนผู้รับผิดชอบครุภัณฑ์ — REQ-005 |
EmailAttempt |
append-only per-try รองรับ retry/backoff — REQ-010 |
Attachment |
single polymorphic — แทนการแนบไฟล์แยกราย entity ใน demo (Course.attached = derived COUNT(*)) |
AppUser / RoleAssignment / Role |
thin-mirror auth — SSO via JWT (ไม่มี password) |
Province / MemberType / KnowledgeType / OrgUnit |
lookup tables — normalize enum/free-text ของ demo |
การยืนยันจาก กสส. (resolved)¶
ประเด็นที่เคยเปิดไว้ — ได้รับการยืนยันจาก กสส. แล้ว:
| ประเด็น | คำตอบที่ยืนยัน | ผลต่อ schema |
|---|---|---|
| IdP ต่อ user class | ใช้ทุกช่องทาง — AD (เจ้าหน้าที่), ThaID (CITIZEN), Keycloak realm rlpd ผ่าน S9 (เหมือน S8) |
ไม่เปลี่ยน — AppUser.AuthSource รองรับหลาย IdP แล้ว |
| Per-Head formula (REQ-004) | (CourseCost + AdminCost) / HeadCount — ไม่มีก้อนค่าใช้จ่ายแยก |
ไม่เปลี่ยน — BudgetCalculation ครบแล้ว |
| Retention ของ log | เก็บ 1 ปี (AuditLog / EmailNotification / EmailAttempt) |
ไม่เปลี่ยนโครงสร้าง — วางแผน archive/purge เมื่ออายุ > 1 ปี |
| OrgUnit master | มี master list ของหน่วยงาน | คง OrgUnit (FK) ; OrgText ไว้รองรับหน่วยงานนอก master เท่านั้น |
| Prerequisites (REQ-008) | มี เงื่อนไขหลักสูตรก่อนสมัคร ; ไม่จำกัดจำนวน รับ | 🆕 เพิ่มตาราง CoursePrerequisite ; capacity คง Course.TargetCount (soft) |
| Post-test / Score | คะแนน กรอกมือ ไม่มีระบบสอบในตัว | ไม่เปลี่ยน — Certificate.Score = ค่าที่กรอก |
HR / Legacy humanright_file integration — เลื่อนเป็น next-phase
SRS (ar_and_crs/README.md → System Integration) ระบุไว้แต่เป็น "ระยะถัดไป" (push ประวัติเข้า HR) และ "ถ้านำมาใช้ / อยู่ระหว่างพิจารณา" (legacy humanright_file)
→ v1 ไม่กระทบ schema (ทำที่ API/ETL layer) · เมื่อ กสส. ตัดสินใจทำเมื่อใด → เพิ่ม export-log table แบบ S9 OpenApiExecuteLog