Skip to main content

แอปสะสมแต้ม (Loyalty)

ภาพรวม

แอปสะสมแต้ม (Loyalty) เป็นหนึ่งใน "Apps" ซึ่งเป็นโมดูลเสริมที่ผู้ดูแลระดับแพลตฟอร์มเปิดหรือปิดให้แต่ละองค์กรได้ ต่างจากโมดูลอื่นของ CMS ที่ควบคุมด้วยระบบสิทธิ์ตามบทบาท

หน้าที่ของแอปคือให้ร้านค้าออกแบบ "บัตรสะสม" ให้ลูกค้าใช้ผ่าน LINE โดยเลือกได้ 2 โหมด

  • โหมดดวงตรา (stamp) — บัตรมีช่องจำนวนคงที่ (5, 10 หรือ 15 ช่อง) พนักงานสแกน QR ให้ลูกค้าได้หนึ่งดวงต่อการซื้อหนึ่งครั้ง โดยไม่บันทึกยอดเงิน
  • โหมดคะแนน (point) — ลูกค้าได้คะแนนตามยอดใช้จ่าย โดยกำหนดได้ว่าใช้จ่ายกี่บาทต่อหนึ่งคะแนน และเปิดใช้ระบบชั้นสมาชิกได้

ผู้เกี่ยวข้องมี 3 กลุ่ม คือ เจ้าของร้านหรือแอดมิน ที่ตั้งค่าทุกอย่างผ่าน CMS, พนักงานหน้าร้าน ที่ไม่ได้ใช้ CMS แต่ได้รับลิงก์เชิญแบบใช้ครั้งเดียวไปเปิดที่หน้าเว็บฝั่งลูกค้า และ ลูกค้า ที่เปิดบัตรผ่านลิงก์ LIFF

หน้าจอทั้งหมดจัดเป็น 8 แท็บ ได้แก่ บัตรสมาชิก, หน้าตาบัตร, ของรางวัล, ชั้นสมาชิก, กลุ่มเป้าหมายสำเร็จรูป, พนักงานและสาขา, บันทึกกิจกรรม และค้นหาลูกค้า

Business Flow

การเข้าถึงและการปรากฏของแอป

  1. เมนูด้านข้างเรียกทะเบียนแอปตอนโหลดหน้า แล้วเก็บเฉพาะแอปที่ถูกเปิดใช้งานไว้ หากพบแอปสะสมแต้มจะเพิ่มรายการเมนูย่อยเข้าไป และเมนูแม่ "Apps" จะปรากฏก็ต่อเมื่อมีแอปที่เปิดอยู่อย่างน้อยหนึ่งตัว
  2. การมองเห็นเมนูไม่ขึ้นกับบทบาทหรือสิทธิ์ของผู้ใช้เลย แต่ขึ้นกับทะเบียนแอปเพียงอย่างเดียว
  3. ทุกหน้าใต้กลุ่ม /apps ถูกห่อด้วยตัวป้องกันเส้นทางร่วมกัน ซึ่งอ่านรหัสแอปจาก URL แล้วตรวจกับทะเบียนแอป ถ้าแอปถูกปิดอยู่จะพากลับไปหน้าแรก และถ้าไม่พบในทะเบียนจะถือว่าไม่ใช่เส้นทางที่ต้องคุม
  4. หากเรียกทะเบียนแอปไม่สำเร็จ ระบบจะอนุญาตให้ผ่าน เพราะฝั่งเซิร์ฟเวอร์ยังคุมข้อมูลอยู่ และการที่ทะเบียนล่มชั่วคราวไม่ควรล็อกผู้ใช้ออกจากแอปที่ตนมีสิทธิ์จริง
  5. ตัวป้องกันนี้จำเป็นเพราะแม้ API จะปฏิเสธคำขอและเมนูจะซ่อนแอปที่ปิดไว้แล้ว แต่ไม่มีอะไรกันผู้ที่พิมพ์ URL เข้ามาตรง ซึ่งจะได้หน้าเปล่าที่ทุกคำขอล้มเหลว
  6. layout ของแอปสะสมแต้มรับผิดชอบการวาดแท็บทั้ง 8 ตัว การตั้ง breadcrumb และเมนูที่ active รวมถึงแสดงป้ายบอกโหมดของโปรแกรมข้างแท็บ ซึ่งจะรีเฟรชเมื่อเปลี่ยนหน้าและเมื่อหน้าบัตรสมาชิกแจ้งว่าบันทึกการตั้งค่าใหม่แล้ว

บัตรสมาชิก

  1. หน้านี้โหลดการตั้งค่าโปรแกรมมาเติมฟอร์ม พร้อมแสดงป้ายสถานะ (ฉบับร่าง / เปิดใช้งาน / หยุดชั่วคราว) และหมายเลขเวอร์ชัน
  2. เลือกโหมดด้วยตัวสลับแบบแบ่งส่วน โดยสลับได้อิสระเพราะยังไม่มีการเขียนลงฐานข้อมูล หากเปลี่ยนไปโหมดคะแนนแล้วรูปแบบวันหมดอายุเดิมใช้ไม่ได้ในโหมดใหม่ ระบบจะรีเซ็ตให้อัตโนมัติ
  3. ฟิลด์ที่แสดงเปลี่ยนตามโหมด โหมดดวงตราจะมีจำนวนช่องบัตร รูปแบบวันหมดอายุ 3 แบบ และเพดานแต้มต้อนรับที่ต่ำกว่า ส่วนโหมดคะแนนจะมีอัตราบาทต่อคะแนน รูปแบบวันหมดอายุ 2 แบบ และสวิตช์เปิดใช้ชั้นสมาชิกพร้อมกฎการลดชั้น
  4. ฟิลด์บางตัวปรากฏตามเงื่อนไข เช่น จำนวนเดือนที่หมดอายุจะแสดงเฉพาะเมื่อเลือกให้มีวันหมดอายุ และจำนวนชั่วโมงพักคอยจะแสดงเฉพาะเมื่อเลือกโหมดพักคอยแบบกำหนดชั่วโมง
  5. เมื่อบันทึกโดยที่เปลี่ยนโหมด ระบบจะเปิดกล่องยืนยันอธิบายผลกระทบ 3 ข้อ คือจะออกเวอร์ชันใหม่, ยอดสะสมเดิมจะหยุดนับแต่ไม่ถูกลบ และบันไดรางวัลจะถูกแทนที่ด้วยเงื่อนไขของโหมดใหม่
  6. ฝั่งเซิร์ฟเวอร์เป็นผู้ตัดสินเองว่าจะแก้ไขในที่เดิมหรือออกเวอร์ชันใหม่ ตามลักษณะของการเปลี่ยนแปลง หน้าเว็บขอแก้ในที่เดิมเองไม่ได้ กลไกนี้คือสิ่งที่ทำให้บัตรที่อยู่ในมือลูกค้าแล้วยังคงเงื่อนไขเดิมไว้
  7. การเผยแพร่หรือหยุดโปรแกรมทำผ่านปุ่มแยกต่างหาก โดยปุ่มเผยแพร่จะใช้งานไม่ได้จนกว่าจะมีโปรแกรมอยู่จริง
  8. คอลัมน์ขวาแสดงการ์ดลิงก์บัตรลูกค้าและการ์ดพรีวิวที่ชี้ไปยังหน้าออกแบบหน้าตาบัตร

ลิงก์บัตรลูกค้า

  1. ระบบอ่านรหัส LINE OA ปัจจุบันจากโปรไฟล์แล้วเรียกข้อมูล OA หนึ่งครั้งต่อการเข้าหน้า เพื่อนำ hash และ LIFF ID มาประกอบลิงก์ เพราะค่าทั้งสองไม่ได้เก็บอยู่ในโปรไฟล์ที่แคชไว้
  2. ลิงก์ที่สร้างได้มี 3 แบบ คือลิงก์บัตรหลักที่ชี้ไปหน้าเว็บฝั่งลูกค้า, ลิงก์แบบ LIFF สำหรับใช้ในช่องริชเมนู และลิงก์เชิญพนักงานที่พ่วง token
  3. หากไม่มี hash ของ OA ระบบจะคืนค่าว่างทุกลิงก์และแสดงคำเตือน โดยไม่มีการแสดง token เปล่าแทน เพราะ token ไม่ใช่ลิงก์ที่ใช้งานได้

หน้าตาบัตร

  1. หน้านี้โหลดข้อมูล 3 ชุดพร้อมกัน คือการตั้งค่าโปรแกรม, รายการของรางวัล และรายการชั้นสมาชิก โดยชั้นสมาชิกอาจไม่มีในโหมดดวงตรา
  2. รูปที่อัปโหลดได้ประกอบด้วยภาพปกแบบกว้าง, โลโก้แบบวงกลม และไอคอนดวงตรา (แสดงเฉพาะโหมดดวงตรา)
  3. สีที่ปรับได้มี 4 ค่า คือสีเน้น, สีตัวอักษร, สีปุ่ม และสีพื้นของดวงตรา (โหมดดวงตราเท่านั้น)
  4. การอัปโหลดรูปยืมบริการของโมดูลจัดการเนื้อหามาใช้ โดยรับเฉพาะไฟล์ JPEG, PNG และ GIF ส่วนไฟล์ SVG ถูกปฏิเสธที่ฝั่งเซิร์ฟเวอร์โดยเจตนาเพราะเป็นช่องทางโจมตีแบบ XSS
  5. คอลัมน์ขวาแสดงพรีวิวบัตรเต็มใบที่เลื่อนดูได้ รวมของรางวัลและเงื่อนไข และหากเปิดใช้ชั้นสมาชิกไว้จะมีตัวสลับให้ดูบัตรของแต่ละชั้นพร้อมคำนวณยอดใช้จ่ายที่ต้องถึงเพื่อเลื่อนชั้นถัดไป
  6. การแก้หน้าตาบัตรถือเป็นการเปลี่ยนแปลงเชิงรูปลักษณ์ ไม่เคยทำให้เกิดเวอร์ชันใหม่ จึงไม่มีกล่องยืนยันใดเมื่อบันทึก

ของรางวัล

  1. ตารางแสดงจำนวนหน่วยที่ต้องใช้, ชื่อพร้อมรูป และคอลัมน์จำกัดเฉพาะสมาชิกระดับหนึ่งขึ้นไป ซึ่งปรากฏเฉพาะในโหมดคะแนนที่มีชั้นสมาชิกอยู่จริง
  2. หัวคอลัมน์แรกเปลี่ยนความหมายตามโหมด โหมดคะแนนอ่านว่า "ราคา" ส่วนโหมดดวงตราอ่านว่า "ที่ช่องที่"
  3. เพดานของจำนวนหน่วยต่างกันตามโหมด โหมดคะแนนกำหนดได้ถึงหนึ่งแสน ส่วนโหมดดวงตราถูกจำกัดไม่ให้เกินจำนวนช่องของบัตร เพราะรางวัลที่อยู่เลยช่องสุดท้ายไม่มีทางไปถึงได้
  4. ตัวเลือกจำกัดชั้นสมาชิกจะปรากฏเฉพาะเมื่อเงื่อนไขพร้อมครบ และหากไม่พร้อมระบบจะส่งค่าว่างเสมอ เพื่อไม่ให้ค่าเดิมค้างอยู่โดยที่ผู้ใช้มองไม่เห็น
  5. ตารางนี้ไม่มีการแบ่งหน้า และการลบใช้การยืนยันแบบป๊อปอัปสั้น

ชั้นสมาชิก

  1. หน้านี้แสดงคำเตือน 2 แบบตามบริบท คือเตือนว่ากฎที่อิงยอดใช้จ่ายใช้ไม่ได้บนบัตรแบบดวงตรา (แต่กฎอื่นยังใช้ได้ จึงไม่ได้ปิดทั้งแท็บ) และเตือนว่าระบบชั้นสมาชิกยังไม่ถูกเปิดใช้งาน
  2. ตารางแสดงลำดับชั้น (โดยเลข 1 คือชั้นต่ำสุด), ชื่อพร้อมจุดสีประจำชั้น และคอลัมน์กฎที่แปลงเงื่อนไขเป็นประโยคอ่านง่าย พร้อมป้ายบอกว่าเงื่อนไขเชื่อมกันแบบ "และ" หรือ "หรือ"
  3. ชั้นที่ไม่มีกฎเลยจะแสดงข้อความสีแดง เพราะระบบประมวลผลตีความว่า "ไม่มีใครเข้าถึงได้" ไม่ใช่ "ทุกคนเข้าถึงได้"
  4. ปุ่มคำนวณใหม่สั่งประเมินชั้นของสมาชิกทั้งหมดทันที และรายงานจำนวนที่ประเมินกับจำนวนที่เปลี่ยนชั้น โดยปกติมีงานตามตารางเวลาทำอยู่แล้วตอนตี 3 ปุ่มนี้มีไว้เพื่อให้เห็นผลได้ทันที
  5. ฟอร์มชั้นสมาชิกประกอบด้วยชื่อ, ลำดับชั้น, สีประจำชั้นและสีตัวอักษร, รูปพื้นหลังบัตร, สวิตช์ตั้งเป็นชั้นเริ่มต้น และชุดกฎเงื่อนไข
  6. รูปพื้นหลังถูกครอปขอบสีเดียวออกอัตโนมัติก่อนอัปโหลด และหากมีการครอปจริงระบบจะแจ้งจำนวนพิกเซลที่ตัดออก
  7. เมื่อเปิดสวิตช์ชั้นเริ่มต้น ส่วนกฎจะถูกซ่อนพร้อมข้อความอธิบาย ส่วนกฎแต่ละข้อเลือกตัวชี้วัด (ยอดใช้จ่าย เฉพาะโหมดคะแนน / จำนวนคำสั่งซื้อ / คะแนน / จำนวนเดือนที่เป็นสมาชิก) ตัวดำเนินการ และค่าเปรียบเทียบ
  8. การลบชั้นสมาชิกเป็นการเก็บเข้าคลัง ไม่ใช่การลบจริง เพราะประวัติและบัญชีของลูกค้ายังอ้างถึงระเบียนนั้นอยู่

กลุ่มเป้าหมายสำเร็จรูป

  1. หน้านี้กรองชุดเงื่อนไขสำเร็จรูปตามโหมดของโปรแกรม โดยชุดที่ไม่ระบุโหมดจะใช้ได้ทั้งสองโหมด
  2. ชุดสำหรับโหมดดวงตราเน้นพฤติกรรมการสะสม เช่น สะสมครบแล้วแต่ยังไม่แลก, ใกล้ครบ, ลูกค้าประจำ และผู้เริ่มสะสมใหม่ ส่วนชุดสำหรับโหมดคะแนนเน้นมูลค่า เช่น ผู้ใช้จ่ายสูง, สมาชิกชั้นสูง, มีคะแนนพอแลกแล้ว และกลุ่มกำลังเติบโต โดยมีชุดที่ใช้ได้ทั้งสองโหมดคือกลุ่มที่กำลังห่างหาย
  3. การ์ดแต่ละใบแสดงชื่อ คำอธิบาย กรณีใช้งาน และเงื่อนไขที่แปลงเป็นคำอ่านง่าย
  4. หน้านี้ไม่ได้สร้างกลุ่มเป้าหมายเอง แต่แปลงเงื่อนไขเป็นพารามิเตอร์แล้วส่งต่อไปยังหน้าสร้างกลุ่มเป้าหมายแบบหลายแหล่งข้อมูลตามปกติ ซึ่งผู้ใช้ยังปรับแต่งต่อได้ทุกอย่าง

พนักงานและสาขา

  1. หน้านี้โหลดรายชื่อพนักงานและรายการสาขาพร้อมกัน และแจ้งเตือนเมื่อมีพนักงานรออนุมัติ
  2. การรับพนักงานเข้าระบบเป็นแบบเชิญเท่านั้น โดยกรอกชื่อและเลือกสาขา (มีตัวเลือก "ทุกสาขา" เป็นแถวชัดเจน ไม่ใช่การเว้นว่าง) แล้วระบบจะออก token ให้
  3. token ถูกส่งกลับมาเพียงครั้งเดียวและอ่านซ้ำไม่ได้ ระบบจึงประกอบเป็นลิงก์แล้วแสดงในหน้าต่างพร้อมปุ่มคัดลอกและคำเตือนให้คัดลอกทันที หากประกอบลิงก์ไม่ได้จะแจ้งข้อผิดพลาดโดยไม่แสดง token เปล่า
  4. ปุ่มเพิ่มพนักงานจะใช้งานไม่ได้ระหว่างที่ข้อมูลลิงก์ยังโหลดไม่เสร็จ เพื่อไม่ให้ออก token จริงโดยที่ไม่มีลิงก์ให้แสดง
  5. วงจรสถานะของพนักงานมี 4 ขั้น คือ ถูกเชิญ ไปเป็น รออนุมัติ (เมื่อคลิกลิงก์) ไปเป็น ใช้งานอยู่ (เมื่อแอดมินอนุมัติ) และ ถูกเพิกถอน โดยลิงก์ที่ถูกใช้แล้วจะพักที่สถานะรออนุมัติเสมอ เพราะลิงก์อาจถูกส่งต่อให้คนอื่น
  6. ตารางแสดงทั้งชื่อที่เจ้าของร้านตั้งและชื่อ LINE ของผู้ที่มาใช้ลิงก์จริง โดยชื่อที่ไม่ตรงกันคือสัญญาณว่าลิงก์อาจถูกส่งต่อ
  7. ปุ่มคำสั่งเปลี่ยนไปตามสถานะ และการเปลี่ยนสาขาทำได้ทันทีจากในตาราง ส่วนสาขาจัดการผ่านฟอร์มสั้นในหน้าเดียวกัน

บันทึกกิจกรรมและค้นหาลูกค้า

  1. บันทึกกิจกรรม ดึงรายการล่าสุด 200 รายการมาแสดงพร้อมกันแล้วแบ่งหน้าที่ฝั่งหน้าเว็บ คอลัมน์ประกอบด้วยเวลา, ประเภทกิจกรรม, จำนวนหน่วยที่เพิ่มหรือลด, ยอดใช้จ่าย (โหมดคะแนนเท่านั้น), ผู้ให้แต้ม, สาขา, เลขบัตร และข้อมูลลูกค้า โดยหน้านี้ไม่มีตัวกรองหรือการค้นหา
  2. ประเภทกิจกรรมถูกแปลงเป็นป้ายสีที่อ่านเข้าใจได้ เช่น การแลกรางวัล, การหมดอายุ, การสแกนของพนักงาน, แต้มต้อนรับ และการปรับด้วยมือจาก CMS
  3. คำเรียกหน่วยทั้งหมดใช้คำที่ร้านตั้งเอง ไม่ได้ใช้คำว่า "ดวงตรา" ตายตัว เพราะเดิมทำให้ผู้ใช้โหมดคะแนนเข้าใจผิดว่าเป็นฟีเจอร์ที่ตัวเองปิดไว้
  4. หน้าค้นหาลูกค้า มีช่องค้นหาเดียวที่รับได้ทั้งเบอร์โทร ชื่อ และอีเมล หากพบผลลัพธ์เดียวจะเปิดรายละเอียดทันทีโดยไม่ต้องคลิก
  5. รายละเอียดลูกค้าแสดงข้อมูลโปรไฟล์จากฐานข้อมูลผู้ใช้ LINE (ซึ่งมีอยู่ก่อนที่บัญชีสะสมแต้มจะเกิด), ตัวเลขสรุปที่เปลี่ยนตามโหมด และรายการธุรกรรมย้อนหลัง หากลูกค้ายังไม่มีบัญชีสะสมแต้มจะแสดงสถานะว่างแทน
  6. หน้านี้เป็นแบบอ่านอย่างเดียว ไม่มีปุ่มปรับหรือเพิ่มแต้มด้วยมือจาก 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