diff --git a/internal/dao/sensor.go b/internal/dao/sensor.go index 6c28927..07a0743 100644 --- a/internal/dao/sensor.go +++ b/internal/dao/sensor.go @@ -2,6 +2,8 @@ package dao import ( "database/sql" + "math" + "sort" "time" "go_rain_dtu/internal/model" @@ -46,6 +48,8 @@ func (dao *SensorDAO) GetAggregatedData(start, end time.Time, interval string) ( var intervalMinutes int switch interval { + case "1min": + intervalMinutes = 1 case "5min": intervalMinutes = 5 case "30min": @@ -116,6 +120,102 @@ func (dao *SensorDAO) GetAggregatedData(start, end time.Time, interval string) ( return nil, err } + // 如果是1分钟粒度,且数据不足,可能需要进行插值 + if intervalMinutes == 1 && len(result) > 0 { + result = fillMissingMinutes(result, start, end) + } + logger.Logger.Printf("聚合查询成功: 返回%d条记录 (间隔=%s)", len(result), interval) return result, nil } + +// fillMissingMinutes 填充缺失的分钟数据点 +func fillMissingMinutes(data []model.AggregatedData, start, end time.Time) []model.AggregatedData { + // 只有在数据点少于应有数量的一半时才进行插值 + expectedPoints := int(end.Sub(start).Minutes()) + 1 + if len(data) > expectedPoints/2 { + return data // 数据点足够多,不需要插值 + } + + // 创建一个映射,用于快速查找已有数据点 + dataMap := make(map[int64]model.AggregatedData) + for _, point := range data { + // 将时间转换为Unix时间戳(秒),便于比较 + key := point.Timestamp.Unix() + dataMap[key] = point + } + + // 初始化结果数组,预分配足够的空间 + result := make([]model.AggregatedData, 0, expectedPoints) + + // 创建按时间排序的索引 + var timestamps []int64 + for k := range dataMap { + timestamps = append(timestamps, k) + } + sort.Slice(timestamps, func(i, j int) bool { + return timestamps[i] < timestamps[j] + }) + + // 如果没有数据点,直接返回 + if len(timestamps) == 0 { + return data + } + + // 获取第一个和最后一个数据点 + firstPointTime := time.Unix(timestamps[0], 0) + lastPointTime := time.Unix(timestamps[len(timestamps)-1], 0) + + // 调整开始和结束时间,确保它们不会超出实际数据范围 + if start.Before(firstPointTime) { + start = firstPointTime + } + if end.After(lastPointTime) { + end = lastPointTime + } + + // 将start向下取整到整分钟 + start = time.Date(start.Year(), start.Month(), start.Day(), start.Hour(), start.Minute(), 0, 0, start.Location()) + + // 按分钟迭代时间范围 + for t := start; !t.After(end); t = t.Add(time.Minute) { + key := t.Unix() + if point, exists := dataMap[key]; exists { + // 使用现有数据点 + result = append(result, point) + } else { + // 使用最近的有效数据点进行插值 + nearestPoint := findNearestPoint(t, timestamps, dataMap) + if !nearestPoint.Timestamp.IsZero() { + // 创建新的数据点 + newPoint := nearestPoint + newPoint.Timestamp = t + result = append(result, newPoint) + } + } + } + + return result +} + +// findNearestPoint 查找最接近指定时间的数据点 +func findNearestPoint(t time.Time, timestamps []int64, dataMap map[int64]model.AggregatedData) model.AggregatedData { + target := t.Unix() + nearest := int64(0) + minDiff := int64(math.MaxInt64) + + for _, ts := range timestamps { + diff := int64(math.Abs(float64(ts - target))) + if diff < minDiff { + minDiff = diff + nearest = ts + } + } + + // 如果最近的点超过10分钟,返回空值 + if minDiff > 10*60 { + return model.AggregatedData{} + } + + return dataMap[nearest] +} diff --git a/static/index.html b/static/index.html index 366e65f..97a7a7a 100644 --- a/static/index.html +++ b/static/index.html @@ -128,9 +128,10 @@