diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1588f65 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: ๐Ÿ› Bug Report +about: ๋ฒ„๊ทธ๋ฅผ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค +title: "[Bug] " +labels: bug +assignees: '' +--- + +## ๐Ÿž ๋ฒ„๊ทธ ์„ค๋ช… +- ๋ฒ„๊ทธ์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## โœ… ์žฌํ˜„ ๋ฐฉ๋ฒ• +- ๋ฒ„๊ทธ๋ฅผ ์žฌํ˜„ํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด์„ธ์š”: + 1. [์˜ˆ: ํŽ˜์ด์ง€ ์ด๋™ ๊ฒฝ๋กœ] + 2. [์˜ˆ: ํŠน์ • ๋ฒ„ํŠผ ํด๋ฆญ] + 3. [์˜ˆ: ๊ธฐ๋Œ€๋˜๋Š” ๊ฒฐ๊ณผ] + +## ๐Ÿ–ฅ ํ™˜๊ฒฝ ์ •๋ณด +- OS: [Windows, macOS, Linux ๋“ฑ] +- Browser: [Chrome, Firefox ๋“ฑ] +- Version: [์•ฑ/๋ธŒ๋ผ์šฐ์ € ๋ฒ„์ „] + +## ๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท +- ๋ฒ„๊ทธ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์Šคํฌ๋ฆฐ์ƒท์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š” (ํ•„์š”ํ•œ ๊ฒฝ์šฐ). diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..9880a67 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,16 @@ +--- +name: โœจ Feature Request +about: ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค +title: "[Feature] " +labels: enhancement +assignees: '' +--- + +## ๐ŸŒŸ ๊ธฐ๋Šฅ ์„ค๋ช… +- ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์€ ๊ธฐ๋Šฅ์ด๋‚˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ„๋žตํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## ๐Ÿค” ์ด์œ  +- ์ด ๊ธฐ๋Šฅ์ด ์™œ ํ•„์š”ํ•œ์ง€, ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”์ง€ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## ๐Ÿ“‹ ์ถ”๊ฐ€ ์ •๋ณด +- ๊ธฐ๋Šฅ๊ณผ ๊ด€๋ จ๋œ ์ฐธ๊ณ  ์ž๋ฃŒ๋‚˜ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ํฌํ•จํ•ด์ฃผ์„ธ์š”. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..3d75a26 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,34 @@ +## ๐Ÿ“‹ ์š”์•ฝ +- ์ด Pull Request์˜ ๋ชฉ์ ๊ณผ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ„๋žตํžˆ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. + +## ๐Ÿ›  ๋ณ€๊ฒฝ ์‚ฌํ•ญ +- ์ด๋ฒˆ Pull Request์—์„œ ์ž‘์—…ํ•œ ์ฃผ์š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: + - [x] ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋˜๋Š” ๋ฒ„๊ทธ ์ˆ˜์ • + - [x] ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง ๋˜๋Š” ์ตœ์ ํ™” + - [x] ๋ฌธ์„œ ์—…๋ฐ์ดํŠธ + +## ๐Ÿ”— ๊ด€๋ จ ์ด์Šˆ +- ์ด PR๋กœ ํ•ด๊ฒฐ๋˜๋Š” ์ด์Šˆ: #[์ด์Šˆ ๋ฒˆํ˜ธ] +- ๊ด€๋ จ๋œ ์ด์Šˆ: #[์ด์Šˆ ๋ฒˆํ˜ธ] + +## ๐Ÿ“ธ ์Šคํฌ๋ฆฐ์ƒท ๋˜๋Š” GIF (ํ•ด๋‹น๋˜๋Š” ๊ฒฝ์šฐ) +- UI ๋ณ€๊ฒฝ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ณด์—ฌ์ฃผ๋Š” ์Šคํฌ๋ฆฐ์ƒท์ด๋‚˜ GIF๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”. + +## โœ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ +- [ ] ์ฝ”๋“œ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. +- [ ] ๊ด€๋ จ ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ–ˆ์Šต๋‹ˆ๋‹ค. (ํ•ด๋‹น๋˜๋Š” ๊ฒฝ์šฐ) +- [ ] ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. +- [ ] ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ํ†ต๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค. + +## ๐Ÿ›ก ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ• +- ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•์„ ๋‹จ๊ณ„๋ณ„๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”: + 1. ๋ธŒ๋žœ์น˜๋ฅผ ๋กœ์ปฌ๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. + 2. ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค: + ``` + # ์˜ˆ์‹œ ๋ช…๋ น์–ด + ./run_tests.sh + ``` + 3. ์˜ˆ์ƒ ๋™์ž‘์ด๋‚˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. + +## ๐Ÿ“š ์ถ”๊ฐ€ ์ฐธ๊ณ  ์‚ฌํ•ญ +- ์ด PR๊ณผ ๊ด€๋ จํ•ด ๋ฆฌ๋ทฐ์–ด๊ฐ€ ์•Œ์•„์•ผ ํ•  ์ถ”๊ฐ€ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. diff --git a/refrigerator/.idea/modules/refrigerator.main.iml b/refrigerator/.idea/modules/refrigerator.main.iml index afc1871..ac8e328 100644 --- a/refrigerator/.idea/modules/refrigerator.main.iml +++ b/refrigerator/.idea/modules/refrigerator.main.iml @@ -1,8 +1,21 @@ - - + + - \ No newline at end of file + + + + + + + + + + + + + + diff --git a/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeIngredient.java b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeIngredient.java index e530f63..7790098 100644 --- a/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeIngredient.java +++ b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeIngredient.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.Data; import moja.refrigerator.aggregate.ingredient.IngredientManagement; +import moja.refrigerator.aggregate.ingredient.IngredientMyRefrigerator; @Data @Entity @@ -19,6 +20,7 @@ public class RecipeIngredient { @JoinColumn(name = "ingredient_management") @ManyToOne private IngredientManagement ingredientManagement; +// private IngredientMyRefrigerator ingredientMyRefrigerator ; @JoinColumn(name = "recipe") @ManyToOne diff --git a/refrigerator/src/main/java/moja/refrigerator/controller/recipe/RecipeController.java b/refrigerator/src/main/java/moja/refrigerator/controller/recipe/RecipeController.java index 58807d0..bd4533b 100644 --- a/refrigerator/src/main/java/moja/refrigerator/controller/recipe/RecipeController.java +++ b/refrigerator/src/main/java/moja/refrigerator/controller/recipe/RecipeController.java @@ -4,6 +4,7 @@ import moja.refrigerator.dto.recipe.request.RecipeCreateRequest; import moja.refrigerator.dto.recipe.request.RecipeUpdateRequest; import moja.refrigerator.dto.recipe.response.RecipeDetailResponse; +import moja.refrigerator.dto.recipe.response.RecipeRecommendResponse; import moja.refrigerator.dto.recipe.response.RecipeResponse; import moja.refrigerator.service.recipe.RecipeService; import org.springframework.beans.factory.annotation.Autowired; @@ -52,4 +53,9 @@ public void updateRecipe( ){ recipeService.updateRecipe(request,files); } + + @GetMapping("/recommend") + public List getRecommendedRecipes(@RequestParam Long userPk) { + return recipeService.getRecommendedRecipes(userPk); + } } diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/RecipeMatchResult.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/RecipeMatchResult.java new file mode 100644 index 0000000..210e602 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/RecipeMatchResult.java @@ -0,0 +1,15 @@ +package moja.refrigerator.dto.recipe; + +import lombok.AllArgsConstructor; +import lombok.Data; +import moja.refrigerator.aggregate.recipe.Recipe; + +@Data +@AllArgsConstructor +public class RecipeMatchResult { + private Recipe recipe; + private boolean matched; + private double matchRate; + private long remainExpirationDays; + private String urgentIngredientName; +} \ No newline at end of file diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeRecommendResponse.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeRecommendResponse.java new file mode 100644 index 0000000..dbb6019 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeRecommendResponse.java @@ -0,0 +1,14 @@ +package moja.refrigerator.dto.recipe.response; + +import lombok.Data; + +@Data +public class RecipeRecommendResponse { + private long recipePk; + private String recipeName; + private String recipeContent; + private int recipeCookingTime; + private double matchRate; + private long remainExpirationDays; + private String urgentIngredientName; +} \ No newline at end of file diff --git a/refrigerator/src/main/java/moja/refrigerator/repository/recipe/RecipeIngredientRepository.java b/refrigerator/src/main/java/moja/refrigerator/repository/recipe/RecipeIngredientRepository.java new file mode 100644 index 0000000..e717f33 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/repository/recipe/RecipeIngredientRepository.java @@ -0,0 +1,13 @@ +package moja.refrigerator.repository.recipe; + +import moja.refrigerator.aggregate.recipe.Recipe; +import moja.refrigerator.aggregate.recipe.RecipeIngredient; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface RecipeIngredientRepository extends JpaRepository { + List findByRecipe(Recipe recipe); +} \ No newline at end of file diff --git a/refrigerator/src/main/java/moja/refrigerator/repository/recipe/ReplacableIngredientRepository.java b/refrigerator/src/main/java/moja/refrigerator/repository/recipe/ReplacableIngredientRepository.java new file mode 100644 index 0000000..50a497b --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/repository/recipe/ReplacableIngredientRepository.java @@ -0,0 +1,13 @@ +package moja.refrigerator.repository.recipe; + +import moja.refrigerator.aggregate.recipe.RecipeIngredient; +import moja.refrigerator.aggregate.recipe.ReplacableIngredient; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ReplacableIngredientRepository extends JpaRepository { + List findByRecipeIngredient(RecipeIngredient recipeIngredient); +} \ No newline at end of file diff --git a/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeService.java b/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeService.java index d94057b..7c89c15 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeService.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeService.java @@ -4,6 +4,7 @@ import moja.refrigerator.dto.recipe.request.RecipeCreateRequest; import moja.refrigerator.dto.recipe.request.RecipeUpdateRequest; import moja.refrigerator.dto.recipe.response.RecipeDetailResponse; +import moja.refrigerator.dto.recipe.response.RecipeRecommendResponse; import moja.refrigerator.dto.recipe.response.RecipeResponse; import moja.refrigerator.repository.recipe.RecipeRepository; import org.springframework.stereotype.Service; @@ -22,4 +23,5 @@ void updateRecipe( RecipeUpdateRequest request ,List files ); + List getRecommendedRecipes(Long userPk); } diff --git a/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeServiceImpl.java b/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeServiceImpl.java index 3dddc12..9f53d69 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeServiceImpl.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeServiceImpl.java @@ -1,24 +1,19 @@ package moja.refrigerator.service.recipe; - import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.DeleteObjectRequest; -import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.ObjectMetadata; -import moja.refrigerator.aggregate.recipe.Recipe; -import moja.refrigerator.aggregate.recipe.RecipeCategory; -import moja.refrigerator.aggregate.recipe.RecipeSource; -import moja.refrigerator.aggregate.recipe.RecipeSourceType; +import moja.refrigerator.aggregate.ingredient.IngredientMyRefrigerator; +import moja.refrigerator.aggregate.recipe.*; import moja.refrigerator.aggregate.user.User; -import moja.refrigerator.dto.ingredient.response.IngredientResponse; +import moja.refrigerator.dto.recipe.RecipeMatchResult; import moja.refrigerator.dto.recipe.request.RecipeCreateRequest; import moja.refrigerator.dto.recipe.request.RecipeUpdateRequest; import moja.refrigerator.dto.recipe.response.RecipeDetailResponse; +import moja.refrigerator.dto.recipe.response.RecipeRecommendResponse; import moja.refrigerator.dto.recipe.response.RecipeResponse; -import moja.refrigerator.repository.recipe.RecipeCategoryRepository; -import moja.refrigerator.repository.recipe.RecipeRepository; -import moja.refrigerator.repository.recipe.RecipeSourceRepository; -import moja.refrigerator.repository.recipe.RecipeSourceTypeRepository; +import moja.refrigerator.repository.ingredient.IngredientMyRefrigeratorRepository; +import moja.refrigerator.repository.recipe.*; import moja.refrigerator.repository.user.UserRepository; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; @@ -28,125 +23,117 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import java.awt.*; import java.io.IOException; -import java.util.List; -import java.util.Objects; -import java.util.UUID; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.*; import java.util.stream.Collectors; @Service public class RecipeServiceImpl implements RecipeService { - private RecipeRepository recipeRepository; - private UserRepository userRepository; - private RecipeSourceRepository recipeSourceRepository; - private RecipeCategoryRepository recipeCategoryRepository; - private ModelMapper mapper; - private RecipeSourceTypeRepository recipeSourceTypeRepository; + private final RecipeRepository recipeRepository; + private final UserRepository userRepository; + private final RecipeSourceRepository recipeSourceRepository; + private final RecipeCategoryRepository recipeCategoryRepository; + private final ModelMapper mapper; + private final RecipeSourceTypeRepository recipeSourceTypeRepository; private final AmazonS3Client amazonS3Client; + private final IngredientMyRefrigeratorRepository ingredientMyRefrigeratorRepository; + private final RecipeIngredientRepository recipeIngredientRepository; + private final ReplacableIngredientRepository replacableIngredientRepository; @Value("${cloud.aws.s3.bucket}") private String bucket; - // ์ด๋ฏธ์ง€์˜ ํ™•์žฅ์ž๋ฅผ ํ™•์ธ, ์ด๋ฅผ ์ด์šฉํ•ด ํƒ€์ž…์„ ๋ถ„๋ฅ˜ํ•œ๋‹ค. - private boolean isImageFile(String fileName) { - String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); - return List.of("jpg", "jpeg", "png", "gif").contains(extension); - } - // ๋™์˜์ƒ์˜ ํ™•์žฅ์ž๋ฅผ ํ™•์ธ, ์ด๋ฅผ ์ด์šฉํ•ด ํƒ€์ž…์„ ๋ถ„๋ฅ˜ํ•œ๋‹ค. - private boolean isVideoFile(String fileName) { - String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); - return List.of("mp4", "avi", "mov", "wmv").contains(extension); - } - // 2๊ฐ€์ง€ ๋™์‹œ์— ๋‹ค ์“ฐ๋Š” ์ด์œ ๋Š”, ๋‘˜๋‹ค ์•„๋‹ ๊ฒฝ์šฐ(ex] exe, java ๋“ฑ ์ž˜๋ชป๋œ ํ™•์žฅ์ž๋ฅผ ์˜ฌ๋ฆด ์‹œ ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๊ธฐ ์œ„ํ•ด) - @Autowired public RecipeServiceImpl( RecipeRepository recipeRepository, - ModelMapper mapper, - RecipeSourceRepository recipeSourceRepository, UserRepository userRepository, + RecipeSourceRepository recipeSourceRepository, RecipeCategoryRepository recipeCategoryRepository, + ModelMapper mapper, RecipeSourceTypeRepository recipeSourceTypeRepository, - AmazonS3Client amazonS3Client + AmazonS3Client amazonS3Client, + IngredientMyRefrigeratorRepository ingredientMyRefrigeratorRepository, + RecipeIngredientRepository recipeIngredientRepository, + ReplacableIngredientRepository replacableIngredientRepository ) { this.recipeRepository = recipeRepository; - this.mapper = mapper; - this.recipeSourceRepository = recipeSourceRepository; this.userRepository = userRepository; + this.recipeSourceRepository = recipeSourceRepository; this.recipeCategoryRepository = recipeCategoryRepository; - this.recipeSourceTypeRepository=recipeSourceTypeRepository; + this.mapper = mapper; + this.recipeSourceTypeRepository = recipeSourceTypeRepository; this.amazonS3Client = amazonS3Client; + this.ingredientMyRefrigeratorRepository = ingredientMyRefrigeratorRepository; + this.recipeIngredientRepository = recipeIngredientRepository; + this.replacableIngredientRepository = replacableIngredientRepository; + } + + private boolean isImageFile(String fileName) { + String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); + return List.of("jpg", "jpeg", "png", "gif").contains(extension); + } + + private boolean isVideoFile(String fileName) { + String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); + return List.of("mp4", "avi", "mov", "wmv").contains(extension); } @Override @Transactional(propagation = Propagation.REQUIRED) - public void createRecipe(RecipeCreateRequest request - ,List files - ) { - Recipe recipe = new Recipe(); // mapper๋ฅผ ํ†ตํ•œ์ธ์‹์ด ์ž˜ ์•ˆ๋จ. -> ๊ทธ๋ƒฅ ์ผ์ผ์ด ์ถ”๊ฐ€. + public void createRecipe(RecipeCreateRequest request, List files) { + Recipe recipe = new Recipe(); recipe.setRecipeName(request.getRecipeName()); recipe.setRecipeContent(request.getRecipeContent()); recipe.setRecipeDifficulty(request.getRecipeDifficulty()); recipe.setRecipeCookingTime(request.getRecipeCookingTime()); - -// User ์กฐํšŒ User user = userRepository.findById(request.getUserPk()) - .orElseThrow(IllegalArgumentException::new); + .orElseThrow(IllegalArgumentException::new); recipe.setUser(user); -// RecipeCategory RecipeCategory recipeCategory = recipeCategoryRepository.findById(request.getRecipeCategoryPk()) .orElseThrow(IllegalArgumentException::new); recipe.setRecipeCategory(recipeCategory); - //RecipeSource ์ถ”๊ฐ€๋ถ€๋ถ„ + if(files != null && !files.isEmpty()) { - for(MultipartFile file : files){ - try{// 1. ์ž‘์„ฑํ•  ๋‚ด์šฉ ์ •๋ฆฌ + for(MultipartFile file : files) { + try { String recipeSourceFileName = file.getOriginalFilename(); UUID uuid = UUID.randomUUID(); - String recipeSourceServername = uuid+recipeSourceFileName; + String recipeSourceServername = uuid + recipeSourceFileName; + String recipeSourceSave = "https://" + bucket + "/recipe/" + recipeSourceServername; - String recipeSourceSave = "https://"+bucket+"/recipe/"+recipeSourceServername; - - - // 2. ์ž‘์„ฑํ•œ ๋‚ด์šฉ ์ €์žฅ RecipeSource recipeSource = new RecipeSource(); recipeSource.setRecipeSourceServername(recipeSourceServername); recipeSource.setRecipeSourceSave(recipeSourceSave); recipeSource.setRecipeSourceFileName(recipeSourceFileName); - - // 3. ์ž๋ฃŒ ํƒ€์ž… ๊ฐ€์ ธ์˜ค๊ธฐ. - RecipeSourceType recipeSourceType; - if(isImageFile(recipeSourceFileName)){ + if(isImageFile(recipeSourceFileName)) { recipeSourceType = recipeSourceTypeRepository.findById(1) .orElseThrow(IllegalArgumentException::new); - }else if (isVideoFile(recipeSourceFileName)){ + } else if (isVideoFile(recipeSourceFileName)) { recipeSourceType = recipeSourceTypeRepository.findById(2) .orElseThrow(IllegalArgumentException::new); - }else{ + } else { throw new IllegalArgumentException("Unsupported file type"); } - // ์•„๋งˆ์กด ์„œ๋ฒ„ ์˜ฌ๋ฆฌ๊ธฐ ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentType(file.getContentType()); objectMetadata.setContentLength(file.getSize()); - amazonS3Client.putObject(bucket,recipeSourceServername,file.getInputStream(),objectMetadata); + amazonS3Client.putObject(bucket, recipeSourceServername, file.getInputStream(), objectMetadata); - // ๋ ˆ์‹œํ”ผ db์— ์ €์žฅ. recipeSource.setRecipeSourceType(recipeSourceType); recipeSource.setRecipe(recipe); recipeSourceRepository.save(recipeSource); break; - }catch (IOException e){ + } catch (IOException e) { System.out.println(e.getMessage()); } - } } @@ -154,30 +141,30 @@ public void createRecipe(RecipeCreateRequest request } @Override - public List getAllRecipes(){ + public List getAllRecipes() { return recipeRepository.findAll().stream() .map(recipe -> mapper.map(recipe, RecipeResponse.class)) .collect(Collectors.toList()); } - public RecipeDetailResponse getRecipe(long id){ - Recipe recipe = recipeRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("recipe not found")); - RecipeDetailResponse response = mapper.map(recipe,RecipeDetailResponse.class); - return response; + @Override + public RecipeDetailResponse getRecipe(long id) { + Recipe recipe = recipeRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("recipe not found")); + return mapper.map(recipe, RecipeDetailResponse.class); } - @Override + @Override public void deleteRecipe(long recipePk) { Recipe recipe = recipeRepository.findByRecipePk(recipePk) .orElseThrow(IllegalArgumentException::new); List sources = recipe.getRecipeSource(); - if(sources!=null && !sources.isEmpty()){ + if(sources != null && !sources.isEmpty()) { for (RecipeSource recipeSource : sources) { - amazonS3Client.deleteObject( new DeleteObjectRequest(bucket,recipeSource.getRecipeSourceServername())); + amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, recipeSource.getRecipeSourceServername())); } } recipeRepository.delete(recipe); - } @Override @@ -186,7 +173,6 @@ public void updateRecipe(RecipeUpdateRequest request, List files) Recipe recipe = recipeRepository.findByRecipePk(request.getRecipePk()) .orElseThrow(() -> new IllegalArgumentException("Recipe not found")); - // ๊ธฐ๋ณธ ํ•„๋“œ ์—…๋ฐ์ดํŠธ if (request.getRecipeName() != null) recipe.setRecipeName(request.getRecipeName()); if (request.getRecipeCookingTime() != 0) recipe.setRecipeCookingTime(request.getRecipeCookingTime()); if (request.getRecipeDifficulty() != 0) recipe.setRecipeDifficulty(request.getRecipeDifficulty()); @@ -195,23 +181,21 @@ public void updateRecipe(RecipeUpdateRequest request, List files) .orElseThrow(() -> new IllegalArgumentException("Category not found"))); } - // ๊ธฐ์กด RecipeSource์™€ ์—…๋กœ๋“œ๋œ ํŒŒ์ผ ๋น„๊ต List sources = recipe.getRecipeSource(); List uploadedFileNames = files.stream() .map(MultipartFile::getOriginalFilename) .toList(); - // ์‚ญ์ œํ•  RecipeSource List recipeSourcesToDelete = sources.stream() .filter(source -> !uploadedFileNames.contains(source.getRecipeSourceFileName())) .toList(); - for (RecipeSource recipeSource : recipeSourcesToDelete) { + + for (RecipeSource recipeSource : recipeSourcesToDelete) { amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, recipeSource.getRecipeSourceServername())); - recipe.getRecipeSource().remove(recipeSource); // ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์—์„œ ์ฐธ์กฐ ์ œ๊ฑฐ - recipeSourceRepository.delete(recipeSource ); + recipe.getRecipeSource().remove(recipeSource); + recipeSourceRepository.delete(recipeSource); } - // ์ถ”๊ฐ€ํ•  ์ƒˆ ํŒŒ์ผ List existingFileNames = sources.stream() .map(RecipeSource::getRecipeSourceFileName) .toList(); @@ -222,7 +206,6 @@ public void updateRecipe(RecipeUpdateRequest request, List files) for (MultipartFile file : filesToAdd) { try { - // ์ƒˆ RecipeSource ์ƒ์„ฑ String recipeSourceFileName = UUID.randomUUID() + "_" + file.getOriginalFilename(); String recipeSourceServername = recipeSourceFileName; String recipeSourceSave = "https://" + bucket + "/recipe/" + recipeSourceServername; @@ -256,8 +239,81 @@ public void updateRecipe(RecipeUpdateRequest request, List files) } } - // ์ตœ์ข… ์ €์žฅ recipeRepository.save(recipe); } + + @Override + public List getRecommendedRecipes(Long userPk) { + List userIngredients = + ingredientMyRefrigeratorRepository.findByUserUserPk(userPk); + List allRecipes = recipeRepository.findAll(); + + return allRecipes.stream() + .map(recipe -> checkRecipeMatch(recipe, userIngredients)) + .filter(RecipeMatchResult::isMatched) + .map(result -> { + RecipeRecommendResponse response = mapper.map(result.getRecipe(), RecipeRecommendResponse.class); + response.setMatchRate(result.getMatchRate()); + response.setRemainExpirationDays(result.getRemainExpirationDays()); + response.setUrgentIngredientName(result.getUrgentIngredientName()); + return response; + }) + .sorted(Comparator.comparingLong(RecipeRecommendResponse::getRemainExpirationDays)) + .collect(Collectors.toList()); + } + + private RecipeMatchResult checkRecipeMatch(Recipe recipe, List userIngredients) { + List recipeIngredients = recipeIngredientRepository.findByRecipe(recipe); + LocalDate currentDate = LocalDate.now(); + + if (recipeIngredients.isEmpty()) { + return new RecipeMatchResult(recipe, false, 0, 0, null); + } + + boolean hasAllNecessaryIngredients = true; + int matchedCount = 0; + long shortestRemainDays = Long.MAX_VALUE; + String urgentIngredientName = null; + + for (RecipeIngredient recipeIngredient : recipeIngredients) { + boolean hasIngredient = false; + + for (IngredientMyRefrigerator userIngredient : userIngredients) { + if (userIngredient.getIngredientManagement().getIngredientManagementPk() == + recipeIngredient.getIngredientManagement().getIngredientManagementPk()) { + + hasIngredient = true; + matchedCount++; + + // ๋‚จ์€ ์ผ์ˆ˜ ๊ณ„์‚ฐ + LocalDate expirationDate = LocalDate.parse(userIngredient.getExpirationDate()); + long remainDays = ChronoUnit.DAYS.between(currentDate, expirationDate); + + // ๋” ์งง์€ ์œ ํ†ต๊ธฐํ•œ ๋ฐœ๊ฒฌ์‹œ ์ •๋ณด ์—…๋ฐ์ดํŠธ + if (remainDays < shortestRemainDays) { + shortestRemainDays = remainDays; + urgentIngredientName = userIngredient.getIngredientManagement().getIngredientName(); + } + break; + } + } + + if (recipeIngredient.isIngredientIsNecessary() && !hasIngredient) { + hasAllNecessaryIngredients = false; + break; + } + } + + double matchRate = ((double) matchedCount / recipeIngredients.size()) * 100; + boolean isMatched = hasAllNecessaryIngredients && matchRate >= 66; + + return new RecipeMatchResult( + recipe, + isMatched, + matchRate, + isMatched ? shortestRemainDays : 0, + isMatched ? urgentIngredientName : null + ); + } } \ No newline at end of file