feat: 新增1分钟的粒度

This commit is contained in:
fengyarnom 2025-05-15 18:38:18 +08:00
parent a673267fd3
commit 74aaef9ad5
2 changed files with 126 additions and 4 deletions

View File

@ -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]
}

View File

@ -128,9 +128,10 @@
<div class="control-group">
<label for="interval">数据粒度:</label>
<select id="interval">
<option value="5min">5分钟</option>
<option value="1min">1分钟</option>
<option value="5min" selected>5分钟</option>
<option value="30min">30分钟</option>
<option value="1hour" selected>1小时</option>
<option value="1hour">1小时</option>
</select>
</div>
@ -255,20 +256,39 @@
function queryLatestData() {
const interval = document.getElementById('interval').value;
// 计算最近1小时的时间范围
// 根据粒度调整查询范围
let hours = 1;
if (interval === "1min") {
hours = 0.5; // 30分钟
} else if (interval === "5min") {
hours = 1; // 1小时
} else if (interval === "30min") {
hours = 6; // 6小时
} else {
hours = 24; // 1天
}
// 计算最近时间范围 - 使用当天0点到现在
const endTime = new Date();
const startTime = new Date(endTime.getTime() - (1 * 60 * 60 * 1000)); // 1小时前
const startTime = new Date(endTime);
startTime.setHours(0, 0, 0, 0); // 设置为当天0点
// 确保时间格式符合后端要求
const startDateTime = startTime.toISOString();
const endDateTime = endTime.toISOString();
// 加载状态指示
document.getElementById('mainChart').style.opacity = 0.5;
fetch(`/api/latest?interval=${interval}&start=${startDateTime}&end=${endDateTime}`)
.then(response => response.json())
.then(data => {
updateChart(data);
updateTable(data);
// 恢复正常显示
document.getElementById('mainChart').style.opacity = 1;
// 自动更新日期选择器为最近查询的时间范围
document.getElementById('startDate').value = formatDateTime(startTime);
document.getElementById('endDate').value = formatDateTime(endTime);
@ -276,6 +296,8 @@
.catch(error => {
console.error('Error:', error);
alert('获取最新数据失败,请检查网络连接');
// 恢复正常显示
document.getElementById('mainChart').style.opacity = 1;
});
}