ยกเลิกนัดหมายผ่าน Postback
ภาพรวม
เมื่อระบบส่งข้อความยืนยันนัดหมายไปหาผู้ใช้ LINE ข้อความนั้นมักมีปุ่ม "ยกเลิกนัด" แนบไปด้วย
ปุ่มดังกล่าวถูกฝัง postback data ในรูปแบบ appt_cancel:{bookingId} ดังนั้นเมื่อผู้ใช้กดปุ่ม
LINE จะส่ง postback event เข้ามาที่ webhook ตัวนี้
หน้าที่ของ webhook-go คือ แปลง postback ให้กลายเป็นคำสั่งยกเลิก แล้วส่งลงคิว
booking_notification เพื่อให้ worker-go ไปยกเลิกจริงในฐานข้อมูลและแจ้งเตือนกลับ
แม้จะเป็นฟีเจอร์ขนาดเล็ก แต่ถูกแยกเป็นหัวข้อเฉพาะ เนื่องจากเป็น เส้นทางเดียวในโปรเจกต์
ที่ publish ลงคิว booking_notification และเป็นตัวอย่างของ postback routing
ที่ hardcode prefix ไว้ในโค้ด
Business Flow
-
LINE ส่ง event ที่มี
type: "postback"เข้ามาที่POST /api/line/:id -
ProcessLineผ่านขั้นตอนอ่านwebhook_configมาแล้ว โดยต้องมี config และmboxEnabled == "1":::note ข้อสังเกตสำคัญ เงื่อนไข postback ทั้งบล็อกถูกครอบด้วย
mboxEnabled == "1"ซึ่งหมายความว่า OA ที่ไม่ได้เปิด mbox จะยกเลิกนัดผ่านปุ่มนี้ไม่ได้ เพราะ postback จะตกไปยังเส้นทาง default คือ publish ลงคิวline_webhookแทน พฤติกรรมนี้เป็น parity ตามระบบต้นฉบับ NestJS ::: -
อ่าน
postback.dataของ postback event ตัวแรก -
หากไม่ขึ้นต้นด้วย
mbox_team:แต่ขึ้นต้นด้วยappt_cancel:จะเข้าสู่เส้นทางนี้ -
ตัดเอาส่วนหลังเครื่องหมาย colon แรกมา parse เป็น int64 หาก parse ไม่ผ่านจะ log error
processLine: appt_cancel invalid bookingIdแล้ว return โดยไม่ publish และไม่ตกไปเส้นทาง default จุดนี้เป็นการ guard ที่จงใจ เพื่อเลี่ยงการ parse แบบเงียบที่จะให้ค่า 0 -
publish bare JSON ลงคิว
booking_notificationในรูปแบบ
{
"type": "cancel",
"bookingId": 12345,
"userId": "LINE user id จาก event.source.userId",
"lineOaId": 123
}
- จากนั้น return ทันทีโดยไม่ publish ลงคิว
line_webhookเพราะบอทไม่จำเป็นต้องเห็น postback นี้
ไฟล์และฟังก์ชันหลัก
ฟีเจอร์นี้เข้าถึงผ่าน endpoint POST /api/line/:id
(ดู ประตูรับ Webhook จาก LINE) โดยไม่มี route ของตัวเอง
| ไฟล์ | จุดที่เกี่ยวข้อง |
|---|---|
internal/line/service.go | Service.ProcessLine ในบล็อกที่ตรวจ prefix appt_cancel: |
internal/line/service.go | type bookingCancelPayload ประกอบด้วย Type, BookingID, UserID และ LineOaID |
internal/line/service.go | splitSecond(data) ตัดเอาส่วนที่สองหลัง colon โดยใช้ SplitN |
internal/line/service.go | postbackData(e), sourceUserID(e), parseLineOaID(raw) |
ชื่อคิวปลายทางมาจาก deps.Config.RabbitMQ.QueueBookingNotification ซึ่งอ่านจากตัวแปร
RABBITMQ_QUEUE_BOOKING_NOTIFICATION และมีค่าเริ่มต้นเป็น booking_notification
จุดเชื่อมต่อกับ Service อื่น
- RabbitMQ — คิว
booking_notificationบน exchangeline_exchange - worker-go — เป็น consumer ที่อัปเดตสถานะ booking ในฐานข้อมูล แล้วส่งข้อความยืนยันการยกเลิกกลับหาผู้ใช้ LINE และแจ้งเจ้าของร้าน
- โดเมนฐานข้อมูล — เกี่ยวข้องกับโดเมน
appointmentซึ่งเก็บข้อมูล booking แต่ webhook-go ไม่ได้แตะฐานข้อมูลเอง เพียงส่งbookingIdไปให้ worker เท่านั้น - cms-api — โมดูล
apps-appointmentเป็นผู้สร้าง booking และเป็นผู้กำหนดว่า ปุ่มยกเลิกจะฝัง postback data ในรูปแบบappt_cancel:ตามด้วย id - Redis —
webhook_configต้องมีค่าlineOaIdและmboxEnabledเป็น"1"(ดู Redis Cache ของ Webhook Config) - โค้ดใช้บล็อกเดียวกับ ส่งต่อให้เจ้าหน้าที่
โดย postback แบบ
mbox_team:จะถูกตรวจก่อน