From 8d7d9fc48538585a296697c3b74fd2b70aef0981 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Sun, 2 Nov 2025 19:33:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0plan=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan_service_refactor.md | 94 +++++++++++-------- internal/domain/plan/plan_service.go | 74 +++++++++++++++ 2 files changed, 130 insertions(+), 38 deletions(-) create mode 100644 internal/domain/plan/plan_service.go diff --git a/design/verification-before-device-deletion/plan_service_refactor.md b/design/verification-before-device-deletion/plan_service_refactor.md index 8a7c60a..6db8f02 100644 --- a/design/verification-before-device-deletion/plan_service_refactor.md +++ b/design/verification-before-device-deletion/plan_service_refactor.md @@ -2,63 +2,81 @@ ## 1. 目标 -将 `internal/domain/scheduler` 包重构为 `internal/domain/plan`,并创建一个新的 `PlanService` 对象,将原 `scheduler` 包中的核心调度逻辑集成到 `PlanService` 中作为一个子服务,统一由 `PlanService` 对外提供服务。此重构旨在提高代码的模块化、可维护性和可测试性,并为后续的“设备删除前校验”功能奠定基础。 +将 `internal/domain/scheduler` 包重构为 `internal/domain/plan`,并创建一个新的 `Service` 对象,将原 `scheduler` +包中的核心调度逻辑集成到 `Service` 中作为一个子服务,统一由 `Service` +对外提供服务。此重构旨在提高代码的模块化、可维护性和可测试性,并为后续的“设备删除前校验”功能奠定基础。 ## 2. 方案详情 ### 2.1. 包重命名 -* 将 `internal/domain/scheduler` 目录重命名为 `internal/domain/plan`。 -* 修改 `internal/domain/plan` 目录下所有 Go 文件中的 `package scheduler` 为 `package plan`。 -* 更新 `internal/domain/plan` 目录下所有 Go 文件中所有引用 `git.huangwc.com/pig/pig-farm-controller/internal/domain/scheduler` 的导入路径为 `git.huangwc.com/pig/pig-farm-controller/internal/domain/plan`。 +* 将 `internal/domain/scheduler` 目录重命名为 `internal/domain/plan`。 +* 修改 `internal/domain/plan` 目录下所有 Go 文件中的 `package scheduler` 为 `package plan`。 +* 更新 `internal/domain/plan` 目录下所有 Go 文件中所有引用 + `git.huangwc.com/pig/pig-farm-controller/internal/domain/scheduler` 的导入路径为 + `git.huangwc.com/pig/pig-farm-controller/internal/domain/plan`。 ### 2.2. `internal/domain/plan` 包内部结构调整 -* **`internal/domain/plan/task.go`**: - * 保持不变。它定义了任务的接口和工厂,是领域内的核心抽象。 +* **`internal/domain/plan/task.go`**: + * 保持不变。它定义了任务的接口和工厂,是领域内的核心抽象。 -* **`internal/domain/plan/scheduler.go`**: - * 将 `Scheduler` 结构体更名为 `PlanExecutionManagerImpl`。这个名称更准确地反映了它作为计划任务执行的协调者和管理者的具体实现。 - * 将 `NewScheduler` 构造函数更名为 `NewPlanExecutionManagerImpl`。 - * 文件内部所有对 `Scheduler` 的引用都将更新为 `PlanExecutionManagerImpl`。 +* **`internal/domain/plan/plan_execution_manager.go`**: + * 将 `Scheduler` 结构体更名为 `ExecutionManagerImpl`。这个名称更准确地反映了它作为计划任务执行的协调者和管理者的具体实现。 + * 将 `NewScheduler` 构造函数更名为 `NewExecutionManagerImpl`。 + * 文件内部所有对 `Scheduler` 的引用都将更新为 `ExecutionManagerImpl`。 -* **`internal/domain/plan/analysis_plan_task_manager.go`**: - * 将 `AnalysisPlanTaskManager` 结构体更名为 `AnalysisPlanTaskManagerImpl`。 - * 将 `NewAnalysisPlanTaskManager` 构造函数更名为 `NewAnalysisPlanTaskManagerImpl`。 - * 文件内部所有对 `AnalysisPlanTaskManager` 的引用都将更新为 `AnalysisPlanTaskManagerImpl`。 +* **`internal/domain/plan/analysis_plan_task_manager.go`**: + * 将 `AnalysisPlanTaskManager` 结构体更名为 `AnalysisPlanTaskManagerImpl`。 + * 将 `NewAnalysisPlanTaskManager` 构造函数更名为 `NewAnalysisPlanTaskManagerImpl`。 + * 文件内部所有对 `AnalysisPlanTaskManager` 的引用都将更新为 `AnalysisPlanTaskManagerImpl`。 -* **定义接口**: - * 在 `internal/domain/plan` 包中定义 `PlanExecutionManager` 接口,包含 `PlanExecutionManagerImpl` 对外暴露的所有公共方法。 - * 在 `internal/domain/plan` 包中定义 `AnalysisPlanTaskManager` 接口,包含 `AnalysisPlanTaskManagerImpl` 对外暴露的所有公共方法。 - * `PlanExecutionManagerImpl` 和 `AnalysisPlanTaskManagerImpl` 将分别实现对应的接口。 +* **定义领域层接口**: + * 在 `internal/domain/plan` 包中定义 `ExecutionManager` 接口,包含 `ExecutionManagerImpl` 对外暴露的所有公共方法。 + * 在 `internal/domain/plan` 包中定义 `AnalysisPlanTaskManager` 接口,包含 `AnalysisPlanTaskManagerImpl` 对外暴露的所有公共方法。 + * `ExecutionManagerImpl` 和 `AnalysisPlanTaskManagerImpl` 将分别实现对应的接口。 -### 2.3. 创建 `internal/app/service/plan_service.go` +### 2.3. 创建 `internal/domain/plan/plan_service.go` -* 创建新文件 `internal/app/service/plan_service.go`。 -* 该文件将定义 `PlanService` 结构体,并包含 `plan.PlanExecutionManager` 接口和 `plan.AnalysisPlanTaskManager` 接口的实例作为其依赖。 -* 实现 `NewPlanService` 构造函数,负责接收 `plan.PlanExecutionManager` 接口和 `plan.AnalysisPlanTaskManager` 接口的实例,并将其注入到 `PlanService` 中。 -* `PlanService` 将对外提供高层次的 API,这些 API 会协调调用其依赖的接口方法。例如: - * `PlanService.Start()` 方法会调用 `PlanExecutionManager` 接口的 `Start()` 方法。 - * `PlanService.Stop()` 方法会调用 `PlanExecutionManager` 接口的 `Stop()` 方法。 - * `PlanService.RefreshPlanTriggers()` 方法会调用 `AnalysisPlanTaskManager` 接口的 `Refresh()` 方法。 - * 未来所有与计划相关的应用层操作,都将通过 `PlanService` 进行。 +* 创建新文件 `internal/domain/plan/plan_service.go`。 + +* **定义领域服务接口**: + * 在 `internal/domain/plan` 包中定义 `Service` 接口,该接口将聚合 `ExecutionManager` 和 `AnalysisPlanTaskManager` + 的所有公共方法,并由 `planServiceImpl` 实现这些方法的委托。 + +* **实现领域服务**: + * 该文件将定义 `planServiceImpl` 结构体,并包含 `ExecutionManager` 接口和 `AnalysisPlanTaskManager` 接口的实例作为其依赖。 + * 实现 `NewService` 构造函数,负责接收 `ExecutionManager` 接口和 `AnalysisPlanTaskManager` 接口的实例,并将其注入到 + `planServiceImpl` 中。 + * `planServiceImpl` 将对外提供高层次的 API,这些 API 会协调调用其依赖的接口方法。例如: + * `Service.Start()` 方法会调用 `ExecutionManager` 接口的 `Start()` 方法。 + * `Service.Stop()` 方法会调用 `ExecutionManager` 接口的 `Stop()` 方法。 + * `Service.RefreshPlanTriggers()` 方法会调用 `AnalysisPlanTaskManager` 接口的 `Refresh()` 方法。 + * `Service.CreateOrUpdateTrigger()` 方法会调用 `AnalysisPlanTaskManager` 接口的 `CreateOrUpdateTrigger()` 方法。 + * `Service.EnsureAnalysisTaskDefinition()` 方法会调用 `AnalysisPlanTaskManager` 接口的 + `EnsureAnalysisTaskDefinition()` 方法。 + * 未来所有与计划相关的领域操作,都将通过 `Service` 接口进行。 ### 2.4. 调整依赖注入和引用 -* **查找并替换导入路径:** 使用 `grep` 命令查找整个项目中所有引用 `git.huangwc.com/pig/pig-farm-controller/internal/domain/scheduler` 的地方,并将其替换为 `git.huangwc.com/pig/pig-farm-controller/internal/domain/plan`。 -* **更新 `internal/core/component_initializers.go`**: - * 在初始化阶段,我们将创建 `plan.PlanExecutionManagerImpl` 和 `plan.AnalysisPlanTaskManagerImpl` 的具体实例。 - * 然后,将这些具体实例作为 `plan.PlanExecutionManager` 接口和 `plan.AnalysisPlanTaskManager` 接口类型传递给 `service.NewPlanService` 构造函数,创建 `PlanService` 实例。 - * 应用程序的其他部分将通过 `PlanService` 来访问计划相关的逻辑,而不是直接访问底层的管理器或其具体实现。 +* **查找并替换导入路径:** 使用 `grep` 命令查找整个项目中所有引用 + `git.huangwc.com/pig/pig-farm-controller/internal/domain/scheduler` 的地方,并将其替换为 + `git.huangwc.com/pig/pig-farm-controller/internal/domain/plan`。 +* **更新 `internal/core/component_initializers.go`**: + * 在初始化阶段,我们将创建 `plan.ExecutionManagerImpl` 和 `plan.AnalysisPlanTaskManagerImpl` 的具体实例。 + * 然后,将这些具体实例作为 `plan.ExecutionManager` 接口和 `plan.AnalysisPlanTaskManager` 接口类型传递给 + `plan.NewService` 构造函数,创建 `planServiceImpl` 实例。 + * 最终,`plan.NewService` 返回 `plan.Service` 接口类型。 + * 应用程序的其他部分将通过 `plan.Service` 接口来访问计划相关的逻辑,而不是直接访问底层的管理器或其具体实现。 ## 3. 优势 -* **职责分离清晰:** `internal/domain/plan` 包专注于计划领域的核心逻辑和管理,而 `internal/app/service` 包则负责应用层的业务编排和对外接口。 -* **符合领域驱动设计:** 领域层包含核心业务逻辑和管理器,应用层作为领域层的协调者。 -* **与现有项目风格一致:** 借鉴 `domain/pig` 包的模式,提高了项目内部的一致性。 -* **可测试性增强:** `PlanService` 可以更容易地进行单元测试,因为其依赖的接口可以被模拟。 -* **可维护性提高:** 当计划相关的业务逻辑发生变化时,可以更精确地定位到需要修改的组件。 -* **松耦合:** `PlanService` 不依赖于具体的实现,而是依赖于接口,提高了系统的灵活性和可扩展性。 +* **职责分离清晰:** `internal/domain/plan` 包专注于计划领域的核心逻辑和管理,并提供统一的 `Service` 接口作为领域服务的入口。 +* **符合领域驱动设计:** 领域层包含核心业务逻辑和管理器,应用层(如果需要)作为领域层的协调者。 +* **与现有项目风格一致:** 借鉴 `domain/pig` 包的模式,提高了项目内部的一致性。 +* **可测试性增强:** `Service` 可以更容易地进行单元测试,因为其依赖的接口可以被模拟。 +* **可维护性提高:** 当计划相关的业务逻辑发生变化时,可以更精确地定位到需要修改的组件。 +* **松耦合:** `Service` 不依赖于具体的实现,而是依赖于接口,提高了系统的灵活性和可扩展性。 ## 4. 验证和测试 diff --git a/internal/domain/plan/plan_service.go b/internal/domain/plan/plan_service.go new file mode 100644 index 0000000..24c9447 --- /dev/null +++ b/internal/domain/plan/plan_service.go @@ -0,0 +1,74 @@ +package plan + +import ( + "git.huangwc.com/pig/pig-farm-controller/internal/infra/logs" +) + +// Service 定义了计划领域服务的接口。 +type Service interface { + // Start 启动计划相关的后台服务,例如计划执行管理器。 + Start() + // Stop 停止计划相关的后台服务,例如计划执行管理器。 + Stop() + // RefreshPlanTriggers 刷新计划触发器,同步数据库中的计划状态和待执行队列中的触发器任务。 + RefreshPlanTriggers() error + // CreateOrUpdateTrigger 为给定的 planID 创建其关联的触发任务。 + // 如果触发器已存在,会根据计划类型更新其执行时间。 + CreateOrUpdateTrigger(planID uint) error + // EnsureAnalysisTaskDefinition 确保计划的分析任务定义存在于 tasks 表中。 + // 如果不存在,则会自动创建。此方法不涉及待执行队列。 + EnsureAnalysisTaskDefinition(planID uint) error + // TODO: 在这里添加其他与计划相关的领域服务方法 +} + +// planServiceImpl 是 Service 接口的具体实现。 +type planServiceImpl struct { + executionManager ExecutionManager + taskManager AnalysisPlanTaskManager + logger *logs.Logger +} + +// NewPlanService 创建一个新的 Service 实例。 +func NewPlanService( + executionManager ExecutionManager, + taskManager AnalysisPlanTaskManager, + logger *logs.Logger, +) Service { + return &planServiceImpl{ + executionManager: executionManager, + taskManager: taskManager, + logger: logger, + } +} + +// Start 启动计划相关的后台服务。 +func (s *planServiceImpl) Start() { + s.logger.Infof("PlanService 正在启动...") + s.executionManager.Start() +} + +// Stop 停止计划相关的后台服务。 +func (s *planServiceImpl) Stop() { + s.logger.Infof("PlanService 正在停止...") + s.executionManager.Stop() +} + +// RefreshPlanTriggers 刷新计划触发器。 +func (s *planServiceImpl) RefreshPlanTriggers() error { + s.logger.Infof("PlanService 正在刷新计划触发器...") + return s.taskManager.Refresh() +} + +// CreateOrUpdateTrigger 为给定的 planID 创建其关联的触发任务。 +// 如果触发器已存在,会根据计划类型更新其执行时间。 +func (s *planServiceImpl) CreateOrUpdateTrigger(planID uint) error { + s.logger.Infof("PlanService 正在为计划 %d 创建或更新触发器...", planID) + return s.taskManager.CreateOrUpdateTrigger(planID) +} + +// EnsureAnalysisTaskDefinition 确保计划的分析任务定义存在于 tasks 表中。 +// 如果不存在,则会自动创建。此方法不涉及待执行队列。 +func (s *planServiceImpl) EnsureAnalysisTaskDefinition(planID uint) error { + s.logger.Infof("PlanService 正在确保计划 %d 的分析任务定义...", planID) + return s.taskManager.EnsureAnalysisTaskDefinition(planID) +}