2025-11-24 13:25:15 +08:00
|
|
|
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)
|
2025-11-26 20:44:41 +08:00
|
|
|
// GenerateRecipeWithAllRawMaterials 添加新方法
|
|
|
|
|
GenerateRecipeWithAllRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error)
|
2025-11-27 21:06:15 +08:00
|
|
|
// GenerateRecipeWithPrioritizedStockRawMaterials 生成新配方,优先使用有库存的原料
|
|
|
|
|
GenerateRecipeWithPrioritizedStockRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error)
|
2025-11-24 13:25:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// recipeServiceImpl 是 RecipeService 接口的实现
|
|
|
|
|
type recipeServiceImpl struct {
|
|
|
|
|
ctx context.Context
|
2025-11-26 20:44:41 +08:00
|
|
|
recipeSvc recipe.Service
|
2025-11-24 13:25:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewRecipeService 创建一个新的 RecipeService 实例
|
2025-11-26 20:44:41 +08:00
|
|
|
func NewRecipeService(ctx context.Context, recipeSvc recipe.Service) RecipeService {
|
2025-11-24 13:25:15 +08:00
|
|
|
return &recipeServiceImpl{
|
|
|
|
|
ctx: ctx,
|
|
|
|
|
recipeSvc: recipeSvc,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-26 20:44:41 +08:00
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-27 21:06:15 +08:00
|
|
|
// GenerateRecipeWithPrioritizedStockRawMaterials 实现生成优先使用库存原料配方的方法
|
|
|
|
|
func (s *recipeServiceImpl) GenerateRecipeWithPrioritizedStockRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error) {
|
|
|
|
|
serviceCtx := logs.AddFuncName(ctx, s.ctx, "GenerateRecipeWithPrioritizedStockRawMaterials")
|
|
|
|
|
// 直接调用领域服务的方法
|
|
|
|
|
return s.recipeSvc.GenerateRecipeWithPrioritizedStockRawMaterials(serviceCtx, pigTypeID)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-24 13:25:15 +08:00
|
|
|
// 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
|
|
|
|
|
}
|