Friends Track
ภาพรวม
Friends Track คือเครื่องมือวัดผลการชวนคนเพิ่มเพื่อนบน LINE OA แต่ละแคมเปญมี token ประจำตัวที่ใช้ประกอบเป็นลิงก์ชวนเพิ่มเพื่อน เมื่อคนเปิดลิงก์แล้วยืนยันตัวตนผ่าน LINE Login จะถูกนับเป็น การเข้าชม และถ้ากดเพิ่มเพื่อนสำเร็จจะถูกนับเป็น การติดตาม ระบบรายงานอัตรา conversion ให้ดูย้อนหลังพร้อมรายชื่อคนที่เข้ามาจริง
จุดเด่นอีกสองข้อคือ (1) เขียนคุณสมบัติให้ผู้ใช้อัตโนมัติเมื่อเข้ามาทางลิงก์นี้ และ (2) รองรับ ลิงก์ย่อยรายบุคคล — อัปโหลดรายการรหัสอ้างอิง (เช่น รหัสพนักงานขายหรือรหัสสาขา) แล้วระบบประกอบลิงก์แยกให้แต่ละรหัส เพื่อรู้ว่าใครหรือช่องทางไหนพาคนมาได้มากที่สุด
บทบาทที่ใช้งาน — ทีมการตลาดที่ต้องวัดผลช่องทางการชวนเพิ่มเพื่อน (โมดูลนี้มี ModuleGate บังคับจริงที่ backend — ดูหัวข้อ สิทธิ์การเข้าถึง)
เส้นทางผู้ใช้งาน
1. ดูรายการแคมเปญ
เปิดเมนู Friends Track หน้ารายการเรียงจากใหม่ไปเก่าตามวันที่สร้าง มีตัวกรอง 2 ช่อง — ค้นหา (ตรงกับ ชื่อ หรือ โทเคน อย่างใดอย่างหนึ่ง) และกรองตามสถานะ (ใช้งาน / ไม่ใช้งาน)
ตารางแสดง ชื่อ, โทเคน (ตัวอักษรแบบ monospace), สถานะ, การเข้าชม LIFF, การติดตาม, อัตราการเปลี่ยนเป็นเพื่อน % และ วันที่สร้าง — สามคอลัมน์กลางเป็นยอด ตลอดอายุแคมเปญ ไม่มีตัวกรองช่วงเวลา ปุ่มท้ายแถวมี 3 ปุ่ม — รายงาน / แก้ไข / ลบ
2. สร้างแคมเปญและแจกลิงก์
- กดสร้างใหม่ กรอกชื่อแคมเปญ (ช่องเดียวที่บังคับ) — ชื่อต้องไม่ซ้ำกับแคมเปญอื่น ภายใน LINE OA เดียวกัน
- ตั้งค่า token — พิมพ์เองก็ได้ หรือกดปุ่มสร้างอัตโนมัติ หรือเว้นว่างให้ backend สร้างให้ (สองทางหลังให้ token ความยาวไม่เท่ากัน — ดูหัวข้อรายละเอียดสำคัญ) token ต้องไม่ซ้ำ ทั้งระบบ ไม่ใช่แค่ใน OA เดียว
- สลับสถานะใช้งาน/ไม่ใช้งาน — ค่าเริ่มต้นของแคมเปญใหม่คือ "ใช้งาน" แคมเปญที่ไม่ใช้งานจะรับการเข้าชมใหม่ไม่ได้
- กรอกคำอธิบาย (ไม่บังคับ) — เป็นบันทึกช่วยจำภายในเท่านั้น ไม่แสดงในหน้ารายการและไม่ปรากฏให้ผู้ใช้ปลายทางเห็น
- ตั้งค่าคุณสมบัติ — เพิ่มได้หลายคู่ แต่ละคู่เลือกคีย์จากรายการ merge tag (ดึงจากเมนู User Attribute Setup ทั้งชุดคุณสมบัติมาตรฐานและคุณสมบัติที่สร้างเอง) แล้วพิมพ์ค่าที่จะเขียนลงไป ทั้งสองช่องบังคับกรอกถ้าเพิ่มแถวขึ้นมาแล้ว
- เลือกคีย์คุณสมบัติอ้างอิง (ไม่บังคับ) — ช่องนี้คือคีย์ที่จะใช้เก็บรหัสอ้างอิงของลิงก์ย่อยไว้กับผู้ใช้ ตัวเลือกมาจากคนละแหล่งกับข้อ 5 คือมาจาก schema ของ Attribute Master โดยตรง (แสดงเป็น
ชุดคุณสมบัติ > ป้ายกำกับ) คำอธิบายใต้ช่องระบุให้ไปสร้างคุณสมบัติที่หน้าจัดการคุณสมบัติที่กำหนดเองก่อน - กดบันทึก ระบบเปิดกล่องยืนยันก่อน แล้วจึงบันทึกและพากลับหน้ารายการ
3. สร้างลิงก์ย่อยรายบุคคล (ไม่บังคับ)
การ์ด "รหัสอ้างอิง" ปรากฏ เฉพาะโหมดแก้ไข ต้องบันทึกแคมเปญให้ได้ id ก่อนจึงจะทำขั้นนี้ได้
- กดลิงก์ดาวน์โหลดไฟล์เทมเพลต — ได้ไฟล์
refid_template.csvที่มีหัวคอลัมน์refIdกับตัวอย่าง 3 บรรทัด (ไฟล์นี้สร้างขึ้นในเบราว์เซอร์ ไม่ได้ดึงจากเซิร์ฟเวอร์) - อัปโหลดไฟล์ CSV ที่มีรหัสอ้างอิงบรรทัดละหนึ่งรายการ ระบบขึ้นข้อความบอกผลทันทีเมื่อเสร็จ
- เมื่อสถานะขึ้นว่า
completedปุ่ม ดาวน์โหลดไฟล์ลิงก์ จะกดได้ (ก่อนหน้านั้นถูกปิดไว้) — ได้ไฟล์ CSV ที่ประกอบลิงก์เต็มของทุกรหัสอ้างอิงให้แล้ว - แจกลิงก์แต่ละอันให้พนักงาน/สาขาที่เกี่ยวข้อง
4. ดูผลลัพธ์
- กดปุ่มรายงานจากหน้ารายการ
- ดูการ์ดสถิติ 4 ใบ — การเข้าชม LIFF ทั้งหมด, การติดตามทั้งหมด, อัตราการเปลี่ยนเป็นเพื่อน, การเลิกติดตาม
- ดูกราฟเส้น "การเข้าชมและการติดตามรายวัน" (มีช่องเลือกช่วงวันที่อยู่บนหัวการ์ด — แต่ยังไม่ทำงาน ดูหัวข้อจุดที่ควรระวัง)
- เลื่อนลงดูการ์ด "ผู้ใช้" ที่ลิสต์คนที่เข้ามาผ่านแคมเปญนี้ — ชื่อที่แสดงพร้อมรูปโปรไฟล์, รหัสผู้ใช้ LINE, วันที่เข้าชม, วันที่ติดตาม, ป้ายสถานะ (เยี่ยมชมแล้ว / ติดตามแล้ว / เลิกติดตามแล้ว) และรหัสอ้างอิงของลิงก์ย่อยที่คนนั้นเข้ามา แบ่งหน้าได้
รายละเอียดสำคัญที่ตรวจสอบจากซอร์สโค้ดจริง
ยืนยันจาก internal/modules/friendtrack/{controller,service,dto}.go ของ line-management-cms-api-go, internal/friendtrack/service.go ของ line-management-client-api-go (ฝั่งที่บันทึกเหตุการณ์จริง) และ internal/friendtrack/service.go ของ line-management-worker-go:
- token ที่ได้จากปุ่ม "สร้างอัตโนมัติ" กับ token ที่ backend สร้างให้ ยาวไม่เท่ากัน — ปุ่มบนหน้าเว็บสร้างจาก UUID v4 ที่ตัดขีดออกแล้วเอา 16 ตัวแรก ได้ token ยาว 16 ตัวอักษร แต่ถ้าเว้นช่อง token ว่างไว้แล้วบันทึก backend จะสร้างให้ด้วย
crypto.randomBytes(16).toString('hex')ซึ่งได้ token ยาว 32 ตัวอักษร — ทั้งสองแบบใช้งานได้เท่ากัน แต่ทำให้ token ในระบบมีความยาวคละกันและลิงก์ที่แจกยาวไม่เท่ากัน - "การเข้าชม" นับเฉพาะคนที่ยืนยันตัวตนกับ LINE สำเร็จแล้ว และนับคนละหนึ่งครั้งเท่านั้น — ฝั่ง client API ต้องตรวจ LIFF ID token (หรือ access token) ให้ผ่านก่อนจึงบันทึกเหตุการณ์
liff_visitได้ คนที่เปิดลิงก์แล้วปิดทิ้งก่อนล็อกอินจะไม่ถูกนับเลย และทุกตัวเลขนับด้วยCOUNT(DISTINCT line_user_id)— เปิดลิงก์เดิมสิบครั้งก็ยังเป็น 1 (ระบบตอบกลับalreadyTracked: true) ยกเว้นกรณีเดียว คือถ้าผู้ใช้เคยกดเลิกติดตาม OA ไปหลังการเข้าชมครั้งก่อน การกลับเข้ามาอีกครั้งจะถือเป็น funnel ใหม่และบันทึกเหตุการณ์เข้าชมเพิ่ม - คนที่เป็นเพื่อนอยู่แล้วจะถูกนับเป็น "ติดตาม" ให้อัตโนมัติ — LINE ไม่ยิง webhook FOLLOW ให้คนที่เป็นเพื่อนอยู่แล้ว ระบบจึงตรวจเองว่าผู้เข้าชมเป็นเพื่อนอยู่หรือไม่ ถ้าใช่จะเขียนเหตุการณ์
followให้แคมเปญนั้นทันที (ถ้ายังไม่มี) — ผลคือ อัตรา conversion อาจสูงถึง 100% ได้จากคนที่เป็นเพื่อนอยู่ก่อนแล้วทั้งหมด ไม่ใช่เพื่อนใหม่ ต้องอ่านตัวเลขนี้ควบคู่กับบริบทของการแจกลิงก์เสมอ - การให้เครดิตแคมเปญเป็นแบบ last-touch และมองข้าม OA ทั้งใบ — เมื่อ webhook FOLLOW เข้ามาที่ worker ระบบไปหา
liff_visitครั้งล่าสุดของผู้ใช้คนนั้น ในทั้ง OA (ไม่ได้จำกัดเฉพาะแคมเปญใดแคมเปญหนึ่ง) แล้วให้เครดิตการติดตามกับแคมเปญของการเข้าชมครั้งนั้น — ถ้าคนหนึ่งกดลิงก์ของแคมเปญ A แล้วต่อด้วยลิงก์ของแคมเปญ B ก่อนจะเพิ่มเพื่อน แคมเปญ B ได้เครดิตไปทั้งหมด ส่วนเหตุการณ์เลิกติดตามจะผูกกับแคมเปญของการติดตามครั้งล่าสุด - การเขียนคุณสมบัติอัตโนมัติทำงานเฉพาะกับผู้ใช้ที่มีข้อมูลอยู่ในระบบแล้ว — ฟังก์ชันรวมคุณสมบัติมี guard ว่าถ้าหาแถว
line_userของผู้ใช้คนนั้นไม่เจอ จะ เงียบ ๆ ไม่ทำอะไรเลย ไม่มี error ไม่มี log เตือน ดังนั้นคนที่มาใหม่จริง ๆ และยังไม่เคยเป็นเพื่อน OA นี้จะยังไม่ได้รับคุณสมบัติในจังหวะที่กดลิงก์ และการรวมค่าเป็นแบบ shallow merge — คุณสมบัติที่ตั้งไว้ในแคมเปญจะ ทับ ค่าเดิมที่มีคีย์เดียวกันของผู้ใช้ทันที - ลิงก์จริงถูกประกอบที่ฝั่งเซิร์ฟเวอร์เท่านั้น และมีได้ 2 รูปแบบในไฟล์เดียว — รูปแบบพื้นฐานคือ
{clientWebUrl}/{oaHash}/friend-track/{token}?ref={refId}แต่ถ้ามี Content page สถานะpublishedที่ผูกกับแคมเปญนี้อยู่ (คอลัมน์friend_track_campaign_id) ไฟล์ CSV จะเปลี่ยนจากหัวคอลัมน์refId,linkเป็นrefId,directLink,contentLinkโดยคอลัมน์ที่สามชี้ไปที่หน้าคอนเทนต์{clientWebUrl}/{oaHash}/content/{slug หรือ publicToken}?ref={refId}แทน — เป็นเหตุผลที่ผู้ใช้บางแคมเปญได้ไฟล์ 2 คอลัมน์ บางแคมเปญได้ 3 คอลัมน์ - การอัปโหลดรหัสอ้างอิงเป็นงานที่ทำเสร็จภายในคำขอเดียว ไม่ใช่งานเบื้องหลัง — cms-api แทรกข้อมูลเป็นชุดละ 1,000 แถวด้วย
INSERT ... ON CONFLICT (campaign_id, ref_id) DO NOTHINGแล้วเขียนสถานะcompletedให้เสร็จก่อนตอบกลับ สถานะprocessingมีอยู่จริงในโค้ดแต่ถูกเขียนแล้วเขียนทับภายในคำขอเดียวกัน ในระบบมีคิวfriend_track_ref_importกับ consumer ฝั่ง worker เตรียมไว้ แต่ไม่มีที่ใดใน cms-api publish งานเข้าคิวนี้เลย จึงเป็นเส้นทางที่ไม่ถูกใช้งานจริง - ตัวเลข "เพิ่มแล้ว/ข้ามไป" ที่แสดงหลังอัปโหลดเป็นยอดสะสม ไม่ใช่ยอดของการอัปโหลดครั้งนั้น — หลังแทรกเสร็จ ระบบนับด้วย
SELECT COUNT(*) FROM friend_track_ref WHERE campaign_id = ?ซึ่งเป็น จำนวนรหัสอ้างอิงทั้งหมดของแคมเปญ ไม่ใช่จำนวนแถวที่เพิ่งเพิ่ม แล้วคำนวณskipped = จำนวนบรรทัดในไฟล์ − ยอดสะสม— ผลคือถ้าอัปโหลดไฟล์ที่สองที่มีรหัสใหม่ทั้งหมดinsertedจะรายงานยอดรวมทั้งแคมเปญ และskippedอาจติดลบได้ เป็นพฤติกรรมที่คงไว้ตรงตามระบบต้นทาง - หัวคอลัมน์ของไฟล์ CSV ถูกตัดออกเมื่อบรรทัดแรกตรงกับ 3 คำนี้เท่านั้น —
refid,ref_idหรือref(เทียบแบบไม่สนตัวพิมพ์เล็กใหญ่) หัวคอลัมน์ชื่ออื่น เช่นreference_idหรือรหัสพนักงานจะถูกบันทึกเป็นรหัสอ้างอิงจริงหนึ่งรายการ - สถานะของแต่ละคนในตารางรายงานคำนวณสด ๆ จากลำดับเวลาของเหตุการณ์ —
เลิกติดตามเมื่อเวลาเลิกติดตามล่าสุดใหม่กว่าเวลาติดตามล่าสุด,ติดตามแล้วเมื่อมีเหตุการณ์ติดตามอยู่, ที่เหลือคือเข้าชมแล้วและรหัสอ้างอิงที่แสดงในคอลัมน์นั้นดึงมาจาก เหตุการณ์เข้าชมเท่านั้น ถ้าเหตุการณ์เข้าชมของคนนั้นไม่มีรหัสอ้างอิงติดมา คอลัมน์นี้จะว่าง แม้เหตุการณ์ติดตามจะมีรหัสก็ตาม - การส่งเหตุการณ์ต่อให้ Workflow เป็นแบบ best-effort — เมื่อบันทึกเหตุการณ์เข้าชมสำเร็จ ระบบ publish ข้อความเข้าคิว trigger ให้ automation ทำงานต่อ แต่หากการ publish ล้มเหลว จะเพียงเขียน log แล้วปล่อยผ่าน ไม่ทำให้คำขอล้มเหลว — เป็นไปได้ที่รายงานนับการเข้าชมนั้นไว้แล้วแต่ Workflow ที่ผูกไว้ไม่ทำงาน
เมนูที่เกี่ยวข้องกันในเส้นทางนี้
- User Attribute Setup — เป็นแหล่งของทั้งสองรายการเลือกในฟอร์ม แต่เป็นสองเส้นทางที่ต่างกัน: ช่อง "คุณสมบัติที่จะเขียน" ดึงรายการ merge tag (ทั้งชุดมาตรฐานและที่สร้างเอง) ส่วนช่อง "คีย์เก็บรหัสอ้างอิง" ดึงจาก schema ของ Attribute Master โดยตรง — คุณสมบัติที่เขียนลงไปจะไปปรากฏในข้อมูลผู้ใช้ที่เมนู Member database
- Workflow — โหนด trigger
trigger_friend_track_eventใช้เหตุการณ์ของ Friends Track เป็นจุดเริ่มของ automation และรายการแคมเปญที่เลือกได้ในโหนดนั้นดึงเฉพาะแคมเปญ สถานะเปิดใช้งาน เท่านั้น - Content management — ถ้าผูก Content page ที่เผยแพร่แล้วกับแคมเปญนี้ ไฟล์ลิงก์ที่ดาวน์โหลดจะมีคอลัมน์ลิงก์หน้าคอนเทนต์เพิ่มขึ้นมาให้ใช้แจกแทนลิงก์ตรง
- Segment audience — คุณสมบัติที่ Friends Track เขียนให้ผู้ใช้ใช้เป็นเงื่อนไขสร้างกลุ่มเป้าหมายในเมนูนั้นต่อได้
จุดที่ควรระวัง
- ช่องเลือกช่วงวันที่บนหน้ารายงานยังไม่มีผลจริง — หน้าเว็บส่งพารามิเตอร์ชื่อ
startDate/endDateแต่ backend อ่านเฉพาะdateFrom/dateToเท่านั้น พารามิเตอร์ที่ส่งไปจึงถูกละเลยทั้งคู่ ตัวเลขในการ์ดสถิติและกราฟจึงเป็น ยอดตลอดอายุแคมเปญเสมอ ไม่ว่าจะเลือกช่วงวันที่ใด (การเลือกวันจะทำให้หน้าเว็บยิงคำขอใหม่จริง แต่ผลลัพธ์ที่กลับมาเหมือนเดิมทุกครั้ง) - คอลัมน์ "ประเภทผู้ใช้" ในตารางรายงานจะแสดงขีด
-เสมอ — backend ไม่ได้ส่งฟิลด์userTypeกลับมาในผลลัพธ์รายชื่อผู้ใช้เลย - ไม่มีทางกรองรายงานตามรหัสอ้างอิงจากหน้าเว็บ — backend รองรับพารามิเตอร์
refIdไว้แล้ว แต่หน้ารายงานไม่ได้ส่งไป จึงดูได้แค่ว่าแต่ละคนมาจากรหัสใด ยังไม่มีทางสรุปยอดแยกตามพนักงาน/สาขาในหน้าจอ ต้องนำข้อมูลออกไปสรุปเอง - ยอดเลิกติดตามอาจมากกว่ายอดติดตามได้ — ระบบไม่บันทึกเหตุการณ์ติดตามซ้ำให้แคมเปญเดิม (มีแล้วข้าม) แต่บันทึกเหตุการณ์เลิกติดตามได้ทุกครั้ง คนที่เพิ่ม-เลิก-เพิ่ม-เลิกจึงเพิ่มยอดเลิกติดตามได้เรื่อย ๆ ขณะที่ยอดติดตามค้างอยู่ที่ 1
- หน้ารายการแสดงเพียง token ไม่ใช่ URL เต็ม — ลิงก์จริงประกอบขึ้นที่ฝั่งเซิร์ฟเวอร์จาก
clientWebUrlกับ hash ของ OA ต้องดาวน์โหลดไฟล์ลิงก์เพื่อได้ URL ที่ใช้แจกได้จริง - สถานะการอัปโหลดตรวจเฉพาะตอนเปิดหน้าและหลังอัปโหลดเสร็จ — ไม่มีการ poll ต่อเนื่อง ถ้าปุ่มดาวน์โหลดไฟล์ลิงก์ยังกดไม่ได้ ให้โหลดหน้าใหม่
- อัปโหลด CSV โดยไม่แนบไฟล์จะได้ 500 แบบไม่มีรหัสข้อผิดพลาด — เป็นพฤติกรรมที่คงไว้ตรงตามระบบต้นทาง (คำตอบเป็น
{"statusCode":500,"message":"Internal server error"}ไม่มีคีย์codeตามรูปแบบปกติของ API นี้) - token ต้องไม่ซ้ำทั้งระบบ ไม่ใช่แค่ใน OA ของตัวเอง — การพิมพ์ token สั้น ๆ ที่เดาง่าย (เช่น
promo2026) มีโอกาสชนกับแคมเปญขององค์กรอื่นและถูกปฏิเสธด้วย "Token already exists"
สิทธิ์การเข้าถึง (ตรวจสอบจากซอร์สโค้ดจริง)
- ฝั่งหน้าเว็บ — ทั้งหน้ารายการ ฟอร์ม และรายงานถูกครอบด้วย
PermissionGuardตรวจ actionVIEWของ subjectfriend-track - ฝั่งเซิร์ฟเวอร์ — ทั้ง 9 เส้นทางของโมดูลนี้ถูกครอบด้วย
ModuleGate("friend-track")ที่บังคับใช้จริง (ผู้เรียกที่เป็น onemoby ข้ามได้) ถ้าผู้ดูแลระดับแพลตฟอร์มปิดโมดูลนี้ให้องค์กรใด ผู้ใช้ในองค์กรนั้นจะได้ 403 ทันทีแม้เรียก API ตรง - policy annotation ระดับการกระทำ (
create/readAll/read/update/delete) มีประกาศไว้ครบทุกเส้นทาง แต่วิ่งผ่าน middleware ที่เป็น no-op เหมือนโมดูลอื่นทั้งระบบ — การบังคับจริงจึงอยู่ที่ ModuleGate ระดับโมดูล - ทุกคำสั่งอ่าน/เขียนถูกจำกัดขอบเขตด้วย
line_oa_idของ token ผู้เรียก — รวมถึงการดึงแคมเปญด้วย id ตรง ๆ ด้วย ผู้ใช้จาก OA หนึ่งจึงเดา id เพื่อไปอ่านแคมเปญของ OA อื่นไม่ได้ DELETE /api/friend-track/:idเป็น soft delete (ประทับdeleted_date) — แคมเปญที่ลบแล้วหายจากรายการทันที แต่เหตุการณ์และรหัสอ้างอิงที่บันทึกไว้ยังอยู่ในฐานข้อมูล- ไม่มีการตรวจสิทธิ์ระดับ Super Admin หรือ hard delete ในโมดูลนี้เลย
ไฟล์อ้างอิงจากซอร์สโค้ด
Frontend (cms-web-2026)
src/app/friend-track/page.tsx,form/page.tsx,report/page.tsx— จุดเข้าทั้งสามหน้าและตัวครอบสิทธิ์src/components/friend-track/list/friend-track-list.container.tsx— ตาราง ตัวกรอง และการลบsrc/components/friend-track/form/friend-track-form.container.tsx— การประกอบ payload, การสร้าง token ฝั่งหน้าเว็บ, การตัด/เติมคำนำหน้าcustom.ของคีย์คุณสมบัติsrc/components/friend-track/form/friend-track-form.tsx— การ์ดรหัสอ้างอิง (อัปโหลด/ดาวน์โหลด/สถานะ), รายการเลือกคุณสมบัติทั้งสองแหล่งsrc/components/friend-track/report/friend-track-report.container.tsx— การ์ดสถิติ 4 ใบ, กราฟเส้น, ตารางรายชื่อ (จุดที่ส่งพารามิเตอร์วันที่ผิดชื่อ)src/services/friend-track.service.ts— endpoint ทั้งหมดที่หน้าเว็บเรียกใช้
Backend (line-management-cms-api-go)
internal/modules/friendtrack/controller.go— 9 เส้นทาง,ModuleGate("friend-track"), การอ่านไฟล์ multipartinternal/modules/friendtrack/service.go— การสร้าง token, การอัปโหลด/ดาวน์โหลดรหัสอ้างอิง, การประกอบลิงก์, การคำนวณรายงานทั้ง 3 ส่วนinternal/modules/friendtrack/dto.go— กฎตรวจสอบข้อมูลขาเข้าและชื่อพารามิเตอร์ที่รองรับจริง (dateFrom/dateTo/refId)
Backend อื่นที่เกี่ยวข้อง
line-management-client-api-go/internal/friendtrack/service.go— จุดที่บันทึกเหตุการณ์เข้าชมจริง กฎ dedup การเขียนคุณสมบัติ และการเขียนเหตุการณ์ติดตามให้คนที่เป็นเพื่อนอยู่แล้วline-management-worker-go/internal/friendtrack/service.go— การให้เครดิตแคมเปญแบบ last-touch และการบันทึกเหตุการณ์ติดตาม/เลิกติดตามจาก webhook
ดูรายละเอียดเชิงเทคนิคเพิ่มเติม (เวอร์ชันก่อนหน้าของทีมเอกสาร) ที่ ติดตามการเพิ่มเพื่อน