package service import ( "encoding/json" "errors" "fmt" "strconv" "git.huangwc.com/pig/pig-farm-controller/internal/app/dto" "git.huangwc.com/pig/pig-farm-controller/internal/domain/device" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" "git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" ) // ErrDeviceInUse 表示设备正在被任务使用,无法删除 var ErrDeviceInUse = errors.New("设备正在被一个或多个任务使用,无法删除") // DeviceService 定义了应用层的设备服务接口,用于协调设备相关的业务逻辑。 type DeviceService interface { CreateDevice(req *dto.CreateDeviceRequest) (*dto.DeviceResponse, error) GetDevice(id string) (*dto.DeviceResponse, error) ListDevices() ([]*dto.DeviceResponse, error) UpdateDevice(id string, req *dto.UpdateDeviceRequest) (*dto.DeviceResponse, error) DeleteDevice(id string) error ManualControl(id string, req *dto.ManualControlDeviceRequest) error CreateAreaController(req *dto.CreateAreaControllerRequest) (*dto.AreaControllerResponse, error) GetAreaController(id string) (*dto.AreaControllerResponse, error) ListAreaControllers() ([]*dto.AreaControllerResponse, error) UpdateAreaController(id string, req *dto.UpdateAreaControllerRequest) (*dto.AreaControllerResponse, error) DeleteAreaController(id string) error CreateDeviceTemplate(req *dto.CreateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) GetDeviceTemplate(id string) (*dto.DeviceTemplateResponse, error) ListDeviceTemplates() ([]*dto.DeviceTemplateResponse, error) UpdateDeviceTemplate(id string, req *dto.UpdateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) DeleteDeviceTemplate(id string) error } // deviceService 是 DeviceService 接口的具体实现。 type deviceService struct { deviceRepo repository.DeviceRepository areaControllerRepo repository.AreaControllerRepository deviceTemplateRepo repository.DeviceTemplateRepository deviceDomainSvc device.Service // 依赖领域服务 } // NewDeviceService 创建一个新的 DeviceService 实例。 func NewDeviceService( deviceRepo repository.DeviceRepository, areaControllerRepo repository.AreaControllerRepository, deviceTemplateRepo repository.DeviceTemplateRepository, deviceDomainSvc device.Service, ) DeviceService { return &deviceService{ deviceRepo: deviceRepo, areaControllerRepo: areaControllerRepo, deviceTemplateRepo: deviceTemplateRepo, deviceDomainSvc: deviceDomainSvc, } } // --- Devices --- func (s *deviceService) CreateDevice(req *dto.CreateDeviceRequest) (*dto.DeviceResponse, error) { propertiesJSON, err := json.Marshal(req.Properties) if err != nil { return nil, err // Consider wrapping this error for better context } device := &models.Device{ Name: req.Name, DeviceTemplateID: req.DeviceTemplateID, AreaControllerID: req.AreaControllerID, Location: req.Location, Properties: propertiesJSON, } if err := device.SelfCheck(); err != nil { return nil, err } if err := s.deviceRepo.Create(device); err != nil { return nil, err } createdDevice, err := s.deviceRepo.FindByID(device.ID) if err != nil { return nil, err } return dto.NewDeviceResponse(createdDevice) } func (s *deviceService) GetDevice(id string) (*dto.DeviceResponse, error) { device, err := s.deviceRepo.FindByIDString(id) if err != nil { return nil, err } return dto.NewDeviceResponse(device) } func (s *deviceService) ListDevices() ([]*dto.DeviceResponse, error) { devices, err := s.deviceRepo.ListAll() if err != nil { return nil, err } return dto.NewListDeviceResponse(devices) } func (s *deviceService) UpdateDevice(id string, req *dto.UpdateDeviceRequest) (*dto.DeviceResponse, error) { existingDevice, err := s.deviceRepo.FindByIDString(id) if err != nil { return nil, err } propertiesJSON, err := json.Marshal(req.Properties) if err != nil { return nil, err } existingDevice.Name = req.Name existingDevice.DeviceTemplateID = req.DeviceTemplateID existingDevice.AreaControllerID = req.AreaControllerID existingDevice.Location = req.Location existingDevice.Properties = propertiesJSON if err := existingDevice.SelfCheck(); err != nil { return nil, err } if err := s.deviceRepo.Update(existingDevice); err != nil { return nil, err } updatedDevice, err := s.deviceRepo.FindByID(existingDevice.ID) if err != nil { return nil, err } return dto.NewDeviceResponse(updatedDevice) } func (s *deviceService) DeleteDevice(id string) error { idUint, err := strconv.ParseUint(id, 10, 64) if err != nil { return err } deviceID := uint(idUint) // 检查设备是否存在 _, err = s.deviceRepo.FindByID(deviceID) if err != nil { return err // 如果未找到,会返回 gorm.ErrRecordNotFound } // 在删除前检查设备是否被任务使用 inUse, err := s.deviceRepo.IsDeviceInUse(deviceID) if err != nil { // 如果检查过程中发生数据库错误,则返回错误 return fmt.Errorf("检查设备使用情况失败: %w", err) } if inUse { // 如果设备正在被使用,则返回特定的业务错误 return ErrDeviceInUse } // 只有在未被使用时,才执行删除操作 return s.deviceRepo.Delete(deviceID) } func (s *deviceService) ManualControl(id string, req *dto.ManualControlDeviceRequest) error { dev, err := s.deviceRepo.FindByIDString(id) if err != nil { return err } if req.Action == nil { return s.deviceDomainSvc.Collect(dev.AreaControllerID, []*models.Device{dev}) } else { action := device.DeviceActionStart switch *req.Action { case "off": action = device.DeviceActionStop case "on": action = device.DeviceActionStart default: return errors.New("invalid action") } return s.deviceDomainSvc.Switch(dev, action) } } // --- Area Controllers --- func (s *deviceService) CreateAreaController(req *dto.CreateAreaControllerRequest) (*dto.AreaControllerResponse, error) { propertiesJSON, err := json.Marshal(req.Properties) if err != nil { return nil, err } ac := &models.AreaController{ Name: req.Name, NetworkID: req.NetworkID, Location: req.Location, Properties: propertiesJSON, } if err := ac.SelfCheck(); err != nil { return nil, err } if err := s.areaControllerRepo.Create(ac); err != nil { return nil, err } return dto.NewAreaControllerResponse(ac) } func (s *deviceService) GetAreaController(id string) (*dto.AreaControllerResponse, error) { idUint, err := strconv.ParseUint(id, 10, 64) if err != nil { return nil, err } ac, err := s.areaControllerRepo.FindByID(uint(idUint)) if err != nil { return nil, err } return dto.NewAreaControllerResponse(ac) } func (s *deviceService) ListAreaControllers() ([]*dto.AreaControllerResponse, error) { acs, err := s.areaControllerRepo.ListAll() if err != nil { return nil, err } return dto.NewListAreaControllerResponse(acs) } func (s *deviceService) UpdateAreaController(id string, req *dto.UpdateAreaControllerRequest) (*dto.AreaControllerResponse, error) { idUint, err := strconv.ParseUint(id, 10, 64) if err != nil { return nil, err } existingAC, err := s.areaControllerRepo.FindByID(uint(idUint)) if err != nil { return nil, err } propertiesJSON, err := json.Marshal(req.Properties) if err != nil { return nil, err } existingAC.Name = req.Name existingAC.NetworkID = req.NetworkID existingAC.Location = req.Location existingAC.Properties = propertiesJSON if err := existingAC.SelfCheck(); err != nil { return nil, err } if err := s.areaControllerRepo.Update(existingAC); err != nil { return nil, err } return dto.NewAreaControllerResponse(existingAC) } func (s *deviceService) DeleteAreaController(id string) error { idUint, err := strconv.ParseUint(id, 10, 64) if err != nil { return err } _, err = s.areaControllerRepo.FindByID(uint(idUint)) if err != nil { return err } return s.areaControllerRepo.Delete(uint(idUint)) } // --- Device Templates --- func (s *deviceService) CreateDeviceTemplate(req *dto.CreateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) { commandsJSON, err := json.Marshal(req.Commands) if err != nil { return nil, err } valuesJSON, err := json.Marshal(req.Values) if err != nil { return nil, err } deviceTemplate := &models.DeviceTemplate{ Name: req.Name, Manufacturer: req.Manufacturer, Description: req.Description, Category: req.Category, Commands: commandsJSON, Values: valuesJSON, } if err := deviceTemplate.SelfCheck(); err != nil { return nil, err } if err := s.deviceTemplateRepo.Create(deviceTemplate); err != nil { return nil, err } return dto.NewDeviceTemplateResponse(deviceTemplate) } func (s *deviceService) GetDeviceTemplate(id string) (*dto.DeviceTemplateResponse, error) { idUint, err := strconv.ParseUint(id, 10, 64) if err != nil { return nil, err } deviceTemplate, err := s.deviceTemplateRepo.FindByID(uint(idUint)) if err != nil { return nil, err } return dto.NewDeviceTemplateResponse(deviceTemplate) } func (s *deviceService) ListDeviceTemplates() ([]*dto.DeviceTemplateResponse, error) { deviceTemplates, err := s.deviceTemplateRepo.ListAll() if err != nil { return nil, err } return dto.NewListDeviceTemplateResponse(deviceTemplates) } func (s *deviceService) UpdateDeviceTemplate(id string, req *dto.UpdateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) { idUint, err := strconv.ParseUint(id, 10, 64) if err != nil { return nil, err } existingDeviceTemplate, err := s.deviceTemplateRepo.FindByID(uint(idUint)) if err != nil { return nil, err } commandsJSON, err := json.Marshal(req.Commands) if err != nil { return nil, err } valuesJSON, err := json.Marshal(req.Values) if err != nil { return nil, err } existingDeviceTemplate.Name = req.Name existingDeviceTemplate.Manufacturer = req.Manufacturer existingDeviceTemplate.Description = req.Description existingDeviceTemplate.Category = req.Category existingDeviceTemplate.Commands = commandsJSON existingDeviceTemplate.Values = valuesJSON if err := existingDeviceTemplate.SelfCheck(); err != nil { return nil, err } if err := s.deviceTemplateRepo.Update(existingDeviceTemplate); err != nil { return nil, err } return dto.NewDeviceTemplateResponse(existingDeviceTemplate) } func (s *deviceService) DeleteDeviceTemplate(id string) error { idUint, err := strconv.ParseUint(id, 10, 64) if err != nil { return err } _, err = s.deviceTemplateRepo.FindByID(uint(idUint)) if err != nil { return err } return s.deviceTemplateRepo.Delete(uint(idUint)) }