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 ตรง ๆ
- กรอกชื่อ audience (ต้องไม่ซ้ำในระบบเดียวกัน)
- เลือกประเภทเงื่อนไข 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 เงื่อนไข แต่ละกลุ่มมีตัวเชื่อมภายใน และมีตัวเชื่อมระดับบนสุดคุมระหว่างกลุ่ม
- ตั้งตัวเชื่อม AND/OR ระดับบนสุด (ค่าเริ่มต้น AND) — สำหรับประเภทแคมเปญ ถ้าสลับไป OR ทั้งที่ติ๊ก Reach ไว้ ระบบจะล้างสิ่งที่เลือกให้
- ดูตัวอย่างรายชื่อ — สี่ประเภทแรกคำนวณอัตโนมัติทุกครั้งที่เงื่อนไขเปลี่ยน (ไม่มี debounce) แต่ยิงจริงเมื่อผ่านเกณฑ์ขั้นต่ำแล้วเท่านั้น เช่น แคมเปญต้องมี Reach หรือ spot อย่างน้อยหนึ่งอัน, Rich menu ต้องเลือกชนิดเมนูและ spot แล้ว ส่วนประเภทฟอร์ม ต้องกดปุ่ม Search เอง และตัวอย่างคืนได้ไม่เกิน 10 รายการ (เพดานบังคับที่ DTO)
- กดสร้าง — ประเภทฟอร์มยิง
POST /audiences-filter/create-form-filterประเภทอื่นยิงPOST /audiences-filter/create-filterเมื่อสำเร็จระบบพากลับหน้ารายการ และกลุ่มพร้อมใช้งานทันที
3. ให้กลุ่มอัปเดตตัวเองอัตโนมัติ
- เปิดสวิตช์ Auto-refresh ที่แถวของกลุ่มนั้น (ต้องเป็นแพ็กเกจที่รองรับ — เป็นฟีเจอร์ระดับพรีเมียม)
- รอบเวลาคำนวณ ตั้งที่ระดับ LINE OA ไม่ใช่ระดับกลุ่ม — สวิตช์ที่แถวเป็นเพียงการบอกว่ากลุ่มนี้เข้าร่วมรอบนั้นด้วยหรือไม่
- งานเบื้องหลังจะไล่คำนวณเมื่อครบรอบ เขียนไฟล์ CSV ใหม่ อัปเดตยอดสมาชิก และ บันทึกล็อกว่าใครเข้าใหม่ใครหลุดออก พร้อมส่งผลต่างนั้นไปกระตุ้น trigger ที่ผูกกับกลุ่มไว้
- อยากคำนวณเดี๋ยวนี้ ก็กดปุ่ม 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จะตอบ 400Not found trackingLineUsersTrackingKeyและcreate-form-filterตอบ 400No matching users foundโดยยังไม่ได้สร้างแถวใด ๆ ทิ้งไว้ — ขณะที่เส้นทาง Multi-source filter ยอมสร้างกลุ่มว่างได้ (ดูหน้านั้น) - งานคำนวณอัตโนมัติเป็น cron ทุกนาที ล็อกด้วย Redis —
scheduler.goลงทะเบียน cron* * * * *และก่อนทำงานทุกครั้งจะจอง lock ด้วยSetNXที่คีย์audience:refresh:scheduler:lockTTL 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 ตรวจเงื่อนไขนี้เหมือนกัน (ปุ่มตอบ 400Audience refresh is already in progress) ถ้า worker ตายกลางการคำนวณ กลุ่มนั้นจะติดค้างที่processingและไม่มีอะไรมาปลดให้อัตโนมัติ ในทางกลับกัน สถานะfailedไม่ทำให้ค้าง เพราะ cron กรองแค่!= 'processing'เท่านั้น รอบถัดไปจึงหยิบไปลองใหม่เอง - worker เรียกกลับเข้ามาคำนวณผ่านเส้นทาง internal ที่กันด้วย API key ไม่ใช่ JWT —
POST /api/audiences-filter/internal/refreshลงทะเบียนบน public group แล้วครอบด้วย guard ที่เทียบ headerx-internal-keyกับ envINTERNAL_API_KEYถ้า env นี้ไม่ได้ตั้งค่าไว้ guard จะปฏิเสธทุกคำขอ (เงื่อนไขเช็กexpected == ""ด้วย) แปลว่าการ deploy ที่ลืมใส่ตัวแปรนี้จะทำให้ auto refresh ทั้งระบบเงียบไปเลยโดยที่หน้าเว็บไม่มีอะไรฟ้อง - ลำดับการคำนวณใหม่ถูกแก้ต่างจากต้นฉบับโดยเจตนา — คอมเมนต์ในซอร์สอธิบายว่าต้นฉบับ NestJS ถอดสมาชิกออกก่อน แล้วค่อยประเมินเงื่อนไขใหม่ ซึ่งเปิดช่องเวลาที่กลุ่มว่างเปล่า และถ้าโปรเซสตายระหว่างนั้นสภาพว่างจะค้างถาวรจนถึงรอบถัดไป พอร์ต Go จึงกลับลำดับเป็น ประเมินเงื่อนไขให้ได้รายชื่อใหม่ก่อน แล้วสลับสมาชิกทั้งชุดในทรานแซกชันเดียว
- ทุกการคำนวณจะจดล็อกสมาชิกและกระตุ้น automation ตามผลต่าง — ระบบเทียบรายชื่อเก่ากับใหม่ ได้
addedและremovedแล้วบันทึกลงตารางaudience_member_log(actionadd/removeพร้อมtriggerTypeเป็นmanualหรือauto_refresh) จากนั้น publish งานเข้าคิว audience-membership-trigger เฉพาะเมื่อมีผลต่างจริง ถ้าคำนวณแล้วสมาชิกไม่เปลี่ยนเลยก็ไม่มี automation ใดถูกกระตุ้น - การกดคำนวณใหม่และการเปิด auto refresh จำกัดที่
dataSource = 'filter'เท่านั้น — ตรวจที่ service ทั้งสองเส้นทาง ตอบ 400Only 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ลงทะเบียนทุกเส้นทางตรงบนauthedgroup โดยมีแต่auth.CheckPolicies(...)ซึ่งเป็น no-op ไม่มีการเรียกmodulegate.ModuleGateที่ใดในไฟล์ ผลคือ แม้ผู้ดูแลระดับแพลตฟอร์มจะปิดโมดูล audiences ให้องค์กรใด เส้นทางสร้างกลุ่มด้วยเงื่อนไข ดู dropdown แคมเปญ/เมนู/ฟอร์ม และดูตัวอย่างรายชื่อ ก็ยังเรียกได้ปกติทั้งหมดถ้ามี JWT ที่ถูกต้อง — การปิดโมดูลกันได้แค่การอ่าน/แก้/ลบตัวกลุ่ม ไม่ได้กันการสร้างกลุ่มใหม่ - เส้นทางเดียวในโมดูล audiencefilter ที่มีการบังคับจริงคือ
POST /audiences-filter/internal/refreshซึ่งอยู่บน public group แต่ครอบด้วย guard เทียบ headerx-internal-keyกับ envINTERNAL_API_KEY(ไม่ผ่าน JWT เลย เป็นทางเข้าสำหรับ worker เท่านั้น) ถ้า env ว่างจะปฏิเสธทุกคำขอด้วย 401Invalid internal API key - ฝั่งหน้าเว็บไม่มี
PermissionGuardบนหน้า audience ใด ๆ — ควบคุมได้แค่การซ่อนเมนูข้างด้วยability.can('view', 'audience')พิมพ์ URL เข้าตรงไม่ถูกกัน - policy annotation ของเส้นทางในโมดูล audiencefilter ประกาศเป็น
create/readบน subjectAudiencesครบทุกเส้นทาง แต่วิ่งผ่าน 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), การประกอบ payloadsrc/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.tsxsrc/components/audience/csv/csv-table.container.tsx— ตารางที่ใช้ร่วมกับหน้า Upload, สวิตช์ auto-refresh, ปุ่ม Refresh nowsrc/services/audience-filter.service.ts,src/services/audience.service.tssrc/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,internalApiKeyGuardinternal/modules/audiencefilter/service.go— การสร้างแบบซิงโครนัส,uploadAndComplete,RefreshAudienceและลำดับที่แก้จากต้นฉบับinternal/modules/audiencefilter/dto.go— กฎ validation, เพดานlimitของตัวอย่างที่ 10internal/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)