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 ใหม่
- กดปุ่ม สร้างข้อความ เข้าสู่หน้าฟอร์มสองฝั่ง (ซ้ายกรอกข้อมูล ขวาดูตัวอย่างแชท)
- กรอกชื่อ — ไม่เกิน 100 ตัวอักษร ต้องไม่ซ้ำกับ Rich message อื่นใน OA เดียวกัน (ฟอร์มมีคำอธิบายกำกับว่า "กรุณาใช้ชื่อใหม่ที่ไม่เคยใช้กับ Rich Message อื่น") ใต้ช่องชื่อมีกล่อง เงื่อนไขการกรอกข้อมูล อธิบายอักขระ/สัญลักษณ์ที่รองรับ และข้อจำกัดว่าห้ามขึ้นต้นด้วยสัญลักษณ์
- เลือกผูก Quick reply หนึ่งชุด (ไม่บังคับ) จากช่อง การตอบกลับด่วน พร้อมคำอธิบายว่า "LINE จะแสดงชิปเหล่านี้เมื่อส่งข้อความนี้" — ปุ่มจะไปเกาะที่บับเบิลสุดท้ายของชุดข้อความ
- เพิ่มรายการเนื้อหาทีละรายการ ในส่วน ตั้งค่าข้อความ — สูงสุด 5 รายการ (ปุ่มเพิ่มถูกปิดเมื่อครบ พร้อมข้อความกำกับเพดาน) ลำดับที่จัดในฟอร์มคือลำดับบับเบิลที่ผู้ใช้ LINE จะเห็นจริง
- เลือกชนิดเนื้อหาต่อรายการจาก 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 ระบบดึงเนื้อหามาแสดงตัวอย่างทันที (มีปุ่ม โหลดใหม่ เผื่อเพิ่งไปแก้เทมเพลตมา)
- ดูตัวอย่างผลลัพธ์ ที่คอลัมน์ขวา ซึ่งอัปเดตสดตามข้อมูลที่กรอก — แสดงเฉพาะรายการที่ผ่านเกณฑ์ความครบถ้วนของชนิดนั้นแล้ว (แต่ละชนิดมีเกณฑ์ของตัวเอง เช่น ชนิดข้อความต้องมีข้อความที่ไม่ใช่ช่องว่างล้วน ชนิดเทมเพลตต้องได้เนื้อหากลับมาแล้ว)
- กดบันทึกเมื่อทุกรายการผ่านเกณฑ์และกรอกชื่อแล้ว — กรณี สร้างใหม่หรือคัดลอก ระบบส่งข้อมูลทันที ส่วนกรณี แก้ไข ระบบเปิดกล่องยืนยันก่อน ("คุณแน่ใจหรือไม่ว่าต้องการแก้ไข...")
3. นำไปใช้ต่อที่เมนูอื่น
- เมนูปลายทางเรียก endpoint แยก
GET /rich-message/find-all-objectซึ่งคืนข้อมูลได้ 3 รูปแบบตามพารามิเตอร์format— แบบ object (ค่าเริ่มต้น), แบบdropdownและแบบdropdownGroupเพื่อให้แต่ละหน้าจอเลือกใช้รูปแบบที่เหมาะกับตัวเอง - Campaign — เลือก Rich message ไปตั้งเป็นเนื้อหาที่จะส่ง (ถ้ามี merge tag จะใช้กับ Broadcast ไม่ได้)
- 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 จะได้ errorAPP_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ตอบ 400APP_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ตรวจ actionVIEWของ subjectrich-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 parameterconfirm=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— จุดเข้าหน้ารายการและฟอร์ม พร้อมPermissionGuardsrc/components/rich-message-management/list/rich-message-management.container.tsx— ตาราง, ปุ่มคัดลอก/ลบ, ตัวกรองsrc/components/rich-message-management/form/rich-message-management-form.tsx— เพดาน 5 รายการ, กฎตรวจไฟล์แต่ละชนิด, ความกว้างขั้นต่ำ 1040pxsrc/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, การถอดรหัส multipartcontent[i][field]internal/modules/richmessage/service.go— การประมวลผลสื่อ, การตรวจ flex กับ LINE API, การคัดลอกเนื้อหาเทมเพลต, การล้าง cache ของ Auto-responseinternal/modules/richmessage/dto.go— กฎตรวจสอบข้อมูลขาเข้า, สามสถานะของquickReplyId, เพดานขนาดไฟล์และ MIME type
Worker (line-management-worker-go)
internal/autoresponse/transform.go— การแปลงรายการเนื้อหาแต่ละชนิดเป็นรูปแบบข้อความของ LINE ตอนส่งจริง
ดูรายละเอียดเชิงเทคนิคเพิ่มเติม (เวอร์ชันก่อนหน้าของทีมเอกสาร) ที่ จัดการ Rich Message