From 4ddb2c5448966a55dff8b4da5395045c7cfab812 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Sun, 14 Sep 2025 15:18:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A1=BA=E5=BA=8F=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/infra/models/plan.go | 37 ++++++ internal/infra/models/plan_test.go | 183 ++++++++++++++++++++++------- 2 files changed, 178 insertions(+), 42 deletions(-) diff --git a/internal/infra/models/plan.go b/internal/infra/models/plan.go index dc4719d..32d26e4 100644 --- a/internal/infra/models/plan.go +++ b/internal/infra/models/plan.go @@ -2,6 +2,7 @@ package models import ( "fmt" + "sort" "gorm.io/datatypes" "gorm.io/gorm" @@ -79,6 +80,42 @@ func (p Plan) ValidateExecutionOrder() error { return nil } +// ReorderSteps 重新排序计划中的步骤(任务或子计划),使其 ExecutionOrder 从 1 开始且连续。 +// 这个方法假设重复的顺序已经被其他方法验证过,它只负责修复断层和不从1开始的序列。 +func (p *Plan) ReorderSteps() { + switch p.ContentType { + case PlanContentTypeTasks: + if len(p.Tasks) == 0 { + return + } + + // 1. 按当前的 ExecutionOrder 对任务进行排序 + sort.Slice(p.Tasks, func(i, j int) bool { + return p.Tasks[i].ExecutionOrder < p.Tasks[j].ExecutionOrder + }) + + // 2. 重新分配连续的 ExecutionOrder + for i := range p.Tasks { + p.Tasks[i].ExecutionOrder = i + 1 + } + + case PlanContentTypeSubPlans: + if len(p.SubPlans) == 0 { + return + } + + // 1. 按当前的 ExecutionOrder 对子计划进行排序 + sort.Slice(p.SubPlans, func(i, j int) bool { + return p.SubPlans[i].ExecutionOrder < p.SubPlans[j].ExecutionOrder + }) + + // 2. 重新分配连续的 ExecutionOrder + for i := range p.SubPlans { + p.SubPlans[i].ExecutionOrder = i + 1 + } + } +} + // SubPlan 代表作为另一个计划一部分的子计划,具有执行顺序 type SubPlan struct { gorm.Model diff --git a/internal/infra/models/plan_test.go b/internal/infra/models/plan_test.go index 9609e58..d34055b 100644 --- a/internal/infra/models/plan_test.go +++ b/internal/infra/models/plan_test.go @@ -1,22 +1,25 @@ package models_test import ( - "fmt" + "sort" "testing" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" "github.com/stretchr/testify/assert" ) -func TestPlan_ValidateExecutionOrder(t *testing.T) { - tests := []struct { - name string - plan models.Plan - expectedError string // 期望的错误信息,如果为nil则表示不期望错误 - }{ +func TestPlan_ReorderSteps(t *testing.T) { + type testCase struct { + name string + initialPlan *models.Plan + expectedOrders []int + } + + testCases := []testCase{ + // --- Test Cases for Tasks --- { - name: "任务类型-无重复执行顺序", - plan: models.Plan{ + name: "Tasks: 完美顺序", + initialPlan: &models.Plan{ ContentType: models.PlanContentTypeTasks, Tasks: []models.Task{ {ExecutionOrder: 1}, @@ -24,31 +27,78 @@ func TestPlan_ValidateExecutionOrder(t *testing.T) { {ExecutionOrder: 3}, }, }, - expectedError: "", + expectedOrders: []int{1, 2, 3}, }, { - name: "任务类型-重复执行顺序", - plan: models.Plan{ + name: "Tasks: 有间断", + initialPlan: &models.Plan{ ContentType: models.PlanContentTypeTasks, Tasks: []models.Task{ {ExecutionOrder: 1}, - {ExecutionOrder: 2}, - {ExecutionOrder: 1}, // 重复 + {ExecutionOrder: 3}, + {ExecutionOrder: 5}, }, }, - expectedError: fmt.Sprintf("任务执行顺序重复: %d", 1), + expectedOrders: []int{1, 2, 3}, }, { - name: "任务类型-空任务列表", - plan: models.Plan{ + name: "Tasks: 从0开始", + initialPlan: &models.Plan{ + ContentType: models.PlanContentTypeTasks, + Tasks: []models.Task{ + {ExecutionOrder: 0}, + {ExecutionOrder: 1}, + {ExecutionOrder: 2}, + }, + }, + expectedOrders: []int{1, 2, 3}, + }, + { + name: "Tasks: 完全无序", + initialPlan: &models.Plan{ + ContentType: models.PlanContentTypeTasks, + Tasks: []models.Task{ + {ExecutionOrder: 8}, + {ExecutionOrder: 2}, + {ExecutionOrder: 4}, + }, + }, + expectedOrders: []int{1, 2, 3}, + }, + { + name: "Tasks: 包含负数", + initialPlan: &models.Plan{ + ContentType: models.PlanContentTypeTasks, + Tasks: []models.Task{ + {ExecutionOrder: -5}, + {ExecutionOrder: 10}, + {ExecutionOrder: 2}, + }, + }, + expectedOrders: []int{1, 2, 3}, + }, + { + name: "Tasks: 空切片", + initialPlan: &models.Plan{ ContentType: models.PlanContentTypeTasks, Tasks: []models.Task{}, }, - expectedError: "", + expectedOrders: []int{}, }, { - name: "子计划类型-无重复执行顺序", - plan: models.Plan{ + name: "Tasks: 单个元素", + initialPlan: &models.Plan{ + ContentType: models.PlanContentTypeTasks, + Tasks: []models.Task{ + {ExecutionOrder: 100}, + }, + }, + expectedOrders: []int{1}, + }, + // --- Test Cases for SubPlans --- + { + name: "SubPlans: 完美顺序", + initialPlan: &models.Plan{ ContentType: models.PlanContentTypeSubPlans, SubPlans: []models.SubPlan{ {ExecutionOrder: 1}, @@ -56,48 +106,97 @@ func TestPlan_ValidateExecutionOrder(t *testing.T) { {ExecutionOrder: 3}, }, }, - expectedError: "", + expectedOrders: []int{1, 2, 3}, }, { - name: "子计划类型-重复执行顺序", - plan: models.Plan{ + name: "SubPlans: 有间断", + initialPlan: &models.Plan{ ContentType: models.PlanContentTypeSubPlans, SubPlans: []models.SubPlan{ {ExecutionOrder: 1}, - {ExecutionOrder: 2}, - {ExecutionOrder: 1}, // 重复 + {ExecutionOrder: 3}, + {ExecutionOrder: 5}, }, }, - expectedError: fmt.Sprintf("子计划执行顺序重复: %d", 1), + expectedOrders: []int{1, 2, 3}, }, { - name: "子计划类型-空子计划列表", - plan: models.Plan{ + name: "SubPlans: 从0开始", + initialPlan: &models.Plan{ + ContentType: models.PlanContentTypeSubPlans, + SubPlans: []models.SubPlan{ + {ExecutionOrder: 0}, + {ExecutionOrder: 1}, + {ExecutionOrder: 2}, + }, + }, + expectedOrders: []int{1, 2, 3}, + }, + { + name: "SubPlans: 完全无序", + initialPlan: &models.Plan{ + ContentType: models.PlanContentTypeSubPlans, + SubPlans: []models.SubPlan{ + {ExecutionOrder: 8}, + {ExecutionOrder: 2}, + {ExecutionOrder: 4}, + }, + }, + expectedOrders: []int{1, 2, 3}, + }, + { + name: "SubPlans: 包含负数", + initialPlan: &models.Plan{ + ContentType: models.PlanContentTypeSubPlans, + SubPlans: []models.SubPlan{ + {ExecutionOrder: -5}, + {ExecutionOrder: 10}, + {ExecutionOrder: 2}, + }, + }, + expectedOrders: []int{1, 2, 3}, + }, + { + name: "SubPlans: 空切片", + initialPlan: &models.Plan{ ContentType: models.PlanContentTypeSubPlans, SubPlans: []models.SubPlan{}, }, - expectedError: "", + expectedOrders: []int{}, }, { - name: "未知内容类型", - plan: models.Plan{ - ContentType: "UNKNOWN_TYPE", // 未知类型 - Tasks: []models.Task{{ExecutionOrder: 1}}, + name: "SubPlans: 单个元素", + initialPlan: &models.Plan{ + ContentType: models.PlanContentTypeSubPlans, + SubPlans: []models.SubPlan{ + {ExecutionOrder: 100}, + }, }, - expectedError: "", // 对于未知类型,ValidateExecutionOrder 不会返回错误,因为它只处理已知类型 + expectedOrders: []int{1}, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := tt.plan.ValidateExecutionOrder() + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // 调用被测试的方法 + tc.initialPlan.ReorderSteps() - if tt.expectedError != "" { - assert.Error(t, err) - assert.Contains(t, err.Error(), tt.expectedError) - } else { - assert.NoError(t, err) + // 提取并验证最终的顺序 + finalOrders := make([]int, 0) + if tc.initialPlan.ContentType == models.PlanContentTypeTasks { + for _, task := range tc.initialPlan.Tasks { + finalOrders = append(finalOrders, task.ExecutionOrder) + } + } else if tc.initialPlan.ContentType == models.PlanContentTypeSubPlans { + for _, subPlan := range tc.initialPlan.SubPlans { + finalOrders = append(finalOrders, subPlan.ExecutionOrder) + } } + + // 对 finalOrders 进行排序,以确保比较的一致性,因为 ReorderSteps 后的顺序是固定的 + sort.Ints(finalOrders) + + assert.Equal(t, tc.expectedOrders, finalOrders, "The final execution orders should be a continuous sequence starting from 1.") }) } }