การส่ง Campaign แบบ Broadcast (ส่งถึงเพื่อนทุกคนของ OA)
ภาพรวม
Broadcast คือการส่ง rich message ของ campaign ไปยัง เพื่อนทุกคนของ LINE OA ด้วยการเรียก LINE Broadcast API เพียงครั้งเดียว โดยไม่ต้องระบุรายชื่อผู้รับ จึงเป็นเส้นทางที่ประหยัดและเร็วที่สุด ต่างจาก multicast ที่ต้องไล่ส่งทีละกลุ่ม
งานนี้รับ job จาก queue line_broadcast_rich_message แล้วทำหน้าที่ตรวจสอบความถูกต้องของ campaign และ OA, สร้าง tracking link ให้ทุก URL และรูปภาพในข้อความ, แปลง message object, ยิง LINE Broadcast API แล้วเขียนผลลัพธ์กลับลงตาราง campaign
Business Flow
- รับ payload ที่ประกอบด้วย
campaignId,audienceId,organizationIdและlineOaId - เปิด claim heartbeat — background ticker อัปเดต
campaign.claimed_atเป็นเวลาปัจจุบันทุก 5 นาทีระหว่างการส่ง โดย guard ด้วยเงื่อนไขstatus='sending'เพื่อไม่ให้ reaper ยึด campaign คืนกลางคัน - Validate campaign —
GetCampaignAndValidateโหลด campaign พร้อมrich_message.contentด้วย LEFT JOIN โดยเคารพ soft delete- สถานะ
draftหรือcancelจะถูกเปลี่ยนเป็นfailedแล้วโยน error ออกไป - สถานะที่ไม่ใช่
sendingถือเป็น duplicate หรือ late redelivery จึงทิ้งเงียบ ๆ ด้วย permanent error ไม่มีการ republish
- สถานะ
- สำเนาเนื้อหาต้นฉบับ — clone เนื้อหา rich message ต้นฉบับเก็บไว้ เพื่อใช้ rollback หากการส่งล้มเหลว
- Validate OA —
GetLineOAAndValidateresolve OA และคืนchannelAccessTokenที่ผ่านการ verify หรือ refresh แล้ว หาก OA ไม่อยู่ในสถานะ active จะเปลี่ยน campaign เป็น failed - สร้าง redirect mapping — ดึงทุก URL และ image URL ในเนื้อหาแล้วแทนที่ด้วย tracked URL
- broadcast ใช้ token และลิงก์แบบ shared ไม่ผูกกับ userId เพราะระบบไม่ทราบล่วงหน้าว่าใครจะเห็นข้อความ
- เลือกเส้นทางตามค่า
CAMPAIGN_REDIRECT_MODEระหว่าง legacy universal-redirect กับ builtin encrypted token (ดูลิงก์ติดตามผลของ Campaign)
- แปลงข้อความ —
TransformMessageObjectsแทนที่ URL เดิมด้วย tracked URL ในโครงสร้าง message และแนบ quick reply หาก campaign ผูกกับquick_reply_idไว้ - ยิง LINE Broadcast API พร้อมแนบ retry key
- เขียนผลกลับ — อัปเดตตาราง
campaignด้วยline_message_object,template_tracking,rich_message_content,end_tracking_date(เท่ากับstart_dateบวก 90 วัน),total_recipientและตั้งสถานะเป็นsent - กรณีล้มเหลว —
broadcastFailedจะคืนเนื้อหาต้นฉบับกลับ ตั้งสถานะเป็นfailedพร้อมบันทึกreasonส่วน error ระดับ validation หรือ setup จะถูก log แล้ว swallow (return nil เพื่อ ack) ตามพฤติกรรมของระบบเดิม
ไฟล์และฟังก์ชันหลัก
internal/linemessageapi/broadcast.goBroadcastService.HandleLineBroadcastRichMessage(ctx, payload)broadcastFailed()และendTrackingDateFrom()ซึ่งกำหนดช่วง tracking 90 วัน- struct
LineBroadcastRichMessagePayload
internal/linemessageapi/consumer.go—Consumer.HandleLineBroadcastRichMessageและwithClaimHeartbeat()ที่ใช้ค่าclaimHeartbeatIntervalเท่ากับ 5 นาทีinternal/linemessageapi/extend.go—extendService.createRedirectMappings(),buildBuiltinMappings(),getGaSettings(),getLineLiffId()internal/linemessageapi/util.go—TransformMessageObjects(),ExtractUrls(),ExtractImageUrls()internal/quickreply/attach.go—LoadItems()สำหรับแนบ quick replyinternal/line/messaging.go—Client.Broadcast(messages, retryKey)- Queue:
line_broadcast_rich_messageบน profilemain
จุดเชื่อมต่อกับ Service อื่น
- แหล่งที่มาของงาน — scanner ของ queue
process_campaign(ดูการจ่ายงาน Campaign ตามกำหนดเวลา) หรือจาก cms-api-go กรณีผู้ใช้สั่งส่งทันที - ตารางที่เกี่ยวข้อง —
campaign(อ่านและอัปเดต),rich_message(อ่าน content และquick_reply_id),line_oa(token),quick_replyและquick_reply_item - LINE API —
POST /v2/bot/message/broadcastสำหรับส่งข้อความ และGET /v2/bot/insight/followersสำหรับนับจำนวน follower เพื่อใช้เป็น total recipient - Redirect service และ campaignlink — ใช้สร้าง tracked URL
- ผลที่ตามมา — เมื่อผู้ใช้คลิกลิงก์ ระบบจะ INSERT ลง
tracking_logทำให้ Postgres trigger ยิงเข้าcampaign_click_triggerและสถิติจะถูกคำนวณโดยการคำนวณสถิติ Campaign