package handler import ( "net/http" "strconv" "spider/internal/model" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // KeywordHandler handles managed keyword CRUD. type KeywordHandler struct { db *gorm.DB } // List returns keywords with optional filters and pagination. // GET /keywords?page=1&page_size=20&category=&status= func (h *KeywordHandler) List(c *gin.Context) { page, pageSize, offset := parsePage(c) query := h.db.Model(&model.ManagedKeyword{}) if category := c.Query("category"); category != "" { query = query.Where("category = ?", category) } if status := c.Query("status"); status != "" { query = query.Where("status = ?", status) } var total int64 if err := query.Count(&total).Error; err != nil { Fail(c, 500, err.Error()) return } var keywords []model.ManagedKeyword if err := query.Order("id DESC").Limit(pageSize).Offset(offset).Find(&keywords).Error; err != nil { Fail(c, 500, err.Error()) return } PageOK(c, keywords, total, page, pageSize) } // Create creates one or more keywords in batch. // POST /keywords body: {keywords:["k1","k2"], category:"机场"} func (h *KeywordHandler) Create(c *gin.Context) { var body struct { Keywords []string `json:"keywords" binding:"required,min=1"` Category string `json:"category"` } if err := c.ShouldBindJSON(&body); err != nil { Fail(c, http.StatusBadRequest, err.Error()) return } var created []model.ManagedKeyword for _, kw := range body.Keywords { if kw == "" { continue } k := model.ManagedKeyword{ Keyword: kw, Category: body.Category, Status: "active", } // Use FirstOrCreate to avoid duplicate errors. if err := h.db.Where(model.ManagedKeyword{Keyword: kw}).FirstOrCreate(&k).Error; err != nil { Fail(c, 500, err.Error()) return } created = append(created, k) } OK(c, created) } // Update modifies a keyword. // PUT /keywords/:id func (h *KeywordHandler) Update(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { Fail(c, http.StatusBadRequest, "invalid id") return } var body struct { Keyword string `json:"keyword"` Category string `json:"category"` Status string `json:"status"` } if err := c.ShouldBindJSON(&body); err != nil { Fail(c, http.StatusBadRequest, err.Error()) return } var kw model.ManagedKeyword if err := h.db.First(&kw, id).Error; err != nil { Fail(c, 404, "keyword not found") return } updates := map[string]interface{}{} if body.Keyword != "" { updates["keyword"] = body.Keyword } if body.Category != "" { updates["category"] = body.Category } if body.Status != "" { updates["status"] = body.Status } if err := h.db.Model(&kw).Updates(updates).Error; err != nil { Fail(c, 500, err.Error()) return } h.db.First(&kw, id) OK(c, kw) } // Delete removes a keyword by ID. // DELETE /keywords/:id func (h *KeywordHandler) Delete(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { Fail(c, http.StatusBadRequest, "invalid id") return } if err := h.db.Delete(&model.ManagedKeyword{}, id).Error; err != nil { Fail(c, 500, err.Error()) return } OK(c, nil) }