From 8a017725467ae8a88a39c0207851260611b3ac2c Mon Sep 17 00:00:00 2001 From: JeongeunChoi <77786996+JeongeunChoi@users.noreply.github.com> Date: Wed, 11 Oct 2023 23:24:25 +0900 Subject: [PATCH 1/5] =?UTF-8?q?refactor:=20fetch=20join=EC=9D=84=20?= =?UTF-8?q?=ED=86=B5=ED=95=9C=20=EC=9E=A5=ED=84=B0=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=ED=8C=90=20=EA=B2=80=EC=83=89=20=EA=B8=B0=EB=8A=A5=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 - 기존 기능은 그대로 두고, fetch join 적용한 코드 추가 related to: #233 --- .../MarketArticleCustomRepository.java | 16 +++++ .../repository/MarketArticleRepository.java | 7 ++- .../MarketArticleRepositoryImpl.java | 60 +++++++++++++++++++ .../market/service/MarketArticleService.java | 30 ++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleCustomRepository.java create mode 100644 db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepositoryImpl.java diff --git a/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleCustomRepository.java b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleCustomRepository.java new file mode 100644 index 0000000..69c242c --- /dev/null +++ b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleCustomRepository.java @@ -0,0 +1,16 @@ +package com.aliens.db.marketarticle.repository; + +import com.aliens.db.marketarticle.entity.MarketArticleEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.time.Instant; +import java.util.Optional; + +public interface MarketArticleCustomRepository { + + Page findAllWithFetchJoin(Pageable pageable); + + Page findAllByTitleContainingOrContentContainingByFetchJoin(String title, String content, Pageable pageable); + +} diff --git a/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepository.java b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepository.java index 55631fb..e0e9ede 100644 --- a/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepository.java +++ b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepository.java @@ -6,11 +6,16 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.Instant; import java.util.List; +import java.util.Optional; public interface MarketArticleRepository extends - JpaRepository { + JpaRepository, MarketArticleCustomRepository { List findAllByTitleContainingOrContentContaining(String title, String content); + List findAllByMember(MemberEntity member); + Page findAllByTitleContainingOrContentContaining(String title, String content, Pageable pageable); + } \ No newline at end of file diff --git a/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepositoryImpl.java b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepositoryImpl.java new file mode 100644 index 0000000..9a64b4a --- /dev/null +++ b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepositoryImpl.java @@ -0,0 +1,60 @@ +package com.aliens.db.marketarticle.repository; + +import com.aliens.db.marketarticle.entity.MarketArticleEntity; +import com.aliens.db.marketarticlecomment.entity.QMarketArticleCommentEntity; +import com.aliens.db.marketbookmark.entity.QMarketBookmarkEntity; +import com.querydsl.jpa.JPQLQuery; +import com.querydsl.jpa.JPQLQueryFactory; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.support.Querydsl; + +import javax.persistence.EntityManager; +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +import static com.aliens.db.marketarticle.entity.QMarketArticleEntity.marketArticleEntity; +import static com.aliens.db.marketarticlecomment.entity.QMarketArticleCommentEntity.*; +import static com.aliens.db.marketbookmark.entity.QMarketBookmarkEntity.*; + +@RequiredArgsConstructor +public class MarketArticleRepositoryImpl implements MarketArticleCustomRepository { + private final EntityManager entityManager; + + @Override + public Page findAllWithFetchJoin(Pageable pageable) { + JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager); + + List result = queryFactory + .selectFrom(marketArticleEntity) + .leftJoin(marketArticleEntity.likes, marketBookmarkEntity) + .leftJoin(marketArticleEntity.comments, marketArticleCommentEntity) + .fetchJoin() + .fetch(); + + return new PageImpl<>(result, pageable, result.size()); + } + + @Override + public Page findAllByTitleContainingOrContentContainingByFetchJoin(String title, String content, Pageable pageable) { + JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager); + + List result = queryFactory + .selectFrom(marketArticleEntity) + .leftJoin(marketArticleEntity.likes, marketBookmarkEntity) + .leftJoin(marketArticleEntity.comments, marketArticleCommentEntity) + .fetchJoin() + .where( + marketArticleEntity.title.containsIgnoreCase(title) + .or(marketArticleEntity.content.containsIgnoreCase(content)) + ) + .fetch(); + + return new PageImpl<>(result, pageable, result.size()); + } + +} diff --git a/membership/src/main/java/com/aliens/friendship/domain/article/market/service/MarketArticleService.java b/membership/src/main/java/com/aliens/friendship/domain/article/market/service/MarketArticleService.java index eb29b65..f530180 100644 --- a/membership/src/main/java/com/aliens/friendship/domain/article/market/service/MarketArticleService.java +++ b/membership/src/main/java/com/aliens/friendship/domain/article/market/service/MarketArticleService.java @@ -83,6 +83,36 @@ public Page searchMarketArticles( )); } + @Transactional(readOnly = true) + public Page searchMarketArticlesWithFetchJoin( + Pageable pageable, + String searchKeyword + ) { + if (searchKeyword == null || searchKeyword.isBlank()) { + return marketArticleRepository.findAllWithFetchJoin(pageable) + .map(article -> + MarketArticleDto.from( + article, + article.getLikes().size(), + article.getComments().size(), + getMarketArticleImages(article) + ) + ); + } + + return marketArticleRepository.findAllByTitleContainingOrContentContainingByFetchJoin( + searchKeyword, + searchKeyword, + pageable + ) + .map(article -> MarketArticleDto.from( + article, + article.getLikes().size(), + article.getComments().size(), + getMarketArticleImages(article) + )); + } + /** * 장터 게시글 상세 조회 */ From 859af903846f55fc176ffc034b99384671e7cf3a Mon Sep 17 00:00:00 2001 From: JeongeunChoi <77786996+JeongeunChoi@users.noreply.github.com> Date: Wed, 11 Oct 2023 23:30:36 +0900 Subject: [PATCH 2/5] =?UTF-8?q?test:=20fetch=20join=ED=95=9C=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=EC=99=80=20=EA=B7=B8=EB=A0=87=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=9D=80=20=EA=B2=BD=EC=9A=B0=20=EC=8B=9C=EA=B0=84=20=EC=B8=A1?= =?UTF-8?q?=EC=A0=95=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to: #233 --- .../service/MarketArticleServiceTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 membership/src/test/java/com/aliens/friendship/domain/article/market/service/MarketArticleServiceTest.java diff --git a/membership/src/test/java/com/aliens/friendship/domain/article/market/service/MarketArticleServiceTest.java b/membership/src/test/java/com/aliens/friendship/domain/article/market/service/MarketArticleServiceTest.java new file mode 100644 index 0000000..9fe6cff --- /dev/null +++ b/membership/src/test/java/com/aliens/friendship/domain/article/market/service/MarketArticleServiceTest.java @@ -0,0 +1,46 @@ +package com.aliens.friendship.domain.article.market.service; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +import javax.transaction.Transactional; + +@SpringBootTest +class MarketArticleServiceTest { + + @Autowired + private MarketArticleService marketArticleService; + + @Test + @Transactional + public void testSearchMarketArticles() { + String searchKeyword = ""; + Pageable pageable = PageRequest.of(0, 10); // Set the page and size as needed + + long startTime = System.currentTimeMillis(); + marketArticleService.searchMarketArticles(pageable, searchKeyword); + long endTime = System.currentTimeMillis(); + + System.out.println("Search time: " + (endTime - startTime) + "ms"); + + // Perform assertions on the result as needed + } + + @Test + @Transactional + public void testSearchMarketArticlesWithFetchJoin() { + String searchKeyword = ""; + Pageable pageable = PageRequest.of(0, 10); // Set the page and size as needed + + long startTime = System.currentTimeMillis(); + marketArticleService.searchMarketArticlesWithFetchJoin(pageable, searchKeyword); + long endTime = System.currentTimeMillis(); + + System.out.println("Search with fetch join time: " + (endTime - startTime) + "ms"); + + // Perform assertions on the result as needed + } +} \ No newline at end of file From a65388cda6cce918c972741610266bf8166f7c57 Mon Sep 17 00:00:00 2001 From: JeongeunChoi <77786996+JeongeunChoi@users.noreply.github.com> Date: Wed, 11 Oct 2023 23:35:07 +0900 Subject: [PATCH 3/5] =?UTF-8?q?exception:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?5=EB=B6=84=20=EB=82=B4=EC=97=90=20=EC=9E=AC=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20=EB=B6=88=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to: #235 --- .../ArticleCreationNotAllowedException.java | 20 +++++++++++++ .../exception/ArticleExceptionCode.java | 4 ++- .../exception/ArticleExceptionHandler.java | 28 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleCreationNotAllowedException.java create mode 100644 membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleExceptionHandler.java diff --git a/membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleCreationNotAllowedException.java b/membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleCreationNotAllowedException.java new file mode 100644 index 0000000..800f40a --- /dev/null +++ b/membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleCreationNotAllowedException.java @@ -0,0 +1,20 @@ +package com.aliens.friendship.domain.article.exception; + +import com.aliens.friendship.global.error.ExceptionCode; +import com.aliens.friendship.global.error.ResourceNotFoundException; + +public class ArticleCreationNotAllowedException + extends ResourceNotFoundException { + + private ExceptionCode exceptionCode; + + public ArticleCreationNotAllowedException() { + super(ArticleExceptionCode.ARTICLE_CREATION_NOT_ALLOWED); + this.exceptionCode = ArticleExceptionCode.ARTICLE_CREATION_NOT_ALLOWED; + } + + @Override + public ExceptionCode getExceptionCode() { + return exceptionCode; + } +} \ No newline at end of file diff --git a/membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleExceptionCode.java b/membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleExceptionCode.java index ee02be4..4eea110 100644 --- a/membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleExceptionCode.java +++ b/membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleExceptionCode.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; +import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.NOT_FOUND; @Getter @@ -13,7 +14,8 @@ public enum ArticleExceptionCode implements ExceptionCode { ARTICLE_NOT_FOUND(NOT_FOUND, "BA-C-001", "존재하지 않는 게시글입니다."), - ARTICLE_COMMENT_NOT_FOUND(NOT_FOUND, "BA-C-001", "존재하지 않는 게시글 댓글입니다."); + ARTICLE_COMMENT_NOT_FOUND(NOT_FOUND, "BA-C-001", "존재하지 않는 게시글 댓글입니다."), + ARTICLE_CREATION_NOT_ALLOWED(BAD_REQUEST, "BA-C-003", "게시글 생성 후 5분 후에 재생성이 가능합니다."); private final HttpStatus httpStatus; private final String code; diff --git a/membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleExceptionHandler.java b/membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleExceptionHandler.java new file mode 100644 index 0000000..1306cdc --- /dev/null +++ b/membership/src/main/java/com/aliens/friendship/domain/article/exception/ArticleExceptionHandler.java @@ -0,0 +1,28 @@ +package com.aliens.friendship.domain.article.exception; + +import com.aliens.friendship.global.error.ErrorResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@Slf4j +@RestControllerAdvice +public class ArticleExceptionHandler { + + /** + * ArticleCreationNotAllowedException 핸들링 + * Custom Exception + */ + @ExceptionHandler(ArticleCreationNotAllowedException.class) + protected ResponseEntity handlingArticleCreationNotAllowedException( + ArticleCreationNotAllowedException e + ) { + log.error("[handling ArticleCreationNotAllowedException] {}", e.getExceptionCode().getMessage()); + return new ResponseEntity<>( + ErrorResponse.of(e.getExceptionCode()), + HttpStatus.valueOf(e.getExceptionCode().getHttpStatus().value()) + ); + } +} \ No newline at end of file From 0ede3ee887f53d5b91c7c80af9a54da5a2ee7337 Mon Sep 17 00:00:00 2001 From: JeongeunChoi <77786996+JeongeunChoi@users.noreply.github.com> Date: Wed, 11 Oct 2023 23:36:28 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20=EC=9E=A5=ED=84=B0=20=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=EA=B8=80=205=EB=B6=84=20=EB=82=B4=EC=97=90=20?= =?UTF-8?q?=EC=9E=AC=EC=83=9D=EC=84=B1=20=EB=B6=88=EA=B0=80=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to: #235 --- .../MarketArticleCustomRepository.java | 1 + .../repository/MarketArticleRepository.java | 1 + .../MarketArticleRepositoryImpl.java | 10 +++++ .../market/service/MarketArticleService.java | 43 ++++++++++++------- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleCustomRepository.java b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleCustomRepository.java index 69c242c..0fa1b71 100644 --- a/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleCustomRepository.java +++ b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleCustomRepository.java @@ -13,4 +13,5 @@ public interface MarketArticleCustomRepository { Page findAllByTitleContainingOrContentContainingByFetchJoin(String title, String content, Pageable pageable); + Optional findLatestArticleTimeByMemberId(Long memberId); } diff --git a/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepository.java b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepository.java index e0e9ede..d800fad 100644 --- a/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepository.java +++ b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepository.java @@ -18,4 +18,5 @@ public interface MarketArticleRepository extends Page findAllByTitleContainingOrContentContaining(String title, String content, Pageable pageable); + Optional findLatestArticleTimeByMemberId(Long memberId); } \ No newline at end of file diff --git a/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepositoryImpl.java b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepositoryImpl.java index 9a64b4a..40775ae 100644 --- a/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepositoryImpl.java +++ b/db/src/main/java/com/aliens/db/marketarticle/repository/MarketArticleRepositoryImpl.java @@ -57,4 +57,14 @@ public Page findAllByTitleContainingOrContentContainingByFe return new PageImpl<>(result, pageable, result.size()); } + @Override + public Optional findLatestArticleTimeByMemberId(Long memberId) { + JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager); + + return Optional.ofNullable(queryFactory + .select(marketArticleEntity.createdAt.max()) + .from(marketArticleEntity) + .where(marketArticleEntity.member.id.eq(memberId)) + .fetchOne()); + } } diff --git a/membership/src/main/java/com/aliens/friendship/domain/article/market/service/MarketArticleService.java b/membership/src/main/java/com/aliens/friendship/domain/article/market/service/MarketArticleService.java index f530180..ae93cc8 100644 --- a/membership/src/main/java/com/aliens/friendship/domain/article/market/service/MarketArticleService.java +++ b/membership/src/main/java/com/aliens/friendship/domain/article/market/service/MarketArticleService.java @@ -12,6 +12,7 @@ import com.aliens.db.productimage.entity.ProductImageEntity; import com.aliens.db.productimage.repsitory.ProductImageRepository; import com.aliens.friendship.domain.article.dto.ArticleDto; +import com.aliens.friendship.domain.article.exception.ArticleCreationNotAllowedException; import com.aliens.friendship.domain.article.market.dto.CreateMarketArticleRequest; import com.aliens.friendship.domain.article.market.dto.MarketArticleDto; import com.aliens.friendship.domain.article.market.dto.UpdateMarketArticleRequest; @@ -27,6 +28,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -139,20 +142,30 @@ public Long saveMarketArticle( CreateMarketArticleRequest request, UserDetails userPrincipal ) throws Exception { + MemberEntity member = getMemberEntity(userPrincipal.getUsername()); + Optional latestArticleTime = marketArticleRepository.findLatestArticleTimeByMemberId(member.getId()); - MarketArticleEntity savedMarketArticle = marketArticleRepository.save(request.toEntity( - getMemberEntity(userPrincipal.getUsername()) - )); + boolean canCreateArticle = latestArticleTime.map( + time -> Duration.between(time, Instant.now()).toMinutes() >= 5 + ).orElse(true); - for (MultipartFile imageUrl : request.getImageUrls()) { - ProductImageEntity productImage = ProductImageEntity.of( - articleImageService.uploadProfileImage(imageUrl), - savedMarketArticle - ); - productImageRepository.save(productImage); - } + if (canCreateArticle) { + MarketArticleEntity savedMarketArticle = marketArticleRepository.save(request.toEntity( + getMemberEntity(userPrincipal.getUsername()) + )); - return savedMarketArticle.getId(); + for (MultipartFile imageUrl : request.getImageUrls()) { + ProductImageEntity productImage = ProductImageEntity.of( + articleImageService.uploadProfileImage(imageUrl), + savedMarketArticle + ); + productImageRepository.save(productImage); + } + + return savedMarketArticle.getId(); + } else { + throw new ArticleCreationNotAllowedException(); + } } /** @@ -212,10 +225,10 @@ public Optional updateArticleLike( MarketArticleEntity marketArticle = getMarketArticleEntity(articleId); MemberEntity member = getMemberEntity(principal.getUsername()); Optional marketBookmark = marketBookmarkRepository.findByMarketArticleAndMemberEntity(marketArticle, member); - if(marketBookmark.isPresent()){ + if (marketBookmark.isPresent()) { deleteBookmark(articleId, principal); return Optional.empty(); - } else{ + } else { return Optional.of(createBookmark(articleId, principal)); } } @@ -254,8 +267,8 @@ private void deleteBookmark( } @Transactional(readOnly = true) - public List getAllBookmarks( MemberEntity loginMemberEntity - ) { + public List getAllBookmarks(MemberEntity loginMemberEntity + ) { List marketArticles = marketBookmarkRepository.findAllByMemberEntity( loginMemberEntity ) From cdd90575c6ad49e7b9e0acb7296c30c5b929a7c6 Mon Sep 17 00:00:00 2001 From: JeongeunChoi <77786996+JeongeunChoi@users.noreply.github.com> Date: Wed, 11 Oct 2023 23:37:30 +0900 Subject: [PATCH 5/5] =?UTF-8?q?refactor:=20=EC=9D=BC=EB=B0=98=20=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=EA=B8=80=205=EB=B6=84=20=EB=82=B4=EC=97=90=20?= =?UTF-8?q?=EC=9E=AC=EC=83=9D=EC=84=B1=20=EB=B6=88=EA=B0=80=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to: #235 --- .../CommunityArticleCustomRepository.java | 9 +++++ .../CommunityArticleRepository.java | 8 +++- .../CommunityArticleRepositoryImpl.java | 27 +++++++++++++ .../service/CommunityArticleService.java | 40 +++++++++++++------ 4 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleCustomRepository.java create mode 100644 db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleRepositoryImpl.java diff --git a/db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleCustomRepository.java b/db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleCustomRepository.java new file mode 100644 index 0000000..38d57c1 --- /dev/null +++ b/db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleCustomRepository.java @@ -0,0 +1,9 @@ +package com.aliens.db.communityarticle.repository; + +import java.time.Instant; +import java.util.Optional; + +public interface CommunityArticleCustomRepository { + Optional findLatestArticleTimeByMemberId(Long memberId); + +} diff --git a/db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleRepository.java b/db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleRepository.java index 1e389f9..f2b399f 100644 --- a/db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleRepository.java +++ b/db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleRepository.java @@ -7,14 +7,20 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.Instant; import java.util.List; +import java.util.Optional; public interface CommunityArticleRepository - extends JpaRepository { + extends JpaRepository, CommunityArticleCustomRepository { Page findAllByCategory(ArticleCategory category, Pageable pageable); List findAllByTitleContainingOrContentContaining(String title, String content); + List findAllByMember(MemberEntity member); + Page findAllByCategoryAndTitleContainingOrContentContaining(ArticleCategory category, String title, String content, Pageable pageable); + Optional findLatestArticleTimeByMemberId(Long memberId); + } \ No newline at end of file diff --git a/db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleRepositoryImpl.java b/db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleRepositoryImpl.java new file mode 100644 index 0000000..ceb2c07 --- /dev/null +++ b/db/src/main/java/com/aliens/db/communityarticle/repository/CommunityArticleRepositoryImpl.java @@ -0,0 +1,27 @@ +package com.aliens.db.communityarticle.repository; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; + +import javax.persistence.EntityManager; +import java.time.Instant; +import java.util.Optional; + +import static com.aliens.db.communityarticle.entity.QCommunityArticleEntity.communityArticleEntity; + +@RequiredArgsConstructor + +public class CommunityArticleRepositoryImpl implements CommunityArticleCustomRepository { + private final EntityManager entityManager; + + @Override + public Optional findLatestArticleTimeByMemberId(Long memberId) { + JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager); + + return Optional.ofNullable(queryFactory + .select(communityArticleEntity.createdAt.max()) + .from(communityArticleEntity) + .where(communityArticleEntity.member.id.eq(memberId)) + .fetchOne()); + } +} diff --git a/membership/src/main/java/com/aliens/friendship/domain/article/community/service/CommunityArticleService.java b/membership/src/main/java/com/aliens/friendship/domain/article/community/service/CommunityArticleService.java index d3a8da1..8dec215 100644 --- a/membership/src/main/java/com/aliens/friendship/domain/article/community/service/CommunityArticleService.java +++ b/membership/src/main/java/com/aliens/friendship/domain/article/community/service/CommunityArticleService.java @@ -13,6 +13,7 @@ import com.aliens.friendship.domain.article.community.dto.CreateCommunityArticleRequest; import com.aliens.friendship.domain.article.community.dto.UpdateCommunityArticleRequest; import com.aliens.friendship.domain.article.dto.ArticleDto; +import com.aliens.friendship.domain.article.exception.ArticleCreationNotAllowedException; import com.aliens.friendship.domain.article.service.ArticleImageService; import com.aliens.friendship.global.error.InvalidResourceOwnerException; import com.aliens.friendship.global.error.ResourceNotFoundException; @@ -25,6 +26,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -102,22 +105,33 @@ public Long saveCommunityArticle( CreateCommunityArticleRequest request, UserDetails principal ) throws Exception { - CommunityArticleEntity communityArticle = communityArticleRepository.save( - request.toEntity(getMemberEntity(principal.getUsername())) - ); + MemberEntity member = getMemberEntity(principal.getUsername()); + Optional latestArticleTime = communityArticleRepository.findLatestArticleTimeByMemberId(member.getId()); - List imageUrls = request.getImageUrls(); - if (imageUrls != null && !imageUrls.isEmpty()) { - for (MultipartFile imageUrl : imageUrls) { - CommunityArticleImageEntity communityArticleImage = CommunityArticleImageEntity.of( - articleImageService.uploadProfileImage(imageUrl), - communityArticle - ); - communityArticleImageRepository.save(communityArticleImage); + boolean canCreateArticle = latestArticleTime.map( + time -> Duration.between(time, Instant.now()).toMinutes() >= 5 + ).orElse(true); + + if (canCreateArticle) { + CommunityArticleEntity communityArticle = communityArticleRepository.save( + request.toEntity(getMemberEntity(principal.getUsername())) + ); + + List imageUrls = request.getImageUrls(); + if (imageUrls != null && !imageUrls.isEmpty()) { + for (MultipartFile imageUrl : imageUrls) { + CommunityArticleImageEntity communityArticleImage = CommunityArticleImageEntity.of( + articleImageService.uploadProfileImage(imageUrl), + communityArticle + ); + communityArticleImageRepository.save(communityArticleImage); + } } - } - return communityArticle.getId(); + return communityArticle.getId(); + } else { + throw new ArticleCreationNotAllowedException(); + } } public void updateCommunityArticle(