391 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			391 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
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))
 | 
						|
}
 |