Skip to main content

ระบบ Apps เสริม

ภาพรวม

"Apps" คือฟีเจอร์เสริมที่ขายเป็นชิ้นให้ลูกค้าเปิดใช้เพิ่มจากระบบหลัก ปัจจุบันมีให้เลือกสามตัว

idชื่อคำอธิบายRoute
appointmentAppointmentจองคิวและนัดหมาย/apps/appointment
loyaltyLoyaltyบัตรสะสมแต้มและแลกรางวัล/apps/loyalty
bulletinBulletin Boardกระดานประกาศ/apps/bulletin

โมดูลนี้ดูแลสองเรื่อง เรื่องแรกคือทะเบียนแอป (appRegistry ที่ hardcode ไว้ในโค้ด) พร้อมสถานะเปิดหรือปิดต่อองค์กร และเรื่องที่สองคือ AppEnabledGuard ซึ่งทุก route ของแอปต้องผ่าน

จุดออกแบบที่สำคัญคือ การเปิดหรือปิดแอปเป็น อำนาจของ platform admin เท่านั้น เพราะถ้าองค์กรเปิดแอปให้ตัวเองได้ การกำกับดูแลเชิงพาณิชย์ก็จะไร้ความหมาย จึงบังคับด้วย modulegate.PlatformOnly

Business Flow

  1. แสดงเมนู — หลังล็อกอิน cms-web เรียก GET /api/apps เพื่อ render เมนู sidebar
    • endpoint นี้ใช้ JwtLoginAuth แทน global guard เพราะในจังหวะนั้น token ยังไม่มี lineOaId หากใช้ guard ที่เข้มกว่าจะได้ 401 แล้ว interceptor ฝั่งเว็บจะเด้งผู้ใช้ออกจากระบบ
    • service อ่านตาราง line_oa_app ตาม organizationId แล้ว merge กับ appRegistry ก่อนคืนข้อมูลแอปพร้อม field id, name, description, icon, route และ enabled
  2. เปิดหรือปิดแอป — platform admin เรียก PUT /api/apps/:appId พร้อม body ที่มี field enabled เป็น boolean
    • ผ่าน modulegate.PlatformOnly(d) หากผู้เรียกเป็น customer หรือระบุตัวตนไม่ได้จะได้ 403
    • guard ตัวนี้ fail closed ต่างจาก ModuleGate ที่ fail open เพราะความเสี่ยงในกรณีนี้คือ privilege escalation
  3. ตรวจสิทธิ์ทุก request ของแอป — request ที่ path ตรงกับรูปแบบ /apps/ ตามด้วยชื่อแอปจะผ่าน AppEnabledGuard
    • guard ดึง app id ออกจาก path ด้วย regular expression
    • หาก path ไม่ตรงรูปแบบจะปล่อยผ่าน
    • หากตรงรูปแบบแต่ IsAppEnabled(orgId, appId) คืนค่า false จะตอบ 403 พร้อมข้อความว่าแอปนั้นยังไม่ถูกเปิดให้องค์กรนี้
    • หาก organizationId หายไปจาก CLS จะกลายเป็น 0 ทำให้หาแถวไม่พบและถือว่าแอปปิดอยู่
  4. เข้าใช้งาน — เมื่อแอปถูกเปิดแล้ว ผู้ใช้จึงเข้าถึง route ของแอปนั้นได้ (ดู แอปจองคิว/นัดหมาย, แอปสะสมแต้ม และ แอปกระดานประกาศ)

ไฟล์และฟังก์ชันหลัก

โค้ดอยู่ที่ internal/modules/apps/ ประกอบด้วย controller.go, service.go, service_appointment.go, guard.go และ dto.go

MethodRouteHandlerGuard / Policy
GET/api/appsct.listAppsJwtLoginAuth (group public) + readAll friend-track
PUT/api/apps/:appIdct.toggleAppgroup authed + modulegate.PlatformOnly(d) + update friend-track

หมายเหตุ policy metadata ใช้ PolicyModuleFriendTrack ซึ่งตรงกับระบบ TypeScript เดิม ไม่ใช่ความผิดพลาดจากการ port

ฟังก์ชันและโครงสร้างที่สำคัญ

  • appRegistry ใน service.go เป็นทะเบียนแอปแบบ hardcode โดยลำดับ field ตรงกับ object literal เดิม
  • AppsService.ListApps(ctx) และ AppsService.IsAppEnabled(ctx, orgID, appID)
  • apps.AppEnabledGuard(d) ถูก export ออกมาเพื่อให้โมดูล bulletin และ loyalty ซึ่งอยู่คนละ package เรียกใช้ได้
  • ตัวจับ path ของแอป
appPathRe = regexp.MustCompile(`/apps/(\w+)`)

จุดเชื่อมต่อกับ Service อื่น

  • สิทธิ์การเข้าถึงPlatformOnly (fail closed) บน endpoint toggle, AppEnabledGuard บนทุก route ของแอป และ JwtLoginAuth บน endpoint list
  • ตารางที่เกี่ยวข้องline_oa_app (แถวที่บันทึกการเปิดแอปต่อองค์กร), organization และ user
  • CLSorganizationId และ selfId
  • เอกสารออกแบบdocs/superpowers/specs/2026-07-26-platform-admin-design.md
  • โมดูลที่เกี่ยวข้อง — Permission & Module Gate, Organization Module Setting, แอปจองคิว/นัดหมาย, แอปสะสมแต้ม และ แอปกระดานประกาศ