diff --git a/internal/app/controller/plan/plan_controller.go b/internal/app/controller/plan/plan_controller.go index 5eb85bd..615587e 100644 --- a/internal/app/controller/plan/plan_controller.go +++ b/internal/app/controller/plan/plan_controller.go @@ -350,9 +350,50 @@ func (c *Controller) DeletePlan(ctx *gin.Context) { // @Success 200 {object} controller.Response "业务码为200代表成功启动计划" // @Router /api/v1/plans/{id}/start [post] func (c *Controller) StartPlan(ctx *gin.Context) { - // 占位符:此处应调用服务层或仓库层来启动计划 - c.logger.Infof("收到启动计划请求 (占位符)") - controller.SendResponse(ctx, controller.CodeSuccess, "启动计划接口占位符", nil) + // 1. 从 URL 路径中获取 ID + idStr := ctx.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + controller.SendErrorResponse(ctx, controller.CodeBadRequest, "无效的计划ID格式") + return + } + + // 2. 检查计划是否存在 + plan, err := c.planRepo.GetBasicPlanByID(uint(id)) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + controller.SendErrorResponse(ctx, controller.CodeNotFound, "计划不存在") + return + } + c.logger.Errorf("启动计划时获取计划信息失败: %v", err) + controller.SendErrorResponse(ctx, controller.CodeInternalError, "获取计划信息时发生内部错误") + return + } + + // 3. 检查计划当前状态 + if plan.Status == models.PlanStatusEnabled { + controller.SendErrorResponse(ctx, controller.CodeBadRequest, "计划已处于启动状态,无需重复操作") + return + } + + // 4. 更新计划状态为“已启动” + if err := c.planRepo.UpdatePlanStatus(plan.ID, models.PlanStatusEnabled); err != nil { + c.logger.Errorf("更新计划 #%d 状态为 '已启动' 失败: %v", plan.ID, err) + controller.SendErrorResponse(ctx, controller.CodeInternalError, "更新计划状态失败") + return + } + c.logger.Infof("已成功更新计划 #%d 的状态为 '已启动'", plan.ID) + + // 5. 为计划创建或更新触发器 + if err := c.analysisPlanTaskManager.CreateOrUpdateTrigger(plan.ID); err != nil { + // 此处错误不回滚状态,因为状态更新已成功,但需要明确告知用户触发器创建失败 + c.logger.Errorf("为计划 #%d 创建或更新触发器失败: %v", plan.ID, err) + controller.SendErrorResponse(ctx, controller.CodeInternalError, "计划状态已更新,但创建执行触发器失败,请检查计划配置或稍后重试") + return + } + + // 6. 发送成功响应 + controller.SendResponse(ctx, controller.CodeSuccess, "计划已成功启动", nil) } // StopPlan godoc diff --git a/internal/app/service/task/analysis_plan_task_manager.go b/internal/app/service/task/analysis_plan_task_manager.go index 6576239..4bac7ec 100644 --- a/internal/app/service/task/analysis_plan_task_manager.go +++ b/internal/app/service/task/analysis_plan_task_manager.go @@ -67,8 +67,7 @@ func (m *AnalysisPlanTaskManager) Refresh() error { } // CreateOrUpdateTrigger 为给定的 planID 创建其关联的触发任务。 -// 这个方法是幂等的:如果一个有效的触发器已存在,它将不会重复创建。 -// 关键修改:如果触发器已存在,会根据计划类型更新其执行时间。 +// 如果触发器已存在,会根据计划类型更新其执行时间。 func (m *AnalysisPlanTaskManager) CreateOrUpdateTrigger(planID uint) error { m.mu.Lock() defer m.mu.Unlock() diff --git a/internal/infra/repository/plan_repository.go b/internal/infra/repository/plan_repository.go index 424ee21..d3977d2 100644 --- a/internal/infra/repository/plan_repository.go +++ b/internal/infra/repository/plan_repository.go @@ -34,6 +34,8 @@ type PlanRepository interface { CreatePlan(plan *models.Plan) error // UpdatePlan 更新计划,包括子计划和任务 UpdatePlan(plan *models.Plan) error + // UpdatePlanStatus 更新指定计划的状态 + UpdatePlanStatus(id uint, status models.PlanStatus) error // DeletePlan 根据ID删除计划,同时删除其关联的任务(非子任务)或子计划关联 DeletePlan(id uint) error // FlattenPlanTasks 递归展开计划,返回按执行顺序排列的所有任务列表 @@ -643,3 +645,15 @@ func (r *gormPlanRepository) FindPlansWithPendingTasks() ([]*models.Plan, error) return plans, err } + +// UpdatePlanStatus 更新指定计划的状态 +func (r *gormPlanRepository) UpdatePlanStatus(id uint, status models.PlanStatus) error { + result := r.db.Model(&models.Plan{}).Where("id = ?", id).Update("status", status) + if result.Error != nil { + return result.Error + } + if result.RowsAffected == 0 { + return gorm.ErrRecordNotFound + } + return nil +}