diff --git a/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeStep.java b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeStep.java index 505ee16..8d13e90 100644 --- a/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeStep.java +++ b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeStep.java @@ -1,6 +1,7 @@ package moja.refrigerator.aggregate.recipe; import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonManagedReference; import jakarta.persistence.*; import lombok.Data; @@ -25,8 +26,8 @@ public class RecipeStep { @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JsonManagedReference - @JoinColumn(name = "recipe_Source_pk") - private RecipeSource recipeSources; + @JoinColumn(name = "recipe_step_source_pk") + private RecipeStepSource recipeStepSource; @ManyToOne @JoinColumn(name = "recipe_pk") diff --git a/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeStepSource.java b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeStepSource.java new file mode 100644 index 0000000..fcd7090 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/aggregate/recipe/RecipeStepSource.java @@ -0,0 +1,41 @@ +package moja.refrigerator.aggregate.recipe; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name="tbl_recipe_step_source") +public class RecipeStepSource { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "recipe_step_source_pk") + private long recipeStepSourcePk; + + @Column(name = "recipe_step_source_save") // 저장위치 + private String recipeStepSourceSave; + + @Column(name = "recipe_step_source_create_time") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd", timezone = "Asia/Seoul") + private LocalDateTime recipeStepSourceCreateTime = LocalDateTime.now(); + + @Column(name = "recipe_step_source_file_name",nullable = false) // 저장 파일 명 + private String recipeStepSourceFileName; + + @Column(name = "recipe_step_source_servername") // 서버 저장한 이름 추가. + private String recipeStepSourceServername; + + @JoinColumn(name = "recipe_step_source_type") // 자료타입 동영상 or 사진 + @ManyToOne + private RecipeSourceType recipeStepSourceType; + + @JsonBackReference + @OneToOne(fetch = FetchType.EAGER) + @JoinColumn(name="recipe_step_pk") + private RecipeStep recipeStep; +} 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 3752bff..c619f98 100644 --- a/refrigerator/src/main/java/moja/refrigerator/controller/recipe/RecipeController.java +++ b/refrigerator/src/main/java/moja/refrigerator/controller/recipe/RecipeController.java @@ -27,13 +27,13 @@ public RecipeController(RecipeService recipeService) { } @PostMapping - public void createRecipe( + public Recipe createRecipe( @RequestPart RecipeCreateRequest request ,@RequestPart (name="recipeSources",required =false) List recipeSources ,@RequestPart (name="recipeStepSources",required =false) List recipeStepSources ){ - recipeService.createRecipe(request + return recipeService.createRecipe(request , recipeSources , recipeStepSources ); @@ -53,12 +53,12 @@ public void deleteRecipe(@RequestParam long recipePk){ } @PutMapping - public void updateRecipe( + public Recipe updateRecipe( @RequestPart RecipeUpdateRequest request ,@RequestPart (name="recipeSources",required =false) List recipeSources ,@RequestPart (name="recipeStepSources",required =false) List recipeStepSources ){ - recipeService.updateRecipe(request,recipeSources,recipeStepSources); + return recipeService.updateRecipe(request,recipeSources,recipeStepSources); } @GetMapping("/recommend") diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepSourceRequest.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepSourceRequest.java new file mode 100644 index 0000000..a306892 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepSourceRequest.java @@ -0,0 +1,18 @@ +package moja.refrigerator.dto.recipe.request; + +import lombok.Data; +import moja.refrigerator.aggregate.recipe.RecipeSourceType; + +import java.time.LocalDateTime; + +@Data +public class RecipeStepSourceRequest { + private long recipeStepSourcePk; + private String recipeStepSourceSave; + private LocalDateTime recipeStepSourceCreateTime; + private String recipeStepSourceFileName; + private String recipeStepSourceServername; + private RecipeSourceType recipeStepSourceType; + + +} 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 index 67b1d78..e56a406 100644 --- a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepUpdateRequest.java +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/request/RecipeStepUpdateRequest.java @@ -7,4 +7,5 @@ public class RecipeStepUpdateRequest { private Long recipeStepPk; private int recipeStepOrder; // JSON에 있는 필드와 이름 동일해야 함 private String recipeStepContent; // JSON의 recipeStepContent와 일치 + private RecipeStepSourceRequest recipeStepSource; } 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 4ac9525..c2038b0 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 @@ -23,7 +23,8 @@ public class RecipeResponse { private LocalDateTime recipeUpdateTime; private List recipeSource; - private List recipeStep; + + private List recipeStep; private User user; private RecipeCategory recipeCategory; diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeStepResponse.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeStepResponse.java new file mode 100644 index 0000000..1a52f2b --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeStepResponse.java @@ -0,0 +1,12 @@ +package moja.refrigerator.dto.recipe.response; + +import lombok.Data; + +@Data +public class RecipeStepResponse { + + private int recipeStepOrder; + private String recipeStepContent; + private RecipeStepSourceResponse recipeStepSource; + +} diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeStepSourceResponse.java b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeStepSourceResponse.java new file mode 100644 index 0000000..5b51094 --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/dto/recipe/response/RecipeStepSourceResponse.java @@ -0,0 +1,16 @@ +package moja.refrigerator.dto.recipe.response; + +import lombok.Data; +import moja.refrigerator.aggregate.recipe.RecipeSourceType; + +import java.time.LocalDateTime; + +@Data +public class RecipeStepSourceResponse { + private long recipeStepSourcePk; + private String recipeStepSourceSave; + private LocalDateTime recipeStepSourceCreateTime; + private String recipeStepSourceFileName; + private String recipeStepSourceServername; + private RecipeSourceType recipeStepSourceType; +} diff --git a/refrigerator/src/main/java/moja/refrigerator/repository/recipe/RecipeStepSourceRepository.java b/refrigerator/src/main/java/moja/refrigerator/repository/recipe/RecipeStepSourceRepository.java new file mode 100644 index 0000000..867b71d --- /dev/null +++ b/refrigerator/src/main/java/moja/refrigerator/repository/recipe/RecipeStepSourceRepository.java @@ -0,0 +1,8 @@ +package moja.refrigerator.repository.recipe; + +import moja.refrigerator.aggregate.recipe.RecipeIngredient; +import moja.refrigerator.aggregate.recipe.RecipeStepSource; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RecipeStepSourceRepository 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 44fe069..4d6f885 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeService.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeService.java @@ -15,14 +15,14 @@ import java.util.List; public interface RecipeService { - void createRecipe(RecipeCreateRequest request + Recipe createRecipe(RecipeCreateRequest request ,List recipeSources ,List recipeStepSources ); List getAllRecipes(); RecipeDetailResponse getRecipe(long id); void deleteRecipe(long recipePk); - void updateRecipe( + Recipe updateRecipe( RecipeUpdateRequest request ,List recipeSources ,List recipeStepSources 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 70a17f6..671926d 100644 --- a/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeServiceImpl.java +++ b/refrigerator/src/main/java/moja/refrigerator/service/recipe/RecipeServiceImpl.java @@ -26,6 +26,11 @@ import java.util.*; import java.util.stream.Collectors; +/** + * Implementation of the RecipeService interface, providing business logic for managing recipes. + * This class interacts with various repositories for CRUD operations related to recipes, ingredients, files, and user data, + * as well as Amazon S3 for file storage. + */ @Service public class RecipeServiceImpl implements RecipeService { @@ -41,6 +46,7 @@ public class RecipeServiceImpl implements RecipeService { private final ReplacableIngredientRepository replacableIngredientRepository; private final RecipeLikeDislikeRepository recipeLikeDislikeRepository; private final RecipeStepRepository recipeStepRepository; + private final RecipeStepSourceRepository recipeStepSourceRepository; @Value("${cloud.aws.s3.bucket}") private String bucket; @@ -58,7 +64,8 @@ public RecipeServiceImpl( RecipeIngredientRepository recipeIngredientRepository, ReplacableIngredientRepository replacableIngredientRepository, RecipeLikeDislikeRepository recipeLikeDislikeRepository, - RecipeStepRepository recipeStepRepository + RecipeStepRepository recipeStepRepository, + RecipeStepSourceRepository recipeStepSourceRepository ) { this.recipeRepository = recipeRepository; this.userRepository = userRepository; @@ -72,6 +79,7 @@ public RecipeServiceImpl( this.replacableIngredientRepository = replacableIngredientRepository; this.recipeLikeDislikeRepository = recipeLikeDislikeRepository; this.recipeStepRepository = recipeStepRepository; + this.recipeStepSourceRepository = recipeStepSourceRepository; } private boolean isImageFile(String fileName) { String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); @@ -83,7 +91,7 @@ private boolean isVideoFile(String fileName) { return List.of("mp4", "avi", "mov", "wmv").contains(extension); } // 파일 처리 전용 메서드 - private RecipeSource processAndSaveFile(MultipartFile file, Recipe recipe, String pathPrefix) { + private RecipeSource RecipeSourceSaveFile(MultipartFile file, Recipe recipe, String pathPrefix) { try { if (file == null || file.isEmpty()) { return null; @@ -118,6 +126,7 @@ private RecipeSource processAndSaveFile(MultipartFile file, Recipe recipe, Strin recipeSource.setRecipeSourceType(recipeSourceType); recipeSource.setRecipe(recipe); + // AWS S3에 파일 업로드 ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentType(file.getContentType()); @@ -132,11 +141,60 @@ private RecipeSource processAndSaveFile(MultipartFile file, Recipe recipe, Strin } } + private RecipeStepSource RecipeStepSourceSaveFile(MultipartFile file, RecipeStep recipeStep, 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 생성 및 설정 + RecipeStepSource recipeSource = new RecipeStepSource(); + recipeSource.setRecipeStepSourceServername(serverFileName); + recipeSource.setRecipeStepSourceSave(fileUrl); + recipeSource.setRecipeStepSourceFileName(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.setRecipeStepSourceType(recipeSourceType); + recipeSource.setRecipeStep(recipeStep); + + // AWS S3에 파일 업로드 + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(file.getContentType()); + metadata.setContentLength(file.getSize()); + amazonS3Client.putObject(bucket, pathPrefix + serverFileName, file.getInputStream(), metadata); + + // RecipeSource 저장 + recipeStepSourceRepository.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 + public Recipe createRecipe(RecipeCreateRequest request ,List recipeSources ,List recipeStepSources ) { @@ -156,7 +214,8 @@ public void createRecipe(RecipeCreateRequest request if(recipeSources != null && !recipeSources.isEmpty()) { for(MultipartFile file : recipeSources) { - processAndSaveFile(file, recipe, "recipe/"); + RecipeSource recipeSource = RecipeSourceSaveFile(file, recipe, "recipe/"); + recipe.getRecipeSource().add(recipeSource); } } @@ -175,16 +234,16 @@ public void createRecipe(RecipeCreateRequest request if (recipeStepSources != null && recipeStepSources.size() > i) { System.out.println("------------------------------"); MultipartFile file = recipeStepSources.get(i); - RecipeSource stepSource = processAndSaveFile(file, recipe, "recipe/step/"); - newStep.setRecipeSources(stepSource); // 파일 매핑 + RecipeStepSource stepSource = RecipeStepSourceSaveFile(file, newStep, "recipe/step/"); + newStep.setRecipeStepSource(stepSource); // 파일 매핑 } recipe.getRecipeStep().add(newStep); } } - recipeRepository.save(recipe); + return recipe; } @Override @@ -213,8 +272,11 @@ public void deleteRecipe(long recipePk) { } List steps = recipe.getRecipeStep(); if(steps != null && !steps.isEmpty()) { - for (RecipeSource recipeSource : sources) { - amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, "recipe/step/"+recipeSource.getRecipeSourceServername())); + for (RecipeStep recipeStep : steps) { + RecipeStepSource stepSource = recipeStep.getRecipeStepSource(); + if(stepSource != null) { + amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, "recipe/step/"+stepSource.getRecipeStepSourceServername())); + } } } recipeRepository.delete(recipe); @@ -224,7 +286,7 @@ public void deleteRecipe(long recipePk) { @Override @Transactional - public void updateRecipe(RecipeUpdateRequest request + public Recipe updateRecipe(RecipeUpdateRequest request ,List recipeSources ,List recipeStepSources ) { @@ -300,8 +362,12 @@ public void updateRecipe(RecipeUpdateRequest request //시간 문제로 레시피 step 전부 삭제 후 생성하는거로 대처 List steps = recipe.getRecipeStep(); if(steps != null && !steps.isEmpty()) { - for (RecipeSource recipeSource : sources) { - amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, "recipe/step/"+recipeSource.getRecipeSourceServername())); + for (RecipeStep step : steps) { + RecipeStepSource stepSource = step.getRecipeStepSource(); + if(stepSource != null) { + amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, "recipe/step/"+stepSource.getRecipeStepSourceServername())); + + } } //기존 사진 소스 다 삭제 후 recipe.getRecipeStep().clear(); @@ -320,19 +386,19 @@ public void updateRecipe(RecipeUpdateRequest request newStep.setRecipe(recipe); if (recipeStepSources != null && recipeStepSources.size() > i) { - System.out.println("------------------------------"); + System.out.println("------------------------------22"); MultipartFile file = recipeStepSources.get(i); - RecipeSource stepSource = processAndSaveFile(file, recipe, "recipe/step/"); - newStep.setRecipeSources(stepSource); // 파일 매핑 + RecipeStepSource stepSource = RecipeStepSourceSaveFile(file, newStep, "recipe/step/"); + newStep.setRecipeStepSource(stepSource); // 파일 매핑 } recipe.getRecipeStep().add(newStep); } } - - recipeRepository.save(recipe); + + return recipe; }