2025-09-15 22:01:00 +08:00
|
|
|
|
package transport
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-09-24 22:34:11 +08:00
|
|
|
|
"encoding/base64" // 新增导入
|
2025-09-24 19:13:15 +08:00
|
|
|
|
"encoding/json"
|
2025-09-15 22:01:00 +08:00
|
|
|
|
"io"
|
|
|
|
|
|
"net/http"
|
2025-09-24 21:53:18 +08:00
|
|
|
|
"time"
|
2025-09-15 22:01:00 +08:00
|
|
|
|
|
|
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
2025-09-24 21:53:18 +08:00
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
|
|
|
|
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
|
|
|
|
|
"gorm.io/datatypes"
|
2025-09-15 22:01:00 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2025-09-24 19:13:15 +08:00
|
|
|
|
// ChirpStackListener 主动发送的请求的event字段, 这个字段代表事件类型
|
|
|
|
|
|
const (
|
2025-09-24 22:34:11 +08:00
|
|
|
|
eventTypeUp = "up" // 上行数据事件:当接收到设备发送的数据时触发,这是最核心的事件。
|
|
|
|
|
|
eventTypeStatus = "status" // 设备状态事件:当设备报告其状态时触发(例如电池电量、信号强度)。
|
|
|
|
|
|
eventTypeJoin = "join" // 入网事件:当设备成功加入网络时触发。
|
|
|
|
|
|
eventTypeAck = "ack" // 下行确认事件:当设备确认收到下行消息时触发。
|
|
|
|
|
|
eventTypeTxAck = "txack" // 网关发送确认事件:当网关确认已发送下行消息时触发(不代表设备已收到)。
|
|
|
|
|
|
eventTypeLog = "log" // 日志事件:当设备或 ChirpStack 产生日志信息时触发。
|
|
|
|
|
|
eventTypeLocation = "location" // 位置事件:当设备的位置被解析或更新时触发。
|
|
|
|
|
|
eventTypeIntegration = "integration" // 集成事件:当其他集成(如第三方服务)处理数据后触发。
|
2025-09-24 19:13:15 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2025-09-15 22:01:00 +08:00
|
|
|
|
// ChirpStackListener 是一个监听器, 用于监听ChirpStack反馈的设备上行事件
|
|
|
|
|
|
type ChirpStackListener struct {
|
2025-09-24 21:53:18 +08:00
|
|
|
|
logger *logs.Logger
|
|
|
|
|
|
sensorDataRepo repository.SensorDataRepository
|
|
|
|
|
|
deviceRepo repository.DeviceRepository
|
2025-09-15 22:01:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-24 21:53:18 +08:00
|
|
|
|
func NewChirpStackListener(logger *logs.Logger, sensorDataRepo repository.SensorDataRepository, deviceRepo repository.DeviceRepository) *ChirpStackListener {
|
2025-09-15 22:01:00 +08:00
|
|
|
|
return &ChirpStackListener{
|
2025-09-24 21:53:18 +08:00
|
|
|
|
logger: logger,
|
|
|
|
|
|
sensorDataRepo: sensorDataRepo,
|
|
|
|
|
|
deviceRepo: deviceRepo,
|
2025-09-15 22:01:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-24 19:13:15 +08:00
|
|
|
|
// Handler 监听ChirpStack反馈的事件, 因为这是个Webhook, 所以直接回复掉再慢慢处理信息
|
2025-09-15 22:01:00 +08:00
|
|
|
|
func (c *ChirpStackListener) Handler() http.HandlerFunc {
|
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
2025-09-24 19:13:15 +08:00
|
|
|
|
defer r.Body.Close()
|
|
|
|
|
|
|
2025-09-15 22:01:00 +08:00
|
|
|
|
b, err := io.ReadAll(r.Body)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.logger.Errorf("读取请求体失败: %v", err)
|
2025-09-24 19:13:15 +08:00
|
|
|
|
http.Error(w, "failed to read body", http.StatusBadRequest)
|
|
|
|
|
|
return
|
2025-09-15 22:01:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
event := r.URL.Query().Get("event")
|
|
|
|
|
|
|
2025-09-24 19:13:15 +08:00
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
|
|
|
|
|
|
|
// 将异步处理逻辑委托给 handler 方法
|
|
|
|
|
|
go c.handler(b, event)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// handler 用于处理 ChirpStack 发送的事件
|
|
|
|
|
|
func (c *ChirpStackListener) handler(data []byte, eventType string) {
|
|
|
|
|
|
switch eventType {
|
2025-09-24 22:34:11 +08:00
|
|
|
|
case eventTypeUp:
|
2025-09-24 19:13:15 +08:00
|
|
|
|
var msg UpEvent
|
|
|
|
|
|
if err := json.Unmarshal(data, &msg); err != nil {
|
|
|
|
|
|
c.logger.Errorf("解析 'up' 事件失败: %v, data: %s", err, string(data))
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.handleUpEvent(&msg)
|
|
|
|
|
|
|
2025-09-24 22:34:11 +08:00
|
|
|
|
case eventTypeJoin:
|
2025-09-24 19:13:15 +08:00
|
|
|
|
var msg JoinEvent
|
|
|
|
|
|
if err := json.Unmarshal(data, &msg); err != nil {
|
|
|
|
|
|
c.logger.Errorf("解析 'join' 事件失败: %v, data: %s", err, string(data))
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.handleJoinEvent(&msg)
|
|
|
|
|
|
|
2025-09-24 22:34:11 +08:00
|
|
|
|
case eventTypeAck:
|
2025-09-24 19:13:15 +08:00
|
|
|
|
var msg AckEvent
|
|
|
|
|
|
if err := json.Unmarshal(data, &msg); err != nil {
|
|
|
|
|
|
c.logger.Errorf("解析 'ack' 事件失败: %v, data: %s", err, string(data))
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.handleAckEvent(&msg)
|
|
|
|
|
|
|
2025-09-24 22:34:11 +08:00
|
|
|
|
case eventTypeTxAck:
|
2025-09-24 19:13:15 +08:00
|
|
|
|
var msg TxAckEvent
|
|
|
|
|
|
if err := json.Unmarshal(data, &msg); err != nil {
|
|
|
|
|
|
c.logger.Errorf("解析 'txack' 事件失败: %v, data: %s", err, string(data))
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.handleTxAckEvent(&msg)
|
|
|
|
|
|
|
2025-09-24 22:34:11 +08:00
|
|
|
|
case eventTypeStatus:
|
2025-09-24 19:13:15 +08:00
|
|
|
|
var msg StatusEvent
|
|
|
|
|
|
if err := json.Unmarshal(data, &msg); err != nil {
|
|
|
|
|
|
c.logger.Errorf("解析 'status' 事件失败: %v, data: %s", err, string(data))
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.handleStatusEvent(&msg)
|
|
|
|
|
|
|
2025-09-24 22:34:11 +08:00
|
|
|
|
case eventTypeLog:
|
2025-09-24 19:13:15 +08:00
|
|
|
|
var msg LogEvent
|
|
|
|
|
|
if err := json.Unmarshal(data, &msg); err != nil {
|
|
|
|
|
|
c.logger.Errorf("解析 'log' 事件失败: %v, data: %s", err, string(data))
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.handleLogEvent(&msg)
|
2025-09-15 22:01:00 +08:00
|
|
|
|
|
2025-09-24 22:34:11 +08:00
|
|
|
|
case eventTypeLocation:
|
2025-09-24 19:13:15 +08:00
|
|
|
|
var msg LocationEvent
|
|
|
|
|
|
if err := json.Unmarshal(data, &msg); err != nil {
|
|
|
|
|
|
c.logger.Errorf("解析 'location' 事件失败: %v, data: %s", err, string(data))
|
|
|
|
|
|
return
|
2025-09-15 22:01:00 +08:00
|
|
|
|
}
|
2025-09-24 19:13:15 +08:00
|
|
|
|
c.handleLocationEvent(&msg)
|
|
|
|
|
|
|
2025-09-24 22:34:11 +08:00
|
|
|
|
case eventTypeIntegration:
|
2025-09-24 19:13:15 +08:00
|
|
|
|
var msg IntegrationEvent
|
|
|
|
|
|
if err := json.Unmarshal(data, &msg); err != nil {
|
|
|
|
|
|
c.logger.Errorf("解析 'integration' 事件失败: %v, data: %s", err, string(data))
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
c.handleIntegrationEvent(&msg)
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
c.logger.Errorf("未知的ChirpStack事件: %s, data: %s", eventType, string(data))
|
2025-09-15 22:01:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-24 19:13:15 +08:00
|
|
|
|
// --- 业务处理函数 ---
|
|
|
|
|
|
|
2025-09-24 22:34:11 +08:00
|
|
|
|
// GenericSensorReading 表示单个传感器读数,包含设备ID、类型和值。
|
|
|
|
|
|
type GenericSensorReading struct {
|
|
|
|
|
|
DeviceID uint `json:"device_id"` // 传感器设备的ID
|
|
|
|
|
|
Type models.SensorDataType `json:"type"` // 传感器类型 (复用 models.SensorDataType)
|
|
|
|
|
|
Value float64 `json:"value"` // 传感器读数
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-24 19:13:15 +08:00
|
|
|
|
// handleUpEvent 处理上行数据事件
|
|
|
|
|
|
func (c *ChirpStackListener) handleUpEvent(event *UpEvent) {
|
|
|
|
|
|
c.logger.Infof("处理 'up' 事件: %+v", event)
|
2025-09-24 21:53:18 +08:00
|
|
|
|
|
|
|
|
|
|
// 记录信号强度
|
|
|
|
|
|
// 根据业务逻辑,一个猪场只有一个网关,所以 RxInfo 中通常只有一个元素,或者 gateway_id 都是相同的。
|
|
|
|
|
|
// 因此,我们只取第一个 RxInfo 中的信号数据即可。
|
|
|
|
|
|
if len(event.RxInfo) > 0 {
|
|
|
|
|
|
rx := event.RxInfo[0] // 取第一个接收到的网关信息
|
|
|
|
|
|
|
|
|
|
|
|
// 构建 SignalMetrics 结构体
|
|
|
|
|
|
signalMetrics := models.SignalMetrics{
|
2025-09-24 22:34:11 +08:00
|
|
|
|
RssiDbm: rx.Rssi,
|
|
|
|
|
|
SnrDb: rx.Snr,
|
2025-09-24 21:53:18 +08:00
|
|
|
|
}
|
2025-09-24 22:34:11 +08:00
|
|
|
|
|
|
|
|
|
|
// 这里的 event.DeviceInfo.DevEui 对应的是区域主控的 DevEui
|
|
|
|
|
|
regionalController, err := c.deviceRepo.FindByDevEui(event.DeviceInfo.DevEui)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.logger.Errorf("处理 'up' 事件失败:无法通过 DevEui '%s' 找到区域主控设备: %v", event.DeviceInfo.DevEui, err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
// 记录区域主控的信号强度
|
|
|
|
|
|
c.recordSensorData(regionalController.ID, regionalController.ID, event.Time, models.SensorDataTypeSignalMetrics, signalMetrics)
|
2025-09-24 21:53:18 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
c.logger.Warnf("处理 'up' 事件时未找到 RxInfo,无法记录信号数据。DevEui: %s", event.DeviceInfo.DevEui)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-24 22:34:11 +08:00
|
|
|
|
// 解析并记录传感器数据 (温度、湿度、重量)
|
|
|
|
|
|
// 假设 event.Data (frmPayload) 是 Base64 编码的 JSON 数组字符串
|
|
|
|
|
|
if event.Data != "" {
|
|
|
|
|
|
decodedData, err := base64.StdEncoding.DecodeString(event.Data)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.logger.Errorf("Base64 解码 'up' 事件的 Data 失败: %v, Data: %s", err, event.Data)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var readings []GenericSensorReading
|
|
|
|
|
|
if err := json.Unmarshal(decodedData, &readings); err != nil {
|
|
|
|
|
|
c.logger.Errorf("解析 'up' 事件的解码后 Data (JSON 数组) 失败: %v, Decoded Data: %s", err, string(decodedData))
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查找区域主控设备,以便记录其ID
|
|
|
|
|
|
regionalController, err := c.deviceRepo.FindByDevEui(event.DeviceInfo.DevEui)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.logger.Errorf("处理 'up' 事件失败:无法通过 DevEui '%s' 找到区域主控设备: %v", event.DeviceInfo.DevEui, err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, reading := range readings {
|
|
|
|
|
|
// 根据类型构建具体的传感器数据结构体
|
|
|
|
|
|
var sensorData interface{}
|
|
|
|
|
|
var sensorDataType models.SensorDataType
|
|
|
|
|
|
|
|
|
|
|
|
switch reading.Type {
|
|
|
|
|
|
case models.SensorDataTypeTemperature: // 使用枚举常量
|
|
|
|
|
|
sensorData = models.TemperatureData{
|
|
|
|
|
|
TemperatureCelsius: reading.Value,
|
|
|
|
|
|
}
|
|
|
|
|
|
sensorDataType = models.SensorDataTypeTemperature
|
|
|
|
|
|
case models.SensorDataTypeHumidity: // 使用枚举常量
|
|
|
|
|
|
sensorData = models.HumidityData{
|
|
|
|
|
|
HumidityPercent: reading.Value,
|
|
|
|
|
|
}
|
|
|
|
|
|
sensorDataType = models.SensorDataTypeHumidity
|
|
|
|
|
|
case models.SensorDataTypeWeight: // 使用枚举常量
|
|
|
|
|
|
sensorData = models.WeightData{
|
|
|
|
|
|
WeightKilograms: reading.Value,
|
|
|
|
|
|
}
|
|
|
|
|
|
sensorDataType = models.SensorDataTypeWeight
|
|
|
|
|
|
default:
|
|
|
|
|
|
c.logger.Warnf("处理 'up' 事件时遇到未知传感器类型: %s, Value: %f. 区域主控DevEui: %s, 传感器设备ID: %d",
|
|
|
|
|
|
reading.Type, reading.Value, event.DeviceInfo.DevEui, reading.DeviceID)
|
|
|
|
|
|
continue // 跳过未知类型
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录普通设备的传感器数据
|
|
|
|
|
|
c.recordSensorData(regionalController.ID, reading.DeviceID, event.Time, sensorDataType, sensorData)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
c.logger.Warnf("处理 'up' 事件时 Data 字段为空,无法记录传感器数据。DevEui: %s", event.DeviceInfo.DevEui)
|
|
|
|
|
|
}
|
2025-09-24 19:13:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// handleStatusEvent 处理设备状态事件
|
|
|
|
|
|
func (c *ChirpStackListener) handleStatusEvent(event *StatusEvent) {
|
|
|
|
|
|
c.logger.Infof("处接收到理 'status' 事件: %+v", event)
|
2025-09-24 21:53:18 +08:00
|
|
|
|
|
|
|
|
|
|
// 记录信号强度
|
|
|
|
|
|
signalMetrics := models.SignalMetrics{
|
2025-09-24 22:34:11 +08:00
|
|
|
|
MarginDb: event.Margin,
|
2025-09-24 21:53:18 +08:00
|
|
|
|
}
|
2025-09-24 22:34:11 +08:00
|
|
|
|
// 这里的 event.DeviceInfo.DevEui 对应的是区域主控的 DevEui
|
|
|
|
|
|
regionalController, err := c.deviceRepo.FindByDevEui(event.DeviceInfo.DevEui)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.logger.Errorf("处理 'status' 事件失败:无法通过 DevEui '%s' 找到区域主控设备: %v", event.DeviceInfo.DevEui, err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
// 记录区域主控的信号强度
|
|
|
|
|
|
c.recordSensorData(regionalController.ID, regionalController.ID, event.Time, models.SensorDataTypeSignalMetrics, signalMetrics)
|
2025-09-24 21:53:18 +08:00
|
|
|
|
|
|
|
|
|
|
// 记录 电量
|
|
|
|
|
|
batteryLevel := models.BatteryLevel{
|
2025-09-24 22:34:11 +08:00
|
|
|
|
BatteryLevelRatio: event.BatteryLevel,
|
2025-09-24 21:53:18 +08:00
|
|
|
|
BatteryLevelUnavailable: event.BatteryLevelUnavailable,
|
|
|
|
|
|
ExternalPower: event.ExternalPower,
|
|
|
|
|
|
}
|
2025-09-24 22:34:11 +08:00
|
|
|
|
// 记录区域主控的电池电量
|
|
|
|
|
|
c.recordSensorData(regionalController.ID, regionalController.ID, event.Time, models.SensorDataTypeBatteryLevel, batteryLevel)
|
2025-09-24 21:53:18 +08:00
|
|
|
|
|
2025-09-24 19:13:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// handleAckEvent 处理下行确认事件
|
|
|
|
|
|
func (c *ChirpStackListener) handleAckEvent(event *AckEvent) {
|
|
|
|
|
|
c.logger.Infof("接收到 'ack' 事件: %+v", event)
|
|
|
|
|
|
// 在这里添加您的业务逻辑
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// handleLogEvent 处理日志事件
|
|
|
|
|
|
func (c *ChirpStackListener) handleLogEvent(event *LogEvent) {
|
2025-09-24 20:26:26 +08:00
|
|
|
|
// 首先,打印完整的事件结构体,用于详细排查
|
|
|
|
|
|
c.logger.Infof("接收到 'log' 事件的完整内容: %+v", event)
|
|
|
|
|
|
|
|
|
|
|
|
// 接着,根据 ChirpStack 日志的级别,使用我们自己的 logger 对应级别来打印核心信息
|
|
|
|
|
|
logMessage := "ChirpStack 日志: [%s] %s (DevEui: %s)"
|
|
|
|
|
|
switch event.Level {
|
|
|
|
|
|
case "INFO":
|
|
|
|
|
|
c.logger.Infof(logMessage, event.Code, event.Description, event.DeviceInfo.DevEui)
|
|
|
|
|
|
case "WARNING":
|
|
|
|
|
|
c.logger.Warnf(logMessage, event.Code, event.Description, event.DeviceInfo.DevEui)
|
|
|
|
|
|
case "ERROR":
|
|
|
|
|
|
c.logger.Errorf(logMessage, event.Code, event.Description, event.DeviceInfo.DevEui)
|
|
|
|
|
|
default:
|
|
|
|
|
|
// 对于未知级别,使用 Warn 级别打印,并明确指出级别未知
|
|
|
|
|
|
c.logger.Warnf("ChirpStack 日志: [未知级别: %s] %s %s (DevEui: %s)",
|
|
|
|
|
|
event.Level, event.Code, event.Description, event.DeviceInfo.DevEui)
|
|
|
|
|
|
}
|
2025-09-24 19:13:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// handleJoinEvent 处理入网事件
|
|
|
|
|
|
func (c *ChirpStackListener) handleJoinEvent(event *JoinEvent) {
|
|
|
|
|
|
c.logger.Infof("接收到 'join' 事件: %+v", event)
|
|
|
|
|
|
// 在这里添加您的业务逻辑
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// handleTxAckEvent 处理网关发送确认事件
|
|
|
|
|
|
func (c *ChirpStackListener) handleTxAckEvent(event *TxAckEvent) {
|
|
|
|
|
|
c.logger.Infof("接收到 'txack' 事件: %+v", event)
|
|
|
|
|
|
// 在这里添加您的业务逻辑
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// handleLocationEvent 处理位置事件
|
|
|
|
|
|
func (c *ChirpStackListener) handleLocationEvent(event *LocationEvent) {
|
|
|
|
|
|
c.logger.Infof("接收到 'location' 事件: %+v", event)
|
|
|
|
|
|
// 在这里添加您的业务逻辑
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// handleIntegrationEvent 处理集成事件
|
|
|
|
|
|
func (c *ChirpStackListener) handleIntegrationEvent(event *IntegrationEvent) {
|
|
|
|
|
|
c.logger.Infof("接收到 'integration' 事件: %+v", event)
|
|
|
|
|
|
// 在这里添加您的业务逻辑
|
2025-09-15 22:01:00 +08:00
|
|
|
|
}
|
2025-09-24 21:53:18 +08:00
|
|
|
|
|
|
|
|
|
|
// recordSensorData 是一个通用方法,用于将传感器数据存入数据库。
|
2025-09-24 22:34:11 +08:00
|
|
|
|
// regionalControllerID: 区域主控设备的ID
|
|
|
|
|
|
// sensorDeviceID: 实际产生传感器数据的普通设备的ID
|
|
|
|
|
|
func (c *ChirpStackListener) recordSensorData(regionalControllerID uint, sensorDeviceID uint, eventTime time.Time, dataType models.SensorDataType, data interface{}) {
|
2025-09-24 21:53:18 +08:00
|
|
|
|
// 2. 序列化数据结构体为 JSON
|
|
|
|
|
|
jsonData, err := json.Marshal(data)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
c.logger.Errorf("记录传感器数据失败:序列化数据为 JSON 时出错: %v", err)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 构建 SensorData 模型
|
|
|
|
|
|
sensorData := &models.SensorData{
|
|
|
|
|
|
Time: eventTime,
|
2025-09-24 22:34:11 +08:00
|
|
|
|
DeviceID: sensorDeviceID,
|
|
|
|
|
|
RegionalControllerID: regionalControllerID,
|
|
|
|
|
|
SensorDataType: dataType,
|
2025-09-24 21:53:18 +08:00
|
|
|
|
Data: datatypes.JSON(jsonData),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 调用仓库创建记录
|
|
|
|
|
|
if err := c.sensorDataRepo.Create(sensorData); err != nil {
|
|
|
|
|
|
c.logger.Errorf("记录传感器数据失败:存入数据库时出错: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|