package api import ( "encoding/json" "log" "net/http" "rain_monitor/db" "rain_monitor/modbus" "rain_monitor/models" "time" ) // StartWebServer 启动Web服务器 func StartWebServer() { http.HandleFunc("/api/status", handleStatus) http.HandleFunc("/api/raw/latest", handleLatestRawData) http.HandleFunc("/api/trigger-query", handleTriggerQuery) http.HandleFunc("/api/data", handleQueryData) http.HandleFunc("/api/latest", handleLatestData) // 静态文件服务 http.Handle("/", http.FileServer(http.Dir("static"))) log.Println("Web服务器已启动,监听端口 10003") err := http.ListenAndServe(":10003", nil) if err != nil { log.Fatalf("Web服务器启动失败: %v", err) } } // handleStatus 处理连接状态请求 func handleStatus(w http.ResponseWriter, r *http.Request) { status := modbus.GetConnectionStatus() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(status) } // handleLatestRawData 获取最新原始数据 func handleLatestRawData(w http.ResponseWriter, r *http.Request) { // 从数据库获取最新数据,而不是从modbus内存中获取 weatherData, err1 := db.GetLatestWeatherData() rainData, err2 := db.GetLatestRainGaugeData() if (weatherData == nil && rainData == nil) || (err1 != nil && err2 != nil) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) json.NewEncoder(w).Encode(map[string]interface{}{ "error": "没有可用的数据", "details": map[string]interface{}{ "weather_error": err1, "rain_error": err2, }, }) return } result := map[string]interface{}{} // 使用数据库中的时间戳,如果可用 if weatherData != nil && !weatherData.Timestamp.IsZero() { result["timestamp"] = weatherData.Timestamp.Format(time.RFC3339) result["formatted_time"] = weatherData.Timestamp.Format("2006-01-02 15:04:05") } else if rainData != nil && !rainData.Timestamp.IsZero() { result["timestamp"] = rainData.Timestamp.Format(time.RFC3339) result["formatted_time"] = rainData.Timestamp.Format("2006-01-02 15:04:05") } else { // 如果都没有时间戳,则使用当前时间作为 fallback result["timestamp"] = time.Now().Format(time.RFC3339) result["formatted_time"] = time.Now().Format("2006-01-02 15:04:05") } if weatherData != nil { result["temperature"] = weatherData.Temperature result["humidity"] = weatherData.Humidity result["wind_speed"] = weatherData.WindSpeed result["wind_direction_8"] = weatherData.WindDirection8 result["wind_direction_360"] = weatherData.WindDirection360 result["atm_pressure"] = weatherData.AtmPressure result["solar_radiation"] = weatherData.SolarRadiation result["weather_rainfall"] = weatherData.Rainfall // 区分气象站的雨量 } if rainData != nil { result["total_rainfall"] = rainData.TotalRainfall // 使用 total_rainfall 表示累计雨量 result["daily_rainfall"] = rainData.DailyRainfall result["instant_rainfall"] = rainData.InstantRainfall } // 为了兼容旧代码,仍然提供一个 rainfall 字段,优先使用雨量计的数据 if rainData != nil { result["rainfall"] = rainData.TotalRainfall } else if weatherData != nil { result["rainfall"] = weatherData.Rainfall } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(result) } // handleTriggerQuery 触发设备查询(保留手动采集功能) func handleTriggerQuery(w http.ResponseWriter, r *http.Request) { err1 := modbus.QueryDevice(modbus.DeviceWeatherStation) err2 := modbus.QueryDevice(modbus.DeviceRainGauge) result := map[string]interface{}{ "success": err1 == nil || err2 == nil, "timestamp": time.Now().Format(time.RFC3339), } if err1 != nil { result["weather_error"] = err1.Error() } if err2 != nil { result["rain_error"] = err2.Error() } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(result) } // handleQueryData 查询历史数据 func handleQueryData(w http.ResponseWriter, r *http.Request) { startStr := r.URL.Query().Get("start") endStr := r.URL.Query().Get("end") interval := r.URL.Query().Get("interval") log.Printf("handleQueryData - 请求参数: start=%s, end=%s, interval=%s", startStr, endStr, interval) if startStr == "" || endStr == "" { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]interface{}{"error": "缺少必要的时间参数"}) return } start, err := time.Parse(time.RFC3339, startStr) if err != nil { log.Printf("开始时间解析失败: %v", err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]interface{}{"error": "开始时间格式错误"}) return } end, err := time.Parse(time.RFC3339, endStr) if err != nil { log.Printf("结束时间解析失败: %v", err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]interface{}{"error": "结束时间格式错误"}) return } data, err := db.GetAggregatedData(start, end) if err != nil { log.Printf("查询聚合数据失败: %v", err) // 返回空数组而不是错误,避免前端报错 w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode([]models.AggregatedData{}) return } log.Printf("查询成功,返回 %d 条记录", len(data)) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(data) } // handleLatestData 获取最新聚合数据 func handleLatestData(w http.ResponseWriter, r *http.Request) { startStr := r.URL.Query().Get("start") endStr := r.URL.Query().Get("end") interval := r.URL.Query().Get("interval") log.Printf("handleLatestData - 请求参数: start=%s, end=%s, interval=%s", startStr, endStr, interval) if startStr == "" || endStr == "" { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]interface{}{"error": "缺少必要的时间参数"}) return } start, err := time.Parse(time.RFC3339, startStr) if err != nil { log.Printf("开始时间解析失败: %v", err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]interface{}{"error": "开始时间格式错误"}) return } end, err := time.Parse(time.RFC3339, endStr) if err != nil { log.Printf("结束时间解析失败: %v", err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]interface{}{"error": "结束时间格式错误"}) return } // 只查数据库,不主动采集 data, err := db.GetAggregatedData(start, end) if err != nil { log.Printf("查询聚合数据失败: %v", err) // 返回空数组而不是错误,避免前端报错 w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode([]models.AggregatedData{}) return } log.Printf("查询成功,返回 %d 条记录", len(data)) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(data) }