จัดการกลุ่มเป้าหมาย (Audience Management)
ภาพรวม
Audience คือ "กลุ่มรายชื่อผู้ใช้ LINE" ที่โมดูลอื่นนำไปอ้างอิงเพื่อกำหนดว่าจะส่งข้อความหรือแสดงเนื้อหาให้ใครเห็น เช่น Campaign, Rich Menu, Content Management, Trigger Rule, Workflow และ Bulletin
หน้าจอนี้เหมาะกับทีมการตลาดที่ต้องแบ่งกลุ่มลูกค้าตามพฤติกรรมจริง (คลิกแคมเปญ กดเมนู ตอบฟอร์ม ระดับสมาชิก) หรือทีมที่มีรายชื่อลูกค้าอยู่แล้วและต้องการนำเข้ามาใช้งานตรง ๆ
ระบบรองรับ audience 3 โหมด
| โหมด | เมนู / เส้นทาง | ที่มาของรายชื่อ |
|---|---|---|
| อัปโหลด CSV | /audience/csv | ไฟล์ CSV ที่มีคอลัมน์ Line UserID ที่ผู้ใช้อัปโหลดเอง |
| Filter แบบแหล่งเดียว | /audience/filter/form | เงื่อนไข 1 ประเภท เลือกจากแคมเปญ / Rich Menu / activity tag / Auto Response / ฟอร์ม |
| Filter แบบหลายแหล่ง | /audience/filter/multi-source/form | หลายแหล่งรวมกันเป็นกลุ่ม พร้อมกำหนด AND/OR ทั้งภายในกลุ่มและระหว่างกลุ่ม |
จุดสำคัญที่ต้องทราบ
- หน้ารายการของโหมด CSV และโหมด Filter ใช้หน้าจอเดียวกัน แต่สลับคอลัมน์และปุ่มดำเนินการตามโหมดที่เปิดอยู่
- audience ที่มาจาก filter รองรับการคำนวณสมาชิกใหม่อัตโนมัติ (auto refresh) เป็นรอบ และสั่งคำนวณใหม่ทันที (refresh now) ได้ โดยรอบเวลาตั้งไว้ที่ระดับ LINE OA
- audience ที่ถูกสร้างโดยระบบภายนอก (3rd party) จะถูกซ่อนปุ่มดำเนินการทั้งหมดและซ่อนสวิตช์ auto refresh เพื่อป้องกันการแก้ไขข้ามระบบ
- หน้ารายละเอียดใช้ร่วมกันทุกโหมด แสดงกฎ filter ที่ใช้สร้าง กฎ Trigger Rule ที่ผูกอยู่ ตารางสมาชิก และปุ่ม export
- ปุ่มสร้างกลุ่มใหม่ในหน้ารายการโหมด Filter จะพาไปฟอร์มแบบหลายแหล่งเป็นค่าเริ่มต้น ส่วนฟอร์มแบบแหล่งเดียวยังเข้าถึงได้จากลิงก์ในหน้าผลตอบแบบฟอร์มของ Form Builder
Business Flow
1. หน้ารายการ (ใช้ร่วมกัน CSV และ Filter)
- เปิดหน้ารายการตามโหมดที่ต้องการ ระบบเลือกชุดคอลัมน์และปุ่มให้อัตโนมัติจากโหมดนั้น
- โหมด Filter จะโหลดการตั้งค่ารอบ refresh ของ LINE OA เพิ่มด้วย แล้วแสดงป้ายข้างหัวข้อว่าเปิด auto refresh อยู่หรือไม่ และตั้งรอบไว้กี่นาที
- กรองข้อมูลได้ด้วยคำค้นและสถานะ (
completed/processing) โดยการค้นหามีการหน่วงเวลาเล็กน้อยเพื่อลดจำนวนคำขอ และมีปุ่มล้างค่า - คอลัมน์ร่วมของทั้งสองโหมด ได้แก่ ลำดับ ชื่อ audience (คลิกเข้าหน้ารายละเอียด) ระบบต้นทาง จำนวนสมาชิก (สำเร็จเทียบกับทั้งหมด) สถานะ วันที่สร้าง และปุ่มดำเนินการ
- โหมด CSV เพิ่มคอลัมน์คำอธิบายและชื่อไฟล์
- โหมด Filter เพิ่มคอลัมน์สวิตช์ auto refresh และเวลาที่คำนวณล่าสุด (แสดงสถานะกำลังประมวลผล หรือ "ยังไม่เคย" ถ้ายังไม่มีข้อมูล)
- ปุ่มดำเนินการต่างกันตามโหมด
- CSV: แก้ไข และลบ
- Filter: สั่งคำนวณใหม่ทันที ทำสำเนาไปยังฟอร์มแบบหลายแหล่ง และลบ (ลบได้เฉพาะรายการที่คำนวณเสร็จแล้ว)
- การเปิด/ปิด auto refresh บันทึกทันทีจากสวิตช์ในตาราง ส่วนการลบต้องยืนยันผ่านกล่องข้อความก่อน
2. โหมดอัปโหลด CSV
- เปิดฟอร์มเพื่อสร้างใหม่หรือแก้ไขรายการเดิม โหมดแก้ไขจะโหลดชื่อ คำอธิบาย และข้อมูลไฟล์เดิมมาแสดง
- กรอกชื่อและคำอธิบาย (บังคับทั้งคู่) แล้วลากไฟล์ CSV ลงพื้นที่อัปโหลด (บังคับเฉพาะตอนสร้างใหม่) หน้าจอมีคำแนะนำการเตรียมไฟล์ 5 ข้อ พร้อมรูปตัวอย่างและปุ่มดาวน์โหลดไฟล์ต้นแบบ
- ระบบตรวจไฟล์ฝั่งหน้าจอทันทีก่อนส่ง โดยตรวจว่าเป็นไฟล์ CSV จริง แล้วอ่านทีละบรรทัดเพื่อหาแถวว่าง รหัสผู้ใช้ซ้ำ และรหัสผู้ใช้ผิดรูปแบบ (ต้องขึ้นต้นด้วย
Uและยาว 33 ตัวอักษร ส่วนแถวหัวตารางจะถูกข้าม) - หากพบข้อผิดพลาด ระบบเปิดกล่องแสดงรายการแถวที่มีปัญหาพร้อมคำอธิบาย และไม่รับไฟล์นั้นไว้ ผู้ใช้ต้องแก้ไฟล์แล้วอัปโหลดใหม่
- เมื่อผ่านการตรวจ ระบบเปิดกล่องยืนยันที่ระบุจำนวนรหัสผู้ใช้ที่วิเคราะห์ได้ (โหมดแก้ไขจะถามยืนยันการแทนที่รายชื่อเดิมทั้งหมด)
- เมื่อยืนยัน ระบบส่งไฟล์พร้อมข้อมูลประกอบขึ้นเซิร์ฟเวอร์ แล้วพากลับหน้ารายการ
- กรณี backend ตรวจพบข้อผิดพลาดเพิ่มเติมในไฟล์ ระบบจะแปลงกลับเป็นรายการข้อผิดพลาดรายแถวและแสดงในกล่องเดียวกันกับการตรวจฝั่งหน้าจอ
โหมดแก้ไขออกแบบมาสำหรับการแนบไฟล์ใหม่ทับของเดิม การกดบันทึกโดยไม่แนบไฟล์ใหม่จึงควรหลีกเลี่ยง
3. โหมด Filter แบบแหล่งเดียว
- กรอกชื่อ audience แล้วเลือกประเภทเงื่อนไข 1 อย่างจาก 5 ตัวเลือก ได้แก่ แคมเปญ, Rich Menu, activity tag, Auto Response และฟอร์ม การเปลี่ยนประเภทจะล้างค่าที่กรอกไว้ของประเภทเดิม
- แต่ละประเภทจะแสดงส่วนตั้งค่าของตัวเองพร้อมโหลดตัวเลือกที่เกี่ยวข้อง
- แคมเปญ — เลือกแคมเปญแล้วติ๊ก tracking spot ที่ต้องการ โดยแยกระหว่างการมองเห็น (รูป/วิดีโอ) กับการคลิก (ลิงก์/ปุ่ม) และเลือกความสัมพันธ์ AND/OR ระหว่าง spot
- Rich Menu — เลือกชนิดเมนูก่อน (guest / member / custom / switch) แล้วจึงเลือกเมนูและ spot
- Activity tag — ติ๊กช่วงความเคลื่อนไหว 4 แบบ ตั้งแต่ใช้งานวันนี้จนถึงไม่ใช้งานเกิน 30 วัน
- Auto Response — เลือกกฎตอบกลับอัตโนมัติได้หลายรายการ
- ฟอร์ม — เลือกฟอร์ม แล้วสร้างกลุ่มเงื่อนไขจากคำถามในฟอร์มได้สูงสุด 5 กลุ่ม
- ระบบแสดงตัวอย่างรายชื่อที่เข้าเงื่อนไขให้เห็นก่อนสร้าง โดยประเภททั่วไปจะคำนวณให้อัตโนมัติเมื่อเงื่อนไขเปลี่ยน ส่วนประเภทฟอร์มต้องกดปุ่มค้นหาในส่วนตั้งค่าเอง
- ตัวอย่างรายชื่อแสดงลำดับ รหัสผู้ใช้ ชื่อที่แสดง และประเภทผู้ใช้ พร้อมสรุปเงื่อนไขที่เลือกไว้ด้านบน
- ปุ่มสร้างจะใช้งานไม่ได้จนกว่าตัวอย่างรายชื่อจะมีอย่างน้อย 1 รายการ
- เมื่อกดสร้างสำเร็จ ระบบพากลับหน้ารายการ หากเซิร์ฟเวอร์แจ้งข้อผิดพลาดรายฟิลด์ ระบบจะแสดงข้อความใต้ฟิลด์ที่เกี่ยวข้อง
4. โหมด Filter แบบหลายแหล่ง
- โครงสร้างข้อมูลคือ audience หนึ่งชุดประกอบด้วยหลาย กลุ่ม และแต่ละกลุ่มประกอบด้วยหลาย แหล่งข้อมูล (source) โดยกำหนดตัวเชื่อม AND/OR ได้ทั้งระหว่างกลุ่มและระหว่าง source ภายในกลุ่ม
- เพิ่ม source ได้ 7 ชนิด ได้แก่ แคมเปญ, Rich Menu, activity tag, Auto Response, ฟอร์ม, custom attribute และ Loyalty โดยแต่ละชนิดมีค่าตั้งต้นของตัวเอง
- แต่ละ source แสดงเป็นการ์ดที่พับเก็บได้ มีสวิตช์เปิด/ปิดใช้งานชั่วคราวและปุ่มลบ การ์ดจะโหลดตัวเลือกของตัวเองเมื่อถูกใช้งานเท่านั้น
- แคมเปญ — ติ๊กการมองเห็นและเลือก spot ได้ทีละหลายรายการ พร้อมกำหนด AND/OR ภายใน source (spot ที่ติดตามไม่ได้จะถูกปิดไว้)
- Rich Menu — เลือกชนิดเมนูแล้วเลือกเมนูและ spot
- Activity tag — ติ๊กช่วงความเคลื่อนไหวพร้อมตัวเชื่อม AND/OR
- Auto Response — เลือกได้หลายรายการแต่ไม่เกิน 5 รายการต่อ source
- ฟอร์ม — เลือกฟอร์มแล้วสร้างเงื่อนไขจากคำถาม (คำถามที่นำมาสร้างเงื่อนไขไม่ได้จะถูกกรองออก) ชนิดของช่องกรอกค่าจะเปลี่ยนตามชนิดคำถาม เช่น วันที่ ตัวเลข ตัวเลือก หรือข้อความ และรองรับเงื่อนไขแบบช่วง
- Custom attribute — เลือก attribute ที่เปิดให้กรองได้ แบ่งกลุ่มเป็นข้อมูลโปรไฟล์ผู้ใช้และ attribute ที่สร้างเอง โดยตัวดำเนินการเปลี่ยนตามชนิดข้อมูล (ข้อความ ตัวเลข วันที่ ค่าจริงเท็จ)
- Loyalty — เลือกตัวชี้วัด 1 อย่างต่อ source เช่น ยอดคงเหลือ แต้มที่ได้รับ แต้มที่ใช้ ยอดใช้จ่าย ระดับสมาชิก จำนวนบัตรที่สะสมครบ หรือจำนวนวันตั้งแต่ได้แต้มล่าสุด พร้อมตัวดำเนินการเปรียบเทียบ
- แผงด้านขวาแสดงไดอะแกรมแบบ Venn ที่วาดใหม่ทันทีตามโครงสร้างที่กำลังสร้าง ช่วยให้เห็นชัดว่า AND/OR ที่เลือกไว้ให้ผลลัพธ์เป็นเซตแบบใด
- กดปุ่มดูตัวอย่างผลลัพธ์เพื่อคำนวณ ระบบจะคืนจำนวนรวม รายชื่อ 10 รายการแรก และจำนวนที่แต่ละ source เข้าเงื่อนไข
- ตารางตัวอย่างแสดงรูปโปรไฟล์ ชื่อที่แสดง รหัสผู้ใช้ที่คัดลอกได้ ป้ายบอกว่าตรงกับ source ใดบ้าง และประเภทผู้ใช้ หากมี source ประเภทฟอร์มเปิดใช้งานอยู่ จะมีคอลัมน์เพิ่มให้เปิดดูคำตอบล่าสุดของผู้ใช้รายนั้น
- ปุ่มสร้างจะเปิดใช้งานก็ต่อเมื่อกดดูตัวอย่างผลลัพธ์สำเร็จแล้ว และการสร้างต้องมีชื่อ audience พร้อม source ที่เปิดใช้งานอย่างน้อย 1 รายการ
- รองรับการเปิดฟอร์มพร้อมเงื่อนไขตั้งต้นที่ส่งมาจากหน้าอื่น (ปัจจุบันคือหน้า Segments ของโมดูล Loyalty) โดยระบบจะแจ้งเตือนเมื่อโหลดเงื่อนไขตั้งต้นสำเร็จ และเปิดฟอร์มเปล่าให้แทนหากข้อมูลไม่ถูกต้อง
5. หน้ารายละเอียด audience
- เปิดจากชื่อ audience ในหน้ารายการ ระบบโหลดข้อมูล 3 ส่วนพร้อมกัน ได้แก่ ข้อมูล audience, Trigger Rule ที่อ้างถึง audience นี้ และรายชื่อสมาชิก
- ถ้า audience สร้างจาก filter จะมีการ์ด "กฎที่ใช้กรอง" อธิบายเงื่อนไขเป็นข้อความอ่านง่าย กรณีหลายแหล่งจะไล่แสดงทีละกลุ่มพร้อมตัวเชื่อมระหว่างกลุ่ม และมีปุ่มเปิดเงื่อนไขชุดนั้นในตัวสร้าง filter ต่อได้
- ถ้ามี Trigger Rule ผูกอยู่ จะมีการ์ดแสดงรายการกฎพร้อมลิงก์ไปยังกฎนั้นและป้ายบอกชนิด action กับสถานะ
- ตารางสมาชิกแสดงลำดับ รหัสผู้ใช้ (ลิงก์ไปหน้ารายละเอียดเพื่อนใน Report) ชื่อที่แสดงพร้อมรูป ชื่อ นามสกุล และสถานะการเป็นเพื่อน โดยกรองด้วยคำค้นและสถานะได้
- ส่งออกข้อมูลได้ 2 แบบ คือ export รายชื่อพื้นฐาน และ export พร้อม attribute ของผู้ใช้เป็นไฟล์ CSV
6. การเลือก audience จากโมดูลอื่น
- โมดูลปลายทางเรียกรายการ audience ทั้งหมดผ่านบริการกลางเดียวกัน จึงเห็น audience ทุกโหมดเหมือนกัน
- มีคอมโพเนนต์ตัวเลือก audience แบบเลือกหลายรายการให้ใช้ร่วม (
src/components/common/audience-select.tsx) โดยผู้เรียกกำหนดข้อความ placeholder เองได้ เพราะความหมายของ "ไม่เลือกอะไรเลย" ต่างกันในแต่ละบริบท - บางโมดูลที่ต้องการค้นหาแบบแบ่งหน้า เช่น Campaign Management จะเรียกรายการ audience ด้วยบริการของตัวเองแทน
หน้าจอและองค์ประกอบหลัก
หน้ารายการ (/audience/csv, /audience/filter)
- แถบกรอง — ช่องค้นหาชื่อและตัวเลือกสถานะ พร้อมปุ่มค้นหาและล้างค่า
- ป้ายสถานะ auto refresh — แสดงข้างหัวข้อในโหมด Filter บอกว่ารอบคำนวณอัตโนมัติเปิดอยู่หรือไม่
- ตารางรายการ — คอลัมน์ร่วมพร้อมคอลัมน์เฉพาะโหมด และปุ่มดำเนินการที่ต่างกันตามโหมด
ไฟล์อ้างอิงหลัก: src/app/audience/csv/page.tsx, src/app/audience/filter/page.tsx, src/components/audience/csv/csv-table.container.tsx
ฟอร์มอัปโหลด CSV (/audience/csv/form)
- ฟอร์มข้อมูลหลัก — ชื่อและคำอธิบาย
- พื้นที่ลากวางไฟล์ — รับเฉพาะไฟล์ CSV พร้อมส่วนคำแนะนำและปุ่มดาวน์โหลดไฟล์ต้นแบบ
- กล่องแสดงข้อผิดพลาดของไฟล์ — ตารางระบุแถวและสาเหตุ
- กล่องยืนยันก่อนบันทึก — สรุปจำนวนรหัสผู้ใช้ที่อ่านได้
ไฟล์อ้างอิงหลัก: src/components/audience/csv/csv-form.container.tsx, src/components/audience/csv/csv-form.tsx
ฟอร์ม Filter แบบแหล่งเดียว (/audience/filter/form)
- ส่วนหัวฟอร์ม — ชื่อ audience และตัวเลือกประเภทเงื่อนไข
- ส่วนตั้งค่าตามประเภท — คอมโพเนนต์แยกต่อประเภท (
section-campaign.tsx,section-rich-menu.tsx,section-activity-tag.tsx,section-auto-response.tsx,section-form-builder.tsx) - ตารางตัวอย่างรายชื่อ — พร้อมส่วนสรุปเงื่อนไขที่เลือก
ไฟล์อ้างอิงหลัก: src/components/audience/filter/filter-form.container.tsx, src/components/audience/filter/filter-table.tsx
ฟอร์ม Filter แบบหลายแหล่ง (/audience/filter/multi-source/form)
- ตัวสร้างเงื่อนไข (ซ้าย) — ชื่อ audience, ตัวเชื่อมระหว่างกลุ่ม, การ์ดกลุ่ม และเมนูเพิ่ม source
- การ์ด source — ฟอร์มย่อยที่เปลี่ยนตามชนิดของ source (
source-entry-card.tsx) - ไดอะแกรม Venn (ขวา) — ภาพประกอบเซตที่วาดด้วย SVG อัปเดตสดตามเงื่อนไข
- ตารางตัวอย่างผลลัพธ์ — สรุปจำนวนต่อ source, รายชื่อ 10 รายการแรก และกล่องดูคำตอบฟอร์ม
ไฟล์อ้างอิงหลัก: src/components/audience/filter/multi-source-filter-form.container.tsx, src/components/audience/filter/multi-source-filter-table.tsx, src/components/audience/filter/components/multi-source-visualizer.tsx
หน้ารายละเอียด (/audience/csv/detail)
- การ์ดกฎที่ใช้กรอง — สรุปเงื่อนไขเป็นข้อความ พร้อมปุ่มเปิดในตัวสร้าง filter
- การ์ด Trigger Rule — กฎที่อ้างถึง audience นี้
- ตารางสมาชิก — พร้อมตัวกรองและปุ่ม export 2 แบบ
ไฟล์อ้างอิงหลัก: src/components/audience/csv-detail-table/filter-detail-table.container.tsx
บริการฝั่ง API
src/services/audience.service.ts — จัดการตัว audience เอง
| ความสามารถ | Endpoint |
|---|---|
| ดึงรายการ audience | GET /audiences |
| ดึงข้อมูลรายตัว | GET /audiences/{id} |
| สร้าง / แก้ไข / ลบ | POST /audiences, PUT /audiences/{id}, DELETE /audiences/{id} |
| เปิด/ปิด auto refresh | PATCH /audiences/{id}/auto-refresh |
| สั่งคำนวณใหม่ทันที | POST /audiences/{id}/refresh |
| อ่านรอบ refresh ของ LINE OA | GET /line-oa/{lineOaId}/audience-refresh-settings |
| ดึงรายชื่อสมาชิก | GET /audiences/{id}/member |
| ส่งออกสมาชิกพร้อม attribute | GET /audiences/{id}/export-detail |
src/services/audience-filter.service.ts — สร้างและ preview เงื่อนไข filter
| ความสามารถ | Endpoint |
|---|---|
| ตัวเลือกแคมเปญ / Rich Menu / Auto Response / ฟอร์ม | GET /audiences-filter/list-dropdown-campaign, .../list-dropdown-richmenu, .../list-dropdown-auto-response, .../list-dropdown-form-builder |
| preview เงื่อนไขจากฟอร์ม | POST /audiences-filter/preview-form-filter |
| สร้าง filter แบบแหล่งเดียว | POST /audiences-filter/create-filter |
| สร้าง filter จากฟอร์ม | POST /audiences-filter/create-form-filter |
| preview filter หลายแหล่ง | POST /audiences-filter/preview-multi-source-filter |
| สร้าง filter หลายแหล่ง | POST /audiences-filter/create-multi-source-filter |
| ดูคำตอบฟอร์มของผู้ใช้ | GET /audiences-filter/form-response |
นอกจากนี้โมดูลยังเรียกใช้บริการอื่นเพื่อประกอบข้อมูล ได้แก่ การ preview รายชื่อของ filter แบบแหล่งเดียว (POST /tracking-line-users), รายการ attribute ที่กรองได้ (GET /attribute-master/filterable), ระดับสมาชิก Loyalty (GET /loyalty/tiers) และ Trigger Rule ที่ผูกกับ audience (GET /trigger-rule/by-audience/{audienceId})
จุดเชื่อมต่อกับฟีเจอร์อื่น
- สิทธิ์การใช้งาน — เมนู Audience และเมนูย่อยทั้งสามโหมดปลดล็อกพร้อมกันจากสิทธิ์โมดูล
audiencesฝั่ง backend โดยเมนูด้านข้างและ Quick Access ตรวจสิทธิ์นี้ก่อนแสดงผล - ฟีเจอร์ที่เป็นแหล่งข้อมูลเข้า (ใช้สร้าง audience)
- Campaign และ Rich Message — ให้ข้อมูล tracking spot ทั้งฝั่งการมองเห็นและการคลิก
- Rich Menu — ให้รายการเมนูและ spot แยกตามชนิดเมนู
- Auto Response — ให้รายการกฎตอบกลับที่ใช้เป็นเงื่อนไขได้
- Form Builder — ให้คำถามและคำตอบสำหรับสร้างเงื่อนไข รวมถึงลิงก์เข้าฟอร์ม filter จากหน้าผลตอบแบบฟอร์ม
- Attribute Setup — ให้รายการ custom attribute ที่เปิดให้กรองได้
- Loyalty — ให้ตัวชี้วัดและระดับสมาชิก รวมถึงส่งเงื่อนไขตั้งต้นมาจากหน้า Segments
- ฟีเจอร์ที่นำ audience ไปใช้ต่อ — Campaign Management, Trigger Rule, Workflow Automation, Rich Menu (เมนูเริ่มต้น), Content Management, Content Links, Menu Builder, หมวดหมู่ Bulletin และรายงานเพื่อนทั้งหมด
- Trigger Rule — หน้ารายละเอียด audience แสดงกฎที่อ้างถึง audience นี้ เพื่อให้เห็นผลกระทบก่อนแก้ไขหรือลบ
- โครงสร้างพื้นฐานร่วม — ใช้ระบบยืนยันตัวตนและ HTTP client กลางของ CMS (ออกจากระบบอัตโนมัติเมื่อ token หมดอายุ) ระบบ breadcrumb และเมนูด้านข้าง ชุด modal มาตรฐาน และข้อมูลโปรไฟล์ผู้ใช้สำหรับอ้างอิง LINE OA ปัจจุบัน
- ไฟล์ประกอบแบบ static — ไฟล์ต้นแบบ CSV ที่
/template/audiences_template.csvและรูปตัวอย่างในส่วนคำแนะนำการอัปโหลด
รายละเอียดฝั่ง Backend (CMS API)
ฝั่ง backend แยกเป็นสองโมดูลที่ทำงานร่วมกัน คือ internal/modules/audience/ (ตัว audience และรายชื่อสมาชิก) และ internal/modules/audiencefilter/ (เครื่องมือสร้าง audience จากเงื่อนไข)
จุดสำคัญที่สุด — สมาชิกไม่ได้เก็บเป็นแถวในฐานข้อมูล
รายชื่อสมาชิกของ audience ถูกเก็บเป็นไฟล์ CSV บน object storage (MinIO/S3) ไม่ใช่แถวในตาราง เพื่อรองรับสมาชิกจำนวนมหาศาลโดยไม่ทำให้ฐานข้อมูลบวม สิ่งที่ตามมาคือ
- การเปิดดูสมาชิกในหน้ารายละเอียด (
GET /api/audiences/:id/member) เป็นการ อ่านไฟล์ CSV กลับมาแบ่งหน้า ผ่านเครื่องมืออ่าน CSV ของระบบ ไม่ใช่การ query ฐานข้อมูล และผลลัพธ์ถูก cache ไว้ราว 30 วินาที การกดรีเฟรชถี่ ๆ จึงอาจเห็นตัวเลขเดิม - การ export (
GET /api/audiences/:id/exportและexport-detail) เป็นการ stream ข้อมูลตรงลง response ไม่ได้สร้างไฟล์รอไว้ให้ดาวน์โหลดทีหลัง - ตัวเลข "จำนวนสมาชิก" ที่เห็นในตารางมาจากข้อมูลสรุปที่บันทึกไว้ตอนคำนวณ ไม่ได้นับใหม่ทุกครั้งที่เปิดหน้า
สิทธิ์ที่ต้องมี
- route ของโมดูล audience ถูกครอบด้วย module gate ของโมดูล
audiencesทั้งหมด - policy แยกตามการกระทำ โดยมี
exportเป็นสิทธิ์แยกต่างหาก จากread— ผู้ที่ดูรายชื่อได้ไม่จำเป็นต้องส่งออกไฟล์ได้ - การเปิด/ปิด auto refresh และการสั่งคำนวณใหม่ทันที ใช้สิทธิ์
updateเพราะถือเป็นการแก้ไข audience - ข้อสังเกต — route ของโมดูล audience filter อยู่บนกลุ่มที่ต้องผ่าน JWT กลางเท่านั้น ไม่ได้ครอบด้วย module gate และ policy ที่ประกาศไว้ยังเป็นเพียงข้อมูลกำกับ นอกจากนี้ endpoint สร้าง audience จากเงื่อนไขแบบฟอร์มยังประกาศ policy เป็น
readทั้งที่ผลลัพธ์คือการสร้างข้อมูลใหม่ ต่างจาก endpoint สร้างแบบหลายแหล่งที่ใช้create— เป็นความไม่สอดคล้องที่ควรทราบ
Validation และ Business Rule ที่ backend ตรวจ
- ตรวจโควตาจำนวน audience ก่อนสร้างเสมอ (ค่า
maxSegmentsของแพ็กเกจ) ทั้งการสร้างจาก CSV และการสร้างจากเงื่อนไข การสร้างจึงถูกปฏิเสธได้แม้ข้อมูลถูกต้องครบ - ชื่อ audience ห้ามซ้ำ ถ้าซ้ำ backend ตอบกลับด้วยรหัสข้อผิดพลาดเฉพาะ (
AUD_001) - การเปิด auto refresh ต้องได้รับสิทธิ์จากแพ็กเกจด้วย ไม่ใช่แค่มีสิทธิ์แก้ไข ถ้าแพ็กเกจไม่รวมความสามารถนี้ สวิตช์จะเปิดไม่ได้
- endpoint สร้าง audience รับข้อมูลแบบ
multipart/form-dataเพราะต้องรับไฟล์ CSV มาพร้อมข้อมูลฟอร์ม - ระบบมีรหัสข้อผิดพลาดเฉพาะเพื่อให้หน้าจอแยกกรณีได้: ชื่อซ้ำ, รายการที่อ้างไม่ใช่ audience, ไฟล์ CSV ไม่ถูกต้อง, ส่งออกไม่ได้ และไม่พบผู้ใช้ LINE
สิ่งที่บันทึกและผลข้างเคียง (Side Effect)
- การสร้าง แก้ไข และลบ audience ไม่ได้ทำงานให้เสร็จในทันที แต่ publish งานลงคิว (
create_audience,update_audience,delete_audience) ให้line-management-worker-goเป็นผู้สร้างหรือปรับไฟล์สมาชิกจริง นี่คือเหตุผลที่สถานะของ audience มีค่า "กำลังประมวลผล" และรายการที่ยังไม่เสร็จจึงลบไม่ได้ - มีงานตามเวลาทำงานทุกนาที เพื่อหาว่ามี audience ใดครบกำหนด auto refresh แล้วส่งงานลงคิว
audience_refreshงานนี้ใช้ ล็อกบน Redis เพื่อกันไม่ให้ระบบที่รันหลายชุดพร้อมกัน (replica) ทำงานซ้อนกัน - การเข้า/ออกของสมาชิกถูกบันทึกเป็น audit log ในตาราง
audience_member_logและยัง publish เป็นเหตุการณ์ลงคิวaudience_membership_triggerให้ Trigger Rule และ Workflow นำไปสั่งงานต่อ การแก้สมาชิก audience จึงอาจกระตุ้นการส่งข้อความออกไปโดยอ้อม - ตารางที่เกี่ยวข้องหลัก ๆ ได้แก่
audience,audience_member_log,line_user,line_oaและorganization - ฝั่ง audience filter ผลลัพธ์ที่คำนวณได้จะถูกเขียนเป็นไฟล์ CSV ลง storage แล้วกลายเป็น audience ปกติที่โมดูล audience ดูแลต่อ (มี auto refresh ได้เหมือนกัน)
เส้นทางภายในระบบที่ไม่ผ่านการล็อกอิน
- โมดูล audience filter เปิด endpoint
POST /api/audiences-filter/internal/refreshไว้สำหรับ service อื่นเรียก ไม่ใช่ผู้ใช้ เช่นตอน worker หรือ webhook ต้องสั่งคำนวณสมาชิกของ audience ที่มาจากเงื่อนไขใหม่ - endpoint นี้ ไม่ได้ป้องกันด้วย JWT แต่ใช้กุญแจลับที่ส่งมาทาง header (
X-Internal-Key) เทียบกับค่าที่ตั้งไว้ในตัวแปรสภาพแวดล้อม - ข้อสังเกตด้านความปลอดภัย — เพราะเป็นการยืนยันตัวตนด้วยคีย์เดียวที่ใช้ร่วมกัน หากคีย์นี้รั่ว ผู้ที่ได้ไปจะสั่งคำนวณ audience ขององค์กรใดก็ได้โดยไม่ต้องล็อกอิน จึงไม่ควรเปิด endpoint นี้ออกสู่อินเทอร์เน็ตสาธารณะ
Edge Case และข้อสังเกตที่ควรรู้
- เพราะการสร้างและอัปเดตเป็นงานที่วิ่งผ่านคิว การกดบันทึกสำเร็จไม่ได้แปลว่ารายชื่อสมาชิกพร้อมใช้แล้ว โมดูลที่นำ audience ไปยิงข้อความต่อควรตรวจสถานะว่าคำนวณเสร็จก่อน
- ถ้า worker ไม่ทำงานหรือคิวติดขัด audience จะค้างอยู่ในสถานะกำลังประมวลผลตลอดไป โดยที่ฝั่ง CMS ไม่มีอาการผิดพลาดใด ๆ ให้เห็น
- งาน auto refresh ทำงานทุกนาทีสำหรับทั้งระบบ ไม่ใช่ต่อ audience — audience ที่ตั้งรอบถี่มากจำนวนมากจะแย่งทรัพยากรกันในรอบเดียวกัน
- โมดูล audience filter เรียกใช้ข้อมูลฟอร์มจาก Form Builder ผ่านหน้าตัดที่ประกาศไว้เฉพาะ เพื่อเลี่ยงการอ้างอิงวนกันระหว่างโมดูล การเพิ่มความสามารถที่ต้องใช้ข้อมูลฟอร์มเพิ่มจึงต้องขยายหน้าตัดนี้ด้วย