From b210404147886e0cb611b1ca6b48781ffe528971 Mon Sep 17 00:00:00 2001 From: yunjaeeun Date: Fri, 13 Dec 2024 18:14:51 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=ED=85=9C=ED=94=8C=EB=A6=BF=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug_report.md | 24 ++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 16 +++++++++++ .github/pull_request_template.md | 34 +++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/pull_request_template.md 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๊ณผ ๊ด€๋ จํ•ด ๋ฆฌ๋ทฐ์–ด๊ฐ€ ์•Œ์•„์•ผ ํ•  ์ถ”๊ฐ€ ๋‚ด์šฉ์ด ์žˆ๋‹ค๋ฉด ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. From 31acc1156ce31528af8dc114e9f2fe237ec4fb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A4=ED=9D=AC=EC=A4=80?= Date: Sun, 15 Dec 2024 19:45:36 +0900 Subject: [PATCH 2/3] =?UTF-8?q?Feat=20:=20=EC=9E=AC=EB=A3=8C=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=20=EB=A0=88=EC=8B=9C=ED=94=BC=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?1.=20=EC=9C=A0=EC=A0=80=EA=B0=80=20=EB=93=B1=EB=A1=9D=ED=95=9C?= =?UTF-8?q?=20=EC=9E=AC=EB=A3=8C=EB=A5=BC=20=EA=B8=B0=EB=B0=98=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A1=B0=ED=9A=8C=202.=20=ED=95=84=EC=88=98?= =?UTF-8?q?=EC=9E=AC=EB=A3=8C=EB=8A=94=20=EB=AA=A8=EB=91=90=20=EC=A1=B4?= =?UTF-8?q?=EC=9E=AC=ED=95=B4=EC=95=BC=ED=95=A8=203.=20=EC=A0=84=EC=B2=B4?= =?UTF-8?q?=EC=9E=AC=EB=A3=8C=20=EC=97=90=EC=84=A0=2066%=20=EC=9D=B4?= =?UTF-8?q?=EC=83=81=EB=A7=8C=20=EC=9E=88=EC=9C=BC=EB=A9=B4=20=EB=90=8C=20?= =?UTF-8?q?4.=20=EC=9C=A0=ED=86=B5=EA=B8=B0=ED=95=9C=EC=9D=B4=20=EA=B0=80?= =?UTF-8?q?=EC=9E=A5=20=EA=B8=B4=EB=B0=95=ED=95=9C=20=EC=9E=AC=EB=A3=8C?= =?UTF-8?q?=EB=AA=85=20,=20=EB=82=A0=EC=A7=9C=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=B4=20Related=20to=20:=20#13?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.idea/modules/refrigerator.main.iml | 3 - .../controller/recipe/RecipeController.java | 6 + .../dto/recipe/RecipeMatchResult.java | 15 ++ .../response/RecipeRecommendResponse.java | 14 ++ .../recipe/RecipeIngredientRepository.java | 13 + .../ReplacableIngredientRepository.java | 13 + .../service/recipe/RecipeService.java | 2 + .../service/recipe/RecipeServiceImpl.java | 229 +++++++++++------- 8 files changed, 208 insertions(+), 87 deletions(-) create mode 100644 refrigerator/src/main/java/moja/refrigerator/dto/recipe/RecipeMatchResult.java create mode 100644 refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeRecommendResponse.java create mode 100644 refrigerator/src/main/java/moja/refrigerator/repository/recipe/RecipeIngredientRepository.java create mode 100644 refrigerator/src/main/java/moja/refrigerator/repository/recipe/ReplacableIngredientRepository.java diff --git a/refrigerator/.idea/modules/refrigerator.main.iml b/refrigerator/.idea/modules/refrigerator.main.iml index 397c268..2d53824 100644 --- a/refrigerator/.idea/modules/refrigerator.main.iml +++ b/refrigerator/.idea/modules/refrigerator.main.iml @@ -1,9 +1,6 @@ - - - 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..924af00 --- /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 String urgentIngredient; // ๊ฐ€์žฅ ์‹œ๊ธ‰ํ•œ ์žฌ๋ฃŒ๋ช… + private String urgentExpirationDate; // ํ•ด๋‹น ์žฌ๋ฃŒ์˜ ์œ ํ†ต๊ธฐํ•œ +} \ 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..46bf0c7 --- /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 String urgentIngredient; + private String urgentExpirationDate; +} \ 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..9ec26a2 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,115 @@ 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.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 recipeSourceSave = "https://"+bucket+"/recipe/"+recipeSourceServername; + String recipeSourceServername = uuid + recipeSourceFileName; + 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 +139,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 +171,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 +179,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 +204,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 +237,88 @@ 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.setUrgentIngredient(result.getUrgentIngredient()); // ์ถ”๊ฐ€ + response.setUrgentExpirationDate(result.getUrgentExpirationDate()); // ์ถ”๊ฐ€ + return response; + }) + .collect(Collectors.toList()); + } + + private RecipeMatchResult checkRecipeMatch(Recipe recipe, List userIngredients) { + List recipeIngredients = recipeIngredientRepository.findByRecipe(recipe); + + if (recipeIngredients.isEmpty()) { + return new RecipeMatchResult(recipe, false, 0, null, null); + } + + // ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์ง„ ์žฌ๋ฃŒ์˜ ID ๋ชฉ๋ก๊ณผ ์œ ํ†ต๊ธฐํ•œ ์ •๋ณด + Map userIngredientMap = new HashMap<>(); + for (IngredientMyRefrigerator ingredient : userIngredients) { + Long ingredientId = ingredient.getIngredientManagement().getIngredientManagementPk(); + userIngredientMap.put(ingredientId, ingredient); + } + + // 1. ํ•„์ˆ˜ ์žฌ๋ฃŒ ์ฒดํฌ + List necessaryIngredients = recipeIngredients.stream() + .filter(RecipeIngredient::isIngredientIsNecessary) + .toList(); + + + boolean hasAllNecessaryIngredients = true; + for (RecipeIngredient necessary : necessaryIngredients) { + Long necessaryIngredientId = necessary.getIngredientManagement().getIngredientManagementPk(); + if (!userIngredientMap.containsKey(necessaryIngredientId)) { + hasAllNecessaryIngredients = false; + break; + } + } + + // 2. ์ „์ฒด ์žฌ๋ฃŒ ๋งค์นญ๋ฅ  ๊ณ„์‚ฐ + long matchedCount = recipeIngredients.stream() + .filter(ri -> userIngredientMap.containsKey( + ri.getIngredientManagement().getIngredientManagementPk())) + .count(); + + double matchRate = (double) matchedCount / recipeIngredients.size() * 100; + + // 3. ๊ฐ€์žฅ ์œ ํ†ต๊ธฐํ•œ์ด ์ž„๋ฐ•ํ•œ ์žฌ๋ฃŒ ์ฐพ๊ธฐ + String urgentIngredient = null; + String urgentExpirationDate = null; + + if (hasAllNecessaryIngredients && matchRate >= 66) { + Optional mostUrgentIngredient = recipeIngredients.stream() + .map(ri -> ri.getIngredientManagement().getIngredientManagementPk()) + .filter(userIngredientMap::containsKey) + .map(userIngredientMap::get) + .min(Comparator.comparing(IngredientMyRefrigerator::getExpirationDate)); + + if (mostUrgentIngredient.isPresent()) { + IngredientMyRefrigerator urgent = mostUrgentIngredient.get(); + urgentIngredient = urgent.getIngredientManagement().getIngredientName(); + urgentExpirationDate = urgent.getExpirationDate(); + } + } + + // 4. ์ตœ์ข… ํŒ๋‹จ: ํ•„์ˆ˜ ์žฌ๋ฃŒ ๋ชจ๋‘ ์žˆ๊ณ  ๋งค์นญ๋ฅ  66% ์ด์ƒ + boolean isMatched = hasAllNecessaryIngredients && matchRate >= 66; + + return new RecipeMatchResult(recipe, isMatched, matchRate, urgentIngredient, urgentExpirationDate); + } } \ No newline at end of file From 0bf19f1cd828c334949eeebb2216fb7d588b5111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A4=ED=9D=AC=EC=A4=80?= Date: Sun, 15 Dec 2024 23:31:59 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=20Refactor=20:=20=EC=9E=AC=EB=A3=8C?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EB=A0=88=EC=8B=9C=ED=94=BC=20=EC=B6=94?= =?UTF-8?q?=EC=B2=9C=201.=20=EC=BD=94=EB=93=9C=20=EA=B0=84=EA=B2=B0?= =?UTF-8?q?=ED=99=94=202.=20=EC=9C=A0=ED=86=B5=EA=B8=B0=ED=95=9C=20?= =?UTF-8?q?=EA=B8=B4=EB=B0=95=ED=95=9C=20=EC=9E=AC=EB=A3=8C=EB=AA=85,=20?= =?UTF-8?q?=EB=82=A8=EC=9D=80=20=EC=9D=BC=EC=88=98=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aggregate/recipe/RecipeIngredient.java | 2 + .../dto/recipe/RecipeMatchResult.java | 4 +- .../response/RecipeRecommendResponse.java | 4 +- .../service/recipe/RecipeServiceImpl.java | 87 +++++++++---------- 4 files changed, 47 insertions(+), 50 deletions(-) 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/dto/recipe/RecipeMatchResult.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/RecipeMatchResult.java index 924af00..210e602 100644 --- a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/RecipeMatchResult.java +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/RecipeMatchResult.java @@ -10,6 +10,6 @@ public class RecipeMatchResult { private Recipe recipe; private boolean matched; private double matchRate; - private String urgentIngredient; // ๊ฐ€์žฅ ์‹œ๊ธ‰ํ•œ ์žฌ๋ฃŒ๋ช… - private String urgentExpirationDate; // ํ•ด๋‹น ์žฌ๋ฃŒ์˜ ์œ ํ†ต๊ธฐํ•œ + 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 index 46bf0c7..dbb6019 100644 --- a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeRecommendResponse.java +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeRecommendResponse.java @@ -9,6 +9,6 @@ public class RecipeRecommendResponse { private String recipeContent; private int recipeCookingTime; private double matchRate; - private String urgentIngredient; - private String urgentExpirationDate; + private long remainExpirationDays; + private String urgentIngredientName; } \ No newline at end of file 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 9ec26a2..9f53d69 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeServiceImpl.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeServiceImpl.java @@ -24,6 +24,8 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; @@ -245,7 +247,6 @@ public void updateRecipe(RecipeUpdateRequest request, List files) public List getRecommendedRecipes(Long userPk) { List userIngredients = ingredientMyRefrigeratorRepository.findByUserUserPk(userPk); - List allRecipes = recipeRepository.findAll(); return allRecipes.stream() @@ -254,71 +255,65 @@ public List getRecommendedRecipes(Long userPk) { .map(result -> { RecipeRecommendResponse response = mapper.map(result.getRecipe(), RecipeRecommendResponse.class); response.setMatchRate(result.getMatchRate()); - response.setUrgentIngredient(result.getUrgentIngredient()); // ์ถ”๊ฐ€ - response.setUrgentExpirationDate(result.getUrgentExpirationDate()); // ์ถ”๊ฐ€ + 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, null, null); + return new RecipeMatchResult(recipe, false, 0, 0, null); } - // ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ€์ง„ ์žฌ๋ฃŒ์˜ ID ๋ชฉ๋ก๊ณผ ์œ ํ†ต๊ธฐํ•œ ์ •๋ณด - Map userIngredientMap = new HashMap<>(); - for (IngredientMyRefrigerator ingredient : userIngredients) { - Long ingredientId = ingredient.getIngredientManagement().getIngredientManagementPk(); - userIngredientMap.put(ingredientId, ingredient); - } + boolean hasAllNecessaryIngredients = true; + int matchedCount = 0; + long shortestRemainDays = Long.MAX_VALUE; + String urgentIngredientName = null; - // 1. ํ•„์ˆ˜ ์žฌ๋ฃŒ ์ฒดํฌ - List necessaryIngredients = recipeIngredients.stream() - .filter(RecipeIngredient::isIngredientIsNecessary) - .toList(); + for (RecipeIngredient recipeIngredient : recipeIngredients) { + boolean hasIngredient = false; + for (IngredientMyRefrigerator userIngredient : userIngredients) { + if (userIngredient.getIngredientManagement().getIngredientManagementPk() == + recipeIngredient.getIngredientManagement().getIngredientManagementPk()) { - boolean hasAllNecessaryIngredients = true; - for (RecipeIngredient necessary : necessaryIngredients) { - Long necessaryIngredientId = necessary.getIngredientManagement().getIngredientManagementPk(); - if (!userIngredientMap.containsKey(necessaryIngredientId)) { - hasAllNecessaryIngredients = false; - break; + 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; + } } - } - // 2. ์ „์ฒด ์žฌ๋ฃŒ ๋งค์นญ๋ฅ  ๊ณ„์‚ฐ - long matchedCount = recipeIngredients.stream() - .filter(ri -> userIngredientMap.containsKey( - ri.getIngredientManagement().getIngredientManagementPk())) - .count(); - - double matchRate = (double) matchedCount / recipeIngredients.size() * 100; - - // 3. ๊ฐ€์žฅ ์œ ํ†ต๊ธฐํ•œ์ด ์ž„๋ฐ•ํ•œ ์žฌ๋ฃŒ ์ฐพ๊ธฐ - String urgentIngredient = null; - String urgentExpirationDate = null; - - if (hasAllNecessaryIngredients && matchRate >= 66) { - Optional mostUrgentIngredient = recipeIngredients.stream() - .map(ri -> ri.getIngredientManagement().getIngredientManagementPk()) - .filter(userIngredientMap::containsKey) - .map(userIngredientMap::get) - .min(Comparator.comparing(IngredientMyRefrigerator::getExpirationDate)); - - if (mostUrgentIngredient.isPresent()) { - IngredientMyRefrigerator urgent = mostUrgentIngredient.get(); - urgentIngredient = urgent.getIngredientManagement().getIngredientName(); - urgentExpirationDate = urgent.getExpirationDate(); + if (recipeIngredient.isIngredientIsNecessary() && !hasIngredient) { + hasAllNecessaryIngredients = false; + break; } } - // 4. ์ตœ์ข… ํŒ๋‹จ: ํ•„์ˆ˜ ์žฌ๋ฃŒ ๋ชจ๋‘ ์žˆ๊ณ  ๋งค์นญ๋ฅ  66% ์ด์ƒ + double matchRate = ((double) matchedCount / recipeIngredients.size()) * 100; boolean isMatched = hasAllNecessaryIngredients && matchRate >= 66; - return new RecipeMatchResult(recipe, isMatched, matchRate, urgentIngredient, urgentExpirationDate); + return new RecipeMatchResult( + recipe, + isMatched, + matchRate, + isMatched ? shortestRemainDays : 0, + isMatched ? urgentIngredientName : null + ); } } \ No newline at end of file