Skip to main content

แอปกระดานข่าว (Bulletin)

ภาพรวม

แอปกระดานข่าว (Bulletin) เป็นหนึ่งใน "Apps" ซึ่งเป็นโมดูลเสริมที่ผู้ดูแลระดับแพลตฟอร์มเปิดหรือปิดให้แต่ละองค์กรได้ ไม่ได้ควบคุมด้วยระบบสิทธิ์ตามบทบาทเหมือนโมดูลอื่น

หน้าที่ของแอปคือทำ บอร์ดปิดประกาศแบบฟอรัม ให้สมาชิกของ LINE OA เข้าอ่าน โพสต์ และแสดงความคิดเห็นผ่านเว็บฝั่งลูกค้า โดยฝั่ง CMS ทำหน้าที่เป็น เครื่องมือกำกับดูแลเนื้อหา (moderation) ครบชุด

ผู้ใช้ฝั่ง CMS คือแอดมินหรือผู้กำกับดูแล ซึ่งทำงาน 7 อย่างผ่าน 7 แท็บ ได้แก่ ตรวจประกาศที่รออนุมัติ, ตรวจความคิดเห็น, ตัดสินรายงานจากผู้ใช้, จัดหมวดหมู่, บล็อกสมาชิก, ตั้งค่าบอร์ดพร้อมธีม และดูบันทึกการตรวจสอบย้อนหลัง

จุดที่ทำให้โมดูลนี้ต่างจากที่อื่นอย่างชัดเจน

  • มี ระบบสถานะตราประทับ ที่เป็นสัญญาร่วมข้ามโปรเจกต์ ไฟล์เดียวกันนี้ถูกทำสำเนาไปอยู่ที่เว็บฝั่งลูกค้าด้วย และมีชุดทดสอบในทั้งสองฝั่งที่รันกับข้อมูลทดสอบชุดเดียวกัน
  • มี ตัวแก้ธีมพร้อมด่านตรวจค่าความต่างของสี ตามมาตรฐาน WCAG ซึ่งเป็นภาพสะท้อนของตรรกะเดียวกันที่ฝั่งเซิร์ฟเวอร์
  • ตัวแสดงประกาศหนึ่งใบมี สองชุดโดยเจตนา เพราะ CMS กับเว็บฝั่งลูกค้าใช้เครื่องมือจัดสไตล์ต่างกัน

Business Flow

การเข้าถึงและการปรากฏของแอป

  1. เมนูด้านข้างเรียกทะเบียนแอปตอนโหลดหน้า แล้วเก็บเฉพาะแอปที่เปิดใช้งาน หากพบกระดานข่าวจะเพิ่มรายการเมนูย่อยพร้อม ป้ายตัวเลขจำนวนรายการที่รอตรวจ ซึ่งเป็นผลรวมของประกาศ ความคิดเห็น และรายงานที่ค้างอยู่
  2. ป้ายตัวเลขจะถูกดึงก็ต่อเมื่อทะเบียนแอปยืนยันว่าแอปเปิดอยู่เท่านั้น เพราะ endpoint นี้จะปฏิเสธคำขอเมื่อแอปถูกปิด และการยิงคำขอที่ถูกปฏิเสธทุกครั้งที่โหลดหน้าจะทำให้ log เต็มไปด้วยข้อมูลที่ไม่จริง
  3. ทุกหน้าใต้กลุ่ม /apps ใช้ตัวป้องกันเส้นทางร่วมกัน ซึ่งอ่านรหัสแอปจาก URL ตรวจกับทะเบียน แล้วพากลับหน้าแรกหากแอปถูกปิด และ อนุญาตให้ผ่านหากเรียกทะเบียนไม่สำเร็จ เพราะฝั่งเซิร์ฟเวอร์ยังคุมข้อมูลอยู่
  4. layout ของแอปวาดแท็บทั้ง 7 ตัว โดย 3 แท็บแรกมีป้ายตัวเลขของตัวเอง ส่วน breadcrumb และเมนูที่ active แต่ละหน้าตั้งเอง

ประกาศและคิวตรวจ

  1. หน้าประกาศโหลดการตั้งค่าบอร์ดครั้งเดียวเพื่อเก็บธีมที่ผ่านการประมวลผลแล้ว (ให้พรีวิววาดด้วยธีมจริงของผู้เช่า) และค่าจำกัดต่าง ๆ สำหรับส่งให้ตัวเขียนประกาศ พร้อมโหลดรายการหมวดหมู่
  2. ตัวเลือกสถานะแบ่งเป็น รอตรวจ / เผยแพร่แล้ว / ซ่อน / ลบแล้ว ประกอบกับตัวเลือกหมวดและช่องค้นหา
  3. เมื่อเลือกสถานะรอตรวจ ระบบจะบังคับดึงทั้งคิวมาในหน้าเดียว เพราะคิวตรวจต้องเห็นทั้งหมด ไม่ใช่ทีละ 20 รายการ และหน้าจอจะเปลี่ยนเป็นมุมมองคิวตรวจแทนมุมมองตาราง

มุมมองคิวตรวจ

  1. ฝั่งซ้ายเป็นรายการคิวที่แสดงเวลา หัวเรื่อง ผู้เขียน และบรรทัดแรกของเนื้อหา พร้อมป้ายสีแดงเมื่อมีรายงานเข้ามา
  2. ฝั่งขวาแสดงประกาศเต็มใบบนพื้นผิวที่ใช้ธีมจริงของบอร์ด โดยบังคับให้วาดใหม่ทุกครั้งที่เปลี่ยนรายการเพื่อให้ภาพเคลื่อนไหวของตราประทับเล่นซ้ำได้
  3. การเลือกรายการอิงตำแหน่ง ไม่ใช่รหัส หลังจากจัดการรายการหนึ่งเสร็จ รายการนั้นจะหายไปจากคิว และตำแหน่งเดิมจะกลายเป็นรายการถัดไปเองโดยอัตโนมัติ ทำให้คิวเดินหน้าต่อโดยไม่กระโดดกลับไปที่หัวรายการ
  4. มีปุ่มลัดคีย์บอร์ด สำหรับเลื่อนขึ้นลง อนุมัติ ซ่อน และลบ พร้อมเงื่อนไขป้องกันไม่ให้ทำงานขณะที่ผู้ใช้กำลังพิมพ์ในช่องกรอก มีหน้าต่างเปิดอยู่ หรือคิวว่าง
  5. การอนุมัติและการซ่อนไม่มีกล่องยืนยัน เพราะย้อนกลับได้ แต่ การลบมีกล่องยืนยัน เพราะระบบบันทึกการตรวจสอบระบุไว้ว่าสถานะที่ถูกลบแล้วกู้กลับไม่ได้
  6. คำสั่งอื่นที่ทำได้จากคิว ได้แก่ ปักหมุด, เปิดปิดการแสดงความคิดเห็น, แก้ไขประกาศ และบล็อกผู้เขียน โดยปุ่มบล็อกจะใช้งานไม่ได้เมื่อประกาศนั้นเขียนโดยแอดมินเอง

มุมมองตาราง

  1. คอลัมน์ประกอบด้วยลำดับ, ประกาศ (หัวเรื่องคลิกเพื่อแก้ไขได้ พร้อมเนื้อหาย่อ), ผู้เขียนพร้อมป้ายบอกประเภท, หมวด, สถานะเป็นป้ายสี, ตัวเลขการมีส่วนร่วม, วันที่สร้าง และปุ่มคำสั่ง
  2. ปุ่มคำสั่งมีเงื่อนไขชัดเจน คือปุ่มแก้ไขใช้ไม่ได้กับประกาศที่ลบแล้ว, ปุ่มปักหมุดใช้ได้เฉพาะประกาศที่เผยแพร่แล้ว และประกาศที่ซ่อนอยู่จะมีปุ่มนำกลับมาเผยแพร่แทนปุ่มซ่อน
  3. หน้านี้แบ่งหน้าที่ฝั่งเซิร์ฟเวอร์

หน้าต่างเขียนและแก้ประกาศทางการ

  1. หน้าต่างนี้ไม่มีช่องเลือกสถานะโดยเจตนา เพราะฝั่งเซิร์ฟเวอร์บังคับให้ประกาศที่แอดมินสร้างเป็นสถานะเผยแพร่เสมอ แม้บอร์ดจะเปิดโหมดต้องอนุมัติก่อนไว้ก็ตาม (เพราะแอดมินคือผู้อนุมัติเอง) และการแก้ของแอดมินจะไม่ทำให้ประกาศที่เผยแพร่แล้วถอยกลับไปรออนุมัติ ระบบจึงใช้ข้อความอธิบายแทนการใส่ control ที่ API ไม่สนใจ
  2. ค่าจำกัดมาจากการตั้งค่าของบอร์ด ไม่ใช่ค่าตายตัว ทั้งจำนวนรูปต่อประกาศและความยาวเนื้อหา ส่วนความยาวหัวเรื่องเป็นค่าคงที่ในโค้ดเพราะเป็นความกว้างของคอลัมน์ในฐานข้อมูล ไม่ควรให้ตั้งต่อบอร์ดได้เกินกว่าที่เก็บได้จริง
  3. รูปอัปโหลดผ่านบริการของโมดูลจัดการเนื้อหา เก็บเป็นรายการ URL และลบทีละใบได้
  4. เมื่อบันทึก ระบบจะส่งค่าหมวดเป็นค่าว่างอย่างชัดเจนหากผู้ใช้ล้างหมวดออก เพราะการละเว้นคีย์จะไม่ทำให้ค่าถูกเปลี่ยน

ความคิดเห็น

  1. หน้านี้เป็นคิวความคิดเห็นระดับบอร์ด แบ่งตามสถานะเดียวกับประกาศ
  2. คอลัมน์แสดงเนื้อความแบบย่อที่กางอ่านต่อได้, ผู้เขียน, ประกาศต้นทาง, จำนวนรายงาน, วันที่ และปุ่มคำสั่ง
  3. ชื่อประกาศต้นทางถูกโหลดแบบชะลอ โดยดึงเพียงคำขอเดียวต่อประกาศหนึ่งรายการที่ไม่ซ้ำกัน และเริ่มดึงเมื่อผู้ใช้เลื่อนเมาส์ไปหรือโฟกัสที่รายการนั้น พร้อมกลไกกันการยิงซ้ำ
  4. ปุ่มคำสั่งปรากฏตามสถานะปัจจุบัน โดยข้อความของปุ่มซ่อนจะเปลี่ยนเป็น "ปฏิเสธ" เมื่อความคิดเห็นนั้นยังรอตรวจอยู่
  5. ทุกคำสั่งที่สำเร็จจะโหลดรายการใหม่และรีเฟรชป้ายตัวเลขบนเมนู

รายงานจากผู้ใช้

  1. หน้านี้แบ่งตามสถานะ 3 แบบ คือเปิดอยู่ / ตัดสินแล้ว / ยกคำร้อง
  2. คอลัมน์แสดงเป้าหมายของรายงาน, เหตุผล, รายละเอียด, ผู้รายงานเป็น LINE user ID ดิบ, วันที่ และปุ่มคำสั่ง
  3. ผู้รายงานแสดงเป็นรหัสดิบเพราะ API ไม่ได้ผูกข้อมูลโปรไฟล์มาด้วย และการไปเดาชื่อจาก endpoint อื่นคือการแสดงข้อมูลของคนอื่นแบบคาดเดา
  4. รายงานที่เปิดอยู่มีปุ่มตัดสินและปุ่มยกคำร้อง ส่วนสถานะอื่นแสดงเป็นป้ายเท่านั้น เพราะการตัดสินย้อนกลับไม่ได้
  5. แถวขยายได้เพื่อดูเป้าหมายจริง โดยหากเป้าหมายเป็นประกาศจะแสดงประกาศเต็มใบพร้อมปุ่มซ่อนและลบ และหากเป็นความคิดเห็นจะแสดงรายละเอียดพร้อมปุ่มจัดการเช่นกัน หากโหลดเป้าหมายไม่ได้จะแจ้งว่าเป้าหมายหายไป
  6. การค้นหาความคิดเห็นตามรหัสเป็นวิธีอ้อมเพื่อชดเชยข้อจำกัดของ API เนื่องจากไม่มี endpoint สำหรับอ่านความคิดเห็นรายตัว ระบบจึงต้องไล่อ่านรายการความคิดเห็นทั้งบอร์ด โดยจำกัดไว้ไม่เกิน 5 หน้า เพราะการไล่อ่านแบบไม่จำกัดจะกลายเป็นการถล่ม API ของตัวเอง

หมวดหมู่

  1. หน้านี้โหลดรายการหมวดพร้อมการตั้งค่าบอร์ด เพราะต้องรู้สิทธิ์การดูระดับบอร์ดเพื่ออธิบายความหมายของการสืบทอดค่า
  2. หมวดมี 2 แกนที่แยกกันอย่างชัดเจน และหน้าจอออกแบบมาเพื่อไม่ให้ผู้ใช้อ่านรวมเป็นค่าเดียว
    • แกนการดู กำหนดว่าใครเห็นหมวดนี้ โดยเลือกได้ระหว่างสืบทอดจากบอร์ด, เปิดให้สมาชิกทุกคน หรือจำกัดตามกลุ่มผู้ชม
    • แกนการเขียน กำหนดว่าใครโพสต์เข้าหมวดนี้ได้ ระหว่างสมาชิกหรือเฉพาะแอดมิน โดยหมวดที่จำกัดให้แอดมินเขียนยังคงให้สมาชิกอ่านได้อยู่
  3. มีด่านตรวจสำคัญที่ฝั่งหน้าเว็บ คือถ้าผู้ใช้เลือกจำกัดตามกลุ่มผู้ชมแต่ไม่เลือกกลุ่มใดเลย ระบบจะไม่ยอมให้บันทึก เพราะการส่งค่าจำกัดพร้อมรายการกลุ่มที่ว่างเปล่ามีความหมายว่า "สมาชิกทุกคน" ไม่ใช่ "ไม่มีใคร"
  4. ฟอร์มหมวดประกอบด้วยชื่อ, สีประจำหมวด, ตัวเลือกสิทธิ์การดู, ตัวเลือกกลุ่มผู้ชม (แสดงเมื่อจำเป็น), ตัวเลือกสิทธิ์การเขียน, ลำดับ และสถานะ

สมาชิกที่ถูกบล็อก

  1. หน้านี้แสดงรายการการบล็อกทั้งหมด โดย API รับเพียงพารามิเตอร์แบ่งหน้า ไม่มีตัวกรองตามสถานะหรือขอบเขต
  2. คอลัมน์แสดง LINE user ID แบบดิบ (ด้วยเหตุผลเดียวกับหน้ารายงาน), ขอบเขตการบล็อก (ทั้งหมด หรือเฉพาะการแสดงความคิดเห็น), สถานะ, วันหมดอายุ, เหตุผล และวันที่บล็อก
  3. สถานะคำนวณจากข้อมูล 3 ส่วน ได้แก่ ถูกยกเลิกแล้ว, หมดอายุแล้ว หรือยังมีผลอยู่
  4. การยกเลิกบล็อกเป็นการเพิกถอนแบบไม่ลบข้อมูลจริง แม้ API จะตอบกลับด้วยข้อความว่าลบสำเร็จก็ตาม
  5. หน้าต่างบล็อกผู้ใช้ (ซึ่งใช้ร่วมกับคิวตรวจ) รับ LINE user ID, ขอบเขต, เหตุผล และวันหมดอายุ โดยมีหลักการสำคัญ 2 ข้อ คือ ไม่ต้องให้แอดมินยกเลิกการบล็อกเดิมก่อน เพราะฝั่งเซิร์ฟเวอร์จัดการให้ในธุรกรรมเดียวกัน การขยายขอบเขตจึงเป็นแค่การสร้างใหม่อีกครั้ง และ ไม่ส่งวันหมดอายุที่ว่าง เพราะการละเว้นค่าหมายถึงการบล็อกถาวรซึ่งเป็นค่าเริ่มต้นอยู่แล้ว

ตั้งค่าบอร์ด

  1. หน้านี้แบ่งเป็น 6 การ์ด ได้แก่ การแชร์ลิงก์, ข้อมูลบอร์ด, สิทธิ์การเข้าถึง, การกำกับดูแล, ค่าจำกัด และธีม
  2. การ์ดแชร์ลิงก์แสดง URL ของบอร์ดทั้งแบบเว็บและแบบ LIFF พร้อมปุ่มคัดลอก และแยกกรณีที่ยังไม่มี hash ออกจากกรณีที่มี hash แต่ไม่มี LIFF ID
  3. การ์ดสิทธิ์การเข้าถึงกำหนดได้แยกกันระหว่างสิทธิ์การดูและสิทธิ์การเขียน โดยแต่ละอันเลือกได้ว่าเปิดให้สมาชิกหรือผู้เยี่ยมชม
  4. การ์ดการกำกับดูแลมีสวิตช์ 3 ตัว คือต้องอนุมัติประกาศก่อน, ต้องอนุมัติความคิดเห็นก่อน และอนุญาตให้ผู้ใช้รายงานเนื้อหา
  5. การ์ดค่าจำกัดกำหนดจำนวนรูปต่อประกาศ, ความยาวเนื้อหา, ความยาวความคิดเห็น, ค่าเริ่มต้นของการเปิดความคิดเห็น และชุดอิโมจิที่ใช้แสดงปฏิกิริยา (จำกัดไม่เกิน 8 ตัว)
  6. สถานะธีมที่เก็บไว้คือชุดค่าที่แอดมินตั้งทับเท่านั้น ไม่ใช่ชุดค่าที่ผ่านการประมวลผลแล้ว เพราะบอร์ดที่สร้างใหม่ยังไม่มีค่าธีมใดเลย หากไปเริ่มต้นด้วยค่าที่ประมวลผลแล้ว การบันทึกครั้งแรกจะเขียนค่าทับทั้งชุดทันทีและทำให้บอร์ดนั้นหลุดออกจากลำดับชั้นของธีมสำเร็จรูปตลอดไป
  7. ก่อนบันทึก ระบบตรวจค่าความต่างของสีที่ฝั่งหน้าเว็บก่อน หากพบปัญหาจะแจ้งทันทีโดยไม่เสียเวลาเดินทางไปกลับเซิร์ฟเวอร์ ทั้งนี้ฝั่งเซิร์ฟเวอร์ตรวจเงื่อนไขชุดเดียวกันและยังเป็นผู้ตัดสินสุดท้าย การตรวจฝั่งหน้าเว็บเป็นเพียงทางลัดเพื่อความรวดเร็ว

ตัวแก้ธีมและด่านตรวจค่าความต่างของสี

  1. ค่าที่เก็บคือชุดค่าที่ตั้งทับ (patch) ไม่ใช่ชุดค่าทั้งหมด การล้างค่าขั้นสูงจะถูกตั้งเป็นค่าว่างซึ่งฝั่งเซิร์ฟเวอร์อ่านว่า "ทิ้งค่าที่ตั้งทับนี้" แล้วตกกลับไปใช้สีเน้น ไม่ใช่ค่าว่างเปล่า
  2. ลำดับการทับค่ามี 3 ชั้น จากชุดสำเร็จรูป ทับด้วยสีเน้นที่ตั้งเอง แล้วทับด้วยค่าเฉพาะแต่ละโทเคน โดยมีชุดสำเร็จรูป 4 แบบ แต่ละแบบกำหนดโทเคนสี 6 ค่าไว้ นอกจากนี้ยังปรับความมนของมุมและความหนาแน่นของการจัดวางได้
  3. ด่านตรวจค่าความต่างของสี ตรวจคู่สีที่เป็นข้อความบนพื้น 3 คู่ ให้ผ่านเกณฑ์อัตราส่วนอย่างน้อย 4.5 ต่อ 1 ตามมาตรฐาน WCAG
  4. มีคู่สีหนึ่งที่ไม่ถูกใส่ในรายการตรวจโดยเจตนา เพราะหากใส่ ชุดสีเริ่มต้นของตัวผลิตภัณฑ์เองจะไม่ผ่าน และด่านตรวจที่ปฏิเสธค่าเริ่มต้นของตัวเองคือด่านที่ผู้ใช้จะเรียนรู้ที่จะหลีกเลี่ยง
  5. ด่านตรวจรับค่าที่ผ่านการประมวลผลแล้ว ไม่ใช่ค่าดิบที่ตั้งทับ เพราะถ้าตัดสินจากค่าที่ตั้งทับล้วน ๆ ด่านจะทำงานเฉพาะเมื่อผู้ใช้ตั้งครบทั้งสองฝั่งของคู่สี ทำให้การตั้งเพียงฝั่งเดียวหลุดผ่านไปแสดงผลที่ค่าความต่างต่ำกว่าเกณฑ์
  6. พื้นผิวของบอร์ดเป็นจุดเดียวที่สีของผู้เช่าไปสัมผัส DOM โดยเขียนเป็นตัวแปร CSS บนกล่องครอบ และยังตรวจรูปแบบของค่าสีซ้ำอีกครั้งแม้เซิร์ฟเวอร์จะตรวจมาแล้ว
  7. พรีวิวในกรอบมือถือแสดงประกาศตัวอย่าง 3 ใบที่เลือกมาให้เห็นทุกส่วนของชุดสีพร้อมกัน ทั้งประกาศทางการที่ปักหมุด ประกาศทั่วไป และประกาศที่รอตรวจ

บันทึกการตรวจสอบย้อนหลัง

  1. หน้านี้มีตัวกรอง 3 ตัว คือประเภทเป้าหมาย (เลือกจากชุดปิด 6 ค่า เพราะค่านอกชุดจะถูกปฏิเสธ ไม่ใช่ได้หน้าว่าง), รหัสเป้าหมาย และประเภทการกระทำ (เลือกจากรายการ 22 รายการ)
  2. รายการการกระทำครอบคลุมทุกอย่างที่ทำได้กับประกาศ ความคิดเห็น รายงาน การบล็อก หมวด และการตั้งค่า โดย ไม่มีรายการแก้ไขความคิดเห็นโดยเจตนา
  3. ป้ายสีของการกระทำถูกกำหนดจากส่วนท้ายของชื่อการกระทำ เช่น การลบและการปฏิเสธเป็นสีแดง การอนุมัติและการสร้างเป็นสีเขียว การซ่อนและการเพิกถอนเป็นสีส้ม
  4. คอลัมน์ผู้ทำใช้ชื่อที่บันทึกไว้ ณ ขณะทำ ไม่ใช่การผูกข้อมูลสด เพราะการเปลี่ยนชื่อในภายหลังต้องไม่ไปเขียนทับประวัติ
  5. แถวขยายได้เพื่อดูค่าก่อนและหลังการเปลี่ยนแปลง โดย ค่าทั้งสองฝั่งถูกแสดงแยกกันเสมอและไม่เคยจับคู่ตามคีย์ เพราะค่าก่อนหน้าใช้รูปแบบชื่อฟิลด์ของ entity แต่ค่าหลังในกรณีที่แก้บางส่วนถูกสร้างจากรายการอัปเดตของ ORM จึงใช้รูปแบบชื่อคอลัมน์ หากจับคู่ตามคีย์จะแสดงผลราวกับว่าฟิลด์ถูกเปลี่ยนเป็นค่าว่าง ทั้งที่เป็นเพียงการสะกดที่ต่างกัน
  6. การจัดวางแบบแนวตั้งของบล็อกนี้เป็นสิ่งจำเป็นเชิงการใช้งาน ไม่ใช่การตกแต่ง เพราะการจัดแบบแนวนอนทำให้คอลัมน์ค่าแคบจนข้อความภาษาไทยและ URL ตัดบรรทัดทีละหนึ่งถึงสองตัวอักษร ซึ่งเป็นต้นเหตุจริงของปัญหาการแสดงผลซ้อนทับที่เคยถูกรายงานมา

ระบบสถานะตราประทับ

ระบบนี้ตัดสินว่าประกาศหนึ่งใบควรแสดงตราประทับแบบใดจากสถานะของประกาศ มุมมองของผู้ดู และประเภทของผู้เขียน

  1. ประกาศที่รอตรวจจะไม่ปรากฏต่อผู้อื่นเลย แต่ผู้เขียนเห็นของตัวเองว่ารอตรวจอยู่ และแอดมินเห็นในคิว
  2. ประกาศที่ถูกซ่อนหรือถูกลบมีเพียงแอดมินเท่านั้นที่เห็นสถานะนั้น
  3. ประกาศที่เผยแพร่แล้วและเขียนโดยแอดมินจะได้ตราประทับว่าเป็นประกาศทางการ
  4. สถานะการกำกับดูแลมีน้ำหนักเหนือประเภทของผู้เขียน ประกาศทางการที่ถูกซ่อนจะแสดงตราว่าถูกซ่อน ไม่ใช่ตราประกาศทางการ เพราะคำถามว่า "ประกาศนี้ยังแสดงอยู่หรือไม่" เร่งด่วนกว่า และการสลับลำดับจะทำให้ประกาศที่ถูกระงับยังคงแบกอำนาจขององค์กรอยู่
  5. ตัวตราประทับมีคำอธิบายสำหรับโปรแกรมอ่านหน้าจอ เพราะการหมุนและสีเป็นตัวสื่อความหมายเพียงอย่างเดียวและทั้งคู่ไม่รอดถึงผู้ใช้ที่ใช้โปรแกรมอ่านหน้าจอ ทั้งนี้ ตราประทับนี้ไม่ใช่เครื่องหมายรับรองตัวตน

หน้าจอและองค์ประกอบหลัก

ตัวป้องกันเส้นทางร่วม (src/app/apps/layout.tsx) และ layout ของแอป (src/app/apps/bulletin/layout.tsx) — ตรวจการเปิดใช้งานแอปและวาดแท็บทั้ง 7 พร้อมป้ายตัวเลข

หน้าตามแท็บ (src/app/apps/bulletin/*/page.tsx) — ประกาศ, ความคิดเห็น, รายงาน, หมวด, สมาชิกที่ถูกบล็อก, ตั้งค่า และบันทึกการตรวจสอบ โดยหน้าประกาศแยกเป็นมุมมองคิวตรวจกับมุมมองตาราง และมีหน้าต่างเขียนประกาศของตัวเอง

คอมโพเนนต์ร่วมของแอป — ตัวแสดงประกาศหนึ่งใบ, พื้นผิวบอร์ดที่ถือค่าสีของผู้เช่า, ตราประทับ และหน้าต่างบล็อกผู้ใช้

ไลบรารีภายในของแอป — ระบบสถานะตราประทับ (lib/stamp.ts ซึ่งห้ามพึ่งพาสิ่งใดเลยเพราะเป็นสำเนาตรงตัวกับเว็บฝั่งลูกค้า), ตัวประมวลผลธีมกับด่านตรวจค่าความต่างของสี (lib/theme.ts), ตัวนับรายการรอตรวจ, ตัวประกอบลิงก์บอร์ด, ตัวค้นหาความคิดเห็นตามรหัส, ตัวจัดการคลิปบอร์ด, ตัวจัดรูปแบบข้อความ และชุดคำภาษาไทยที่ใช้ในพรีวิว

เซอร์วิสsrc/services/bulletin.service.ts ครอบคลุมทุกคำสั่งของแอปพร้อมตัวแปลข้อผิดพลาดกลาง ซึ่งรองรับรูปแบบข้อผิดพลาด 3 แบบ ได้แก่ ข้อผิดพลาดการตรวจสอบที่ส่งข้อความมาเป็นรายการ, ข้อผิดพลาดเพดานการปักหมุดที่ต้องแปลงรหัสเป็นประโยคบอกวิธีแก้ และข้อผิดพลาดทั่วไปที่อาจไม่มีข้อความมาด้วย

จุดเชื่อมต่อกับฟีเจอร์อื่น

  • ทะเบียนแอปและผู้ดูแลระดับแพลตฟอร์ม — เป็นตัวกำหนดว่าแอปนี้จะปรากฏหรือไม่ CMS อ่านได้อย่างเดียว
  • สิทธิ์การเข้าถึง — มีการประกาศ subject ไว้แต่ไม่มีการแม็ปจากฝั่งหลังบ้าน จึงถูกใช้เป็นเพียงคีย์ของเมนูและ breadcrumb การควบคุมจริงอยู่ที่ทะเบียนแอปและตัวป้องกันฝั่ง API
  • เว็บฝั่งลูกค้า — เป็นที่ตั้งของบอร์ดสำหรับสมาชิก และ แชร์ไฟล์เชิงสัญญา 2 ไฟล์ กับ CMS ได้แก่ ระบบสถานะตราประทับ (ทำสำเนาตรงตัวและใช้ข้อมูลทดสอบชุดเดียวกัน) และชุดคำที่แสดงในพรีวิว (ต้องเทียบกันได้ทั้งคีย์และคำ)
  • API ฝั่ง Go — เป็นแหล่งอ้างอิงหลักของโครงสร้างข้อมูลและตรรกะการประมวลผลธีม โดยไฟล์ธีมฝั่งหน้าเว็บเป็นภาพสะท้อนที่ถูกตรึงไว้ด้วยชุดทดสอบ
  • กลุ่มผู้ชม (Audience) — ใช้จำกัดการมองเห็นของหมวดหมู่
  • จัดการเนื้อหา (Content Management) — แอปยืมบริการอัปโหลดรูปของโมดูลนี้มาใช้
  • LIFF และคอนโซลผู้พัฒนา LINE — URL ปลายทางของ LIFF ถูกตรึงไว้ที่รากของเว็บฝั่งลูกค้า ลิงก์บอร์ดจึงต้องต่อ path เพิ่มท้าย มิฉะนั้นจะเปิดไปที่หน้าแรกแทนที่จะเป็นบอร์ด
  • ข้อจำกัดของ API ที่ต้องแก้ทางที่ฝั่งหน้าเว็บ — ไม่มี endpoint อ่านความคิดเห็นรายตัว, รายการการบล็อกไม่มีตัวกรอง และทั้งรายการการบล็อกกับรายการรายงานไม่ได้ผูกข้อมูลโปรไฟล์ LINE มาด้วย

รายละเอียดฝั่ง Backend (CMS API)

โมดูลนี้อยู่ที่ internal/modules/bulletin/ ทุก endpoint อยู่ใต้ /api/apps/bulletin/ ต่างจากโมดูลอื่นตรงที่ ไม่ได้ยกมาจากระบบเดิม แต่เขียนใหม่ตาม design spec จึงมีกติกาด้านความปลอดภัยของข้อมูลที่ชัดเจนและถูกบังคับทั่วทั้งโมดูล

กติกาความปลอดภัยของข้อมูล 6 ข้อ

กติกาชุดนี้ระบุไว้ในโค้ดทุกไฟล์ของโมดูล และอธิบายพฤติกรรมหลายอย่างที่หน้าจอเห็น

  1. ทุกคำสั่งฐานข้อมูลผ่านตัวช่วยที่บังคับขอบเขตให้อัตโนมัติ โดยบังคับทั้งรหัสองค์กรและรหัส LINE OA พร้อมกัน ไม่ใช่แค่อย่างใดอย่างหนึ่ง ทำให้ไม่มีทางเผลอเขียน query ที่ข้ามขอบเขตองค์กรได้
  2. รหัสที่ผู้เรียกส่งมาถูกตรวจ "ความเป็นเจ้าของ" ไม่ใช่แค่ว่ามีอยู่จริง และเมื่อไม่พบจะตอบ 404 ไม่ใช่ 403 โดยตั้งใจ เพราะการตอบ 403 เท่ากับยืนยันว่าแถวนั้นมีอยู่จริงในองค์กรอื่น ซึ่งเป็นการรั่วข้อมูล
  3. ทุกการเปลี่ยนแปลงข้อมูลกับการเขียน audit log อยู่ในธุรกรรมเดียวกัน จึงไม่มีกรณีที่การกระทำสำเร็จแต่ประวัติหาย หรือกลับกัน
  4. โค้ดห้ามเขียนคอลัมน์ตัวนับ (จำนวนความคิดเห็น จำนวนรีแอ็กชัน จำนวนรายงาน) เพราะ trigger ของฐานข้อมูลเป็นเจ้าของตัวเลขเหล่านี้ ตัวเลขที่หน้าจอเห็นจึงมาจากฐานข้อมูลโดยตรงและไม่มีทางเพี้ยนจากการเขียนซ้ำซ้อน
  5. ห้ามแก้โพสต์กับความคิดเห็นในธุรกรรมเดียวกัน เพราะ trigger จับ advisory lock แยกตามชนิดเป้าหมาย การสลับลำดับการล็อกจะทำให้เกิด deadlock
  6. การลบคือการเปลี่ยนสถานะเป็น "ลบแล้ว" พร้อมประทับเวลาเสมอ ไม่มีการลบจริง ข้อมูลจึงยังตรวจสอบย้อนหลังได้

สิทธิ์ที่ต้องมี

  • ทุก route ถูกครอบด้วย AppEnabledGuardองค์กรต้องถูกเปิดแอปนี้ให้ก่อน มิฉะนั้นได้ 403 และการเปิดแอปเป็นอำนาจของผู้ดูแลระดับแพลตฟอร์มเท่านั้น
  • ลำดับการตรวจคือ ตรวจว่าแอปเปิดอยู่ไหม แล้วจึงตรวจ policy แล้วจึงเข้า handler
  • policy metadata ประกาศไว้เป็นโมดูล line-oa แยกตามการกระทำ แต่ตัวที่มีผลจริงในทางปฏิบัติคือการตรวจว่าแอปถูกเปิดหรือไม่ ซึ่งสอดคล้องกับที่ฝั่งหน้าเว็บใช้ subject เป็นเพียงคีย์ของเมนู

พฤติกรรมของคิวงานดูแลเนื้อหา

  • GET /api/apps/bulletin/pending-count มีไว้ให้เมนูแสดงตัวเลขค้างอนุมัติโดยเฉพาะ จึงเรียกได้ถี่กว่าการโหลดคิวจริง
  • แถวรายงานเนื้อหาจะไม่ถูกลบ แม้จะปัดตกไปแล้ว เพราะมีดัชนีที่ใช้กันไม่ให้คนเดิมรายงานเนื้อหาชิ้นเดิมซ้ำ ถ้าลบแถวทิ้ง คนเดิมจะรายงานซ้ำได้เรื่อย ๆ การ "จัดการแล้ว" จึงเป็นการเปลี่ยนสถานะของรายงาน
  • การยกเลิกการบล็อก (DELETE /api/apps/bulletin/blocks/:id) เป็นการเพิกถอน ไม่ใช่การลบแถว ประวัติว่าเคยบล็อกใครไว้ยังคงอยู่
  • GET /api/apps/bulletin/audit-log เป็น อ่านอย่างเดียว ไม่มี endpoint ใดให้แก้หรือลบประวัติการกระทำของทีมงาน

ตารางและกลไกฐานข้อมูลที่เกี่ยวข้อง

ตารางในกลุ่ม bulletin (กระดาน, โพสต์, ความคิดเห็น, การมีส่วนร่วม, รายงาน, การบล็อก และ audit log) โดยมี trigger ของฐานข้อมูลเป็นเจ้าของคอลัมน์ตัวนับและเป็นผู้จับ advisory lock ซึ่งเป็นเหตุผลว่าทำไมกติกาข้อ 4 และ 5 จึงเข้มงวด