package recipe import ( "context" "errors" "fmt" "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" "gorm.io/gorm" ) // 定义领域特定的错误 var ( ErrRecipeNotFound = fmt.Errorf("配方不存在") ErrRecipeNameConflict = fmt.Errorf("配方名称已存在") ) // RecipeCoreService 定义了配方领域的核心业务服务接口 type RecipeCoreService interface { CreateRecipe(ctx context.Context, recipe *models.Recipe) (*models.Recipe, error) GetRecipeByID(ctx context.Context, id uint32) (*models.Recipe, error) GetRecipeByName(ctx context.Context, name string) (*models.Recipe, error) UpdateRecipe(ctx context.Context, recipe *models.Recipe) (*models.Recipe, error) DeleteRecipe(ctx context.Context, id uint32) error ListRecipes(ctx context.Context, opts repository.RecipeListOptions, page, pageSize int) ([]models.Recipe, int64, error) UpdateRecipeIngredients(ctx context.Context, recipeID uint32, ingredients []models.RecipeIngredient) error } // recipeCoreServiceImpl 是 RecipeCoreService 的实现 type recipeCoreServiceImpl struct { ctx context.Context uow repository.UnitOfWork recipeRepo repository.RecipeRepository } // NewRecipeCoreService 创建一个新的 RecipeCoreService 实例 func NewRecipeCoreService(ctx context.Context, uow repository.UnitOfWork, recipeRepo repository.RecipeRepository) RecipeCoreService { return &recipeCoreServiceImpl{ ctx: ctx, uow: uow, recipeRepo: recipeRepo, } } // CreateRecipe 实现了创建配方的核心业务逻辑 func (s *recipeCoreServiceImpl) CreateRecipe(ctx context.Context, recipe *models.Recipe) (*models.Recipe, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateRecipe") // 检查名称是否已存在 existing, err := s.recipeRepo.GetRecipeByName(serviceCtx, recipe.Name) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("检查配方名称失败: %w", err) } if existing != nil { return nil, ErrRecipeNameConflict } if err := s.recipeRepo.CreateRecipe(serviceCtx, recipe); err != nil { return nil, fmt.Errorf("创建配方失败: %w", err) } return recipe, nil } // GetRecipeByID 实现了获取单个配方的逻辑 func (s *recipeCoreServiceImpl) GetRecipeByID(ctx context.Context, id uint32) (*models.Recipe, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetRecipeByID") recipe, err := s.recipeRepo.GetRecipeByID(serviceCtx, id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrRecipeNotFound } return nil, fmt.Errorf("获取配方失败: %w", err) } return recipe, nil } // GetRecipeByName 实现了根据名称获取单个配方的逻辑 func (s *recipeCoreServiceImpl) GetRecipeByName(ctx context.Context, name string) (*models.Recipe, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetRecipeByName") recipe, err := s.recipeRepo.GetRecipeByName(serviceCtx, name) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrRecipeNotFound } return nil, fmt.Errorf("获取配方失败: %w", err) } return recipe, nil } // UpdateRecipe 实现了更新配方的核心业务逻辑 func (s *recipeCoreServiceImpl) UpdateRecipe(ctx context.Context, recipe *models.Recipe) (*models.Recipe, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRecipe") // 检查要更新的实体是否存在 existingRecipe, err := s.recipeRepo.GetRecipeByID(serviceCtx, recipe.ID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrRecipeNotFound } return nil, fmt.Errorf("获取待更新的配方失败: %w", err) } // 如果名称有变动,检查新名称是否与其它记录冲突 if existingRecipe.Name != recipe.Name { existing, err := s.recipeRepo.GetRecipeByName(serviceCtx, recipe.Name) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("检查新的配方名称失败: %w", err) } if existing != nil && existing.ID != recipe.ID { return nil, ErrRecipeNameConflict } } if err := s.recipeRepo.UpdateRecipe(serviceCtx, recipe); err != nil { return nil, fmt.Errorf("更新配方失败: %w", err) } return recipe, nil } // DeleteRecipe 实现了删除配方的核心业务逻辑 func (s *recipeCoreServiceImpl) DeleteRecipe(ctx context.Context, id uint32) error { serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteRecipe") // 检查实体是否存在 _, err := s.recipeRepo.GetRecipeByID(serviceCtx, id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrRecipeNotFound } return fmt.Errorf("获取待删除的配方失败: %w", err) } if err := s.recipeRepo.DeleteRecipe(serviceCtx, id); err != nil { return fmt.Errorf("删除配方失败: %w", err) } return nil } // ListRecipes 实现了列出配方的逻辑 func (s *recipeCoreServiceImpl) ListRecipes(ctx context.Context, opts repository.RecipeListOptions, page, pageSize int) ([]models.Recipe, int64, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListRecipes") recipes, total, err := s.recipeRepo.ListRecipes(serviceCtx, opts, page, pageSize) if err != nil { return nil, 0, fmt.Errorf("获取配方列表失败: %w", err) } return recipes, total, nil } // UpdateRecipeIngredients 实现了全量更新配方原料的业务逻辑 func (s *recipeCoreServiceImpl) UpdateRecipeIngredients(ctx context.Context, recipeID uint32, ingredients []models.RecipeIngredient) error { serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRecipeIngredients") // 1. 检查配方是否存在 if _, err := s.recipeRepo.GetRecipeByID(serviceCtx, recipeID); err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return ErrRecipeNotFound } return fmt.Errorf("获取待更新原料的配方失败: %w", err) } // 2. 在事务中执行替换操作 err := s.uow.ExecuteInTransaction(serviceCtx, func(tx *gorm.DB) error { // 2.1. 删除旧的关联记录 if err := s.recipeRepo.DeleteRecipeIngredientsByRecipeIDTx(serviceCtx, tx, recipeID); err != nil { return err // 错误已在仓库层封装,直接返回 } // 2.2. 创建新的关联记录 // 确保每个原料都设置了正确的 RecipeID for i := range ingredients { ingredients[i].RecipeID = recipeID } if err := s.recipeRepo.CreateBatchRecipeIngredientsTx(serviceCtx, tx, ingredients); err != nil { return err // 错误已在仓库层封装,直接返回 } return nil }) if err != nil { return fmt.Errorf("更新配方原料事务执行失败: %w", err) } // 3. 操作成功,直接返回 nil return nil }