From f0b269e4f682f06e9262e00c62d06bdf5c54687d Mon Sep 17 00:00:00 2001 From: Hanvp Date: Wed, 10 Jan 2024 19:29:46 +0900 Subject: [PATCH] =?UTF-8?q?:sparkles:=20Issue/282=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=9A=A9=20uri=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit controller, service, converter, repository까지 작업 완 --- .../server/aws/s3/AmazonS3Manager.java | 8 + .../zipdabang/server/config/AmazonConfig.java | 6 + .../server/converter/RecipeConverter.java | 209 ++++++++++++++++++ .../server/domain/test/TestComment.java | 39 ++++ .../server/domain/test/TestIngredient.java | 42 ++++ .../server/domain/test/TestLikes.java | 50 +++++ .../server/domain/test/TestRecipe.java | 140 ++++++++++++ .../test/TestRecipeCategoryMapping.java | 39 ++++ .../server/domain/test/TestScrap.java | 50 +++++ .../server/domain/test/TestStep.java | 50 +++++ .../TestIngredientRepository.java | 7 + .../TestRecipeCategoryMappingRepository.java | 7 + .../testRepository/TestRecipeRepository.java | 6 + .../testRepository/TestStepRepository.java | 7 + .../TestRecipeRepositoryCustom.java | 15 ++ .../TestRecipeRepositoryImpl.java | 66 ++++++ .../server/service/RecipeService.java | 7 + .../serviceImpl/RecipeServiceImpl.java | 80 ++++++- .../web/controller/RecipeRestController.java | 69 ++++++ src/main/resources/application.yml | 5 + 20 files changed, 900 insertions(+), 2 deletions(-) create mode 100644 src/main/java/zipdabang/server/domain/test/TestComment.java create mode 100644 src/main/java/zipdabang/server/domain/test/TestIngredient.java create mode 100644 src/main/java/zipdabang/server/domain/test/TestLikes.java create mode 100644 src/main/java/zipdabang/server/domain/test/TestRecipe.java create mode 100644 src/main/java/zipdabang/server/domain/test/TestRecipeCategoryMapping.java create mode 100644 src/main/java/zipdabang/server/domain/test/TestScrap.java create mode 100644 src/main/java/zipdabang/server/domain/test/TestStep.java create mode 100644 src/main/java/zipdabang/server/repository/testRepository/TestIngredientRepository.java create mode 100644 src/main/java/zipdabang/server/repository/testRepository/TestRecipeCategoryMappingRepository.java create mode 100644 src/main/java/zipdabang/server/repository/testRepository/TestRecipeRepository.java create mode 100644 src/main/java/zipdabang/server/repository/testRepository/TestStepRepository.java create mode 100644 src/main/java/zipdabang/server/repository/testRepository/testRecipeRepositoryCustom/TestRecipeRepositoryCustom.java create mode 100644 src/main/java/zipdabang/server/repository/testRepository/testRecipeRepositoryImpl/TestRecipeRepositoryImpl.java diff --git a/src/main/java/zipdabang/server/aws/s3/AmazonS3Manager.java b/src/main/java/zipdabang/server/aws/s3/AmazonS3Manager.java index aebe3a8..b5e1cb5 100644 --- a/src/main/java/zipdabang/server/aws/s3/AmazonS3Manager.java +++ b/src/main/java/zipdabang/server/aws/s3/AmazonS3Manager.java @@ -69,6 +69,14 @@ public String generateStepKeyName(Uuid uuid) { return amazonConfig.getRecipeStep() + '/' + uuid.getUuid(); } + public String generateTestThumbnailKeyName(Uuid uuid) { + return amazonConfig.getTestThumbnail() + '/' + uuid.getUuid(); + } + + public String generateTestStepKeyName(Uuid uuid) { + return amazonConfig.getTestStep() + '/' + uuid.getUuid(); + } + public String generateInqueryKeyName(Uuid uuid) {return amazonConfig.getInquery() + '/' + uuid.getUuid();} // 중복된 UUID가 있다면 중복이 없을때까지 재귀적으로 동작 diff --git a/src/main/java/zipdabang/server/config/AmazonConfig.java b/src/main/java/zipdabang/server/config/AmazonConfig.java index 55d7990..d6a00e8 100644 --- a/src/main/java/zipdabang/server/config/AmazonConfig.java +++ b/src/main/java/zipdabang/server/config/AmazonConfig.java @@ -35,6 +35,12 @@ public class AmazonConfig { @Value("${cloud.aws.s3.folder.zipdabang-recipe-steps}") private String recipeStep; + @Value("${cloud.aws.s3.folder.zipdabang-test-thumbnail}") + private String testThumbnail; + + @Value("${cloud.aws.s3.folder.zipdabang-test-steps}") + private String testStep; + @Value("${cloud.aws.s3.folder.zipdabang-proifile}") private String userProfile; diff --git a/src/main/java/zipdabang/server/converter/RecipeConverter.java b/src/main/java/zipdabang/server/converter/RecipeConverter.java index e5e5ef1..375cec2 100644 --- a/src/main/java/zipdabang/server/converter/RecipeConverter.java +++ b/src/main/java/zipdabang/server/converter/RecipeConverter.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; @@ -12,6 +13,10 @@ import zipdabang.server.domain.etc.Uuid; import zipdabang.server.domain.member.Member; import zipdabang.server.domain.recipe.*; +import zipdabang.server.domain.test.TestIngredient; +import zipdabang.server.domain.test.TestRecipe; +import zipdabang.server.domain.test.TestRecipeCategoryMapping; +import zipdabang.server.domain.test.TestStep; import zipdabang.server.service.RecipeService; import zipdabang.server.utils.converter.TimeConverter; import zipdabang.server.web.dto.requestDto.RecipeRequestDto; @@ -693,4 +698,208 @@ public static ReportedComment toCommentReport(Report report, Comment comment, Me .build(); } + /** + * 부하 테스트용 + */ +// @Value("${cloud.aws.s3.user-default-image}") +// String userDefaultImage; + + public static RecipeResponseDto.RecipeStatusDto toTestRecipeStatusDto(TestRecipe recipe) { + return RecipeResponseDto.RecipeStatusDto.builder() + .recipeId(recipe.getId()) + .calledAt(staticTimeConverter.ConvertTime(recipe.getCreatedAt())) + .build(); + } + + public static String uploadTestThumbnail(MultipartFile thumbnail) throws IOException { + Uuid uuid = staticAmazonS3Manager.createUUID(); + String keyName = staticAmazonS3Manager.generateTestThumbnailKeyName(uuid); + String fileUrl = staticAmazonS3Manager.uploadFile(keyName, thumbnail); + log.info("S3에 업로드 한 test thumbnail 파일의 url : {}", fileUrl); + return fileUrl; + } + + public static String uploadTestStep(MultipartFile thumbnail) throws IOException { + Uuid uuid = staticAmazonS3Manager.createUUID(); + String keyName = staticAmazonS3Manager.generateTestStepKeyName(uuid); + String fileUrl = staticAmazonS3Manager.uploadFile(keyName, thumbnail); + log.info("S3에 업로드 한 test thumbnail 파일의 url : {}", fileUrl); + return fileUrl; + } + + public static TestRecipe toTestRecipe(RecipeRequestDto.CreateRecipeDto request, MultipartFile thumbnail) throws IOException { + + TestRecipe recipe = TestRecipe.builder() + .isBarista(false) + .name(request.getName()) + .intro(request.getIntro()) + .recipeTip(request.getRecipeTip()) + .time(request.getTime()) + .build(); + + String imageUrl = null; + if(thumbnail != null) + imageUrl = uploadTestThumbnail(thumbnail); + else + throw new RecipeException(CommonStatus.NULL_RECIPE_ERROR); + recipe.setThumbnail(imageUrl); + + return recipe; + } + + public static List toTestRecipeCategory(List categoryIds, TestRecipe recipe) { + return categoryIds.stream() + .map(recipeCategoryId -> toTestRecipeCategoryMappingDto(recipeCategoryId, recipe)) + .collect(Collectors.toList()); + } + + private static TestRecipeCategoryMapping toTestRecipeCategoryMappingDto(Long categoryId, TestRecipe recipe) { + return TestRecipeCategoryMapping.builder() + .category(staticRecipeService.getRecipeCategory(categoryId)) + .recipe(recipe) + .build(); + } + + public static List toTestStep(RecipeRequestDto.CreateRecipeDto request, TestRecipe recipe, List stepImages) { + return request.getSteps().stream() + .map(step-> { + if (step.getDescription() == null) + throw new RecipeException(CommonStatus.NULL_RECIPE_ERROR); + try { + return toTestStepDto(step, recipe, stepImages); + } catch (IOException e) { + throw new RuntimeException(e); + } + }) + .collect(Collectors.toList()); + } + + private static TestStep toTestStepDto(RecipeRequestDto.StepDto step, TestRecipe recipe, List stepImages) throws IOException { + TestStep createdStep = TestStep.builder() + .stepNum(step.getStepNum()) + .description(step.getDescription()) + .recipe(recipe) + .build(); + + MultipartFile stepImage = null; + + for (int i = 0; i < stepImages.size(); i++) { + Integer imageNum = Integer.parseInt(stepImages.get(i).getOriginalFilename().substring(0,1)) + 1; + if (imageNum == step.getStepNum()){ + stepImage = stepImages.get(i); + break; + } + } + + String imageUrl = null; + if(stepImages != null) + imageUrl = uploadTestStep(stepImage); + else + throw new RecipeException(CommonStatus.NULL_RECIPE_ERROR); + createdStep.setImage(imageUrl); + + return createdStep; + } + + public static List toTestIngredient(RecipeRequestDto.CreateRecipeDto request, TestRecipe recipe) { + return request.getIngredients().stream() + .map(ingredient -> toTestIngredientDto(ingredient, recipe)) + .collect(Collectors.toList()); + } + + private static TestIngredient toTestIngredientDto(RecipeRequestDto.NewIngredientDto ingredient, TestRecipe recipe) { + return TestIngredient.builder() + .name(ingredient.getIngredientName()) + .quantity(ingredient.getQuantity()) + .recipe(recipe) + .build(); + } + + public static RecipeResponseDto.RecipeInfoDto toTestRecipeInfoDto(TestRecipe recipe) { + return RecipeResponseDto.RecipeInfoDto.builder() + .recipeInfo(toResponseTestRecipeDto(recipe)) + .ownerId(0L) + .isOwner(false) + .steps(toResponseTestStepDto(recipe)) + .ingredients(toResponseTestIngredientDto(recipe)) + .build(); + } + + public static RecipeResponseDto.RecipeDto toResponseTestRecipeDto(TestRecipe recipe){ + + return RecipeResponseDto.RecipeDto.builder() + .recipeId(recipe.getId()) + .categoryId(getTestCategoryIds(recipe)) + .recipeName(recipe.getName()) + .ownerImage("") + .nickname("test") + .thumbnailUrl(recipe.getThumbnailUrl()) + .time(recipe.getTime()) + .intro(recipe.getIntro()) + .recipeTip(recipe.getRecipeTip()) + .createdAt(staticTimeConverter.ConvertTime(recipe.getCreatedAt())) + .updatedAt(staticTimeConverter.ConvertTime(recipe.getUpdatedAt())) + .likes(recipe.getTotalLike()) + .comments(0L) + .scraps(recipe.getTotalScrap()) + .isLiked(false) + .isScrapped(false) + .build(); + } + + public static List getTestCategoryIds(TestRecipe recipe){ + return recipe.getCategoryMappingList().stream() + .map(categoryMapping -> categoryMapping.getCategory().getId()) + .collect(Collectors.toList()); + } + + public static List toResponseTestStepDto(TestRecipe recipe) { + + return recipe.getStepList().stream() + .map(step-> RecipeResponseDto.StepDto.builder() + .stepNum(step.getStepNum()) + .description(step.getDescription()) + .image(step.getImageUrl()) + .build()) + .collect(Collectors.toList()); + } + + public static List toResponseTestIngredientDto(TestRecipe recipe) { + return recipe.getIngredientList().stream() + .map(ingredient -> RecipeResponseDto.IngredientDto.builder() + .IngredientName(ingredient.getName()) + .quantity(ingredient.getQuantity()) + .build()) + .collect(Collectors.toList()); + } + + public static RecipeResponseDto.RecipePageListDto toPagingTestRecipeDtoList(Page recipes) { + return RecipeResponseDto.RecipePageListDto.builder() + .recipeList(recipes.toList().stream() + .map(recipe -> toResponseTestRecipeSimpleDto(recipe)) + .collect(Collectors.toList())) + .totalElements(recipes.getTotalElements()) + .currentPageElements(recipes.getNumberOfElements()) + .totalPage(recipes.getTotalPages()) + .isFirst(recipes.isFirst()) + .isLast(recipes.isLast()) + .build(); + } + + private static RecipeResponseDto.RecipeSimpleDto toResponseTestRecipeSimpleDto(TestRecipe recipe) { + return RecipeResponseDto.RecipeSimpleDto.builder() + .recipeId(recipe.getId()) + .categoryId(getTestCategoryIds(recipe)) + .recipeName(recipe.getName()) + .nickname("test") + .thumbnailUrl(recipe.getThumbnailUrl()) + .createdAt(staticTimeConverter.ConvertTime(recipe.getCreatedAt())) + .updatedAt(staticTimeConverter.ConvertTime(recipe.getUpdatedAt())) + .comments(0L) + .likes(recipe.getTotalLike()) + .scraps(recipe.getTotalScrap()) + .isLiked(false) + .isScrapped(false) + .build(); + } } diff --git a/src/main/java/zipdabang/server/domain/test/TestComment.java b/src/main/java/zipdabang/server/domain/test/TestComment.java new file mode 100644 index 0000000..a8c96f9 --- /dev/null +++ b/src/main/java/zipdabang/server/domain/test/TestComment.java @@ -0,0 +1,39 @@ +package zipdabang.server.domain.test; + +import lombok.*; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import zipdabang.server.domain.common.BaseEntity; +import zipdabang.server.domain.member.Member; + +import javax.persistence.*; + +@Entity +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@DynamicInsert +@DynamicUpdate +public class TestComment extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + private Long id; + + @Column(columnDefinition = "TEXT", nullable = false) + private String content; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipe_id", nullable = false) + private TestRecipe recipe; + + public TestComment updateContent(String content) { + this.content = content; + return this; + } +} diff --git a/src/main/java/zipdabang/server/domain/test/TestIngredient.java b/src/main/java/zipdabang/server/domain/test/TestIngredient.java new file mode 100644 index 0000000..0d81f62 --- /dev/null +++ b/src/main/java/zipdabang/server/domain/test/TestIngredient.java @@ -0,0 +1,42 @@ +package zipdabang.server.domain.test; + +import lombok.*; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import zipdabang.server.domain.common.BaseEntity; +import zipdabang.server.domain.recipe.Recipe; + +import javax.persistence.*; + +@Entity +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@DynamicInsert +@DynamicUpdate +public class TestIngredient extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + private Long id; + + @Column(nullable = false) + private String name; + + @Column(length = 100, nullable = false) + private String quantity; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipe_id", nullable = false) + private TestRecipe recipe; + + public TestIngredient setRecipe(TestRecipe recipe){ + if(this.recipe != null) + recipe.getIngredientList().remove(this); + this.recipe = recipe; + recipe.getIngredientList().add(this); + + return this; + } +} diff --git a/src/main/java/zipdabang/server/domain/test/TestLikes.java b/src/main/java/zipdabang/server/domain/test/TestLikes.java new file mode 100644 index 0000000..1700b72 --- /dev/null +++ b/src/main/java/zipdabang/server/domain/test/TestLikes.java @@ -0,0 +1,50 @@ +package zipdabang.server.domain.test; + +import lombok.*; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import zipdabang.server.domain.common.BaseEntity; +import zipdabang.server.domain.member.Member; + +import javax.persistence.*; + +@Entity +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@DynamicInsert +@DynamicUpdate +public class TestLikes extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipe_id", nullable = false) + private TestRecipe recipe; + + public TestLikes deleteLikes(TestRecipe recipe){ + if(this.recipe != null) + recipe.getLikesList().remove(this); + recipe.updateLike(-1); + return this; + } + + public TestLikes setRecipe(TestRecipe recipe){ + + recipe.updateLike(1); + + if(this.recipe != null) + recipe.getLikesList().remove(this); + this.recipe = recipe; + recipe.getLikesList().add(this); + + return this; + } +} diff --git a/src/main/java/zipdabang/server/domain/test/TestRecipe.java b/src/main/java/zipdabang/server/domain/test/TestRecipe.java new file mode 100644 index 0000000..319a78d --- /dev/null +++ b/src/main/java/zipdabang/server/domain/test/TestRecipe.java @@ -0,0 +1,140 @@ +package zipdabang.server.domain.test; + +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import zipdabang.server.domain.common.BaseEntity; +import zipdabang.server.web.dto.requestDto.RecipeRequestDto; + +import javax.persistence.*; +import java.util.List; + +@Slf4j +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@DynamicInsert +@DynamicUpdate +@Entity +public class TestRecipe extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + private Long id; + + @Column(nullable = false) + private Boolean isBarista; + + @Column(columnDefinition = "boolean default false", nullable = false) + private Boolean isOfficial; + + @Column(nullable = false) + private String name; + + @Column(columnDefinition = "TEXT", nullable = false) + private String thumbnailUrl; + + @Column(length = 500, nullable = false) + private String intro; + + @Column(length = 500, nullable = false) + private String recipeTip; + + @Column(nullable = false) + private String time; + + @Column(columnDefinition = "BIGINT DEFAULT 0") + private Long totalComments; + + @Column(columnDefinition = "BIGINT DEFAULT 0") + private Long totalView; + + @Column(columnDefinition = "BIGINT DEFAULT 0") + private Long totalLike; + + @Column(columnDefinition = "BIGINT DEFAULT 0") + private Long totalScrap; + + @Column(columnDefinition = "BIGINT DEFAULT 0") + private Long weekView; + + @Column(columnDefinition = "BIGINT DEFAULT 0") + private Long weekLike; + + @Column(columnDefinition = "BIGINT DEFAULT 0") + private Long weekScrap; + + //updated_at +// +// @ManyToOne(fetch = FetchType.LAZY) +// @JoinColumn(name = "member_id", nullable = false) +// private Member member; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL) + private List categoryMappingList; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL) + private List commentList; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL) + private List likesList; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL) + private List scrapList; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL) + private List stepList; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL) + private List ingredientList; + +// @OneToMany(mappedBy = "targetRecipe", cascade = CascadeType.ALL) +// private List pushAlarmList; + + + public TestRecipe setThumbnail(String imageUrl) { + this.thumbnailUrl = imageUrl; + return this; + } + + public TestRecipe updateComment(Integer i) { + this.totalComments += i; + return this; + } + + public TestRecipe updateLike(Integer i){ + this.weekLike += i; + if (weekLike < 0) + this.weekLike = 0L; + + this.totalLike += i; + return this; + } + + public TestRecipe updateScrap(Integer i){ + this.weekScrap += i; + if (weekScrap < 0) + this.weekScrap = 0L; + + this.totalScrap += i; + return this; + } + + public TestRecipe updateView(){ + this.weekView += 1; + this.totalView += 1; + log.info("totalLike= ", this.totalView, ", weekView= ", this.weekView); + return this; + } + + public TestRecipe updateInfo(RecipeRequestDto.UpdateRecipeDto request) { + this.name = request.getName(); + this.intro = request.getIntro(); + this.recipeTip = request.getRecipeTip(); + this.time = request.getTime(); + + return this; + } +} diff --git a/src/main/java/zipdabang/server/domain/test/TestRecipeCategoryMapping.java b/src/main/java/zipdabang/server/domain/test/TestRecipeCategoryMapping.java new file mode 100644 index 0000000..75d6baf --- /dev/null +++ b/src/main/java/zipdabang/server/domain/test/TestRecipeCategoryMapping.java @@ -0,0 +1,39 @@ +package zipdabang.server.domain.test; + +import lombok.*; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import zipdabang.server.domain.recipe.RecipeCategory; + +import javax.persistence.*; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@DynamicInsert +@DynamicUpdate +@Entity +public class TestRecipeCategoryMapping { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipe_id", nullable = false) + private TestRecipe recipe; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipe_category_id", nullable = false) + private RecipeCategory category; + + public TestRecipeCategoryMapping setRecipe(TestRecipe recipe){ + if(this.recipe != null) + recipe.getCategoryMappingList().remove(this); + this.recipe = recipe; + recipe.getCategoryMappingList().add(this); + + return this; + } +} diff --git a/src/main/java/zipdabang/server/domain/test/TestScrap.java b/src/main/java/zipdabang/server/domain/test/TestScrap.java new file mode 100644 index 0000000..dffd2c3 --- /dev/null +++ b/src/main/java/zipdabang/server/domain/test/TestScrap.java @@ -0,0 +1,50 @@ +package zipdabang.server.domain.test; + +import lombok.*; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import zipdabang.server.domain.common.BaseEntity; +import zipdabang.server.domain.member.Member; + +import javax.persistence.*; + +@Entity +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@DynamicInsert +@DynamicUpdate +public class TestScrap extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipe_id") + private TestRecipe recipe; + + public TestScrap deleteScrap(TestRecipe recipe){ + if(this.recipe != null) + recipe.getScrapList().remove(this); + recipe.updateScrap(-1); + return this; + } + + public TestScrap setRecipe(TestRecipe recipe){ + + recipe.updateScrap(1); + + if(this.recipe != null) + recipe.getScrapList().remove(this); + this.recipe = recipe; + recipe.getScrapList().add(this); + + return this; + } +} diff --git a/src/main/java/zipdabang/server/domain/test/TestStep.java b/src/main/java/zipdabang/server/domain/test/TestStep.java new file mode 100644 index 0000000..fa30036 --- /dev/null +++ b/src/main/java/zipdabang/server/domain/test/TestStep.java @@ -0,0 +1,50 @@ +package zipdabang.server.domain.test; + +import lombok.*; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; +import zipdabang.server.domain.common.BaseEntity; +import zipdabang.server.domain.recipe.Recipe; + +import javax.persistence.*; + +@Entity +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@DynamicInsert +@DynamicUpdate +public class TestStep extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false) + private Long id; + + @Column(nullable = false) + private Integer stepNum; + + @Column(columnDefinition = "TEXT", nullable = false) + private String imageUrl; + + @Column(columnDefinition = "TEXT", nullable = false) + private String description; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "recipe_id", nullable = false) + private TestRecipe recipe; + + public TestStep setImage(String imageUrl) { + this.imageUrl = imageUrl; + return this; + } + + public TestStep setRecipe(TestRecipe recipe){ + if(this.recipe != null) + recipe.getStepList().remove(this); + this.recipe = recipe; + recipe.getStepList().add(this); + + return this; + } +} \ No newline at end of file diff --git a/src/main/java/zipdabang/server/repository/testRepository/TestIngredientRepository.java b/src/main/java/zipdabang/server/repository/testRepository/TestIngredientRepository.java new file mode 100644 index 0000000..29f71d5 --- /dev/null +++ b/src/main/java/zipdabang/server/repository/testRepository/TestIngredientRepository.java @@ -0,0 +1,7 @@ +package zipdabang.server.repository.testRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +import zipdabang.server.domain.test.TestIngredient; + +public interface TestIngredientRepository extends JpaRepository { +} diff --git a/src/main/java/zipdabang/server/repository/testRepository/TestRecipeCategoryMappingRepository.java b/src/main/java/zipdabang/server/repository/testRepository/TestRecipeCategoryMappingRepository.java new file mode 100644 index 0000000..e25b1c8 --- /dev/null +++ b/src/main/java/zipdabang/server/repository/testRepository/TestRecipeCategoryMappingRepository.java @@ -0,0 +1,7 @@ +package zipdabang.server.repository.testRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +import zipdabang.server.domain.test.TestRecipeCategoryMapping; + +public interface TestRecipeCategoryMappingRepository extends JpaRepository { +} diff --git a/src/main/java/zipdabang/server/repository/testRepository/TestRecipeRepository.java b/src/main/java/zipdabang/server/repository/testRepository/TestRecipeRepository.java new file mode 100644 index 0000000..ad1a1b2 --- /dev/null +++ b/src/main/java/zipdabang/server/repository/testRepository/TestRecipeRepository.java @@ -0,0 +1,6 @@ +package zipdabang.server.repository.testRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +import zipdabang.server.domain.test.TestRecipe; + +public interface TestRecipeRepository extends JpaRepository{} diff --git a/src/main/java/zipdabang/server/repository/testRepository/TestStepRepository.java b/src/main/java/zipdabang/server/repository/testRepository/TestStepRepository.java new file mode 100644 index 0000000..e68914b --- /dev/null +++ b/src/main/java/zipdabang/server/repository/testRepository/TestStepRepository.java @@ -0,0 +1,7 @@ +package zipdabang.server.repository.testRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +import zipdabang.server.domain.test.TestStep; + +public interface TestStepRepository extends JpaRepository { +} diff --git a/src/main/java/zipdabang/server/repository/testRepository/testRecipeRepositoryCustom/TestRecipeRepositoryCustom.java b/src/main/java/zipdabang/server/repository/testRepository/testRecipeRepositoryCustom/TestRecipeRepositoryCustom.java new file mode 100644 index 0000000..d65ec38 --- /dev/null +++ b/src/main/java/zipdabang/server/repository/testRepository/testRecipeRepositoryCustom/TestRecipeRepositoryCustom.java @@ -0,0 +1,15 @@ +package zipdabang.server.repository.testRepository.testRecipeRepositoryCustom; + +import com.querydsl.core.types.dsl.BooleanExpression; +import zipdabang.server.domain.member.Member; +import zipdabang.server.domain.test.TestRecipe; + +import java.util.List; + +public interface TestRecipeRepositoryCustom { + + List testRecipesOrderBy(Integer pageIndex, Integer pageSize, String order, BooleanExpression... booleanExpressions); + + Long testRecipeTotalCount(BooleanExpression... booleanExpressions); + +} diff --git a/src/main/java/zipdabang/server/repository/testRepository/testRecipeRepositoryImpl/TestRecipeRepositoryImpl.java b/src/main/java/zipdabang/server/repository/testRepository/testRecipeRepositoryImpl/TestRecipeRepositoryImpl.java new file mode 100644 index 0000000..bce3534 --- /dev/null +++ b/src/main/java/zipdabang/server/repository/testRepository/testRecipeRepositoryImpl/TestRecipeRepositoryImpl.java @@ -0,0 +1,66 @@ +package zipdabang.server.repository.testRepository.testRecipeRepositoryImpl; + +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import zipdabang.server.domain.member.Member; +import zipdabang.server.domain.test.QTestRecipe; +import zipdabang.server.domain.test.TestRecipe; +import zipdabang.server.repository.testRepository.testRecipeRepositoryCustom.TestRecipeRepositoryCustom; + +import java.util.List; + +import static zipdabang.server.domain.recipe.QRecipe.recipe; +import static zipdabang.server.domain.test.QTestRecipe.testRecipe; + +@Slf4j +@RequiredArgsConstructor +public class TestRecipeRepositoryImpl implements TestRecipeRepositoryCustom { + + private final JPAQueryFactory queryFactory; + QTestRecipe qTestRecipe = testRecipe; + + @Override + public List testRecipesOrderBy(Integer pageIndex, Integer pageSize, String order, BooleanExpression... booleanExpressions) { + + BooleanExpression combinedExpression = null; + + for (BooleanExpression expression : booleanExpressions) { + if (combinedExpression == null) { + combinedExpression = expression; + } else { + combinedExpression = combinedExpression.and(expression); + } + } + + return queryFactory + .selectFrom(testRecipe) + .orderBy(recipe.createdAt.desc()) + .offset(pageIndex * pageSize) + .limit(pageSize) + .fetch(); + } + + @Override + public Long testRecipeTotalCount(BooleanExpression... booleanExpressions) { + + BooleanExpression combinedExpression = null; + + for (BooleanExpression expression : booleanExpressions) { + if (combinedExpression == null) { + combinedExpression = expression; + } else { + combinedExpression = combinedExpression.and(expression); + } + } + + return queryFactory + .select(recipe.count()) + .from(recipe) + .where( + combinedExpression + ) + .fetchOne(); + } +} diff --git a/src/main/java/zipdabang/server/service/RecipeService.java b/src/main/java/zipdabang/server/service/RecipeService.java index 9d38c90..abfac86 100644 --- a/src/main/java/zipdabang/server/service/RecipeService.java +++ b/src/main/java/zipdabang/server/service/RecipeService.java @@ -4,6 +4,7 @@ import org.springframework.web.multipart.MultipartFile; import zipdabang.server.domain.member.Member; import zipdabang.server.domain.recipe.*; +import zipdabang.server.domain.test.TestRecipe; import zipdabang.server.web.dto.requestDto.RecipeRequestDto; import zipdabang.server.web.dto.responseDto.RecipeResponseDto; @@ -101,4 +102,10 @@ public interface RecipeService { Long getWrittenByRecipeCounting(String writtenby, Member member); Long getCommentCount(Recipe recipe, Member member); + + TestRecipe testCreate(RecipeRequestDto.CreateRecipeDto request, MultipartFile thumbnail, List stepImages) throws IOException; + + TestRecipe getTestRecipe(Long recipeId); + + Page testRecipeListByCategory(Long categoryId, Integer pageIndex, String order); } diff --git a/src/main/java/zipdabang/server/service/serviceImpl/RecipeServiceImpl.java b/src/main/java/zipdabang/server/service/serviceImpl/RecipeServiceImpl.java index 6b6f4b3..4595ab9 100644 --- a/src/main/java/zipdabang/server/service/serviceImpl/RecipeServiceImpl.java +++ b/src/main/java/zipdabang/server/service/serviceImpl/RecipeServiceImpl.java @@ -1,7 +1,6 @@ package zipdabang.server.service.serviceImpl; import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.jpa.impl.JPAQuery; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -22,6 +21,7 @@ import zipdabang.server.domain.inform.PushAlarm; import zipdabang.server.domain.member.*; import zipdabang.server.domain.recipe.*; +import zipdabang.server.domain.test.TestRecipe; import zipdabang.server.firebase.fcm.service.FirebaseService; import zipdabang.server.repository.AlarmRepository.AlarmCategoryRepository; import zipdabang.server.repository.AlarmRepository.PushAlarmRepository; @@ -32,12 +32,16 @@ import zipdabang.server.repository.recipeRepositories.recipeRepositoryCustom.CommentRepositoryCustom; import zipdabang.server.repository.recipeRepositories.recipeRepositoryCustom.RecipeRepositoryCustom; import zipdabang.server.repository.recipeRepositories.recipeRepositoryCustom.TempRecipeRepositoryCustom; +import zipdabang.server.repository.testRepository.TestIngredientRepository; +import zipdabang.server.repository.testRepository.TestRecipeCategoryMappingRepository; +import zipdabang.server.repository.testRepository.TestRecipeRepository; +import zipdabang.server.repository.testRepository.TestStepRepository; +import zipdabang.server.repository.testRepository.testRecipeRepositoryCustom.TestRecipeRepositoryCustom; import zipdabang.server.service.RecipeService; import zipdabang.server.web.dto.requestDto.RecipeRequestDto; import zipdabang.server.web.dto.responseDto.RecipeResponseDto; import java.io.IOException; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -888,4 +892,76 @@ public Boolean checkIsScrapped(Recipe recipe, Member member) { public RecipeCategory getRecipeCategory(Long categoryId) { return recipeCategoryRepository.findById(categoryId).get(); } + + /** + * 부하테스트용 서비스 + */ + private final TestRecipeRepository testRecipeRepository; + private final TestRecipeCategoryMappingRepository testRecipeCategoryMappingRepository; + private final TestStepRepository testStepRepository; + private final TestIngredientRepository testIngredientRepository; + private final TestRecipeRepositoryCustom testRecipeRepositoryCustom; + + @Override + @Transactional(readOnly = false) + public TestRecipe testCreate(RecipeRequestDto.CreateRecipeDto request, MultipartFile thumbnail, List stepImages) throws IOException { + TestRecipe buildRecipe = RecipeConverter.toTestRecipe(request, thumbnail); + TestRecipe recipe = testRecipeRepository.save(buildRecipe); + + RecipeConverter.toTestRecipeCategory(request.getCategoryId(),recipe).stream() + .map(categoryMapping -> testRecipeCategoryMappingRepository.save(categoryMapping)) + .collect(Collectors.toList()) + .stream() + .map(categoryMapping -> categoryMapping.setRecipe(recipe)); + + + RecipeConverter.toTestStep(request, recipe, stepImages).stream() + .map(step -> testStepRepository.save(step)) + .collect(Collectors.toList()) + .stream() + .map(step -> step.setRecipe(recipe)); + + RecipeConverter.toTestIngredient(request, recipe).stream() + .map(ingredient -> testIngredientRepository.save(ingredient)) + .collect(Collectors.toList()) + .stream() + .map(ingredient -> ingredient.setRecipe(recipe)); + + return recipe; + } + + @Override + public TestRecipe getTestRecipe(Long recipeId) { + TestRecipe findRecipe = testRecipeRepository.findById(recipeId).orElseThrow(()->new RecipeException(CommonStatus.NO_RECIPE_EXIST)); + + findRecipe.updateView(); + return findRecipe; + } + + @Transactional(readOnly = false) + @Override + public Page testRecipeListByCategory(Long categoryId, Integer pageIndex, String order) { + + List recipeCategory = recipeCategoryRepository.findAllById(categoryId); + + if(recipeCategory.isEmpty()) + throw new RecipeException(CommonStatus.RECIPE_NOT_FOUND); + + List content = new ArrayList<>(); + + BooleanExpression whereCondition = recipeRepositoryCustom.recipesInCategoryCondition(categoryId); + + + content = testRecipeRepositoryCustom.testRecipesOrderBy(pageIndex,pageSize, order, whereCondition); + + log.info("서비스단의 상황 : {}", content.size()); + Long count = testRecipeRepositoryCustom.testRecipeTotalCount(whereCondition); + + if (count < pageIndex*pageSize) + throw new RecipeException(CommonStatus.OVER_PAGE_INDEX_ERROR); + if (content.size() > count - pageIndex*pageSize) + content = content.subList(0, count.intValue()-pageIndex*pageSize); + + return new PageImpl<>(content,PageRequest.of(pageIndex,pageSize), count); + } } diff --git a/src/main/java/zipdabang/server/web/controller/RecipeRestController.java b/src/main/java/zipdabang/server/web/controller/RecipeRestController.java index 333b80c..95ed898 100644 --- a/src/main/java/zipdabang/server/web/controller/RecipeRestController.java +++ b/src/main/java/zipdabang/server/web/controller/RecipeRestController.java @@ -22,6 +22,7 @@ import zipdabang.server.converter.RecipeConverter; import zipdabang.server.domain.member.Member; import zipdabang.server.domain.recipe.*; +import zipdabang.server.domain.test.TestRecipe; import zipdabang.server.service.RecipeService; import zipdabang.server.validation.annotation.CheckPage; import zipdabang.server.validation.annotation.CheckTempMember; @@ -955,4 +956,72 @@ else if (page < 1) return ResponseDto.of(recipeService.getScrapRecipes(page, member)); } + /** + * 부하 테스트용 컨트롤러 + */ + @Operation(summary = "레시피 등록 테스트 API 🔑 ✔") + @ApiResponses({ + @ApiResponse(responseCode = "2000"), + @ApiResponse(responseCode = "4100", description = "레시피 작성시 누락된 내용이 있습니다. 미완료는 임시저장으로 가세요", content = @Content(schema = @Schema(implementation = ResponseDto.class))), + @ApiResponse(responseCode = "5000", description = "SERVER ERROR, 백앤드 개발자에게 알려주세요", content = @Content(schema = @Schema(implementation = ResponseDto.class))), + }) + @PostMapping(value = "/test/members/recipes") + public ResponseDto testCreateRecipe( + @RequestPart(value = "content") RecipeRequestDto.CreateRecipeDto request, + @RequestPart(value = "thumbnail") MultipartFile thumbnail, + @RequestPart(value = "stepImages") List stepImages) throws IOException { + + log.info("사용자가 준 정보 : {}", request.toString()); + + TestRecipe recipe = recipeService.testCreate(request, thumbnail, stepImages); + return ResponseDto.of(RecipeConverter.toTestRecipeStatusDto(recipe)); + } + + @Operation(summary = "레시피 상세 정보 조회 테스트 API 🔑 ✔") + @ApiResponses({ + @ApiResponse(responseCode = "2000"), + @ApiResponse(responseCode = "4101", description = "BAD_REQUEST, 해당 recipeId를 가진 recipe가 없어요", content = @Content(schema = @Schema(implementation = ResponseDto.class))), + @ApiResponse(responseCode = "5000", description = "SERVER ERROR, 백앤드 개발자에게 알려주세요", content = @Content(schema = @Schema(implementation = ResponseDto.class))), + }) + @GetMapping(value = "/test/members/recipes/{recipeId}") + public ResponseDto testRecipeDetail(@PathVariable(name = "recipeId") Long recipeId) { + + TestRecipe recipe = recipeService.getTestRecipe(recipeId); + + return ResponseDto.of(RecipeConverter.toTestRecipeInfoDto(recipe)); + } + + @Operation(summary = "카테고리 별 레시피 목록 조회 테스트 API 🔑 ✔") + @ApiResponses({ + @ApiResponse(responseCode = "2000", description = "OK, 목록이 있을 땐 이 응답임"), + @ApiResponse(responseCode = "2100", description = "OK, 목록이 없을 경우, result = null", content = @Content(schema = @Schema(implementation = ResponseDto.class))), + @ApiResponse(responseCode = "4053", description = "BAD_REQUEST, 넘겨받은 categoryId와 일치하는 카테고리 없음. 1~6 사이로 보내세요", content = @Content(schema = @Schema(implementation = ResponseDto.class))), + @ApiResponse(responseCode = "4055", description = "BAD_REQUEST, 페이지 인덱스 범위 초과함", content = @Content(schema = @Schema(implementation = ResponseDto.class))), + @ApiResponse(responseCode = "4104", description = "BAD_REQUEST, 조회 방식 타입이 잘못되었습니다. likes, follow, lastest중 하나로 보내주세요.", content = @Content(schema = @Schema(implementation = ResponseDto.class))), + @ApiResponse(responseCode = "4105", description = "BAD_REQUEST, 해당 id를 가진 레시피 카테고리가 없습니다. 잘못 보내줬어요", content = @Content(schema = @Schema(implementation = ResponseDto.class))), + @ApiResponse(responseCode = "5000", description = "SERVER ERROR, 백앤드 개발자에게 알려주세요", content = @Content(schema = @Schema(implementation = ResponseDto.class))), + }) + @Parameters({ + @Parameter(name = "pageIndex", description = "query string 페이지 번호, 무조건 값 줘야 함, 0 이런거 주면 에러 뱉음"), + @Parameter(name = "order", description = "query string 조회 방식. 인기순: likes, 팔로우순: follow, 최신순: latest로 넘겨주세요") + }) + @GetMapping(value = "/test/members/recipes/categories/{categoryId}") + public ResponseDto testRecipeListByCategory(@ExistRecipeCategory @PathVariable Long categoryId, @CheckPage @RequestParam(name = "pageIndex") Integer pageIndex) { + if (pageIndex == null) + pageIndex = 1; + + pageIndex -= 1; + + Page recipes = recipeService.testRecipeListByCategory(categoryId, pageIndex, "latest"); + + + log.info(recipes.toString()); + + if (recipes.getTotalElements() == 0) + throw new RecipeException(CommonStatus.RECIPE_NOT_FOUND); + if (pageIndex >= recipes.getTotalPages()) + throw new RecipeException(CommonStatus.OVER_PAGE_INDEX_ERROR); + + return ResponseDto.of(RecipeConverter.toPagingTestRecipeDtoList(recipes)); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6d1b4db..c6b1e7f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -72,6 +72,8 @@ cloud: folder: zipdabang-recipe-thumbNail: recipe/thumbnail zipdabang-recipe-steps : recipe/steps + zipdabang-test-thumbnail: test/thumbnail + zipdabang-test-steps: test/steps zipdabang-proifile : user zipdabang-inquery : inquery # zipdabang-facilities : facilities @@ -84,6 +86,7 @@ cloud: accessKey: ${AWS_ACCESS_KEY_ID} secretKey: ${AWS_SECRET_ACCESS_KEY} + jwt: header: Authorization secret: ${JWT_SECRET} @@ -166,6 +169,8 @@ cloud: folder: zipdabang-recipe-thumbNail: recipe/thumbnail zipdabang-recipe-steps: recipe/steps + zipdabang-test-thumbnail: test/thumbnail + zipdabang-test-steps: test/steps zipdabang-proifile: user zipdabang-inquery: inquery # zipdabang-facilities : facilities