Compare commits
3 Commits
2599b00626
...
c6c9015cd3
| Author | SHA1 | Date | |
|---|---|---|---|
| c6c9015cd3 | |||
| 0067c2d15e | |||
| fe240df61c |
55
db/db.go
55
db/db.go
@ -3,11 +3,11 @@ package db
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"log"
|
"log"
|
||||||
|
"math"
|
||||||
"rain_monitor/models"
|
"rain_monitor/models"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var db *sql.DB
|
var db *sql.DB
|
||||||
@ -84,6 +84,44 @@ func SaveWeatherData(data *models.WeatherData) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SaveRainGaugeData(data *models.RainGaugeData) (int64, error) {
|
func SaveRainGaugeData(data *models.RainGaugeData) (int64, error) {
|
||||||
|
// 检查是否在短时间内已经插入过类似数据
|
||||||
|
// 获取最近5分钟内的数据
|
||||||
|
fiveMinutesAgo := data.Timestamp.Add(-5 * time.Minute)
|
||||||
|
rows, err := db.Query(`
|
||||||
|
SELECT id, timestamp, total_rainfall
|
||||||
|
FROM rain_gauge_data
|
||||||
|
WHERE timestamp BETWEEN ? AND ?
|
||||||
|
ORDER BY timestamp DESC
|
||||||
|
`, fiveMinutesAgo, data.Timestamp)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("查询最近雨量计数据失败: %v", err)
|
||||||
|
// 即使查询失败,我们仍然尝试插入新数据
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
// 检查是否有相似数据
|
||||||
|
for rows.Next() {
|
||||||
|
var id int64
|
||||||
|
var ts time.Time
|
||||||
|
var rainfall float64
|
||||||
|
|
||||||
|
if err := rows.Scan(&id, &ts, &rainfall); err != nil {
|
||||||
|
log.Printf("扫描雨量计数据失败: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果时间非常接近(小于1分钟)且雨量数据相同或非常接近,则认为是重复数据
|
||||||
|
timeDiff := data.Timestamp.Sub(ts)
|
||||||
|
rainfallDiff := math.Abs(data.TotalRainfall - rainfall)
|
||||||
|
|
||||||
|
if timeDiff < time.Minute && rainfallDiff < 0.1 {
|
||||||
|
log.Printf("检测到重复的雨量计数据,跳过插入。ID=%d, 时间=%v", id, ts)
|
||||||
|
return id, nil // 返回已存在的记录ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result, err := db.Exec(models.InsertRainGaugeDataSQL,
|
result, err := db.Exec(models.InsertRainGaugeDataSQL,
|
||||||
data.Timestamp, data.DailyRainfall, data.InstantRainfall, data.YesterdayRainfall,
|
data.Timestamp, data.DailyRainfall, data.InstantRainfall, data.YesterdayRainfall,
|
||||||
data.TotalRainfall, data.HourlyRainfall, data.LastHourRainfall, data.Max24hRainfall,
|
data.TotalRainfall, data.HourlyRainfall, data.LastHourRainfall, data.Max24hRainfall,
|
||||||
@ -96,6 +134,7 @@ func SaveRainGaugeData(data *models.RainGaugeData) (int64, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("获取插入ID失败: %v", err)
|
return 0, fmt.Errorf("获取插入ID失败: %v", err)
|
||||||
}
|
}
|
||||||
|
log.Printf("雨量计数据已保存,ID=%d, 时间=%v", id, data.Timestamp)
|
||||||
|
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
@ -216,7 +255,19 @@ func GetAggregatedData(start, end time.Time) ([]models.AggregatedData, error) {
|
|||||||
log.Printf("扫描行数据失败: %v", err)
|
log.Printf("扫描行数据失败: %v", err)
|
||||||
return nil, fmt.Errorf("扫描聚合数据失败: %v", err)
|
return nil, fmt.Errorf("扫描聚合数据失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置格式化时间字符串
|
||||||
data.FormattedTime = timestampStr
|
data.FormattedTime = timestampStr
|
||||||
|
|
||||||
|
// 尝试解析时间字符串为time.Time类型
|
||||||
|
timestamp, err := time.Parse("2006-01-02 15:04:05", timestampStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("解析时间字符串失败: %v, 原始字符串: %s", err, timestampStr)
|
||||||
|
// 即使解析失败,也继续处理,前端会使用FormattedTime
|
||||||
|
} else {
|
||||||
|
data.Timestamp = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
result = append(result, data)
|
result = append(result, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -160,7 +160,7 @@ LIMIT 1
|
|||||||
const QueryAggregatedDataSQL = `
|
const QueryAggregatedDataSQL = `
|
||||||
SELECT
|
SELECT
|
||||||
w.time_hour as timestamp,
|
w.time_hour as timestamp,
|
||||||
MAX(r.daily_rainfall) as rainfall,
|
MAX(r.hourly_rainfall) as rainfall,
|
||||||
AVG(w.temperature) as avg_temperature,
|
AVG(w.temperature) as avg_temperature,
|
||||||
AVG(w.humidity) as avg_humidity,
|
AVG(w.humidity) as avg_humidity,
|
||||||
AVG(w.wind_speed) as avg_wind_speed,
|
AVG(w.wind_speed) as avg_wind_speed,
|
||||||
@ -176,7 +176,7 @@ FROM
|
|||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
(SELECT
|
(SELECT
|
||||||
DATE_FORMAT(timestamp, '%Y-%m-%d %H:00:00') as time_hour,
|
DATE_FORMAT(timestamp, '%Y-%m-%d %H:00:00') as time_hour,
|
||||||
daily_rainfall
|
hourly_rainfall
|
||||||
FROM rain_gauge_data
|
FROM rain_gauge_data
|
||||||
WHERE timestamp BETWEEN ? AND ?
|
WHERE timestamp BETWEEN ? AND ?
|
||||||
) r ON w.time_hour = r.time_hour
|
) r ON w.time_hour = r.time_hour
|
||||||
|
|||||||
@ -247,7 +247,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>时间</th>
|
<th>时间</th>
|
||||||
<th>降雨量(mm)</th>
|
<th>小时降雨量(mm)</th>
|
||||||
<th>温度(℃)</th>
|
<th>温度(℃)</th>
|
||||||
<th>湿度(%)</th>
|
<th>湿度(%)</th>
|
||||||
<th>风速(m/s)</th>
|
<th>风速(m/s)</th>
|
||||||
@ -544,22 +544,34 @@
|
|||||||
try {
|
try {
|
||||||
// 按时间正序排列数据(从早到晚)
|
// 按时间正序排列数据(从早到晚)
|
||||||
data.sort((a, b) => {
|
data.sort((a, b) => {
|
||||||
const timeA = a.timestamp ? new Date(a.timestamp) : new Date(a.formatted_time);
|
const timeA = a.formatted_time ? new Date(a.formatted_time) : new Date(a.timestamp);
|
||||||
const timeB = b.timestamp ? new Date(b.timestamp) : new Date(b.formatted_time);
|
const timeB = b.formatted_time ? new Date(b.formatted_time) : new Date(b.timestamp);
|
||||||
return timeA - timeB;
|
return timeA - timeB;
|
||||||
});
|
});
|
||||||
|
|
||||||
const labels = data.map(item => {
|
const labels = data.map(item => {
|
||||||
// 解析时间字符串为本地时间
|
// 优先使用formatted_time,如果不存在则尝试使用timestamp
|
||||||
const timeStr = item.formatted_time || item.timestamp;
|
let timeStr = item.formatted_time || item.timestamp;
|
||||||
const date = new Date(timeStr);
|
|
||||||
|
|
||||||
// 格式化为中文日期时间格式
|
try {
|
||||||
return date.getFullYear() + '/' +
|
// 解析时间字符串为本地时间
|
||||||
(date.getMonth() + 1).toString().padStart(2, '0') + '/' +
|
const date = new Date(timeStr);
|
||||||
date.getDate().toString().padStart(2, '0') + ' ' +
|
|
||||||
date.getHours().toString().padStart(2, '0') + ':' +
|
// 检查日期是否有效
|
||||||
date.getMinutes().toString().padStart(2, '0');
|
if (isNaN(date.getTime())) {
|
||||||
|
return timeStr; // 如果无法解析,直接返回原始字符串
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化为中文日期时间格式
|
||||||
|
return date.getFullYear() + '/' +
|
||||||
|
(date.getMonth() + 1).toString().padStart(2, '0') + '/' +
|
||||||
|
date.getDate().toString().padStart(2, '0') + ' ' +
|
||||||
|
date.getHours().toString().padStart(2, '0') + ':' +
|
||||||
|
date.getMinutes().toString().padStart(2, '0');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('图表时间解析错误:', e);
|
||||||
|
return timeStr; // 出错时返回原始字符串
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 是否使用自适应范围
|
// 是否使用自适应范围
|
||||||
@ -588,7 +600,7 @@
|
|||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
label: '降雨量(mm)',
|
label: '小时降雨量(mm)',
|
||||||
data: data.map(item => item.rainfall !== null && !isNaN(item.rainfall) ? item.rainfall : 0),
|
data: data.map(item => item.rainfall !== null && !isNaN(item.rainfall) ? item.rainfall : 0),
|
||||||
backgroundColor: 'rgba(54, 162, 235, 0.5)',
|
backgroundColor: 'rgba(54, 162, 235, 0.5)',
|
||||||
borderColor: 'rgba(54, 162, 235, 1)',
|
borderColor: 'rgba(54, 162, 235, 1)',
|
||||||
@ -618,7 +630,7 @@
|
|||||||
position: 'left',
|
position: 'left',
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: '降雨量(mm)'
|
text: '小时降雨量(mm)'
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
drawOnChartArea: false
|
drawOnChartArea: false
|
||||||
@ -654,20 +666,37 @@
|
|||||||
tbody.innerHTML = '';
|
tbody.innerHTML = '';
|
||||||
|
|
||||||
// 按时间倒序排列数据(从晚到早),这样最新的数据在表格顶部
|
// 按时间倒序排列数据(从晚到早),这样最新的数据在表格顶部
|
||||||
const sortedData = [...data].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
|
const sortedData = [...data].sort((a, b) => {
|
||||||
|
// 使用formatted_time进行排序,如果不存在则尝试使用timestamp
|
||||||
|
const timeA = a.formatted_time ? new Date(a.formatted_time) : new Date(a.timestamp);
|
||||||
|
const timeB = b.formatted_time ? new Date(b.formatted_time) : new Date(b.timestamp);
|
||||||
|
return timeB - timeA;
|
||||||
|
});
|
||||||
|
|
||||||
sortedData.forEach(item => {
|
sortedData.forEach(item => {
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
// 解析时间字符串为本地时间
|
|
||||||
const date = new Date(item.timestamp);
|
|
||||||
|
|
||||||
const formattedDate =
|
// 优先使用formatted_time,如果不存在则尝试使用timestamp
|
||||||
date.getFullYear() + '/' +
|
let formattedDate;
|
||||||
(date.getMonth() + 1).toString().padStart(2, '0') + '/' +
|
if (item.formatted_time) {
|
||||||
date.getDate().toString().padStart(2, '0') + ' ' +
|
// 如果已经有格式化好的时间,直接使用
|
||||||
date.getHours().toString().padStart(2, '0') + ':' +
|
formattedDate = item.formatted_time;
|
||||||
date.getMinutes().toString().padStart(2, '0') + ':' +
|
} else {
|
||||||
date.getSeconds().toString().padStart(2, '0');
|
// 否则尝试解析timestamp并格式化
|
||||||
|
try {
|
||||||
|
const date = new Date(item.timestamp);
|
||||||
|
formattedDate =
|
||||||
|
date.getFullYear() + '/' +
|
||||||
|
(date.getMonth() + 1).toString().padStart(2, '0') + '/' +
|
||||||
|
date.getDate().toString().padStart(2, '0') + ' ' +
|
||||||
|
date.getHours().toString().padStart(2, '0') + ':' +
|
||||||
|
date.getMinutes().toString().padStart(2, '0') + ':' +
|
||||||
|
date.getSeconds().toString().padStart(2, '0');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('时间解析错误:', e);
|
||||||
|
formattedDate = '时间格式错误';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<td>${formattedDate}</td>
|
<td>${formattedDate}</td>
|
||||||
@ -686,13 +715,13 @@
|
|||||||
function exportData() {
|
function exportData() {
|
||||||
// 从表格中获取完整数据
|
// 从表格中获取完整数据
|
||||||
const tableRows = document.querySelectorAll('#tableBody tr');
|
const tableRows = document.querySelectorAll('#tableBody tr');
|
||||||
let csv = '时间,降雨量(mm),温度(℃),湿度(%),风速(m/s),大气压(kPa),太阳辐射(W/m²)\n';
|
let csv = '时间,小时降雨量(mm),温度(℃),湿度(%),风速(m/s),大气压(kPa),太阳辐射(W/m²)\n';
|
||||||
|
|
||||||
tableRows.forEach(row => {
|
tableRows.forEach(row => {
|
||||||
const cells = row.querySelectorAll('td');
|
const cells = row.querySelectorAll('td');
|
||||||
const rowData = [
|
const rowData = [
|
||||||
cells[0].textContent, // 时间
|
cells[0].textContent, // 时间
|
||||||
cells[1].textContent, // 降雨量
|
cells[1].textContent, // 小时降雨量
|
||||||
cells[2].textContent, // 温度
|
cells[2].textContent, // 温度
|
||||||
cells[3].textContent, // 湿度
|
cells[3].textContent, // 湿度
|
||||||
cells[4].textContent, // 风速
|
cells[4].textContent, // 风速
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user