add tai lieu system
All checks were successful
Deploy on Master Change / deploy (push) Successful in 43s

This commit is contained in:
2026-07-01 14:47:46 +07:00
parent 6919513165
commit dd766206f7
15 changed files with 952 additions and 140 deletions

View File

@@ -66,6 +66,7 @@ func AutoMigrate(db *gorm.DB) error {
&models.StaffGitHubAuth{},
&models.StaffGitHubRepo{},
&models.SystemSetting{},
&models.GuideLink{},
); err != nil {
return err
}

View File

@@ -422,6 +422,11 @@ func CancelExamRoomHandler(db *gorm.DB) fiber.Handler {
// DELETE /api/exam-rooms/:id
func DeleteExamRoomHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
email, _ := c.Locals("staffEmail").(string)
if email != "phuocntb@rikkeiacademy.com" {
return c.Status(403).JSON(fiber.Map{"error": "Bạn không có quyền thực hiện chức năng này"})
}
id, err := parseUintParam(c, "id")
if err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid id"})

View File

@@ -0,0 +1,124 @@
package handlers
import (
"strconv"
"server/internal/models"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)
// Helper check email
func isSuperAdmin(c *fiber.Ctx) bool {
email, ok := c.Locals("staffEmail").(string)
return ok && email == "phuocntb@rikkeiacademy.com"
}
// GET /api/guide-links
func ListGuideLinksHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
var links []models.GuideLink
if err := db.Order("created_at desc").Find(&links).Error; err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Không thể tải danh sách tài liệu"})
}
return c.JSON(fiber.Map{"data": links})
}
}
// POST /api/guide-links
func CreateGuideLinkHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
if !isSuperAdmin(c) {
return c.Status(403).JSON(fiber.Map{"error": "Bạn không có quyền thực hiện chức năng này"})
}
type Request struct {
Title string `json:"title"`
URL string `json:"url"`
}
var req Request
if err := c.BodyParser(&req); err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})
}
if req.Title == "" || req.URL == "" {
return c.Status(400).JSON(fiber.Map{"error": "Tiêu đề và đường dẫn không được để trống"})
}
link := models.GuideLink{
Title: req.Title,
URL: req.URL,
}
if err := db.Create(&link).Error; err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Lưu tài liệu thất bại"})
}
return c.JSON(fiber.Map{"ok": true, "data": link})
}
}
// PUT /api/guide-links/:id
func UpdateGuideLinkHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
if !isSuperAdmin(c) {
return c.Status(403).JSON(fiber.Map{"error": "Bạn không có quyền thực hiện chức năng này"})
}
idParam := c.Params("id")
id, err := strconv.ParseUint(idParam, 10, 32)
if err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid ID"})
}
var link models.GuideLink
if err := db.First(&link, uint(id)).Error; err != nil {
return c.Status(404).JSON(fiber.Map{"error": "Không tìm thấy tài liệu"})
}
type Request struct {
Title string `json:"title"`
URL string `json:"url"`
}
var req Request
if err := c.BodyParser(&req); err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})
}
if req.Title == "" || req.URL == "" {
return c.Status(400).JSON(fiber.Map{"error": "Tiêu đề và đường dẫn không được để trống"})
}
link.Title = req.Title
link.URL = req.URL
if err := db.Save(&link).Error; err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Cập nhật tài liệu thất bại"})
}
return c.JSON(fiber.Map{"ok": true, "data": link})
}
}
// DELETE /api/guide-links/:id
func DeleteGuideLinkHandler(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
if !isSuperAdmin(c) {
return c.Status(403).JSON(fiber.Map{"error": "Bạn không có quyền thực hiện chức năng này"})
}
idParam := c.Params("id")
id, err := strconv.ParseUint(idParam, 10, 32)
if err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid ID"})
}
var link models.GuideLink
if err := db.First(&link, uint(id)).Error; err != nil {
return c.Status(404).JSON(fiber.Map{"error": "Không tìm thấy tài liệu"})
}
if err := db.Delete(&link).Error; err != nil {
return c.Status(500).JSON(fiber.Map{"error": "Xóa tài liệu thất bại"})
}
return c.JSON(fiber.Map{"ok": true})
}
}

View File

@@ -188,3 +188,15 @@ type SystemSetting struct {
func (SystemSetting) TableName() string { return "system_settings" }
// GuideLink đại diện cho tài liệu hướng dẫn Google Docs public
type GuideLink struct {
ID uint `gorm:"primaryKey" json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Title string `gorm:"column:title;size:512;not null" json:"title"`
URL string `gorm:"column:url;size:1024;not null" json:"url"`
}
func (GuideLink) TableName() string { return "guide_links" }

View File

@@ -202,6 +202,11 @@ func main() {
staff.Patch("/exam-rooms/:id/git-settings", handlers.UpdateExamRoomGitSettingsHandler(gormDB))
staff.Get("/exam-rooms/:id/submissions/:subId/download", handlers.DownloadExamSubmissionHandler(gormDB))
staff.Get("/guide-links", handlers.ListGuideLinksHandler(gormDB))
staff.Post("/guide-links", handlers.CreateGuideLinkHandler(gormDB))
staff.Put("/guide-links/:id", handlers.UpdateGuideLinkHandler(gormDB))
staff.Delete("/guide-links/:id", handlers.DeleteGuideLinkHandler(gormDB))
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()