ข้ามไปที่เนื้อหา

S4 — eMediation — Backend Implementation Blueprint

ขอบเขตเอกสาร

map schema DELTA (schema.sql) → backend repo repos/emediation/be (NestJS) + ลำดับพัฒนา · backend = สร้างใหม่ ทับ V2 (Webify Node/Express เดิม) ด้วย NestJS โครงเดียวกับ repos/pjos/be · ดู spine: index.md

1. Architecture — V3 = รวม V1 + V2 ขึ้น stack ใหม่

S4 ถูกสั่งให้ สร้างใหม่โดยรวม V1 (PHP/CI, .41) + V2 (Node/Vue, .87) เป็นระบบเดียว (ดู systems/s4_combine_summary.md) → "version 3" = backend NestJS ใหม่ที่:

  1. คงฟีเจอร์เดิมครบ — อ่าน/เขียนตาราง cms_* เดิมทั้ง 95 ตาราง (REUSE) บน schema MEDIATE (PSDBPRDDB)
  2. เพิ่มฟีเจอร์ใหม่ — 23 ตาราง NEW + 7 ALTER ตาม mockup fe + SRS v4.0.0
  3. UI เดียว — React fe (mockup branch) แทนทั้ง PHP SSR (V1) และ Vue SPA (V2)
ส่วน เทคโนโลยี อ้างอิง
Framework NestJS (TypeScript, microservice-oriented, Docker, Linux VM ใหม่) emediation/be, pjos/be README
ORM Prisma datasource provider = "sqlserver" → schema MEDIATE
Observability @rlpdjs/observabilityConfigModule/LoggerModule/PrometheusModule/HttpModule/DatabaseModule.forRoot({ prisma }) rlpdjs/README.md
AuthN ThaiD / Keycloak SSO → JWT (อ่าน role จาก token) design_p2 D8
AuthZ RBAC จาก cms_user_role/cms_user_permission (sync → Keycloak client roles) REQ-MED-007
API docs OpenAPI /api/docs pjos/be convention
// emediation/be — AppModule (รูปแบบเดียวกับ pjos/be, ใช้ rlpdjs)
@Module({
  imports: [
    ConfigModule.forRoot(),
    LoggerModule.forRoot({ serviceName: 'emediation', enabled: true }),
    PrometheusModule.forRoot(),
    HttpModule.forRoot({ timeout: 30000 }),
    DatabaseModule.forRoot({ prisma: new PrismaClient() }),  // Prisma → sqlserver MEDIATE
    // domain modules:
    WorkflowModule, MediationModule, MediatorModule, CenterModule,
    TrainingModule, CompensationModule, AdminModule, PublicModule,
  ],
})
export class AppModule implements NestModule {
  configure(c: MiddlewareConsumer) { c.apply(LoggerMiddleware).forRoutes('*'); }
}

Prisma introspect ของจริงก่อนเขียน model

cms_* 95 ตารางมีจริงบน MEDIATE แล้ว → prisma db pull (introspect) เพื่อ generate model จาก DB จริง แล้วจึง apply DELTA (schema.sql) เพิ่ม 23 ตาราง NEW + ALTER · อย่าเขียน model มือจาก 0

2. REQ → Endpoint → Service → Table

REQ Endpoint (ตัวอย่าง REST) Service / Logic ตารางหลัก (R/M/N)
001 GET/POST /courses · POST /exams · POST /exam-attempts/:id/submit (auto-grade) · POST /courses/:id/checkin จัดการหลักสูตร → สุ่มข้อสอบจาก category+question_count → ตรวจอัตโนมัติ score/total≥pass_pct (retake ≤3) → ออก cert R cms_training_topic,cms_training_register,cms_training_management_checkin,cms_training_certificate · N cms_exam_question,cms_exam,cms_exam_attempt,cms_trainer_eval_category/subject
002 POST /mediators · POST /:id/renew · POST /:id/edit-request · POST /:id/ending ขึ้นทะเบียน→วาระ+expiry ; ต่ออายุ 90 วันล่วงหน้า ; ≤3 สังกัด ; bookbank/K-COP ลงนามก่อน R cms_mediator_applicant,cms_mediator_registra,cms_mediator_appointment,cms_mediator_cert,cms_mediator_request_edit,cms_mediator_ending,cms_profile_ktbcorp*
003 POST /centers · POST /:id/participants ตั้งศูนย์ + คณะทำงาน 5 ตำแหน่ง ; ตรวจ idcard 4 ตำแหน่งหลักไม่ซ้ำข้ามศูนย์ ; ≥1 ผ่านอบรม R cms_people_center,cms_people_center_participant,cms_master_center_position
004 POST /centers/:id/committees · POST /assessments/:id/score ตั้งคณะกรรมการ (สาย workflow) → ให้คะแนนรายบุคคล → ถ่วงน้ำหนัก 4 ด้าน (20/20/25/35) → cert รายปี (ไม่ทับปีเก่า) N cms_assessment_committee(_member/_role) · M cms_master_assessment_category(+weight) · R cms_people_center_assessment(_detail/_result)
005 POST /mediations · POST /:id/appoint · POST /:id/sessions · POST /:id/agreement/sign รับคำร้อง→แต่งตั้ง→นัดหมาย+สอบถามความสมัครใจ (eSign) ; รายงาน 1–4 ; ข้อตกลงครั้งที่ 5 eSign (thaid/manual) ; บังคับ all_parties_replied_date R cms_mediation,cms_mediation_mediator/witness/docs,cms_master_civil_dispute · M cms_mediation,cms_mediation_books
006 POST /mediations/:id/certify · POST /compensation-claims นายทะเบียนรับรอง/ไม่รับรอง/ส่งกลับ ; เบิกค่าตอบแทน (เฉพาะหลังเสร็จ ; matrix เดินทาง) R cms_compensation_main/history/requisition_form/payment_receipt,cms_comment_certificate,cms_mediation_compensation(_rate)
007 GET /wf/tasks (inbox) · POST /wf/instances/:id/approve\|return\|skip · POST /wf/delegations · GET /wf/audit engine: เดินตาม cms_wf_step ตามสาย 7 ระดับ ; skip ต้องมี skip_reason ; delegation override assignee ; ทุก action → cms_wf_audit N cms_wf_* (9) · M cms_user_role(+kind/base_role) · R cms_user_permission,cms_menu
008 GET/PUT /integrations/:id/toggle · POST /news (draft→publish) เปิด/ปิดราย integration (ThaID/eSign/K-COP/ศาล) ; authoring ข่าว/เอกสาร publish_state N cms_integration_setting · M cms_news,cms_people_center_announcement
009 GET /dashboard/* · GET /reports/* (export) aggregate ราย จังหวัด/ปีงบ — query/view ไม่มีตารางเก็บ views บน R
010 (scheduled job) evaluateCenterTermination() ประเมินไม่ผ่าน → consecutive_fails++ ; ≥2 → เปิด cms_wf_instance สายยุติศูนย์ M cms_people_center(+consecutive_fails) · N cms_wf_instance
011 GET/POST /document-templates · POST /:id/render template + {var} → merge ข้อมูลจริง → PDF N cms_document_template · R cms_master_document_type
012 (scheduled job) notifyExpiring() เทียบ expiry/registra date กับ today+90 → เขียน cms_notify R cms_notify,cms_mediator_registra
013 POST /public/training-signup · GET /public/training/lookup?idcard&dob 2 รอบ: สมัคร(stage='สมัครอบรม')→ขึ้นทะเบียน('ขึ้นทะเบียน') ; ตรวจตนด้วยบัตร+วันเกิด M cms_training_register(+stage/pre_member_no/selection_result)
015 POST /center-process-costs รายการค่าใช้จ่าย + ใบเสร็จ (≤10MB) → เพดานงบ → สาย 7 ระดับ N cms_center_process_cost(_item) · R cms_wf_instance
016 GET/POST /award-criteria · POST /centers/:id/award เกณฑ์รายปีงบ (รวม 100%) → คะแนน → cert PDF + QR token N cms_award_criteria,cms_center_award · M cms_people_center(+is_model_org)
017 GET /centers/:id/performance sync จาก REQ-005 ราย ปีงบ → รายงาน PDF/Excel R cms_people_center_report/result,cms_master_performance

3. Cross-cutting

  • AuthN — SSO ทุกผู้ใช้ — V2 เริ่ม SSO ไว้แล้ว (ssoController + .env API_SSO_URL, Bearer JWT = active path ปัจจุบัน) → V3 ทำต่อให้เป็น SSO ล้วน: Keycloak (realm rlpd, federate AD) + ThaiD (ประชาชน/ผู้ไกล่เกลี่ย) → JWT · ตัด legacy ของ V2 ทิ้ง: LDAP (ldapController→AD .15, last มิ.ย. 2025, แทบไม่ใช้) + local password (comment ออกแล้ว) + users.dn · cms_users = thin mirror auto-provision ตอน login ครั้งแรก (ไม่มีคอลัมน์ password/lockout — central SSO จัดการ) · บทบาทกลางที่ S9 Portal → sync Keycloak client roles
  • AuthZ — guard อ่าน role จาก JWT → เทียบ cms_user_permission (action_* ราย menu×role) ; MEDIATOR/CITIZEN scope = self, OFFICER scope = province (cms_wf_instance.province_id, cms_*.province_id)
  • Workflow — service กลาง WorkflowService.start(requestType, subject) / .act(instanceId, action, actor) เรียกจากทุก domain (center-reg / compensation / eval-committee / process-cost) — subject ผูก polymorphic ผ่าน subject_table+subject_id
  • Audit@rlpdjs/observability logger + cms_wf_audit (workflow) + cms_user_logs (login) + audit septet (created_by/updated_by/*_ip) ทุก mutation [NFR]
  • Attachments — คง pattern เดิม *_ext (ชื่อไฟล์ใน DB, binary บนดิสก์/blob) ; ไฟล์ ≤10–50MB ; PDF/JPG
  • PII / PDPAidcard/bank/signature เข้ารหัส at-rest AES-256 ; ThaID ดึงข้อมูลแล้ว lock field
  • eSigncms_mediation_books.sign_method = thaid (ETDA link) | manual (manual_sign_json = เจ้าหน้าที่ยืนยัน)

4. ลำดับพัฒนา (suggested order)

  1. Foundationprisma db pull introspect MEDIATE → apply DELTA (NEW + ALTER) → AppModule + rlpdjs + Keycloak guard
  2. Wave A (ของเดิมให้ทำงานเหมือนเดิม) — Mediation (005) · Mediator (002) · Center (003) · Training (001 ส่วน reuse) · Compensation (006) — port logic จาก V2 mssqlController*.js ขึ้น NestJS service
  3. Wave B (ฟีเจอร์ใหม่ตาม mockup/SRS)Workflow Engine (007) ก่อน (ฐานของ 004/010/015) → Exam bank (001) → Assessment Committee (004) → Document Template (011) → Process Cost (015) → Award (016) → Integration toggles (008)
  4. Wave C — Dashboard/reports (009/017 = views) · scheduled jobs (010/012) · public portal (013) · UX/bug/format (014)
  5. Data merge (นอก TOR — ดู §5) — reconcile mediate2020 (.43, 55K) ↔ MEDIATE (.149, 12K) → migration scripts → validate

5. V1 + V2 merge — สิ่งที่ schema นี้ "รองรับ" vs "ต้องทำต่อ"

ยืนยันจาก source V2 จริง (SSH 10.136.27.87 · 2026-06-17)

mediation-backend2025 (Express, active) = dual-DB / dual-API: (1) MSSQL cms_* (MEDIATE) ผ่าน generic /mssql/:table (getall/get/join/add/update/... + uploadSignature/uploadPDF/updateEvidence/updateDocMoney) — mockup เรียกแบบนี้ตรง ๆ ; (2) MySQL (mediation@localhost:3306, Sequelize) ผ่าน /model/:model — online mediation (Webex) / eKYC / eSign / exam / online training · ldapjs+jsonwebtoken = auth · webexController/smsController/mailController/ssoController = integrations จริงที่มีอยู่

ด้าน schema DELTA นี้ครอบคลุม ต้องทำต่อ (นอกขอบ schema)
โครงสร้าง MSSQL ✅ ใช้ MEDIATE (.149) เป็นฐาน + DELTA diff คอลัมน์ vs mediate2020 (.43) — idcard varchar(20 vs 13), binary cols (evidence/doc_money/certificate ✅ ยืนยันจาก endpoint)
ฐาน MySQL (V2 Phase-1) ✅ A9 (5 ตาราง online mediation) + cms_exam* = ปลายทาง migrate เขียน migration MySQL→MSSQL: meetings/meetingAttendances/signatures/signs/webex_auths/meetingReports/meeting_memos/exams/questions/trainings/registers/trainingAttendances/mediationsetting
ฟีเจอร์เดิม ✅ REUSE 95 ตาราง + A9 = ทำงานเหมือนเดิม port logic จาก mssqlController*.js (1,827 บรรทัด) + modelController ขึ้น NestJS · reverse-engineer ฟอร์ม PDF V1 PHP (K-CoP/ใบเสร็จ/ค่าเดินทาง) ที่ไม่มีใน V2
ข้อมูล — (schema ไม่ migrate ข้อมูล) reconcile 55K(.43) vs 12K(.149) + ข้อมูล MySQL · migration script + validate
integration จริง cms_integration_setting + cms_webex_auth port webexController/ssoController/smsController/mailController/ldapController
ฟีเจอร์ใหม่ ✅ 28 ตาราง NEW + 7 ALTER ตาม mockup + SRS v4

5.1 MySQL → single DB migration map (ครบทั้ง 21 ตาราง — "ย้ายให้หมด")

นโยบาย V3 = single DB ไม่เหลือ MySQL · ทุกตารางใน mediation@3306 มีปลายทางใน MEDIATE:

MySQL (V2) rows/สถานะ ปลายทางใน MEDIATE (single DB) วิธี
meetings 291 🟢 cms_online_meeting (A9) migrate ตรง + reconcile กับ cms_mediation_booking_online (R)
meetingAttendances 1,677 🟢 cms_meeting_attendance (A9, +eKYC) migrate ตรง
signatures 1,442 🟢 cms_meeting_signature (A9) blob data → ไฟล์/signature_ext
signs 2,684 🟢 cms_meeting_signature (A9, รวม sign_status) merge เข้ากับ signatures ตาม meetingAttendances_id
meetingReports 0 ⚪ cms_meeting_report (A9) สร้าง schema รอ (ว่าง)
meetingReportContents 0 ⚪ cms_meeting_report (คอลัมน์ content / child) fold-in
meeting_memos 6 🟡 cms_meeting_report (report_type='memo') หรือ cms_mediation_books (R) migrate + map
memo_users 12 🟡 cms_meeting_report (parties) / cms_mediation_witness (R) map คู่กรณี
internalMeetings 0 ⚪ cms_online_meeting (category='internal') fold-in (ว่าง)
webex_auths 1 🟡 cms_webex_auth (A9) migrate (secret → vault)
meetingCategories 2 🟡 cms_setting (R) หรือ master เล็ก lookup
users 121 🟢 cms_users (R, thin mirror) + cms_member (R) reconcile/merge ตาม id_card_number (ระวังซ้ำกับ MSSQL cms_users 3,185) · เลิก password/dn (LDAP) → identity จาก SSO (Keycloak+ThaiD)
loginLogs 276 🟢 cms_user_logs (R) migrate ตรง
images 543 🟢 ไฟล์ + *_ext (R, แบบ MSSQL) blob → file store
mediationSettings 1 🟡 cms_setting (R, mediator_fee) key/value
exams 1 ⚪ cms_exam (A3) stub — สร้างใหม่ตาม SRS (ไม่ต้อง migrate data)
questions 4 ⚪ cms_exam_question (A3) stub
courses 1 ⚪ cms_training_topic (R) stub
trainings 13 ⚪ cms_training_topic (R, +webex cols ถ้าจำเป็น) stub/เก่า 2022
trainingAttendances 4 ⚪ cms_training_management_checkin (R) stub
registers 2 ⚪ cms_training_register (R) stub

🟢 ต้อง migrate data จริง · 🟡 migrate น้อย/lookup · ⚪ stub/ว่าง (สร้าง schema ใหม่ตาม SRS, ไม่ต้องยกข้อมูล) · หลัง migrate ครบ → ปิด MySQL + ชี้ backend NestJS ไป MEDIATE อย่างเดียว

ขอบเขต merge = Scope Change (ดู s4_combine_summary.md)

การรวม V1+V2 ไม่อยู่ใน TOR 7.14 (ระบุแค่ "ปรับปรุง V2") + มี 2 ฐานข้อมูลในตัว V2 เอง (MSSQL+MySQL) → effort +67–97 man-days · เอกสารนี้เตรียม schema target (V3) พร้อม แต่ reverse-engineer V1 / MySQL→MSSQL migration / cutover เป็นงานแยกที่ต้องขอ Change Request · แนะนำ Phased: ส่งมอบ DELTA ตาม TOR ตรงเวลา → รวมข้อมูลจริง (รวม MySQL store) เฟส 2