แอปสะสมแต้ม (Loyalty)
ภาพรวม
แอปสะสมแต้ม (Loyalty) เป็นหนึ่งใน "Apps" ซึ่งเป็นโมดูลเสริมที่ผู้ดูแลระดับแพลตฟอร์มเปิดหรือปิดให้แต่ละองค์กรได้ ต่างจากโมดูลอื่นของ CMS ที่ควบคุมด้วยระบบสิทธิ์ตามบทบาท
หน้าที่ของแอปคือให้ร้านค้าออกแบบ "บัตรสะสม" ให้ลูกค้าใช้ผ่าน LINE โดยเลือกได้ 2 โหมด
- โหมดดวงตรา (stamp) — บัตรมีช่องจำนวนคงที่ (5, 10 หรือ 15 ช่อง) พนักงานสแกน QR ให้ลูกค้าได้หนึ่งดวงต่อการซื้อหนึ่งครั้ง โดยไม่บันทึกยอดเงิน
- โหมดคะแนน (point) — ลูกค้าได้คะแนนตามยอดใช้จ่าย โดยกำหนดได้ว่าใช้จ่ายกี่บาทต่อหนึ่งคะแนน และเปิดใช้ระบบชั้นสมาชิกได้
ผู้เกี่ยวข้องมี 3 กลุ่ม คือ เจ้าของร้านหรือแอดมิน ที่ตั้งค่าทุกอย่างผ่าน CMS, พนักงานหน้าร้าน ที่ไม่ได้ใช้ CMS แต่ได้รับลิงก์เชิญแบบใช้ครั้งเดียวไปเปิดที่หน้าเว็บฝั่งลูกค้า และ ลูกค้า ที่เปิดบัตรผ่านลิงก์ LIFF
หน้าจอทั้งหมดจัดเป็น 8 แท็บ ได้แก่ บัตรสมาชิก, หน้าตาบัตร, ของรางวัล, ชั้นสมาชิก, กลุ่มเป้าหมายสำเร็จรูป, พนักงานและสาขา, บันทึกกิจกรรม และค้นหาลูกค้า
Business Flow
การเข้าถึงและการปรากฏของแอป
- เมนูด้านข้างเรียกทะเบียนแอปตอนโหลดหน้า แล้วเก็บเฉพาะแอปที่ถูกเปิดใช้งานไว้ หากพบแอปสะสมแต้มจะเพิ่มรายการเมนูย่อยเข้าไป และเมนูแม่ "Apps" จะปรากฏก็ต่อเมื่อมีแอปที่เปิดอยู่อย่างน้อยหนึ่งตัว
- การมองเห็นเมนูไม่ขึ้นกับบทบาทหรือสิทธิ์ของผู้ใช้เลย แต่ขึ้นกับทะเบียนแอปเพียงอย่างเดียว
- ทุกหน้าใต้กลุ่ม
/appsถูกห่อด้วยตัวป้องกันเส้นทางร่วมกัน ซึ่งอ่านรหัสแอปจาก URL แล้วตรวจกับทะเบียนแอป ถ้าแอปถูกปิดอยู่จะพากลับไปหน้าแรก และถ้าไม่พบในทะเบียนจะถือว่าไม่ใช่เส้นทางที่ต้องคุม - หากเรียกทะเบียนแอปไม่สำเร็จ ระบบจะอนุญาตให้ผ่าน เพราะฝั่งเซิร์ฟเวอร์ยังคุมข้อมูลอยู่ และการที่ทะเบียนล่มชั่วคราวไม่ควรล็อกผู้ใช้ออกจากแอปที่ตนมีสิทธิ์จริง
- ตัวป้องกันนี้จำเป็นเพราะแม้ API จะปฏิเสธคำขอและเมนูจะซ่อนแอปที่ปิดไว้แล้ว แต่ไม่มีอะไรกันผู้ที่พิมพ์ URL เข้ามาตรง ซึ่งจะได้หน้าเปล่าที่ทุกคำขอล้มเหลว
- layout ของแอปสะสมแต้มรับผิดชอบการวาดแท็บทั้ง 8 ตัว การตั้ง breadcrumb และเมนูที่ active รวมถึงแสดงป้ายบอกโหมดของโปรแกรมข้างแท็บ ซึ่งจะรีเฟรชเมื่อเปลี่ยนหน้าและเมื่อหน้าบัตรสมาชิกแจ้งว่าบันทึกการตั้งค่าใหม่แล้ว
บัตรสมาชิก
- หน้านี้โหลดการตั้งค่าโปรแกรมมาเติมฟอร์ม พร้อมแสดงป้ายสถานะ (ฉบับร่าง / เปิดใช้งาน / หยุดชั่วคราว) และหมายเลขเวอร์ชัน
- เลือกโหมดด้วยตัวสลับแบบแบ่งส่วน โดยสลับได้อิสระเพราะยังไม่มีการเขียนลงฐานข้อมูล หากเปลี่ยนไปโหมดคะแนนแล้วรูปแบบวันหมดอายุเดิมใช้ไม่ได้ในโหมดใหม่ ระบบจะรีเซ็ตให้อัตโนมัติ
- ฟิลด์ที่แสดงเปลี่ยนตามโหมด โหมดดวงตราจะมีจำนวนช่องบัตร รูปแบบวันหมดอายุ 3 แบบ และเพดานแต้มต้อนรับที่ต่ำกว่า ส่วนโหมดคะแนนจะมีอัตราบาทต่อคะแนน รูปแบบวันหมดอายุ 2 แบบ และสวิตช์เปิดใช้ชั้นสมาชิกพร้อมกฎการลดชั้น
- ฟิลด์บางตัวปรากฏตามเงื่อนไข เช่น จำนวนเดือนที่หมดอายุจะแสดงเฉพาะเมื่อเลือกให้มีวันหมดอายุ และจำนวนชั่วโมงพักคอยจะแสดงเฉพาะเมื่อเลือกโหมดพักคอยแบบกำหนดชั่วโมง
- เมื่อบันทึกโดยที่เปลี่ยนโหมด ระบบจะเปิดกล่องยืนยันอธิบายผลกระทบ 3 ข้อ คือจะออกเวอร์ชันใหม่, ยอดสะสมเดิมจะหยุดนับแต่ไม่ถูกลบ และบันไดรางวัลจะถูกแทนที่ด้วยเงื่อนไขของโหมดใหม่
- ฝั่งเซิร์ฟเวอร์เป็นผู้ตัดสินเองว่าจะแก้ไขในที่เดิมหรือออกเวอร์ชันใหม่ ตามลักษณะของการเปลี่ยนแปลง หน้าเว็บขอแก้ในที่เดิมเองไม่ได้ กลไกนี้คือสิ่งที่ทำให้บัตรที่อยู่ในมือลูกค้าแล้วยังคงเงื่อนไขเดิมไว้
- การเผยแพร่หรือหยุดโปรแกรมทำผ่านปุ่มแยกต่างหาก โดยปุ่มเผยแพร่จะใช้งานไม่ได้จนกว่าจะมีโปรแกรมอยู่จริง
- คอลัมน์ขวาแสดงการ์ดลิงก์บัตรลูกค้าและการ์ดพรีวิวที่ชี้ไปยังหน้าออกแบบหน้าตาบัตร
ลิงก์บัตรลูกค้า
- ระบบอ่านรหัส LINE OA ปัจจุบันจากโปรไฟล์แล้วเรียกข้อมูล OA หนึ่งครั้งต่อการเข้าหน้า เพื่อนำ hash และ LIFF ID มาประกอบลิงก์ เพราะค่าทั้งสองไม่ได้เก็บอยู่ในโปรไฟล์ที่แคชไว้
- ลิงก์ที่สร้างได้มี 3 แบบ คือลิงก์บัตรหลักที่ชี้ไปหน้าเว็บฝั่งลูกค้า, ลิงก์แบบ LIFF สำหรับใช้ในช่องริชเมนู และลิงก์เชิญพนักงานที่พ่วง token
- หากไม่มี hash ของ OA ระบบจะคืนค่าว่างทุกลิงก์และแสดงคำเตือน โดยไม่มีการแสดง token เปล่าแทน เพราะ token ไม่ใช่ลิงก์ที่ใช้งานได้
หน้าตาบัตร
- หน้านี้โหลดข้อมูล 3 ชุดพร้อมกัน คือการตั้งค่าโปรแกรม, รายการของรางวัล และรายการชั้นสมาชิก โดยชั้นสมาชิกอาจไม่มีในโหมดดวงตรา
- รูปที่อัปโหลดได้ประกอบด้วยภาพปกแบบกว้าง, โลโก้แบบวงกลม และไอคอนดวงตรา (แสดงเฉพาะโหมดดวงตรา)
- สีที่ปรับได้มี 4 ค่า คือสีเน้น, สีตัวอักษร, สีปุ่ม และสีพื้นของดวงตรา (โหมดดวงตราเท่านั้น)
- การอัปโหลดรูปยืมบริการของโมดูลจัดการเนื้อหามาใช้ โดยรับเฉพาะไฟล์ JPEG, PNG และ GIF ส่วนไฟล์ SVG ถูกปฏิเสธที่ฝั่งเซิร์ฟเวอร์โดยเจตนาเพราะเป็นช่องทางโจมตีแบบ XSS
- คอลัมน์ขวาแสดงพรีวิวบัตรเต็มใบที่เลื่อนดูได้ รวมของรางวัลและเงื่อนไข และหากเปิดใช้ชั้นสมาชิกไว้จะมีตัวสลับให้ดูบัตรของแต่ละชั้นพร้อมคำนวณยอดใช้จ่ายที่ต้องถึงเพื่อเลื่อนชั้นถัดไป
- การแก้หน้าตาบัตรถือเป็นการเปลี่ยนแปลงเชิงรูปลักษณ์ ไม่เคยทำให้เกิดเวอร์ชันใหม่ จึงไม่มีกล่องยืนยันใดเมื่อบันทึก
ของรางวัล
- ตารางแสดงจำนวนหน่วยที่ต้องใช้, ชื่อพร้อมรูป และคอลัมน์จำกัดเฉพาะสมาชิกระดับหนึ่งขึ้นไป ซึ่งปรากฏเฉพาะในโหมดคะแนนที่มีชั้นสมาชิกอยู่จริง
- หัวคอลัมน์แรกเปลี่ยนความหมายตามโหมด โหมดคะแนนอ่านว่า "ราคา" ส่วนโหมดดวงตราอ่านว่า "ที่ช่องที่"
- เพดานของจำนวนหน่วยต่างกันตามโหมด โหมดคะแนนกำหนดได้ถึงหนึ่งแสน ส่วนโหมดดวงตราถูกจำกัดไม่ให้เกินจำนวนช่องของบัตร เพราะรางวัลที่อยู่เลยช่องสุดท้ายไม่มีทางไปถึงได้
- ตัวเลือกจำกัดชั้นสมาชิกจะปรากฏเฉพาะเมื่อเงื่อนไขพร้อมครบ และหากไม่พร้อมระบบจะส่งค่าว่างเสมอ เพื่อไม่ให้ค่าเดิมค้างอยู่โดยที่ผู้ใช้มองไม่เห็น
- ตารางนี้ไม่มีการแบ่งหน้า และการลบใช้การยืนยันแบบป๊อปอัปสั้น
ชั้นสมาชิก
- หน้านี้แสดงคำเตือน 2 แบบตามบริบท คือเตือนว่ากฎที่อิงยอดใช้จ่ายใช้ไม่ได้บนบัตรแบบดวงตรา (แต่กฎอื่นยังใช้ได้ จึงไม่ได้ปิดทั้งแท็บ) และเตือนว่าระบบชั้นสมาชิกยังไม่ถูกเปิดใช้งาน
- ตารางแสดงลำดับชั้น (โดยเลข 1 คือชั้นต่ำสุด), ชื่อพร้อมจุดสีประจำชั้น และคอลัมน์กฎที่แปลงเงื่อนไขเป็นประโยคอ่านง่าย พร้อมป้ายบอกว่าเงื่อนไขเชื่อมกันแบบ "และ" หรือ "หรือ"
- ชั้นที่ไม่มีกฎเลยจะแสดงข้อความสีแดง เพราะระบบประมวลผลตีความว่า "ไม่มีใครเข้าถึงได้" ไม่ใช่ "ทุกคนเข้าถึงได้"
- ปุ่มคำนวณใหม่สั่งประเมินชั้นของสมาชิกทั้งหมดทันที และรายงานจำนวนที่ประเมินกับจำนวนที่เปลี่ยนชั้น โดยปกติมีงานตามตารางเวลาทำอยู่แล้วตอนตี 3 ปุ่มนี้มีไว้เพื่อให้เห็นผลได้ทันที
- ฟอร์มชั้นสมาชิกประกอบด้วยชื่อ, ลำดับชั้น, สีประจำชั้นและสีตัวอักษร, รูปพื้นหลังบัตร, สวิตช์ตั้งเป็นชั้นเริ่มต้น และชุดกฎเงื่อนไข
- รูปพื้นหลังถูกครอปขอบสีเดียวออกอัตโนมัติก่อนอัปโหลด และหากมีการครอปจริงระบบจะแจ้งจำนวนพิกเซลที่ตัดออก
- เมื่อเปิดสวิตช์ชั้นเริ่มต้น ส่วนกฎจะถูกซ่อนพร้อมข้อความอธิบาย ส่วนกฎแต่ละข้อเลือกตัวชี้วัด (ยอดใช้จ่าย เฉพาะโหมดคะแนน / จำนวนคำสั่งซื้อ / คะแนน / จำนวนเดือนที่เป็นสมาชิก) ตัวดำเนินการ และค่าเปรียบเทียบ
- การลบชั้นสมาชิกเป็นการเก็บเข้าคลัง ไม่ใช่การลบจริง เพราะประวัติและบัญชีของลูกค้ายังอ้างถึงระเบียนนั้นอยู่
กลุ่มเป้าหมายสำเร็จรูป
- หน้านี้กรองชุดเงื่อนไขสำเร็จรูปตามโหมดของโปรแกรม โดยชุดที่ไม่ระบุโหมดจะใช้ได้ทั้งสองโหมด
- ชุดสำหรับโหมดดวงตราเน้นพฤติกรรมการสะสม เช่น สะสมครบแล้วแต่ยังไม่แลก, ใกล้ครบ, ลูกค้าประจำ และผู้เริ่มสะสมใหม่ ส่วนชุดสำหรับโหมดคะแนนเน้นมูลค่า เช่น ผู้ใช้จ่ายสูง, สมาชิกชั้นสูง, มีคะแนนพอแลกแล้ว และกลุ่มกำลังเติบโต โดยมีชุดที่ใช้ได้ทั้งสองโหมดคือกลุ่มที่กำลังห่างหาย
- การ์ดแต่ละใบแสดงชื่อ คำอธิบาย กรณีใช้งาน และเงื่อนไขที่แปลงเป็นคำอ่านง่าย
- หน้านี้ไม่ได้สร้างกลุ่มเป้าหมายเอง แต่แปลงเงื่อนไขเป็นพารามิเตอร์แล้วส่งต่อไปยังหน้าสร้างกลุ่มเป้าหมายแบบหลายแหล่งข้อมูลตามปกติ ซึ่งผู้ใช้ยังปรับแต่งต่อได้ทุกอย่าง
พนักงานและสาขา
- หน้านี้โหลดรายชื่อพนักงานและรายการสาขาพร้อมกัน และแจ้งเตือนเมื่อมีพนักงานรออนุมัติ
- การรับพนักงานเข้าระบบเป็นแบบเชิญเท่านั้น โดยกรอกชื่อและเลือกสาขา (มีตัวเลือก "ทุกสาขา" เป็นแถวชัดเจน ไม่ใช่การเว้นว่าง) แล้วระบบจะออก token ให้
- token ถูกส่งกลับมาเพียงครั้งเดียวและอ่านซ้ำไม่ได้ ระบบจึงประกอบเป็นลิงก์แล้วแสดงในหน้าต่างพร้อมปุ่มคัดลอกและคำเตือนให้คัดลอกทันที หากประกอบลิงก์ไม่ได้จะแจ้งข้อผิดพลาดโดยไม่แสดง token เปล่า
- ปุ่มเพิ่มพนักงานจะใช้งานไม่ได้ระหว่างที่ข้อมูลลิงก์ยังโหลดไม่เสร็จ เพื่อไม่ให้ออก token จริงโดยที่ไม่มีลิงก์ให้แสดง
- วงจรสถานะของพนักงานมี 4 ขั้น คือ ถูกเชิญ ไปเป็น รออนุมัติ (เมื่อคลิกลิงก์) ไปเป็น ใช้งานอยู่ (เมื่อแอดมินอนุมัติ) และ ถูกเพิกถอน โดยลิงก์ที่ถูกใช้แล้วจะพักที่สถานะรออนุมัติเสมอ เพราะลิงก์อาจถูกส่งต่อให้คนอื่น
- ตารางแสดงทั้งชื่อที่เจ้าของร้านตั้งและชื่อ LINE ของผู้ที่มาใช้ลิงก์จริง โดยชื่อที่ไม่ตรงกันคือสัญญาณว่าลิงก์อาจถูกส่งต่อ
- ปุ่มคำสั่งเปลี่ยนไปตามสถานะ และการเปลี่ยนสาขาทำได้ทันทีจากในตาราง ส่วนสาขาจัดการผ่านฟอร์มสั้นในหน้าเดียวกัน
บันทึกกิจกรรมและค้นหาลูกค้า
- บันทึกกิจกรรม ดึงรายการล่าสุด 200 รายการมาแสดงพร้อมกันแล้วแบ่งหน้าที่ฝั่งหน้าเว็บ คอลัมน์ประกอบด้วยเวลา, ประเภทกิจกรรม, จำนวนหน่วยที่เพิ่มหรือลด, ยอดใช้จ่าย (โหมดคะแนนเท่านั้น), ผู้ให้แต้ม, สาขา, เลขบัตร และข้อมูลลูกค้า โดยหน้านี้ไม่มีตัวกรองหรือการค้นหา
- ประเภทกิจกรรมถูกแปลงเป็นป้ายสีที่อ่านเข้าใจได้ เช่น การแลกรางวัล, การหมดอายุ, การสแกนของพนักงาน, แต้มต้อนรับ และการปรับด้วยมือจาก CMS
- คำเรียกหน่วยทั้งหมดใช้คำที่ร้านตั้งเอง ไม่ได้ใช้คำว่า "ดวงตรา" ตายตัว เพราะเดิมทำให้ผู้ใช้โหมดคะแนนเข้าใจผิดว่าเป็นฟีเจอร์ที่ตัวเองปิดไว้
- หน้าค้นหาลูกค้า มีช่องค้นหาเดียวที่รับได้ทั้งเบอร์โทร ชื่อ และอีเมล หากพบผลลัพธ์เดียวจะเปิดรายละเอียดทันทีโดยไม่ต้องคลิก
- รายละเอียดลูกค้าแสดงข้อมูลโปรไฟล์จากฐานข้อมูลผู้ใช้ LINE (ซึ่งมีอยู่ก่อนที่บัญชีสะสมแต้มจะเกิด), ตัวเลขสรุปที่เปลี่ยนตามโหมด และรายการธุรกรรมย้อนหลัง หากลูกค้ายังไม่มีบัญชีสะสมแต้มจะแสดงสถานะว่างแทน
- หน้านี้เป็นแบบอ่านอย่างเดียว ไม่มีปุ่มปรับหรือเพิ่มแต้มด้วยมือจาก CMS แม้บันทึกกิจกรรมจะมีประเภทการปรับด้วยมืออยู่
หน้าจอและองค์ประกอบหลัก
ตัวป้องกันเส้นทางร่วม (src/app/apps/layout.tsx) — ตรวจว่าแอปในเส้นทางนั้นถูกเปิดใช้งานหรือไม่ พร้อมพฤติกรรมอนุญาตให้ผ่านเมื่อเกิดข้อผิดพลาด
Layout ของแอป (src/app/apps/loyalty/layout.tsx) — แท็บทั้ง 8 ตัว, breadcrumb, เมนูที่ active และป้ายบอกโหมดของโปรแกรม
หน้าตามแท็บ (src/app/apps/loyalty/*/page.tsx) — บัตรสมาชิก, หน้าตาบัตร, ของรางวัล, ชั้นสมาชิก, กลุ่มเป้าหมายสำเร็จรูป, พนักงาน, บันทึกกิจกรรม และค้นหาลูกค้า โดยฟอร์มของรางวัลและชั้นสมาชิกแยกเป็นไฟล์หน้าต่างของตัวเอง
ไลบรารีภายในของแอป — ตัวแปลงกฎเป็นประโยค (tiers/lib/describe-rule.ts), ตัวครอปขอบรูป (tiers/lib/trim-border.ts), นิยามชุดเงื่อนไขสำเร็จรูป (segments/lib/presets.ts) และตัวประกอบลิงก์บัตร (lib/use-loyalty-links.ts)
คอมโพเนนต์พรีวิว — การ์ดลิงก์บัตร, พรีวิวบัตรแบบย่อ และพรีวิวบัตรเต็มใบ ซึ่งใช้ทั้งในหน้าออกแบบและหน้าอื่น
เซอร์วิส — src/services/loyalty.service.ts ครอบคลุมทุกคำสั่งของแอป ตั้งแต่โปรแกรม, ของรางวัล, ชั้นสมาชิก, สาขา, พนักงาน, บันทึกกิจกรรม ไปจนถึงการค้นหาลูกค้า ส่วน src/services/apps.service.ts ใช้อ่านทะเบียนแอปอย่างเดียว เพราะการเปิดปิดแอปเป็นหน้าที่ของผู้ดูแลระดับแพลตฟอร์ม
จุดเชื่อมต่อกับฟีเจอร์อื่น
- ทะเบียนแอปและผู้ดูแลระดับแพลตฟอร์ม — เป็นตัวกำหนดว่าแอปนี้จะปรากฏหรือไม่ โดย CMS อ่านได้อย่างเดียว การเปิดปิดทำที่คอนโซลของผู้ดูแลแพลตฟอร์มเท่านั้น
- สิทธิ์การเข้าถึง — มีการประกาศ subject ของแอปไว้ในระบบ แต่ไม่มีการแม็ปจากฝั่งหลังบ้าน จึงถูกใช้เป็นเพียงคีย์ของเมนูและ breadcrumb การควบคุมการเข้าถึงจริงอยู่ที่ทะเบียนแอปและตัวป้องกันฝั่ง API
- เว็บฝั่งลูกค้า — เป็นที่ตั้งของหน้าบัตรลูกค้าและหน้าพนักงาน ซึ่งเริ่มต้น LIFF เอง
- LINE OA Management — เป็นแหล่งของ hash และ LIFF ID ที่ใช้ประกอบลิงก์ทั้งหมด
- กลุ่มเป้าหมาย (Audience) — หน้ากลุ่มเป้าหมายสำเร็จรูปส่งเงื่อนไขต่อไปยังหน้าสร้างกลุ่มเป้าหมายแบบหลายแหล่งข้อมูล
- จัดการเนื้อหา (Content Management) — แอปยืมบริการอัปโหลดรูปของโมดูลนี้มาใช้ เพราะไม่มี endpoint อัปโหลดของตัวเอง
- งานตามตารางเวลา — มีงานประเมินชั้นสมาชิกทำงานอัตโนมัติทุกวันตอนตี 3 โดยปุ่มคำนวณใหม่ในหน้าจอเป็นการสั่งงานเดียวกันแบบทันที
- ฐานข้อมูลผู้ใช้ LINE — ข้อมูลโปรไฟล์ในหน้าค้นหาลูกค้าและชื่อกับรูปในบันทึกกิจกรรมดึงมาจากตารางนี้แบบสด
- สถาปัตยกรรมภายใน — ต่างจากโมดูลอื่นของ CMS ตรงที่หน้าทั้งหมดของแอปนี้เรียกบริการตรงและจัดการสถานะเอง ไม่ได้ใช้ชั้นจัดการคำขอกลาง
รายละเอียดฝั่ง Backend (CMS API)
โมดูลนี้อยู่ที่ internal/modules/loyalty/ ทุก endpoint อยู่ใต้ /api/apps/loyalty/ และต้องผ่านกลไกของระบบแอปเสริมก่อนเสมอ
ระบบแอปเสริมควบคุมการเข้าถึงอย่างไร
- คำขอทุกเส้นที่ path มีรูป
/apps/ตามด้วยชื่อแอป จะถูกAppEnabledGuardดักไว้ก่อน โดยดึงรหัสแอปจาก path แล้วตรวจว่าองค์กรนี้เปิดแอปนั้นไว้หรือไม่ ถ้าไม่ได้เปิดจะถูกปฏิเสธด้วยสถานะ 403 พร้อมข้อความว่าองค์กรยังไม่ได้เปิดใช้แอปนี้ - ถ้าอ่านรหัสองค์กรจาก context ไม่ได้ ระบบจะถือว่าปิด ไม่ใช่ถือว่าเปิด จึงเป็นการปฏิเสธไว้ก่อนเมื่อข้อมูลไม่ครบ
- การเปิด/ปิดแอปเป็นอำนาจของผู้ดูแลระดับแพลตฟอร์มเท่านั้น
PUT /api/apps/:appIdถูกครอบด้วยการตรวจสิทธิ์ระดับแพลตฟอร์ม ผู้เรียกที่เป็นผู้ใช้ฝั่งลูกค้า หรือระบุตัวตนไม่ได้ จะได้ 403 การตรวจนี้ ปฏิเสธไว้ก่อนเมื่อไม่แน่ใจ ต่างจากตัวตรวจโมดูลทั่วไปของระบบที่ปล่อยผ่านเมื่ออ่านข้อมูลไม่ได้ เหตุผลคือความเสี่ยงตรงนี้คือการยกระดับสิทธิ์ตัวเอง ถ้าองค์กรเปิดแอปให้ตัวเองได้ การกำกับดูแลก็ไม่มีความหมาย GET /api/appsที่หน้าเว็บเรียกหลังล็อกอินเพื่อ render เมนู ใช้การตรวจ token ระดับ "ล็อกอินแล้ว" ไม่ใช่ตัวตรวจเต็มรูปแบบ เพราะ ณ จังหวะนั้น token ยังไม่ผูกกับ LINE OA ถ้าใช้ตัวตรวจเข้มจะได้ 401 แล้วหน้าเว็บจะเด้งออกจากระบบทันที- ข้อสังเกต: policy metadata ของ
GET /api/appsและPUT /api/apps/:appIdประกาศไว้เป็นโมดูล friend-track ซึ่งยกมาตามระบบเดิม ไม่ใช่ความผิดพลาดในการแปลงโค้ด แต่เป็นความผูกพันที่ชื่อไม่สื่อและควรระวังเมื่อจะปรับสิทธิ์ - ภายในโมดูล loyalty เอง policy ที่ประกาศไว้ใช้ของโมดูล
line-oaแยกตามการกระทำ (read/readAll/create/update/delete) แต่ตัวที่บังคับใช้จริงในทางปฏิบัติคือAppEnabledGuard
กฎธุรกิจที่ backend ตรวจจริง (พร้อมรหัสข้อผิดพลาด)
โมดูลนี้ใช้ รหัสข้อผิดพลาดชุดเดียวกับฝั่ง client-api เพื่อให้ทั้งหลังบ้านและแอปฝั่งลูกค้าสื่อความหมายตรงกัน รหัสที่ควรรู้:
LOYALTY_PROGRAM_MISSING— เรียกอ่านโปรแกรมทั้งที่ยังไม่เคยตั้งค่า ไม่ได้คืนค่าว่าง แต่คืนเป็นข้อผิดพลาด หน้าจอจึงต้องแยกแยะระหว่าง "ยังไม่ตั้งค่า" กับ "ผิดพลาดจริง"LOYALTY_CARD_SIZE_INVALID— ขนาดบัตรที่ส่งมาไม่ถูกต้อง ตรวจตอนบันทึกโปรแกรมLOYALTY_TIER_POINT_MODE_ONLY— ระดับสมาชิกใช้ได้เฉพาะโหมดแต้มเท่านั้น ถ้าโปรแกรมตั้งเป็นโหมดแสตมป์แล้วพยายามสร้างหรือแก้ระดับสมาชิก จะถูกปฏิเสธที่ฝั่งเซิร์ฟเวอร์ ไม่ใช่แค่ซ่อนเมนูLOYALTY_TIER_INVALID— ตัวเลขของระดับสมาชิกไม่สมเหตุสมผลLOYALTY_TIER_RANK_TAKEN— อันดับของระดับสมาชิกชนกับที่มีอยู่แล้ว อันดับต้องไม่ซ้ำ
การคำนวณระดับสมาชิกใหม่
POST /api/apps/loyalty/tiers/recalculate ประเมินระดับสมาชิกใหม่ ทันที นอกเหนือจากงานอัตโนมัติที่ทำงานทุกคืนตอนตี 3 ที่ฝั่ง worker เหตุผลที่มีปุ่มนี้คือร้านที่เพิ่งตั้งบันไดระดับสมาชิกเสร็จไม่ควรต้องรอถึงเช้าวันรุ่งขึ้นเพื่อดูว่าตั้งถูกหรือไม่ ทั้งสองทางเรียกงานเดียวกัน
สิ่งที่ CMS ทำไม่ได้
การให้แต้มและการแลกรางวัลจริง ไม่ได้เกิดที่ CMS API แต่เกิดที่ client-api ผ่านหน้า LIFF ของพนักงานและลูกค้า หน้าหลังบ้านนี้ทำหน้าที่ตั้งค่าโปรแกรม บันไดรางวัล สาขา และรายชื่อพนักงานที่มีสิทธิ์ แล้วอ่านประวัติกลับมาดูเท่านั้น การเชิญพนักงานเป็นระบบคำเชิญ โดยการลบรายการพนักงานมีความหมายว่าเพิกถอนคำเชิญ
ตารางที่เกี่ยวข้อง
กลุ่มตารางของ loyalty (โปรแกรม, เป้าหมายสะสม, ระดับสมาชิก, สาขา, พนักงาน, ประวัติการทำรายการ, ข้อมูลสมาชิก) ร่วมกับ line_oa_app (แถวที่บอกว่าองค์กรเปิดแอปใดไว้), line_oa และ line_user