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 ( ErrRawMaterialNameConflict = errors.New("原料名称已存在") ErrRawMaterialNotFound = errors.New("原料不存在") ) // RawMaterialService 定义了原料相关的应用服务接口 type RawMaterialService interface { CreateRawMaterial(ctx context.Context, req *dto.CreateRawMaterialRequest) (*dto.RawMaterialResponse, error) UpdateRawMaterial(ctx context.Context, id uint32, req *dto.UpdateRawMaterialRequest) (*dto.RawMaterialResponse, error) DeleteRawMaterial(ctx context.Context, id uint32) error GetRawMaterial(ctx context.Context, id uint32) (*dto.RawMaterialResponse, error) ListRawMaterials(ctx context.Context, req *dto.ListRawMaterialRequest) (*dto.ListRawMaterialResponse, error) UpdateRawMaterialNutrients(ctx context.Context, id uint32, req *dto.UpdateRawMaterialNutrientsRequest) (*dto.RawMaterialResponse, error) } // rawMaterialServiceImpl 是 RawMaterialService 接口的实现 type rawMaterialServiceImpl struct { ctx context.Context recipeSvc recipe.Service } // NewRawMaterialService 创建一个新的 RawMaterialService 实例 func NewRawMaterialService(ctx context.Context, recipeSvc recipe.Service) RawMaterialService { return &rawMaterialServiceImpl{ ctx: ctx, recipeSvc: recipeSvc, } } // CreateRawMaterial 创建原料 func (s *rawMaterialServiceImpl) CreateRawMaterial(ctx context.Context, req *dto.CreateRawMaterialRequest) (*dto.RawMaterialResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateRawMaterial") rawMaterial, err := s.recipeSvc.CreateRawMaterial(serviceCtx, req.Name, req.Description, req.ReferencePrice, req.MaxAdditionRatio) if err != nil { if errors.Is(err, recipe.ErrRawMaterialNameConflict) { return nil, ErrRawMaterialNameConflict } return nil, fmt.Errorf("创建原料失败: %w", err) } return dto.ConvertRawMaterialToDTO(rawMaterial), nil } // UpdateRawMaterial 更新原料 func (s *rawMaterialServiceImpl) UpdateRawMaterial(ctx context.Context, id uint32, req *dto.UpdateRawMaterialRequest) (*dto.RawMaterialResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRawMaterial") rawMaterial, err := s.recipeSvc.UpdateRawMaterial(serviceCtx, id, req.Name, req.Description, req.ReferencePrice, req.MaxAdditionRatio) if err != nil { if errors.Is(err, recipe.ErrRawMaterialNotFound) { return nil, ErrRawMaterialNotFound } if errors.Is(err, recipe.ErrRawMaterialNameConflict) { return nil, ErrRawMaterialNameConflict } return nil, fmt.Errorf("更新原料失败: %w", err) } return dto.ConvertRawMaterialToDTO(rawMaterial), nil } // DeleteRawMaterial 删除原料 func (s *rawMaterialServiceImpl) DeleteRawMaterial(ctx context.Context, id uint32) error { serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteRawMaterial") err := s.recipeSvc.DeleteRawMaterial(serviceCtx, id) if err != nil { if errors.Is(err, recipe.ErrRawMaterialNotFound) { return ErrRawMaterialNotFound } return fmt.Errorf("删除原料失败: %w", err) } return nil } // GetRawMaterial 获取单个原料 func (s *rawMaterialServiceImpl) GetRawMaterial(ctx context.Context, id uint32) (*dto.RawMaterialResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetRawMaterial") rawMaterial, err := s.recipeSvc.GetRawMaterial(serviceCtx, id) if err != nil { if errors.Is(err, recipe.ErrRawMaterialNotFound) { return nil, ErrRawMaterialNotFound } return nil, fmt.Errorf("获取原料失败: %w", err) } return dto.ConvertRawMaterialToDTO(rawMaterial), nil } // ListRawMaterials 列出原料 func (s *rawMaterialServiceImpl) ListRawMaterials(ctx context.Context, req *dto.ListRawMaterialRequest) (*dto.ListRawMaterialResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListRawMaterials") opts := repository.RawMaterialListOptions{ Name: req.Name, NutrientName: req.NutrientName, MinReferencePrice: req.MinReferencePrice, MaxReferencePrice: req.MaxReferencePrice, OrderBy: req.OrderBy, } rawMaterials, total, err := s.recipeSvc.ListRawMaterials(serviceCtx, opts, req.Page, req.PageSize) if err != nil { return nil, fmt.Errorf("获取原料列表失败: %w", err) } return dto.ConvertRawMaterialListToDTO(rawMaterials, total, req.Page, req.PageSize), nil } // UpdateRawMaterialNutrients 全量更新原料的营养成分 func (s *rawMaterialServiceImpl) UpdateRawMaterialNutrients(ctx context.Context, id uint32, req *dto.UpdateRawMaterialNutrientsRequest) (*dto.RawMaterialResponse, error) { serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRawMaterialNutrients") // 1. 将 DTO 转换为领域模型 nutrients := make([]models.RawMaterialNutrient, len(req.Nutrients)) for i, item := range req.Nutrients { nutrients[i] = models.RawMaterialNutrient{ RawMaterialID: id, NutrientID: item.NutrientID, Value: item.Value, } } // 2. 调用领域服务执行更新命令 err := s.recipeSvc.UpdateRawMaterialNutrients(serviceCtx, id, nutrients) if err != nil { if errors.Is(err, recipe.ErrRawMaterialNotFound) { return nil, ErrRawMaterialNotFound } // 此处可以根据领域层可能返回的其他特定错误进行转换 return nil, fmt.Errorf("更新原料营养成分失败: %w", err) } // 3. 更新成功后,调用查询服务获取最新的原料信息 updatedRawMaterial, err := s.recipeSvc.GetRawMaterial(serviceCtx, id) if err != nil { if errors.Is(err, recipe.ErrRawMaterialNotFound) { // 理论上不应该发生,因为刚更新成功 return nil, ErrRawMaterialNotFound } return nil, fmt.Errorf("更新后获取原料信息失败: %w", err) } // 4. 将领域模型转换为 DTO 并返回 return dto.ConvertRawMaterialToDTO(updatedRawMaterial), nil }