ฐานข้อมูลสมาชิก (Member Database)
ภาพรวม
Member Database (ฝั่ง backend ใช้ชื่อ customer-database) คือคลังข้อมูลสมาชิกที่ผู้ดูแลระบบอัปโหลดขึ้นมาเองในรูปแบบไฟล์ CSV เพื่อใช้เป็น "ตารางอ้างอิง" ให้ฟีเจอร์อื่นนำไปตรวจสอบสิทธิ์หรือยืนยันตัวตนของผู้ใช้
ผู้ใช้หลักคือผู้ดูแล CMS ที่มีรายชื่อสมาชิกเดิมอยู่ในระบบ CRM หรือไฟล์ Excel แล้วต้องการนำเข้ามาให้ระบบ LINE ใช้งานได้ ปลายทางหลักคือ Form Builder ซึ่งจะผูก (map) ฐานข้อมูลนี้เข้ากับฟอร์ม เพื่อตรวจสอบว่าผู้กรอกฟอร์มเป็นสมาชิกจริงหรือไม่
ข้อแตกต่างจาก Import Mapping — Member Database ไม่ได้เขียนทับข้อมูลโปรไฟล์ของผู้ใช้ LINE แต่เก็บ CSV ไว้เป็นชุดข้อมูลแยกต่างหาก แล้วให้ฟีเจอร์อื่นเรียกใช้เมื่อต้องการตรวจสอบ ส่วน Import Mapping เป็นการอัปเดตค่าลงในโปรไฟล์เพื่อนโดยตรง
ข้อมูล 1 รายการประกอบด้วย
| ข้อมูล | คำอธิบาย |
|---|---|
| ชื่อ | ชื่อฐานข้อมูลที่ใช้อ้างอิง บังคับกรอก ไม่เกิน 255 ตัวอักษร |
| คำอธิบาย | รายละเอียดเพิ่มเติม ไม่เกิน 1,000 ตัวอักษร |
| คอลัมน์ | รายชื่อคอลัมน์ที่ระบบอ่านได้จากไฟล์ CSV |
| จำนวนแถว | จำนวนข้อมูลสมาชิกในไฟล์ |
| ฟอร์มที่ผูกอยู่ | รายการฟอร์มที่กำลังใช้ฐานข้อมูลนี้ตรวจสอบสมาชิก |
| วันที่สร้าง | วันที่อัปโหลดฐานข้อมูลนี้ |
ข้อจำกัดที่ควรทราบ
- รองรับเฉพาะไฟล์
.csvและอัปโหลดได้ครั้งละ 1 ไฟล์ - ไม่มีหน้ารายละเอียดแยก ผู้ใช้ดูข้อมูลได้จากตารางและ popover รายชื่อคอลัมน์เท่านั้น
- ไม่มีฟังก์ชันส่งออกข้อมูลกลับเป็น CSV
- ตัวกรองมีเฉพาะช่องค้นหา ไม่มีการกรองตามสถานะหรือฟอร์มที่ผูก และไม่มีการเรียงลำดับคอลัมน์
- การตรวจสอบความถูกต้องของไฟล์ทำที่ฝั่ง backend ทั้งหมด ผลลัพธ์จะกลับมาแสดงในขั้นตอนพรีวิว
Business Flow
ดูรายการฐานข้อมูล
- เข้าหน้า
/member-databaseระบบตรวจสิทธิ์การเข้าถึงก่อน หากไม่ผ่านจะไม่แสดงเนื้อหา - ระบบตั้ง breadcrumb และไฮไลต์เมนูด้านข้าง แล้วโหลดรายการฐานข้อมูลตามหน้าและคำค้นปัจจุบัน
- ตารางแสดงชื่อพร้อมคำอธิบายเป็นบรรทัดรอง จำนวนคอลัมน์ จำนวนแถว ฟอร์มที่ผูกอยู่ วันที่สร้าง และปุ่มดำเนินการ
- คลิกหรือชี้ที่จำนวนคอลัมน์เพื่อเปิด popover แสดงชื่อคอลัมน์ทั้งหมดของไฟล์นั้น
- คอลัมน์ฟอร์มที่ผูกอยู่จะแสดงชื่อฟอร์มเป็นป้ายกำกับ หากยังไม่มีฟอร์มใดใช้งานจะแสดงป้าย "not mapped"
- ช่องค้นหาใช้ค้นตามชื่อ เมื่อกดค้นหาระบบจะกลับไปหน้าแรกเสมอ และมีปุ่มล้างค่าสำหรับรีเซ็ตเงื่อนไข
อัปโหลดฐานข้อมูลใหม่ (wizard 3 ขั้น)
- กดปุ่ม "Upload Database" ที่หัวข้อหน้าเพื่อเปิดหน้าต่างอัปโหลดแบบ 3 ขั้นตอน
- ขั้นที่ 1 — เลือกไฟล์ ลากไฟล์ CSV มาวางหรือกดเลือกไฟล์ ระบบจะไม่อัปโหลดทันที แต่ส่งไฟล์ไปให้ backend อ่านพรีวิวก่อน
- หากอ่านไฟล์สำเร็จ ระบบจะเก็บผลลัพธ์ (รายชื่อคอลัมน์ จำนวนแถว ตัวอย่างข้อมูล และข้อผิดพลาดที่พบ) แล้วพาไปขั้นถัดไป หากไม่สำเร็จจะแจ้งข้อความผิดพลาดที่ backend ส่งกลับมา
- ขั้นที่ 2 — ตรวจสอบข้อมูล แสดงแถบแจ้งเตือนหากพบข้อผิดพลาดในไฟล์ พร้อมบรรทัดสรุปจำนวนคอลัมน์และแถว รายชื่อคอลัมน์ในรูปแบบป้ายกำกับ และตารางตัวอย่างข้อมูลบางแถว
- หากไฟล์มีข้อผิดพลาด ปุ่มถัดไปจะถูกปิด ผู้ใช้ต้องย้อนกลับไปเลือกไฟล์ใหม่ การกดย้อนกลับจะล้างไฟล์และผลพรีวิวเดิมทิ้งทั้งหมด
- ขั้นที่ 3 — กรอกรายละเอียด กรอกชื่อฐานข้อมูล (บังคับ) และคำอธิบาย
- กด Create ระบบจะตรวจความถูกต้องของฟอร์ม แล้วส่งชื่อ คำอธิบาย และไฟล์ไปสร้างฐานข้อมูลใหม่ เมื่อสำเร็จจะปิดหน้าต่าง ล้างสถานะ และรีเฟรชตาราง
แก้ไขหรือแทนที่ไฟล์
- กดปุ่มแก้ไขในตารางเพื่อเปิดหน้าต่างแก้ไข ระบบจะเติมชื่อและคำอธิบายเดิมลงฟอร์มให้อัตโนมัติ
- หากฐานข้อมูลนี้มีฟอร์มผูกอยู่ ระบบจะแสดงแถบเตือนพร้อมจำนวนและชื่อฟอร์มทั้งหมด เพื่อให้ผู้ใช้รู้ผลกระทบก่อนแก้ไข
- หากต้องการเปลี่ยนข้อมูล ให้เลือกไฟล์ CSV ใหม่ ระบบจะพรีวิวไฟล์ใหม่ให้ตรวจสอบเช่นเดียวกับตอนอัปโหลด พร้อมปุ่มยกเลิกการแทนที่ไฟล์
- กด Save เพื่อบันทึก หากไม่ได้เลือกไฟล์ใหม่ ระบบจะแก้เฉพาะชื่อและคำอธิบายโดยไม่แตะข้อมูลเดิม
- ปุ่มบันทึกจะถูกปิดหากไฟล์ใหม่ที่เลือกมีข้อผิดพลาด
ลบฐานข้อมูล
- กดปุ่มลบในตารางเพื่อเปิดกล่องยืนยัน โดยหัวข้อจะระบุชื่อฐานข้อมูลที่กำลังจะลบ
- เมื่อยืนยัน ระบบจะส่งคำสั่งลบและแจ้งผลสำเร็จพร้อมรีเฟรชตาราง
- หาก backend ปฏิเสธคำขอ เช่น กรณีฐานข้อมูลยังถูกฟอร์มผูกอยู่ ระบบจะแสดงข้อความผิดพลาดที่ได้รับกลับมาแล้วปิดกล่องยืนยัน
การนำฐานข้อมูลไปใช้ใน Form Builder
- เมื่อเปิดหน้าตั้งค่าฟอร์ม ระบบจะโหลดรายการฐานข้อมูลสมาชิกมาเตรียมไว้ล่วงหน้า (สูงสุด 100 รายการ)
- ในส่วนตั้งค่า Profile Mapping ผู้ใช้เลือกฐานข้อมูลที่ต้องการ แล้วระบบจะแสดงรายชื่อคอลัมน์ของฐานข้อมูลนั้นให้จับคู่กับฟิลด์ในฟอร์ม พร้อมนับจำนวนคู่ที่จับแล้ว
- การจับคู่นี้เองคือที่มาของข้อมูล "ฟอร์มที่ผูกอยู่" ที่กลับมาแสดงในตารางหน้า Member Database และเป็นเหตุผลที่การแก้ไขหรือลบฐานข้อมูลจะมีคำเตือน
หน้าจอและองค์ประกอบหลัก
ฟีเจอร์นี้มีหน้าจอเดียวคือ /member-database การสร้างและแก้ไขทั้งหมดทำผ่านหน้าต่าง modal ไม่มีหน้าฟอร์มแยก
หน้ารายการ
- หัวข้อหน้าและปุ่มอัปโหลด — ใช้ส่วนหัวมาตรฐานของ CMS พร้อมปุ่ม Upload Database
- แถบค้นหา — ช่องค้นหาชื่อเพียงช่องเดียว พร้อมปุ่มค้นหาและล้างค่า
- ตารางรายการ — ชื่อพร้อมคำอธิบาย จำนวนคอลัมน์ (มี popover แสดงชื่อคอลัมน์) จำนวนแถว ป้ายฟอร์มที่ผูกอยู่ วันที่สร้าง และปุ่มแก้ไข/ลบ
ไฟล์อ้างอิงหลัก: src/app/member-database/page.tsx, src/components/member-database/member-database.container.tsx, src/components/member-database/member-database-table.tsx, src/components/member-database/member-database-filter.tsx
หน้าต่างอัปโหลด
- แถบขั้นตอน — แสดงความคืบหน้า 3 ขั้น ได้แก่ เลือกไฟล์ ตรวจสอบข้อมูล และกรอกรายละเอียด
- พื้นที่ลากวางไฟล์ — รับไฟล์
.csvทีละ 1 ไฟล์ พร้อมข้อความแนะนำขนาดไฟล์ - ส่วนแสดงผลพรีวิว — ใช้คอมโพเนนต์ร่วมกับหน้าต่างแก้ไข ประกอบด้วยแถบแจ้งข้อผิดพลาด บรรทัดสรุป ป้ายรายชื่อคอลัมน์ และตารางตัวอย่าง
ไฟล์อ้างอิงหลัก: src/components/member-database/upload-database-modal.tsx, src/components/member-database/csv-preview-detail.tsx
หน้าต่างแก้ไข
- ฟอร์มข้อมูลพื้นฐาน — ชื่อและคำอธิบาย เติมค่าเดิมให้อัตโนมัติทุกครั้งที่เปิด
- แถบเตือนฟอร์มที่ผูกอยู่ — แสดงเมื่อฐานข้อมูลถูกฟอร์มใช้งานอยู่ พร้อมรายชื่อฟอร์ม
- ส่วนแทนที่ไฟล์ — เลือกไฟล์ CSV ใหม่พร้อมพรีวิว และปุ่มยกเลิกการแทนที่
ไฟล์อ้างอิงหลัก: src/components/member-database/edit-database-modal.tsx
บริการฝั่ง API
รวมอยู่ที่ src/services/customer-database.service.ts ภายใต้ path หลัก customer-database โดยคำสั่งที่มีการแนบไฟล์จะส่งเป็น multipart/form-data
| ความสามารถ | Endpoint |
|---|---|
| ดึงรายการฐานข้อมูล | GET /customer-database |
| ดึงข้อมูลรายตัว | GET /customer-database/{id} |
| พรีวิวไฟล์ CSV ก่อนบันทึก | POST /customer-database/preview |
| สร้างฐานข้อมูลใหม่ | POST /customer-database |
| แก้ไขข้อมูลหรือแทนที่ไฟล์ | PUT /customer-database/{id} |
| ลบฐานข้อมูล | DELETE /customer-database/{id} |
จุดเชื่อมต่อกับฟีเจอร์อื่น
- สิทธิ์การใช้งาน — หน้าจอถูกควบคุมด้วยสิทธิ์ดูข้อมูลของโมดูล
customer-databaseซึ่งแมปกับเมนูmember-databaseเมนูด้านข้างจะแสดงเมื่อผู้ใช้มีสิทธิ์ และมีทางลัดใน Quick Access ด้วย - Form Builder — เป็นผู้บริโภคหลักของฟีเจอร์นี้ ส่วนตั้งค่า Profile Mapping ใช้ฐานข้อมูลสมาชิกเป็นแหล่งตรวจสอบสิทธิ์ผู้กรอกฟอร์ม และมีลิงก์กลับมายังหน้า Member Database เพื่อจัดการข้อมูล
- Import Mapping — เป็นฟีเจอร์คนละตัว ใช้สำหรับอัปเดตโปรไฟล์เพื่อนโดยตรง ไม่ได้ใช้ฐานข้อมูลชุดนี้
- คอมโพเนนต์กลางของ CMS — ใช้ส่วนหัวหน้า กล่องยืนยันมาตรฐาน และพื้นที่เลื่อนตารางร่วมกับหน้ารายการอื่นในระบบ
- โครงสร้างพื้นฐานร่วม — ใช้ระบบยืนยันตัวตนและ HTTP client กลางของ CMS (แนบ token อัตโนมัติและออกจากระบบเมื่อ token หมดอายุ) ระบบ breadcrumb และเมนูด้านข้าง
รายละเอียดฝั่ง Backend (CMS API)
โค้ดฝั่ง backend อยู่ที่ internal/modules/customerdatabase/ โดยมีส่วนที่แยกออกมาเป็นไฟล์เฉพาะทาง ได้แก่ ตัวอ่าน CSV และ ตัวรักษาลำดับคอลัมน์ เพื่อให้ลำดับคอลัมน์ในผลลัพธ์ตรงกับลำดับในไฟล์ต้นฉบับเสมอ
สิทธิ์ที่ต้องมี
- ทุก route ถูกครอบด้วย module gate ของโมดูล
customer-database - policy แยกตามการกระทำ:
readAllสำหรับดึงรายการ,readสำหรับดูรายตัว,createสำหรับสร้าง,updateและdeleteตามการกระทำ - ข้อสังเกต — endpoint พรีวิวไฟล์ CSV ใช้ policy
createไม่ใช่readเพราะถือเป็นขั้นตอนหนึ่งของการสร้าง ผู้ที่มีสิทธิ์เพียงดูข้อมูลจึงไม่สามารถลากไฟล์มาพรีวิวได้
ตัวอ่าน CSV และพฤติกรรมที่ต้องรู้
ตัวอ่าน CSV ของโมดูลนี้ถูกเขียนขึ้นให้เลียนแบบไลบรารีอ่าน CSV ฝั่ง JavaScript แบบเป๊ะทุกรายละเอียด เพื่อให้ผลลัพธ์ตรงกับระบบเดิม ซึ่งหมายความว่ามันสืบทอด "พฤติกรรมแปลก" ของไลบรารีนั้นมาด้วย
- ไฟล์ CSV ที่มีคอลัมน์เดียวจะถูกปฏิเสธ ด้วยข้อผิดพลาดว่าหาตัวคั่นคอลัมน์ไม่ได้ ทั้งที่ตัวไฟล์อาจถูกต้องสมบูรณ์ในสายตาผู้ใช้ ถ้าจำเป็นต้องนำเข้าข้อมูลคอลัมน์เดียว ให้เพิ่มคอลัมน์หลอกอีกหนึ่งคอลัมน์
- เมื่อพบข้อผิดพลาด รายการแถวข้อมูลที่คืนกลับมาจะกลายเป็นค่าว่างทั้งหมด ไม่ใช่คืนเฉพาะแถวที่อ่านได้ ดังนั้นหน้าจอพรีวิวที่แสดงข้อผิดพลาดจะไม่มีตัวอย่างข้อมูลให้ดูควบคู่กัน
- การตรวจไฟล์เกิดขึ้นที่ backend ทั้งหมด หน้าจอเป็นเพียงผู้แสดงผลที่ backend รายงานกลับมา
สิ่งที่บันทึกและผลข้างเคียง (Side Effect)
- ข้อมูลสรุปของฐานข้อมูล (ชื่อ คำอธิบาย รายชื่อคอลัมน์ จำนวนแถว) เก็บในตาราง
customer_databaseส่วนข้อมูลรายแถวเก็บแยกในตารางcustomer_database_row - การสร้างแทรกข้อมูลแบบเป็นชุด ครั้งละ 1,000 แถว ภายใน transaction เดียว ทั้งหมด ถ้าล้มเหลวกลางทางจะถูกยกเลิกทั้งก้อน ไม่ทิ้งข้อมูลค้างครึ่ง ๆ กลาง ๆ
- การแก้ไขที่แนบไฟล์ใหม่คือการแทนที่แถวข้อมูลทั้งหมด ไม่ใช่การผสานข้อมูล แถวเดิมทั้งชุดถูกลบแล้วเขียนใหม่ใน transaction เดียวกัน
- ไฟล์ CSV ต้นฉบับถูกเก็บไว้บน object storage ควบคู่กับข้อมูลในตาราง
- คอลัมน์ "ฟอร์มที่ผูกอยู่" ไม่ได้เก็บเป็นความสัมพันธ์ในฐานข้อมูล แต่ คำนวณสด ๆ ตอนดึงรายการ โดยค้นเข้าไปในค่าการตั้งค่า profile mapping ของตาราง
form_builderว่ามีฟอร์มใดอ้างถึงรหัสฐานข้อมูลนี้อยู่บ้าง
Edge Case และข้อสังเกตที่ควรรู้
- การลบเป็น soft delete แบบอิงสถานะ โดยเปลี่ยนค่าสถานะของแถวเป็น "ลบแล้ว" ไม่ใช่การประทับเวลาลบในคอลัมน์แยกแบบโมดูลอื่น การดึงรายการจึงต้องกรองสถานะนี้ออกเองทุกครั้ง
- เพราะความสัมพันธ์กับฟอร์มถูกคำนวณจากฝั่ง
form_builderไม่ใช่เก็บเป็นความสัมพันธ์จริง การเปลี่ยนแปลงที่ฝั่งฐานข้อมูลนี้จึงไม่ได้แจ้งกลับไปที่ฟอร์มโดยอัตโนมัติ ฟอร์มที่ผูกไว้อาจชี้ไปยังชุดข้อมูลที่เนื้อหาเปลี่ยนไปแล้วโดยไม่รู้ตัว - ทุก query ถูกจำกัดขอบเขตตาม LINE OA ที่เลือกอยู่ ฐานข้อมูลของ OA อื่นจะไม่ถูกดึงมาแม้จะระบุรหัสตรง ๆ
- ลำดับของคอลัมน์เป็นสิ่งที่โมดูลนี้ตั้งใจรักษาไว้ เพราะการจับคู่คอลัมน์ใน Form Builder อาศัยลำดับและชื่อคอลัมน์ที่ตรงกับไฟล์ต้นฉบับ
- โมดูลนี้ไม่ได้ส่งงานเข้าคิวและไม่มีการประมวลผลเบื้องหลัง การอัปโหลดไฟล์ใหญ่จึงใช้เวลาอยู่ในคำขอเดียวจนกว่าจะแทรกข้อมูลครบทุกชุด