From d5056af676ee9582e7be5cc81ea8b4139ee254d8 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Mon, 1 Dec 2025 17:49:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81ota=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E7=9B=B8=E5=BA=94=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/app/dto/device_converter.go | 35 ++++++++++++++++--------- internal/app/dto/device_dto.go | 17 ++++++------ internal/app/listener/lora_listener.go | 36 +++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/internal/app/dto/device_converter.go b/internal/app/dto/device_converter.go index 20a5ce3..5cdf028 100644 --- a/internal/app/dto/device_converter.go +++ b/internal/app/dto/device_converter.go @@ -1,7 +1,6 @@ package dto import ( - "encoding/json" "fmt" "time" @@ -65,22 +64,34 @@ func NewAreaControllerResponse(ac *models.AreaController) (*AreaControllerRespon return nil, nil } - var props map[string]interface{} + // 解析 firmware_version + var firmwareVersion string + // 使用模型上的辅助方法来解析强类型属性 + acProps := &models.AreaControllerProperties{} + if err := ac.ParseProperties(acProps); err == nil { + firmwareVersion = acProps.FirmwareVersion + } + // 如果解析出错,firmwareVersion 将保持为空字符串,这通常是可接受的降级行为 + + // 解析完整的 properties 以便向后兼容或用于其他未知属性 + var allProps map[string]interface{} if len(ac.Properties) > 0 && string(ac.Properties) != "null" { - if err := json.Unmarshal(ac.Properties, &props); err != nil { - return nil, fmt.Errorf("解析区域主控属性失败 (ID: %d): %w", ac.ID, err) + // 这里我们使用通用的 ParseProperties 方法 + if err := ac.ParseProperties(&allProps); err != nil { + return nil, fmt.Errorf("解析区域主控完整属性失败 (ID: %d): %w", ac.ID, err) } } return &AreaControllerResponse{ - ID: ac.ID, - Name: ac.Name, - NetworkID: ac.NetworkID, - Location: ac.Location, - Status: ac.Status, - Properties: props, - CreatedAt: ac.CreatedAt.Format(time.RFC3339), - UpdatedAt: ac.UpdatedAt.Format(time.RFC3339), + ID: ac.ID, + Name: ac.Name, + NetworkID: ac.NetworkID, + FirmwareVersion: firmwareVersion, + Location: ac.Location, + Status: ac.Status, + Properties: allProps, // 填充完整的 properties + CreatedAt: ac.CreatedAt.Format(time.RFC3339), + UpdatedAt: ac.UpdatedAt.Format(time.RFC3339), }, nil } diff --git a/internal/app/dto/device_dto.go b/internal/app/dto/device_dto.go index 5ac018b..d5bd843 100644 --- a/internal/app/dto/device_dto.go +++ b/internal/app/dto/device_dto.go @@ -78,14 +78,15 @@ type DeviceResponse struct { // AreaControllerResponse 定义了返回给客户端的单个区域主控信息的结构 type AreaControllerResponse struct { - ID uint32 `json:"id"` - Name string `json:"name"` - NetworkID string `json:"network_id"` - Location string `json:"location"` - Status string `json:"status"` - Properties map[string]interface{} `json:"properties"` - CreatedAt string `json:"created_at"` - UpdatedAt string `json:"updated_at"` + ID uint32 `json:"id"` + Name string `json:"name"` + NetworkID string `json:"network_id"` + FirmwareVersion string `json:"firmware_version"` + Location string `json:"location"` + Status string `json:"status"` + Properties map[string]interface{} `json:"properties"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` } // DeviceTemplateResponse 定义了返回给客户端的单个设备模板信息的结构 diff --git a/internal/app/listener/lora_listener.go b/internal/app/listener/lora_listener.go index 9486fdd..07d51ef 100644 --- a/internal/app/listener/lora_listener.go +++ b/internal/app/listener/lora_listener.go @@ -57,9 +57,7 @@ func (l *loraListener) HandleInstruction(upstreamCtx context.Context, sourceAddr return l.handleCollectResult(ctx, sourceAddr, p.CollectResult) case *proto.Instruction_OtaUpgradeStatus: - logger.Infow("收到OTA升级状态,暂未实现处理逻辑", "来源地址", sourceAddr, "状态", p.OtaUpgradeStatus) - // TODO: 在这里实现OTA升级状态的处理逻辑 - return nil + return l.handleOtaStatus(ctx, sourceAddr, p.OtaUpgradeStatus) case *proto.Instruction_LogUploadRequest: logger.Infow("收到设备日志上传请求,暂未实现处理逻辑", "来源地址", sourceAddr, "日志条数", len(p.LogUploadRequest.Entries)) @@ -275,3 +273,35 @@ func (l *loraListener) recordSensorData(ctx context.Context, areaControllerID ui ) } } + +// handleOtaStatus 处理设备上报的OTA升级状态。 +func (l *loraListener) handleOtaStatus(ctx context.Context, sourceAddr string, status *proto.OtaUpgradeStatus) error { + reqCtx, logger := logs.Trace(ctx, l.selfCtx, "handleOtaStatus") + logger.Infow("开始处理OTA升级状态", + "来源地址", sourceAddr, + "状态码", status.StatusCode, + "处理结果", status.StatusCode == 0, + "当前版本", status.CurrentFirmwareVersion, + ) + + // 1. 查找区域主控 + areaController, err := l.areaControllerRepo.FindByNetworkID(reqCtx, sourceAddr) + if err != nil { + return fmt.Errorf("处理OTA状态失败:无法找到区域主控: %w", err) + } + + // 2. 更新固件版本号 + // 我们信任设备上报的版本号,无论成功失败都进行更新 + if status.CurrentFirmwareVersion != "" { + err = l.areaControllerRepo.UpdateFirmwareVersion(reqCtx, areaController.ID, status.CurrentFirmwareVersion) + if err != nil { + logger.Errorw("更新区域主控固件版本号失败", "主控ID", areaController.ID, "error", err) + return fmt.Errorf("更新固件版本号失败: %w", err) + } + logger.Infow("成功更新区域主控固件版本号", "主控ID", areaController.ID, "新版本", status.CurrentFirmwareVersion) + } + + // TODO: 后续可以在这里增加逻辑,比如记录一条操作日志,或者发送一个通知 + + return nil +}