Files
pig-farm-controller/internal/app/service/device_service.go

423 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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"
)
var (
// ErrDeviceInUse 表示设备正在被任务使用,无法删除
ErrDeviceInUse = errors.New("设备正在被一个或多个任务使用,无法删除")
// ErrAreaControllerInUse 表示区域主控正在被设备使用,无法删除
ErrAreaControllerInUse = errors.New("区域主控正在被一个或多个设备使用,无法删除")
// ErrDeviceTemplateInUse 表示设备模板正在被设备使用,无法删除
ErrDeviceTemplateInUse = 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 fmt.Errorf("无效的ID格式: %w", err)
}
acID := uint(idUint)
// 1. 检查是否存在
_, err = s.areaControllerRepo.FindByID(acID)
if err != nil {
return err // 如果未找到gorm会返回 ErrRecordNotFound
}
// 2. 检查是否被使用(业务逻辑)
inUse, err := s.deviceRepo.IsAreaControllerInUse(acID)
if err != nil {
return err // 返回数据库检查错误
}
if inUse {
return ErrAreaControllerInUse // 返回业务错误
}
// 3. 执行删除
return s.areaControllerRepo.Delete(acID)
}
// --- 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 fmt.Errorf("无效的ID格式: %w", err)
}
dtID := uint(idUint)
// 1. 检查是否存在
_, err = s.deviceTemplateRepo.FindByID(dtID)
if err != nil {
return err
}
// 2. 检查是否被使用(业务逻辑)
inUse, err := s.deviceTemplateRepo.IsInUse(dtID)
if err != nil {
return err
}
if inUse {
return ErrDeviceTemplateInUse // 返回业务错误
}
// 3. 执行删除
return s.deviceTemplateRepo.Delete(dtID)
}