จัดการ Template Message
ภาพรวม
Template Message คือคลัง LINE Flex Message ที่สร้างขึ้นจากแม่แบบสำเร็จรูป โดยระบบเก็บผลลัพธ์ไว้สองรูปแบบควบคู่กัน
- ข้อมูลที่ผู้ใช้กรอก — เก็บไว้เพื่อให้กลับมาเปิดแก้ไขด้วยฟอร์มเดิมได้
- Flex JSON ที่พร้อมส่ง — เก็บไว้ให้โมดูลอื่นดึงไปใช้ส่งได้ทันทีโดยไม่ต้องประกอบใหม่
ฟีเจอร์นี้ออกแบบมาสำหรับทีมคอนเทนต์ที่ต้องการสร้าง Flex Message โดยไม่ต้องเขียน JSON เอง แต่ก็ยังเปิดช่องให้ผู้ที่ถนัดเขียน JSON ทำได้โดยตรง
ทุกฟิลด์ข้อความรองรับ merge tag ในรูปแบบ {{key}} เพื่อแทนค่าข้อมูลของผู้รับแต่ละคนตอนส่งจริง
แม่แบบทั้ง 6 ชนิด
| ชนิด | โครงสร้าง | จำนวนบับเบิล |
|---|---|---|
| Register | ไอคอน หัวเรื่อง คำอธิบาย และปุ่มหนึ่งปุ่ม | 1 |
| Shopping | รูปภาพ หัวเรื่อง หัวเรื่องรอง และปุ่มสองปุ่ม | 1 |
| Notification | รูปภาพ ไอคอน หัวเรื่อง คำอธิบาย และปุ่มหนึ่งปุ่ม | 1 |
| Carousel | การ์ดหลายใบเรียงแนวนอน แต่ละใบเป็นรูปเต็มใบพร้อมการกระทำหนึ่งอย่าง | เริ่มที่ 2 ใบ เพิ่มได้ถึง 6 ใบ |
| Custom JSON | เขียน Flex JSON เองทั้งหมด | ตามที่ JSON กำหนด |
| Card Builder | เครื่องมือประกอบการ์ดแบบ visual จากบล็อกย่อย แล้วแปลงเป็นบับเบิลอัตโนมัติ | 1 |
Card Builder เป็นชนิดเริ่มต้นเมื่อสร้างเทมเพลตใหม่
Business Flow
1. หน้ารายการ
- ระบบตรวจสิทธิ์ก่อน แล้วโหลดเงื่อนไขการค้นหาล่าสุดจาก sessionStorage
- ตารางแสดงชื่อเทมเพลต (คลิกเพื่อดูรายละเอียด) ชนิดแม่แบบ วันที่แก้ไขล่าสุด (หรือวันที่สร้างหากยังไม่เคยแก้ไข) และปุ่มดำเนินการ
- ปุ่มดำเนินการมีเฉพาะแก้ไขและลบ ส่วนการเข้าดูรายละเอียดทำได้ผ่านการคลิกที่ชื่อเทมเพลตเท่านั้น
- ตัวกรองมีช่องค้นหาและตัวเลือกชนิดแม่แบบ
- การลบต้องยืนยันก่อน และเมื่อสำเร็จจะแสดงกล่องยืนยันผลพร้อมพากลับหน้ารายการ
ข้อควรทราบ — ตัวเลือกชนิดในตัวกรองยังไม่ครอบคลุมแม่แบบ Card Builder จึงยังกรองหาเทมเพลตชนิดนี้จากตัวกรองไม่ได้
2. โครงการทำงานร่วมของหน้าฟอร์ม
- หน้าฟอร์มรองรับสี่โหมด คือ สร้างใหม่ แก้ไข คัดลอก และดูรายละเอียด
- เมื่อสร้างใหม่ ระบบตั้งชนิดเป็น Card Builder และเตรียมบล็อกเริ่มต้นให้ชุดหนึ่ง (รูปภาพหลัก ข้อความหัวเรื่อง และปุ่ม)
- เมื่อเปิดข้อมูลเดิม ระบบแยกเส้นทางการอ่านข้อมูลตามชนิดแม่แบบ โดยแม่แบบแบบมีโครงสร้างจะแปลงที่อยู่ของรูปภาพกลับเป็นไฟล์เพื่อให้แก้ไขต่อได้
- การเปลี่ยนชนิดแม่แบบจะเตือนก่อนเสมอ เพราะข้อมูลเดิมจะถูกแทนที่ด้วยค่าตั้งต้นของชนิดใหม่
- เนื้อหาตัวอย่างที่ระบบเตรียมไว้ให้เป็นภาษาอังกฤษโดยตั้งใจ ไม่ได้แปลตามภาษาที่ผู้ใช้เลือก มีเพียงชื่อของชนิดแม่แบบเท่านั้นที่แปล
3. แม่แบบแบบมีโครงสร้าง (Register, Shopping, Notification, Carousel)
- ระบบแสดงรายการการ์ดที่แก้ไขได้ โดยแม่แบบสามชนิดแรกมีใบเดียว ส่วน Carousel แสดงหัวการ์ดพร้อมปุ่มเลื่อนขึ้นลงและลบ และมีปุ่มเพิ่มการ์ดจนกว่าจะครบหกใบ
- ฟิลด์ที่แสดงเปลี่ยนตามชนิด
- หัวเรื่องแสดงในทุกชนิดยกเว้น Carousel
- คำอธิบายจำกัดความยาว 200 ตัวอักษร
- รูปภาพรับไฟล์ JPG, JPEG และ PNG ขนาดไม่เกิน 10 เมกะไบต์
- ไอคอนรับไฟล์ชนิดเดียวกันแต่ขนาดไม่เกิน 1 เมกะไบต์ และเป็นฟิลด์บังคับในแม่แบบที่มีไอคอน
- หากรูปหรือไอคอนยังเป็นภาพตัวอย่างที่ระบบเตรียมไว้ หน้าจอจะขึ้นข้อความแจ้งเตือนให้เปลี่ยนเป็นภาพจริง
- ปุ่มในการ์ด เลือกชนิดการกระทำได้สี่แบบ
- ส่งข้อความ — กรอกป้ายกำกับ (ไม่เกิน 50 ตัวอักษร) และข้อความที่จะส่ง
- เปิดลิงก์ — กรอกป้ายกำกับ (ไม่เกิน 100 ตัวอักษร) และ URL
- โทรออก — ใช้ช่องป้ายกำกับเก็บหมายเลขโทรศัพท์โดยตรง ไม่มีช่องป้ายกำกับแยก
- Postback — กรอกข้อมูลที่จะส่งกลับและข้อความที่แสดง
- ในแม่แบบ Carousel ช่องป้ายกำกับถูกซ่อนทั้งหมด เพราะการกระทำผูกกับรูปทั้งใบแทนที่จะเป็นปุ่ม
- จำนวนปุ่มต่อการ์ดถูกกำหนดตั้งแต่ค่าตั้งต้นของแม่แบบ ไม่มีปุ่มเพิ่มหรือลบปุ่มในหน้าจอ
4. แม่แบบ Custom JSON
- หน้าจอเป็นกล่องข้อความขนาดใหญ่สำหรับพิมพ์ Flex JSON พร้อมตัวช่วยแทรก merge tag ณ ตำแหน่งเคอร์เซอร์
- ระบบตรวจว่า JSON แปลงค่าได้จริงเมื่อผู้ใช้ออกจากช่อง และแสดงข้อความผิดพลาดหากไม่ผ่าน
- หน้าจอตัวอย่างจะอัปเดตเฉพาะเมื่อ JSON ถูกต้อง หาก JSON ยังไม่สมบูรณ์ ตัวอย่างจะคงค่าเดิมไว้โดยไม่แสดงข้อผิดพลาด
5. แม่แบบ Card Builder
Card Builder เป็นเครื่องมือประกอบการ์ดจากบล็อกย่อย เหมาะกับผู้ใช้ที่ต้องการอิสระในการจัดวางโดยไม่ต้องเขียน JSON
- นอกจากบล็อกแล้ว ยังต้องกรอกข้อความสำรอง (alt text) ที่จะปรากฏในรายการแชทและการแจ้งเตือน จำกัดความยาว 400 ตัวอักษร
- บล็อกที่เพิ่มได้มี 5 ชนิด
- รูปภาพหลัก — อัปโหลดรูป เลือกอัตราส่วน (หรือใช้อัตราส่วนจริงของรูป) เลือกวิธีจัดวางระหว่างเต็มกรอบแบบครอบตัด กับแสดงรูปทั้งใบ และกำหนดการกระทำเมื่อกดได้
- วิดีโอ — วาง URL ของไฟล์ MP4 โดยตรง (ไม่ได้อัปโหลดผ่าน CMS) พร้อมอัปโหลดภาพหน้าปก และเลือกอัตราส่วน หากบล็อกนี้ไม่ได้อยู่ตำแหน่งแรก ระบบจะเตือนว่าจะกลายเป็นภาพนิ่ง เพราะ LINE เล่นวิดีโอได้เฉพาะตำแหน่งบนสุดของการ์ด
- ข้อความ — ข้อความพร้อมตัวเลือกขนาด ความหนา การจัดวาง สีตัวอักษร และสีพื้นหลัง
- ปุ่ม — ป้ายกำกับ รูปแบบปุ่ม (ทึบ เส้นขอบ หรือข้อความล้วน) สี ความสูง สีพื้นหลัง และการกระทำ
- เส้นคั่น — เส้นคั่นพร้อมเลือกสี
- การกระทำต่อบล็อกเลือกได้สามแบบ คือ ไม่มีการกระทำ เปิดลิงก์ (ต้องเป็น URL แบบ HTTP หรือ HTTPS) และส่งข้อความ
- เรียงลำดับบล็อกได้ทั้งการลากวางและปุ่มลูกศรขึ้นลง และลบได้ทีละบล็อก
- รูปภาพในบล็อกถูกอัปโหลดขึ้นเซิร์ฟเวอร์ทันทีที่เลือก แล้วเก็บเฉพาะที่อยู่ของรูปไว้ ทำให้ตอนบันทึกส่งเป็น JSON ล้วนโดยไม่มีไฟล์แนบ
- การแปลงบล็อกเป็นบับเบิล ทำที่ฝั่งหน้าเว็บ ตามกฎดังนี้
- หากบล็อกแรกเป็นรูปภาพหรือวิดีโอ จะถูกยกขึ้นเป็นส่วนหัวของการ์ดแบบเต็มขอบ
- หากบล็อกแรกไม่ใช่รูปหรือวิดีโอ จะไม่ยกอะไรขึ้นเลย ทุกบล็อกอยู่ในเนื้อการ์ดตามลำดับที่ผู้ใช้จัดไว้ ทำให้รองรับกรณีที่ต้องการหัวเรื่องนำหน้ารูป
- บล็อกข้อความ ปุ่ม และเส้นคั่นที่ไม่ได้กำหนดสีพื้นหลัง จะถูกจัดกลุ่มไว้ในกรอบที่มีระยะขอบ เพื่อให้อ่านง่าย ส่วนบล็อกที่กำหนดสีพื้นหลังจะแสดงเต็มความกว้าง
- วิดีโอที่ไม่ได้อยู่ตำแหน่งบนสุดจะถูกลดรูปเป็นภาพหน้าปกแทน
- ตัวแปลงถูกออกแบบให้ยืดหยุ่น บล็อกที่ยังกรอกไม่ครบจะถูกข้ามไปโดยไม่ทำให้ทั้งกระบวนการล้มเหลว เพื่อให้หน้าจอตัวอย่างยังทำงานต่อได้
- เกณฑ์การเปิดปุ่มบันทึกแยกจากตัวแปลงและเข้มงวดกว่า คือต้องมีอย่างน้อยหนึ่งบล็อก ทุกบล็อกต้องกรอกครบ และต้องมีบล็อกที่แสดงเนื้อหาจริง ไม่ใช่มีเพียงเส้นคั่น
6. หน้าจอตัวอย่าง การตรวจสอบ และการบันทึก
- หน้าจอตัวอย่างอัปเดตอัตโนมัติแบบหน่วงเวลาเมื่อค่าในฟอร์มเปลี่ยน และแสดงผลด้วยตัวเรนเดอร์ Flex Message จริง
- เงื่อนไขการเปิดปุ่มบันทึกแตกต่างกันตามชนิดแม่แบบ
- Custom JSON — ต้องมีชื่อเทมเพลตและ JSON ที่ถูกต้อง
- Card Builder — ต้องมีชื่อเทมเพลตและผ่านเกณฑ์ของ Card Builder
- แม่แบบแบบมีโครงสร้าง — ต้องมีชื่อเทมเพลต มีการ์ดอย่างน้อยหนึ่งใบ ทุกใบต้องมีปุ่มอย่างน้อยหนึ่งปุ่ม กรอกฟิลด์บังคับของชนิดนั้นครบ และทุกปุ่มต้องผ่านกฎของชนิดการกระทำที่เลือก
- การสร้างใหม่จะบันทึกทันที ส่วนการแก้ไขจะเปิดกล่องยืนยันก่อน
- เมื่อบันทึก ระบบส่งข้อมูลต่างกันตามชนิด โดยแม่แบบแบบมีโครงสร้างส่งไฟล์รูปและข้อมูลของแต่ละการ์ด Custom JSON ส่ง JSON ที่พิมพ์ไว้ และ Card Builder ส่งทั้งโครงบล็อกและ Flex JSON ที่แปลงแล้วขึ้นไปพร้อมกัน
- หากเซิร์ฟเวอร์แจ้งข้อผิดพลาดรายฟิลด์ ระบบจะแสดงที่ฟิลด์นั้น มิฉะนั้นจะเปิดกล่องแจ้งข้อผิดพลาดที่รองรับรูปแบบข้อความหลายแบบ
หน้าจอและองค์ประกอบหลัก
หน้ารายการ
ตัวควบคุมรายการ (src/components/template-message/list/template-message.container.tsx) ดูแลตาราง ตัวกรอง การเรียงลำดับ และการลบ
หน้าฟอร์ม
หน้าฟอร์มแบ่งเป็นตัวควบคุมที่ดูแลการโหลดและแปลงข้อมูล การประกอบ JSON สำหรับหน้าจอตัวอย่าง กฎการตรวจสอบ และการบันทึก กับคอมโพเนนต์ฟอร์มขนาดใหญ่ที่รวมส่วนแสดงผลของทุกแม่แบบ การจัดการปุ่ม และการอัปโหลดไฟล์
Card Builder
Card Builder อยู่ในโฟลเดอร์ src/components/template-message/form/flex-card-builder/ แยกความรับผิดชอบเป็นสี่ไฟล์ที่ชัดเจน
- ส่วนติดต่อผู้ใช้สำหรับเพิ่ม แก้ไข เรียงลำดับ และลบบล็อก
- ตัวแปลงบล็อกเป็นบับเบิล ซึ่งเขียนเป็นฟังก์ชันบริสุทธิ์ที่ไม่พึ่งพา UI จึงทดสอบได้อิสระ และถูกใช้ร่วมกันทั้งในหน้าจอตัวอย่างและตอนบันทึก
- ตัวตรวจสอบเกณฑ์การบันทึก ซึ่งแยกจากตัวแปลงโดยเจตนา เพราะตัวแปลงต้องยืดหยุ่นแต่การบันทึกต้องเข้มงวด
- โรงงานสร้างบล็อกใหม่พร้อมค่าตั้งต้น
ตัวช่วย merge tag
ระบบมีตัวช่วยสองแบบทำงานร่วมกัน คือปุ่มเลือก merge tag จากรายการ ซึ่งโหลดข้อมูลครั้งแรกที่เปิดใช้ และช่องกรอกที่แสดงรายการแนะนำอัตโนมัติเมื่อผู้ใช้พิมพ์วงเล็บปีกกา โดยรายการที่โหลดมาถูกเก็บไว้ใช้ซ้ำ
ปลายทาง API
| การทำงาน | ปลายทาง |
|---|---|
| รายการเทมเพลต | GET /template-message |
| ข้อมูลเทมเพลตรายตัว | GET /template-message/{id} |
| รายการสำหรับ dropdown ของโมดูลอื่น | GET /template-message/find-all-object |
| รายการ merge tag | GET /template-message/merge-tags |
| สร้างเทมเพลต | POST /template-message |
| อัปโหลดรูปของ Card Builder | POST /template-message/upload-image |
| แก้ไขเทมเพลต | PUT /template-message/{id} |
| ลบเทมเพลต | DELETE /template-message/{id} |
ข้อจำกัดที่กำหนดไว้ตายตัว
Carousel เริ่มที่ 2 ใบและเพิ่มได้ถึง 6 ใบ, รูปภาพไม่เกิน 10 เมกะไบต์, ไอคอนไม่เกิน 1 เมกะไบต์, ชื่อเทมเพลตไม่เกิน 100 ตัวอักษร, คำอธิบายไม่เกิน 200 ตัวอักษร, ป้ายกำกับปุ่มส่งข้อความไม่เกิน 50 ตัวอักษร, ป้ายกำกับปุ่มลิงก์ไม่เกิน 100 ตัวอักษร และข้อความสำรองของ Card Builder ไม่เกิน 400 ตัวอักษร
จุดเชื่อมต่อกับฟีเจอร์อื่น
- จัดการ Rich Message — รายการเนื้อหาชนิดเทมเพลตดึงทั้งรายการและเนื้อหาจากโมดูลนี้
- กฎทริกเกอร์ การติดตามเพื่อน เวิร์กโฟลว์ และตัวสร้างฟอร์ม — ทั้งหมดเลือกเทมเพลตจากโมดูลนี้ไปใช้กำหนดข้อความที่จะส่ง
- การส่งจริง — เกิดขึ้นที่โมดูลแคมเปญและตอบกลับอัตโนมัติ ผ่าน Rich Message อีกทอดหนึ่ง
- ตอบกลับอัตโนมัติ — ช่องข้อความของปุ่มช่วยเติมคำจากคีย์เวิร์ดของโมดูลตอบกลับอัตโนมัติ
- ริชเมนู — ใช้ชุดค่าคงที่ของชนิดการกระทำร่วมกัน
- สัญญากับฐานข้อมูล — ระบบเก็บทั้งข้อมูลต้นทางที่ผู้ใช้กรอกและ Flex JSON ที่พร้อมส่งไว้คนละฟิลด์ โดยแม่แบบ Card Builder แปลงที่ฝั่งหน้าเว็บแล้วส่งทั้งสองก้อนขึ้นไปพร้อมกัน
- สิทธิ์การเข้าถึง — โมดูล
template-messageฝั่งเซิร์ฟเวอร์ปลดล็อกทั้งหน้ารายการและหน้าฟอร์มอย่างสอดคล้องกัน
รายละเอียดฝั่ง Backend (CMS API)
โควตาจำนวนเทมเพลต
จุดที่มองจากหน้าเว็บไม่เห็นเลยคือ จำนวนเทมเพลตที่สร้างได้ถูกจำกัดด้วยโควตาระดับองค์กร backend จะตรวจโควตาก่อนสร้างทุกครั้ง ถ้าเต็มแล้วจะปฏิเสธพร้อมข้อความว่าถึงเพดานแพ็กเกจ ค่าเริ่มต้นของแพลตฟอร์มคือ 20 เทมเพลต และผู้ดูแลระดับแพลตฟอร์มปรับค่านี้รายองค์กรได้จากหน้าตั้งค่าโมดูลของ organization
หน้าเว็บไม่ได้ตรวจโควตาไว้ล่วงหน้า ผู้ใช้จึงกรอกฟอร์มจนเสร็จแล้วจึงพบว่าบันทึกไม่ได้ ซึ่งควรทราบเมื่อช่วยผู้ใช้ไล่ปัญหา
merge tag มาจากไหน
GET /api/template-message/merge-tags ไม่ได้คืนรายการที่ hard-code ไว้ แต่ประกอบขึ้นจากสองแหล่ง
- tag ระบบ เช่น ชื่อผู้ใช้ และชื่อที่แสดงบน LINE
- tag จากแอตทริบิวต์ที่องค์กรกำหนดเอง ของ LINE OA นั้น ๆ ดังนั้นรายการที่เห็นในตัวช่วยเลือก merge tag จะต่างกันไปตาม channel และเปลี่ยนตามการตั้งค่าแอตทริบิวต์
พารามิเตอร์ showSystem เป็นตัวควบคุมว่าจะรวม tag ระบบเข้าไปด้วยหรือไม่
การแทนค่า merge tag ไม่ได้เกิดที่โมดูลนี้ — เทมเพลตเก็บข้อความที่ยังมี merge tag อยู่ตามเดิม การแทนค่าจริงเกิดตอนประกอบข้อความเพื่อส่ง ซึ่งอยู่ในบริการจัดการข้อความของ LINE ที่แคมเปญ ระบบตอบกลับอัตโนมัติ และเวิร์กโฟลว์เรียกใช้ร่วมกัน
กฎอื่นที่ backend ตรวจ
- ชื่อซ้ำถูกปฏิเสธ ภายในขอบเขตของ OA เดียวกัน
- ทั้งการสร้างและแก้ไขรับข้อมูลแบบ multipart เพื่อรองรับไฟล์รูปที่แนบมาพร้อมข้อมูลอื่น
POST /api/template-message/upload-imageเป็นเส้นทางแยกสำหรับรูปของ Card Builder และถูกวางไว้เป็นเส้นทางแบบคงที่โดยเจตนา เพื่อไม่ให้ถูกตีความเป็นรหัสเทมเพลตในการจับคู่เส้นทาง
สิทธิ์
- ทุกเส้นทางถูกครอบด้วย ModuleGate ของโมดูล
template-messageซึ่งบังคับใช้จริง — องค์กรที่ถูกปิดโมดูลนี้จะใช้งานไม่ได้ทั้งชุด - ข้อมูลกำกับสิทธิ์รายการกระทำมีอยู่แต่ยังไม่บังคับใช้
GET /api/template-message/find-all-objectที่โมดูลอื่นใช้ทำ dropdown ไม่มีข้อมูลกำกับสิทธิ์เลย ตามที่ระบบเวอร์ชันก่อนหน้าเป็น
ผลข้างเคียงและข้อควรรู้
- Redis cache — backend เก็บ cache ทั้งรายการและรายละเอียดเทมเพลต
- รูปของ Card Builder ถูกเก็บบน object storage สิ่งที่บันทึกในเทมเพลตคือที่อยู่ของรูป สอดคล้องกับที่หน้าเว็บอัปโหลดรูปทันทีที่เลือกแล้วส่งเฉพาะที่อยู่ตอนบันทึก
- backend ไม่ได้ตรวจสอบความถูกต้องของ Flex JSON ในโมดูลนี้ ต่างจาก Rich Message ที่ส่งไปให้ LINE ตรวจ ดังนั้น JSON ที่ผิดโครงสร้างอาจบันทึกผ่านที่นี่ แล้วไปพบปัญหาตอนถูกนำไปส่งจริง