diff --git a/internal/app/service/task/scheduler.go b/internal/app/service/task/scheduler.go index de821ee..6e93590 100644 --- a/internal/app/service/task/scheduler.go +++ b/internal/app/service/task/scheduler.go @@ -246,48 +246,7 @@ func (s *Scheduler) processTask(claimedLog *models.TaskExecutionLog) { // 如果此计划执行中,未完成的任务只剩下当前这一个(因为当前任务的状态此时在数据库中仍为 'started'), // 则认为整个计划已完成。 if incompleteCount == 1 { - s.logger.Infof("计划执行 %d 的所有任务已完成,开始处理计划完成逻辑...", claimedLog.PlanExecutionLogID) - - // 通过 PlanExecutionLog 反查正确的顶层 PlanID - planExecutionLog, err := s.executionLogRepo.FindPlanExecutionLogByID(claimedLog.PlanExecutionLogID) - if err != nil { - s.logger.Errorf("获取计划执行日志 %d 失败: %v", claimedLog.PlanExecutionLogID, err) - return - } - planID := planExecutionLog.PlanID // 这才是正确的顶层计划ID - - // 获取计划的最新数据,这里我们只需要基本信息来判断执行类型和次数 - plan, err := s.planRepo.GetBasicPlanByID(planID) - if err != nil { - s.logger.Errorf("获取计划 %d 的基本信息失败: %v", planID, err) - return - } - - // 在内存中计算新的计数值和状态 - newExecuteCount := plan.ExecuteCount + 1 - newStatus := plan.Status // 默认为当前状态 - - // 如果是自动计划且达到执行次数上限,或计划是手动类型,则更新计划状态为已停止 - if (plan.ExecutionType == models.PlanExecutionTypeAutomatic && plan.ExecuteNum > 0 && newExecuteCount >= plan.ExecuteNum) || plan.ExecutionType == models.PlanExecutionTypeManual { - newStatus = models.PlanStatusStopeed - s.logger.Infof("计划 %d 已完成执行,状态更新为 '执行完毕'。", planID) - } - - // 使用新的、专门的方法来原子性地更新计数值和状态 - if err := s.planRepo.UpdatePlanStateAfterExecution(planID, newExecuteCount, newStatus); err != nil { - s.logger.Errorf("更新计划 %d 的执行后状态失败: %v", planID, err) - return - } - - // 更新计划执行日志状态为完成 - if err := s.executionLogRepo.UpdatePlanExecutionLogStatus(claimedLog.PlanExecutionLogID, models.ExecutionStatusCompleted); err != nil { - s.logger.Errorf("更新计划执行日志 %d 状态为 '完成' 失败: %v", claimedLog.PlanExecutionLogID, err) - } - - // 调用共享的 Manager 来处理触发器更新逻辑 - if err := s.analysisPlanTaskManager.CreateOrUpdateTrigger(planID); err != nil { - s.logger.Errorf("为计划 %d 创建/更新触发器失败: %v", planID, err) - } + s.handlePlanCompletion(claimedLog.PlanExecutionLogID) } } @@ -387,6 +346,13 @@ func (s *Scheduler) analysisPlan(claimedLog *models.TaskExecutionLog) error { return err } + // --- 处理空计划的边缘情况 --- + // 如果一个计划被解析后,发现其任务列表为空, + // 那么它实际上已经“执行”完毕了,我们需要在这里手动为它创建下一次的触发器。 + if len(tasks) == 0 { + s.handlePlanCompletion(planLog.ID) + } + return nil } @@ -429,3 +395,49 @@ func (s *Scheduler) handlePlanTermination(planLogID uint, reason string) { s.logger.Errorf("更新计划 %d 状态为 '失败' 时出错: %v", planLog.PlanID, err) } } + +// handlePlanCompletion 集中处理计划成功完成后的所有逻辑 +func (s *Scheduler) handlePlanCompletion(planLogID uint) { + s.logger.Infof("计划执行 %d 的所有任务已完成,开始处理计划完成逻辑...", planLogID) + + // 1. 通过 PlanExecutionLog 反查正确的顶层 PlanID + planExecutionLog, err := s.executionLogRepo.FindPlanExecutionLogByID(planLogID) + if err != nil { + s.logger.Errorf("获取计划执行日志 %d 失败: %v", planLogID, err) + return + } + topLevelPlanID := planExecutionLog.PlanID // 这才是正确的顶层计划ID + + // 2. 获取计划的最新数据,这里我们只需要基本信息来判断执行类型和次数 + plan, err := s.planRepo.GetBasicPlanByID(topLevelPlanID) + if err != nil { + s.logger.Errorf("获取计划 %d 的基本信息失败: %v", topLevelPlanID, err) + return + } + + // 3. 在内存中计算新的计数值和状态 + newExecuteCount := plan.ExecuteCount + 1 + newStatus := plan.Status // 默认为当前状态 + + // 如果是自动计划且达到执行次数上限,或计划是手动类型,则更新计划状态为已停止 + if (plan.ExecutionType == models.PlanExecutionTypeAutomatic && plan.ExecuteNum > 0 && newExecuteCount >= plan.ExecuteNum) || plan.ExecutionType == models.PlanExecutionTypeManual { + newStatus = models.PlanStatusStopeed + s.logger.Infof("计划 %d 已完成执行,状态更新为 '执行完毕'。", topLevelPlanID) + } + + // 4. 使用专门的方法来原子性地更新计数值和状态 + if err := s.planRepo.UpdatePlanStateAfterExecution(topLevelPlanID, newExecuteCount, newStatus); err != nil { + s.logger.Errorf("更新计划 %d 的执行后状态失败: %v", topLevelPlanID, err) + return + } + + // 5. 更新计划执行日志状态为完成 + if err := s.executionLogRepo.UpdatePlanExecutionLogStatus(planLogID, models.ExecutionStatusCompleted); err != nil { + s.logger.Errorf("更新计划执行日志 %d 状态为 '完成' 失败: %v", planLogID, err) + } + + // 6. 调用共享的 Manager 来处理触发器更新逻辑 + if err := s.analysisPlanTaskManager.CreateOrUpdateTrigger(topLevelPlanID); err != nil { + s.logger.Errorf("为计划 %d 创建/更新触发器失败: %v", topLevelPlanID, err) + } +}