S1 — Legal Consultation System (LCS) — ER Diagram (Target Design)¶
TARGET DESIGN
ความสัมพันธ์ตาม schema.sql (FK ทุกเส้นถูกประกาศจริงใน DDL — solid line) · เป็น greenfield design modeled จาก mockup repos/lcs/fe — ยังไม่ลงฐานข้อมูลจริง · ดู data_dict.md และ index.md
ทุกตาราง business มี audit columns CreatedBy/UpdatedBy (int FK → AppUser) — ละไว้ในไดอะแกรมเพื่อความชัด วาดเฉพาะ FK เชิงความหมาย (ownership / actor) · audit/soft-delete ละไว้ทั้งหมด · รายการ FK ครบถ้วนอยู่ที่ตารางสรุปท้ายเอกสาร
ไดอะแกรมแยกตาม domain — ตารางที่เป็นสะพานข้าม domain (Consultant, ConsultationRequest, Province, DutySession) จะปรากฏมากกว่าหนึ่งบล็อก
Auth & Identity (thin SSO mirror)¶
erDiagram
Province ||--o{ AppUser : "ProvinceId"
AppUser ||--o{ RoleAssignment : "AppUserId"
Role ||--o{ RoleAssignment : "RoleId"
Province ||--o{ RoleAssignment : "ProvinceId (scope)"
AppUser ||--o{ RoleAssignment : "GrantedBy"
AppUser ||--o{ PermissionLog : "TargetUserId"
AppUser เป็น mirror จาก SSO กลาง (ไม่มี password) · PermissionLog เป็น append-only บันทึกการ GRANT/CHANGE/REVOKE สิทธิ์ (บังคับเหตุผล, REQ-005)
Consultant Registry (ทะเบียนที่ปรึกษา + วาระ + เพิกถอน)¶
erDiagram
ImportBatch ||--o{ ImportError : "ImportBatchId"
ImportBatch ||--o{ ConsultantApplication : "SourceImportBatchId"
ImportBatch ||--o{ ConsultantTerm : "SourceImportBatchId"
ConsultantApplication }o--o| Consultant : "ConsultantId (on approve)"
AppUser ||--o{ ConsultantApplication : "ReviewedBy / ApprovedBy"
Consultant ||--o{ ConsultantSpecialty : "ConsultantId"
Specialty ||--o{ ConsultantSpecialty : "SpecialtyId"
Consultant ||--o{ ConsultantProvince : "ConsultantId"
Province ||--o{ ConsultantProvince : "ProvinceId"
Consultant ||--o{ ConsultantTerm : "ConsultantId"
Consultant ||--o{ ConsultantRevocation : "ConsultantId"
AppUser ||--o| Consultant : "AppUserId (self-service)"
ConsultantApplication คือ workflow ลงทะเบียน (ตรวจคุณสมบัติ → อนุมัติ) → เมื่ออนุมัติสร้าง/ผูก Consultant · spec[] ของ mockup → junction ConsultantSpecialty · "1 คนหลายจังหวัด" → junction ConsultantProvince · ConsultantTerm = วาระ 3 ปี (รอบนำเข้าใหม่ = วาระใหม่) · ConsultantRevocation = append-only การเพิกถอน
Duty Roster & Attendance (แผนเวร vs ปฏิบัติจริง)¶
erDiagram
Province ||--o{ DutyRoster : "ProvinceId"
ImportBatch ||--o{ DutyRoster : "SourceImportBatchId"
DutyRoster ||--o{ DutySlot : "DutyRosterId"
Consultant ||--o{ DutySlot : "PlannedConsultantId"
Consultant ||--o{ ConsultantAvailability : "ConsultantId"
DutySlot ||--o{ DutySession : "DutySlotId"
Province ||--o{ DutySession : "ProvinceId"
Consultant ||--o{ DutySession : "ConsultantId (actual)"
Consultant ||--o{ DutySession : "ReplacedConsultantId"
DutySlot ||--o{ DutyCancellation : "DutySlotId"
Consultant ||--o{ DutyCancellation : "ConsultantId / ReplacementConsultantId"
DutyRoster/DutySlot = แผน (อัปโหลด Template) · DutySession = ชั่วโมงจริง (แหล่งคำนวณค่าตอบแทน, รองรับผู้มาแทน + flag นับสถิติ) · DutyCancellation = สถิติการยกเลิก + ผู้แทน (REQ-009)
Consultation Requests (คำขอ → มอบหมาย → ผล → QA → Escalation)¶
erDiagram
Channel ||--o{ ConsultationRequest : "ChannelId"
Province ||--o{ ConsultationRequest : "ProvinceId"
CaseType ||--o{ ConsultationRequest : "CaseTypeId"
ConsultationTopic ||--o{ ConsultationRequest : "TopicId"
ConsultationTopic ||--o{ ConsultationTopic : "ParentTopicId"
ConsultationRequest ||--o{ RequestAssignment : "RequestId"
Consultant ||--o{ RequestAssignment : "ConsultantId"
AppUser ||--o{ RequestAssignment : "AssignedBy / AssigneeUserId"
ConsultationRequest ||--o{ ConsultationResult : "RequestId"
Consultant ||--o{ ConsultationResult : "ConsultantId"
ConsultationRequest ||--o{ QAReview : "RequestId"
ConsultationResult ||--o{ QAReview : "ResultId"
AppUser ||--o{ QAReview : "ReviewerId"
QAReview ||--o{ QAReviewItem : "QAReviewId"
ConsultationRequest ||--o{ Escalation : "RequestId"
ConsultationRequest ||--o{ RequestStatusHistory : "RequestId"
ConsultationResult เป็น versioned (แก้ไข/บันทึกย้อนหลังได้, REQ-007) · QAReview = pass/fail + ตีกลับ · Escalation ส่งต่อ OCIPA ผ่าน Web Service (เก็บ ExternalRefId) · RequestStatusHistory = append-only รองรับ public tracking + audit
Compensation (ค่าตอบแทน)¶
erDiagram
Province ||--o{ CompensationCalculation : "ProvinceId"
Consultant ||--o{ CompensationCalculation : "ConsultantId"
CompensationCalculation ||--o{ CompensationItem : "CompensationId"
DutySession ||--o{ CompensationItem : "DutySessionId"
CompensationCalculation = รอบคำนวณต่อที่ปรึกษา/จังหวัด (8 ชม.=1,000, ไม่ครบไม่จ่าย) · CompensationItem โยงแต่ละบรรทัดกลับ DutySession (ชั่วโมงจริง) เพื่อตรวจสอบย้อนกลับ · Status='SENT_S5' + S5Reference = ส่งต่อระบบ S5 [OPEN-S5]
สรุป FK ทั้งหมด (รวม audit CreatedBy/UpdatedBy)¶
ทุกตาราง business มี CreatedBy/UpdatedBy → LCS.AppUser(AppUserId) (nullable — รองรับ system/import rows) นอกเหนือจาก FK เชิงความหมายที่วาดด้านบน · ดู Section 2 ของ schema.sql สำหรับรายการ ALTER TABLE ... ADD CONSTRAINT FK_* ครบทุกเส้น (รวม 50+ FK)
| ตาราง | FK เชิงความหมายหลัก |
|---|---|
AppUser |
→ Province |
RoleAssignment |
→ AppUser, Role, Province, AppUser(GrantedBy) |
ConsultantApplication |
→ ImportBatch, Consultant, AppUser(ReviewedBy/ApprovedBy) |
Consultant |
→ AppUser |
ConsultantSpecialty / ConsultantProvince |
→ Consultant + Specialty / Province |
ConsultantTerm |
→ Consultant, ImportBatch |
ConsultantRevocation |
→ Consultant, AppUser(RevokedBy) |
ImportError |
→ ImportBatch |
DutyRoster |
→ Province, ImportBatch |
DutySlot |
→ DutyRoster, Consultant(Planned) |
DutySession |
→ DutySlot, Province, Consultant, Consultant(Replaced) |
DutyCancellation |
→ DutySlot, Consultant, Consultant(Replacement) |
ConsultantAvailability |
→ Consultant |
ConsultationRequest |
→ Channel, Province, CaseType, ConsultationTopic |
RequestAssignment |
→ ConsultationRequest, Consultant, AppUser(Assignee/By) |
ConsultationResult |
→ ConsultationRequest, Consultant |
QAReview |
→ ConsultationRequest, ConsultationResult, AppUser(Reviewer) |
QAReviewItem |
→ QAReview |
Escalation |
→ ConsultationRequest, AppUser(SentBy) |
RequestStatusHistory |
→ ConsultationRequest, AppUser(ChangedBy) |
CompensationCalculation |
→ Province, Consultant |
CompensationItem |
→ CompensationCalculation, DutySession |
Notification |
→ AppUser(Recipient) |
PermissionLog |
→ AppUser(Target) |
ConsultationTopic |
→ ConsultationTopic(Parent, self-ref) |