Skip to main content

Rich message

ภาพรวม​

Rich message คือคลังเนื้อหาที่ประกอบขึ้นจากรายการเนื้อหาหลายรายการ (ข้อความ, รูปภาพ, วิดีโอ, รูปภาพแตะได้, Flex, หรืออ้างอิง Template message) แล้วส่งออกไปเป็นข้อความติดกันหลายบับเบิลบน LINE

จุดที่ต้องเข้าใจก่อนเริ่มคือ Rich message ไม่ได้ถูกส่งออกจากเมนูนี้เอง เป็นเพียงคลังเนื้อหาที่เมนู Campaign, Auto-response message และระบบ automation อื่นดึงไปใช้ส่งจริง

อีกจุดที่ต้องรู้คือเรื่อง การอ้างอิงสองแบบที่ทำงานต่างกัน — ชุด Quick reply ที่ผูกไว้เป็นการอ้างอิงสด (อ่านค่าล่าสุดตอนส่งทุกครั้ง) แต่ Template message ที่เลือกมาเป็นรายการเนื้อหานั้น ถูกคัดลอกเนื้อหามาเก็บไว้ตอนกดบันทึก แล้วอาศัยกลไก cascade จากฝั่งเมนูเทมเพลตตามมาแก้ให้เมื่อเทมเพลตถูกแก้ไข ซึ่งเป็นกลไกแบบ best-effort และไม่ทำงานตอนลบเทมเพลต (ดูหัวข้อ รายละเอียดสำคัญ)

บทบาทที่ใช้งาน — ทีมคอนเทนต์และทีมการตลาดที่เตรียมเนื้อหาไว้ล่วงหน้าก่อนนำไปใช้ในแคมเปญหรือกฎตอบกลับ (โมดูลนี้มี ModuleGate บังคับจริงที่ backend — ดูหัวข้อ สิทธิ์การเข้าถึง)

เส้นทางผู้ใช้งาน​

1. ดูรายการ Rich message​

เปิดเมนู จัดการ Rich Message หน้ารายการเป็นตารางเดียว คอลัมน์มี ลำดับ / ชื่อ / วันที่สร้าง (กำกับ "(เวลากรุงเทพฯ)") / จัดการ ตัวกรองมี 2 ช่อง — ค้นหาจากชื่อ และช่วงวันที่สร้าง ค่าตัวกรองถูกจดจำไว้ใน sessionStorage

หน้ารายการนี้ ไม่มีคอลัมน์สถานะและไม่มีสวิตช์เปิด/ปิด ต่างจากเมนู Quick reply และ Auto-response message — เพราะโมดูลนี้ไม่มีแนวคิดเรื่องการปิดใช้งานเลย (ดูรายละเอียดสำคัญ) แต่ละแถวมี 3 ปุ่ม — แก้ไข, คัดลอก (พาไปฟอร์มสร้างใหม่โดยดึงข้อมูลจากรายการเดิมมาเป็นต้นแบบ), และลบ (ต้องยืนยันก่อน)

2. สร้าง Rich message ใหม่​

  1. กดปุ่ม สร้างข้อความ เข้าสู่หน้าฟอร์มสองฝั่ง (ซ้ายกรอกข้อมูล ขวาดูตัวอย่างแชท)
  2. กรอกชื่อ — ไม่เกิน 100 ตัวอักษร ต้องไม่ซ้ำกับ Rich message อื่นใน OA เดียวกัน (ฟอร์มมีคำอธิบายกำกับว่า "กรุณาใช้ชื่อใหม่ที่ไม่เคยใช้กับ Rich Message อื่น") ใต้ช่องชื่อมีกล่อง เงื่อนไขการกรอกข้อมูล อธิบายอักขระ/สัญลักษณ์ที่รองรับ และข้อจำกัดว่าห้ามขึ้นต้นด้วยสัญลักษณ์
  3. เลือกผูก Quick reply หนึ่งชุด (ไม่บังคับ) จากช่อง การตอบกลับด่วน พร้อมคำอธิบายว่า "LINE จะแสดงชิปเหล่านี้เมื่อส่งข้อความนี้" — ปุ่มจะไปเกาะที่บับเบิลสุดท้ายของชุดข้อความ
  4. เพิ่มรายการเนื้อหาทีละรายการ ในส่วน ตั้งค่าข้อความ — สูงสุด 5 รายการ (ปุ่มเพิ่มถูกปิดเมื่อครบ พร้อมข้อความกำกับเพดาน) ลำดับที่จัดในฟอร์มคือลำดับบับเบิลที่ผู้ใช้ LINE จะเห็นจริง
  5. เลือกชนิดเนื้อหาต่อรายการจาก 6 แบบ
    • ข้อความ — ไม่เกิน 500 ตัวอักษร
    • รูปภาพ — รับเฉพาะ JPG / JPEG / PNG ขนาดไม่เกิน 10 MB
    • วิดีโอ — รูปแบบที่แนะนำ MP4 / M4V / MOV ไม่เกิน 200 MB ระบบสร้างภาพตัวอย่าง (thumbnail) ให้อัตโนมัติโดยดึงเฟรมจากวิดีโอบนเบราว์เซอร์แล้วอัปโหลดขึ้นพื้นที่เก็บชั่วคราวผ่าน endpoint แยก (POST /rich-message/gen-thumbnail-video) — ปุ่มบันทึกจะยังไม่ทำงานจนกว่าอัปโหลดจะเสร็จจริง (ฟอร์มเช็กว่าได้ thumbnail และ fileName กลับมาแล้ว ไม่ได้เช็กแค่ว่าเลือกไฟล์แล้ว)
    • รูปภาพแตะได้ — JPG / JPEG / PNG ความกว้างขั้นต่ำ 1040px จากนั้นเปิดหน้าต่างวาดพื้นที่แตะได้สูงสุด 20 จุด กำหนดการกระทำต่อจุดได้ 3 แบบ — เปิดลิงก์, ส่งข้อความ, โทรออก
    • ข้อความ Flex — วางโค้ด JSON เอง (ฟอร์มมีคำแนะนำว่าคัดลอกวางได้)
    • ข้อความเทมเพลต — เลือกเทมเพลตที่สร้างไว้จากเมนู Template message ระบบดึงเนื้อหามาแสดงตัวอย่างทันที (มีปุ่ม โหลดใหม่ เผื่อเพิ่งไปแก้เทมเพลตมา)
  6. ดูตัวอย่างผลลัพธ์ ที่คอลัมน์ขวา ซึ่งอัปเดตสดตามข้อมูลที่กรอก — แสดงเฉพาะรายการที่ผ่านเกณฑ์ความครบถ้วนของชนิดนั้นแล้ว (แต่ละชนิดมีเกณฑ์ของตัวเอง เช่น ชนิดข้อความต้องมีข้อความที่ไม่ใช่ช่องว่างล้วน ชนิดเทมเพลตต้องได้เนื้อหากลับมาแล้ว)
  7. กดบันทึกเมื่อทุกรายการผ่านเกณฑ์และกรอกชื่อแล้ว — กรณี สร้างใหม่หรือคัดลอก ระบบส่งข้อมูลทันที ส่วนกรณี แก้ไข ระบบเปิดกล่องยืนยันก่อน ("คุณแน่ใจหรือไม่ว่าต้องการแก้ไข...")

3. นำไปใช้ต่อที่เมนูอื่น​

  1. เมนูปลายทางเรียก endpoint แยก GET /rich-message/find-all-object ซึ่งคืนข้อมูลได้ 3 รูปแบบตามพารามิเตอร์ format — แบบ object (ค่าเริ่มต้น), แบบ dropdown และแบบ dropdownGroup เพื่อให้แต่ละหน้าจอเลือกใช้รูปแบบที่เหมาะกับตัวเอง
  2. Campaign — เลือก Rich message ไปตั้งเป็นเนื้อหาที่จะส่ง (ถ้ามี merge tag จะใช้กับ Broadcast ไม่ได้)
  3. Auto-response message — ผูก Rich message เข้ากับ keyword เพื่อตอบกลับอัตโนมัติ

รายละเอียดสำคัญที่ตรวจสอบจากซอร์สโค้ดจริง​

ยืนยันจาก internal/modules/richmessage/{controller,service,dto}.go ของ line-management-cms-api-go และ internal/autoresponse/transform.go ของ line-management-worker-go:

  • Flex message ถูกตรวจกับ LINE API จริง ไม่ใช่แค่ตรวจว่าเป็น JSON ถูกรูปแบบ — ตอนกดบันทึก backend ห่อ JSON ที่กรอกไว้เป็นข้อความ flex แล้วยิงไปให้ LINE ตรวจความถูกต้อง (ValidateJsonFlexMessage) ถ้า LINE ตอบว่าไม่ผ่านจะได้ 400 พร้อมระบุตำแหน่งเป็นราย index (content[0][flex], content[2][flex], ...) และ ระบบเก็บข้อผิดพลาดของทุกรายการไว้จนจบลูปก่อนแล้วตอบกลับพร้อมกันครั้งเดียว ไม่ได้หยุดที่รายการแรกที่พัง จึงเห็นปัญหาทุกจุดในรอบเดียว — แยกข้อความผิดพลาดได้ 3 แบบ: "Flex message is required" (ว่าง), "Invalid JSON format for flex message" (JSON พัง), และ "Failed to validate with LINE API" (JSON ใช้ได้แต่ LINE ไม่รับ)
  • altText ของ flex ถูกส่งเป็น "New Flex Message" เสมอ — backend รับฟิลด์ altText ได้และใช้มันเป็นข้อความสำรองตอนตรวจกับ LINE แต่ หน้าเว็บไม่เคยส่งฟิลด์นี้มาเลย จึงตกไปใช้ค่าเริ่มต้นที่ hardcode ไว้ทุกครั้ง
  • รายการชนิด "ข้อความเทมเพลต" ถูกคัดลอกเนื้อหามาเก็บไว้ตอนบันทึก (snapshot) แล้วอาศัยกลไก cascade ตามให้ทีหลัง — ตอนบันทึก backend อ่านแถวเทมเพลตแล้วเขียน flex ของเทมเพลตนั้นลงในฟิลด์ flex ของรายการเนื้อหา และตอนส่งจริง worker อ่านค่า flex ที่เก็บไว้นั้นตรง ๆ ไม่ได้ย้อนกลับไปอ่านแถวเทมเพลตอีกเลย สิ่งที่ทำให้เนื้อหาไม่ค้างคือฝั่งเมนู Template message: ทุกครั้งที่ แก้ไข เทมเพลต ระบบจะไล่ patch ค่า flex ที่เก็บไว้ในทุก Rich message ที่อ้างอิงเทมเพลตนั้นให้เอง (ดูรายละเอียดกลไกนี้ที่หน้า Template message) แต่ กลไกนี้ทำงานเฉพาะตอนแก้ไขเทมเพลต ไม่ทำงานตอนลบเทมเพลต — เทมเพลตที่ถูกลบไปแล้วจะทิ้งเนื้อหาชุดสุดท้ายไว้ใน Rich message ต่อไปเรื่อย ๆ และ Rich message นั้นยังส่งออกได้ตามปกติ ถ้าเทมเพลตถูกลบไปแล้ว ในตอนที่กดบันทึก Rich message จะได้ error APP_007 แทน
  • เพดานเกือบทั้งหมดที่เห็นในฟอร์มเป็นข้อจำกัดฝั่งหน้าเว็บเท่านั้น — DTO ฝั่ง backend ไม่มีการจำกัดจำนวนรายการเนื้อหาเลย (เพดาน 5 รายการเป็นค่าคงที่ในคอมโพเนนต์ฝั่งหน้าเว็บ) เช่นเดียวกับชื่อ 100 ตัวอักษร, ข้อความ 500 ตัวอักษร, รูปภาพ 10 MB, ความกว้างขั้นต่ำ 1040px และเพดาน 20 จุดแตะ — ทั้งหมดไม่มีการตรวจซ้ำที่ backend ส่วนที่ backend บังคับจริงคือ ชนิดไฟล์รูปภาพต้องเป็น image/jpeg หรือ image/png เท่านั้น (ไม่รับ GIF/WebP) และ ขนาดไฟล์ไม่เกิน 200,000,000 ไบต์ ซึ่งเป็นค่าเดียวกันทั้งรูปภาพและวิดีโอ ไม่ใช่ 10 MB อย่างที่ฟอร์มบอก
  • การอ้างอิงชุด Quick reply เป็นสามสถานะ และตรวจความเป็นเจ้าของจริง — ฟิลด์ quickReplyId แยกความหมายระหว่าง "ไม่ส่งคีย์มาเลย" (ไม่แตะค่าเดิม), "ส่งมาเป็น null หรือสตริงว่าง" (ล้างค่าเป็น NULL) และ "ส่งค่ามา" (บันทึกค่านั้น) และก่อนบันทึกทุกครั้ง backend ตรวจว่า id นั้น มีอยู่จริง ไม่ถูกลบ และอยู่ใน OA ของผู้เรียก ไม่ผ่านจะได้ 404 — กันการแนบชุด quick reply ข้ามองค์กรอย่างเงียบ ๆ ในทางปฏิบัติหน้าเว็บ ส่งคีย์นี้มาทุกครั้ง (ส่งสตริงว่างเมื่อเลือก "ไม่มี") จึงไม่เคยใช้สถานะ "ไม่ส่งคีย์มาเลย"
  • Rich message ไม่มีสถานะเปิด/ปิด — ทุกรายการเป็น active ตลอดชีวิต — ตอนสร้าง backend hardcode status = active และ type = text ไว้ตายตัว (คอลัมน์ type จึงเป็น text เสมอไม่ว่าเนื้อหาข้างในจะเป็นชนิดใด) และคำสั่งแก้ไข ไม่แตะคอลัมน์ status เลย ทั้งโมดูลไม่มีเส้นทางเปลี่ยนสถานะด้วย ดังนั้นคำว่า "Rich message ที่เปิดใช้งานอยู่" ในเมนูอื่นแท้จริงหมายถึงแค่ "ยังไม่ถูกลบ" ส่วนตัวกรองสถานะของ endpoint รายการรับได้แค่ active/inactive และ ปฏิเสธค่า delete ด้วยข้อความ "Invalid status"
  • การลบ Rich message ไปล้าง cache ของ Auto-response message ให้ด้วย — ก่อนและหลังการ soft delete ระบบไล่อ่าน Redis hash ของกฎตอบกลับอัตโนมัติ (AUTO_RESPONSE:LINE_OA_ID:<id>) แล้วลบทุก field ที่ชี้มาที่ Rich message นี้ออก โค้ดเรียกขั้นตอนนี้สองครั้ง (ก่อนและหลัง) ซึ่งคอมเมนต์ระบุว่าเป็นการรักษาพฤติกรรมเดิมของระบบต้นฉบับไว้ตรง ๆ ("called twice in TS") ผลที่ผู้ใช้เห็นคือกฎตอบกลับที่ผูกกับ Rich message ที่ถูกลบจะเลิกตอบทันทีโดยไม่ต้องรอ cache หมดอายุ
  • การย้ายไฟล์จากที่เก็บชั่วคราวไปที่เก็บถาวรถูกแก้บั๊กไว้ให้ล้มเหลวแบบดัง ๆ — คอมเมนต์ในโค้ดระบุว่าเดิมผลของการคัดลอกไฟล์วิดีโอถูกทิ้งไป ทำให้กรณีคัดลอกไม่สำเร็จ ระบบยังบันทึก URL "ถาวร" ของไฟล์ที่ไม่เคยถูกวางไว้ที่นั้นจริง และ มองไม่เห็นปัญหาจนกว่าจะเปิดรายการนั้นขึ้นมาแก้ไข ปัจจุบันแก้ให้ตอบ 500 ทันทีที่คัดลอกไม่สำเร็จ ทั้งไฟล์วิดีโอและภาพตัวอย่างของวิดีโอ
  • มีจุดที่ Go port ตั้งใจเขียนต่างจากระบบต้นฉบับ — ฟิลด์ text เดิมประกาศเป็น optional ทำให้ข้อความที่เป็นช่องว่างล้วน (" ") ผ่านการตรวจไปได้แล้วทำให้การส่งไป LINE ล้มเหลวปลายทาง โค้ด Go จึงบังคับว่าเมื่อชนิดของรายการเป็น text ค่านั้นต้องไม่ว่างหลังตัดช่องว่างแล้ว (คอมเมนต์ระบุเหตุผลไว้ตรง ๆ ว่า "whitespace-only input slipped through the optional check and broke sends")
  • เส้นทางรายการมีโหมด cache แบบเลือกเปิด และคีย์ cache ถูกสร้างให้ตรงกับระบบเดิมทีละไบต์ — ส่งพารามิเตอร์ cache=true มาจะเข้าเส้นทางที่อ่านจาก Redis โดยคีย์สร้างจากการเลียนแบบ JSON.stringify(query) ของ JavaScript รวมถึง ลำดับของคีย์ (page, orderBy, order, limit มาก่อน แล้วค่อยพารามิเตอร์อื่นตามลำดับที่ปรากฏใน URL) เพื่อให้คีย์ที่ระบบเก่าเคยเขียนไว้ยังใช้ร่วมกันได้ และ ทุกการเขียน (สร้าง/แก้ไข/ลบ) จะล้าง cache ของโมดูลนี้ทั้งหมด
  • ไฟล์ของ "รูปภาพแตะได้" ไม่ถูกตรวจสอบที่ชั้น DTO เลย — DTO ตรวจแค่ฟิลด์ image ส่วน tappableImage เข้ามาในฐานะฟิลด์ส่วนเกิน (การตรวจแบบ whitelist ปิดอยู่) แล้วถูกใช้งานทีหลังในขั้นตอนประมวลผลสื่อ ผลคือถ้าไม่มีไฟล์แนบมาจริง จะได้ 500 พร้อมข้อความ Cannot read properties of undefined (reading 'originalName') ซึ่งเป็นข้อความ TypeError ของ JavaScript ที่ถูกคงไว้โดยเจตนาเพื่อความเข้ากันได้กับระบบเดิม
  • การอ่านรายการเดียวกับการแก้ไขตอบรหัสข้อผิดพลาดไม่เหมือนกัน — GET /rich-message/:id ตอบ 400 APP_007 เมื่อไม่พบหรือเป็น id ของ OA อื่น ส่วน PUT /rich-message/:id ตอบ 404 "Rich message not found" ในกรณีเดียวกัน ทั้งสองเส้นทางผูก line_oa_id ของผู้เรียกไว้แล้วทั้งคู่ (คอมเมนต์ระบุว่าเป็นการอุดช่องอ่าน/แก้ข้อมูลข้ามองค์กรที่เคยมี)

เมนูที่เกี่ยวข้องกันในเส้นทางนี้​

  • Quick reply — ชุดปุ่มที่ผูกไว้เป็นอ้างอิงสด และ Rich message เป็น ตารางเดียว ที่กลไกป้องกันการปิด/ลบของเมนู Quick reply สแกนหา (ปิดชุดที่ถูก Rich message อ้างอิงอยู่จะถูกเตือนก่อน ส่วนการลบทำไม่ได้เลย)
  • Template message — เป็นแหล่งของรายการเนื้อหาชนิด "ข้อความเทมเพลต" โดย คัดลอกเนื้อหามาตอนบันทึก แล้วเมนูเทมเพลตจะ cascade กลับมาแก้ค่าที่คัดลอกไว้ให้เมื่อเทมเพลตถูกแก้ไข (แต่ไม่ทำเมื่อถูกลบ)
  • Campaign — ผู้ใช้ปลายทางหลัก แคมเปญไม่สร้างเนื้อหาเองแต่เลือก Rich message ไปส่ง และเป็นที่ที่กฎ merge tag มีผล (Rich message ที่มี merge tag ใช้กับ Broadcast ไม่ได้)
  • Auto-response message — ผูก Rich message เข้ากับ keyword และการลบ Rich message ที่นี่จะไปล้าง cache ของกฎตอบกลับที่ชี้มาที่มันให้ด้วย

จุดที่ควรระวัง​

  • การ cascade เนื้อหาเทมเพลตเป็นแบบ best-effort ล้มเหลวได้เงียบ ๆ — ถ้าการ patch ล้มเหลว ระบบเพียงเขียน log ไว้ ไม่ทำให้การแก้ไขเทมเพลตล้มเหลวและไม่แจ้งผู้ใช้ ผลคือ Rich message บางรายการอาจค้างเนื้อหาชุดเก่าไว้โดยไม่มีสัญญาณเตือน ถ้าสงสัยว่าเนื้อหาไม่อัปเดต ให้เปิด Rich message นั้นขึ้นมากดบันทึกใหม่เพื่อดึงเนื้อหาชุดล่าสุดเข้ามาแน่ ๆ (ปุ่ม โหลดใหม่ ในฟอร์มช่วยดึงมาแสดงได้ แต่จะมีผลจริงเมื่อกดบันทึกแล้วเท่านั้น)
  • ลบเทมเพลตแล้ว Rich message ยังส่งเนื้อหาชุดเดิมต่อไป — กลไก cascade ไม่ทำงานตอนลบ Rich message ที่อ้างอิงเทมเพลตที่ถูกลบไปแล้วจะยังส่งเนื้อหาชุดสุดท้ายที่คัดลอกไว้ออกไปได้เรื่อย ๆ โดยไม่มีข้อผิดพลาด (จะเจอปัญหาก็ตอนเปิดรายการนั้นขึ้นมากดบันทึกใหม่ ซึ่งจะได้ APP_007)
  • แก้ Rich message ที่ผูกกับแคมเปญหรือกฎตอบกลับไว้แล้ว จะเปลี่ยนสิ่งที่ถูกส่งออกไปจริงในครั้งถัดไปทันที — ไม่มีเวอร์ชันหรือขั้นอนุมัติคั่นกลาง และหน้าจอไม่ได้บอกว่ามีใครอ้างอิง Rich message นี้อยู่บ้าง (ต่างจากเมนู Quick reply ที่มีการ์ด "ใช้งานโดย")
  • ข้อความแจ้งขนาดไฟล์ของ "รูปภาพแตะได้" ไม่ตรงกับกฎที่ใช้จริง — ฟอร์มตรวจที่ 10 MB (ค่าเดียวกับรูปภาพทั่วไป) และคำอธิบายเหนือช่องอัปโหลดก็บอก 10 MB ถูกต้อง แต่ข้อความแจ้งเตือนเมื่อไฟล์ใหญ่เกินยังเขียนว่า "รูปภาพต้องมีขนาดเล็กกว่า 1 MB!" ซึ่งเป็นข้อความที่ค้างอยู่ ไม่ใช่กฎจริง
  • ไฟล์รูปแบบ GIF หรือ WebP บันทึกไม่ผ่าน — ทั้งฟอร์มและ backend รับเฉพาะ JPEG และ PNG (backend ตรวจที่ค่า MIME type ตรง ๆ)
  • ปุ่มบันทึกของรายการชนิดวิดีโออาจดูเหมือนไม่ทำงาน — ระบบตั้งใจไม่ให้บันทึกได้จนกว่าการอัปโหลดวิดีโอและการสร้างภาพตัวอย่างจะเสร็จจริง ถ้าอัปโหลดค้างหรือล้มเหลว ปุ่มจะยังไม่พร้อมใช้งาน ให้ลองอัปโหลดไฟล์ใหม่
  • ชื่อซ้ำถูกปฏิเสธเป็นรายฟิลด์ ไม่ใช่ข้อความรวม — ระบบตอบ 400 พร้อมชี้ที่ฟิลด์ title ว่า "Title is already exist" หน้าเว็บจึงขึ้นข้อผิดพลาดใต้ช่องชื่อโดยตรง
  • การลบเป็นการลบแบบนุ่มนวล (soft delete) ข้อมูลยังอยู่ในฐานข้อมูล ส่วนการลบถาวรต้องเป็น Super Admin และเรียก API เอง

สิทธิ์การเข้าถึง (ตรวจสอบจากซอร์สโค้ดจริง)​

  • ฝั่งหน้าเว็บ — ถูกครอบด้วย PermissionGuard ตรวจ action VIEW ของ subject rich-message-management
  • ฝั่งเซิร์ฟเวอร์ — ทุกเส้นทางทั้ง 8 เส้นทางของโมดูลนี้ถูกครอบด้วย ModuleGate(d, "rich-message") ที่บังคับใช้จริง (เหมือนโมดูล Campaign และต่างจากโมดูล Quick reply ที่ไม่มี ModuleGate เลย) คอมเมนต์ในโค้ดระบุพฤติกรรมไว้ชัด: ผู้เรียกประเภทลูกค้าที่องค์กรถูกผู้ดูแลระดับแพลตฟอร์มปิดโมดูล "rich-message" ไว้ จะได้ 403 ส่วนผู้เรียกภายใน (onemoby) ผ่านได้
  • policy annotation ระดับการกระทำ (create/update/delete/read/readAll) มีประกาศไว้ครบ แต่วิ่งผ่าน middleware ที่เป็น no-op เหมือนโมดูลอื่นส่วนใหญ่ — การบังคับจริงจึงอยู่ที่ ModuleGate ระดับโมดูล ไม่ใช่ที่ policy ระดับการกระทำ
  • DELETE /api/rich-message/:id เป็น soft delete ส่วนการลบจริง DELETE /api/rich-message/:id/hard ต้องเป็น Super Admin และต้องส่ง query parameter confirm=true มาด้วย — ถ้าไม่ส่ง จะถูกปฏิเสธด้วย 400 ที่มีข้อความเป็นสตริงดิบ ErrorResponse.AUS_10 (ข้อความที่ไม่ถูกแปลเป็นภาษาผู้ใช้ คงไว้ตามระบบเดิมโดยเจตนา) และ confirm ที่ไม่ใช่ true/false ตรงตัวจะถูกปฏิเสธด้วย "Validation failed (boolean string is expected)"
  • การกันข้อมูลข้ามองค์กรบังคับที่ทุกเส้นทาง — ทั้งการอ่าน รายการ และการแก้ไข ผูก line_oa_id ของผู้เรียกไว้ทั้งหมด (คอมเมนต์ระบุว่าเป็นการอุดช่องที่เคยอ่าน/แก้/แนบ quick reply ข้ามองค์กรได้)

ไฟล์อ้างอิงจากซอร์สโค้ด​

Frontend (cms-web-2026)

  • src/app/rich-message-management/page.tsx, src/app/rich-message-management/form/page.tsx — จุดเข้าหน้ารายการและฟอร์ม พร้อม PermissionGuard
  • src/components/rich-message-management/list/rich-message-management.container.tsx — ตาราง, ปุ่มคัดลอก/ลบ, ตัวกรอง
  • src/components/rich-message-management/form/rich-message-management-form.tsx — เพดาน 5 รายการ, กฎตรวจไฟล์แต่ละชนิด, ความกว้างขั้นต่ำ 1040px
  • src/components/rich-message-management/form/rich-message-management-form.container.tsx — การประกอบ FormData ที่ส่งขึ้น backend, การดึงเนื้อหาเทมเพลต, ขั้นตอนยืนยันก่อนแก้ไข
  • src/components/rich-message-management/form/utils/validate-rich-message-item.ts — เกณฑ์ความครบถ้วนของแต่ละชนิดเนื้อหา
  • src/components/rich-message-management/form/modal/action-spot.tsx — หน้าต่างวาดพื้นที่แตะได้ และเพดาน 20 จุด
  • src/services/rich-message-management.service.ts — endpoint ทั้งหมดที่หน้าเว็บเรียกใช้
  • messages/th/rich-message-management.json, messages/en/rich-message-management.json — ข้อความ UI ทั้งหมด

Backend (line-management-cms-api-go)

  • internal/modules/richmessage/controller.go — 8 เส้นทาง, ModuleGate("rich-message"), SuperAdminGuard ของ hard delete, การถอดรหัส multipart content[i][field]
  • internal/modules/richmessage/service.go — การประมวลผลสื่อ, การตรวจ flex กับ LINE API, การคัดลอกเนื้อหาเทมเพลต, การล้าง cache ของ Auto-response
  • internal/modules/richmessage/dto.go — กฎตรวจสอบข้อมูลขาเข้า, สามสถานะของ quickReplyId, เพดานขนาดไฟล์และ MIME type

Worker (line-management-worker-go)

  • internal/autoresponse/transform.go — การแปลงรายการเนื้อหาแต่ละชนิดเป็นรูปแบบข้อความของ LINE ตอนส่งจริง

ดูรายละเอียดเชิงเทคนิคเพิ่มเติม (เวอร์ชันก่อนหน้าของทีมเอกสาร) ที่ จัดการ Rich Message