AMLO Screening — Open Items
สิ่งที่ต้องตัดสินใจก่อนเขียนโค้ด, ช่องโหว่ที่ต้องปิดก่อน go-live, ขอบบนของความ stale, การแบ่งเฟส และรายการที่ยังต้องไปยืนยันกับ IaC/env
อัปเดต: 2026-08-18
อ่านหลัง Architecture และ Scenarios หน้านี้คือสิ่งที่ ยังไม่ถูกตัดสิน และสิ่งที่ ต้องปิดก่อนถือว่าควบคุมนี้ใช้งานได้จริง — ไม่ใช่ backlog ทั่วไป
1. คำถามที่ต้องตอบก่อนเขียนโค้ด (Owner / Compliance ตัดสิน)
1.1 🔴 ไฟล์ AMLO เป็น full snapshot หรือ delta
ทำไมสำคัญ: ถ้าเป็น delta แล้วเราไปใช้นิยาม “ติดถ้ามีใน batch ล่าสุด” ผู้ที่ถูกบล็อกเกือบทั้งหมดจะถูก ปลดบล็อกพร้อมกันในรอบแรกหลัง go-live และไม่มีใครสังเกตเห็น
สถานะ: เอกสารนี้เลี่ยงความเสี่ยงไปแล้วด้วยนิยาม sticky (ดู Architecture §3) ซึ่งถูกต้องทั้งสองกรณี คำตอบนี้จึงไม่บล็อกเฟส 1 — แต่บล็อกเฟส 2 (การปลดบล็อกอัตโนมัติ และ payload แบบ newly-flagged/cleared)
วิธีตอบที่ถูกที่สุด — ใช้ข้อมูลที่ ingest ไปแล้ว ไม่ต้องนัดคุยกับต้นทาง:
SELECT b.file_name, b.stamp_date, b.row_count,
count(DISTINCT m.cust_code) AS distinct_cust
FROM los.amlo_ingestion_batch b
LEFT JOIN los.amlo_screening_match m ON m.batch_id = b.id
GROUP BY 1,2,3
ORDER BY b.stamp_date DESC;
แล้วดู overlap ของชุด cust_code ระหว่าง 2 batch ล่าสุดที่จบแล้ว — ชุดใหญ่คล้ายกันมาก = snapshot · ชุดเล็กและแทบไม่ซ้ำกัน = delta
ถ้า env นั้นมี batch ที่จบแล้วน้อยกว่า 2 ใบ query นี้ตอบไม่ได้ ให้บันทึกว่า “ยังไม่มีคำตอบ” ห้ามเขียนว่า “น่าจะเป็น snapshot”
1.2 🔴 match ทุกแถวบล็อกหมด หรือมีเกณฑ์
schema เก็บ Similarity (numeric 9,8) และ SearchMethod = การคัดกรองเป็นแบบ fuzzy ไม่ใช่ exact match
“มีแถว = บล็อก” ที่ threshold ต่ำ = เหตุการณ์ false positive หมู่ในวันแรกที่เปิดใช้
ต้องตอบ: เกณฑ์ similarity เท่าไหร่ / AmloType ไหนบ้างที่บล็อก / ใครเป็นเจ้าของเกณฑ์นี้ (Compliance ไม่ใช่ dev) และเกณฑ์นั้นต้องแก้ได้โดยไม่ deploy หรือไม่
1.3 นิยามของ “ห้ามทำกระบวนการใด ๆ”
ต้องระบุให้ชัดว่าอันไหนได้อันไหนไม่ได้ ข้อเสนอ (รอ Owner ยืนยัน):
| การกระทำ | ข้อเสนอ |
|---|---|
| login เข้าระบบ | ✅ ได้ — ไม่งั้นเขาจะไม่มีวันเห็นสาเหตุและสลับบริษัทไม่ได้ |
| อ่านข้อมูลเดิม / ดูสถานะงานเก่า | ✅ ได้ (read-only) |
| สร้าง/ส่ง/อนุมัติรายการใหม่ ทุกชนิด | ❌ ไม่ได้ |
| สลับไปบริษัทที่สะอาด | ✅ ได้เสมอ |
| ออกจากระบบ | ✅ ได้เสมอ |
1.4 งานที่ค้างอยู่ของบริษัทที่เพิ่งติด
FlowInstance ที่ยัง Draft/Submitted, งานที่รออนุมัติในคิว maker/approver — freeze หรือ reject ข้อเสนอ: freeze (ห้ามเดินต่อ แต่ไม่ทำลายข้อมูล) เพราะ reject แล้วปลดบล็อกทีหลังจะกู้งานไม่ได้ และการ reject อัตโนมัติจำนวนมากคือ event ที่อธิบายกับลูกค้ายาก — แต่ ผู้อนุมัติฝั่งธนาคารต้องเห็นว่างานนั้นถูก freeze เพราะ AMLO ไม่ใช่ค้างเงียบ ๆ
1.5 อนุมัติให้แตะ Sentinel + APIM policy หรือไม่
การรับประกันความครอบคลุมทั้งหมดใน Architecture §4 วางอยู่บนการให้ resolve-session คืน amloBlocked แล้วให้ facade ปฏิเสธ
ถ้า Owner ไม่อนุมัติ → ต้องเลือกทางสำรอง (บังคับใช้ใน middleware ของ SupApp_util_lib + บัญชีเวอร์ชัน lib รายบริการ) และต้องยอมรับว่า service ที่ยังไม่ deploy ใหม่ = ยังไม่ถูกบังคับใช้
2. 🔴 ช่องที่ต้องปิดก่อน go-live (ไม่ใช่ nice-to-have)
การควบคุมด้าน compliance ห้ามสร้างอยู่บน endpoint ที่เปิดโล่ง — รายการนี้คือของที่ มีอยู่จริงตอนนี้ บน origin/development
| # | ช่อง | หลักฐาน | สิ่งที่ต้องทำ |
|---|---|---|---|
| 1 | AmloScreeningController ไม่มี auth เลย — ไม่มี [RequireApiKey] ไม่มี [Authorize] และ ApiKeyAuthFilter เป็น opt-in (ไม่ติด attribute = ปล่อยผ่าน) ทั้งที่ไฟล์ import security namespace ไว้แล้ว | Centralized01.API/Controllers/v1/AmloScreeningController.cs:1-26, Backend_Package/src/Filters/ApiKeyAuthFilter.cs:33-41 | ติด [RequireApiKey] — endpoint นี้คืนข้อมูล watchlist ดิบ (similarity, ประเภท, วิธีค้น) |
| 2 | GET /v1/companies/cust-codes เป็น [AllowAnonymous] และ ไม่มี [RequireApiKey] ในไฟล์เลย (ไม่ใช่แค่ถูก comment ไว้) — และมันคืน แผนที่ custCode → companyId → userId ของทั้งระบบ ซึ่ง design นี้ไปพึ่งพา | UserService01.API/Controllers/v1/CompanyController.cs:196,199 | เพิ่ม [RequireApiKey] (กลไกพร้อมใช้อยู่แล้ว register ไว้ที่ Program.cs:115) |
| 3 | UserCacheController (warm/refresh/delete) เป็น [AllowAnonymous] โดย [RequireApiKey] comment ไว้ | UserService01.API/Controllers/v1/UserCacheController.cs:16-18 | uncomment — แม้ warm/refresh จะสร้างค่าจาก DB ใหม่ (ปลอมค่าไม่ได้) แต่ใช้กระหน่ำ DB และดันทุก request ไปเส้น fallback ที่เป็นเส้น fail-open ได้ |
| 4 | rate limit ของ Centralized เป็น 100/นาที รวมทั้ง service ไม่แยก caller → ยิง endpoint sync มือรัว ๆ กิน budget ของทั้ง service | Centralized01.API/Extensions/ApiExtensions.cs:81-87 | policy แยกเฉพาะ route sync มือ (เช่น 1/นาที ต่อ caller) |
| 5 | audit publisher degrade เป็น NullAuditEventPublisher เงียบ ๆ ถ้า env ไม่ได้ตั้ง Service Bus → audit trail หายทั้งชุดโดยไม่มี error | Centralized02.Infrastructure/DependencyInjection.cs:252-269 | startup guard / health check ใน env ที่เปิดใช้ควบคุมนี้ |
| 6 | ไม่มีใครรู้เมื่อ ingestion ไม่ได้รันสำเร็จ — ข้อมูลค้างเก่าเงียบ ๆ คือความเสี่ยง compliance โดยตรง | — | alert เมื่อไม่มี batch ที่จบสำเร็จภายในกรอบเวลาที่คาด รวมถึงรอบที่ได้ 0 แถว |
3. ขอบบนของความ stale (ต้องยอมรับหรือแก้ให้ต่ำลง)
| แหล่งความล่าช้า | ขนาด | หมายเหตุ |
|---|---|---|
| รอบตรวจวันละครั้ง | ≤ 24 ชม. | บริษัทที่ติดในไฟล์ที่มาถึงหลังรอบตี 5 จะเห็นวันถัดไป (ยังไม่นับ lag ของต้นทางเอง) |
| Redis user-info TTL | ≤ ค่าจริงต่อ env (default 86400 = 24 ชม.) | การอ่านไม่ต่ออายุ TTL — ถ้า invalidate หลุด blob จะหมดอายุเองแล้ว re-warm ค่าถูก |
| APIM facade cache ของ resolve-session | 120 วินาที | นี่คือ staleness ของชั้นที่ 1 ซึ่งเล็กที่สุดในสามชั้น |
| SignalR ไม่ถึงผู้ใช้ | ไม่จำกัด (แต่ไม่กระทบการบังคับใช้) | กระทบแค่ว่าเห็น modal ช้า |
worst case ที่ต้องบอก Owner: ~48 ชั่วโมง จากบริษัทติดจริงจนผู้ใช้ทุกคนถูกปิดกั้นครบทุกเส้นทาง (24 ชม. รอบตรวจ + 24 ชม. TTL ที่แย่สุด) สมมติฐาน: CacheTtlSeconds เป็นค่า default และ invalidate หลุด · ถ้าตัวเลขนี้ยอมรับไม่ได้ตามข้อกำหนด compliance ต้องเพิ่มรอบตรวจ (เช่น ทุก 4 ชม.) และลด TTL — ทั้งสองอย่างแก้ที่ IaC ไม่ใช่โค้ด
4. การแบ่งเฟส
เฟส 1 — พอสำหรับ 4 scenario ที่ Owner ขอ และปิดความเสี่ยงจริง
- นิยาม sticky + 3 สถานะ (Flagged / Clear / NotScreenable) + clearance ที่บันทึกได้
- state ระดับบริษัทที่ materialize ใน UserService (Centralized ออกจากเส้นทาง runtime)
- บังคับใช้ที่ชั้น session (resolve-session + facade allowlist) — พร้อม tri-state fail-closed ในชั้น 2/3
- reconcile-by-pull หลังทุกรอบ ingestion + timer สำรอง · push เป็น best-effort
/users/meเพิ่มamloBlockedต่อบริษัท +amloAsOfUtc- modal 2 หน้าแบบปิดไม่ได้ + allowlist
- sync มือสั่ง recompute ได้จริง
DailyRunHourBangkok=5ที่ IaC ทุก env- ปิดช่องทั้ง 6 ข้อใน §2
- audit event ครบ 5 ประเภท
เฟส 2 — ทำได้ดีขึ้นแต่ไม่ใช่เงื่อนไขของความถูกต้อง
- payload แบบ diff (newlyFlagged/cleared) — รอคำตอบ §1.1
- ปลดบล็อกอัตโนมัติเมื่อพ้นจากไฟล์ — รอคำตอบ §1.1 เช่นกัน
- transactional outbox ทั้งสองฝั่ง (ตอนนี้ reconcile ทำหน้าที่แทนอยู่)
- 409 + outcome enum ของ endpoint sync มือ (ถ้ายังไม่ทำในเฟส 1)
- endpoint บาง ๆ
GET /v1/users/me/amlo-status— ทำเมื่อวัดได้จริงว่า payload ของ/users/meเป็นปัญหา ไม่ใช่ทำเพราะดูสะอาดกว่า - นโยบาย freeze/reject ของงานค้าง (§1.4) ถ้า Owner เลือกให้จัดการย้อนหลัง
5. รายการที่ยังต้องไปยืนยัน (ยังไม่ verify ในรอบนี้)
- IaC / APIM — route
/api/user-service/v1/cache/*และ/companies/cust-codesถูกเปิดออกภายนอกผ่าน APIM หรือไม่ (ถ้าเปิด ความรุนแรงของ §2 ข้อ 2–3 สูงขึ้นมาก) - ค่าจริงต่อ env ของ
RedisUserInfo:CacheTtlSeconds— เป็นตัวกำหนดตัวเลข 48 ชม. ใน §3 - ค่าจริงต่อ env ของ
AmloIngestion:EnabledและSftp:*(base repo เป็นfalse+ ค่าว่าง ค่าจริงมาจาก IaC + KeyVault) - เวลาที่ไฟล์ AMLO มาถึง SFTP จริง — ต้องรู้ก่อนตั้งชั่วโมงรัน มิฉะนั้น
StabilityWindowMinutesจะทำให้รอบตี 5 ข้ามไฟล์ของวันนั้นไปทั้งใบ - รูปแบบ custCode ของ
Companies.CustCodeเทียบกับlos.amlo_screening_match.cust_code(การเติม 0 ข้างหน้า / ขีด / ช่องว่าง) — ถ้าไม่ตรงกัน ทุกบริษัทจะกลายเป็น NotScreenable เงียบ ๆ พร้อมกับนับจำนวนบริษัทที่CustCodeเป็น null/ว่าง - สัญญาของ
resolve-session— ต้องอ่าน response DTO จริงของ Sentinel ก่อนตกลงว่าจะเติมamloBlockedตรงไหน และ facade policy ต้องแก้ตรงไหน (Backend_Iac/src/apim/policies/sentinel-facade-inbound-v6.xml) - บัญชีเวอร์ชัน
SupApp_util_libรายบริการ — ถ้าเลือกทางสำรองใน §1.5 นี่คือสิ่งที่ต้องมีก่อนประกาศว่าควบคุมนี้ทำงาน