Skip to main content
Glama
summary.go7.49 kB
package inspection import ( "context" "encoding/json" "fmt" "time" "github.com/duke-git/lancet/v2/slice" "github.com/gin-gonic/gin" "github.com/weibaohui/k8m/internal/dao" "github.com/weibaohui/k8m/pkg/comm/utils" "github.com/weibaohui/k8m/pkg/comm/utils/amis" "github.com/weibaohui/k8m/pkg/constants" "github.com/weibaohui/k8m/pkg/lua" "github.com/weibaohui/k8m/pkg/models" "gorm.io/gorm" "k8s.io/klog/v2" ) // @Summary 统计巡检计划执行情况 // @Description 统计指定巡检计划的执行情况,支持按时间范围和集群过滤 // @Security BearerAuth // @Param id path string false "巡检计划ID" // @Param cluster path string false "集群名称" // @Param start_time path string false "开始时间(RFC3339格式)" // @Param end_time path string false "结束时间(RFC3339格式)" // @Success 200 {object} string // @Router /admin/inspection/schedule/id/{id}/summary [post] // @Router /admin/inspection/schedule/id/{id}/summary/cluster/{cluster}/start_time/{start_time}/end_time/{end_time} [post] func (s *AdminScheduleController) SummaryBySchedule(c *gin.Context) { params := dao.BuildParams(c) params.PerPage = 100000000 // 1. 获取scheduleID参数 scheduleID := c.Param("id") // 新增:解析时间范围参数 var startTime, endTime time.Time var err error var cluster string startTimeStr := c.Param("start_time") endTimeStr := c.Param("end_time") if startTimeStr != "" { startTime, err = time.Parse(time.RFC3339, startTimeStr) if err != nil { amis.WriteJsonError(c, fmt.Errorf("start_time 格式错误,应为 RFC3339")) return } } if endTimeStr != "" { endTime, err = time.Parse(time.RFC3339, endTimeStr) if err != nil { amis.WriteJsonError(c, fmt.Errorf("end_time 格式错误,应为 RFC3339")) return } } // 获取cluster参数 clusterBase64 := c.Param("cluster") if clusterDecode, err := utils.UrlSafeBase64Decode(clusterBase64); err == nil { cluster = string(clusterDecode) } else { klog.V(6).Infof("cluster=%s,%v ", cluster, err) } // 2. 查询所有该scheduleID下的InspectionRecord recordModel := &models.InspectionRecord{} records, _, err := recordModel.List(params, func(db *gorm.DB) *gorm.DB { query := db if cluster != "" { query = query.Where("cluster = ?", cluster) } if scheduleID != "" { query = query.Where("schedule_id = ?", scheduleID) } if !startTime.IsZero() { query = query.Where("created_at >= ?", startTime) } if !endTime.IsZero() { query = query.Where("created_at <= ?", endTime) } return query.Order("id desc") }) if err != nil { amis.WriteJsonError(c, err) return } if len(records) == 0 { amis.WriteJsonData(c, gin.H{"summary": "无执行记录"}) return } tempScheduleIDs := make([]*uint, 0, 20) clusterSet := map[string]struct{}{} for _, r := range records { tempScheduleIDs = append(tempScheduleIDs, r.ScheduleID) clusterSet[r.Cluster] = struct{}{} } // 对ScheduleID进行去重 tempScheduleIDs = slice.UniqueBy(tempScheduleIDs, func(item *uint) uint { return *item }) // 3. 查询所有相关InspectionCheckEvent eventModel := &models.InspectionCheckEvent{} events, _, err := eventModel.List(params, func(db *gorm.DB) *gorm.DB { query := db if cluster != "" { query = query.Where("cluster = ?", cluster) } if scheduleID != "" { query = query.Where("schedule_id in ?", tempScheduleIDs) } if !startTime.IsZero() { query = query.Where("created_at >= ?", startTime) } if !endTime.IsZero() { query = query.Where("created_at <= ?", endTime) } return query.Order("id desc") }) if err != nil { amis.WriteJsonError(c, err) return } // 4. 聚合统计 totalClusters := len(clusterSet) totalRuns := len(records) // 巡检计划执行次数 clusterKindMap := map[string]map[string]int{} // cluster -> kind -> count clusterKindErrMap := map[string]map[string]int{} // cluster -> kind -> error count for _, e := range events { if _, ok := clusterKindMap[e.Cluster]; !ok { clusterKindMap[e.Cluster] = map[string]int{} clusterKindErrMap[e.Cluster] = map[string]int{} } clusterKindMap[e.Cluster][e.Kind]++ if e.EventStatus != string(constants.LuaEventStatusNormal) { clusterKindErrMap[e.Cluster][e.Kind]++ } } // 5. 构建返回结构 result := gin.H{ "total_clusters": totalClusters, "total_runs": totalRuns, // 新增字段:执行次数 "clusters": []gin.H{}, } // 新增:如果 scheduleID 为空,增加运行巡检计划数 if scheduleID == "" { var count int64 dao.DB().Model(&models.InspectionRecord{}).Distinct("schedule_id").Count(&count) result["total_schedules"] = count } // 统计每个集群的执行次数 clusterRunCount := map[string]int{} for _, r := range records { clusterRunCount[r.Cluster]++ } for cluster, kindMap := range clusterKindMap { var kindArr []gin.H for kind, count := range kindMap { errCount := clusterKindErrMap[cluster][kind] kindArr = append(kindArr, gin.H{ "kind": kind, "count": count, "error_count": errCount, }) } result["clusters"] = append(result["clusters"].([]gin.H), gin.H{ "cluster": cluster, "run_count": clusterRunCount[cluster], // 新增字段:该集群执行次数 "kinds": kindArr, }) } // 新增:统计最新一次执行情况 var latestRun gin.H if len(records) > 0 { latestRecord := records[0] kindStatus := map[string]map[string]int{} // kind -> status -> count for _, e := range events { if e.RecordID == latestRecord.ID { if _, ok := kindStatus[e.Kind]; !ok { kindStatus[e.Kind] = map[string]int{"pass": 0, "fail": 0} } if e.EventStatus == string(constants.LuaEventStatusNormal) { kindStatus[e.Kind]["pass"]++ } else { kindStatus[e.Kind]["fail"]++ } } } var kindArr []gin.H for kind, statusMap := range kindStatus { kindArr = append(kindArr, gin.H{ "kind": kind, "normal_count": statusMap["pass"], "error_count": statusMap["fail"], }) } latestRun = gin.H{ "record_id": latestRecord.ID, "schedule_id": latestRecord.ScheduleID, "run_time": latestRecord.CreatedAt, "kinds": kindArr, } result["latest_run"] = latestRun } amis.WriteJsonData(c, result) } // @Summary 生成巡检记录AI总结 // @Description 为指定巡检记录生成AI总结 // @Security BearerAuth // @Param id path string true "巡检记录ID" // @Success 200 {object} string // @Router /admin/inspection/schedule/record/id/{id}/summary [post] func (s *AdminScheduleController) SummaryByRecordID(c *gin.Context) { recordIDStr := c.Param("id") if recordIDStr == "" { amis.WriteJsonError(c, fmt.Errorf("缺少 record_id 参数")) return } recordID := utils.ToUInt(recordIDStr) if recordID == 0 { amis.WriteJsonError(c, fmt.Errorf("record_id 参数无效")) return } sb := lua.ScheduleBackground{} msg, err := sb.GetSummaryMsg(recordID) if err != nil { amis.WriteJsonError(c, err) return } // 将原始巡检结果转换为JSON字符串 resultRawBytes, err := json.Marshal(msg) if err != nil { klog.Errorf("序列化原始巡检结果失败: %v", err) resultRawBytes = []byte("{}") } resultRaw := string(resultRawBytes) summary, summaryErr := sb.SummaryByAI(context.Background(), msg) err = sb.SaveSummaryBack(recordID, summary, summaryErr, resultRaw) if err != nil { amis.WriteJsonError(c, err) return } amis.WriteJsonData(c, summary) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/weibaohui/k8m'

If you have feedback or need assistance with the MCP directory API, please join our Discord server