Skip to main content

ระบบจองนัดหมาย

ภาพรวม

แอปเสริมสำหรับ จองคิวและนัดหมาย ผ่าน LIFF โดยลูกค้าเลือกสาขา ตามด้วยบริการ ผู้ให้บริการ และวันเวลา แล้วได้รับการจองพร้อมการแจ้งเตือนล่วงหน้า

จุดสำคัญเชิงสถาปัตยกรรมคือตารางทั้งหมดอยู่ใน PostgreSQL schema แยกชื่อ appointment ไม่ได้อยู่ใน public และ ไม่มีนิยามอยู่ใน Prisma เลย โดยใช้ไฟล์ SQL ที่เรียงลำดับหมายเลขของตัวเอง และ apply ด้วย psql ซึ่งเป็นแนวทางเดียวกับแอป bulletin และ loyalty

โครงสร้างข้อมูลหลัก

appointment.location — สาขาหรือสถานที่

  • ข้อมูลพื้นฐาน: name, description, address, phone
  • working_hours แบบ JSONB เก็บเวลาทำการรายวัน และ blocked_dates เก็บวันหยุด
  • timezone (ค่าเริ่มต้น Asia/Bangkok), sort_order, status และ soft delete deleted_date
  • ผูกขอบเขตด้วย line_oa_id และ organization_id

appointment.service — บริการที่จองได้

  • location_id เป็น FK แบบ ON DELETE CASCADE
  • duration_minutes (ค่าเริ่มต้น 30), max_bookings_per_slot (ค่าเริ่มต้น 1) และ requires_staff สามค่านี้คือตัวกำหนดการคำนวณช่วงเวลาว่าง
  • ราคา: price แบบ DECIMAL(10,2) และ currency (ค่าเริ่มต้น THB)

appointment.staff — ผู้ให้บริการ

  • location_id เป็น FK พร้อม name, title และ avatar_url
  • service_ids แบบ JSONB เก็บรายการบริการที่พนักงานคนนี้ทำได้ในรูปแบบอาเรย์ แทนการใช้ตารางเชื่อมแยกต่างหาก

appointment.journey — คอนฟิกขั้นตอนการจอง

หนึ่ง journey เท่ากับหนึ่งลิงก์จอง

  • token VARCHAR(64) แบบ unique ทำหน้าที่เป็นคีย์ใน URL ของ LIFF
  • auto_confirm (ค่าเริ่มต้น true) และ steps แบบ JSONB นิยามลำดับขั้นตอนที่ลูกค้าเห็น
  • reminder_hours_before (ค่าเริ่มต้น 24) และ no_show_grace_minutes (ค่าเริ่มต้น 30)

appointment.booking — การจองจริง

  • FK: journey_id, location_id, service_id และ staff_id (เว้นว่างได้)
  • user_id VARCHAR(255) คือ LINE userId ของผู้จอง
  • เวลา: booking_date แบบ DATE พร้อม start_time และ end_time แบบ TIME
  • สถานะ: status (ค่าเริ่มต้น pending), notes, form_data แบบ JSONB และ cancel_reason พร้อม timestamp ครบทุกสถานะ ได้แก่ confirmed_date, cancelled_date, completed_date
  • reminder_sent มาพร้อม partial index (status, reminder_sent, booking_date) WHERE reminder_sent = false ทำให้ cron หาการจองที่ยังไม่ได้แจ้งเตือนได้เร็ว
  • index (location_id, booking_date, status) รองรับหน้าปฏิทินฝั่ง CMS

Trigger แจ้งเหตุการณ์การจอง

trg_booking_event_insert และ trg_booking_event_update เรียกฟังก์ชัน appointment.notify_booking_event() ซึ่งยิง pg_notify ไปที่ช่อง booking_event พร้อมบันทึกลง event_outbox แบบ best-effort โดยดักจับ undefined_table แล้วข้ามไปหากยังไม่มีตาราง

เหตุการณ์จะถูกยิงเมื่อสร้างการจองใหม่ (booking_created) และเมื่อ สถานะเปลี่ยนเท่านั้น (booking_status_changed) ไม่ยิงทุกครั้งที่มีการอัปเดตแถว

ไฟล์ที่เกี่ยวข้อง

  • apps/appointment/migrations/001_create_schema.sql — คำสั่ง CREATE SCHEMA appointment
  • apps/appointment/migrations/002_create_tables.sql — ตารางหลักทั้ง 5 ตาราง
  • apps/appointment/migrations/003_add_reminder_columns.sql — คอลัมน์ที่เกี่ยวกับการแจ้งเตือน
  • apps/appointment/migrations/004_add_booking_event_trigger.sqlpg_notify และ outbox
  • schema-dumps/2026-07-24/schema.sql:26 (CREATE SCHEMA appointment) และช่วง :582:807

จุดเชื่อมต่อกับ Service อื่น

  • client-api-go ให้บริการฝั่ง LIFF ของลูกค้า โดยคำนวณช่วงเวลาว่างจาก working_hours ประกอบกับ blocked_dates, duration_minutes และ max_bookings_per_slot ก่อนสร้างการจอง
  • cms-api-go จัดการสาขา บริการ พนักงาน journey และหน้าปฏิทินการจอง
  • worker-go ฟังช่อง booking_event เพื่อส่งการแจ้งเตือนผ่าน LINE และเป็นจุดตั้งต้นของ trigger rule รวมถึงรัน cron ส่ง reminder ตามเวลาที่ตั้งไว้
  • การเปิดใช้แอปควบคุมด้วย line_oa_app.app_id ใน ช่องทาง LINE OA ส่วนเหตุการณ์ทั้งหมดไหลไปที่ pg_notify และ Event Outbox