Files
pig-farm-controller/internal/app/service/device_service.go
2025-12-05 14:44:36 +08:00

197 lines
6.2 KiB
Go

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 定义了应用层的设备服务接口,用于协调设备相关的业务逻辑。
type DeviceService interface {
CreateDevice(ctx context.Context, req *dto.CreateDeviceRequest) (*dto.DeviceResponse, error)
GetDevice(ctx context.Context, id uint32) (*dto.DeviceResponse, error)
ListDevices(ctx context.Context) ([]*dto.DeviceResponse, error)
UpdateDevice(ctx context.Context, id uint32, req *dto.UpdateDeviceRequest) (*dto.DeviceResponse, error)
DeleteDevice(ctx context.Context, id uint32) error
ManualControl(ctx context.Context, id uint32, req *dto.ManualControlDeviceRequest) error
}
// deviceService 是 DeviceService 接口的具体实现。
type deviceService struct {
ctx context.Context
deviceRepo repository.DeviceRepository
deviceDomainSvc device.DeviceOperator
thresholdAlarmService ThresholdAlarmService
}
// NewDeviceService 创建一个新的 DeviceService 实例。
func NewDeviceService(
ctx context.Context,
deviceRepo repository.DeviceRepository,
deviceDomainSvc device.DeviceOperator,
thresholdAlarmService ThresholdAlarmService,
) DeviceService {
return &deviceService{
ctx: ctx,
deviceRepo: deviceRepo,
deviceDomainSvc: deviceDomainSvc,
thresholdAlarmService: thresholdAlarmService,
}
}
// --- 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 uint32) (*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 uint32, 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 uint32) 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, []models.TaskType{models.TaskTypeDeviceThresholdCheck})
if err != nil {
// 如果检查过程中发生数据库错误,则返回错误
return fmt.Errorf("检查设备使用情况失败: %w", err)
}
if inUse {
// 如果设备正在被使用,则返回特定的业务错误
return ErrDeviceInUse
}
// TODO 这个应该用事务处理
err = s.thresholdAlarmService.DeleteDeviceThresholdAlarmByDeviceID(serviceCtx, id)
if err != nil {
return fmt.Errorf("删除设备阈值告警失败: %w", err)
}
// 只有在未被使用时,才执行删除操作
return s.deviceRepo.Delete(serviceCtx, id)
}
func (s *deviceService) ManualControl(ctx context.Context, id uint32, 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)
}
}