From 687c2f12ee8889b2da4c1bf6c73f2aba9930ea6d Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Sun, 2 Nov 2025 20:47:25 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A9=E4=BB=BB=E5=8A=A1=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E6=8F=90=E4=BE=9B=E8=87=AA=E8=BA=AB=E4=BD=BF=E7=94=A8=E8=AE=BE?= =?UTF-8?q?=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../add_get_device_id_configs_to_task.md | 34 ++++++++ .../index.md | 18 +++++ internal/domain/plan/task.go | 9 +++ internal/domain/task/delay_task.go | 4 + internal/domain/task/full_collection_task.go | 9 ++- .../domain/task/release_feed_weight_task.go | 78 +++++++++++-------- 6 files changed, 119 insertions(+), 33 deletions(-) create mode 100644 design/verification-before-device-deletion/add_get_device_id_configs_to_task.md diff --git a/design/verification-before-device-deletion/add_get_device_id_configs_to_task.md b/design/verification-before-device-deletion/add_get_device_id_configs_to_task.md new file mode 100644 index 0000000..db541fb --- /dev/null +++ b/design/verification-before-device-deletion/add_get_device_id_configs_to_task.md @@ -0,0 +1,34 @@ +# 任务接口增加获取关联设备ID方法设计 + +## 1. 需求 + +为了在设备删除前进行验证,需要为任务接口增加一个方法,该方法能够直接返回指定任务配置中所有关联的设备ID列表。所有实现 `task` 接口的对象都必须实现此方法。 + +## 2. 新接口定义:`TaskDeviceIDResolver` + +```go +// TaskDeviceIDResolver 定义了从任务配置中解析设备ID的方法 +type TaskDeviceIDResolver interface { + // ResolveDeviceIDs 从任务配置中解析并返回所有关联的设备ID列表 + // 返回值: uint数组,每个字符串代表一个设备ID + ResolveDeviceIDs() ([]uint, error) +} +``` + +## 3. `task` 接口更新 + +`task` 接口将嵌入 `TaskDeviceIDResolver` 接口。 + +```go +// Task 接口(示例,具体结构可能不同) +type Task interface { + // ... 其他现有方法 ... + + // 嵌入 TaskDeviceIDResolver 接口 + TaskDeviceIDResolver +} +``` + +## 4. 实现要求 + +所有当前及未来实现 `Task` 接口的类型,都必须实现 `TaskDeviceIDResolver` 接口中定义的所有方法,即 `ResolveDeviceIDs` 方法。 diff --git a/design/verification-before-device-deletion/index.md b/design/verification-before-device-deletion/index.md index e69de29..4501a4b 100644 --- a/design/verification-before-device-deletion/index.md +++ b/design/verification-before-device-deletion/index.md @@ -0,0 +1,18 @@ +# 需求 + +删除设备/设备模板/区域主控前进行校验 + +## issue + +http://git.huangwc.com/pig/pig-farm-controller/issues/50 + +## 需求描述 + +1. 删除设备时检测是否被任务使用 +2. 删除设备模板时检测是否被设备使用 +3. 删除区域主控时检测是否被设备使用 + +# 实现 + +1. [重构计划领域](./plan_service_refactor.md) +2. [让任务可以提供自身使用设备](./add_get_device_id_configs_to_task.md) \ No newline at end of file diff --git a/internal/domain/plan/task.go b/internal/domain/plan/task.go index 3c505e7..124a1a5 100644 --- a/internal/domain/plan/task.go +++ b/internal/domain/plan/task.go @@ -14,6 +14,15 @@ type Task interface { // log: 任务执行的上下文。 // executeErr: 从 Execute 方法返回的原始错误。 OnFailure(executeErr error) + + TaskDeviceIDResolver +} + +// TaskDeviceIDResolver 定义了从任务配置中解析设备ID的方法 +type TaskDeviceIDResolver interface { + // ResolveDeviceIDs 从任务配置中解析并返回所有关联的设备ID列表 + // 返回值: uint数组,每个字符串代表一个设备ID + ResolveDeviceIDs() ([]uint, error) } // TaskFactory 是一个工厂接口,用于根据任务执行日志创建任务实例。 diff --git a/internal/domain/task/delay_task.go b/internal/domain/task/delay_task.go index 9abc888..3281667 100644 --- a/internal/domain/task/delay_task.go +++ b/internal/domain/task/delay_task.go @@ -65,3 +65,7 @@ func (d *DelayTask) parseParameters() error { func (d *DelayTask) OnFailure(executeErr error) { d.logger.Errorf("任务 %v: 执行失败: %v", d.executionTask.TaskID, executeErr) } + +func (d *DelayTask) ResolveDeviceIDs() ([]uint, error) { + return []uint{}, nil +} diff --git a/internal/domain/task/full_collection_task.go b/internal/domain/task/full_collection_task.go index fc7d7f9..768f91e 100644 --- a/internal/domain/task/full_collection_task.go +++ b/internal/domain/task/full_collection_task.go @@ -4,6 +4,7 @@ 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" "git.huangwc.com/pig/pig-farm-controller/internal/infra/models" "git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" @@ -23,7 +24,7 @@ func NewFullCollectionTask( deviceRepo repository.DeviceRepository, deviceService device.Service, logger *logs.Logger, -) *FullCollectionTask { +) plan.Task { return &FullCollectionTask{ log: log, deviceRepo: deviceRepo, @@ -91,3 +92,9 @@ func (t *FullCollectionTask) OnFailure(executeErr error) { "error", executeErr, ) } + +// ResolveDeviceIDs 获取当前任务需要使用的设备ID列表 +func (t *FullCollectionTask) ResolveDeviceIDs() ([]uint, error) { + // 全量采集任务不和任何设备绑定, 每轮采集都会重新获取全量传感器 + return []uint{}, nil +} diff --git a/internal/domain/task/release_feed_weight_task.go b/internal/domain/task/release_feed_weight_task.go index 38d8d2c..5c65117 100644 --- a/internal/domain/task/release_feed_weight_task.go +++ b/internal/domain/task/release_feed_weight_task.go @@ -3,6 +3,7 @@ package task import ( "encoding/json" "fmt" + "sync" "time" "git.huangwc.com/pig/pig-farm-controller/internal/domain/device" @@ -31,6 +32,9 @@ type ReleaseFeedWeightTask struct { feedPort device.Service + // onceParse 保证解析参数只执行一次 + onceParse sync.Once + logger *logs.Logger } @@ -117,45 +121,48 @@ func (r *ReleaseFeedWeightTask) getNowWeight() (float64, error) { } func (r *ReleaseFeedWeightTask) parseParameters() error { - if r.claimedLog.Task.Parameters == nil { - r.logger.Errorf("任务 %v: 缺少参数", r.claimedLog.TaskID) - return fmt.Errorf("任务 %v: 参数不全", r.claimedLog.TaskID) - } + var err error + r.onceParse.Do(func() { + if r.claimedLog.Task.Parameters == nil { + r.logger.Errorf("任务 %v: 缺少参数", r.claimedLog.TaskID) + err = fmt.Errorf("任务 %v: 参数不全", r.claimedLog.TaskID) + } - var params ReleaseFeedWeightTaskParams - err := r.claimedLog.Task.ParseParameters(¶ms) - if err != nil { - r.logger.Errorf("任务 %v: 解析参数失败: %v", r.claimedLog.TaskID, err) - return fmt.Errorf("任务 %v: 解析参数失败: %v", r.claimedLog.TaskID, err) - } + var params ReleaseFeedWeightTaskParams + err := r.claimedLog.Task.ParseParameters(¶ms) + if err != nil { + r.logger.Errorf("任务 %v: 解析参数失败: %v", r.claimedLog.TaskID, err) + err = fmt.Errorf("任务 %v: 解析参数失败: %v", r.claimedLog.TaskID, err) + } - // 校验参数是否存在 - if params.ReleaseWeight == 0 { - r.logger.Errorf("任务 %v: 参数 release_weight 缺失或无效", r.claimedLog.TaskID) - return fmt.Errorf("任务 %v: 参数 release_weight 缺失或无效", r.claimedLog.TaskID) - } - if params.FeedPortDeviceID == 0 { - r.logger.Errorf("任务 %v: 参数 feed_port_device_id 缺失或无效", r.claimedLog.TaskID) - return fmt.Errorf("任务 %v: 参数 feed_port_device_id 缺失或无效", r.claimedLog.TaskID) - } - if params.MixingTankDeviceID == 0 { - r.logger.Errorf("任务 %v: 参数 mixing_tank_device_id 缺失或无效", r.claimedLog.TaskID) - return fmt.Errorf("任务 %v: 参数 mixing_tank_device_id 缺失或无效", r.claimedLog.TaskID) - } + // 校验参数是否存在 + if params.ReleaseWeight == 0 { + r.logger.Errorf("任务 %v: 参数 release_weight 缺失或无效", r.claimedLog.TaskID) + err = fmt.Errorf("任务 %v: 参数 release_weight 缺失或无效", r.claimedLog.TaskID) + } + if params.FeedPortDeviceID == 0 { + r.logger.Errorf("任务 %v: 参数 feed_port_device_id 缺失或无效", r.claimedLog.TaskID) + err = fmt.Errorf("任务 %v: 参数 feed_port_device_id 缺失或无效", r.claimedLog.TaskID) + } + if params.MixingTankDeviceID == 0 { + r.logger.Errorf("任务 %v: 参数 mixing_tank_device_id 缺失或无效", r.claimedLog.TaskID) + err = fmt.Errorf("任务 %v: 参数 mixing_tank_device_id 缺失或无效", r.claimedLog.TaskID) + } - r.releaseWeight = params.ReleaseWeight - r.mixingTankDeviceID = params.MixingTankDeviceID - r.feedPortDevice, err = r.deviceRepo.FindByID(params.FeedPortDeviceID) - if err != nil { - r.logger.Errorf("任务 %v: 获取设备信息失败: %v", r.claimedLog.TaskID, err) - return fmt.Errorf("任务 %v: 获取设备信息失败: %v", r.claimedLog.TaskID, err) - } + r.releaseWeight = params.ReleaseWeight + r.mixingTankDeviceID = params.MixingTankDeviceID + r.feedPortDevice, err = r.deviceRepo.FindByID(params.FeedPortDeviceID) + if err != nil { + r.logger.Errorf("任务 %v: 获取设备信息失败: %v", r.claimedLog.TaskID, err) + err = fmt.Errorf("任务 %v: 获取设备信息失败: %v", r.claimedLog.TaskID, err) + } - return nil + }) + return err } func (r *ReleaseFeedWeightTask) OnFailure(executeErr error) { - r.logger.Errorf("开始善后处理, 日志ID:%v", r.claimedLog.ID) + r.logger.Errorf("开始善后处理, 日志ID:%v; 错误信息: %v", r.claimedLog.ID, executeErr) if r.feedPort != nil { err := r.feedPort.Switch(r.feedPortDevice, device.DeviceActionStop) if err != nil { @@ -166,3 +173,10 @@ func (r *ReleaseFeedWeightTask) OnFailure(executeErr error) { } r.logger.Errorf("善后处理完成, 日志ID:%v", r.claimedLog.ID) } + +func (r *ReleaseFeedWeightTask) ResolveDeviceIDs() ([]uint, error) { + if err := r.parseParameters(); err != nil { + return nil, err + } + return []uint{r.feedPortDevice.ID, r.mixingTankDeviceID}, nil +}