S2 — Proactive Justice Operation System (PJOS) — ER Diagram (Target Design)¶
TARGET DESIGN
ความสัมพันธ์ตาม schema.sql (FK ทุกเส้นถูกประกาศจริงใน DDL — solid line) · เป็น greenfield design modeled จาก mockup repos/pjos/fe — ยังไม่ลงฐานข้อมูลจริง · ดู data_dict.md และ index.md
ทุกตาราง business มี audit columns CreatedBy/UpdatedBy (int FK → AppUser) — ละไว้ในไดอะแกรมเพื่อความชัด วาดเฉพาะ FK เชิงความหมาย (ownership / actor) · audit/soft-delete ละไว้ทั้งหมด · รายการ FK ครบถ้วนอยู่ที่ตารางสรุปท้ายเอกสาร
ไดอะแกรมแยกตาม domain — ตารางที่เป็นสะพานข้าม domain (Case, AppUser, Province, RightsArea) จะปรากฏมากกว่าหนึ่งบล็อก
Auth & Identity (thin SSO mirror via S9 Portal)¶
erDiagram
Province ||--o{ AppUser : "ProvinceId"
AppUser ||--o{ RoleAssignment : "AppUserId"
Role ||--o{ RoleAssignment : "RoleId"
Province ||--o{ RoleAssignment : "ProvinceId (scope)"
AppUser ||--o{ RoleAssignment : "GrantedBy"
AppUser ||--o| OfficerProfile : "AppUserId"
Unit ||--o{ OfficerProfile : "UnitId"
AppUser ||--o{ PermissionLog : "TargetUserId"
AppUser เป็น mirror จาก SSO ของ S9 Web Portal (ไม่มี password) · OfficerProfile เก็บ Capacity/Unit ของเจ้าหน้าที่ผู้รับเคส (workload คำนวณจาก CaseAssignment) · PermissionLog append-only บันทึก GRANT/CHANGE/REVOKE/REASSIGN (บังคับเหตุผล)
Intake → Case core (รับเรื่อง → เคส → ผู้เสียหาย → มอบหมาย → แจ้งสิทธิ)¶
erDiagram
Channel ||--o{ IntakeRecord : "ChannelId"
IntakeRecord ||--o| Case : "ConfirmedCaseId"
Case ||--o{ IntakeRecord : "MatchedCaseId (dedup)"
Channel ||--o{ Case : "ChannelId"
Province ||--o{ Case : "ProvinceId"
HelpType ||--o{ Case : "HelpTypeId"
OffenseBase ||--o{ Case : "OffenseBaseId"
PoliceStation ||--o{ Case : "PoliceStationId"
HelpType ||--o{ HelpType : "ParentHelpTypeId"
Case ||--o{ CaseVictim : "CaseId"
VulnerableGroup ||--o{ CaseVictim : "VulnerableGroupId"
Case ||--o{ CaseAssignment : "CaseId"
AppUser ||--o{ CaseAssignment : "AssigneeUserId / AssignedBy"
Case ||--o{ CaseRightsCheck : "CaseId"
RightsChecklistItem ||--o{ CaseRightsCheck : "RightsChecklistItemId"
IntakeRecord = 4 ช่องทางรับเข้า + dedup (REQ-001) → ยืนยันแล้วสร้าง Case · CaseVictim ถือมิติประชากร (เพศ/อายุ/กลุ่มเปราะบาง) + media consent (CR-5) — รองรับ multi-victim + รายงาน "จำนวนราย" · CaseAssignment = multi-assignee + ประวัติ (IsCurrent)
Outreach Reports + Trigger Gate + Plan + Approval (การจัดการรายกรณี)¶
erDiagram
Case ||--o{ OutreachReport : "CaseId"
Case ||--o{ CaseTrigger : "CaseId"
RightsArea ||--o{ CaseTrigger : "RightsAreaId"
RightsArea ||--o{ RightsAreaItem : "RightsAreaId"
Case ||--o{ AssistancePlan : "CaseId"
AssistancePlan ||--o{ PlanItem : "AssistancePlanId"
RightsArea ||--o{ PlanItem : "RightsAreaId"
RightsAreaItem ||--o{ PlanItem : "RightsAreaItemId"
OutreachReport ||--o{ ApprovalAction : "OwnerEntityId (OutreachReport)"
AssistancePlan ||--o{ ApprovalAction : "OwnerEntityId (AssistancePlan version)"
AppUser ||--o{ ApprovalAction : "ApproverUserId"
CaseTrigger = gate 4 ด้าน (ต้องการ/ไม่ต้องการ, REQ-005) · RightsAreaItem = รายการย่อย admin CRUD (CR-4) · AssistancePlan versioned (1 แถว/Version, IsCurrent) · ApprovalAction เป็น polymorphic (OwnerEntityType = OutreachReport/AssistancePlan) ชี้ Version เฉพาะ → รองรับ loop-back + version history (REQ-006) · ลำดับขั้น config ที่ ApprovalStep
Implementation / Referral / Termination (ผลดำเนินการ → ส่งต่อ → ยุติโปรแกรม)¶
erDiagram
Case ||--o{ ImplementationResult : "CaseId"
PlanItem ||--o{ ImplementationResult : "PlanItemId"
AppUser ||--o{ ImplementationResult : "PerformedByUserId"
Case ||--o{ Referral : "CaseId"
Unit ||--o{ Referral : "TargetUnitId"
AppUser ||--o{ Referral : "SentBy"
Case ||--o{ ProgramTermination : "CaseId"
AppUser ||--o{ ProgramTermination : "RequestedBy / DecidedBy"
ImplementationResult = บันทึกผล Rich Text + ผลภาคสนาม + สถานะการยุติแผน (DONE/NOT_DONE/UNABLE) + แนบหลักฐานบังคับ (REQ-007) · Referral ส่งต่อ S1/S4/S6 (Web Service) หรือภายนอก (Manual) เก็บ ExternalRefId (REQ-008) · ProgramTermination = ขอยุติ→พิจารณา (ผอ.)→ออกจากโปรแกรม
Cross-cutting (แนบไฟล์ / แจ้งเตือน / ประวัติสถานะ / audit)¶
erDiagram
Case ||--o{ CaseStatusHistory : "CaseId"
AppUser ||--o{ CaseStatusHistory : "ChangedBy"
AppUser ||--o{ Notification : "RecipientUserId"
Attachment (polymorphic, OwnerEntityType/OwnerEntityId) แนบได้ทุก entity — ไม่วาดเส้น FK (รูปภาพหลักฐาน ≥50MB) · CaseStatusHistory append-only รองรับ tracking + audit · AuditLog/PermissionLog append-only (รวม Action='VIEW_PII' ตาม PDPA) · SystemConfig/ApprovalStep = config (ไม่มี FK เชิงความหมาย)
สรุป FK ทั้งหมด (รวม audit CreatedBy/UpdatedBy)¶
ทุกตาราง business มี CreatedBy/UpdatedBy → PJOS.AppUser(AppUserId) (nullable — รองรับ system/sync rows) นอกเหนือจาก FK เชิงความหมายที่วาดด้านบน · ดู Section 2 ของ schema.sql สำหรับรายการ ALTER TABLE ... ADD CONSTRAINT FK_* ครบทุกเส้น (40+ FK)
| ตาราง | FK เชิงความหมายหลัก |
|---|---|
AppUser |
→ Province |
RoleAssignment |
→ AppUser, Role, Province, AppUser(GrantedBy) |
OfficerProfile |
→ AppUser, Unit |
HelpType |
→ HelpType(Parent, self-ref) |
PoliceStation |
→ Province |
RightsAreaItem |
→ RightsArea |
IntakeRecord |
→ Channel, Province, Case(Matched), Case(Confirmed) |
Case |
→ Channel, IntakeRecord, Province, HelpType, OffenseBase, PoliceStation |
CaseVictim |
→ Case, VulnerableGroup |
CaseAssignment |
→ Case, AppUser(Assignee), AppUser(AssignedBy) |
CaseRightsCheck |
→ Case, RightsChecklistItem |
OutreachReport |
→ Case |
CaseTrigger |
→ Case, RightsArea |
AssistancePlan |
→ Case |
PlanItem |
→ AssistancePlan, RightsArea, RightsAreaItem |
ApprovalAction |
→ AppUser(Approver) ; polymorphic → OutreachReport / AssistancePlan |
ImplementationResult |
→ Case, PlanItem, AppUser(PerformedBy) |
Referral |
→ Case, Unit, AppUser(SentBy) |
ProgramTermination |
→ Case, AppUser(RequestedBy/DecidedBy) |
Notification |
→ AppUser(Recipient) |
CaseStatusHistory |
→ Case, AppUser(ChangedBy) |
PermissionLog |
→ AppUser(Target) |
Attachment |
polymorphic (OwnerEntityType/OwnerEntityId — ไม่มี FK) |