Skip to main content

การคำนวณสถิติ Rich Menu

ภาพรวม

รายงานที่ตอบคำถามว่า "ปุ่มไหนบนเมนูถูกกดมากที่สุด" เกิดจากการ aggregate ข้อมูลใน tracking_log เทียบกับผังปุ่มของแต่ละเมนูที่เก็บไว้ในตาราง rich_menu_action

งานนี้แยกออกเป็น 2 ระดับเพื่อให้ scale ได้:

  • calculate_rich_menu_stat — งานระดับ fan-out ทำหน้าที่หาว่ามี rich menu ตัวไหนที่ต้องคำนวณบ้าง แล้วแตกออกเป็นงานย่อย
  • calculate_rich_menu_stat_item — งาน คำนวณจริง ของ rich menu หนึ่งตัว

การแยกในลักษณะนี้ทำให้ rich menu ที่มี log จำนวนมากไม่ block ตัวอื่น และสามารถ retry ได้เป็นรายเมนู

Business Flow

Trigger จาก cron (profile cron-scheduler)

  1. ทุกชั่วโมงตรง (0 * * * *) RichMenuStatScannerService จะสแกน Redis เพื่อหา key RICH_MENU_STAT_DIRTY: ตามด้วย rich menu id ซึ่งถูกตั้งไว้โดยเส้นทาง tracking เมื่อมีผู้ใช้คลิกเมนู
  2. แกะ id ออกจาก key แล้ว publish หนึ่ง job ต่อหนึ่ง rich menu เข้า queue calculate_rich_menu_stat_item
  3. หากไม่พบ key dirty เลย จะข้ามรอบนั้นไป

calculate_rich_menu_stat (profile main)

processCalculateRichMenuStat จะค้นหา rich menu ที่ยังมีสถานะ active ทั้งหมด แล้ว publish calculate_rich_menu_stat_item ทีละตัว ถือเป็นการ fan-out แบบเต็มรอบ

calculate_rich_menu_stat_item (profile main)

  1. โหลด rich menu พร้อม rich_menu_action ของทุกปุ่มด้วยการ join โดยเก็บค่า type, label, link (ปลายทางต้นฉบับก่อนถูกห่อด้วย tracking redirect) และ index
  2. transformRichMenu จัดโครงข้อมูลให้เป็นเมนูหนึ่งตัวพร้อมรายการ action
  3. ดึง tracking_log ของเมนูนั้นมาสรุปด้วย summarizeRichMenuStats
    • totalClick — จำนวนคลิกทั้งหมดของแต่ละปุ่ม
    • uniqueClick — จำนวน line_uid ที่ไม่ซ้ำกัน
  4. ผลลัพธ์ที่ได้อยู่ในรูป statData ซึ่งเป็น array ของรายการที่มีฟิลด์ type, label, link, totalClick และ uniqueClick
  5. เขียนผลกลับลงตาราง rich_menu และ rich_menu_archive สำหรับเมนูประเภท member/guest ที่มีการเก็บ archive ไว้

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

  • internal/richmenu/consumer.goOnProcessCalculateRichMenuStat, OnProcessCalculateRichMenuStatItem, struct RichMenuStatPayload
  • internal/richmenu/service_stats.go
    • Service.calculateRichMenuStats(ctx, richMenuInfo)
    • transformRichMenu(), summarizeRichMenuStats()
    • struct statData และ statItem (ฟิลด์ Type, Label, Link, TotalClick, UniqueClick)
  • internal/richmenu/service_methods.goprocessCalculateRichMenuStat(), processCalculateRichMenuStatItem()
  • internal/cronscheduler/rich_menu_stat_scanner.goRichMenuStatScannerService.Run(ctx) และค่าคงที่ richMenuDirtyKeyPrefix = "RICH_MENU_STAT_DIRTY:"
  • Queue: calculate_rich_menu_stat และ calculate_rich_menu_stat_item (profile main) โดยมี cron ที่ profile cron-scheduler เป็นผู้ publish

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

  • ตารางที่เกี่ยวข้อง: tracking_log (ข้อมูลดิบ), rich_menu และ rich_menu_action (ผังปุ่ม), rich_menu_archive (เก็บ snapshot สถิติของเมนูที่ถูกเปลี่ยน)
  • Redis: key ที่ขึ้นต้นด้วย RICH_MENU_STAT_DIRTY: ใช้เป็นตัวชี้ว่าเมนูใดมีข้อมูลใหม่รอคำนวณ
  • ผู้บริโภคผลลัพธ์: cms-api-go และ cms-web ในหน้ารายงาน rich menu
  • งานนี้ไม่มีการเรียก LINE API