diff --git a/refrigerator/.idea/modules.xml b/refrigerator/.idea/modules.xml index bdc72c3..738a4c5 100644 --- a/refrigerator/.idea/modules.xml +++ b/refrigerator/.idea/modules.xml @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/refrigerator/.idea/modules/refrigerator.main.iml b/refrigerator/.idea/modules/refrigerator.main.iml index afc1871..1cbf656 100644 --- a/refrigerator/.idea/modules/refrigerator.main.iml +++ b/refrigerator/.idea/modules/refrigerator.main.iml @@ -5,4 +5,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/Recipe.java b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/Recipe.java index 575058e..5c8068b 100644 --- a/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/Recipe.java +++ b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/Recipe.java @@ -56,6 +56,10 @@ public class Recipe { @JsonManagedReference private List recipeSource = new ArrayList<>() ; // 여러 Source가 들어갈 수 있으니까 list로 수정 + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true,fetch = FetchType.LAZY) + @JsonManagedReference + private List recipeStep = new ArrayList<>(); + @JoinColumn(name = "recipe_category") @ManyToOne diff --git a/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeSource.java b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeSource.java index c69c19b..8862847 100644 --- a/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeSource.java +++ b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeSource.java @@ -8,6 +8,7 @@ import lombok.Data; import java.time.LocalDateTime; +import java.util.List; @Data @Entity diff --git a/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeStep.java b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeStep.java new file mode 100644 index 0000000..505ee16 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeStep.java @@ -0,0 +1,36 @@ +package moja.refrigerator.aggregate.recipe; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.*; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +@Entity +@Table(name = "tbl_recipe_step") +public class RecipeStep { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "recipe_step_pk") + private long recipeStepPk; + + @Column(name = "recipe_step_order") + private int recipeStepOrder; + + @Column(name = "recipe_step_content") + private String recipeStepContent; + + @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JsonManagedReference + @JoinColumn(name = "recipe_Source_pk") + private RecipeSource recipeSources; + + @ManyToOne + @JoinColumn(name = "recipe_pk") + @JsonBackReference + private Recipe recipe; + +} 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 03ecb0a..3752bff 100644 --- a/refrigerator/src/main/java/moja/refrigerator/controller/recipe/RecipeController.java +++ b/refrigerator/src/main/java/moja/refrigerator/controller/recipe/RecipeController.java @@ -29,10 +29,13 @@ public RecipeController(RecipeService recipeService) { @PostMapping public void createRecipe( @RequestPart RecipeCreateRequest request - , @RequestPart (required =false) List files + ,@RequestPart (name="recipeSources",required =false) List recipeSources + ,@RequestPart (name="recipeStepSources",required =false) List recipeStepSources ){ + recipeService.createRecipe(request - , files + , recipeSources + , recipeStepSources ); } @@ -52,9 +55,10 @@ public void deleteRecipe(@RequestParam long recipePk){ @PutMapping public void updateRecipe( @RequestPart RecipeUpdateRequest request - ,@RequestPart (required =false) List files + ,@RequestPart (name="recipeSources",required =false) List recipeSources + ,@RequestPart (name="recipeStepSources",required =false) List recipeStepSources ){ - recipeService.updateRecipe(request,files); + recipeService.updateRecipe(request,recipeSources,recipeStepSources); } @GetMapping("/recommend") diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeCreateRequest.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeCreateRequest.java index ef8640d..fe1ee5d 100644 --- a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeCreateRequest.java +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeCreateRequest.java @@ -1,9 +1,9 @@ package moja.refrigerator.dto.recipe.request; import lombok.Data; -import moja.refrigerator.aggregate.recipe.RecipeCategory; -import moja.refrigerator.aggregate.recipe.RecipeSource; -import moja.refrigerator.aggregate.user.User; +import moja.refrigerator.aggregate.recipe.RecipeStep; + +import java.util.List; @Data public class RecipeCreateRequest { @@ -11,12 +11,8 @@ public class RecipeCreateRequest { private int recipeCookingTime; private int recipeDifficulty; private String recipeContent; -// private long recipeSource; private int recipeCategoryPk; + private List recipeSteps; private long userPk; -// private long recipePk; // 자동 추가 -// private String recipeCreateTime; //자동 추가 -// private String recipeUpdateTime; //자동 추가 -// private long recipeViews; // 조회 시 올리는 것으로 } diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepRequest.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepRequest.java new file mode 100644 index 0000000..a7acb05 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepRequest.java @@ -0,0 +1,9 @@ +package moja.refrigerator.dto.recipe.request; + +import lombok.Data; + +@Data +public class RecipeStepRequest { + private int recipeStepOrder; // JSON에 있는 필드와 이름 동일해야 함 + private String recipeStepContent; // JSON의 recipeStepContent와 일치 +} diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepUpdateRequest.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepUpdateRequest.java new file mode 100644 index 0000000..67b1d78 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepUpdateRequest.java @@ -0,0 +1,10 @@ +package moja.refrigerator.dto.recipe.request; + +import lombok.Data; + +@Data +public class RecipeStepUpdateRequest { + private Long recipeStepPk; + private int recipeStepOrder; // JSON에 있는 필드와 이름 동일해야 함 + private String recipeStepContent; // JSON의 recipeStepContent와 일치 +} diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeUpdateRequest.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeUpdateRequest.java index dd7c2d9..da9bea1 100644 --- a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeUpdateRequest.java +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeUpdateRequest.java @@ -3,6 +3,8 @@ import lombok.Data; +import java.util.List; + @Data public class RecipeUpdateRequest { private long recipePk; @@ -13,5 +15,6 @@ public class RecipeUpdateRequest { private String recipeSource; private String recipeCategory; private String userPk; + private List recipeSteps; } diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeResponse.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeResponse.java index 5686857..4ac9525 100644 --- a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeResponse.java +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeResponse.java @@ -3,6 +3,7 @@ import lombok.Data; import moja.refrigerator.aggregate.recipe.RecipeCategory; import moja.refrigerator.aggregate.recipe.RecipeSource; +import moja.refrigerator.aggregate.recipe.RecipeStep; import moja.refrigerator.aggregate.user.User; import java.time.LocalDateTime; @@ -22,6 +23,7 @@ public class RecipeResponse { private LocalDateTime recipeUpdateTime; private List recipeSource; + private List recipeStep; private User user; private RecipeCategory recipeCategory; diff --git a/refrigerator/src/main/java/moja/refrigerator/repository/recipe/RecipeStepRepository.java b/refrigerator/src/main/java/moja/refrigerator/repository/recipe/RecipeStepRepository.java new file mode 100644 index 0000000..ecfb529 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/repository/recipe/RecipeStepRepository.java @@ -0,0 +1,8 @@ +package moja.refrigerator.repository.recipe; + +import moja.refrigerator.aggregate.recipe.RecipeStep; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RecipeStepRepository extends JpaRepository { + +} 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 81c69a5..44fe069 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeService.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeService.java @@ -16,14 +16,16 @@ public interface RecipeService { void createRecipe(RecipeCreateRequest request - ,List files + ,List recipeSources + ,List recipeStepSources ); List getAllRecipes(); RecipeDetailResponse getRecipe(long id); void deleteRecipe(long recipePk); void updateRecipe( RecipeUpdateRequest request - ,List files + ,List recipeSources + ,List recipeStepSources ); List getRecommendedRecipes(Long userPk); RecipeRecommendResponse getRandomRecipe(); 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 42030fb..70a17f6 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeServiceImpl.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeServiceImpl.java @@ -7,9 +7,7 @@ import moja.refrigerator.aggregate.recipe.*; import moja.refrigerator.aggregate.user.User; import moja.refrigerator.dto.recipe.RecipeMatchResult; -import moja.refrigerator.dto.recipe.request.RecipeCreateRequest; -import moja.refrigerator.dto.recipe.request.RecipeLikeRequest; -import moja.refrigerator.dto.recipe.request.RecipeUpdateRequest; +import moja.refrigerator.dto.recipe.request.*; import moja.refrigerator.dto.recipe.response.*; import moja.refrigerator.repository.ingredient.IngredientMyRefrigeratorRepository; import moja.refrigerator.repository.recipe.*; @@ -42,6 +40,7 @@ public class RecipeServiceImpl implements RecipeService { private final RecipeIngredientRepository recipeIngredientRepository; private final ReplacableIngredientRepository replacableIngredientRepository; private final RecipeLikeDislikeRepository recipeLikeDislikeRepository; + private final RecipeStepRepository recipeStepRepository; @Value("${cloud.aws.s3.bucket}") private String bucket; @@ -58,7 +57,8 @@ public RecipeServiceImpl( IngredientMyRefrigeratorRepository ingredientMyRefrigeratorRepository, RecipeIngredientRepository recipeIngredientRepository, ReplacableIngredientRepository replacableIngredientRepository, - RecipeLikeDislikeRepository recipeLikeDislikeRepository + RecipeLikeDislikeRepository recipeLikeDislikeRepository, + RecipeStepRepository recipeStepRepository ) { this.recipeRepository = recipeRepository; this.userRepository = userRepository; @@ -71,6 +71,7 @@ public RecipeServiceImpl( this.recipeIngredientRepository = recipeIngredientRepository; this.replacableIngredientRepository = replacableIngredientRepository; this.recipeLikeDislikeRepository = recipeLikeDislikeRepository; + this.recipeStepRepository = recipeStepRepository; } private boolean isImageFile(String fileName) { String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); @@ -81,10 +82,64 @@ private boolean isVideoFile(String fileName) { String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); return List.of("mp4", "avi", "mov", "wmv").contains(extension); } + // 파일 처리 전용 메서드 + private RecipeSource processAndSaveFile(MultipartFile file, Recipe recipe, String pathPrefix) { + try { + if (file == null || file.isEmpty()) { + return null; + } + String originalFileName = file.getOriginalFilename(); + if (originalFileName == null) { + throw new IllegalArgumentException("Empty file name is not allowed"); + } + + // 파일 이름 생성 및 저장 경로 설정 + UUID uuid = UUID.randomUUID(); + String serverFileName = uuid + "_" + originalFileName; + String fileUrl = "https://" + bucket + "/" + pathPrefix + serverFileName; + + // RecipeSource 생성 및 설정 + RecipeSource recipeSource = new RecipeSource(); + recipeSource.setRecipeSourceServername(serverFileName); + recipeSource.setRecipeSourceSave(fileUrl); + recipeSource.setRecipeSourceFileName(originalFileName); + + // 파일 타입 처리 + RecipeSourceType recipeSourceType; + if (isImageFile(originalFileName)) { + recipeSourceType = recipeSourceTypeRepository.findById(1) + .orElseThrow(() -> new IllegalArgumentException("Image type not found")); + } else if (isVideoFile(originalFileName)) { + recipeSourceType = recipeSourceTypeRepository.findById(2) + .orElseThrow(() -> new IllegalArgumentException("Video type not found")); + } else { + throw new IllegalArgumentException("Unsupported file type: " + originalFileName); + } + recipeSource.setRecipeSourceType(recipeSourceType); + recipeSource.setRecipe(recipe); + + // AWS S3에 파일 업로드 + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(file.getContentType()); + metadata.setContentLength(file.getSize()); + amazonS3Client.putObject(bucket, pathPrefix + serverFileName, file.getInputStream(), metadata); + + // RecipeSource 저장 + recipeSourceRepository.save(recipeSource); + return recipeSource; + } catch (IOException e) { + throw new RuntimeException("File upload failed: " + e.getMessage(), e); + } + } + + @Override @Transactional(propagation = Propagation.REQUIRED) - public void createRecipe(RecipeCreateRequest request, List files) { + public void createRecipe(RecipeCreateRequest request + ,List recipeSources + ,List recipeStepSources + ) { Recipe recipe = new Recipe(); recipe.setRecipeName(request.getRecipeName()); recipe.setRecipeContent(request.getRecipeContent()); @@ -99,45 +154,36 @@ public void createRecipe(RecipeCreateRequest request, List files) .orElseThrow(IllegalArgumentException::new); recipe.setRecipeCategory(recipeCategory); - if(files != null && !files.isEmpty()) { - for(MultipartFile file : files) { - try { - String recipeSourceFileName = file.getOriginalFilename(); - UUID uuid = UUID.randomUUID(); - String recipeSourceServername = uuid + recipeSourceFileName; - String recipeSourceSave = "https://" + bucket + "/recipe/" + recipeSourceServername; - - RecipeSource recipeSource = new RecipeSource(); - recipeSource.setRecipeSourceServername(recipeSourceServername); - recipeSource.setRecipeSourceSave(recipeSourceSave); - recipeSource.setRecipeSourceFileName(recipeSourceFileName); - - RecipeSourceType recipeSourceType; - if(isImageFile(recipeSourceFileName)) { - recipeSourceType = recipeSourceTypeRepository.findById(1) - .orElseThrow(IllegalArgumentException::new); - } else if (isVideoFile(recipeSourceFileName)) { - recipeSourceType = recipeSourceTypeRepository.findById(2) - .orElseThrow(IllegalArgumentException::new); - } else { - throw new IllegalArgumentException("Unsupported file type"); - } + if(recipeSources != null && !recipeSources.isEmpty()) { + for(MultipartFile file : recipeSources) { + processAndSaveFile(file, recipe, "recipe/"); + } + } - ObjectMetadata objectMetadata = new ObjectMetadata(); - objectMetadata.setContentType(file.getContentType()); - objectMetadata.setContentLength(file.getSize()); - amazonS3Client.putObject(bucket, recipeSourceServername, file.getInputStream(), objectMetadata); - recipeSource.setRecipeSourceType(recipeSourceType); - recipeSource.setRecipe(recipe); - recipeSourceRepository.save(recipeSource); - break; - } catch (IOException e) { - System.out.println(e.getMessage()); + List recipeSteps = request.getRecipeSteps(); + if(recipeSteps != null ) { + for (int i = 0; i < recipeSteps.size(); i++) { + RecipeStepRequest step = recipeSteps.get(i); + + // RecipeStep 저장 + RecipeStep newStep = new RecipeStep(); + newStep.setRecipeStepOrder(i + 1); + newStep.setRecipeStepContent(step.getRecipeStepContent()); + newStep.setRecipe(recipe); + + if (recipeStepSources != null && recipeStepSources.size() > i) { + System.out.println("------------------------------"); + MultipartFile file = recipeStepSources.get(i); + RecipeSource stepSource = processAndSaveFile(file, recipe, "recipe/step/"); + newStep.setRecipeSources(stepSource); // 파일 매핑 } + + recipe.getRecipeStep().add(newStep); } } + recipeRepository.save(recipe); } @@ -162,15 +208,26 @@ public void deleteRecipe(long recipePk) { List sources = recipe.getRecipeSource(); if(sources != null && !sources.isEmpty()) { for (RecipeSource recipeSource : sources) { - amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, recipeSource.getRecipeSourceServername())); + amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, "recipe/"+recipeSource.getRecipeSourceServername())); + } + } + List steps = recipe.getRecipeStep(); + if(steps != null && !steps.isEmpty()) { + for (RecipeSource recipeSource : sources) { + amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, "recipe/step/"+recipeSource.getRecipeSourceServername())); } } recipeRepository.delete(recipe); } + + @Override @Transactional - public void updateRecipe(RecipeUpdateRequest request, List files) { + public void updateRecipe(RecipeUpdateRequest request + ,List recipeSources + ,List recipeStepSources + ) { Recipe recipe = recipeRepository.findByRecipePk(request.getRecipePk()) .orElseThrow(() -> new IllegalArgumentException("Recipe not found")); @@ -183,7 +240,7 @@ public void updateRecipe(RecipeUpdateRequest request, List files) } List sources = recipe.getRecipeSource(); - List uploadedFileNames = files.stream() + List uploadedFileNames = recipeSources.stream() .map(MultipartFile::getOriginalFilename) .toList(); @@ -201,7 +258,7 @@ public void updateRecipe(RecipeUpdateRequest request, List files) .map(RecipeSource::getRecipeSourceFileName) .toList(); - List filesToAdd = files.stream() + List filesToAdd = recipeSources.stream() .filter(file -> !existingFileNames.contains(file.getOriginalFilename())) .toList(); @@ -240,6 +297,41 @@ public void updateRecipe(RecipeUpdateRequest request, List files) } } + //시간 문제로 레시피 step 전부 삭제 후 생성하는거로 대처 + List steps = recipe.getRecipeStep(); + if(steps != null && !steps.isEmpty()) { + for (RecipeSource recipeSource : sources) { + amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, "recipe/step/"+recipeSource.getRecipeSourceServername())); + } + //기존 사진 소스 다 삭제 후 + recipe.getRecipeStep().clear(); + } + + List recipeSteps = request.getRecipeSteps(); + + if(recipeSteps != null ) { + for (int i = 0; i < recipeSteps.size(); i++) { + RecipeStepUpdateRequest step = recipeSteps.get(i); + + // RecipeStep 저장 + RecipeStep newStep = new RecipeStep(); + newStep.setRecipeStepOrder(i + 1); + newStep.setRecipeStepContent(step.getRecipeStepContent()); + newStep.setRecipe(recipe); + + if (recipeStepSources != null && recipeStepSources.size() > i) { + System.out.println("------------------------------"); + MultipartFile file = recipeStepSources.get(i); + RecipeSource stepSource = processAndSaveFile(file, recipe, "recipe/step/"); + newStep.setRecipeSources(stepSource); // 파일 매핑 + } + + recipe.getRecipeStep().add(newStep); + } + } + + + recipeRepository.save(recipe); }