2025-07-10 11:57:10 +08:00

278 lines
7.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package db
import (
"database/sql"
"fmt"
_ "gi
"log"
/go-sql-driver/mysql"
"rain_monitor/models"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
type DBConfig struct {
Host string
Port int
User string
Password string
DBName string
}
func InitDB(config DBConfig) error {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=true&loc=Local",
config.User, config.Password, config.Host, config.Port, config.DBName)
var err error
db, err = sql.Open("mysql", dsn)
if err != nil {
return fmt.Errorf("数据库连接失败: %v", err)
}
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(time.Hour)
if err = db.Ping(); err != nil {
return fmt.Errorf("数据库Ping失败: %v", err)
}
if err = createTables(); err != nil {
return fmt.Errorf("创建表失败: %v", err)
}
log.Println("数据库连接成功")
return nil
}
func CloseDB() {
if db != nil {
db.Close()
}
}
func createTables() error {
_, err := db.Exec(models.CreateWeatherDataTable)
if err != nil {
return fmt.Errorf("创建气象站数据表失败: %v", err)
}
_, err = db.Exec(models.CreateRainGaugeDataTable)
if err != nil {
return fmt.Errorf("创建雨量计数据表失败: %v", err)
}
return nil
}
func SaveWeatherData(data *models.WeatherData) (int64, error) {
result, err := db.Exec(models.InsertWeatherDataSQL,
data.Timestamp, data.WindSpeed, data.WindForce, data.WindDirection8, data.WindDirection360,
data.Humidity, data.Temperature, data.Noise, data.PM25, data.PM10, data.AtmPressure,
data.LuxHigh, data.LuxLow, data.LightIntensity, data.Rainfall, data.CompassAngle, data.SolarRadiation)
if err != nil {
return 0, fmt.Errorf("保存气象站数据失败: %v", err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("获取插入ID失败: %v", err)
}
return id, nil
}
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
if err != nil {
log.Printf("查询最近雨量计数据失败: %v", err)
// 即使查询失败,我们仍然尝试插入新数据
} else {
// 检查是否有相似数据
for rows.Next() {
var id int64
var ts time.Time
if err := rows.Scan(&id, &ts, &rainfall); err != nil {
log.Printf("扫描雨量计数据失败: %v", err)
continue
// 如果时间非常接近小于1分钟且雨量数据相同或非常接近则认为是重复数据
timeDiff := data.Timestamp.Sub(ts)
if timeDiff < time.Minute && rainfallDiff < 0.1 {
log.Printf("检测到重复的雨量计数据跳过插入。ID=%d, 时间=%v", id, ts)
return id, nil // 返回已存在的记录ID
}
}
}
result, err := db.Exec(models.InsertRainGaugeDataSQL,
data.Timestamp, data.DailyRainfall, data.InstantRainfall, data.YesterdayRainfall,
data.TotalRainfall, data.HourlyRainfall, data.LastHourRainfall, data.Max24hRainfall,
data.Max24hRainfallPeriod, data.Min24hRainfall, data.Min24hRainfallPeriod)
if err != nil {
return 0, fmt.Errorf("保存雨量计数据失败: %v", err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("获取插入ID失败: %v", err)
}
log.Printf("雨量计数据已保存ID=%d, 时间=%v", id, data.Timestamp)
return id, nil
}
func GetWeatherDataByTimeRange(start, end time.Time) ([]models.WeatherData, error) {
rows, err := db.Query(models.QueryWeatherDataByTimeRangeSQL, start, end)
if err != nil {
return nil, fmt.Errorf("查询气象站数据失败: %v", err)
}
defer rows.Close()
var result []models.WeatherData
for rows.Next() {
var data models.WeatherData
err := rows.Scan(
&data.ID, &data.Timestamp, &data.WindSpeed, &data.WindForce, &data.WindDirection8,
&data.WindDirection360, &data.Humidity, &data.Temperature, &data.Noise, &data.PM25,
&data.PM10, &data.AtmPressure, &data.LuxHigh, &data.LuxLow, &data.LightIntensity,
&data.Rainfall, &data.CompassAngle, &data.SolarRadiation,
)
if err != nil {
return nil, fmt.Errorf("扫描气象站数据失败: %v", err)
}
result = append(result, data)
}
return result, nil
}
func GetRainGaugeDataByTimeRange(start, end time.Time) ([]models.RainGaugeData, error) {
rows, err := db.Query(models.QueryRainGaugeDataByTimeRangeSQL, start, end)
if err != nil {
return nil, fmt.Errorf("查询雨量计数据失败: %v", err)
}
defer rows.Close()
var result []models.RainGaugeData
for rows.Next() {
var data models.RainGaugeData
err := rows.Scan(
&data.ID, &data.Timestamp, &data.DailyRainfall, &data.InstantRainfall,
&data.YesterdayRainfall, &data.TotalRainfall, &data.HourlyRainfall,
&data.LastHourRainfall, &data.Max24hRainfall, &data.Max24hRainfallPeriod,
&data.Min24hRainfall, &data.Min24hRainfallPeriod,
)
if err != nil {
return nil, fmt.Errorf("扫描雨量计数据失败: %v", err)
}
result = append(result, data)
}
return result, nil
}
func GetLatestWeatherData() (*models.WeatherData, error) {
row := db.QueryRow(models.QueryLatestWeatherDataSQL)
if row == nil {
return nil, fmt.Errorf("没有气象站数据")
}
data := &models.WeatherData{}
err := row.Scan(
&data.ID, &data.Timestamp, &data.WindSpeed, &data.WindForce, &data.WindDirection8,
&data.WindDirection360, &data.Humidity, &data.Temperature, &data.Noise, &data.PM25,
&data.PM10, &data.AtmPressure, &data.LuxHigh, &data.LuxLow, &data.LightIntensity,
&data.Rainfall, &data.CompassAngle, &data.SolarRadiation,
)
if err == sql.ErrNoRows {
return nil, nil
} else if err != nil {
return nil, fmt.Errorf("扫描最新气象站数据失败: %v", err)
}
return data, nil
}
func GetLatestRainGaugeData() (*models.RainGaugeData, error) {
row := db.QueryRow(models.QueryLatestRainGaugeDataSQL)
if row == nil {
return nil, fmt.Errorf("没有雨量计数据")
}
data := &models.RainGaugeData{}
err := row.Scan(
&data.ID, &data.Timestamp, &data.DailyRainfall, &data.InstantRainfall,
&data.YesterdayRainfall, &data.TotalRainfall, &data.HourlyRainfall,
&data.LastHourRainfall, &data.Max24hRainfall, &data.Max24hRainfallPeriod,
&data.Min24hRainfall, &data.Min24hRainfallPeriod,
)
if err == sql.ErrNoRows {
return nil, nil
} else if err != nil {
return nil, fmt.Errorf("扫描最新雨量计数据失败: %v", err)
}
return data, nil
}
func GetAggregatedData(start, end time.Time) ([]models.AggregatedData, error) {
log.Printf("GetAggregatedData调用 - 时间范围: 开始=%v, 结束=%v", start, end)
rows, err := db.Query(models.QueryAggregatedDataSQL, start, end, start, end)
if err != nil {
log.Printf("SQL查询失败: %v", err)
return nil, fmt.Errorf("查询聚合数据失败: %v", err)
}
defer rows.Close()
var result []models.AggregatedData
for rows.Next() {
var data models.AggregatedData
var timestampStr string
err := rows.Scan(
&timestampStr, &data.Rainfall, &data.AvgTemperature, &data.AvgHumidity,
&data.AvgWindSpeed, &data.AtmPressure, &data.SolarRadiation,
)
if err != nil {
log.Printf("扫描行数据失败: %v", err)
return nil, fmt.Errorf("扫描聚合数据失败: %v", err)
// 设置格式化时间字符串
// 尝试解析时间字符串为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)
}
log.Printf("查询结果: 找到 %d 条记录", len(result))
return result, nil
}