package handler import ( "encoding/json" "fmt" "net/http" "time" "go_rain_dtu/internal/dao" "go_rain_dtu/internal/model" "go_rain_dtu/internal/tcp" "go_rain_dtu/pkg/logger" ) type SensorHandler struct { dao *dao.SensorDAO } func NewSensorHandler(dao *dao.SensorDAO) *SensorHandler { return &SensorHandler{dao: dao} } func (h *SensorHandler) GetAggregatedData(w http.ResponseWriter, r *http.Request) { interval := r.URL.Query().Get("interval") if interval == "" { interval = "1hour" } // 获取中国时区 chinaLoc, locErr := time.LoadLocation("Asia/Shanghai") if locErr != nil { chinaLoc = time.FixedZone("CST", 8*60*60) } endTime := time.Now().In(chinaLoc) startTime := endTime.Add(-24 * time.Hour) // 默认显示24小时数据 if startStr := r.URL.Query().Get("start"); startStr != "" { if t, parseErr := time.Parse(time.RFC3339, startStr); parseErr == nil { startTime = t.In(chinaLoc) } } if endStr := r.URL.Query().Get("end"); endStr != "" { if t, parseErr := time.Parse(time.RFC3339, endStr); parseErr == nil { endTime = t.In(chinaLoc) } } var data []model.AggregatedData var err error // 判断是否获取原始数据 if interval == "raw" { data, err = h.dao.GetRawData(startTime, endTime) } else { data, err = h.dao.GetAggregatedData(startTime, endTime, interval) } if err != nil { logger.Logger.Printf("获取数据失败: %v", err) http.Error(w, "服务器内部错误", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") // 使用自定义的编码器,设置时间格式 encoder := json.NewEncoder(w) encoder.SetIndent("", " ") if err := encoder.Encode(data); err != nil { logger.Logger.Printf("JSON编码失败: %v", err) http.Error(w, "服务器内部错误", http.StatusInternalServerError) return } } // 处理静态文件 func (h *SensorHandler) ServeStatic(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { http.ServeFile(w, r, "static/index.html") return } http.FileServer(http.Dir("static")).ServeHTTP(w, r) } // GetConnectionStatus 返回当前TCP连接状态 func (h *SensorHandler) GetConnectionStatus(w http.ResponseWriter, r *http.Request) { connected, ip, port := tcp.GetConnectionStatus() status := struct { Connected bool `json:"connected"` IP string `json:"ip"` Port string `json:"port"` Count int `json:"count"` // 添加连接数量 }{ Connected: connected, IP: ip, Port: port, Count: tcp.GetActiveConnectionCount(), // 获取活跃连接数 } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(status); err != nil { logger.Logger.Printf("JSON编码失败: %v", err) http.Error(w, "服务器内部错误", http.StatusInternalServerError) } } // GetLatestData 获取最新的聚合数据 func (h *SensorHandler) GetLatestData(w http.ResponseWriter, r *http.Request) { interval := r.URL.Query().Get("interval") if interval == "" { interval = "5min" // 默认使用更细粒度的时间间隔 } // 默认查询最近1小时的数据 endTime := time.Now() startTime := endTime.Add(-1 * time.Hour) // 如果提供了时间范围,则使用提供的范围 if startStr := r.URL.Query().Get("start"); startStr != "" { if t, err := time.Parse(time.RFC3339, startStr); err == nil { startTime = t } } if endStr := r.URL.Query().Get("end"); endStr != "" { if t, err := time.Parse(time.RFC3339, endStr); err == nil { endTime = t } } // 查询数据库获取数据 var data []model.AggregatedData var err error // 判断是否获取原始数据 if interval == "raw" { data, err = h.dao.GetRawData(startTime, endTime) } else { data, err = h.dao.GetAggregatedData(startTime, endTime, interval) } if err != nil { logger.Logger.Printf("获取数据失败: %v", err) http.Error(w, "服务器内部错误", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") encoder := json.NewEncoder(w) encoder.SetIndent("", " ") if err := encoder.Encode(data); err != nil { logger.Logger.Printf("JSON编码失败: %v", err) http.Error(w, "服务器内部错误", http.StatusInternalServerError) return } } // GetLatestSensorRawData 获取最新的一条传感器原始数据 func (h *SensorHandler) GetLatestSensorRawData(w http.ResponseWriter, r *http.Request) { data, err := h.dao.GetLatestSensorData() if err != nil { logger.Logger.Printf("获取最新传感器数据失败: %v", err) http.Error(w, "服务器内部错误", http.StatusInternalServerError) return } if data == nil { // 没有数据,返回空对象 w.Header().Set("Content-Type", "application/json") w.Write([]byte("{}")) return } // 将时间调整为中国时区(UTC+8) chinaLoc, err := time.LoadLocation("Asia/Shanghai") if err != nil { // 如果无法加载时区,手动创建UTC+8的时区 chinaLoc = time.FixedZone("CST", 8*60*60) } chinaTime := data.Timestamp.In(chinaLoc) // 转换为前端友好的格式 response := struct { ID int64 `json:"id"` Timestamp time.Time `json:"timestamp"` FormattedTime string `json:"formatted_time"` WindSpeed float64 `json:"wind_speed"` // 风速(m/s) WindForce int `json:"wind_force"` // 风力(级) WindDirection8 int `json:"wind_direction_8"` // 8方位风向 WindDirection360 int `json:"wind_direction_360"` // 360度风向 Humidity float64 `json:"humidity"` // 湿度(%) Temperature float64 `json:"temperature"` // 温度(℃) AtmPressure float64 `json:"atm_pressure"` // 大气压(kPa) SolarRadiation int `json:"solar_radiation"` // 太阳辐射(W/m²) Rainfall float64 `json:"rainfall"` // 累计雨量(mm) }{ ID: data.ID, Timestamp: data.Timestamp, FormattedTime: chinaTime.Format("2006-01-02 15:04:05"), WindSpeed: float64(data.WindSpeed) / 100.0, WindForce: data.WindForce, WindDirection8: data.WindDirection8, WindDirection360: data.WindDirection360, Humidity: float64(data.Humidity) / 10.0, Temperature: float64(data.Temperature) / 10.0, AtmPressure: float64(data.AtmPressure) / 10.0, SolarRadiation: data.SolarRadiation, Rainfall: float64(data.OpticalRain) / 10.0, } w.Header().Set("Content-Type", "application/json") encoder := json.NewEncoder(w) encoder.SetIndent("", " ") if err := encoder.Encode(response); err != nil { logger.Logger.Printf("JSON编码失败: %v", err) http.Error(w, "服务器内部错误", http.StatusInternalServerError) return } } // TriggerManualQuery 手动触发向所有设备发送查询指令 func (h *SensorHandler) TriggerManualQuery(w http.ResponseWriter, r *http.Request) { count := tcp.TriggerManualQuery() response := struct { Success bool `json:"success"` Message string `json:"message"` Count int `json:"count"` }{ Success: count > 0, Message: fmt.Sprintf("成功向%d个设备发送查询指令", count), Count: count, } w.Header().Set("Content-Type", "application/json") encoder := json.NewEncoder(w) if err := encoder.Encode(response); err != nil { logger.Logger.Printf("JSON编码失败: %v", err) http.Error(w, "服务器内部错误", http.StatusInternalServerError) } }