将所有Regional更改为Area

This commit is contained in:
2025-11-10 21:32:18 +08:00
parent 4f3148eaa2
commit 75306941c2
11 changed files with 61 additions and 60 deletions

View File

@@ -143,4 +143,5 @@
11. 实现区域阈值告警和设备阈值告警的增删改查 11. 实现区域阈值告警和设备阈值告警的增删改查
12. 实现任务11应的八个web接口 12. 实现任务11应的八个web接口
13. 实现根据区域ID或设备ID清空对应阈值告警任务 13. 实现根据区域ID或设备ID清空对应阈值告警任务
14. 设备和区域主控删除时清除对应区域阈值告警或设备阈值告警任务 14. 设备和区域主控删除时清除对应区域阈值告警或设备阈值告警任务
15. 将所有Regional更改为Area

View File

@@ -6816,6 +6816,9 @@ const docTemplate = `{
"dto.SensorDataDTO": { "dto.SensorDataDTO": {
"type": "object", "type": "object",
"properties": { "properties": {
"area_controller_id": {
"type": "integer"
},
"data": { "data": {
"type": "array", "type": "array",
"items": { "items": {
@@ -6825,9 +6828,6 @@ const docTemplate = `{
"device_id": { "device_id": {
"type": "integer" "type": "integer"
}, },
"regional_controller_id": {
"type": "integer"
},
"sensor_type": { "sensor_type": {
"$ref": "#/definitions/models.SensorType" "$ref": "#/definitions/models.SensorType"
}, },

View File

@@ -6808,6 +6808,9 @@
"dto.SensorDataDTO": { "dto.SensorDataDTO": {
"type": "object", "type": "object",
"properties": { "properties": {
"area_controller_id": {
"type": "integer"
},
"data": { "data": {
"type": "array", "type": "array",
"items": { "items": {
@@ -6817,9 +6820,6 @@
"device_id": { "device_id": {
"type": "integer" "type": "integer"
}, },
"regional_controller_id": {
"type": "integer"
},
"sensor_type": { "sensor_type": {
"$ref": "#/definitions/models.SensorType" "$ref": "#/definitions/models.SensorType"
}, },

View File

@@ -1369,14 +1369,14 @@ definitions:
type: object type: object
dto.SensorDataDTO: dto.SensorDataDTO:
properties: properties:
area_controller_id:
type: integer
data: data:
items: items:
type: integer type: integer
type: array type: array
device_id: device_id:
type: integer type: integer
regional_controller_id:
type: integer
sensor_type: sensor_type:
$ref: '#/definitions/models.SensorType' $ref: '#/definitions/models.SensorType'
time: time:

View File

@@ -11,11 +11,11 @@ func NewListSensorDataResponse(data []models.SensorData, total int64, page, page
dtos := make([]SensorDataDTO, len(data)) dtos := make([]SensorDataDTO, len(data))
for i, item := range data { for i, item := range data {
dtos[i] = SensorDataDTO{ dtos[i] = SensorDataDTO{
Time: item.Time, Time: item.Time,
DeviceID: item.DeviceID, DeviceID: item.DeviceID,
RegionalControllerID: item.RegionalControllerID, AreaControllerID: item.AreaControllerID,
SensorType: item.SensorType, SensorType: item.SensorType,
Data: json.RawMessage(item.Data), Data: json.RawMessage(item.Data),
} }
} }

View File

@@ -22,11 +22,11 @@ type ListSensorDataRequest struct {
// SensorDataDTO 是用于API响应的传感器数据结构 // SensorDataDTO 是用于API响应的传感器数据结构
type SensorDataDTO struct { type SensorDataDTO struct {
Time time.Time `json:"time"` Time time.Time `json:"time"`
DeviceID uint `json:"device_id"` DeviceID uint `json:"device_id"`
RegionalControllerID uint `json:"regional_controller_id"` AreaControllerID uint `json:"area_controller_id"`
SensorType models.SensorType `json:"sensor_type"` SensorType models.SensorType `json:"sensor_type"`
Data json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }
// ListSensorDataResponse 是获取传感器数据列表的响应结构 // ListSensorDataResponse 是获取传感器数据列表的响应结构

View File

@@ -162,17 +162,17 @@ func (c *ChirpStackListener) handleUpEvent(ctx context.Context, event *UpEvent)
logger.Infof("开始处理 'up' 事件, DevEui: %s", event.DeviceInfo.DevEui) logger.Infof("开始处理 'up' 事件, DevEui: %s", event.DeviceInfo.DevEui)
// 1. 查找区域主控设备 // 1. 查找区域主控设备
regionalController, err := c.areaControllerRepo.FindByNetworkID(reqCtx, event.DeviceInfo.DevEui) areaController, err := c.areaControllerRepo.FindByNetworkID(reqCtx, event.DeviceInfo.DevEui)
if err != nil { if err != nil {
logger.Errorf("处理 'up' 事件失败:无法通过 DevEui '%s' 找到区域主控设备: %v", event.DeviceInfo.DevEui, err) logger.Errorf("处理 'up' 事件失败:无法通过 DevEui '%s' 找到区域主控设备: %v", event.DeviceInfo.DevEui, err)
return return
} }
// 依赖 SelfCheck 确保区域主控有效 // 依赖 SelfCheck 确保区域主控有效
if err := regionalController.SelfCheck(); err != nil { if err := areaController.SelfCheck(); err != nil {
logger.Errorf("处理 'up' 事件失败:区域主控 %v(ID: %d) 未通过自检: %v", regionalController.Name, regionalController.ID, err) logger.Errorf("处理 'up' 事件失败:区域主控 %v(ID: %d) 未通过自检: %v", areaController.Name, areaController.ID, err)
return return
} }
logger.Infof("找到区域主控: %s (ID: %d)", regionalController.Name, regionalController.ID) logger.Infof("找到区域主控: %s (ID: %d)", areaController.Name, areaController.ID)
// 2. 记录区域主控的信号强度 (如果存在) // 2. 记录区域主控的信号强度 (如果存在)
if len(event.RxInfo) > 0 { if len(event.RxInfo) > 0 {
@@ -187,8 +187,8 @@ func (c *ChirpStackListener) handleUpEvent(ctx context.Context, event *UpEvent)
} }
// 记录信号强度 // 记录信号强度
c.recordSensorData(reqCtx, regionalController.ID, regionalController.ID, event.Time, models.SensorTypeSignalMetrics, signalMetrics) c.recordSensorData(reqCtx, areaController.ID, areaController.ID, event.Time, models.SensorTypeSignalMetrics, signalMetrics)
logger.Infof("已记录区域主控 (ID: %d) 的信号强度: RSSI=%d, SNR=%.2f", regionalController.ID, rx.Rssi, rx.Snr) logger.Infof("已记录区域主控 (ID: %d) 的信号强度: RSSI=%d, SNR=%.2f", areaController.ID, rx.Rssi, rx.Snr)
} else { } else {
logger.Warnf("处理 'up' 事件时未找到 RxInfo无法记录信号数据。DevEui: %s", event.DeviceInfo.DevEui) logger.Warnf("处理 'up' 事件时未找到 RxInfo无法记录信号数据。DevEui: %s", event.DeviceInfo.DevEui)
} }
@@ -316,7 +316,7 @@ func (c *ChirpStackListener) handleUpEvent(ctx context.Context, event *UpEvent)
} }
// 5.5 记录传感器数据 // 5.5 记录传感器数据
c.recordSensorData(reqCtx, regionalController.ID, dev.ID, event.Time, valueDescriptor.Type, dataToRecord) c.recordSensorData(reqCtx, areaController.ID, dev.ID, event.Time, valueDescriptor.Type, dataToRecord)
logger.Infof("成功记录传感器数据: 设备ID=%d, 类型=%s, 原始值=%f, 解析值=%.2f", dev.ID, valueDescriptor.Type, rawSensorValue, parsedValue) logger.Infof("成功记录传感器数据: 设备ID=%d, 类型=%s, 原始值=%f, 解析值=%.2f", dev.ID, valueDescriptor.Type, rawSensorValue, parsedValue)
} }
@@ -334,7 +334,7 @@ func (c *ChirpStackListener) handleStatusEvent(ctx context.Context, event *Statu
logger.Infof("处接收到理 'status' 事件: %+v", event) logger.Infof("处接收到理 'status' 事件: %+v", event)
// 查找区域主控设备 // 查找区域主控设备
regionalController, err := c.areaControllerRepo.FindByNetworkID(reqCtx, event.DeviceInfo.DevEui) areaController, err := c.areaControllerRepo.FindByNetworkID(reqCtx, event.DeviceInfo.DevEui)
if err != nil { if err != nil {
logger.Errorf("处理 'status' 事件失败:无法通过 DevEui '%s' 找到区域主控设备: %v", event.DeviceInfo.DevEui, err) logger.Errorf("处理 'status' 事件失败:无法通过 DevEui '%s' 找到区域主控设备: %v", event.DeviceInfo.DevEui, err)
return return
@@ -344,8 +344,8 @@ func (c *ChirpStackListener) handleStatusEvent(ctx context.Context, event *Statu
signalMetrics := models.SignalMetrics{ signalMetrics := models.SignalMetrics{
MarginDb: event.Margin, MarginDb: event.Margin,
} }
c.recordSensorData(reqCtx, regionalController.ID, regionalController.ID, event.Time, models.SensorTypeSignalMetrics, signalMetrics) c.recordSensorData(reqCtx, areaController.ID, areaController.ID, event.Time, models.SensorTypeSignalMetrics, signalMetrics)
logger.Infof("已记录区域主控 (ID: %d) 的信号状态: %+v", regionalController.ID, signalMetrics) logger.Infof("已记录区域主控 (ID: %d) 的信号状态: %+v", areaController.ID, signalMetrics)
// 记录电量 // 记录电量
batteryLevel := models.BatteryLevel{ batteryLevel := models.BatteryLevel{
@@ -353,8 +353,8 @@ func (c *ChirpStackListener) handleStatusEvent(ctx context.Context, event *Statu
BatteryLevelUnavailable: event.BatteryLevelUnavailable, BatteryLevelUnavailable: event.BatteryLevelUnavailable,
ExternalPower: event.ExternalPower, ExternalPower: event.ExternalPower,
} }
c.recordSensorData(reqCtx, regionalController.ID, regionalController.ID, event.Time, models.SensorTypeBatteryLevel, batteryLevel) c.recordSensorData(reqCtx, areaController.ID, areaController.ID, event.Time, models.SensorTypeBatteryLevel, batteryLevel)
logger.Infof("已记录区域主控 (ID: %d) 的电池状态: %+v", regionalController.ID, batteryLevel) logger.Infof("已记录区域主控 (ID: %d) 的电池状态: %+v", areaController.ID, batteryLevel)
} }
// handleAckEvent 处理下行确认事件 // handleAckEvent 处理下行确认事件
@@ -425,11 +425,11 @@ func (c *ChirpStackListener) handleIntegrationEvent(ctx context.Context, event *
} }
// recordSensorData 是一个通用方法,用于将传感器数据存入数据库。 // recordSensorData 是一个通用方法,用于将传感器数据存入数据库。
// regionalControllerID: 区域主控设备的ID // areaControllerID: 区域主控设备的ID
// sensorDeviceID: 实际产生传感器数据的普通设备的ID // sensorDeviceID: 实际产生传感器数据的普通设备的ID
// sensorType: 传感器值的类型 (例如 models.SensorTypeTemperature) // sensorType: 传感器值的类型 (例如 models.SensorTypeTemperature)
// data: 具体的传感器数据结构体实例 (例如 models.TemperatureData) // data: 具体的传感器数据结构体实例 (例如 models.TemperatureData)
func (c *ChirpStackListener) recordSensorData(ctx context.Context, regionalControllerID uint, sensorDeviceID uint, eventTime time.Time, sensorType models.SensorType, data interface{}) { func (c *ChirpStackListener) recordSensorData(ctx context.Context, areaControllerID uint, sensorDeviceID uint, eventTime time.Time, sensorType models.SensorType, data interface{}) {
reqCtx, logger := logs.Trace(ctx, c.ctx, "recordSensorData") reqCtx, logger := logs.Trace(ctx, c.ctx, "recordSensorData")
// 1. 将传入的结构体序列化为 JSON // 1. 将传入的结构体序列化为 JSON
jsonData, err := json.Marshal(data) jsonData, err := json.Marshal(data)
@@ -440,11 +440,11 @@ func (c *ChirpStackListener) recordSensorData(ctx context.Context, regionalContr
// 2. 构建 SensorData 模型 // 2. 构建 SensorData 模型
sensorData := &models.SensorData{ sensorData := &models.SensorData{
Time: eventTime, Time: eventTime,
DeviceID: sensorDeviceID, DeviceID: sensorDeviceID,
RegionalControllerID: regionalControllerID, AreaControllerID: areaControllerID,
SensorType: sensorType, SensorType: sensorType,
Data: datatypes.JSON(jsonData), Data: datatypes.JSON(jsonData),
} }
// 3. 调用仓库创建记录 // 3. 调用仓库创建记录

View File

@@ -28,7 +28,7 @@ type Service interface {
Switch(ctx context.Context, device *models.Device, action DeviceAction) error Switch(ctx context.Context, device *models.Device, action DeviceAction) error
// Collect 用于发起对指定区域主控下的多个设备的批量采集请求。 // Collect 用于发起对指定区域主控下的多个设备的批量采集请求。
Collect(ctx context.Context, regionalControllerID uint, devicesToCollect []*models.Device) error Collect(ctx context.Context, areaControllerID uint, devicesToCollect []*models.Device) error
} }
// 设备操作指令通用结构(最外层) // 设备操作指令通用结构(最外层)

View File

@@ -133,7 +133,7 @@ func (g *GeneralDeviceService) Switch(ctx context.Context, device *models.Device
} }
// Collect 实现了 Service 接口,用于发起对指定区域主控下的多个设备的批量采集请求。 // Collect 实现了 Service 接口,用于发起对指定区域主控下的多个设备的批量采集请求。
func (g *GeneralDeviceService) Collect(ctx context.Context, regionalControllerID uint, devicesToCollect []*models.Device) error { func (g *GeneralDeviceService) Collect(ctx context.Context, areaControllerID uint, devicesToCollect []*models.Device) error {
serviceCtx, logger := logs.Trace(ctx, g.ctx, "Collect") serviceCtx, logger := logs.Trace(ctx, g.ctx, "Collect")
if len(devicesToCollect) == 0 { if len(devicesToCollect) == 0 {
logger.Info("待采集设备列表为空,无需执行采集任务。") logger.Info("待采集设备列表为空,无需执行采集任务。")
@@ -141,12 +141,12 @@ func (g *GeneralDeviceService) Collect(ctx context.Context, regionalControllerID
} }
// 1. 从设备列表中获取预加载的区域主控,并进行校验 // 1. 从设备列表中获取预加载的区域主控,并进行校验
regionalController := &devicesToCollect[0].AreaController areaController := &devicesToCollect[0].AreaController
if regionalController.ID != regionalControllerID { if areaController.ID != areaControllerID {
return fmt.Errorf("设备列表与指定的区域主控ID (%d) 不匹配", regionalControllerID) return fmt.Errorf("设备列表与指定的区域主控ID (%d) 不匹配", areaControllerID)
} }
if err := regionalController.SelfCheck(); err != nil { if err := areaController.SelfCheck(); err != nil {
return fmt.Errorf("区域主控 (ID: %d) 未通过自检: %w", regionalControllerID, err) return fmt.Errorf("区域主控 (ID: %d) 未通过自检: %w", areaControllerID, err)
} }
// 2. 准备采集任务列表 // 2. 准备采集任务列表
@@ -208,13 +208,13 @@ func (g *GeneralDeviceService) Collect(ctx context.Context, regionalControllerID
} }
// 3. 构建并发送指令 // 3. 构建并发送指令
networkID := regionalController.NetworkID networkID := areaController.NetworkID
// 4. 创建待处理请求记录 // 4. 创建待处理请求记录
correlationID := uuid.New().String() correlationID := uuid.New().String()
pendingReq := &models.PendingCollection{ pendingReq := &models.PendingCollection{
CorrelationID: correlationID, CorrelationID: correlationID,
DeviceID: regionalController.ID, DeviceID: areaController.ID,
CommandMetadata: childDeviceIDs, CommandMetadata: childDeviceIDs,
Status: models.PendingStatusPending, Status: models.PendingStatusPending,
CreatedAt: time.Now(), CreatedAt: time.Now(),
@@ -223,7 +223,7 @@ func (g *GeneralDeviceService) Collect(ctx context.Context, regionalControllerID
logger.Errorf("创建待采集请求失败 (CorrelationID: %s): %v", correlationID, err) logger.Errorf("创建待采集请求失败 (CorrelationID: %s): %v", correlationID, err)
return err return err
} }
logger.Infof("成功创建待采集请求 (CorrelationID: %s, DeviceID: %d)", correlationID, regionalController.ID) logger.Infof("成功创建待采集请求 (CorrelationID: %s, DeviceID: %d)", correlationID, areaController.ID)
// 5. 构建最终的空中载荷 // 5. 构建最终的空中载荷
batchCmd := &proto.BatchCollectCommand{ batchCmd := &proto.BatchCollectCommand{

View File

@@ -57,8 +57,8 @@ type SensorData struct {
// DeviceID 是传感器的唯一标识符,作为复合主键的另一部分。 // DeviceID 是传感器的唯一标识符,作为复合主键的另一部分。
DeviceID uint `gorm:"primaryKey" json:"device_id"` DeviceID uint `gorm:"primaryKey" json:"device_id"`
// RegionalControllerID 是上报此数据的区域主控的ID。 // AreaControllerID 是上报此数据的区域主控的ID。
RegionalControllerID uint `json:"regional_controller_id"` AreaControllerID uint `json:"area_controller_id"`
// SensorType 是传感数据的类型 // SensorType 是传感数据的类型
SensorType SensorType `gorm:"not null;index" json:"sensor_type"` SensorType SensorType `gorm:"not null;index" json:"sensor_type"`

View File

@@ -406,13 +406,13 @@ func (t *LoRaMeshUartPassthroughTransport) handleUpstreamMessage(ctx context.Con
logger.Infof("成功解析采集响应 (CorrelationID: %s),包含 %d 个值。", correlationID, len(collectResp.Values)) logger.Infof("成功解析采集响应 (CorrelationID: %s),包含 %d 个值。", correlationID, len(collectResp.Values))
// 3. 查找区域主控 (注意LoRa Mesh 的 SourceAddr 对应于区域主控的 NetworkID) // 3. 查找区域主控 (注意LoRa Mesh 的 SourceAddr 对应于区域主控的 NetworkID)
regionalController, err := t.areaControllerRepo.FindByNetworkID(loraCtx, msg.SourceAddr) areaController, err := t.areaControllerRepo.FindByNetworkID(loraCtx, msg.SourceAddr)
if err != nil { if err != nil {
logger.Errorf("处理上行消息失败:无法通过源地址 '%s' 找到区域主控设备: %v", msg.SourceAddr, err) logger.Errorf("处理上行消息失败:无法通过源地址 '%s' 找到区域主控设备: %v", msg.SourceAddr, err)
return return
} }
if err := regionalController.SelfCheck(); err != nil { if err := areaController.SelfCheck(); err != nil {
logger.Errorf("处理上行消息失败:区域主控 %v(ID: %d) 未通过自检: %v", regionalController.Name, regionalController.ID, err) logger.Errorf("处理上行消息失败:区域主控 %v(ID: %d) 未通过自检: %v", areaController.Name, areaController.ID, err)
return return
} }
@@ -489,7 +489,7 @@ func (t *LoRaMeshUartPassthroughTransport) handleUpstreamMessage(ctx context.Con
dataToRecord = map[string]float64{"value": parsedValue} dataToRecord = map[string]float64{"value": parsedValue}
} }
t.recordSensorData(loraCtx, regionalController.ID, dev.ID, time.Now(), valueDescriptor.Type, dataToRecord) t.recordSensorData(loraCtx, areaController.ID, dev.ID, time.Now(), valueDescriptor.Type, dataToRecord)
logger.Infof("成功记录传感器数据: 设备ID=%d, 类型=%s, 原始值=%f, 解析值=%.2f", dev.ID, valueDescriptor.Type, rawSensorValue, parsedValue) logger.Infof("成功记录传感器数据: 设备ID=%d, 类型=%s, 原始值=%f, 解析值=%.2f", dev.ID, valueDescriptor.Type, rawSensorValue, parsedValue)
} }
@@ -502,7 +502,7 @@ func (t *LoRaMeshUartPassthroughTransport) handleUpstreamMessage(ctx context.Con
} }
// recordSensorData 是一个通用方法,用于将传感器数据存入数据库。 // recordSensorData 是一个通用方法,用于将传感器数据存入数据库。
func (t *LoRaMeshUartPassthroughTransport) recordSensorData(ctx context.Context, regionalControllerID uint, sensorDeviceID uint, eventTime time.Time, sensorType models.SensorType, data interface{}) { func (t *LoRaMeshUartPassthroughTransport) recordSensorData(ctx context.Context, areaControllerID uint, sensorDeviceID uint, eventTime time.Time, sensorType models.SensorType, data interface{}) {
loraCtx, logger := logs.Trace(ctx, t.ctx, "recordSensorData") loraCtx, logger := logs.Trace(ctx, t.ctx, "recordSensorData")
jsonData, err := json.Marshal(data) jsonData, err := json.Marshal(data)
@@ -512,11 +512,11 @@ func (t *LoRaMeshUartPassthroughTransport) recordSensorData(ctx context.Context,
} }
sensorData := &models.SensorData{ sensorData := &models.SensorData{
Time: eventTime, Time: eventTime,
DeviceID: sensorDeviceID, DeviceID: sensorDeviceID,
RegionalControllerID: regionalControllerID, AreaControllerID: areaControllerID,
SensorType: sensorType, SensorType: sensorType,
Data: datatypes.JSON(jsonData), Data: datatypes.JSON(jsonData),
} }
if err := t.sensorDataRepo.Create(loraCtx, sensorData); err != nil { if err := t.sensorDataRepo.Create(loraCtx, sensorData); err != nil {