feat: 新增1分钟的粒度
This commit is contained in:
parent
a673267fd3
commit
74aaef9ad5
@ -2,6 +2,8 @@ package dao
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go_rain_dtu/internal/model"
|
"go_rain_dtu/internal/model"
|
||||||
@ -46,6 +48,8 @@ func (dao *SensorDAO) GetAggregatedData(start, end time.Time, interval string) (
|
|||||||
var intervalMinutes int
|
var intervalMinutes int
|
||||||
|
|
||||||
switch interval {
|
switch interval {
|
||||||
|
case "1min":
|
||||||
|
intervalMinutes = 1
|
||||||
case "5min":
|
case "5min":
|
||||||
intervalMinutes = 5
|
intervalMinutes = 5
|
||||||
case "30min":
|
case "30min":
|
||||||
@ -116,6 +120,102 @@ func (dao *SensorDAO) GetAggregatedData(start, end time.Time, interval string) (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果是1分钟粒度,且数据不足,可能需要进行插值
|
||||||
|
if intervalMinutes == 1 && len(result) > 0 {
|
||||||
|
result = fillMissingMinutes(result, start, end)
|
||||||
|
}
|
||||||
|
|
||||||
logger.Logger.Printf("聚合查询成功: 返回%d条记录 (间隔=%s)", len(result), interval)
|
logger.Logger.Printf("聚合查询成功: 返回%d条记录 (间隔=%s)", len(result), interval)
|
||||||
return result, nil
|
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]
|
||||||
|
}
|
||||||
|
|||||||
@ -128,9 +128,10 @@
|
|||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label for="interval">数据粒度:</label>
|
<label for="interval">数据粒度:</label>
|
||||||
<select id="interval">
|
<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="30min">30分钟</option>
|
||||||
<option value="1hour" selected>1小时</option>
|
<option value="1hour">1小时</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -255,20 +256,39 @@
|
|||||||
function queryLatestData() {
|
function queryLatestData() {
|
||||||
const interval = document.getElementById('interval').value;
|
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 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 startDateTime = startTime.toISOString();
|
||||||
const endDateTime = endTime.toISOString();
|
const endDateTime = endTime.toISOString();
|
||||||
|
|
||||||
|
// 加载状态指示
|
||||||
|
document.getElementById('mainChart').style.opacity = 0.5;
|
||||||
|
|
||||||
fetch(`/api/latest?interval=${interval}&start=${startDateTime}&end=${endDateTime}`)
|
fetch(`/api/latest?interval=${interval}&start=${startDateTime}&end=${endDateTime}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
updateChart(data);
|
updateChart(data);
|
||||||
updateTable(data);
|
updateTable(data);
|
||||||
|
|
||||||
|
// 恢复正常显示
|
||||||
|
document.getElementById('mainChart').style.opacity = 1;
|
||||||
|
|
||||||
// 自动更新日期选择器为最近查询的时间范围
|
// 自动更新日期选择器为最近查询的时间范围
|
||||||
document.getElementById('startDate').value = formatDateTime(startTime);
|
document.getElementById('startDate').value = formatDateTime(startTime);
|
||||||
document.getElementById('endDate').value = formatDateTime(endTime);
|
document.getElementById('endDate').value = formatDateTime(endTime);
|
||||||
@ -276,6 +296,8 @@
|
|||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
alert('获取最新数据失败,请检查网络连接');
|
alert('获取最新数据失败,请检查网络连接');
|
||||||
|
// 恢复正常显示
|
||||||
|
document.getElementById('mainChart').style.opacity = 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user