From 026dad93742d1bbbcb95656dccbff81e97321cc1 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Sun, 2 Nov 2025 23:26:16 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9C=80=E6=B1=82=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../index.md | 3 +- .../plan_service_refactor_to_domain.md | 179 ++++++++++++++++++ internal/infra/repository/plan_repository.go | 8 - project_structure.txt | 3 + 4 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 design/verification-before-device-deletion/plan_service_refactor_to_domain.md diff --git a/design/verification-before-device-deletion/index.md b/design/verification-before-device-deletion/index.md index 4501a4b..f4ffa6c 100644 --- a/design/verification-before-device-deletion/index.md +++ b/design/verification-before-device-deletion/index.md @@ -15,4 +15,5 @@ http://git.huangwc.com/pig/pig-farm-controller/issues/50 # 实现 1. [重构计划领域](./plan_service_refactor.md) -2. [让任务可以提供自身使用设备](./add_get_device_id_configs_to_task.md) \ No newline at end of file +2. [让任务可以提供自身使用设备](./add_get_device_id_configs_to_task.md) +3. [现有计划管理逻辑迁移](./plan_service_refactor_to_domain.md) \ No newline at end of file diff --git a/design/verification-before-device-deletion/plan_service_refactor_to_domain.md b/design/verification-before-device-deletion/plan_service_refactor_to_domain.md new file mode 100644 index 0000000..70cd46a --- /dev/null +++ b/design/verification-before-device-deletion/plan_service_refactor_to_domain.md @@ -0,0 +1,179 @@ +# 重构方案:将 `app/service/plan_service.go` 的核心逻辑迁移到 `domain/plan/plan_service.go` + +## 目标: + +* `app/service/plan_service.go` (应用服务层): 仅负责接收 DTO、将 DTO 转换为领域实体、调用 `domain/plan/plan_service` 的领域方法,并将领域方法返回的领域实体转换为 DTO 返回。 +* `domain/plan/plan_service.go` (领域层): 封装所有与计划相关的业务逻辑、验证规则、状态管理以及对领域实体的查询操作。 + +## 详细步骤: + +### 第一步:修改 `domain/plan/plan_service.go` (领域层) + +1. **引入必要的依赖**: + * `repository.PlanRepository`:用于与计划数据存储交互。 + * `repository.DeviceRepository`:如果计划逻辑中需要设备信息。 + * `models.Plan`:领域实体。 + * `errors` 和 `gorm.ErrRecordNotFound`:用于错误处理。 + * `models.PlanTypeSystem`, `models.PlanStatusEnabled`, `models.PlanContentTypeSubPlans`, `models.PlanContentTypeTasks` 等常量。 + * `git.huangwc.com/pig/pig-farm-controller/internal/infra/models` + * `git.huangwc.com/pig/pig-farm-controller/internal/infra/repository` + * `errors` + * `gorm.io/gorm` + +2. **定义领域层错误**: 将 `app/service/plan_service.go` 中定义的错误(`ErrPlanNotFound`, `ErrPlanCannotBeModified` 等)迁移到 `domain/plan/plan_service.go`,并根据领域层的语义进行调整。 + + ```go + var ( + // ErrPlanNotFound 表示未找到计划 + ErrPlanNotFound = errors.New("计划不存在") + // ErrPlanCannotBeModified 表示计划不允许修改 + ErrPlanCannotBeModified = errors.New("系统计划不允许修改") + // ErrPlanCannotBeDeleted 表示计划不允许删除 + ErrPlanCannotBeDeleted = errors.New("系统计划不允许删除") + // ErrPlanCannotBeStarted 表示计划不允许手动启动 + ErrPlanCannotBeStarted = errors.New("系统计划不允许手动启动") + // ErrPlanAlreadyEnabled 表示计划已处于启动状态 + ErrPlanAlreadyEnabled = errors.New("计划已处于启动状态,无需重复操作") + // ErrPlanNotEnabled 表示计划未处于启动状态 + ErrPlanNotEnabled = errors.New("计划当前不是启用状态") + // ErrPlanCannotBeStopped 表示计划不允许停止 + ErrPlanCannotBeStopped = errors.New("系统计划不允许停止") + ) + ``` + +3. **扩展 `plan.Service` 接口**: + * 将 `app/service/plan_service.go` 中 `PlanService` 接口的所有方法(`CreatePlan`, `GetPlanByID`, `ListPlans`, `UpdatePlan`, `DeletePlan`, `StartPlan`, `StopPlan`)添加到 `domain/plan/Service` 接口中。 + * 这些方法的参数和返回值将直接使用领域实体(`*models.Plan`)或基本类型,而不是 DTO。例如: + * `CreatePlan(plan *models.Plan) (*models.Plan, error)` + * `GetPlanByID(id uint) (*models.Plan, error)` + * `ListPlans(opts repository.ListPlansOptions, page, pageSize int) ([]models.Plan, int64, error)` + * `UpdatePlan(plan *models.Plan) (*models.Plan, error)` + * `DeletePlan(id uint) error` + * `StartPlan(id uint) error` + * `StopPlan(id uint) error` + +4. **修改 `planServiceImpl` 结构体**: + * 添加 `planRepo repository.PlanRepository` 字段。 + * 添加 `deviceRepo repository.DeviceRepository` 字段 (如果需要)。 + * `analysisPlanTaskManager plan.AnalysisPlanTaskManager` 字段保持不变。 + + ```go + type planServiceImpl struct { + executionManager ExecutionManager + taskManager AnalysisPlanTaskManager + planRepo repository.PlanRepository // 新增 + // deviceRepo repository.DeviceRepository // 如果需要,新增 + logger *logs.Logger + } + ``` + +5. **实现 `plan.Service` 接口中的新方法**: + * 将 `app/service/plan_service.go` 中 `planService` 的所有业务逻辑方法(`CreatePlan`, `GetPlanByID`, `ListPlans`, `UpdatePlan`, `DeletePlan`, `StartPlan`, `StopPlan`)的实现,迁移到 `domain/plan/planServiceImpl` 中。 + * **关键修改点**: + * **参数和返回值**: 确保这些方法现在接收和返回的是 `*models.Plan` 或其他领域实体,而不是 DTO。 + * **业务逻辑**: 保留所有的业务规则、验证和状态管理逻辑。 + * **依赖**: 这些方法将直接调用 `planRepo` 和 `analysisPlanTaskManager`。 + * **日志**: 日志记录保持不变,但可能需要调整日志信息以反映领域层的上下文。 + * **错误处理**: 错误处理逻辑保持不变,但现在将返回领域层定义的错误。 + * **ContentType 自动判断**: `CreatePlan` 和 `UpdatePlan` 中的 `ContentType` 自动判断逻辑应该保留在领域层。 + * **执行计数器重置**: `UpdatePlan` 和 `StartPlan` 中的执行计数器重置逻辑应该保留在领域层。 + * **系统计划限制**: 对系统计划的修改、删除、启动、停止限制逻辑应该保留在领域层。 + * **验证和重排顺序**: `models.Plan` 的 `ValidateExecutionOrder()` 和 `ReorderSteps()` 方法的调用应该在 `CreatePlan` 和 `UpdatePlan` 方法的领域层实现中进行,而不是在 DTO 转换函数中。 + +6. **修改 `NewPlanService` 函数**: 接收 `repository.PlanRepository` 和 `repository.DeviceRepository` (如果需要) 作为参数,并注入到 `planServiceImpl` 中。 + + ```go + func NewPlanService( + executionManager ExecutionManager, + taskManager AnalysisPlanTaskManager, + planRepo repository.PlanRepository, // 新增 + // deviceRepo repository.DeviceRepository, // 如果需要,新增 + logger *logs.Logger, + ) Service { + return &planServiceImpl{ + executionManager: executionManager, + taskManager: taskManager, + planRepo: planRepo, // 注入 + // deviceRepo: deviceRepo, // 注入 + logger: logger, + } + } + ``` + +### 第二步:修改 `app/service/plan_service.go` (应用服务层) + +1. **修改 `planService` 结构体**: + * 移除 `planRepo repository.PlanRepository` 字段。 + * 将 `analysisPlanTaskManager plan.AnalysisPlanTaskManager` 字段替换为 `domainPlanService plan.Service`。 + + ```go + type planService struct { + logger *logs.Logger + // planRepo repository.PlanRepository // 移除 + domainPlanService plan.Service // 替换为领域层的服务接口 + // analysisPlanTaskManager plan.AnalysisPlanTaskManager // 移除,由 domainPlanService 内部持有 + } + ``` + +2. **修改 `NewPlanService` 函数**: + * 接收 `domainPlanService plan.Service` 作为参数。 + * 将 `planRepo` 和 `analysisPlanTaskManager` 的注入替换为 `domainPlanService`。 + + ```go + func NewPlanService( + logger *logs.Logger, + // planRepo repository.PlanRepository, // 移除 + domainPlanService plan.Service, // 接收领域层服务 + // analysisPlanTaskManager plan.AnalysisPlanTaskManager, // 移除 + ) PlanService { + return &planService{ + logger: logger, + domainPlanService: domainPlanService, // 注入领域层服务 + } + } + ``` + +3. **修改 `PlanService` 接口**: + * 接口定义保持不变,仍然接收和返回 DTO。 + +4. **修改 `planService` 接口实现**: + * **`CreatePlan`**: + * 接收 `dto.CreatePlanRequest`。 + * 使用 `dto.NewPlanFromCreateRequest` 将 DTO 转换为 `*models.Plan`。**注意:此时 `NewPlanFromCreateRequest` 不再包含 `ValidateExecutionOrder()` 和 `ReorderSteps()` 的调用。** + * 调用 `s.domainPlanService.CreatePlan(*models.Plan)`。 + * 将返回的 `*models.Plan` 转换为 `dto.PlanResponse`。 + * **`GetPlanByID`**: + * 调用 `s.domainPlanService.GetPlanByID(id)`。 + * 将返回的 `*models.Plan` 转换为 `dto.PlanResponse`。 + * **`ListPlans`**: + * 将 `dto.ListPlansQuery` 转换为 `repository.ListPlansOptions`。 + * 调用 `s.domainPlanService.ListPlans(...)`。 + * 将返回的 `[]models.Plan` 转换为 `[]dto.PlanResponse`。 + * **`UpdatePlan`**: + * 使用 `dto.NewPlanFromUpdateRequest` 将 `dto.UpdatePlanRequest` 转换为 `*models.Plan`。**注意:此时 `NewPlanFromUpdateRequest` 不再包含 `ValidateExecutionOrder()` 和 `ReorderSteps()` 的调用。** + * 设置 `plan.ID = id`。 + * 调用 `s.domainPlanService.UpdatePlan(*models.Plan)`。 + * 将返回的 `*models.Plan` 转换为 `dto.PlanResponse`。 + * **`DeletePlan`**: + * 调用 `s.domainPlanService.DeletePlan(id)`。 + * **`StartPlan`**: + * 调用 `s.domainPlanService.StartPlan(id)`。 + * **`StopPlan`**: + * 调用 `s.domainPlanService.StopPlan(id)`。 + * **错误处理**: 应用服务层将捕获领域层返回的错误,并可能将其转换为更适合应用服务层或表示层的错误信息(例如,将领域层的 `ErrPlanNotFound` 转换为 `app/service` 层定义的 `ErrPlanNotFound`)。 + +### 第三步:修改 `internal/app/dto/plan_converter.go` + +1. **移除 `NewPlanFromCreateRequest` 和 `NewPlanFromUpdateRequest` 中的领域逻辑**: + * 从 `NewPlanFromCreateRequest` 和 `NewPlanFromUpdateRequest` 函数中移除 `plan.ValidateExecutionOrder()` 和 `plan.ReorderSteps()` 的调用。这些逻辑应该由领域服务来处理。 + +### 第四步:更新 `main.go` 或其他依赖注入点 + +* 调整 `NewPlanService` 的调用,确保正确注入 `domain/plan/Service` 的实现。 + +## 风险与注意事项: + +* **事务管理**: 如果领域层的方法需要事务,确保事务在领域层内部或由应用服务层协调。 +* **错误映射**: 仔细处理领域层错误到应用服务层错误的映射,确保对外暴露的错误信息是恰当的。 +* **循环依赖**: 确保 `domain` 层不依赖 `app` 层,`app` 层可以依赖 `domain` 层。 +* **测试**: 重构后需要对所有相关功能进行全面的单元测试和集成测试。 \ No newline at end of file diff --git a/internal/infra/repository/plan_repository.go b/internal/infra/repository/plan_repository.go index 641898d..21e0648 100644 --- a/internal/infra/repository/plan_repository.go +++ b/internal/infra/repository/plan_repository.go @@ -77,9 +77,6 @@ type PlanRepository interface { // FindPlansWithPendingTasks 查找所有正在执行的计划 FindPlansWithPendingTasks() ([]*models.Plan, error) - // DB 返回底层的数据库连接实例,用于服务层事务 - DB() *gorm.DB - // StopPlanTransactionally 停止一个计划的执行,包括更新状态、移除待执行任务和更新执行日志 StopPlanTransactionally(planID uint) error @@ -715,11 +712,6 @@ func (r *gormPlanRepository) FindPlansWithPendingTasks() ([]*models.Plan, error) return plans, err } -// DB 返回底层的数据库连接实例 -func (r *gormPlanRepository) DB() *gorm.DB { - return r.db -} - // StopPlanTransactionally 停止一个计划的执行,包括更新状态、移除待执行任务和更新执行日志。 func (r *gormPlanRepository) StopPlanTransactionally(planID uint) error { return r.db.Transaction(func(tx *gorm.DB) error { diff --git a/project_structure.txt b/project_structure.txt index 8f0336c..ea77ee3 100644 --- a/project_structure.txt +++ b/project_structure.txt @@ -10,8 +10,10 @@ RELAY_API.md TODO-List.txt config.example.yml config.yml +design/verification-before-device-deletion/add_get_device_id_configs_to_task.md design/verification-before-device-deletion/index.md design/verification-before-device-deletion/plan_service_refactor.md +design/verification-before-device-deletion/plan_service_refactor_to_domain.md docs/docs.go docs/swagger.json docs/swagger.yaml @@ -72,6 +74,7 @@ internal/domain/pig/pig_sick_manager.go internal/domain/pig/pig_trade_manager.go internal/domain/plan/analysis_plan_task_manager.go internal/domain/plan/plan_execution_manager.go +internal/domain/plan/plan_service.go internal/domain/plan/task.go internal/domain/task/delay_task.go internal/domain/task/full_collection_task.go