任务调度器关于任务执行部分实现(没测没检查, 但应该实现完了)
This commit is contained in:
@@ -12,6 +12,7 @@ type ExecutionLogRepository interface {
|
||||
UpdatePlanExecutionLog(log *models.PlanExecutionLog) error
|
||||
CreateTaskExecutionLogsInBatch(logs []*models.TaskExecutionLog) error
|
||||
UpdateTaskExecutionLog(log *models.TaskExecutionLog) error
|
||||
FindTaskExecutionLogByID(id uint) (*models.TaskExecutionLog, error)
|
||||
}
|
||||
|
||||
// executionLogRepository 是使用 GORM 的具体实现。
|
||||
@@ -30,11 +31,11 @@ func (r *executionLogRepository) CreatePlanExecutionLog(log *models.PlanExecutio
|
||||
return r.db.Create(log).Error
|
||||
}
|
||||
|
||||
// UpdatePlanExecutionLog 使用 Save 方法全量更新一个计划执行日志。
|
||||
// GORM 的 Save 会自动根据主键是否存在来决定是执行 UPDATE 还是 INSERT。
|
||||
// UpdatePlanExecutionLog 使用 Updates 方法更新一个计划执行日志。
|
||||
// GORM 的 Updates 传入 struct 时,只会更新非零值字段。
|
||||
// 在这里,我们期望传入的对象一定包含一个有效的 ID。
|
||||
func (r *executionLogRepository) UpdatePlanExecutionLog(log *models.PlanExecutionLog) error {
|
||||
return r.db.Save(log).Error
|
||||
return r.db.Updates(log).Error
|
||||
}
|
||||
|
||||
// CreateTaskExecutionLogsInBatch 在一次数据库调用中创建多个任务执行日志条目。
|
||||
@@ -44,8 +45,21 @@ func (r *executionLogRepository) CreateTaskExecutionLogsInBatch(logs []*models.T
|
||||
return r.db.Create(&logs).Error
|
||||
}
|
||||
|
||||
// UpdateTaskExecutionLog 使用 Save 方法全量更新一个任务执行日志。
|
||||
// UpdateTaskExecutionLog 使用 Updates 方法更新一个任务执行日志。
|
||||
// GORM 的 Updates 传入 struct 时,只会更新非零值字段。
|
||||
// 这种方式代码更直观,上层服务可以直接修改模型对象后进行保存。
|
||||
func (r *executionLogRepository) UpdateTaskExecutionLog(log *models.TaskExecutionLog) error {
|
||||
return r.db.Save(log).Error
|
||||
return r.db.Updates(log).Error
|
||||
}
|
||||
|
||||
// FindTaskExecutionLogByID 根据 ID 查找单个任务执行日志。
|
||||
// 它会预加载关联的 Task 信息。
|
||||
func (r *executionLogRepository) FindTaskExecutionLogByID(id uint) (*models.TaskExecutionLog, error) {
|
||||
var log models.TaskExecutionLog
|
||||
// 使用 Preload("Task") 来确保关联的任务信息被一并加载
|
||||
err := r.db.Preload("Task").First(&log, id).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &log, nil
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@ type PlanRepository interface {
|
||||
UpdatePlan(plan *models.Plan) error
|
||||
// DeletePlan 根据ID删除计划,同时删除其关联的任务(非子任务)或子计划关联
|
||||
DeletePlan(id uint) error
|
||||
// FlattenPlanTasks 递归展开计划,返回按执行顺序排列的所有任务列表
|
||||
FlattenPlanTasks(planID uint) ([]models.Task, error)
|
||||
// DeleteTask 根据ID删除任务
|
||||
DeleteTask(id int) error
|
||||
// FindPlanAnalysisTaskByParamsPlanID 根据Parameters中的ParamsPlanID字段值查找TaskPlanAnalysis类型的Task
|
||||
FindPlanAnalysisTaskByParamsPlanID(paramsPlanID uint) (*models.Task, error)
|
||||
}
|
||||
|
||||
// gormPlanRepository 是 PlanRepository 的 GORM 实现
|
||||
@@ -288,7 +294,7 @@ func (r *gormPlanRepository) reconcileTasks(tx *gorm.DB, plan *models.Plan) erro
|
||||
return err
|
||||
}
|
||||
|
||||
existingTaskMap := make(map[uint]bool)
|
||||
existingTaskMap := make(map[int]bool)
|
||||
for _, task := range existingTasks {
|
||||
existingTaskMap[task.ID] = true
|
||||
}
|
||||
@@ -308,7 +314,7 @@ func (r *gormPlanRepository) reconcileTasks(tx *gorm.DB, plan *models.Plan) erro
|
||||
}
|
||||
}
|
||||
|
||||
var tasksToDelete []uint
|
||||
var tasksToDelete []int
|
||||
for id := range existingTaskMap {
|
||||
tasksToDelete = append(tasksToDelete, id)
|
||||
}
|
||||
@@ -401,3 +407,116 @@ func (r *gormPlanRepository) DeletePlan(id uint) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// FlattenPlanTasks 递归展开计划,返回按执行顺序排列的所有任务列表
|
||||
func (r *gormPlanRepository) FlattenPlanTasks(planID uint) ([]models.Task, error) {
|
||||
plan, err := r.GetPlanByID(planID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取计划(ID: %d)失败: %w", planID, err)
|
||||
}
|
||||
|
||||
return r.flattenPlanTasksRecursive(plan)
|
||||
}
|
||||
|
||||
// flattenPlanTasksRecursive 递归展开计划的内部实现
|
||||
func (r *gormPlanRepository) flattenPlanTasksRecursive(plan *models.Plan) ([]models.Task, error) {
|
||||
var tasks []models.Task
|
||||
|
||||
switch plan.ContentType {
|
||||
case models.PlanContentTypeTasks:
|
||||
// 如果计划直接包含任务,直接返回这些任务
|
||||
// 由于GetPlanByID已经预加载并排序了任务,这里直接使用即可
|
||||
tasks = append(tasks, plan.Tasks...)
|
||||
|
||||
case models.PlanContentTypeSubPlans:
|
||||
// 如果计划包含子计划,则递归处理每个子计划
|
||||
for _, subPlan := range plan.SubPlans {
|
||||
// 获取子计划的任务列表
|
||||
var subTasks []models.Task
|
||||
var err error
|
||||
|
||||
// 确保子计划已经被加载
|
||||
if subPlan.ChildPlan != nil {
|
||||
subTasks, err = r.flattenPlanTasksRecursive(subPlan.ChildPlan)
|
||||
} else {
|
||||
// 如果子计划未加载,则从数据库获取并递归展开
|
||||
subTasks, err = r.FlattenPlanTasks(subPlan.ChildPlanID)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("展开子计划(ID: %d)失败: %w", subPlan.ChildPlanID, err)
|
||||
}
|
||||
|
||||
// 将子计划的任务添加到结果中
|
||||
tasks = append(tasks, subTasks...)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("未知的计划内容类型: %v", plan.ContentType)
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// DeleteTask 根据ID删除任务
|
||||
func (r *gormPlanRepository) DeleteTask(id int) error {
|
||||
// 使用事务确保操作的原子性
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// 1. 检查是否有待执行任务引用了这个任务
|
||||
var pendingTaskCount int64
|
||||
if err := tx.Model(&models.PendingTask{}).Where("task_id = ?", id).Count(&pendingTaskCount).Error; err != nil {
|
||||
return fmt.Errorf("检查待执行任务时出错: %w", err)
|
||||
}
|
||||
|
||||
// 如果有待执行任务引用该任务,不能删除
|
||||
if pendingTaskCount > 0 {
|
||||
return fmt.Errorf("无法删除任务(ID: %d),因为存在 %d 条待执行任务引用该任务", id, pendingTaskCount)
|
||||
}
|
||||
|
||||
// 2. 检查是否有计划仍在使用这个任务
|
||||
var planCount int64
|
||||
if err := tx.Model(&models.Plan{}).Joins("JOIN tasks ON plans.id = tasks.plan_id").Where("tasks.id = ?", id).Count(&planCount).Error; err != nil {
|
||||
return fmt.Errorf("检查计划引用任务时出错: %w", err)
|
||||
}
|
||||
|
||||
// 如果有计划在使用该任务,不能删除
|
||||
if planCount > 0 {
|
||||
return fmt.Errorf("无法删除任务(ID: %d),因为存在 %d 个计划仍在使用该任务", id, planCount)
|
||||
}
|
||||
|
||||
// 3. 执行删除操作
|
||||
result := tx.Delete(&models.Task{}, id)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("删除任务失败: %w", result.Error)
|
||||
}
|
||||
|
||||
// 检查是否实际删除了记录
|
||||
if result.RowsAffected == 0 {
|
||||
return gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// FindPlanAnalysisTaskByParamsPlanID 根据Parameters中的ParamsPlanID字段值查找TaskPlanAnalysis类型的Task
|
||||
func (r *gormPlanRepository) FindPlanAnalysisTaskByParamsPlanID(paramsPlanID uint) (*models.Task, error) {
|
||||
var task models.Task
|
||||
|
||||
// 构造JSON查询条件,查找Parameters中包含指定ParamsPlanID且Type为TaskPlanAnalysis的任务
|
||||
// TODO 在JSON字段中查找特定键值的语法取决于数据库类型,这里使用PostgreSQL的语法
|
||||
// TODO 如果使用的是MySQL,则需要相应调整查询条件
|
||||
result := r.db.Where(
|
||||
"type = ? AND parameters->>'plan_id' = ?",
|
||||
models.TaskPlanAnalysis,
|
||||
fmt.Sprintf("%d", paramsPlanID),
|
||||
).First(&task)
|
||||
|
||||
if result.Error != nil {
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, fmt.Errorf("未找到Parameters.PlanID为%d的TaskPlanAnalysis类型任务", paramsPlanID)
|
||||
}
|
||||
return nil, fmt.Errorf("查找任务时出错: %w", result.Error)
|
||||
}
|
||||
|
||||
return &task, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user