package service import ( "context" "errors" "fmt" "git.huangwc.com/pig/pig-farm-controller/internal/app/dto" "git.huangwc.com/pig/pig-farm-controller/internal/domain/recipe" "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" ) // 定义配方服务特定的错误 var ( ErrRecipeNameConflict = errors.New("配方名称已存在") ErrRecipeNotFound = errors.New("配方不存在") ) // RecipeService 定义了配方相关的应用服务接口 type RecipeService interface { CreateRecipe(ctx context.Context, req *dto.CreateRecipeRequest) (*dto.RecipeResponse, error) UpdateRecipe(ctx context.Context, id uint32, req *dto.UpdateRecipeRequest) (*dto.RecipeResponse, error) DeleteRecipe(ctx context.Context, id uint32) error GetRecipeByID(ctx context.Context, id uint32) (*dto.RecipeResponse, error) ListRecipes(ctx context.Context, req *dto.ListRecipeRequest) (*dto.ListRecipeResponse, error) // GenerateRecipeWithAllRawMaterials 添加新方法 GenerateRecipeWithAllRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error) } // recipeServiceImpl 是 RecipeService 接口的实现 type recipeServiceImpl struct { ctx context.Context recipeSvc recipe.Service } // NewRecipeService 创建一个新的 RecipeService 实例 func NewRecipeService(ctx context.Context, recipeSvc recipe.Service) RecipeService { return &recipeServiceImpl{ ctx: ctx, recipeSvc: recipeSvc, } } // GenerateRecipeWithAllRawMaterials 实现新方法 func (s *recipeServiceImpl) GenerateRecipeWithAllRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "GenerateRecipeWithAllRawMaterials") // 直接调用领域服务的方法 return s.recipeSvc.GenerateRecipeWithAllRawMaterials(serviceCtx, pigTypeID) } // CreateRecipe 创建配方 func (s *recipeServiceImpl) CreateRecipe(ctx context.Context, req *dto.CreateRecipeRequest) (*dto.RecipeResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateRecipe") recipeModel := dto.ConvertCreateRecipeRequestToModel(req) createdRecipe, err := s.recipeSvc.CreateRecipe(serviceCtx, recipeModel) if err != nil { if errors.Is(err, recipe.ErrRecipeNameConflict) { return nil, ErrRecipeNameConflict } return nil, fmt.Errorf("创建配方失败: %w", err) } // 创建成功后,获取包含完整信息的配方 fullRecipe, err := s.recipeSvc.GetRecipeByID(serviceCtx, createdRecipe.ID) if err != nil { // 理论上不应该发生,因为刚创建成功 return nil, fmt.Errorf("创建后获取配方信息失败: %w", err) } return dto.ConvertRecipeToDto(fullRecipe), nil } // UpdateRecipe 更新配方 func (s *recipeServiceImpl) UpdateRecipe(ctx context.Context, id uint32, req *dto.UpdateRecipeRequest) (*dto.RecipeResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRecipe") // 1. 转换 DTO 为模型 recipeModel := dto.ConvertUpdateRecipeRequestToModel(req) recipeModel.ID = id // 2. 更新配方基础信息 _, err := s.recipeSvc.UpdateRecipe(serviceCtx, recipeModel) if err != nil { if errors.Is(err, recipe.ErrRecipeNotFound) { return nil, ErrRecipeNotFound } if errors.Is(err, recipe.ErrRecipeNameConflict) { return nil, ErrRecipeNameConflict } return nil, fmt.Errorf("更新配方基础信息失败: %w", err) } // 3. 更新配方原料 ingredients := make([]models.RecipeIngredient, len(req.RecipeIngredients)) for i, item := range req.RecipeIngredients { ingredients[i] = models.RecipeIngredient{ RecipeID: id, RawMaterialID: item.RawMaterialID, Percentage: item.Percentage, } } err = s.recipeSvc.UpdateRecipeIngredients(serviceCtx, id, ingredients) if err != nil { if errors.Is(err, recipe.ErrRecipeNotFound) { return nil, ErrRecipeNotFound } return nil, fmt.Errorf("更新配方原料失败: %w", err) } // 4. 更新成功后,获取最新的完整配方信息并返回 updatedRecipe, err := s.recipeSvc.GetRecipeByID(serviceCtx, id) if err != nil { if errors.Is(err, recipe.ErrRecipeNotFound) { // 理论上不应该发生,因为刚更新成功 return nil, ErrRecipeNotFound } return nil, fmt.Errorf("更新后获取配方信息失败: %w", err) } return dto.ConvertRecipeToDto(updatedRecipe), nil } // DeleteRecipe 删除配方 func (s *recipeServiceImpl) DeleteRecipe(ctx context.Context, id uint32) error { serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteRecipe") err := s.recipeSvc.DeleteRecipe(serviceCtx, id) if err != nil { if errors.Is(err, recipe.ErrRecipeNotFound) { return ErrRecipeNotFound } return fmt.Errorf("删除配方失败: %w", err) } return nil } // GetRecipeByID 获取单个配方 func (s *recipeServiceImpl) GetRecipeByID(ctx context.Context, id uint32) (*dto.RecipeResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetRecipeByID") recipeModel, err := s.recipeSvc.GetRecipeByID(serviceCtx, id) if err != nil { if errors.Is(err, recipe.ErrRecipeNotFound) { return nil, ErrRecipeNotFound } return nil, fmt.Errorf("获取配方失败: %w", err) } return dto.ConvertRecipeToDto(recipeModel), nil } // ListRecipes 列出配方 func (s *recipeServiceImpl) ListRecipes(ctx context.Context, req *dto.ListRecipeRequest) (*dto.ListRecipeResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListRecipes") opts := repository.RecipeListOptions{ Name: req.Name, OrderBy: req.OrderBy, } recipes, total, err := s.recipeSvc.ListRecipes(serviceCtx, opts, req.Page, req.PageSize) if err != nil { return nil, fmt.Errorf("获取配方列表失败: %w", err) } return dto.ConvertRecipeListToDTO(recipes, total, req.Page, req.PageSize), nil }