package device import ( "context" "fmt" "time" "iot-base-station/pkg/database" "iot-base-station/pkg/security" "github.com/go-redis/redis/v8" "go.uber.org/zap" "gorm.io/gorm" ) // Manager 设备管理器 type Manager struct { db *gorm.DB redis *redis.Client logger *zap.Logger } // NewManager 创建设备管理器 func NewManager(db *gorm.DB, redis *redis.Client, logger *zap.Logger) *Manager { return &Manager{ db: db, redis: redis, logger: logger, } } // CreateDevice 创建设备 func (m *Manager) CreateDevice(ctx context.Context, req *CreateDeviceRequest) (*DeviceResponse, error) { // 生成设备ID deviceID := generateDeviceID(req.Type) // 创建设备对象 device := &Device{ ID: deviceID, Name: req.Name, Type: req.Type, Protocol: req.Protocol, Status: StatusOffline, Metadata: req.Metadata, Config: req.Config, Location: req.Location, Firmware: FirmwareInfo{ Version: "1.0.0", ReleaseDate: time.Now(), }, } // 保存到数据库 if err := m.db.WithContext(ctx).Create(device).Error; err != nil { m.logger.Error("Failed to create device", zap.Error(err)) return nil, fmt.Errorf("failed to create device: %w", err) } // 创建设备事件 event := &DeviceEvent{ ID: generateEventID(), DeviceID: deviceID, Type: EventConnect, Severity: SeverityInfo, Message: "设备已创建", Timestamp: time.Now(), } if err := m.db.WithContext(ctx).Create(event).Error; err != nil { m.logger.Error("Failed to create device event", zap.Error(err)) } m.logger.Info("Device created", zap.String("device_id", deviceID)) return &device.ToResponse(), nil } // ListDevices 获取设备列表 func (m *Manager) ListDevices(ctx context.Context, page, pageSize int, deviceType string) ([]DeviceResponse, int64, error) { var devices []Device var total int64 query := m.db.WithContext(ctx).Model(&Device{}) // 按类型筛选 if deviceType != "" { query = query.Where("type = ?", deviceType) } // 获取总数 if err := query.Count(&total).Error; err != nil { m.logger.Error("Failed to count devices", zap.Error(err)) return nil, 0, fmt.Errorf("failed to count devices: %w", err) } // 分页查询 offset := (page - 1) * pageSize if err := query.Offset(offset).Limit(pageSize).Find(&devices).Error; err != nil { m.logger.Error("Failed to list devices", zap.Error(err)) return nil, 0, fmt.Errorf("failed to list devices: %w", err) } // 转换为响应格式 responses := make([]DeviceResponse, len(devices)) for i, device := range devices { responses[i] = device.ToResponse() } return responses, total, nil } // GetDevice 获取设备详情 func (m *Manager) GetDevice(ctx context.Context, deviceID string) (*DeviceResponse, error) { var device Device if err := m.db.WithContext(ctx).Where("id = ?", deviceID).First(&device).Error; err != nil { if err == gorm.ErrRecordNotFound { return nil, fmt.Errorf("device not found") } m.logger.Error("Failed to get device", zap.Error(err)) return nil, fmt.Errorf("failed to get device: %w", err) } return &device.ToResponse(), nil } // UpdateDevice 更新设备信息 func (m *Manager) UpdateDevice(ctx context.Context, deviceID string, req *UpdateDeviceRequest) (*DeviceResponse, error) { var device Device // 查询设备 if err := m.db.WithContext(ctx).Where("id = ?", deviceID).First(&device).Error; err != nil { if err == gorm.ErrRecordNotFound { return nil, fmt.Errorf("device not found") } m.logger.Error("Failed to get device", zap.Error(err)) return nil, fmt.Errorf("failed to get device: %w", err) } // 更新字段 if req.Name != "" { device.Name = req.Name } if req.Metadata != nil { device.Metadata = req.Metadata } if req.Config.ReportInterval > 0 { device.Config.ReportInterval = req.Config.ReportInterval } if req.Config.Threshold != nil { device.Config.Threshold = req.Config.Threshold } if req.Location.Address != "" { device.Location = req.Location } // 保存更新 if err := m.db.WithContext(ctx).Save(&device).Error; err != nil { m.logger.Error("Failed to update device", zap.Error(err)) return nil, fmt.Errorf("failed to update device: %w", err) } // 创建配置更新事件 event := &DeviceEvent{ ID: generateEventID(), DeviceID: deviceID, Type: EventConfig, Severity: SeverityInfo, Message: "设备配置已更新", Timestamp: time.Now(), } if err := m.db.WithContext(ctx).Create(event).Error; err != nil { m.logger.Error("Failed to create device event", zap.Error(err)) } m.logger.Info("Device updated", zap.String("device_id", deviceID)) return &device.ToResponse(), nil } // DeleteDevice 删除设备 func (m *Manager) DeleteDevice(ctx context.Context, deviceID string) error { // 软删除设备 if err := m.db.WithContext(ctx).Delete(&Device{}, "id = ?", deviceID).Error; err != nil { m.logger.Error("Failed to delete device", zap.Error(err)) return fmt.Errorf("failed to delete device: %w", err) } m.logger.Info("Device deleted", zap.String("device_id", deviceID)) return nil } // UpdateDeviceConfig 更新设备配置 func (m *Manager) UpdateDeviceConfig(ctx context.Context, deviceID string, req *UpdateDeviceConfigRequest) error { var device Device // 查询设备 if err := m.db.WithContext(ctx).Where("id = ?", deviceID).First(&device).Error; err != nil { if err == gorm.ErrRecordNotFound { return fmt.Errorf("device not found") } m.logger.Error("Failed to get device", zap.Error(err)) return fmt.Errorf("failed to get device: %w", err) } // 更新配置 device.Config = req.Config // 保存更新 if err := m.db.WithContext(ctx).Save(&device).Error; err != nil { m.logger.Error("Failed to update device config", zap.Error(err)) return fmt.Errorf("failed to update device config: %w", err) } // 创建配置更新事件 event := &DeviceEvent{ ID: generateEventID(), DeviceID: deviceID, Type: EventConfig, Severity: SeverityInfo, Message: "设备配置已更新", Timestamp: time.Now(), } if err := m.db.WithContext(ctx).Create(event).Error; err != nil { m.logger.Error("Failed to create device event", zap.Error(err)) } m.logger.Info("Device config updated", zap.String("device_id", deviceID)) return nil } // UpdateFirmware 更新设备固件 func (m *Manager) UpdateFirmware(ctx context.Context, deviceID string, req *UpdateFirmwareRequest) error { var device Device // 查询设备 if err := m.db.WithContext(ctx).Where("id = ?", deviceID).First(&device).Error; err != nil { if err == gorm.ErrRecordNotFound { return fmt.Errorf("device not found") } m.logger.Error("Failed to get device", zap.Error(err)) return fmt.Errorf("failed to get device: %w", err) } // 更新固件信息 device.Firmware = FirmwareInfo{ Version: req.Version, URL: req.URL, Description: req.Description, ReleaseDate: time.Now(), } // 更新设备状态为更新中 device.Status = StatusUpdating // 保存更新 if err := m.db.WithContext(ctx).Save(&device).Error; err != nil { m.logger.Error("Failed to update device firmware", zap.Error(err)) return fmt.Errorf("failed to update device firmware: %w", err) } // 创建固件更新事件 event := &DeviceEvent{ ID: generateEventID(), DeviceID: deviceID, Type: EventFirmware, Severity: SeverityInfo, Message: fmt.Sprintf("固件更新开始,版本: %s", req.Version), Timestamp: time.Now(), } if err := m.db.WithContext(ctx).Create(event).Error; err != nil { m.logger.Error("Failed to create device event", zap.Error(err)) } m.logger.Info("Device firmware update started", zap.String("device_id", deviceID), zap.String("version", req.Version)) // TODO: 实际的固件更新逻辑 // 这里应该通过协议适配器发送固件更新命令 return nil } // StartStatusCheck 启动设备状态检查 func (m *Manager) StartStatusCheck() { ticker := time.NewTicker(5 * time.Minute) defer ticker.Stop() for { select { case <-ticker.C: m.checkDeviceStatus() } } } // checkDeviceStatus 检查设备状态 func (m *Manager) checkDeviceStatus() { ctx := context.Background() var devices []Device // 查询所有在线设备 if err := m.db.WithContext(ctx).Where("status = ?", StatusOnline).Find(&devices).Error; err != nil { m.logger.Error("Failed to get online devices", zap.Error(err)) return } // 检查每个设备的最后在线时间 now := time.Now() for _, device := range devices { if device.LastSeen != nil && now.Sub(*device.LastSeen) > 10*time.Minute { // 设备离线 device.Status = StatusOffline m.db.WithContext(ctx).Save(&device) // 创建离线事件 event := &DeviceEvent{ ID: generateEventID(), DeviceID: device.ID, Type: EventDisconnect, Severity: SeverityWarning, Message: "设备离线", Timestamp: now, } m.db.WithContext(ctx).Create(event) m.logger.Info("Device went offline", zap.String("device_id", device.ID)) } } } // generateDeviceID 生成设备ID func generateDeviceID(deviceType string) string { return fmt.Sprintf("%s_%d", deviceType, time.Now().UnixNano()) } // generateEventID 生成事件ID func generateEventID() string { return fmt.Sprintf("event_%d", time.Now().UnixNano()) }