S2 — Proactive Justice Operation System (PJOS) — Target Schema (Phase 2)¶
TARGET DESIGN — ไม่ใช่ schema ปัจจุบัน
ออกแบบใหม่ (greenfield) เพื่อ implement requirement จาก SRS S2 v2.0.0 (TOR 7.12.1–7.12.2) · ระบบนี้ยังไม่มี DB จริง — de-facto contract คือ mockup repos/pjos/fe/src/data.jsx + screens-*.jsx (การแปลง mockup → schema สรุปใน Mockup → Target mapping) · DDL เต็ม: schema.sql · ERD · Data Dictionary · 🛠️ Backend Implementation
ทำไมต้องออกแบบใหม่ (greenfield)¶
S2 เป็นระบบใหม่ ไม่มี legacy ให้ migrate — mockup pjos/fe (React/Vite) เก็บข้อมูลเป็น mock array ใน data.jsx
(CASES/mkCase, OFFICERS, UNITS, SERVICE_CENTER_QUEUE, FIELD_RESULTS, PLAN_STEPS, RIGHTS_AREAS …) +
ค่าฝังใน screens-*.jsx เท่านั้น pjos/be (NestJS) ยังเป็น placeholder ว่าง งานนี้คือแปลง mock contract นั้นเป็น
clean target schema ระดับ production:
- normalize root field → ราย/junction —
mkCaseเก็บgender/age/vulnerableGroupที่ราก (คิดแบบเหยื่อคนเดียว) แต่ SRS เป็น Multi-victim (REQ-003) + รายงาน "จำนวนราย แยกเพศ/อายุ" (REQ-010) → ย้ายมิติประชากรไปCaseVictim;victims[]/assignees[]→ ตารางCaseVictim/CaseAssignment - เพิ่ม FK + audit + workflow ที่ mockup ไม่มี — FK referential integrity ครบทุก relation, audit cols (
CreatedAt/CreatedBy/UpdatedAt/UpdatedBy), workflow state (intake→dedup→case→assign→plan→approve→implement→refer→terminate) - trigger gate + แผน versioned —
CaseTrigger(ต้องการ/ไม่ต้องการ 4 ด้าน) แยกจากAssistancePlan(เก็บทุก Version เพื่อเทียบย้อนหลัง + loop-back) - generic
ApprovalAction— 1 ตารางพิจารณาแบบลำดับชั้น ใช้ร่วม 3 flow (รายงานรอบ1 / รอบ2 / แผน) — ดู D-PJO-1 (ตาราง deviation ด้านล่าง) - polymorphic
Attachment— 1 ตารางแนบไฟล์ใช้ร่วมทุก entity (รูปภาพหลักฐาน ≥50MB) - thin-mirror auth —
AppUser/RoleAssignment/Roleเป็นเงา SSO ของ S9 Web Portal ไม่เก็บรหัสผ่าน (ดู [OPEN-AUTH])
S2 PK = int IDENTITY (เหมือน S1/S3, ต่างจาก design_p2 D7)
S8/S9 ใช้ uniqueidentifier เพื่อ carry over GUID จาก legacy · S2 เป็น greenfield ไม่มี GUID เดิม
จึงใช้ int IDENTITY(1,1) เป็น PK ทุกตาราง (log ปริมาณสูง = bigint IDENTITY) · CreatedBy/UpdatedBy = int FK → PJOS.AppUser
Schema ที่ ship จริง (ตั้งแต่ Plan 1, 2026-07/08) เบี่ยงจาก target design นี้ 2 จุด
เอกสารนี้ยังเป็นพิมพ์เขียวต้นทาง — คงไว้อ้างอิงเหตุผลเชิง requirement/normalization ข้างต้น แต่ implementation จริง
(repos/pjos/backend/prisma/migrations/V001–V007, ดู backend/README.md เป็น source of truth) ตัดสินใจต่างจากที่นี่
2 จุด — ไม่ใช่การเขียนผิด แต่เป็นการตัดสินใจ ณ เวลา implement ที่ควรบันทึกไว้กันงง:
- Naming + PK type:
snake_case+UNIQUEIDENTIFIER/NEWID()แทนPascalCase+int IDENTITY. ตารางจริงคือPJOS.case,PJOS.case_result,PJOS.case_result_attachmentฯลฯ (ตัวพิมพ์เล็กทั้งหมด, snake_case column) ใช้ GUID เป็น PK ไม่ใช่ int IDENTITY ตามที่โน้ตด้านบนระบุไว้ — เหตุผลไม่ได้บันทึกละเอียดใน migration comment (เป็นทางเลือกของทีม implement ตอนเริ่ม Plan 1) แต่ สม่ำเสมอทั้ง schema ตั้งแต่ V001 จนถึง V007 PJOS.Attachmentแบบ polymorphic → ไม่ได้สร้าง. REQ-PJO-007 (V007) ใช้ตารางเฉพาะcase_result_attachment(FK ตรงไปยังcase_result.id) แทน เหตุผลบันทึกไว้ในตัว migration (V007_pjos_case_result.sql, comment ด้านบนCREATE TABLE): คู่(OwnerEntityType, OwnerEntityId)แบบ polymorphic บังคับ FK ไม่ได้ — แถวกำพร้า/ชี้ entity ที่ไม่มีจริงเกิดได้โดย DB ไม่ห้าม ต้องคุมที่ app layer ทั้งหมด ขัดกับ ทุกตารางอื่นใน schema นี้ที่ให้ DB บังคับ invariant (filtered unique ของ victim/case_assignee, CHECK ของ agency.kind) และ ณ วันที่ V007 ship, REQ-PJO-008 (ส่งต่อ) ซึ่งเป็น entity ถัดไปที่ต้องการไฟล์แนบยังไม่ถูกสร้าง จึงไม่มี entity ตัวที่สองให้ "ใช้ร่วม" จริง ๆ วันนี้ — ถ้าวันหน้ามี ≥3 entity ที่ต้องการไฟล์แนบจริง ค่อยพิจารณาแยก ตารางกลางใหม่ (ที่ยังคง FK ต่อ entity ได้ผ่านตารางเชื่อม ไม่ใช่ polymorphic คู่ ownerType/ownerId แบบเดิม)
Stack & Architecture (จาก SRS §2.1, §5.3)¶
| ส่วน | เทคโนโลยี (ตาม SRS) |
|---|---|
| Backend | Node.js / RESTful API (pjos.rlpd.go.th) — realize เป็น NestJS (ตาม pjos/be placeholder) |
| Frontend | Responsive Web (Desktop/Tablet สำหรับเจ้าหน้าที่ภาคสนาม) + Rich Text Editor — mockup pjos/fe |
| Database | MS SQL Server — schema ใหม่ PJOS |
| Auth | ผ่าน S9 Web Portal — ThaiD/AD + SSO (ไม่เปิดให้ประชาชน — เจ้าหน้าที่กรมฯ เท่านั้น) |
| Integration (In) | Service Center (รับ Line OA + ข่าวจากช่องทางต่าง ๆ) ผ่าน Web Service / REST |
| Integration (Out) | Web Service ส่งต่อ S1 ที่ปรึกษากฎหมาย, S4 ไกล่เกลี่ย, S6 OCIPA + หน่วยงานภายนอก |
| Master Data | Sync จาก OCIPA (ประเภทความช่วยเหลือ/สถานีตำรวจ) + Service Center |
| File Storage | Object Storage (รูปภาพ/เอกสารหลักฐาน ≥ 50 MB/ไฟล์) |
| Observability | NestJS app ใช้ไลบรารีกลาง @rlpdjs/observability (logger/trace/metrics/DB query log) → ELK [NFR-M02] |
ความสัมพันธ์กับ repo rlpdjs
rlpdjs = แพ็กเกจ @rlpdjs/observability (ไลบรารี NestJS ที่ใช้ร่วมกันทุกระบบ S1…S9) — ไม่ใช่ backend ของ S2
→ backend ของ S2 สร้างใหม่ใน pjos/be (NestJS) แล้ว install @rlpdjs/observability เป็น dependency ; Prisma ของ S2 ตั้ง provider = sqlserver ชี้ schema PJOS · ดู implementation.md
Role Hierarchy (SRS §3.1, 5 roles)¶
| Role Code | บทบาท | สิทธิ์โดยสรุป |
|---|---|---|
| SYS_ADMIN | ผู้ดูแลระบบ | จัดการ Master Data, สิทธิ์ผู้ใช้, Configuration, Audit Log |
| EXECUTIVE | ผู้บริหาร | อนุมัติแผนขั้นสุดท้าย + เปลี่ยนผู้รับมอบหมาย, Dashboard ภาพรวม, เห็นเคสทั้งหมด |
| SUPERVISOR | หัวหน้างาน | ตรวจ/อนุมัติแผนขั้นต้น + Comment, มอบหมายงาน, อนุมัติรายงานรอบ 24 ชม. |
| OFFICER_PROACTIVE | เจ้าหน้าที่เชิงรุก | บันทึกคำร้อง วางแผน Case Management ดำเนินการตามแผน (เห็นเคสไม่ได้รับมอบหมายแบบ metadata-only) |
| OFFICER_PROVINCE | สำนักงานยุติธรรมจังหวัด (สยจ.) | ผู้ใช้รายจังหวัด — เห็นเฉพาะจังหวัดตน [REQ-PJO-009] |
กำหนดสิทธิ์ที่ S9 Web Portal (SSO via JWT) — สอดคล้อง design_p2 D8
ยืนยันตัวตน + มอบสิทธิ์อยู่ที่ S9 Web Portal (ThaiD/AD + SSO realm rlpd)
PJOS อ่านบทบาทจริงจาก JWT ขณะ runtime · AppUser/RoleAssignment/Role เป็น local mirror
(auto-provision เมื่อ login ครั้งแรก) — ไม่มีคอลัมน์ password/lockout (lockout 5 ครั้ง/NFR-S07 ทำที่ SSO)
🔑 Requirement → Table Traceability (spine)¶
ทุก REQ-PJO-001…012 มีแถว — map ไปอย่างน้อย 1 ตาราง หรือ ระบุชัดว่าไม่มีผลต่อ schema
| REQ | TOR | สาระสำคัญ | ตารางที่รองรับ |
|---|---|---|---|
| REQ-PJO-001 | 7.12.1 | รับคำร้องจาก Service Center 4 ช่องทาง + Dedup + แจ้งสิทธิภายใน 24 ชม. + consent | IntakeRecord, Case, CaseRightsCheck, RightsChecklistItem, Channel |
| REQ-PJO-002 | 7.12.1 | บันทึกรายงานผล 2 ช่วง SLA (24 ชม./15 วัน) + Approval ต่างกันแต่ละรอบ | OutreachReport, ApprovalAction, ApprovalStep, Notification |
| REQ-PJO-003 | 7.12.2 | นำเข้า/บันทึกคำขอ + Multi-victim + PDPA media consent + ประเภทช่วยเหลือ/สน. (Master) | Case, CaseVictim, HelpType, PoliceStation, OffenseBase, VulnerableGroup |
| REQ-PJO-004 | 7.12.2 | มอบหมาย Multi-assignee + เปลี่ยนผู้รับมอบหมาย (เฉพาะหัวหน้า/ผู้บริหาร) + Audit | CaseAssignment, PermissionLog, OfficerProfile, AppUser |
| REQ-PJO-005 | 7.12.2 | Trigger gate มาตรฐาน 4 ด้าน (ต้องการ/ไม่ต้องการ) + รายการย่อย admin CRUD + "ไม่มีแผน" | CaseTrigger, RightsArea, RightsAreaItem, AssistancePlan, PlanItem |
| REQ-PJO-006 | 7.12.2 | บันทึกผลพิจารณาแผน + Approval ลำดับชั้น (หัวหน้า→ผอ.) + Comment + loop-back + version | AssistancePlan (versioned), ApprovalAction, ApprovalStep |
| REQ-PJO-007 | 7.12.2 | บันทึกผลตามแผน (Rich Text) + แนบหลักฐานบังคับ + สถานะ/เหตุผลการยุติ + ผลภาคสนาม | ImplementationResult, Attachment, ProgramTermination |
| REQ-PJO-008 | 7.12.2 | ส่งต่อ S1/S4/S6 (Web Service) + ภายนอก (Manual) + เก็บ Reference + Audit | Referral, Unit, Attachment |
| REQ-PJO-009 | 7.12.2 | สืบค้น (ช่วงเวลา/ผู้รับผิดชอบ/จังหวัด) + สิทธิ์การมองเห็น (metadata-only) | Case, CaseStatusHistory, SystemConfig — query + scope ที่ app layer |
| REQ-PJO-010 | 7.12.2 | Dashboard ≥5 รายงาน (เรื่อง vs ราย, SLA 24 ชม./15 วัน, เพศ/อายุ/ฐานความผิด/สน.) | aggregate queries บน Case/CaseVictim/OffenseBase ; export → AuditLog |
| REQ-PJO-011 | 7.12.2 | ดาวน์โหลด/Export รายบุคคล + Dashboard (.xls/.xlsx/.pdf/.csv) | (report engine ที่ app layer) ; export action → AuditLog |
| REQ-PJO-012 | Cross | Master Data + Workflow Governance + Audit Log + PDPA (5 บทบาท) | ApprovalStep, SystemConfig, AuditLog, PermissionLog, lookup tables, Role/RoleAssignment |
| Auth | §3.1 | SSO via JWT (S9 Portal) + RBAC 5 บทบาท | AppUser (mirror), RoleAssignment (mirror), Role (catalog) |
REQ ที่ไม่มีผลต่อ schema (จัดการที่ app/infra layer)
- REQ-PJO-009 (scope การมองเห็น) — สยจ. เห็นเฉพาะจังหวัด, เจ้าหน้าที่เชิงรุกเห็นเคสไม่ได้รับมอบหมายแบบ metadata-only (ปกปิด PII) = logic ที่ query/interceptor layer จาก JWT ; schema แค่เก็บ
ProvinceId+CaseAssignment - REQ-PJO-010/011 — chart rendering + export
.xlsx/.pdf/.csv= report engine ที่ app layer ; schema เก็บได้แค่ export action ในAuditLog - NFR ทั้งหมด — TLS 1.2+ (NFR-S01), AES-256 at-rest (NFR-S02), lockout (NFR-S07), CSP (NFR-S08) = infra/app layer ; ที่กระทบ schema คือ "PII ต้องเข้ารหัส at-rest" → คอลัมน์
CitizenId/ไฟล์แนบวางแผนเข้ารหัส (ดู data_dict.md)
Table Inventory (34 ตาราง, schema PJOS)¶
| กลุ่ม | ตาราง |
|---|---|
| Lookup (11) | Province, Channel, HelpType (self-ref), OffenseBase, PoliceStation, VulnerableGroup, Unit, RightsArea, RightsAreaItem, RightsChecklistItem, Role |
| Identity/Auth — mirror (3) | AppUser (SSO, no password), RoleAssignment, OfficerProfile |
| Intake (1) | IntakeRecord (4 ช่องทาง + dedup) |
| Case core (4) | Case, CaseVictim (multi-victim + demographics), CaseAssignment (multi-assignee), CaseRightsCheck |
| Outreach reports (1) | OutreachReport (2 รอบ SLA) |
| Trigger / Plan / Approval (4) | CaseTrigger, AssistancePlan (versioned), PlanItem, ApprovalAction (polymorphic) |
| Implementation / Referral / Termination (3) | ImplementationResult, Referral, ProgramTermination |
| Cross-cutting (7) | Attachment (polymorphic), Notification, CaseStatusHistory, ApprovalStep, SystemConfig, PermissionLog, AuditLog |
Mockup → Target mapping (รองรับ frontend)¶
mockup data.jsx มี entity หลักจาก mkCase/CASES + constants — ตารางที่เหลือเป็น support/workflow/audit ที่เพิ่มเพื่อ integrity
| Mockup (data.jsx / screens) | Target (PJOS) | หมายเหตุ |
|---|---|---|
CASES/mkCase (id, serviceCenterRef, channel, province, helpType, summary, status, priority, consent, budgetEstimate, contactDueAt, reportDueAt) |
Case |
id→CaseNo UNIQUE ; channel/helpType→lookup FK ; status/consent/priority→CHECK |
mkCase.victims[] (name, citizenMasked, phone, vulnerableGroup, mediaConsent, consentAt) + root gender/age |
CaseVictim |
array→ตาราง 1:N ; มิติประชากร (เพศ/อายุ/กลุ่มเปราะบาง) ย้ายมาที่ราย (SRS multi-victim + รายงาน "จำนวนราย") |
mkCase.assignees[]/assignedTo (string ชื่อ) + OFFICERS (capacity) |
CaseAssignment (multi-assignee) + OfficerProfile |
ชื่อ→FK AppUser ; multi-assignee + ประวัติ (IsCurrent/UnassignedAt) ; capacity→OfficerProfile ; active/due derived |
RIGHTS_CHECKLIST (6 ข้อ) + mkCase.rightsChecked[] |
RightsChecklistItem + CaseRightsCheck |
enum→lookup + junction ต่อเคส |
SERVICE_CENTER_QUEUE + duplicateRisk/sourceConfidence |
IntakeRecord |
รายการรับเข้า 4 ช่องทางก่อนยืนยัน + dedup score (O-6) → ยืนยันแล้วสร้าง Case |
mkCase.reportRounds (round1 24ชม./round2 15วัน + approver) |
OutreachReport + ApprovalAction |
RoundNo 1/2 ; ขั้นอนุมัติ→ApprovalAction (รอบ2 = 2 ขั้น) |
RIGHTS_AREAS (4 ด้าน + subItems) + trigger ต้องการ/ไม่ต้องการ |
RightsArea + RightsAreaItem + CaseTrigger |
subItems = admin CRUD (CR-4) ; gate→CaseTrigger.IsWanted ; ไม่ต้องการครบ→Case.NoPlanFlag |
PLAN_STEPS (dimension, owner, due, budget) + planVersion/approval |
AssistancePlan (versioned) + PlanItem + ApprovalAction |
แผนเก็บทุก Version (IsCurrent) ; loop-back ตีกลับ→Version ใหม่ |
FIELD_RESULTS (area, officer, result, files) |
ImplementationResult (+ Attachment) |
ผลภาคสนาม = ผลดำเนินการ (Rich Text + พื้นที่) ; หลักฐานบังคับ |
| Referral/Escalation (target S1/S4/S6, external) | Referral |
internal system/unit vs external ; web service vs manual |
TERMINATION_REASONS + สถานะ รอขอยุติ/ออกจากโปรแกรม |
ProgramTermination |
ขอยุติ→พิจารณา (ผอ.)→ออกจากโปรแกรม |
UNITS (referral target) |
Unit |
หน่วยงานภายในกรมฯ (admin เพิ่ม/ลด) |
CHANNELS/STATUSES/PROVINCES/HELP_TYPES/OFFENSE_BASES/POLICE_STATIONS constants |
Channel/Province/HelpType/OffenseBase/PoliceStation + CHECK |
enum ป้ายไทย → lookup/CHECK |
Support tables ที่เพิ่มใหม่ (ไม่มีใน mockup): CaseStatusHistory (tracking/audit), ApprovalStep/SystemConfig (workflow/SLA config), Notification, Attachment (polymorphic), AppUser/RoleAssignment/Role (auth mirror), PermissionLog/AuditLog (PDPA/governance).
หลักการแปลง mockup → relational (ทำไม mock ขึ้น production ตรง ๆ ไม่ได้)¶
data.jsx เป็น in-memory mock — บอกแค่รูปร่างข้อมูลที่ UI ต้องใช้ ไม่ได้บอกวิธีจัดเก็บ การแปลงหลัก:
- id string → surrogate PK + business code —
S2-2569-00120/SC-IMPORT-001(ปนความหมายธุรกิจกับ key) → PKint IDENTITY+ คอลัมน์ business codeUNIQUE(CaseNo/IntakeNo) - array / root field → ตาราง 1:N —
victims[]/assignees[]→CaseVictim/CaseAssignment; rootgender/age(mock คิดแบบเหยื่อคนเดียว) → ย้ายไปCaseVictimเพราะ SRS = multi-victim + รายงานรายบุคคล - enum ป้ายไทย → CHECK/lookup —
'รอพิจารณาแผน'/'เร่งด่วน'/'ประสงค์รับความช่วยเหลือ'(เปราะ) → CHECK constraint + lookup table - string ชื่อ → FK —
assignedTo:'นางสาวพิชญา สีหราช'→CaseAssignment.AssigneeUserId(FK จริง) - field snapshot → derived —
OFFICERS.active/due(workload) คำนวณจากCaseAssignmentไม่เก็บซ้ำ ; เก็บเฉพาะCapacity
สิ่งที่ "ตัดออก" ไม่ยกมาเป็นตาราง: STATUSES palette (สี = presentation), NAV/ROLE_NAV (routing config — ได้จาก role ใน JWT), fmtThaiDate/fmtNum (utility ฝั่ง UI), caseScope/canSeeDetail/maskPII (logic การมองเห็น = interceptor ที่ app layer ใช้ ProvinceId+CaseAssignment)
ประเด็นออกแบบที่เป็น deviation จาก S1¶
| Tag | ประเด็น | เหตุผล |
|---|---|---|
| D-PJO-1 | ApprovalAction แบบ polymorphic (แทน per-entity status + ApprovalStep ของ S1) |
PJOS มี 3 flow อนุมัติที่ต่างกัน — รายงานรอบ1 (หัวหน้างานขั้นเดียว), รอบ2 (หัวหน้างาน→ผู้บริหาร 2 ขั้น), พิจารณาแผน (หัวหน้างาน→ผอ. + loop-back + comment + version) · 1 ตาราง append-only เก็บทุกการตัดสินใจ (StepNo/Role/Action/Decision/Comment) ใช้ร่วมได้ทั้ง 3 ; ลำดับขั้นกำหนดที่ ApprovalStep (config ไม่ hard-code) |
| D-PJO-2 | แผน versioned (AssistancePlan 1 แถว/Version) |
ApprovalAction.OwnerEntityId ชี้ AssistancePlanId ของ Version เฉพาะ → ตอบโจทย์ "v1 หัวหน้าเห็นชอบ, ผอ.ตีกลับ→REWORK, สร้าง v2" + เทียบ Version ย้อนหลังได้ (REQ-006) ; IsCurrent ชี้ Version ล่าสุด |
| D-PJO-3 | OfficerProfile แยกจาก AppUser |
AppUser เป็น thin SSO mirror (ไม่มี password) — ข้อมูลปฏิบัติงาน (Capacity/Unit) ของเจ้าหน้าที่ผู้รับเคสจึงแยกออกมา ; workload (active/due) คำนวณ จาก CaseAssignment |
ประเด็นที่ต้อง align กับลูกค้า (OPEN — จาก SRS §5.2 TBD Checklist)¶
| Tag | ประเด็น | สถานะ / แนวทางในเอกสารนี้ |
|---|---|---|
| [OPEN-AUTH] | NFR-S07 ระบุ lockout 5 ครั้ง แต่ design_p2 D8 = auth รวมศูนย์ที่ S9 Portal/Keycloak | เลือก thin-mirror ไม่มี password/lockout (เหมือน S1/S3) — lockout ทำที่ SSO · ถ้าลูกค้ายืนยันต้องเก็บใน PJOS เอง → เพิ่ม PasswordHash/FailedLoginCount/LockedUntil ใน AppUser |
| [OPEN-DB] | SRS §5.3 ระบุ "ฐานข้อมูล PJOS" (pjos.rlpd.go.th) แต่ design_p2 D1 = Single DB PSDBPRDDB |
ตามแนว design_p2 — schema namespace PJOS ใน PSDBPRDDB · ถ้าลูกค้ายืนยัน DB แยกจริง → DDL เดิมใช้ได้ เพียงเปลี่ยน target DB |
| [O-5] | REQ-008 — รายชื่อระบบปลายทาง Escalation (นอกจาก S1/S4/S6) + โปรโตคอล | Referral.TargetSystem (varchar) + TargetType รองรับหลายปลายทาง ; รายชื่อ + spec [รอยืนยัน] |
| [O-6] | REQ-008/001 — Deduplicate เคสซ้ำจากหลายช่องทาง (MSC + Service Center + Line) | IntakeRecord.DuplicateScore/MatchedCaseId/Status='MERGED' รองรับ ; เกณฑ์/threshold dedup [รอยืนยัน] |
| [CR-4] | REQ-005 — รายการย่อยใต้ 4 ด้าน (Default Master Data) | RightsAreaItem (admin CRUD) รองรับ ; ชุดเริ่มต้น [รอยืนยัน] |
| [TBD-victim] | REQ-003 — Limit จำนวนผู้เสียหายต่อเคส | CaseVictim 1:N ไม่จำกัดใน schema ; limit (ถ้ามี) เก็บใน SystemConfig |
| [TBD-5] | REQ-007 — รายการเหตุผล "ไม่เสร็จสิ้น/ดำเนินการไม่ได้" + เหตุยุติโปรแกรม (Master) | ImplementationResult.CompletionReason + ProgramTermination.Reason (varchar code) ; ชุดเหตุผล Master [รอยืนยัน] |
| [O-9] | REQ-012 — นโยบาย Masking PII (ใครเห็นเต็ม/เห็น Mask) | Mask = app layer (จาก JWT scope) ; การเข้าถึง PII log ใน AuditLog (Action='VIEW_PII') ; map สิทธิ์ใน SystemConfig [รอยืนยัน] |
| [TBD-meta] | REQ-009 — ระดับ Metadata ที่ผู้ไม่ได้รับมอบหมายเห็น | SystemConfig (key = field ที่เปิดเผย) ; รายการ field [รอยืนยัน] |