增加任务增删改查时对设备任务关联表的维护
This commit is contained in:
1
internal/domain/plan/device_id_extractor.go
Normal file
1
internal/domain/plan/device_id_extractor.go
Normal file
@@ -0,0 +1 @@
|
||||
package plan
|
||||
@@ -2,6 +2,7 @@ package plan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||
@@ -55,25 +56,31 @@ type Service interface {
|
||||
type planServiceImpl struct {
|
||||
executionManager ExecutionManager
|
||||
taskManager AnalysisPlanTaskManager
|
||||
planRepo repository.PlanRepository // 新增
|
||||
// deviceRepo repository.DeviceRepository // 如果需要,新增
|
||||
logger *logs.Logger
|
||||
planRepo repository.PlanRepository
|
||||
deviceRepo repository.DeviceRepository
|
||||
unitOfWork repository.UnitOfWork
|
||||
taskFactory TaskFactory
|
||||
logger *logs.Logger
|
||||
}
|
||||
|
||||
// NewPlanService 创建一个新的 Service 实例。
|
||||
func NewPlanService(
|
||||
executionManager ExecutionManager,
|
||||
taskManager AnalysisPlanTaskManager,
|
||||
planRepo repository.PlanRepository, // 新增
|
||||
// deviceRepo repository.DeviceRepository, // 如果需要,新增
|
||||
planRepo repository.PlanRepository,
|
||||
deviceRepo repository.DeviceRepository,
|
||||
unitOfWork repository.UnitOfWork,
|
||||
taskFactory TaskFactory,
|
||||
logger *logs.Logger,
|
||||
) Service {
|
||||
return &planServiceImpl{
|
||||
executionManager: executionManager,
|
||||
taskManager: taskManager,
|
||||
planRepo: planRepo, // 注入
|
||||
// deviceRepo: deviceRepo, // 注入
|
||||
logger: logger,
|
||||
planRepo: planRepo,
|
||||
deviceRepo: deviceRepo,
|
||||
unitOfWork: unitOfWork,
|
||||
taskFactory: taskFactory,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,13 +124,40 @@ func (s *planServiceImpl) CreatePlan(planToCreate *models.Plan) (*models.Plan, e
|
||||
}
|
||||
planToCreate.ReorderSteps()
|
||||
|
||||
// 3. 调用仓库方法创建计划
|
||||
if err := s.planRepo.CreatePlan(planToCreate); err != nil {
|
||||
// 3. 在调用仓库前,准备好所有数据,包括设备关联
|
||||
for i := range planToCreate.Tasks {
|
||||
taskModel := &planToCreate.Tasks[i]
|
||||
// 使用工厂创建临时领域对象
|
||||
taskResolver, err := s.taskFactory.CreateTaskFromModel(taskModel)
|
||||
if err != nil {
|
||||
// 如果一个任务类型不支持,我们可以选择跳过或报错
|
||||
s.logger.Warnf("跳过为任务类型 '%s' 解析设备ID: %v", taskModel.Type, err)
|
||||
continue
|
||||
}
|
||||
|
||||
deviceIDs, err := taskResolver.ResolveDeviceIDs()
|
||||
if err != nil {
|
||||
// 在事务外解析失败,直接返回错误
|
||||
return nil, fmt.Errorf("为任务 '%s' 提取设备ID失败: %w", taskModel.Name, err)
|
||||
}
|
||||
if len(deviceIDs) > 0 {
|
||||
// 优化:无需查询完整的设备对象,只需构建包含ID的结构体即可建立关联
|
||||
devices := make([]models.Device, len(deviceIDs))
|
||||
for i, id := range deviceIDs {
|
||||
devices[i] = models.Device{Model: gorm.Model{ID: id}}
|
||||
}
|
||||
taskModel.Devices = devices
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 调用仓库方法创建计划,该方法内部会处理事务
|
||||
err := s.planRepo.CreatePlan(planToCreate)
|
||||
if err != nil {
|
||||
s.logger.Errorf("%s: 数据库创建计划失败: %v", actionType, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4. 创建成功后,调用 manager 确保触发器任务定义存在,但不立即加入待执行队列
|
||||
// 5. 创建成功后,调用 manager 确保触发器任务定义存在,但不立即加入待执行队列
|
||||
if err := s.taskManager.EnsureAnalysisTaskDefinition(planToCreate.ID); err != nil {
|
||||
// 这是一个非阻塞性错误,我们只记录日志,因为主流程(创建计划)已经成功
|
||||
s.logger.Errorf("为新创建的计划 %d 确保触发器任务定义失败: %v", planToCreate.ID, err)
|
||||
@@ -203,7 +237,32 @@ func (s *planServiceImpl) UpdatePlan(planToUpdate *models.Plan) (*models.Plan, e
|
||||
planToUpdate.ExecuteCount = 0
|
||||
s.logger.Infof("计划 #%d 被更新,执行计数器已重置为 0。", planToUpdate.ID)
|
||||
|
||||
if err := s.planRepo.UpdatePlanMetadataAndStructure(planToUpdate); err != nil {
|
||||
// 在调用仓库前,准备好所有数据,包括设备关联
|
||||
for i := range planToUpdate.Tasks {
|
||||
taskModel := &planToUpdate.Tasks[i]
|
||||
taskResolver, err := s.taskFactory.CreateTaskFromModel(taskModel)
|
||||
if err != nil {
|
||||
s.logger.Warnf("跳过为任务类型 '%s' 解析设备ID: %v", taskModel.Type, err)
|
||||
continue
|
||||
}
|
||||
|
||||
deviceIDs, err := taskResolver.ResolveDeviceIDs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("为任务 '%s' 提取设备ID失败: %w", taskModel.Name, err)
|
||||
}
|
||||
if len(deviceIDs) > 0 {
|
||||
// 优化:无需查询完整的设备对象,只需构建包含ID的结构体即可建立关联
|
||||
devices := make([]models.Device, len(deviceIDs))
|
||||
for i, id := range deviceIDs {
|
||||
devices[i] = models.Device{Model: gorm.Model{ID: id}}
|
||||
}
|
||||
taskModel.Devices = devices
|
||||
}
|
||||
}
|
||||
|
||||
// 调用仓库方法更新计划,该方法内部会处理事务
|
||||
err = s.planRepo.UpdatePlanMetadataAndStructure(planToUpdate)
|
||||
if err != nil {
|
||||
s.logger.Errorf("%s: 数据库更新计划失败: %v, Plan: %+v", actionType, err, planToUpdate)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -29,4 +29,6 @@ type TaskDeviceIDResolver interface {
|
||||
type TaskFactory interface {
|
||||
// Production 根据指定的任务执行日志创建一个任务实例。
|
||||
Production(claimedLog *models.TaskExecutionLog) Task
|
||||
// CreateTaskFromModel 仅根据任务模型创建一个任务实例,用于非执行场景(如参数解析)。
|
||||
CreateTaskFromModel(taskModel *models.Task) (TaskDeviceIDResolver, error)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/domain/device"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/domain/plan"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||
@@ -43,3 +45,27 @@ func (t *taskFactory) Production(claimedLog *models.TaskExecutionLog) plan.Task
|
||||
panic("不支持的任务类型") // 显式panic防编译器报错
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTaskFromModel 实现了 TaskFactory 接口,用于从模型创建任务实例。
|
||||
func (t *taskFactory) CreateTaskFromModel(taskModel *models.Task) (plan.TaskDeviceIDResolver, error) {
|
||||
// 这个方法不关心 claimedLog 的其他字段,所以可以构造一个临时的
|
||||
// 它只用于访问那些不依赖于执行日志的方法,比如 ResolveDeviceIDs
|
||||
tempLog := &models.TaskExecutionLog{Task: *taskModel}
|
||||
|
||||
switch taskModel.Type {
|
||||
case models.TaskTypeWaiting:
|
||||
return NewDelayTask(t.logger, tempLog), nil
|
||||
case models.TaskTypeReleaseFeedWeight:
|
||||
return NewReleaseFeedWeightTask(
|
||||
tempLog,
|
||||
t.sensorDataRepo,
|
||||
t.deviceRepo,
|
||||
t.deviceService,
|
||||
t.logger,
|
||||
), nil
|
||||
case models.TaskTypeFullCollection:
|
||||
return NewFullCollectionTask(tempLog, t.deviceRepo, t.deviceService, t.logger), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持为类型 '%s' 的任务创建模型实例", taskModel.Type)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user