Skip to main content

Segment audience — Filter audience

ภาพรวม​

Filter audience คือโหมดสร้างกลุ่มเป้าหมายจาก เงื่อนไขเดียว โดยเลือกได้จาก 5 แหล่งข้อมูล — แคมเปญ, Rich menu, activity tag, Auto Response, หรือฟอร์ม แทนการอัปโหลดรายชื่อเอง กลุ่มที่สร้างจากโหมดนี้ (และจากโหมด Multi-source filter) เป็นกลุ่มเดียวในระบบที่รองรับการคำนวณสมาชิกใหม่อัตโนมัติเป็นรอบ (auto refresh) และการกดคำนวณใหม่ทันที

จุดที่ต่างจากโหมด Upload audience อย่างชัดเจนคือ การสร้างกลุ่มด้วยเงื่อนไขทำงานแบบซิงโครนัสทั้งหมดในคำขอเดียว ไม่มีการเข้าคิว — backend คำนวณรายชื่อ สร้างแถว ผูกสมาชิก เขียนไฟล์ CSV ขึ้น object storage แล้วปิดสถานะเป็น completed ให้เสร็จก่อนตอบกลับ กลุ่มที่สร้างสำเร็จจึงพร้อมใช้งานทันที (ดูหัวข้อ รายละเอียดสำคัญ)

บทบาทที่ใช้งาน — ทีมการตลาดที่ต้องแบ่งกลุ่มลูกค้าตามพฤติกรรมจริง เช่น คนที่คลิกแคมเปญ กดเมนู หรือตอบฟอร์ม

เส้นทางผู้ใช้งาน​

1. ดูรายการกลุ่มจากเงื่อนไข​

เปิดเมนู Segment audience > Filter audience หน้ารายการนี้ใช้ container เดียวกันกับ Upload audience แต่ตรึง dataSource=filter ไว้ จึงแสดงกลุ่มที่มาจากเงื่อนไขทั้งแบบแหล่งเดียวและแบบหลายแหล่งรวมกันในตารางเดียว ตัวกรองมีค้นหาจากชื่อและเลือกสถานะ

คอลัมน์ที่ต่างจากหน้า Upload คือ Auto-refresh และ Last synced

  • ที่หัวตารางมีป้ายบอกรอบที่ตั้งไว้ระดับ LINE OA — "Auto-refresh: {นาที} min" หรือ "Auto-refresh: Off" ดึงจาก GET /line-oa/{id}/audience-refresh-settings
  • แต่ละแถวมีสวิตช์เปิด/ปิดการเข้าร่วมรอบอัตโนมัติของกลุ่มนั้น (PATCH /audiences/{id}/auto-refresh) — กลุ่มของระบบภายนอกจะแสดง - แทน
  • คอลัมน์ Last synced แสดง Never ถ้ายังไม่เคยคำนวณ, ป้าย "Syncing..." แบบหมุนถ้า refreshStatus = processing หรือวันเวลาที่คำนวณล่าสุด
  • ปุ่มรูปลูกศรวน "Refresh now" (POST /audiences/{id}/refresh) ปิดใช้งานอยู่ระหว่างที่กำลังคำนวณ

ปุ่มดำเนินการอื่นในแถว — Copy (พาไปฟอร์ม Multi-source filter พร้อม action=copy) และ Delete ซึ่งจะขึ้นให้กดได้เฉพาะกลุ่มที่สถานะ completed แล้วเท่านั้น

2. สร้างกลุ่มจากเงื่อนไขเดียว​

ปุ่ม Create segment ที่หัวตารางพาไปฟอร์ม Multi-source filter เป็นค่าเริ่มต้น ฟอร์มแบบแหล่งเดียวที่อธิบายในหัวข้อนี้เข้าถึงได้จากลิงก์ในหน้าผลตอบแบบฟอร์มของ Form builder หรือพิมพ์ URL /audience/filter/form?id=create ตรง ๆ

  1. กรอกชื่อ audience (ต้องไม่ซ้ำในระบบเดียวกัน)
  2. เลือกประเภทเงื่อนไข 1 อย่างจาก 5 ตัวเลือก — การสลับประเภทจะล้างค่าที่เลือกไว้ทั้งหมดทิ้ง
    • แคมเปญ — เลือกแคมเปญจากเมนู Campaign แล้วติ๊ก tracking spot ที่ต้องการ มีเช็กบ็อกซ์ "Reach (View)" สำหรับการมองเห็น และกลุ่มเช็กบ็อกซ์ "Click" ต่อ bubble โดยข้อความชนิดรูปภาพกดได้จะแตกเป็น "Spot A/B/…" ชนิด Flex จะเป็นตารางให้ติ๊กทีละแถว และข้อความตัวอักษรล้วนจะถูกปิดไว้ (ติดตามคลิกไม่ได้)
    • Rich Menu — เลือกชนิดเมนูก่อน (guest / member / custom / switch) จากนั้นค่อยเลือกเมนูจาก Rich menu และติ๊ก spot โดยเปิดให้ติ๊กเฉพาะ action ชนิด Link, Text และ Switch เท่านั้น (ชนิดอื่นไม่บันทึกการคลิก)
    • Activity tag — ติ๊กช่วงความเคลื่อนไหว 4 แบบ — active-today, active-in-week (7 วันล่าสุด ไม่รวมวันนี้), active-over-week (เกิน 7 วัน), inactive-over-30-day (ไม่ใช้งานเกิน 30 วัน)
    • Auto Response — เพิ่มแถวเลือกกฎจากเมนู Auto-response message ได้สูงสุด 5 แถว (หรือเท่าจำนวนกฎที่มีจริงถ้ามีน้อยกว่า)
    • ฟอร์ม — เลือกฟอร์มจาก Form builder (เฉพาะฟอร์มสถานะ active) แล้วสร้างเงื่อนไขในแผง "Advance filter" ได้สูงสุด 5 กลุ่ม กลุ่มละ 5 เงื่อนไข แต่ละกลุ่มมีตัวเชื่อมภายใน และมีตัวเชื่อมระดับบนสุดคุมระหว่างกลุ่ม
  3. ตั้งตัวเชื่อม AND/OR ระดับบนสุด (ค่าเริ่มต้น AND) — สำหรับประเภทแคมเปญ ถ้าสลับไป OR ทั้งที่ติ๊ก Reach ไว้ ระบบจะล้างสิ่งที่เลือกให้
  4. ดูตัวอย่างรายชื่อ — สี่ประเภทแรกคำนวณอัตโนมัติทุกครั้งที่เงื่อนไขเปลี่ยน (ไม่มี debounce) แต่ยิงจริงเมื่อผ่านเกณฑ์ขั้นต่ำแล้วเท่านั้น เช่น แคมเปญต้องมี Reach หรือ spot อย่างน้อยหนึ่งอัน, Rich menu ต้องเลือกชนิดเมนูและ spot แล้ว ส่วนประเภทฟอร์ม ต้องกดปุ่ม Search เอง และตัวอย่างคืนได้ไม่เกิน 10 รายการ (เพดานบังคับที่ DTO)
  5. กดสร้าง — ประเภทฟอร์มยิง POST /audiences-filter/create-form-filter ประเภทอื่นยิง POST /audiences-filter/create-filter เมื่อสำเร็จระบบพากลับหน้ารายการ และกลุ่มพร้อมใช้งานทันที

3. ให้กลุ่มอัปเดตตัวเองอัตโนมัติ​

  1. เปิดสวิตช์ Auto-refresh ที่แถวของกลุ่มนั้น (ต้องเป็นแพ็กเกจที่รองรับ — เป็นฟีเจอร์ระดับพรีเมียม)
  2. รอบเวลาคำนวณ ตั้งที่ระดับ LINE OA ไม่ใช่ระดับกลุ่ม — สวิตช์ที่แถวเป็นเพียงการบอกว่ากลุ่มนี้เข้าร่วมรอบนั้นด้วยหรือไม่
  3. งานเบื้องหลังจะไล่คำนวณเมื่อครบรอบ เขียนไฟล์ CSV ใหม่ อัปเดตยอดสมาชิก และ บันทึกล็อกว่าใครเข้าใหม่ใครหลุดออก พร้อมส่งผลต่างนั้นไปกระตุ้น trigger ที่ผูกกับกลุ่มไว้
  4. อยากคำนวณเดี๋ยวนี้ ก็กดปุ่ม Refresh now ได้ตลอด — ทั้งสองเส้นทางลงเอยที่ตรรกะคำนวณตัวเดียวกัน ต่างกันแค่ค่า triggerType ที่บันทึกลงล็อก (manual กับ auto_refresh)

รายละเอียดสำคัญที่ตรวจสอบจากซอร์สโค้ดจริง​

ยืนยันจาก internal/modules/audiencefilter/{controller,service,dto,multi_source_query_builder}.go และ internal/modules/audience/scheduler.go ของ line-management-cms-api-go:

  • การสร้างกลุ่มด้วยเงื่อนไขเป็นงานซิงโครนัสทั้งหมด ไม่มีคิวเข้ามาเกี่ยวเลย — ต่างจากโหมด Upload อย่างสิ้นเชิง CreateAudienceFilter ทำครบทุกขั้นในคำขอเดียว: ค้นรายชื่อจาก tracking → สร้างแถว audience สถานะ processing → เขียน audience_ids ให้ผู้ใช้ทุกคน → สร้างไฟล์ CSV คอลัมน์เดียว (Line UserID) อัปขึ้น object storage → อัปเดตสถานะเป็น completed พร้อมสถิติ แล้วจึงตอบกลับ ถ้าคำขอสำเร็จ กลุ่มก็พร้อมใช้เลย ไม่ต้องรอรอบใด ๆ (คำเตือนเรื่อง "เข้าคิวคำนวณ" ใช้กับโหมด Upload เท่านั้น)
  • สร้างกลุ่มที่ไม่มีใครเข้าเงื่อนไขไม่ได้ (เฉพาะสองเส้นทางนี้) — ถ้าคำนวณแล้วได้ศูนย์รายชื่อ create-filter จะตอบ 400 Not found trackingLineUsersTrackingKey และ create-form-filter ตอบ 400 No matching users found โดยยังไม่ได้สร้างแถวใด ๆ ทิ้งไว้ — ขณะที่เส้นทาง Multi-source filter ยอมสร้างกลุ่มว่างได้ (ดูหน้านั้น)
  • งานคำนวณอัตโนมัติเป็น cron ทุกนาที ล็อกด้วย Redis — scheduler.go ลงทะเบียน cron * * * * * และก่อนทำงานทุกครั้งจะจอง lock ด้วย SetNX ที่คีย์ audience:refresh:scheduler:lock TTL 55 วินาที ซึ่งตั้งไว้ให้สั้นกว่ารอบ 1 นาทีโดยเจตนา เพื่อให้ lock หมดอายุเองก่อนรอบถัดไปมาถึงแม้กระบวนการจะตายกลางทาง ถ้าจอง lock ไม่ได้ก็ออกทันที (แปลว่ารันหลาย pod พร้อมกันได้ปลอดภัย มีแค่ตัวเดียวที่ทำงานจริงต่อรอบ)
  • เงื่อนไขคัดกลุ่มที่ถึงกำหนดคำนวณมี 5 ข้อ — cron จะอ่าน LINE OA ที่ audience_refresh_enabled = true และมี audience_refresh_interval ตั้งไว้ แล้วในแต่ละ OA คัดกลุ่มที่ auto_refresh_enabled = true และ data_source = 'filter' และ ยังไม่ถูกลบ และ refresh_status != 'processing' และ (last_refresh_date IS NULL หรือเก่ากว่า NOW() - INTERVAL 'N minutes') — จากนั้นประทับสถานะ processing ก่อน ส่งงานเข้าคิวเสมอ เพื่อกันไม่ให้รอบถัดไปหยิบกลุ่มเดิมซ้ำ
  • สถานะ processing ที่ค้างอยู่ทำให้กลุ่มหยุดคำนวณไปตลอด — เพราะทั้ง cron และปุ่ม Refresh now ตรวจเงื่อนไขนี้เหมือนกัน (ปุ่มตอบ 400 Audience refresh is already in progress) ถ้า worker ตายกลางการคำนวณ กลุ่มนั้นจะติดค้างที่ processing และไม่มีอะไรมาปลดให้อัตโนมัติ ในทางกลับกัน สถานะ failed ไม่ทำให้ค้าง เพราะ cron กรองแค่ != 'processing' เท่านั้น รอบถัดไปจึงหยิบไปลองใหม่เอง
  • worker เรียกกลับเข้ามาคำนวณผ่านเส้นทาง internal ที่กันด้วย API key ไม่ใช่ JWT — POST /api/audiences-filter/internal/refresh ลงทะเบียนบน public group แล้วครอบด้วย guard ที่เทียบ header x-internal-key กับ env INTERNAL_API_KEY ถ้า env นี้ไม่ได้ตั้งค่าไว้ guard จะปฏิเสธทุกคำขอ (เงื่อนไขเช็ก expected == "" ด้วย) แปลว่าการ deploy ที่ลืมใส่ตัวแปรนี้จะทำให้ auto refresh ทั้งระบบเงียบไปเลยโดยที่หน้าเว็บไม่มีอะไรฟ้อง
  • ลำดับการคำนวณใหม่ถูกแก้ต่างจากต้นฉบับโดยเจตนา — คอมเมนต์ในซอร์สอธิบายว่าต้นฉบับ NestJS ถอดสมาชิกออกก่อน แล้วค่อยประเมินเงื่อนไขใหม่ ซึ่งเปิดช่องเวลาที่กลุ่มว่างเปล่า และถ้าโปรเซสตายระหว่างนั้นสภาพว่างจะค้างถาวรจนถึงรอบถัดไป พอร์ต Go จึงกลับลำดับเป็น ประเมินเงื่อนไขให้ได้รายชื่อใหม่ก่อน แล้วสลับสมาชิกทั้งชุดในทรานแซกชันเดียว
  • ทุกการคำนวณจะจดล็อกสมาชิกและกระตุ้น automation ตามผลต่าง — ระบบเทียบรายชื่อเก่ากับใหม่ ได้ added และ removed แล้วบันทึกลงตาราง audience_member_log (action add/remove พร้อม triggerType เป็น manual หรือ auto_refresh) จากนั้น publish งานเข้าคิว audience-membership-trigger เฉพาะเมื่อมีผลต่างจริง ถ้าคำนวณแล้วสมาชิกไม่เปลี่ยนเลยก็ไม่มี automation ใดถูกกระตุ้น
  • การกดคำนวณใหม่และการเปิด auto refresh จำกัดที่ dataSource = 'filter' เท่านั้น — ตรวจที่ service ทั้งสองเส้นทาง ตอบ 400 Only filter-based audiences can be auto-refreshed / Only filter-based audiences can be refreshed และการเปิด auto refresh ยังต้องผ่าน AssertAutoRefreshAllowed ซึ่งเป็นด่านฟีเจอร์ระดับแพ็กเกจอีกชั้น
  • ตัวเลือก Rich menu มีการแก้จากต้นฉบับสองจุด ระบุเหตุผลไว้ในคอมเมนต์ตรง ๆ — (1) ชื่อเมนูใน dropdown ถูกทำให้ตกกลับไปใช้ชื่อของเมนูเองเสมอ เพราะต้นฉบับตั้งชื่อไว้แค่ภายในลูปของข้อมูล tracking ทำให้เมนูที่ยังไม่มีข้อมูล tracking ถูกส่งออกมาโดยไม่มีชื่อ และหน้าเว็บโชว์เป็นเลข id ดิบ (เป็น defect ที่ QA เจอ) และ (2) คีย์ spot ของเมนูแบบ custom ถูกเปลี่ยนเป็นรูปแบบ 3 ส่วน {richMenuId}:0:{actionIndex} เพราะการคลิก rich menu ถูกบันทึกในรูปแบบ 3 ส่วนเสมอ คีย์แบบ 2 ส่วนที่ต้นฉบับสร้างจึงไม่เคยตรงกับข้อมูลที่เก็บไว้ ทำให้ การกรองด้วย rich menu แบบ custom เคยคืนผลเป็นศูนย์คนแบบเงียบ ๆ
  • คำถามในฟอร์มไม่ได้เอามาให้กรองได้ทุกชนิด — GET /audiences-filter/form-questions/{formId} ตัดชนิด header1, header2, divider และ terms ออกก่อนส่งกลับ เพราะเป็นองค์ประกอบตกแต่งที่ไม่มีคำตอบให้เทียบ
  • การผูกสมาชิกของประเภทฟอร์มยิงเป็นก้อนละ 5,000 รายชื่อ — ต่างจากประเภทอื่นที่ยิงทีเดียวจบ เพราะกลุ่มจากฟอร์มมีโอกาสได้รายชื่อจำนวนมากพร้อมกัน
  • ชื่อซ้ำตอบกลับพร้อมรายละเอียดฟิลด์ — ทุกเส้นทางสร้างในโมดูลนี้ตอบ 400 Title is already exist พร้อม data: [{field: "title", message, value}] ให้หน้าเว็บผูกข้อความไปที่ช่องชื่อได้ตรง ๆ

เมนูที่เกี่ยวข้องกันในเส้นทางนี้​

  • Campaign — แหล่งข้อมูลของเงื่อนไขประเภทแคมเปญ ดึงผ่าน GET /audiences-filter/list-dropdown-campaign ซึ่งรับพารามิเตอร์ statusCampaign (sent / scheduled / cancel / draft) และคืน tracking spot ทั้งหมดของ Rich message ที่ผูกอยู่มาให้ติ๊ก
  • Rich menu — แหล่งข้อมูลของเงื่อนไขประเภท Rich Menu ผ่าน GET /audiences-filter/list-dropdown-richmenu โดยชนิด guest/member จะดึงจากประวัติเมนู (archive) ส่วน custom/switch ดึงจากเมนูปัจจุบัน
  • Auto-response message — แหล่งข้อมูลของเงื่อนไขประเภท Auto Response ผ่าน GET /audiences-filter/list-dropdown-auto-response
  • Form builder — แหล่งข้อมูลของเงื่อนไขประเภทฟอร์ม ผ่าน GET /audiences-filter/list-dropdown-form-builder และ GET /audiences-filter/form-questions/{formId} และเป็นทางเข้าเดียวที่พาไปฟอร์มแบบแหล่งเดียวได้จากหน้าเว็บ
  • Multi-source filter — ทางเลือกเมื่อต้องรวมหลายเงื่อนไขเข้าด้วยกัน ใช้หน้ารายการและหน้ารายละเอียดร่วมกันกับหน้านี้ทั้งหมด และเป็นที่ที่ปุ่ม Create segment พาไปเป็นค่าเริ่มต้น
  • Upload audience — โหมดพี่น้องที่ใช้ไฟล์แทนเงื่อนไข เก็บในตารางเดียวกันแต่ dataSource = 'csv' และไม่รองรับ auto refresh

จุดที่ควรระวัง​

  • สถานะ processing ที่ค้างคือทางตาย — ถ้ากลุ่มไหนติดที่ "Syncing..." นานผิดปกติ ทั้งรอบอัตโนมัติและปุ่ม Refresh now จะปฏิเสธกลุ่มนั้นตลอดไป ต้องให้ทีมระบบเข้าไปรีเซ็ต refresh_status ให้
  • รอบเวลา auto refresh เป็นค่าระดับ LINE OA ค่าเดียวสำหรับทุกกลุ่ม — ไม่สามารถตั้งให้กลุ่ม A คำนวณทุก 15 นาที และกลุ่ม B ทุก 6 ชั่วโมงได้ ตั้งได้แค่ "เข้าร่วมรอบ" หรือ "ไม่เข้าร่วม"
  • ทุกครั้งที่คำนวณใหม่ กลุ่มจะกระตุ้น automation ตามผลต่างสมาชิก — ถ้าผูกกลุ่มไว้กับ trigger ที่ส่งข้อความ การเปิด auto refresh รอบสั้น ๆ อาจทำให้ลูกค้าที่วนเข้าออกกลุ่มถูกยิงข้อความซ้ำ ควรตรวจ trigger ที่ผูกอยู่ก่อนเปิด
  • ปุ่ม Copy ในหน้ารายการพาไปฟอร์ม Multi-source เสมอ และฟอร์มนั้นจะดึงค่าเดิมมาให้เฉพาะกลุ่มที่เป็นแบบหลายแหล่ง — กด Copy ที่กลุ่มแบบแหล่งเดียวจะได้ฟอร์มเปล่า ๆ โดยไม่มีคำอธิบายว่าทำไม
  • ฟอร์มแบบแหล่งเดียวเข้าถึงได้ยาก — ไม่มีปุ่มใดในหน้ารายการพาไปที่ฟอร์มนี้ (ปุ่มสร้างไปที่ Multi-source ทั้งหมด) เข้าได้ทางลิงก์จาก Form builder หรือพิมพ์ URL เอง
  • ปุ่มสร้างในฟอร์มแบบแหล่งเดียวเปิดใช้งานได้กว้างเกินจริง — ปุ่มปิดเฉพาะกรณีที่ตัวอย่างคืนอาเรย์ว่างมาแล้ว ดังนั้นก่อนกดดูตัวอย่างเลยหรือกรณีประเภทฟอร์ม (ที่ไม่ได้ใช้ query ตัวเดียวกัน) ปุ่มจะกดได้อยู่ แล้วไปเจอ error 400 จาก backend แทน

สิทธิ์การเข้าถึง (ตรวจสอบจากซอร์สโค้ดจริง)​

หัวข้อนี้คือจุดที่ต่างกันชัดที่สุดระหว่างสองโมดูล backend ที่ประกอบกันเป็นเมนูนี้ — ตรวจแล้วว่าไม่เท่ากัน

  • โมดูล audience (เส้นทาง /api/audiences/*) มี ModuleGate บังคับจริง — internal/modules/audience/controller.go ใส่ modulegate.ModuleGate(d, "audiences") ครอบทั้งกลุ่ม ทุกเส้นทางของ audience (รวม PATCH /:id/auto-refresh และ POST /:id/refresh ที่หน้านี้ใช้) จึงคืน 403 ให้ผู้ใช้ประเภท customer ขององค์กรที่โมดูลถูกปิด
  • โมดูล audiencefilter (เส้นทาง /api/audiences-filter/*) ไม่มี ModuleGate เลย — internal/modules/audiencefilter/controller.go ลงทะเบียนทุกเส้นทางตรงบน authed group โดยมีแต่ auth.CheckPolicies(...) ซึ่งเป็น no-op ไม่มีการเรียก modulegate.ModuleGate ที่ใดในไฟล์ ผลคือ แม้ผู้ดูแลระดับแพลตฟอร์มจะปิดโมดูล audiences ให้องค์กรใด เส้นทางสร้างกลุ่มด้วยเงื่อนไข ดู dropdown แคมเปญ/เมนู/ฟอร์ม และดูตัวอย่างรายชื่อ ก็ยังเรียกได้ปกติทั้งหมดถ้ามี JWT ที่ถูกต้อง — การปิดโมดูลกันได้แค่การอ่าน/แก้/ลบตัวกลุ่ม ไม่ได้กันการสร้างกลุ่มใหม่
  • เส้นทางเดียวในโมดูล audiencefilter ที่มีการบังคับจริงคือ POST /audiences-filter/internal/refresh ซึ่งอยู่บน public group แต่ครอบด้วย guard เทียบ header x-internal-key กับ env INTERNAL_API_KEY (ไม่ผ่าน JWT เลย เป็นทางเข้าสำหรับ worker เท่านั้น) ถ้า env ว่างจะปฏิเสธทุกคำขอด้วย 401 Invalid internal API key
  • ฝั่งหน้าเว็บไม่มี PermissionGuard บนหน้า audience ใด ๆ — ควบคุมได้แค่การซ่อนเมนูข้างด้วย ability.can('view', 'audience') พิมพ์ URL เข้าตรงไม่ถูกกัน
  • policy annotation ของเส้นทางในโมดูล audiencefilter ประกาศเป็น create/read บน subject Audiences ครบทุกเส้นทาง แต่วิ่งผ่าน middleware no-op เหมือนกันทั้งระบบ — จึงไม่มีการบังคับสิทธิ์ระดับการกระทำที่ใดเลยในโมดูลนี้
  • ทุกคำสั่งยังถูกจำกัดขอบเขตข้อมูลด้วย line_oa_id จาก CLS context เสมอ ข้อมูลข้าม OA จึงไม่รั่วถึงกัน — ข้อจำกัดที่ขาดไปคือด่านระดับโมดูล ไม่ใช่ด่าน tenant

ไฟล์อ้างอิงจากซอร์สโค้ด​

Frontend (cms-web-2026)

  • src/app/audience/filter/page.tsx, src/app/audience/filter/form/page.tsx — หน้ารายการและฟอร์มแบบแหล่งเดียว
  • src/components/audience/filter/filter-form.container.tsx, filter-form.tsx, filter-form.util.ts — ประเภทเงื่อนไข 5 แบบ, เกณฑ์ก่อนยิงตัวอย่าง (processCheckBeforeSubmit), การประกอบ payload
  • src/components/audience/filter/components/ — section-campaign.tsx, section-rich-menu.tsx, section-activity-tag.tsx, section-auto-response.tsx, section-form-builder.tsx, form-builder-filter-group.tsx, form-builder-condition-row.tsx, common-condition.tsx
  • src/components/audience/csv/csv-table.container.tsx — ตารางที่ใช้ร่วมกับหน้า Upload, สวิตช์ auto-refresh, ปุ่ม Refresh now
  • src/services/audience-filter.service.ts, src/services/audience.service.ts
  • src/components/audience/constants/audience.constant.ts — TRACKABLE_CAMPAIGN_TYPES, TRACKABLE_RICH_MENU_TYPES

Backend (line-management-cms-api-go)

  • internal/modules/audiencefilter/controller.go — 12 เส้นทาง, ไม่มี ModuleGate, internalApiKeyGuard
  • internal/modules/audiencefilter/service.go — การสร้างแบบซิงโครนัส, uploadAndComplete, RefreshAudience และลำดับที่แก้จากต้นฉบับ
  • internal/modules/audiencefilter/dto.go — กฎ validation, เพดาน limit ของตัวอย่างที่ 10
  • internal/modules/audiencefilter/tracking_line_users_repository.go, list_repositories.go, form_filter_query_builder.go — ตัวค้นรายชื่อและ dropdown ทั้งหมด
  • internal/modules/audience/scheduler.go — cron ทุกนาที, Redis lock TTL 55 วินาที, เงื่อนไขคัดกลุ่มที่ถึงกำหนด
  • internal/modules/audience/service.go — UpdateAutoRefresh, TriggerManualRefresh, GetMemberLogs

ดูรายละเอียดเชิงเทคนิคเพิ่มเติมที่ จัดการกลุ่มเป้าหมาย (Audience Management)