package service import ( "context" "encoding/json" "errors" "fmt" "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/logs" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" "git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" ) var ( // ErrDeviceInUse 表示设备正在被任务使用,无法删除 ErrDeviceInUse = errors.New("设备正在被一个或多个任务使用,无法删除") // ErrAreaControllerInUse 表示区域主控正在被设备使用,无法删除 ErrAreaControllerInUse = errors.New("区域主控正在被一个或多个设备使用,无法删除") // ErrDeviceTemplateInUse 表示设备模板正在被设备使用,无法删除 ErrDeviceTemplateInUse = errors.New("设备模板正在被一个或多个设备使用,无法删除") ) // DeviceService 定义了应用层的设备服务接口,用于协调设备相关的业务逻辑。 // DeviceService 定义了应用层的设备服务接口,用于协调设备相关的业务逻辑。 type DeviceService interface { CreateDevice(ctx context.Context, req *dto.CreateDeviceRequest) (*dto.DeviceResponse, error) GetDevice(ctx context.Context, id uint) (*dto.DeviceResponse, error) ListDevices(ctx context.Context) ([]*dto.DeviceResponse, error) UpdateDevice(ctx context.Context, id uint, req *dto.UpdateDeviceRequest) (*dto.DeviceResponse, error) DeleteDevice(ctx context.Context, id uint) error ManualControl(ctx context.Context, id uint, req *dto.ManualControlDeviceRequest) error CreateAreaController(ctx context.Context, req *dto.CreateAreaControllerRequest) (*dto.AreaControllerResponse, error) GetAreaController(ctx context.Context, id uint) (*dto.AreaControllerResponse, error) ListAreaControllers(ctx context.Context) ([]*dto.AreaControllerResponse, error) UpdateAreaController(ctx context.Context, id uint, req *dto.UpdateAreaControllerRequest) (*dto.AreaControllerResponse, error) DeleteAreaController(ctx context.Context, id uint) error CreateDeviceTemplate(ctx context.Context, req *dto.CreateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) GetDeviceTemplate(ctx context.Context, id uint) (*dto.DeviceTemplateResponse, error) ListDeviceTemplates(ctx context.Context) ([]*dto.DeviceTemplateResponse, error) UpdateDeviceTemplate(ctx context.Context, id uint, req *dto.UpdateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) DeleteDeviceTemplate(ctx context.Context, id uint) error } // deviceService 是 DeviceService 接口的具体实现。 type deviceService struct { ctx context.Context deviceRepo repository.DeviceRepository areaControllerRepo repository.AreaControllerRepository deviceTemplateRepo repository.DeviceTemplateRepository deviceDomainSvc device.Service } // NewDeviceService 创建一个新的 DeviceService 实例。 func NewDeviceService( ctx context.Context, deviceRepo repository.DeviceRepository, areaControllerRepo repository.AreaControllerRepository, deviceTemplateRepo repository.DeviceTemplateRepository, deviceDomainSvc device.Service, ) DeviceService { return &deviceService{ ctx: ctx, deviceRepo: deviceRepo, areaControllerRepo: areaControllerRepo, deviceTemplateRepo: deviceTemplateRepo, deviceDomainSvc: deviceDomainSvc, } } // --- Devices --- func (s *deviceService) CreateDevice(ctx context.Context, req *dto.CreateDeviceRequest) (*dto.DeviceResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateDevice") 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(serviceCtx, device); err != nil { return nil, err } createdDevice, err := s.deviceRepo.FindByID(serviceCtx, device.ID) if err != nil { return nil, err } return dto.NewDeviceResponse(createdDevice) } func (s *deviceService) GetDevice(ctx context.Context, id uint) (*dto.DeviceResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetDevice") device, err := s.deviceRepo.FindByID(serviceCtx, id) if err != nil { return nil, err } return dto.NewDeviceResponse(device) } func (s *deviceService) ListDevices(ctx context.Context) ([]*dto.DeviceResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListDevices") devices, err := s.deviceRepo.ListAll(serviceCtx) if err != nil { return nil, err } return dto.NewListDeviceResponse(devices) } func (s *deviceService) UpdateDevice(ctx context.Context, id uint, req *dto.UpdateDeviceRequest) (*dto.DeviceResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateDevice") existingDevice, err := s.deviceRepo.FindByID(serviceCtx, 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(serviceCtx, existingDevice); err != nil { return nil, err } updatedDevice, err := s.deviceRepo.FindByID(serviceCtx, existingDevice.ID) if err != nil { return nil, err } return dto.NewDeviceResponse(updatedDevice) } func (s *deviceService) DeleteDevice(ctx context.Context, id uint) error { serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteDevice") // 检查设备是否存在 _, err := s.deviceRepo.FindByID(serviceCtx, id) if err != nil { return err // 如果未找到,会返回 gorm.ErrRecordNotFound } // 在删除前检查设备是否被任务使用 inUse, err := s.deviceRepo.IsDeviceInUse(serviceCtx, id) if err != nil { // 如果检查过程中发生数据库错误,则返回错误 return fmt.Errorf("检查设备使用情况失败: %w", err) } if inUse { // 如果设备正在被使用,则返回特定的业务错误 return ErrDeviceInUse } // 只有在未被使用时,才执行删除操作 return s.deviceRepo.Delete(serviceCtx, id) } func (s *deviceService) ManualControl(ctx context.Context, id uint, req *dto.ManualControlDeviceRequest) error { serviceCtx := logs.AddFuncName(ctx, s.ctx, "ManualControl") dev, err := s.deviceRepo.FindByID(serviceCtx, id) if err != nil { return err } if req.Action == nil { return s.deviceDomainSvc.Collect(serviceCtx, 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(serviceCtx, dev, action) } } // --- Area Controllers --- func (s *deviceService) CreateAreaController(ctx context.Context, req *dto.CreateAreaControllerRequest) (*dto.AreaControllerResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateAreaController") 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(serviceCtx, ac); err != nil { return nil, err } return dto.NewAreaControllerResponse(ac) } func (s *deviceService) GetAreaController(ctx context.Context, id uint) (*dto.AreaControllerResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetAreaController") ac, err := s.areaControllerRepo.FindByID(serviceCtx, id) if err != nil { return nil, err } return dto.NewAreaControllerResponse(ac) } func (s *deviceService) ListAreaControllers(ctx context.Context) ([]*dto.AreaControllerResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListAreaControllers") acs, err := s.areaControllerRepo.ListAll(serviceCtx) if err != nil { return nil, err } return dto.NewListAreaControllerResponse(acs) } func (s *deviceService) UpdateAreaController(ctx context.Context, id uint, req *dto.UpdateAreaControllerRequest) (*dto.AreaControllerResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateAreaController") existingAC, err := s.areaControllerRepo.FindByID(serviceCtx, id) 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(serviceCtx, existingAC); err != nil { return nil, err } return dto.NewAreaControllerResponse(existingAC) } func (s *deviceService) DeleteAreaController(ctx context.Context, id uint) error { serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteAreaController") // 1. 检查是否存在 _, err := s.areaControllerRepo.FindByID(serviceCtx, id) if err != nil { return err // 如果未找到,gorm会返回 ErrRecordNotFound } // 2. 检查是否被使用(业务逻辑) inUse, err := s.deviceRepo.IsAreaControllerInUse(serviceCtx, id) if err != nil { return err // 返回数据库检查错误 } if inUse { return ErrAreaControllerInUse // 返回业务错误 } // 3. 执行删除 return s.areaControllerRepo.Delete(serviceCtx, id) } // --- Device Templates --- func (s *deviceService) CreateDeviceTemplate(ctx context.Context, req *dto.CreateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateDeviceTemplate") 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(serviceCtx, deviceTemplate); err != nil { return nil, err } return dto.NewDeviceTemplateResponse(deviceTemplate) } func (s *deviceService) GetDeviceTemplate(ctx context.Context, id uint) (*dto.DeviceTemplateResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetDeviceTemplate") deviceTemplate, err := s.deviceTemplateRepo.FindByID(serviceCtx, id) if err != nil { return nil, err } return dto.NewDeviceTemplateResponse(deviceTemplate) } func (s *deviceService) ListDeviceTemplates(ctx context.Context) ([]*dto.DeviceTemplateResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListDeviceTemplates") deviceTemplates, err := s.deviceTemplateRepo.ListAll(serviceCtx) if err != nil { return nil, err } return dto.NewListDeviceTemplateResponse(deviceTemplates) } func (s *deviceService) UpdateDeviceTemplate(ctx context.Context, id uint, req *dto.UpdateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateDeviceTemplate") existingDeviceTemplate, err := s.deviceTemplateRepo.FindByID(serviceCtx, id) 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(serviceCtx, existingDeviceTemplate); err != nil { return nil, err } return dto.NewDeviceTemplateResponse(existingDeviceTemplate) } func (s *deviceService) DeleteDeviceTemplate(ctx context.Context, id uint) error { serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteDeviceTemplate") // 1. 检查是否存在 _, err := s.deviceTemplateRepo.FindByID(serviceCtx, id) if err != nil { return err } // 2. 检查是否被使用(业务逻辑) inUse, err := s.deviceTemplateRepo.IsInUse(serviceCtx, id) if err != nil { return err } if inUse { return ErrDeviceTemplateInUse // 返回业务错误 } // 3. 执行删除 return s.deviceTemplateRepo.Delete(serviceCtx, id) }