package controller import ( "ezops/model" "net/http" "strconv" "time" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // MetricsHandler 指标浏览器 handler // Logan.Li type MetricsHandler struct { db *gorm.DB } // NewMetricsHandler 创建指标 handler func NewMetricsHandler(db *gorm.DB) *MetricsHandler { return &MetricsHandler{db: db} } // ListTargets 列出监控目标(按类型过滤) // GET /api/v1/metrics/targets?type=host&status=online func (h *MetricsHandler) ListTargets(c *gin.Context) { var targets []model.MonitorTarget q := h.db.Model(&model.MonitorTarget{}) if t := c.Query("type"); t != "" { q = q.Where("type = ?", t) } if s := c.Query("status"); s != "" { q = q.Where("status = ?", s) } if kw := c.Query("keyword"); kw != "" { q = q.Where("name LIKE ? OR ip LIKE ?", "%"+kw+"%", "%"+kw+"%") } // 分页 page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "50")) var total int64 q.Count(&total) q.Offset((page - 1) * pageSize).Limit(pageSize).Order("name ASC").Find(&targets) if targets == nil { targets = []model.MonitorTarget{} } // 附带最新指标值 type TargetWithMetrics struct { model.MonitorTarget LatestMetrics map[string]float64 `json:"latest_metrics"` } result := make([]TargetWithMetrics, len(targets)) for i, t := range targets { result[i] = TargetWithMetrics{MonitorTarget: t, LatestMetrics: make(map[string]float64)} // 查询该目标最近 10 分钟内的最新指标 var histories []model.MetricHistory h.db.Where("target_id = ? AND timestamp > ?", t.ID, now()-600). Order("timestamp DESC").Limit(10).Find(&histories) for _, mh := range histories { if _, ok := result[i].LatestMetrics[mh.MetricName]; !ok { result[i].LatestMetrics[mh.MetricName] = mh.Value } } } c.JSON(http.StatusOK, model.Response{Code: 200, Data: gin.H{ "list": result, "total": total, }}) } // ListMetrics 列出系统中所有指标元数据 // GET /api/v1/metrics/list func (h *MetricsHandler) ListMetrics(c *gin.Context) { var metrics []model.MonitorMetric h.db.Order("category ASC, name ASC").Find(&metrics) if metrics == nil { metrics = []model.MonitorMetric{} } c.JSON(http.StatusOK, model.Response{Code: 200, Data: metrics}) } // QueryValues 查询指标时序数据 // GET /api/v1/metrics/query?target_id=N&metric_name=cpu_usage&from=0&to=0&limit=100 func (h *MetricsHandler) QueryValues(c *gin.Context) { var histories []model.MetricHistory q := h.db.Model(&model.MetricHistory{}) if tid := c.Query("target_id"); tid != "" { id, _ := strconv.ParseUint(tid, 10, 64) q = q.Where("target_id = ?", id) } if mn := c.Query("metric_name"); mn != "" { q = q.Where("metric_name = ?", mn) } if from := c.Query("from"); from != "" { ts, _ := strconv.ParseInt(from, 10, 64) q = q.Where("timestamp >= ?", ts) } if to := c.Query("to"); to != "" { ts, _ := strconv.ParseInt(to, 10, 64) q = q.Where("timestamp <= ?", ts) } limit, _ := strconv.Atoi(c.DefaultQuery("limit", "200")) q.Order("timestamp ASC").Limit(limit).Find(&histories) if histories == nil { histories = []model.MetricHistory{} } c.JSON(http.StatusOK, model.Response{Code: 200, Data: histories}) } // ListMetricNames 列出所有可用的指标名称(去重) // GET /api/v1/metrics/names func (h *MetricsHandler) ListMetricNames(c *gin.Context) { var names []string h.db.Model(&model.MetricHistory{}). Distinct("metric_name"). Order("metric_name ASC"). Pluck("metric_name", &names) if names == nil { names = []string{} } c.JSON(http.StatusOK, model.Response{Code: 200, Data: names}) } // TopHosts 返回指定指标的 Top N 主机排行 // GET /api/v1/metrics/top?metric=cpu&limit=5 func (h *MetricsHandler) TopHosts(c *gin.Context) { metric := c.DefaultQuery("metric", "cpu") limit, _ := strconv.Atoi(c.DefaultQuery("limit", "5")) type HostRank struct { Hostname string `json:"hostname"` IP string `json:"ip"` CPUUsage float64 `json:"cpu_usage"` MemUsage float64 `json:"memory_usage"` DiskUsage float64 `json:"disk_usage"` CPUCores int `json:"cpu_cores"` MemMB int64 `json:"memory_mb"` DiskGB int64 `json:"disk_gb"` Status string `json:"status"` } var servers []model.Server orderField := "cpu_usage DESC" switch metric { case "cpu": orderField = "cpu_usage DESC" case "mem", "memory": orderField = "memory_usage DESC" case "disk": orderField = "disk_usage DESC" default: orderField = "cpu_usage DESC" } h.db.Where("agent_status = ?", "online"). Order(orderField). Limit(limit). Find(&servers) result := make([]HostRank, len(servers)) for i, s := range servers { result[i] = HostRank{ Hostname: s.Hostname, IP: s.IP, CPUUsage: s.CPUUsage, MemUsage: s.MemoryUsage, DiskUsage: s.DiskUsage, CPUCores: s.CPU, MemMB: s.Memory, DiskGB: s.Disk, Status: s.Status, } } if result == nil { result = []HostRank{} } c.JSON(http.StatusOK, model.Response{Code: 200, Data: result}) } func now() int64 { return time.Now().Unix() }