บันทึก 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
- รับคำขอ
POST /api/trackingโดยไม่ต้องผ่านการยืนยันตัวตน - แปลง header ทั้งหมดเป็น map ที่ใช้ key เป็นตัวพิมพ์เล็ก header ที่มีหลายค่าจะถูกรวมด้วย
", "เพื่อเลียนแบบพฤติกรรมของreq.headersใน Express ที่ NestJS ส่งให้ controller - bind body เป็น
map[string]anyโดยละทิ้ง error ที่เกิดขึ้นอย่างตั้งใจ ดังนั้น body ที่ว่าง เสียหาย หรือไม่ใช่ JSON ก็ยังได้รับสถานะ 200 กลับไป (ต้นฉบับไม่มี validation เลย) - spawn goroutine ที่มี
recover()ป้องกัน panic แล้ว publish ด้วยcontext.Background()ที่กำหนด timeout ไว้ 10 วินาที - ตอบกลับ
200พร้อม body{"code":"RES_SUCCESS_001","message":"OK"}ทันที โดยไม่รอผลการ publish - payload ที่ถูกส่งลงคิว
tracking_logเป็น bare JSON ตามรูปแบบนี้
{
"headers": { "...header ทั้งหมด..." },
"body": { "...body ที่ส่งเข้ามา..." },
"logEventType": "redirect"
}
- หากการ publish ล้มเหลว รวมถึงกรณีที่ AMQP ไม่ได้ถูกตั้งค่า (
amqp.ErrNotConfigured) ระบบจะบันทึก log errorError processing tracking logเท่านั้น ไม่มี retry และไม่มี DLQ ฝั่งผู้เรียกจึงไม่มีทางทราบว่าข้อความสูญหาย
ลำดับของ key ใน JSON (headers, body, logEventType) ถูกจัดให้ตรงกับ object spread
ของ NestJS เดิมทุกประการ เพราะ consumer ปลายทาง parse ข้อมูลตามชื่อฟิลด์
ไฟล์และฟังก์ชันหลัก
| Method | Route | Auth | Handler |
|---|---|---|---|
| POST | /api/tracking | ไม่มี | Handler.Track |
ซอร์สโค้ดทั้งหมดอยู่ภายใต้ internal/tracking/
| ไฟล์ | ของสำคัญ |
|---|---|
internal/tracking/handler.go | Register(r, deps), NewHandler, Handler.Track, flattenHeaders |
internal/tracking/service.go | NewService(pub, exchange, queue), Service.ProcessTrackingLog(ctx, headers, body) |
internal/tracking/service.go | type redirectTrackingPayload, const logEventTypeRedirect = "redirect", interface publisher |
ค่าคงที่ที่เกี่ยวข้อง: resSuccess001 = "RES_SUCCESS_001" และ publishTimeout = 10 * time.Second
จุดเชื่อมต่อกับ Service อื่น
- RabbitMQ — คิว
tracking_log(envRABBITMQ_QUEUE_TRACKING_LOG) บน exchangeline_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 ส่วนกลาง)