diff --git a/internal/infra/database/postgres.go b/internal/infra/database/postgres.go index bfab23d..87aa403 100644 --- a/internal/infra/database/postgres.go +++ b/internal/infra/database/postgres.go @@ -240,13 +240,13 @@ func (ps *PostgresStorage) creatingIndex() error { ps.logger.Info("成功为 tasks 的 parameters 字段创建 GIN 索引 (或已存在)") // 为 devices 表的 properties 字段创建 GIN 索引 - ps.logger.Info("正在为 devices 表的 properties 字段创建 GIN 索引") - ginDevicePropertiesIndexSQL := "CREATE INDEX IF NOT EXISTS idx_devices_properties_gin ON devices USING GIN (properties);" - if err := ps.db.Exec(ginDevicePropertiesIndexSQL).Error; err != nil { - ps.logger.Errorw("为 devices 的 properties 字段创建 GIN 索引失败", "error", err) - return fmt.Errorf("为 devices 的 properties 字段创建 GIN 索引失败: %w", err) - } - ps.logger.Info("成功为 devices 的 properties 字段创建 GIN 索引 (或已存在)") + //ps.logger.Info("正在为 devices 表的 properties 字段创建 GIN 索引") + //ginDevicePropertiesIndexSQL := "CREATE INDEX IF NOT EXISTS idx_devices_properties_gin ON devices USING GIN (properties);" + //if err := ps.db.Exec(ginDevicePropertiesIndexSQL).Error; err != nil { + // ps.logger.Errorw("为 devices 的 properties 字段创建 GIN 索引失败", "error", err) + // return fmt.Errorf("为 devices 的 properties 字段创建 GIN 索引失败: %w", err) + //} + //ps.logger.Info("成功为 devices 的 properties 字段创建 GIN 索引 (或已存在)") return nil } diff --git a/internal/infra/models/device.go b/internal/infra/models/device.go index 86279cb..cc9da17 100644 --- a/internal/infra/models/device.go +++ b/internal/infra/models/device.go @@ -3,6 +3,7 @@ package models import ( "encoding/json" "errors" + "strings" "gorm.io/datatypes" "gorm.io/gorm" @@ -29,9 +30,8 @@ type LoraProperties struct { // BusProperties 定义了总线设备的特有属性 type BusProperties struct { - BusID int `json:"bus_id"` // 485 总线号 - BusAddress int `json:"bus_address"` // 485 总线地址 - RelayChannel int `json:"relay_channel"` // 继电器通道号 + BusID int `json:"bus_id"` // 485 总线号 + BusAddress int `json:"bus_address"` // 485 总线地址 } // AreaController 是一个LoRa转总线(如485)的通信网关 @@ -55,6 +55,14 @@ type AreaController struct { Properties datatypes.JSON `json:"properties"` } +// SelfCheck 对 AreaController 的关键字段进行业务逻辑验证。 +func (ac *AreaController) SelfCheck() error { + if strings.TrimSpace(ac.NetworkID) == "" { + return errors.New("区域主控的 NetworkID 不能为空") + } + return nil +} + // TableName 自定义 GORM 使用的数据库表名 func (AreaController) TableName() string { return "area_controllers" @@ -68,9 +76,6 @@ type Device struct { // Name 是设备的业务名称,应清晰可读,例如 "1号猪舍温度传感器" Name string `gorm:"not null" json:"name"` - // Location 描述了设备的物理安装位置,例如 "1号猪舍东侧",方便运维。建立索引以优化按位置查询。 - Location string `gorm:"index" json:"location"` - // DeviceTemplateID 是设备模板的外键 DeviceTemplateID uint `gorm:"not null;index" json:"device_template_id"` @@ -83,11 +88,40 @@ type Device struct { // AreaController 是设备所属的区域主控 AreaController AreaController `json:"area_controller"` + // Location 描述了设备的物理安装位置,例如 "1号猪舍东侧",方便运维。建立索引以优化按位置查询。 + Location string `gorm:"index" json:"location"` + // Properties 用于存储特定类型设备的独有属性,采用JSON格式。 // 建议在应用层为不同子类型的设备定义专用的属性结构体(如 LoraProperties, BusProperties),以保证数据一致性。 Properties datatypes.JSON `json:"properties"` } +// SelfCheck 对 Device 的关键字段和属性进行业务逻辑验证。 +func (d *Device) SelfCheck() error { + if d.AreaControllerID == 0 { + return errors.New("设备必须关联一个区域主控 (AreaControllerID不能为0)") + } + if d.DeviceTemplateID == 0 { + return errors.New("设备必须关联一个设备模板 (DeviceTemplateID不能为0)") + } + + // 验证 Properties 是否包含必要的总线地址信息 + if d.Properties == nil { + return errors.New("设备属性 (Properties) 不能为空") + } + + var props map[string]interface{} + if err := json.Unmarshal(d.Properties, &props); err != nil { + return errors.New("无法解析设备属性 (Properties)") + } + + if _, ok := props[BusAddress]; !ok { + return errors.New("设备属性 (Properties) 中缺少总线地址 (bus_address)") + } + + return nil +} + // TableName 自定义 GORM 使用的数据库表名 func (Device) TableName() string { return "devices"