package handlers import ( "math" "net/http" "time" "ezops/internal/models" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // DashboardHandler 仪表盘处理器 type DashboardHandler struct { db *gorm.DB } // NewDashboardHandler 创建仪表盘处理器 func NewDashboardHandler(db *gorm.DB) *DashboardHandler { return &DashboardHandler{db: db} } // GetStats 获取仪表盘统计 // @Summary 获取仪表盘统计 // @Description 获取仪表盘所有统计数据 // @Tags 仪表盘 // @Produce json // @Security Bearer // @Success 200 {object} models.Response{data=models.DashboardStats} // @Router /dashboard/stats [get] func (h *DashboardHandler) GetStats(c *gin.Context) { var stats models.DashboardStats // 服务器统计 var serverStats models.ServerStats h.db.Model(&models.Server{}).Count(&serverStats.Total) h.db.Model(&models.Server{}).Where("status = ?", "running").Count(&serverStats.Running) h.db.Model(&models.Server{}).Where("status = ?", "stopped").Count(&serverStats.Stopped) h.db.Model(&models.Server{}).Where("status = ?", "maintenance").Count(&serverStats.Maintenance) h.db.Model(&models.Server{}).Where("status = ?", "error").Count(&serverStats.Error) var avgCPU, avgMemory, avgDisk float64 h.db.Model(&models.Server{}).Select("AVG(cpu_usage)").Scan(&avgCPU) h.db.Model(&models.Server{}).Select("AVG(memory_usage)").Scan(&avgMemory) h.db.Model(&models.Server{}).Select("AVG(disk_usage)").Scan(&avgDisk) serverStats.AvgCPU = math.Round(avgCPU*100) / 100 serverStats.AvgMemory = math.Round(avgMemory*100) / 100 serverStats.AvgDisk = math.Round(avgDisk*100) / 100 stats.ServerStats = serverStats // K8s集群统计 var k8sStats models.K8sClusterStats h.db.Model(&models.K8sCluster{}).Count(&k8sStats.Total) h.db.Model(&models.K8sCluster{}).Where("status = ?", "healthy").Count(&k8sStats.Healthy) h.db.Model(&models.K8sCluster{}).Where("status = ?", "warning").Count(&k8sStats.Warning) h.db.Model(&models.K8sCluster{}).Where("status = ?", "error").Count(&k8sStats.Error) var totalNodes, totalPods, totalServices int64 h.db.Model(&models.K8sNode{}).Count(&totalNodes) h.db.Model(&models.K8sPod{}).Count(&totalPods) h.db.Model(&models.K8sService{}).Count(&totalServices) k8sStats.TotalNodes = totalNodes k8sStats.TotalPods = totalPods k8sStats.TotalServices = totalServices stats.K8sStats = k8sStats // 告警统计 var alertStats models.AlertStats h.db.Model(&models.AlertEvent{}).Count(&alertStats.Total) h.db.Model(&models.AlertEvent{}).Where("status = ?", "firing").Count(&alertStats.Firing) h.db.Model(&models.AlertEvent{}).Where("status = ?", "resolved").Count(&alertStats.Resolved) h.db.Model(&models.AlertEvent{}).Where("severity = ?", "critical").Count(&alertStats.Critical) h.db.Model(&models.AlertEvent{}).Where("severity = ?", "warning").Count(&alertStats.Warning) h.db.Model(&models.AlertEvent{}).Where("severity = ?", "info").Count(&alertStats.Info) // 今日告警 today := time.Now().Format("2006-01-02") h.db.Model(&models.AlertEvent{}).Where("DATE(created_at) = ?", today).Count(&alertStats.TodayCount) // 本周告警 weekAgo := time.Now().AddDate(0, 0, -7).Format("2006-01-02") h.db.Model(&models.AlertEvent{}).Where("created_at >= ?", weekAgo).Count(&alertStats.WeekCount) stats.AlertStats = alertStats // 流水线统计 var pipelineStats models.PipelineStats h.db.Model(&models.PipelineRun{}).Count(&pipelineStats.Total) h.db.Model(&models.PipelineRun{}).Where("status = ?", "success").Count(&pipelineStats.Success) h.db.Model(&models.PipelineRun{}).Where("status = ?", "failed").Count(&pipelineStats.Failed) h.db.Model(&models.PipelineRun{}).Where("status = ?", "running").Count(&pipelineStats.Running) h.db.Model(&models.PipelineRun{}).Where("DATE(created_at) = ?", today).Count(&pipelineStats.TodayCount) if pipelineStats.Total > 0 { pipelineStats.SuccessRate = math.Round(float64(pipelineStats.Success)/float64(pipelineStats.Total)*10000) / 100 } stats.PipelineStats = pipelineStats // 成本统计 costStats := models.CostStats{ TotalCost: 450000, MonthCost: 107700, CloudCosts: []models.CloudCost{ {Cloud: "阿里云", Cost: 28500}, {Cloud: "AWS", Cost: 42000}, {Cloud: "腾讯云", Cost: 15800}, {Cloud: "华为云", Cost: 8900}, {Cloud: "Azure", Cost: 12500}, }, MonthTrend: []models.MonthTrend{ {Month: "1月", Cost: 85000}, {Month: "2月", Cost: 92000}, {Month: "3月", Cost: 88000}, {Month: "4月", Cost: 95000}, {Month: "5月", Cost: 107700}, }, } stats.CostStats = costStats // Top资源使用 var topCPU, topMemory, topDisk []models.Server h.db.Order("cpu_usage DESC").Limit(5).Find(&topCPU) h.db.Order("memory_usage DESC").Limit(5).Find(&topMemory) h.db.Order("disk_usage DESC").Limit(5).Find(&topDisk) var topResourceUsage models.TopResourceUsage for _, s := range topCPU { topResourceUsage.CPU = append(topResourceUsage.CPU, models.ServerResource{ ID: s.ID, Hostname: s.Hostname, IP: s.IP, Usage: s.CPUUsage, }) } for _, s := range topMemory { topResourceUsage.Memory = append(topResourceUsage.Memory, models.ServerResource{ ID: s.ID, Hostname: s.Hostname, IP: s.IP, Usage: s.MemoryUsage, }) } for _, s := range topDisk { topResourceUsage.Disk = append(topResourceUsage.Disk, models.ServerResource{ ID: s.ID, Hostname: s.Hostname, IP: s.IP, Usage: s.DiskUsage, }) } stats.TopResourceUsage = topResourceUsage // 即将过期的证书 var domains []models.Domain h.db.Where("ssl_enabled = ?", true).Find(&domains) var expiringCerts []models.DomainCert now := time.Now() for _, d := range domains { if d.SSLExpireDate != nil { days := int(d.SSLExpireDate.Sub(now).Hours() / 24) if days < 90 { expiringCerts = append(expiringCerts, models.DomainCert{ ID: d.ID, Name: d.Name, ExpireDate: d.SSLExpireDate.Format("2006-01-02"), DaysUntilExpiry: days, }) } } } stats.ExpiringCerts = expiringCerts c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "success", Data: stats, }) } // GetRecentAlerts 获取最近告警 // @Summary 获取最近告警 // @Description 获取最近触发的告警事件 // @Tags 仪表盘 // @Produce json // @Security Bearer // @Param limit query int false "数量限制" // @Success 200 {object} models.Response{data=[]models.AlertEvent} // @Router /dashboard/recent-alerts [get] func (h *DashboardHandler) GetRecentAlerts(c *gin.Context) { limit := 10 if l := c.Query("limit"); l != "" { if parsed, err := time.Parse("2006-01-02", l); err == nil { _ = parsed } } var alerts []models.AlertEvent h.db.Preload("Rule").Where("status = ?", "firing"). Order("created_at DESC"). Limit(limit). Find(&alerts) c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "success", Data: alerts, }) } // GetRecentReleases 获取最近发布 // @Summary 获取最近发布 // @Description 获取最近的应用发布记录 // @Tags 仪表盘 // @Produce json // @Security Bearer // @Param limit query int false "数量限制" // @Success 200 {object} models.Response{data=[]models.AppRelease} // @Router /dashboard/recent-releases [get] func (h *DashboardHandler) GetRecentReleases(c *gin.Context) { limit := 5 var releases []models.AppRelease h.db.Order("created_at DESC"). Limit(limit). Find(&releases) c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "success", Data: releases, }) } // GetMetrics 获取监控指标 // @Summary 获取监控指标 // @Description 获取系统监控指标数据 // @Tags 仪表盘 // @Produce json // @Security Bearer // @Param hours query int false "小时数" // @Success 200 {object} models.Response{data=[]models.MetricData} // @Router /dashboard/metrics [get] func (h *DashboardHandler) GetMetrics(c *gin.Context) { hours := 24 // 生成模拟数据 var metrics []models.MetricData now := time.Now() for i := 0; i < hours; i++ { timestamp := now.Add(-time.Duration(hours-i) * time.Hour) metrics = append(metrics, models.MetricData{ Name: "cpu", Value: 40 + float64(i%30), Timestamp: timestamp, }) metrics = append(metrics, models.MetricData{ Name: "memory", Value: 50 + float64(i%20), Timestamp: timestamp, }) metrics = append(metrics, models.MetricData{ Name: "disk", Value: 45 + float64(i%10), Timestamp: timestamp, }) } c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "success", Data: metrics, }) }