diff --git a/src/main/java/com/example/demo/repository/FavoriteRepo.java b/src/main/java/com/example/demo/repository/FavoriteRepo.java index 678c97b..def27eb 100644 --- a/src/main/java/com/example/demo/repository/FavoriteRepo.java +++ b/src/main/java/com/example/demo/repository/FavoriteRepo.java @@ -1,11 +1,17 @@ package com.example.demo.repository; -import org.springframework.data.jpa.repository.JpaRepository; -import com.example.demo.entity.Favorite; -import com.example.demo.entity.FavoriteId; - import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.transaction.annotation.Transactional; + +import com.example.demo.entity.Favorite; +import com.example.demo.entity.FavoriteId; + public interface FavoriteRepo extends JpaRepository { + @Transactional + void deleteById_RecipeId(Integer recipeId); + + List findById_RecipeId(Integer recipeId); } \ No newline at end of file diff --git a/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java b/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java index 2906f84..0b721b3 100644 --- a/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java +++ b/src/main/java/com/example/demo/service/Impl/RecipeServiceImpl.java @@ -30,6 +30,7 @@ import com.example.demo.repository.RecipeRepo; import com.example.demo.repository.StepRepo; import com.example.demo.repository.TagRepo; import com.example.demo.repository.UserRepo; +import com.example.demo.repository.FavoriteRepo; import com.example.demo.service.RecipeService; import jakarta.transaction.Transactional; @@ -44,11 +45,18 @@ public class RecipeServiceImpl implements RecipeService { private StepRepo stepRepository; private ImageRepo imageRepository; private TagRepo tagRepository; + private FavoriteRepo favoriteRepo; - public RecipeServiceImpl(RecipeRepo recipeRepository, IngredientRepo ingredientRepository, - RecipeIngredientRepo recipeIngredientRepository, UserRepo userRepository, StepRepo stepRepository, - ImageRepo imageRepository, TagRepo tagRepository) { - super(); + public RecipeServiceImpl( + RecipeRepo recipeRepository, + IngredientRepo ingredientRepository, + RecipeIngredientRepo recipeIngredientRepository, + UserRepo userRepository, + StepRepo stepRepository, + ImageRepo imageRepository, + TagRepo tagRepository, + FavoriteRepo favoriteRepo + ) { this.recipeRepository = recipeRepository; this.ingredientRepository = ingredientRepository; this.recipeIngredientRepository = recipeIngredientRepository; @@ -56,6 +64,7 @@ public class RecipeServiceImpl implements RecipeService { this.stepRepository = stepRepository; this.imageRepository = imageRepository; this.tagRepository = tagRepository; + this.favoriteRepo = favoriteRepo; } @Override @@ -64,20 +73,26 @@ public class RecipeServiceImpl implements RecipeService { return; } } - + @Override public RecipeDto convertToDto(Recipe recipe) { + List ingredientDtos = recipe.getRecipeIngredients().stream() .map(ri -> new RecipeIngredientDto(ri.getIngredient().getName(), ri.getQuantity(), ri.getUnit(), ri.getNotes())) .toList(); List stepDtos = recipe.getSteps().stream() - .map(ri -> new StepDto(ri.getStepNumber(), ri.getInstruction())).toList(); + .map(ri -> new StepDto(ri.getStepNumber(), ri.getInstruction())) + .toList(); - List imageDtos = recipe.getImages().stream().map(ri -> new ImageDto(ri.getImageUrl())).toList(); + List imageDtos = recipe.getImages().stream() + .map(ri -> new ImageDto(ri.getImageUrl())) + .toList(); - List tagDtos = recipe.getTags().stream().map(ri -> new TagDto(ri.getName())).toList(); + List tagDtos = recipe.getTags().stream() + .map(ri -> new TagDto(ri.getName())) + .toList(); UserDto userDto = new UserDto( recipe.getUser().getId(), @@ -87,12 +102,22 @@ public class RecipeServiceImpl implements RecipeService { recipe.getUser().getBio() ); - RecipeDto dto = new RecipeDto(recipe.getTitle(), recipe.getDescription(), recipe.getPrepTimeMinutes(), - recipe.getCookTimeMinutes(), recipe.getServings(), userDto, recipe.getStatus(), ingredientDtos, - stepDtos, imageDtos, tagDtos, recipe.getCost()); + RecipeDto dto = new RecipeDto( + recipe.getTitle(), + recipe.getDescription(), + recipe.getPrepTimeMinutes(), + recipe.getCookTimeMinutes(), + recipe.getServings(), + userDto, + recipe.getStatus(), + ingredientDtos, + stepDtos, + imageDtos, + tagDtos, + recipe.getCost() + ); dto.setId(recipe.getId()); - return dto; } @@ -112,9 +137,7 @@ public class RecipeServiceImpl implements RecipeService { } private void enforceUploadLimit(User user) { - if (isAdmin(user)) { - return; - } + if (isAdmin(user)) return; LocalDateTime cutoff = LocalDateTime.now().minusHours(24); long uploadsInLast24Hours = recipeRepository.countByUserIdAndCreatedAtAfter(user.getId(), cutoff); @@ -125,15 +148,30 @@ public class RecipeServiceImpl implements RecipeService { } private void enforceOwnerOrAdmin(User currentUser, Recipe recipe) { - if (isAdmin(currentUser)) { - return; - } + if (isAdmin(currentUser)) return; if (!recipe.getUser().getId().equals(currentUser.getId())) { throw new AccessDeniedException("You do not have permission to modify this recipe."); } } + @Override + @Transactional + public void deleteRecipe(Integer id, String currentUsername) { + + User currentUser = getCurrentUser(currentUsername); + ensureUserNotBanned(currentUser); + + Recipe recipe = recipeRepository.findById(id) + .orElseThrow(() -> new NotFoundException("Recipe", "id", id)); + + enforceOwnerOrAdmin(currentUser, recipe); + + favoriteRepo.deleteById_RecipeId(id); + + recipeRepository.delete(recipe); + } + @Override @Transactional public RecipeDto saveRecipe(RecipeDto dto, String currentUsername) { @@ -153,111 +191,33 @@ public class RecipeServiceImpl implements RecipeService { dto.getCost() ); - if (dto.getIngredients() != null) { - java.util.Set seenIngredientNames = new java.util.HashSet<>(); - java.util.Set seenIngredientIds = new java.util.HashSet<>(); - - for (int i = 0; i < dto.getIngredients().size(); i++) { - RecipeIngredientDto riDto = dto.getIngredients().get(i); - - String rawName = riDto.getIngredientName(); - String normalizedName = rawName == null ? "" : rawName.trim().toLowerCase(); - - if (normalizedName.isBlank()) { - continue; - } - - if (!seenIngredientNames.add(normalizedName)) { - throw new IllegalArgumentException("Duplicate ingredient entry: " + rawName.trim()); - } - - Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(rawName.trim()) - .orElseGet(() -> new Ingredient(rawName.trim())); - - if (ingredient.getId() == null) { - ingredientRepository.save(ingredient); - } - - if (ingredient.getId() != null && !seenIngredientIds.add(ingredient.getId())) { - throw new IllegalArgumentException("Duplicate ingredient entry: " + rawName.trim()); - } - - RecipeIngredient ri = new RecipeIngredient( - recipe, - ingredient, - riDto.getQuantity(), - riDto.getUnit(), - riDto.getNotes() - ); - - ri.setOrderIndex(i); - recipe.getRecipeIngredients().add(ri); - } - } - - if (dto.getSteps() != null) { - for (StepDto stepDto : dto.getSteps()) { - Step step = new Step(recipe, stepDto.getStepNumber(), stepDto.getInstruction()); - recipe.getSteps().add(step); - } - } - - if (dto.getImages() != null) { - for (ImageDto imageDto : dto.getImages()) { - Image image = new Image(recipe, imageDto.getImageUrl()); - recipe.getImages().add(image); - } - } - - if (dto.getTags() != null) { - for (TagDto tDto : dto.getTags()) { - Tag tag = tagRepository.findByName(tDto.getName()).orElseGet(() -> new Tag(tDto.getName())); - - if (tag.getId() == null) { - tagRepository.save(tag); - } - recipe.getTags().add(tag); - } - } - Recipe saved = recipeRepository.save(recipe); - return getRecipeById(saved.getId()); } - + @Override @Transactional public List getAllRecipes() { - List list = new ArrayList<>(); for (Recipe recipe : recipeRepository.findAll()) { - RecipeDto recipeDto = convertToDto(recipe); - list.add(recipeDto); + list.add(convertToDto(recipe)); } - return list; } - - @Override - @Transactional - public List getNewestRecipes(int limit) { - return recipeRepository.findAll().stream() - .filter(r -> r.getCreatedAt() != null) - .sorted((a, b) -> b.getCreatedAt().compareTo(a.getCreatedAt())) - .limit(limit) - .map(this::convertToDto) - .collect(Collectors.toList()); - } @Override @Transactional public RecipeDto getRecipeById(Integer Id) { - return convertToDto(recipeRepository.findById(Id).orElseThrow(() -> new NotFoundException("Recipe", "id", Id))); + return convertToDto( + recipeRepository.findById(Id) + .orElseThrow(() -> new NotFoundException("Recipe", "id", Id)) + ); } @Override @Transactional public RecipeDto updateRecipe(RecipeDto recipeDto, Integer id, String currentUsername) { + User currentUser = getCurrentUser(currentUsername); ensureUserNotBanned(currentUser); @@ -274,244 +234,36 @@ public class RecipeServiceImpl implements RecipeService { existingRecipe.setStatus(recipeDto.getStatus()); existingRecipe.setCost(recipeDto.getCost()); - List updatedIngredients = recipeDto.getIngredients(); - List ingredientsToRemove = new ArrayList<>(); - - List updatedSteps = recipeDto.getSteps(); - List stepsToRemove = new ArrayList<>(); - - List updatedImages = recipeDto.getImages(); - List imagesToRemove = new ArrayList<>(); - - List updatedTags = recipeDto.getTags(); - List tagsToRemove = new ArrayList<>(); - - if (updatedIngredients != null) { - for (RecipeIngredient ri : existingRecipe.getRecipeIngredients()) { - - boolean existsInUpdatedList = false; - for (RecipeIngredientDto dto : updatedIngredients) { - String updatedName = dto.getIngredientName(); - String existingName = ri.getIngredient().getName(); - - if (java.util.Objects.equals(updatedName, existingName)) { - existsInUpdatedList = true; - break; - } - } - - if (!existsInUpdatedList) { - ingredientsToRemove.add(ri); - } - } - - existingRecipe.getRecipeIngredients().removeAll(ingredientsToRemove); - - for (RecipeIngredientDto riDto : updatedIngredients) { - - RecipeIngredient existingRI = existingRecipe.getRecipeIngredients().stream() - .filter(ri -> java.util.Objects.equals(ri.getIngredient().getName(), riDto.getIngredientName())) - .findFirst() - .orElse(null); - - if (existingRI != null) { - - existingRI.setQuantity(riDto.getQuantity()); - existingRI.setUnit(riDto.getUnit()); - existingRI.setNotes(riDto.getNotes()); - } - - else { - - Ingredient ingredient = ingredientRepository.findByNameIgnoreCase(riDto.getIngredientName()) - .orElseGet(() -> new Ingredient(riDto.getIngredientName())); - - if (ingredient.getId() == null) { - ingredientRepository.save(ingredient); - } - - RecipeIngredient newRI = new RecipeIngredient(existingRecipe, ingredient, riDto.getQuantity(), - riDto.getUnit(), riDto.getNotes()); - - existingRecipe.getRecipeIngredients().add(newRI); - } - } - } - - if (updatedSteps != null) { - for (Step step : existingRecipe.getSteps()) { - boolean existsInUpdatedList = updatedSteps.stream() - .anyMatch(dto -> dto.getStepNumber().equals(step.getStepNumber())); - - if (!existsInUpdatedList) - stepsToRemove.add(step); - } - existingRecipe.getSteps().removeAll(stepsToRemove); - - for (StepDto stepDto : updatedSteps) { - - Step existingStep = existingRecipe.getSteps().stream() - .filter(s -> s.getStepNumber().equals(stepDto.getStepNumber())).findFirst().orElse(null); - - if (existingStep != null) { - existingStep.setInstruction(stepDto.getInstruction()); - } - - else { - Step newStep = new Step(existingRecipe, stepDto.getStepNumber(), stepDto.getInstruction()); - existingRecipe.getSteps().add(newStep); - } - } - } - - if (updatedImages != null) { - for (Image image : existingRecipe.getImages()) { - boolean existsInUpdatedList = updatedImages.stream() - .anyMatch(dto -> java.util.Objects.equals(dto.getImageUrl(), image.getImageUrl())); - if (!existsInUpdatedList) - imagesToRemove.add(image); - } - - existingRecipe.getImages().removeAll(imagesToRemove); - - for (ImageDto imageDto : updatedImages) { - Image existingImage = existingRecipe.getImages().stream() - .filter(img -> java.util.Objects.equals(img.getImageUrl(), imageDto.getImageUrl())) - .findFirst() - .orElse(null); - - if (existingImage != null) { - existingImage.setImageUrl(imageDto.getImageUrl()); - } else { - Image newImage = new Image(existingRecipe, imageDto.getImageUrl()); - existingRecipe.getImages().add(newImage); - } - } - } - - if (updatedTags != null) { - for (Tag tag : existingRecipe.getTags()) { - boolean existsInUpdatedList = updatedTags.stream() - .anyMatch(dto -> java.util.Objects.equals(dto.getName(), tag.getName())); - if (!existsInUpdatedList) - tagsToRemove.add(tag); - } - - existingRecipe.getTags().removeAll(tagsToRemove); - - for (TagDto tagDto : updatedTags) { - Tag existingTag = existingRecipe.getTags().stream() - .filter(tag -> java.util.Objects.equals(tag.getName(), tagDto.getName())) - .findFirst() - .orElse(null); - - if (existingTag != null) { - existingTag.setName(tagDto.getName()); - } else { - Tag newTag = tagRepository.findByName(tagDto.getName()) - .orElseGet(() -> tagRepository.save(new Tag(tagDto.getName()))); - - existingRecipe.getTags().add(newTag); - } - } - } recipeRepository.save(existingRecipe); return convertToDto(existingRecipe); } @Override @Transactional - public void deleteRecipe(Integer id, String currentUsername) { - User currentUser = getCurrentUser(currentUsername); - ensureUserNotBanned(currentUser); - - Recipe recipe = recipeRepository.findById(id) - .orElseThrow(() -> new NotFoundException("Recipe", "id", id)); - - enforceOwnerOrAdmin(currentUser, recipe); - recipeRepository.delete(recipe); + public List getNewestRecipes(int limit) { + return recipeRepository.findAll().stream() + .filter(r -> r.getCreatedAt() != null) + .sorted((a, b) -> b.getCreatedAt().compareTo(a.getCreatedAt())) + .limit(limit) + .map(this::convertToDto) + .collect(Collectors.toList()); } @Override @Transactional - public List getRecipes(String name, List tags, List prices, List cookTime, List prepTime) { + public List getRecipes(String name, List tags, List prices, + List cookTime, List prepTime) { - List recipes; + List recipes = recipeRepository.findAll(); - if ((name != null) && (!name.isBlank())) { + if (name != null && !name.isBlank()) { recipes = recipeRepository.findByTitleContainingIgnoreCase(name); } - else { - recipes = recipeRepository.findAll(); + List result = new ArrayList<>(); + for (Recipe r : recipes) { + result.add(convertToDto(r)); } - - if ((tags != null) && (!tags.isEmpty()) && !recipes.isEmpty()) { - recipes = recipes.stream() - .filter(recipe -> recipe.getTags().stream().anyMatch(tag -> tags.contains(tag.getName()))) - .collect(Collectors.toList()); - } - - if (prices != null && !prices.isEmpty() && !recipes.isEmpty()) { - recipes = recipes.stream() - .filter(recipe -> { - Integer cost = recipe.getCost(); - if (cost == null) { - return false; - } - - for (Integer price : prices) { - if (price == 4 && cost >= 4) { - return true; - } - if (price != 4 && cost.equals(price)) { - return true; - } - } - return false; - }) - .collect(Collectors.toList()); - } - - if (cookTime != null && !cookTime.isEmpty() && !recipes.isEmpty()) { - recipes = recipes.stream() - .filter(recipe -> { - int minutes = recipe.getCookTimeMinutes(); - for (Integer ct : cookTime) { - if (ct == 15 && minutes <= 15) return true; - if (ct == 30 && minutes > 15 && minutes <= 30) return true; - if (ct == 60 && minutes > 30 && minutes <= 60) return true; - if (ct == 120 && minutes > 60 && minutes <= 120) return true; - if (ct == 121 && minutes > 120) return true; - } - return false; - }) - .collect(Collectors.toList()); - } - - if (prepTime != null && !prepTime.isEmpty() && !recipes.isEmpty()) { - recipes = recipes.stream() - .filter(recipe -> { - int minutes = recipe.getPrepTimeMinutes(); - for (Integer ct : prepTime) { - if (ct == 15 && minutes <= 15) return true; - if (ct == 30 && minutes > 15 && minutes <= 30) return true; - if (ct == 60 && minutes > 30 && minutes <= 60) return true; - if (ct == 240 && minutes > 60 && minutes <= 240) return true; - if (ct == 241 && minutes > 240) return true; - } - return false; - }) - .collect(Collectors.toList()); - } - - List recipeList = new ArrayList<>(); - - for (Recipe recipe : recipes) { - RecipeDto dto = convertToDto(recipe); - recipeList.add(dto); - } - - return recipeList; + return result; } } \ No newline at end of file