package handler import ( "fmt" "sort" "strconv" "strings" "time" "github.com/monitor/backend/internal/storage" ) // MetricData 处理后的监控数据 type MetricData struct { Time time.Time `json:"time"` Value float64 `json:"value"` } // ParseInterval 解析时间区间字符串,返回秒数 // 支持的格式:10s, 1m, 5m, 1h, 1d等 func ParseInterval(intervalStr string) (int, error) { if intervalStr == "" { return 10, nil // 默认10秒 } intervalStr = strings.ToLower(intervalStr) var multiplier int // 解析时间单位 switch { case strings.HasSuffix(intervalStr, "s"): multiplier = 1 intervalStr = strings.TrimSuffix(intervalStr, "s") case strings.HasSuffix(intervalStr, "m"): multiplier = 60 intervalStr = strings.TrimSuffix(intervalStr, "m") case strings.HasSuffix(intervalStr, "h"): multiplier = 3600 intervalStr = strings.TrimSuffix(intervalStr, "h") case strings.HasSuffix(intervalStr, "d"): multiplier = 86400 intervalStr = strings.TrimSuffix(intervalStr, "d") default: return 0, fmt.Errorf("unsupported interval format: %s", intervalStr) } // 解析数字部分 value, err := strconv.Atoi(intervalStr) if err != nil { return 0, fmt.Errorf("invalid interval value: %s", intervalStr) } return value * multiplier, nil } // FormatTimeByInterval 根据时间区间格式化时间 func FormatTimeByInterval(t time.Time, intervalSeconds int) string { // 按指定秒数对齐时间 rounded := t.Truncate(time.Duration(intervalSeconds) * time.Second) // 根据区间长度选择不同的格式化字符串 if intervalSeconds < 60 { // 秒级,显示到秒 return rounded.Format("2006-01-02 15:04:05") } else if intervalSeconds < 3600 { // 分钟级,显示到分钟 return rounded.Format("2006-01-02 15:04:00") } else { // 小时级,显示到小时 return rounded.Format("2006-01-02 15:00:00") } } // ProcessMetricData 处理监控数据,支持动态时间区间 func ProcessMetricData(points []storage.MetricPoint, aggregation string, intervalStr string, startTime, endTime string) []MetricData { // 解析时间区间 intervalSeconds, err := ParseInterval(intervalStr) if err != nil { // 解析失败,使用默认值10秒 intervalSeconds = 10 } // 解析开始和结束时间 var start, end time.Time var parseErr error // 解析开始时间 if strings.HasPrefix(startTime, "-") || startTime == "now()" { // 相对时间,使用当前时间作为基准 now := time.Now() if startTime == "now()" { start = now } else { // 相对时间,如-24h relDuration, parseErr := time.ParseDuration(startTime) if parseErr == nil { start = now.Add(relDuration) } else { // 解析失败,使用默认时间 start = now.Add(-24 * time.Hour) } } } else { // 绝对时间,尝试解析 start, parseErr = time.Parse(time.RFC3339, startTime) if parseErr != nil { // 尝试其他格式 start, parseErr = time.Parse("2006-01-02T15:04", startTime) if parseErr != nil { // 尝试YYYY-MM-DD HH:MM格式 start, parseErr = time.Parse("2006-01-02 15:04", startTime) if parseErr != nil { // 解析失败,使用默认时间 start = time.Now().Add(-24 * time.Hour) } } } } // 解析结束时间 if endTime == "now()" { end = time.Now() } else if strings.HasPrefix(endTime, "-") { // 相对时间,使用当前时间作为基准 now := time.Now() relDuration, parseErr := time.ParseDuration(endTime) if parseErr == nil { end = now.Add(relDuration) } else { // 解析失败,使用当前时间 end = now } } else { // 绝对时间,尝试解析 end, parseErr = time.Parse(time.RFC3339, endTime) if parseErr != nil { // 尝试其他格式 end, parseErr = time.Parse("2006-01-02T15:04", endTime) if parseErr != nil { // 尝试YYYY-MM-DD HH:MM格式 end, parseErr = time.Parse("2006-01-02 15:04", endTime) if parseErr != nil { // 解析失败,使用当前时间 end = time.Now() } } } } // 如果没有数据点,生成覆盖整个时间范围的空数据点 if len(points) == 0 { return generateEmptyData(start, end, intervalSeconds) } // 根据时间区段精细程度自动调整聚合策略 // 如果时间区段精细程度为1s,显示全部数据(去重后) // 如果时间区段精细程度>1s,根据聚合类型进行聚合 if intervalSeconds == 1 { // 1秒时间区段,返回全部原始数据(去重后) return processRawData(points) } else { // 大于1秒时间区段,根据聚合类型处理 switch aggregation { case "raw": // 对于raw聚合类型,返回去重后的原始数据 return processRawData(points) case "average": return processAverageData(points, intervalSeconds, start, end) case "max": return processMaxData(points, intervalSeconds, start, end) case "min": return processMinData(points, intervalSeconds, start, end) case "sum": return processSumData(points, intervalSeconds, start, end) default: // 默认返回平均值 return processAverageData(points, intervalSeconds, start, end) } } } // generateEmptyData 生成覆盖整个时间范围的空数据点 func generateEmptyData(start, end time.Time, intervalSeconds int) []MetricData { // 生成空数据点,确保覆盖整个时间范围 result := make([]MetricData, 0) currentTime := start.Truncate(time.Duration(intervalSeconds) * time.Second) // 循环生成数据点,直到超过结束时间 for currentTime.Before(end) { result = append(result, MetricData{ Time: currentTime, Value: 0, // 使用0表示无数据 }) currentTime = currentTime.Add(time.Duration(intervalSeconds) * time.Second) } return result } // processRawData 处理原始数据,添加去重逻辑 func processRawData(points []storage.MetricPoint) []MetricData { // 使用map进行去重,key为时间戳(精确到秒)+值的组合 deduplicated := make(map[string]storage.MetricPoint) for _, point := range points { // 精确到秒的时间戳 key := fmt.Sprintf("%d_%.2f", point.Time.Unix(), point.Value) deduplicated[key] = point } // 将去重后的数据转换为切片 result := make([]MetricData, 0, len(deduplicated)) for _, point := range deduplicated { result = append(result, MetricData{ Time: point.Time, Value: point.Value, }) } // 按时间排序 sort.Slice(result, func(i, j int) bool { return result[i].Time.Before(result[j].Time) }) return result } // processAverageData 处理平均值数据 func processAverageData(points []storage.MetricPoint, intervalSeconds int, start, end time.Time) []MetricData { // 按指定时间区间聚合的平均值 // 如果没有数据点,生成空数据 if len(points) == 0 { return generateEmptyData(start, end, intervalSeconds) } // 先对原始数据按时间排序 sort.Slice(points, func(i, j int) bool { return points[i].Time.Before(points[j].Time) }) // 初始化结果和指针 result := make([]MetricData, 0) currentTime := start.Truncate(time.Duration(intervalSeconds) * time.Second) pointIndex := 0 pointCount := len(points) // 循环处理每个时间区间 for currentTime.Before(end.Add(time.Duration(intervalSeconds) * time.Second)) { intervalStart := currentTime intervalEnd := intervalStart.Add(time.Duration(intervalSeconds) * time.Second) // 收集当前区间内的数据点 var sum float64 = 0 var count int = 0 for pointIndex < pointCount && points[pointIndex].Time.Before(intervalEnd) { // 只处理区间内的数据点 if points[pointIndex].Time.After(intervalStart) || points[pointIndex].Time.Equal(intervalStart) { sum += points[pointIndex].Value count++ } pointIndex++ } // 计算平均值 var value float64 = 0 if count > 0 { value = sum / float64(count) } // 添加到结果 result = append(result, MetricData{ Time: currentTime, Value: value, }) // 移动到下一个时间区间 currentTime = intervalEnd } return result } // processMaxData 处理最大值数据 func processMaxData(points []storage.MetricPoint, intervalSeconds int, start, end time.Time) []MetricData { // 按指定时间区间聚合的最大值 // 如果没有数据点,生成空数据 if len(points) == 0 { return generateEmptyData(start, end, intervalSeconds) } // 先对原始数据按时间排序 sort.Slice(points, func(i, j int) bool { return points[i].Time.Before(points[j].Time) }) // 初始化结果和指针 result := make([]MetricData, 0) currentTime := start.Truncate(time.Duration(intervalSeconds) * time.Second) pointIndex := 0 pointCount := len(points) // 循环处理每个时间区间 for currentTime.Before(end.Add(time.Duration(intervalSeconds) * time.Second)) { intervalStart := currentTime intervalEnd := intervalStart.Add(time.Duration(intervalSeconds) * time.Second) // 查找当前区间内的最大值 var value float64 = 0 var foundData bool = false for pointIndex < pointCount && points[pointIndex].Time.Before(intervalEnd) { // 只处理区间内的数据点 if points[pointIndex].Time.After(intervalStart) || points[pointIndex].Time.Equal(intervalStart) { if !foundData { value = points[pointIndex].Value foundData = true } else if points[pointIndex].Value > value { value = points[pointIndex].Value } } pointIndex++ } // 添加到结果 result = append(result, MetricData{ Time: currentTime, Value: value, }) // 移动到下一个时间区间 currentTime = intervalEnd } return result } // processMinData 处理最小值数据 func processMinData(points []storage.MetricPoint, intervalSeconds int, start, end time.Time) []MetricData { // 按指定时间区间聚合的最小值 // 如果没有数据点,生成空数据 if len(points) == 0 { return generateEmptyData(start, end, intervalSeconds) } // 先对原始数据按时间排序 sort.Slice(points, func(i, j int) bool { return points[i].Time.Before(points[j].Time) }) // 初始化结果和指针 result := make([]MetricData, 0) currentTime := start.Truncate(time.Duration(intervalSeconds) * time.Second) pointIndex := 0 pointCount := len(points) // 循环处理每个时间区间 for currentTime.Before(end.Add(time.Duration(intervalSeconds) * time.Second)) { intervalStart := currentTime intervalEnd := intervalStart.Add(time.Duration(intervalSeconds) * time.Second) // 查找当前区间内的最小值 var value float64 = 0 var foundData bool = false for pointIndex < pointCount && points[pointIndex].Time.Before(intervalEnd) { // 只处理区间内的数据点 if points[pointIndex].Time.After(intervalStart) || points[pointIndex].Time.Equal(intervalStart) { if !foundData { value = points[pointIndex].Value foundData = true } else if points[pointIndex].Value < value { value = points[pointIndex].Value } } pointIndex++ } // 添加到结果 result = append(result, MetricData{ Time: currentTime, Value: value, }) // 移动到下一个时间区间 currentTime = intervalEnd } return result } // processSumData 处理总和数据 func processSumData(points []storage.MetricPoint, intervalSeconds int, start, end time.Time) []MetricData { // 按指定时间区间聚合的总和 // 如果没有数据点,生成空数据 if len(points) == 0 { return generateEmptyData(start, end, intervalSeconds) } // 先对原始数据按时间排序 sort.Slice(points, func(i, j int) bool { return points[i].Time.Before(points[j].Time) }) // 初始化结果和指针 result := make([]MetricData, 0) currentTime := start.Truncate(time.Duration(intervalSeconds) * time.Second) pointIndex := 0 pointCount := len(points) // 循环处理每个时间区间 for currentTime.Before(end.Add(time.Duration(intervalSeconds) * time.Second)) { intervalStart := currentTime intervalEnd := intervalStart.Add(time.Duration(intervalSeconds) * time.Second) // 收集当前区间内的数据点 intervalPoints := make([]storage.MetricPoint, 0) for pointIndex < pointCount && points[pointIndex].Time.Before(intervalEnd) { // 只处理区间内的数据点 if points[pointIndex].Time.After(intervalStart) || points[pointIndex].Time.Equal(intervalStart) { intervalPoints = append(intervalPoints, points[pointIndex]) } pointIndex++ } // 计算总和 sum := 0.0 intervalPointCount := len(intervalPoints) if intervalPointCount > 0 { if intervalPointCount < 2 { // 如果数据点数量小于2,直接返回第一个数据点的值 sum = intervalPoints[0].Value } else { // 计算区间内的流量总和 for i := 1; i < intervalPointCount; i++ { // 计算时间差(秒) timeDiff := intervalPoints[i].Time.Sub(intervalPoints[i-1].Time).Seconds() if timeDiff > 0 { // 计算这个时间段的平均速率 averageRate := (intervalPoints[i].Value + intervalPoints[i-1].Value) / 2 // 计算这个时间段的流量 flow := averageRate * timeDiff // 累加到总和 sum += flow } } } } // 添加到结果 result = append(result, MetricData{ Time: currentTime, Value: sum, }) // 移动到下一个时间区间 currentTime = intervalEnd } return result }