package controller import ( "encoding/json" "net/http" "strconv" ezops "ezops/model" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // CustomDashboardHandler 自定义监控仪表盘 handler // Logan.Li type CustomDashboardHandler struct { db *gorm.DB } // NewCustomDashboardHandler 创建 handler func NewCustomDashboardHandler(db *gorm.DB) *CustomDashboardHandler { return &CustomDashboardHandler{db: db} } // ============================================ // DTO // ============================================ type CreateDashboardReq struct { Name string `json:"name" binding:"required"` Description string `json:"description"` Datasource string `json:"datasource"` DatasourceURL string `json:"datasource_url"` Panels string `json:"panels"` // JSON string IsDefault bool `json:"is_default"` } type UpdateDashboardReq struct { Name *string `json:"name"` Description *string `json:"description"` Datasource *string `json:"datasource"` DatasourceURL *string `json:"datasource_url"` Panels *string `json:"panels"` IsDefault *bool `json:"is_default"` IsFavorite *bool `json:"is_favorite"` } // ============================================ // CRUD // ============================================ // List 仪表盘列表 // GET /api/v1/custom-dashboards func (h *CustomDashboardHandler) List(c *gin.Context) { var dashboards []ezops.CustomDashboard if err := h.db.Order("is_default DESC, updated_at DESC").Find(&dashboards).Error; err != nil { c.JSON(http.StatusInternalServerError, ezops.Response{Code: 500, Message: "查询失败: " + err.Error()}) return } // 转换为前端期望的格式(id 转为 string,panels JSON 解析为数组) result := make([]gin.H, 0, len(dashboards)) for _, d := range dashboards { item := gin.H{ "id": strconv.Itoa(int(d.ID)), "name": d.Name, "description": d.Description, "datasource": d.Datasource, "datasourceUrl": d.DatasourceURL, "datasource_url": d.DatasourceURL, "createdAt": d.CreatedAt, "updatedAt": d.UpdatedAt, "createdBy": d.CreatedBy, "isDefault": d.IsDefault, "is_default": d.IsDefault, } // 解析 panels JSON var panels []interface{} if d.Panels != "" { if err := json.Unmarshal([]byte(d.Panels), &panels); err == nil { item["panels"] = panels } else { item["panels"] = []interface{}{} } } else { item["panels"] = []interface{}{} } result = append(result, item) } c.JSON(http.StatusOK, ezops.Response{Code: 200, Data: gin.H{ "list": result, "total": len(result), }}) } // Get 获取单个仪表盘 // GET /api/v1/custom-dashboards/:id func (h *CustomDashboardHandler) Get(c *gin.Context) { id := c.Param("id") var d ezops.CustomDashboard if err := h.db.First(&d, id).Error; err != nil { c.JSON(http.StatusNotFound, ezops.Response{Code: 404, Message: "仪表盘不存在"}) return } var panels []interface{} if d.Panels != "" { json.Unmarshal([]byte(d.Panels), &panels) } if panels == nil { panels = []interface{}{} } c.JSON(http.StatusOK, ezops.Response{Code: 200, Data: gin.H{ "id": strconv.Itoa(int(d.ID)), "name": d.Name, "description": d.Description, "datasource": d.Datasource, "datasourceUrl": d.DatasourceURL, "datasource_url": d.DatasourceURL, "panels": panels, "createdAt": d.CreatedAt, "updatedAt": d.UpdatedAt, "createdBy": d.CreatedBy, "isDefault": d.IsDefault, "is_default": d.IsDefault, }}) } // Create 创建仪表盘 // POST /api/v1/custom-dashboards func (h *CustomDashboardHandler) Create(c *gin.Context) { var req CreateDashboardReq if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, ezops.Response{Code: 400, Message: "参数错误: " + err.Error()}) return } if req.Datasource == "" { req.Datasource = "prometheus" } if req.Panels == "" { req.Panels = "[]" } // 如果设为默认,先清除其他默认 if req.IsDefault { h.db.Model(&ezops.CustomDashboard{}).Where("is_default = ?", true).Update("is_default", false) } // 从 JWT 获取用户名 username := "admin" if u, exists := c.Get("username"); exists { if s, ok := u.(string); ok { username = s } } d := ezops.CustomDashboard{ Name: req.Name, Description: req.Description, Datasource: req.Datasource, DatasourceURL: req.DatasourceURL, Panels: req.Panels, CreatedBy: username, IsDefault: req.IsDefault, } if err := h.db.Create(&d).Error; err != nil { c.JSON(http.StatusInternalServerError, ezops.Response{Code: 500, Message: "创建失败: " + err.Error()}) return } c.JSON(http.StatusOK, ezops.Response{Code: 200, Message: "创建成功", Data: gin.H{ "success": true, "id": strconv.Itoa(int(d.ID)), }}) } // Update 更新仪表盘 // PUT /api/v1/custom-dashboards/:id func (h *CustomDashboardHandler) Update(c *gin.Context) { id := c.Param("id") var d ezops.CustomDashboard if err := h.db.First(&d, id).Error; err != nil { c.JSON(http.StatusNotFound, ezops.Response{Code: 404, Message: "仪表盘不存在"}) return } var req UpdateDashboardReq if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, ezops.Response{Code: 400, Message: "参数错误: " + err.Error()}) return } updates := map[string]interface{}{} if req.Name != nil { updates["name"] = *req.Name } if req.Description != nil { updates["description"] = *req.Description } if req.Datasource != nil { updates["datasource"] = *req.Datasource } if req.DatasourceURL != nil { updates["datasource_url"] = *req.DatasourceURL } if req.Panels != nil { updates["panels"] = *req.Panels } if req.IsFavorite != nil { updates["is_favorite"] = *req.IsFavorite } if req.IsDefault != nil && *req.IsDefault { // 设为默认前先清除其他默认 h.db.Model(&ezops.CustomDashboard{}).Where("is_default = ? AND id != ?", true, d.ID).Update("is_default", false) updates["is_default"] = true } if len(updates) > 0 { if err := h.db.Model(&d).Updates(updates).Error; err != nil { c.JSON(http.StatusInternalServerError, ezops.Response{Code: 500, Message: "更新失败: " + err.Error()}) return } } c.JSON(http.StatusOK, ezops.Response{Code: 200, Message: "更新成功", Data: gin.H{"success": true}}) } // Delete 删除仪表盘 // DELETE /api/v1/custom-dashboards/:id func (h *CustomDashboardHandler) Delete(c *gin.Context) { id := c.Param("id") if err := h.db.Delete(&ezops.CustomDashboard{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, ezops.Response{Code: 500, Message: "删除失败: " + err.Error()}) return } c.JSON(http.StatusOK, ezops.Response{Code: 200, Message: "删除成功", Data: gin.H{"success": true}}) } // SetDefault 设为默认仪表盘 // POST /api/v1/custom-dashboards/:id/default func (h *CustomDashboardHandler) SetDefault(c *gin.Context) { id := c.Param("id") var d ezops.CustomDashboard if err := h.db.First(&d, id).Error; err != nil { c.JSON(http.StatusNotFound, ezops.Response{Code: 404, Message: "仪表盘不存在"}) return } // 清除其他默认 h.db.Model(&ezops.CustomDashboard{}).Where("is_default = ? AND id != ?", true, d.ID).Update("is_default", false) // 设置当前为默认 h.db.Model(&d).Update("is_default", true) c.JSON(http.StatusOK, ezops.Response{Code: 200, Message: "设置成功", Data: gin.H{"success": true}}) }