แอปนัดหมาย (Appointment)
ภาพรวม
แอปนัดหมาย (Appointment) เป็นหนึ่งใน "Apps" ซึ่งเป็นโมดูลเสริมที่ผู้ดูแลระดับแพลตฟอร์มเปิดหรือปิดให้แต่ละองค์กรได้ ไม่ได้ควบคุมด้วยระบบสิทธิ์ตามบทบาทเหมือนโมดูลอื่น
หน้าที่ของแอปคือให้ธุรกิจตั้งระบบ จองนัดหมายผ่าน LINE โดยแบ่งเป็น 3 ส่วนหลัก
- สาขา — ที่ตั้ง เวลาทำการรายวันทั้ง 7 วัน และเขตเวลา โดยแต่ละสาขามีรายการ บริการ และ พนักงาน ของตัวเอง
- เส้นทางการจอง — ตัวสร้างขั้นตอนการจองแบบลากวาง ไล่ตั้งแต่เลือกบริการ เลือกพนักงาน เลือกวันเวลา กรอกข้อมูลผู้จอง ไปจนถึงหน้ายืนยัน พร้อม token สำหรับให้ลูกค้าเข้าใช้งาน
- การจอง — รายการนัดหมายที่เข้ามา ดูได้ทั้งแบบตารางและแบบปฏิทินรายสัปดาห์ พร้อมปุ่มเปลี่ยนสถานะตามวงจรที่กำหนด
ผู้ใช้ฝั่ง CMS คือแอดมินที่ตั้งค่าสาขา บริการ พนักงาน ออกแบบเส้นทางการจอง และจัดการสถานะการนัดหมาย ส่วนลูกค้าเข้าใช้งานผ่าน token ของเส้นทางการจองที่เว็บฝั่งลูกค้า โดยข้อมูลผู้จองสามารถเติมจากชื่อที่แสดงบน LINE ให้อัตโนมัติได้
เมื่อเทียบกับแอปสะสมแต้มและแอปกระดานข่าวซึ่งเป็นโมดูลที่โตกว่าในกลุ่มเดียวกัน แอปนี้ยังอยู่ในระยะที่บางกว่าอย่างชัดเจน เซอร์วิสของแอปยังไม่มีการประกาศชนิดข้อมูลแยกเป็นไฟล์ ไม่มีตัวแปลข้อผิดพลาดกลาง และหน้าจอทุกหน้าอ่านผลลัพธ์แบบยืดหยุ่นสองทางเพราะรูปแบบที่ API ส่งกลับยังไม่คงที่
Business Flow
การเข้าถึงและการปรากฏของแอป
- เมนูด้านข้างเรียกทะเบียนแอปตอนโหลดหน้า เก็บเฉพาะแอปที่เปิดใช้งาน หากพบแอปนัดหมายจะเพิ่มรายการเมนูย่อยที่ชี้ตรงไปยังแท็บสาขา และเมนูแม่ "Apps" จะปรากฏก็ต่อเมื่อมีแอปที่เปิดอยู่อย่างน้อยหนึ่งตัว
- การมองเห็นเมนูไม่ขึ้นกับบทบาทหรือสิทธิ์ของผู้ใช้เลย แต่ขึ้นกับทะเบียนแอปเพียงอย่างเดียว และแอปนี้ไม่มีป้ายตัวเลขบนเมนูเหมือนแอปกระดานข่าว
- ทุกหน้าใต้กลุ่ม
/appsใช้ตัวป้องกันเส้นทางร่วมกัน ซึ่งอ่านรหัสแอปจาก URL ตรวจกับทะเบียน แล้วพากลับหน้าแรกหากแอปถูกปิด และ อนุญาตให้ผ่านหากเรียกทะเบียนไม่สำเร็จ เพราะฝั่งเซิร์ฟเวอร์ยังคุมข้อมูลอยู่ และการที่ทะเบียนล่มชั่วคราวไม่ควรล็อกผู้ใช้ออกจากแอปที่ตนมีสิทธิ์จริง - โครงสร้างที่ขับเคลื่อนด้วยทะเบียนนี้ทำให้แอปใหม่ที่เพิ่มเข้ามาถูกควบคุมโดยอัตโนมัติ
- layout ของแอปเป็นเพียงตัววาดแท็บ 3 ตัว (สาขา / เส้นทางการจอง / การจอง) โดย breadcrumb และเมนูที่ active แต่ละหน้าตั้งเอง
สาขา
- หน้าสาขาโหลดรายการสาขาทั้งหมดแล้วแสดงเป็นตารางที่แบ่งหน้าฝั่งหน้าเว็บ คอลัมน์ประกอบด้วยลำดับ, ชื่อสาขา (คลิกเพื่อเข้าหน้ารายละเอียด), ที่อยู่, เบอร์โทร, สถานะ และปุ่มคำสั่ง
- หน้าต่างสร้างและแก้ไขสาขาประกอบด้วยชื่อ (บังคับ), ที่อยู่ (บังคับ), เบอร์โทร, เขตเวลา (บังคับ เลือกจากรายการที่กำหนดไว้ 6 รายการ) และสถานะ
- เวลาทำการทั้ง 7 วันจัดการเป็นสถานะแยกจากฟอร์มหลัก แต่ละวันมีสวิตช์เปิดปิดและช่องเลือกเวลาเริ่มกับเวลาสิ้นสุด โดยช่องเวลาจะใช้งานไม่ได้เมื่อสวิตช์ปิด
- ค่าเริ่มต้นคือเปิดทำการวันจันทร์ถึงศุกร์ ปิดวันเสาร์และอาทิตย์ โดยทุกวันตั้งเวลา 09:00 ถึง 18:00 น. และหากผู้ใช้ล้างค่าเวลาออก ระบบจะเติมค่าเริ่มต้นกลับให้แทนที่จะปล่อยเป็นค่าว่าง
- เมื่อบันทึก ระบบจะรวมข้อมูลฟอร์มกับเวลาทำการเข้าด้วยกันแล้วส่ง โดยข้อผิดพลาดจากการตรวจฟอร์มจะไม่แสดงข้อความแจ้งเตือนซ้ำ เพราะฟอร์มแสดงข้อความใต้ฟิลด์อยู่แล้ว
- การลบสาขาต้องยืนยันก่อน
บริการและพนักงานของสาขา
- หน้ารายละเอียดสาขาอ่านรหัสสาขาจาก URL แล้วโหลดข้อมูลสาขาเพื่อนำชื่อไปใส่ในส่วนหัวและ breadcrumb พร้อมโหลดรายการบริการและรายการพนักงานของสาขานั้น
- หน้านี้แบ่งเป็น 2 แท็บภายใน โดยปุ่มมุมขวาบนจะเปลี่ยนตามแท็บที่เปิดอยู่ระหว่างเพิ่มบริการกับเพิ่มพนักงาน
- แท็บบริการ แสดงลำดับ, ชื่อบริการ, ระยะเวลา, ราคา, จำนวนที่รับได้ต่อช่วงเวลา, สถานะว่าต้องระบุพนักงานหรือไม่ และสถานะ โดยหน้าต่างเพิ่มและแก้ไขกำหนดชื่อ (บังคับ), ระยะเวลาเป็นนาที (บังคับ), จำนวนที่รับได้ต่อช่วงเวลา, ราคา, ตัวเลือกว่าต้องระบุพนักงานหรือไม่ และสถานะ
- แท็บพนักงาน แสดงลำดับ, ชื่อ, ตำแหน่ง และรายการบริการที่รับผิดชอบซึ่งแสดงเป็นป้ายหลายอัน โดยหน้าต่างเพิ่มและแก้ไขกำหนดชื่อ (บังคับ), ตำแหน่ง และรายการบริการที่เลือกได้หลายรายการจากบริการของสาขานั้น
- ข้อสังเกตเชิงโครงสร้างคือคำสั่งสร้างต้องระบุสาขากำกับ แต่คำสั่งแก้ไขไม่ต้อง เพราะใช้ endpoint คนละชุดกัน
เส้นทางการจอง
- หน้ารายการโหลดเส้นทางการจองพร้อมรายการสาขา (ใช้ทั้งเป็นตัวเลือกในฟอร์มและใช้เติมชื่อสาขาในตาราง)
- คอลัมน์ประกอบด้วยลำดับ, ชื่อ, สาขา, สถานะการยืนยันอัตโนมัติ, token, สถานะ และปุ่มคำสั่ง
- token แสดงเป็นข้อความแบบย่อพร้อมปุ่มคัดลอก และคัดลอกได้เฉพาะ token ดิบเท่านั้น แอปนี้ไม่ได้ประกอบ URL ที่ลูกค้ากดได้ให้ ต่างจากแอปกระดานข่าวและแอปสะสมแต้มที่มีตัวประกอบลิงก์ของตัวเอง การแปลง token ให้เป็นลิงก์ที่ใช้งานได้จึงต้องทำที่อื่น
- หน้าต่างสร้างและแก้ไขกำหนดได้เพียง 3 ฟิลด์ คือชื่อ (บังคับ), สาขา (บังคับ) และการยืนยันอัตโนมัติ ส่วนขั้นตอนของเส้นทางแก้ที่หน้าแยกต่างหาก
- ปุ่มคำสั่งต่อแถวมี 3 ปุ่ม คือเข้าหน้าแก้ไขขั้นตอน, เปิดหน้าต่างแก้ข้อมูลพื้นฐาน และลบ
การแก้ขั้นตอนของเส้นทางการจอง
- หน้านี้อ่านรหัสเส้นทางจาก URL แล้วโหลดข้อมูล โดยดึงชื่อ, จำนวนชั่วโมงที่จะส่งการแจ้งเตือนล่วงหน้า และระยะเวลาผ่อนผันก่อนนับว่าไม่มาตามนัด
- หากเส้นทางนั้นยังไม่มีขั้นตอนใด ระบบจะสร้างชุดขั้นตอนเริ่มต้นให้ ทั้งในกรณีที่เป็นเส้นทางใหม่และกรณีที่โหลดข้อมูลไม่สำเร็จ
- หากเส้นทางผูกกับสาขาไว้ ระบบจะโหลดรายการบริการและพนักงานของสาขานั้นพร้อมกัน เพื่อนำไปแสดงเป็นข้อมูลอ้างอิงในการ์ดของแต่ละขั้นตอน
- ขั้นตอนมี 5 ประเภทตายตัว ได้แก่ เลือกบริการ, เลือกพนักงาน, เลือกวันเวลา, กรอกข้อมูลผู้จอง และยืนยัน โดยแต่ละประเภทมีสีประจำตัวเพื่อให้แยกออกจากกันได้ในสายตา
- ชุดขั้นตอนเริ่มต้นถูกสร้างจากข้อความที่แปลแล้ว เพื่อให้ทั้งหัวการ์ดและค่าตั้งต้นที่ระบบสร้างขึ้นพูดภาษาเดียวกับภาษาที่ผู้ใช้กำลังใช้อยู่ โดยขั้นตอนเลือกพนักงานถูกปิดไว้เป็นค่าเริ่มต้น และขั้นตอนกรอกข้อมูลผู้จองมาพร้อมฟิลด์ตั้งต้น 3 ช่อง คือชื่อ (เติมจาก LINE อัตโนมัติ), เบอร์โทร และหมายเหตุ
- การเรียงลำดับขั้นตอนใช้การลากวาง และรองรับการเรียงด้วยคีย์บอร์ดด้วย โดย ขั้นตอนยืนยันถูกแยกออกจากรายการที่ลากได้เสมอ และวาดต่อท้ายในฐานะขั้นตอนสุดท้ายที่ตายตัว
- แต่ละประเภทมีได้เพียงหนึ่งขั้นตอนเท่านั้น เนื่องจากระบบใช้ประเภทของขั้นตอนเป็นตัวระบุตัวตนในการลากวาง จึงไม่สามารถเพิ่มขั้นตอนประเภทเดียวกันซ้ำได้
- การ์ดของแต่ละขั้นตอนมีที่จับสำหรับลาก, เลขลำดับในวงกลมสีประจำขั้นตอน, ชื่อขั้นตอนพร้อมหัวข้อย่อย, สวิตช์เปิดปิด (ซ่อนสำหรับขั้นตอนยืนยัน) และปุ่มพับหรือกางเพื่อดูการตั้งค่า โดยกางพร้อมกันได้หลายการ์ด
- ฟอร์มการตั้งค่าแยกตามประเภทของขั้นตอน
- เลือกบริการ — หัวข้อ, หัวข้อย่อย, รูปแบบการแสดงผล (การ์ดหรือรายการ), แสดงราคาหรือไม่, แสดงระยะเวลาหรือไม่ พร้อมแสดงรายการบริการจริงของสาขาให้เห็น หรือขึ้นคำเตือนหากยังไม่ได้ตั้งบริการไว้
- เลือกพนักงาน — หัวข้อ, หัวข้อย่อย, แสดงรูปหรือไม่, แสดงตำแหน่งหรือไม่, อนุญาตให้เลือกพนักงานคนใดก็ได้หรือไม่ พร้อมแสดงรายชื่อพนักงานจริงหรือคำเตือนในทำนองเดียวกัน
- เลือกวันเวลา — หัวข้อ, หัวข้อย่อย, จำนวนวันล่วงหน้าที่จองได้ และการแสดงช่วงเวลาที่เต็มแล้วหรือไม่
- กรอกข้อมูลผู้จอง — หัวข้อ, หัวข้อย่อย และตัวจัดการฟิลด์แบบเพิ่มลบได้ โดยแต่ละฟิลด์กำหนดคีย์, ป้ายกำกับ, ชนิดของช่องกรอก, การเติมค่าอัตโนมัติจาก LINE และสถานะบังคับกรอก
- ยืนยัน — หัวข้อ, ข้อความบนปุ่มยืนยัน และการแสดงสรุปรายการหรือไม่
- เหนือรายการขั้นตอนมีฟิลด์ระดับเส้นทาง 2 ตัว คือจำนวนชั่วโมงที่จะแจ้งเตือนล่วงหน้าและระยะเวลาผ่อนผันก่อนนับว่าไม่มาตามนัด
- การบันทึกส่งเฉพาะขั้นตอนและฟิลด์ระดับเส้นทาง 2 ตัวนี้เท่านั้น ไม่ได้ส่งชื่อ สาขา หรือการยืนยันอัตโนมัติ เพราะสามค่านั้นแก้จากหน้าต่างในหน้ารายการ
การจอง
- หน้านี้โหลดรายการสาขาครั้งเดียว และโหลดรายการจองใหม่ทุกครั้งที่ตัวกรองเปลี่ยน โดยส่งเฉพาะพารามิเตอร์ที่มีค่าจริง
- มุมมองมี 2 แบบ คือรายการและปฏิทิน โดยค่าเริ่มต้นคือปฏิทิน
- แถบตัวกรองประกอบด้วยชุดช่วงเวลาสำเร็จรูป (ทั้งหมด / เมื่อวาน / วันนี้ / พรุ่งนี้ / สัปดาห์นี้ / เดือนนี้), ตัวเลือกช่วงวันที่แบบกำหนดเอง, ตัวเลือกสาขา, ตัวเลือกสถานะ และปุ่มล้างตัวกรอง โดยเมื่อเปลี่ยนวันเริ่มต้นของช่วง ปฏิทินจะเลื่อนไปยังสัปดาห์ของวันนั้นให้อัตโนมัติ
- การแบ่งหน้าของตารางทำที่ฝั่งหน้าเว็บ และจะกลับไปหน้าแรกทุกครั้งที่ตัวกรองเปลี่ยน มิฉะนั้นเมื่อกรองให้แคบลงผู้ใช้จะเจอหน้าท้าย ๆ ที่ว่างเปล่า
- สถานะการจองมี 5 ค่า คือรอยืนยัน, ยืนยันแล้ว, เสร็จสิ้น, ยกเลิก และไม่มาตามนัด
- ปุ่มคำสั่งปรากฏตามสถานะปัจจุบัน โดยรายการที่รอยืนยันมีปุ่มยืนยันกับยกเลิก, รายการที่ยืนยันแล้วมีปุ่มปิดงาน ปุ่มไม่มาตามนัด และปุ่มยกเลิก ส่วนสถานะปลายทางทั้งสามไม่มีปุ่มใดเลย
- การยกเลิกต้องผ่านหน้าต่างที่ขอเหตุผล ต่างจากปุ่มอื่นที่ทำงานทันที แม้ว่าเหตุผลจะไม่ได้บังคับกรอกก็ตาม
- คลิกที่ชื่อลูกค้าในตารางหรือคลิกที่บล็อกในปฏิทินจะเปิดหน้าต่างรายละเอียด 2 คอลัมน์ ฝั่งซ้ายแสดงข้อมูลผู้จอง เลขที่การจอง สถานะ ช่องทางติดต่อ LINE user ID และ ข้อมูลที่ลูกค้ากรอกในขั้นตอนกรอกข้อมูลจริง ส่วนฝั่งขวาแสดงบริการ พนักงาน สาขา วันที่ ช่วงเวลา หมายเหตุ และวันที่สร้าง
ปฏิทินรายสัปดาห์
- ปฏิทินรับรายการจองและวันเริ่มต้นของสัปดาห์เข้ามา ส่วนแถบควบคุมการเลื่อนสัปดาห์อยู่ที่หน้าหลักไม่ใช่ในตัวปฏิทิน
- แกนเวลาถูกกำหนดตายตัวไว้ที่ 07:00 ถึง 18:00 น. โดยไม่ได้อ่านจากเวลาทำการของสาขา การจองที่อยู่นอกช่วงนี้จึงถูกวางนอกกริด
- ความสูงของบล็อกคำนวณตามระยะเวลาจริงของการจอง โดยมีความสูงขั้นต่ำเพื่อให้ยังอ่านได้
- บล็อกแต่ละอันมีชุดสีตามสถานะ และ แสดงข้อมูลมากหรือน้อยตามความสูงของบล็อก โดยบรรทัดช่วงเวลากับชื่อบริการแสดงเสมอ ส่วนชื่อลูกค้า ชื่อพนักงาน และสาขาจะทยอยปรากฏเมื่อบล็อกสูงพอ
- วันปัจจุบันถูกไฮไลต์ด้วยสีเขียวและมีเส้นบอกเวลาปัจจุบันวาดอยู่ในคอลัมน์นั้น ส่วนวันหยุดสุดสัปดาห์ใช้พื้นสีเทาอ่อน
หน้าจอและองค์ประกอบหลัก
ตัวป้องกันเส้นทางร่วม (src/app/apps/layout.tsx) — ตรวจว่าแอปในเส้นทางนั้นถูกเปิดใช้งานหรือไม่ พร้อมพฤติกรรมอนุญาตให้ผ่านเมื่อเกิดข้อผิดพลาด
Layout ของแอป (src/app/apps/appointment/layout.tsx) — วาดแท็บ 3 ตัวเท่านั้น ไม่ได้ตั้ง breadcrumb ให้
หน้าสาขา (src/app/apps/appointment/locations/page.tsx) — ตาราง, หน้าต่างสาขา และตัวตั้งเวลาทำการรายวัน
หน้ารายละเอียดสาขา (.../locations/detail/page.tsx) — 2 แท็บภายในสำหรับบริการและพนักงาน พร้อมหน้าต่างจัดการของทั้งสองอย่าง โดยอ่านรหัสสาขาจาก query string
หน้าเส้นทางการจอง (.../journeys/page.tsx) — ตาราง, หน้าต่างข้อมูลพื้นฐาน, การคัดลอก token และทางเข้าสู่หน้าแก้ขั้นตอน
หน้าแก้ขั้นตอน (.../journeys/edit/page.tsx) — ตัวสร้างเส้นทางแบบลากวางพร้อมฟอร์มการตั้งค่าแยกตามประเภทขั้นตอน
หน้าการจอง (.../bookings/page.tsx และ .../bookings/WeeklyCalendar.tsx) — ตัวกรอง, การสลับมุมมอง, ปุ่มเปลี่ยนสถานะตามวงจร, หน้าต่างยกเลิกและหน้าต่างรายละเอียด รวมถึงตัวปฏิทินรายสัปดาห์
เซอร์วิส (src/services/appointment.service.ts) — ครอบคลุมทุกคำสั่งของแอป ทั้งสาขา บริการ พนักงาน เส้นทางการจอง และการจอง โดยยังเป็นไฟล์ขนาดเล็กที่ยังไม่ได้ประกาศชนิดข้อมูลอย่างละเอียด
จุดเชื่อมต่อกับฟีเจอร์อื่น
- ทะเบียนแอปและผู้ดูแลระดับแพลตฟอร์ม — เป็นตัวกำหนดว่าแอปนี้จะปรากฏหรือไม่ CMS อ่านได้อย่างเดียว
- สิทธิ์การเข้าถึง — มีการประกาศ subject ไว้แต่ไม่มีการแม็ปจากฝั่งหลังบ้าน จึงถูกใช้เป็นเพียงคีย์ของเมนูและ breadcrumb การควบคุมจริงอยู่ที่ทะเบียนแอปและตัวป้องกันฝั่ง API
- เว็บฝั่งลูกค้าและ LIFF — เป็นที่ที่ลูกค้าเข้าจองผ่าน token ของเส้นทาง โดยการตั้งค่าของแต่ละขั้นตอนคือสิ่งที่ฝั่งนั้นนำไปแสดงผล
- โปรไฟล์ LINE — ใช้เติมชื่อผู้จองอัตโนมัติในขั้นตอนกรอกข้อมูล และหน้าต่างรายละเอียดแสดง LINE user ID กับรูปของลูกค้า
- ระบบแจ้งเตือนเบื้องหลัง — จำนวนชั่วโมงที่จะแจ้งเตือนล่วงหน้าและระยะเวลาผ่อนผันก่อนนับว่าไม่มาตามนัดถูกเก็บที่ระดับเส้นทางการจอง เพื่อให้ระบบหลังบ้านนำไปส่งการแจ้งเตือนและตัดสถานะ โดย CMS ทำหน้าที่เพียงตั้งค่า
- คอมโพเนนต์ร่วมของ CMS — พื้นที่เลื่อนตาราง, ส่วนหัวหน้า, ขนาดหน้าตาราง และสไตล์ของ control ในแถบตัวกรอง
- สถาปัตยกรรมภายใน — เช่นเดียวกับแอปอื่นในกลุ่ม หน้าทั้งหมดเรียกบริการตรงและจัดการสถานะเอง ไม่ได้ใช้ชั้นจัดการคำขอกลาง และแอปนี้ไม่มีการอัปโหลดรูปจึงไม่ได้พึ่งบริการของโมดูลจัดการเนื้อหา
ส่วนที่ยังไม่สมบูรณ์
เอกสารส่วนนี้ระบุไว้เพื่อให้ผู้อ่านทราบสถานะจริงของโมดูล
- หน้าบริการและหน้าพนักงานที่อยู่ระดับบนสุดของแอปยังเป็นหน้าเปล่าที่ยังไม่ได้ต่อ API การจัดการจริงทำอยู่ในแท็บภายในหน้ารายละเอียดสาขา
- ยังไม่มีตัวประกอบ URL สำหรับลูกค้า เส้นทางการจองจึงให้เพียง token ดิบสำหรับคัดลอก และปุ่มคัดลอกยังไม่ได้จัดการกรณีที่เบราว์เซอร์ไม่รองรับหรือไม่ได้อยู่ในบริบทที่ปลอดภัย
- ปฏิทินครอบเวลา 07:00 ถึง 18:00 น. แบบตายตัว ไม่ได้อ่านจากเวลาทำการของสาขา และตำแหน่งของเส้นบอกเวลาปัจจุบันคำนวณด้วยอัตราส่วนที่ไม่ตรงกับการวางบล็อก
- ตัวจัดการฟิลด์ในขั้นตอนกรอกข้อมูลยังไม่ตรวจว่าคีย์ว่างหรือซ้ำกันหรือไม่
- หน้าแก้ขั้นตอนยังไม่มีการตรวจว่ามีการแก้ไขค้างอยู่ก่อนออกจากหน้า
รายละเอียดฝั่ง Backend (CMS API)
โมดูลนี้อยู่ที่ internal/modules/apps/ (ไฟล์ service_appointment.go) ทุก endpoint อยู่ใต้ /api/apps/appointment/
การป้องกันแต่ละกลุ่ม endpoint ไม่เท่ากัน (ข้อสังเกตด้านความปลอดภัย)
นี่คือจุดที่ควรรู้ที่สุดของแอปนี้ — ระดับการป้องกันของแต่ละกลุ่มไม่สม่ำเสมอ ซึ่งยกมาจากระบบเดิมตรง ๆ
- การจอง (bookings) — ป้องกันแน่นที่สุด ต้องผ่าน
JwtAuthระดับ global และAppEnabledGuardและประกาศ policy แยกตามการกระทำ - ขั้นตอนการจอง (journeys), บริการ (services) และพนักงาน (staff) — อยู่บนกลุ่มสาธารณะและตรวจด้วย token ระดับ "ล็อกอินแล้ว" เท่านั้น ไม่ผ่าน
AppEnabledGuardหมายความว่าองค์กรที่ยังไม่ได้เปิดแอปนี้ก็ยังยิง endpoint เหล่านี้ได้ถ้ามีบัญชีที่ล็อกอินได้ - สาขา (locations) — อยู่บนกลุ่มที่ต้องล็อกอิน แต่ ไม่ได้ประกาศ policy ใด ๆ เลย
สรุปคือการควบคุมที่พึ่งพาได้จริงมีเฉพาะกลุ่มการจอง ส่วนกลุ่มการตั้งค่าควรถือว่าเข้าถึงได้ด้วยสิทธิ์ที่ต่ำกว่าที่คาด เป็นรายการที่ควรจัดการเมื่อจะปรับสิทธิ์ทั้งระบบ
สิ่งที่เกิดขึ้นเมื่อเปลี่ยนสถานะการจอง
PUT /api/apps/appointment/bookings/:id/status ไม่ได้แค่เขียนสถานะลงฐานข้อมูล แต่ยัง ประกาศข้อความลงคิว booking_event_trigger บน RabbitMQ ด้วย ผลที่ตามมาคือ
- ระบบกฎอัตโนมัติ (Trigger Rule) รับเหตุการณ์นี้ไปสั่งส่งข้อความแจ้งผู้จองต่อได้ เช่น แจ้งยืนยันการจองหรือแจ้งยกเลิก การตั้งค่าข้อความจึงไม่ได้อยู่ในแอปนี้ แต่อยู่ที่หน้ากฎอัตโนมัติ
- ถ้าคิวมีปัญหาหรือ worker ไม่ทำงาน สถานะการจองจะเปลี่ยนสำเร็จแต่ผู้จองไม่ได้รับข้อความ โดยหน้า CMS ไม่แสดงความผิดพลาดใด ๆ
- รูปของข้อความที่ประกาศลงคิวถูกตรึงให้ตรงกับระบบเดิมทุกไบต์ เพราะผู้บริโภคเดิมยังอ่านอยู่
พฤติกรรมร่วมของทุกกลุ่ม CRUD
- ขอบเขตข้อมูล (
lineOaId,organizationId, ผู้ใช้ปัจจุบัน และภาษา) อ่านจาก context ของ request ทั้งหมด ไม่ได้รับจาก payload - การอ่านรายตัวตรวจทั้งรหัสและ LINE OA ประกอบกัน ถ้ารหัสนั้นเป็นของ OA อื่นจะได้ 404 เหมือนกับกรณีไม่มีอยู่จริง จึงไม่รั่วข้อมูลว่ามีระเบียนนั้นอยู่ที่อื่น
- การอ่านรายการคืนเป็น array ตรง ๆ ไม่ได้ห่อในโครงสร้างมาตรฐาน และไม่รวมรายการที่ถูกลบไปแล้ว
- การลบเป็น soft delete ที่ผูกกับกลไกอัตโนมัติของ ORM จริง ต่างจากหลายโมดูลในระบบที่ต้องเขียนเงื่อนไขกรองเอง คำสั่งอ่านจึงกรองรายการที่ลบแล้วให้อัตโนมัติโดยไม่ต้องระวัง
สิ่งที่ CMS ทำไม่ได้
การจองจริงของลูกค้า ไม่ได้เกิดที่ CMS API แต่เกิดที่ client-api ผ่านหน้า LIFF ซึ่งลูกค้าเลือกสาขา บริการ พนักงาน และเวลา แล้วยืนยัน หน้าหลังบ้านนี้ทำหน้าที่ตั้งค่าโครงสร้าง (สาขา บริการ พนักงาน ขั้นตอนการจอง) แล้วดูแลรายการจองที่เกิดขึ้นแล้ว
ตารางและระบบภายนอกที่เกี่ยวข้อง
ตาราง booking, location, service, staff, journey ร่วมกับ line_oa_app และ line_oa ส่วนระบบภายนอกคือ RabbitMQ (คิว booking_event_trigger)