Skip to main content

บันทึก Tracking Log (Redirect Tracking)

ภาพรวม

POST /api/tracking เป็น endpoint สาธารณะที่ทำหน้าที่รับ event การคลิกลิงก์ติดตาม (redirect) เมื่อผู้ใช้กดลิงก์ที่ผ่านระบบ tracking ของเรา ไม่ว่าจะมาจาก rich menu, rich message, แคมเปญ หรือลิงก์ในหน้าคอนเทนต์ ตัว redirector จะยิง POST มาที่ endpoint นี้เพื่อบันทึกว่ามีการคลิกเกิดขึ้น

โมดูลนี้เป็นโมดูลที่บางที่สุดในโปรเจกต์ ไม่มีการเชื่อมต่อฐานข้อมูล ไม่มี Redis และไม่มี validation ใด ๆ ทั้งสิ้น สิ่งที่ทำคือรับข้อมูลเข้ามา ห่อเป็น payload แล้วส่งลงคิว tracking_log เพื่อให้ worker-go นำไปแยกและเขียนลงฐานข้อมูลต่อ

:::warning ห้ามปรับเป็น synchronous ในโค้ดมี comment กำกับไว้ชัดเจนว่าห้ามปรับ endpoint นี้ให้ทำงานแบบ synchronous เนื่องจากระบบ tracking กำลังถูกออกแบบใหม่ผ่าน client-api พฤติกรรมของ endpoint นี้จึงต้องคงไว้เหมือนเดิมทุกประการ :::

Business Flow

  1. รับคำขอ POST /api/tracking โดยไม่ต้องผ่านการยืนยันตัวตน
  2. แปลง header ทั้งหมดเป็น map ที่ใช้ key เป็นตัวพิมพ์เล็ก header ที่มีหลายค่าจะถูกรวมด้วย ", " เพื่อเลียนแบบพฤติกรรมของ req.headers ใน Express ที่ NestJS ส่งให้ controller
  3. bind body เป็น map[string]any โดยละทิ้ง error ที่เกิดขึ้นอย่างตั้งใจ ดังนั้น body ที่ว่าง เสียหาย หรือไม่ใช่ JSON ก็ยังได้รับสถานะ 200 กลับไป (ต้นฉบับไม่มี validation เลย)
  4. spawn goroutine ที่มี recover() ป้องกัน panic แล้ว publish ด้วย context.Background() ที่กำหนด timeout ไว้ 10 วินาที
  5. ตอบกลับ 200 พร้อม body {"code":"RES_SUCCESS_001","message":"OK"} ทันที โดยไม่รอผลการ publish
  6. payload ที่ถูกส่งลงคิว tracking_log เป็น bare JSON ตามรูปแบบนี้
{
"headers": { "...header ทั้งหมด..." },
"body": { "...body ที่ส่งเข้ามา..." },
"logEventType": "redirect"
}
  1. หากการ publish ล้มเหลว รวมถึงกรณีที่ AMQP ไม่ได้ถูกตั้งค่า (amqp.ErrNotConfigured) ระบบจะบันทึก log error Error processing tracking log เท่านั้น ไม่มี retry และไม่มี DLQ ฝั่งผู้เรียกจึงไม่มีทางทราบว่าข้อความสูญหาย
note

ลำดับของ key ใน JSON (headers, body, logEventType) ถูกจัดให้ตรงกับ object spread ของ NestJS เดิมทุกประการ เพราะ consumer ปลายทาง parse ข้อมูลตามชื่อฟิลด์

ไฟล์และฟังก์ชันหลัก

MethodRouteAuthHandler
POST/api/trackingไม่มีHandler.Track

ซอร์สโค้ดทั้งหมดอยู่ภายใต้ internal/tracking/

ไฟล์ของสำคัญ
internal/tracking/handler.goRegister(r, deps), NewHandler, Handler.Track, flattenHeaders
internal/tracking/service.goNewService(pub, exchange, queue), Service.ProcessTrackingLog(ctx, headers, body)
internal/tracking/service.gotype redirectTrackingPayload, const logEventTypeRedirect = "redirect", interface publisher

ค่าคงที่ที่เกี่ยวข้อง: resSuccess001 = "RES_SUCCESS_001" และ publishTimeout = 10 * time.Second

จุดเชื่อมต่อกับ Service อื่น

  • RabbitMQ — คิว tracking_log (env RABBITMQ_QUEUE_TRACKING_LOG) บน exchange line_exchange โดยใช้ routing key เดียวกับชื่อคิว รายละเอียดการ publish ดูที่ RabbitMQ Publisher และ Topology
  • worker-go — เป็น consumer ของคิว tracking_log ทำหน้าที่แกะ header และ body ไปเขียนลงตารางในโดเมน tracking และผูกกับ LINE user เมื่อระบุตัวตนได้
  • client-api — เป็นเจ้าของระบบ tracking รุ่นใหม่ที่กำลังทยอยรับช่วงต่อ ซึ่งเป็นเหตุผลหลัก ที่ endpoint นี้ห้ามเปลี่ยนพฤติกรรม
  • cms-api — โมดูล tracking-link เป็นผู้สร้าง token และลิงก์ที่ปลายทางจะยิงกลับมาที่ endpoint นี้ ส่วนโมดูล tracking-line-users ใช้อ่านผลลัพธ์
  • ไม่มีการใช้ Postgres หรือ Redis ในเส้นทางนี้ (Redis ถูกใช้เฉพาะโดย rate limiter ส่วนกลาง)