From 740e2660a0bcfa1697aa6c995cd35adc4287770c Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sat, 16 Mar 2024 22:38:28 +0900 Subject: [PATCH 01/15] [feat] add article success code --- .../common/code/success/ArticleSuccessCode.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/common/code/success/ArticleSuccessCode.java b/src/main/java/com/newsnack/www/newsnackserver/common/code/success/ArticleSuccessCode.java index 607874d..c663acd 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/common/code/success/ArticleSuccessCode.java +++ b/src/main/java/com/newsnack/www/newsnackserver/common/code/success/ArticleSuccessCode.java @@ -11,7 +11,15 @@ public enum ArticleSuccessCode implements SuccessCode{ /** * 200 OK **/ - GET_ARTICLES_SUCCESS(HttpStatus.OK, "기사 조회에 성공했습니다"); + GET_ARTICLES_SUCCESS(HttpStatus.OK, "기사 조회에 성공했습니다"), + /** + * 201 Created + **/ + ARTICLE_LIKE_SUCCESS(HttpStatus.OK, "기사 좋아요 성공"), + /** + * 204 No Content + **/ + ARTICLE_LIKE_CANCEL_SUCCESS(HttpStatus.OK, "기사 좋아요 취소 성공"); private final HttpStatus httpStatus; private final String message; From a77198671113601d5e521f7e1e4b00fefc95f229 Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sat, 16 Mar 2024 22:38:44 +0900 Subject: [PATCH 02/15] [feat] add article failure code --- .../common/code/failure/ArticleFailureCode.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/common/code/failure/ArticleFailureCode.java b/src/main/java/com/newsnack/www/newsnackserver/common/code/failure/ArticleFailureCode.java index f7aff58..b49e44a 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/common/code/failure/ArticleFailureCode.java +++ b/src/main/java/com/newsnack/www/newsnackserver/common/code/failure/ArticleFailureCode.java @@ -16,7 +16,13 @@ public enum ArticleFailureCode implements FailureCode{ /** * 404 NOT FOUND */ - ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "뉴스를 찾을 수 없습니다"); + ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "뉴스를 찾을 수 없습니다"), + ARTICLE_HEART_NOT_FOUND(HttpStatus.NOT_FOUND, "기사 좋아요가 없습니다"), + + /** + * 409 CONFLICT + */ + ARTICLE_HEART_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 좋아요한 게시물입니다"); private final HttpStatus httpStatus; private final String message; From 4986786a5f493320b1a11459403028d8efdf3421 Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sat, 16 Mar 2024 22:39:12 +0900 Subject: [PATCH 03/15] [feat] create custom article exception --- .../common/exception/ArticleException.java | 4 +++- .../common/handler/NewSnackControllerAdvice.java | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/common/exception/ArticleException.java b/src/main/java/com/newsnack/www/newsnackserver/common/exception/ArticleException.java index 0176a4a..6ee61ec 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/common/exception/ArticleException.java +++ b/src/main/java/com/newsnack/www/newsnackserver/common/exception/ArticleException.java @@ -1,13 +1,15 @@ package com.newsnack.www.newsnackserver.common.exception; import com.newsnack.www.newsnackserver.common.code.failure.FailureCode; +import lombok.Getter; +@Getter public class ArticleException extends RuntimeException{ private final FailureCode failureCode; public ArticleException(FailureCode failureCode) { - super("[AuthException] : " + failureCode.getMessage()); + super("[ArticleException] : " + failureCode.getMessage()); this.failureCode = failureCode; } diff --git a/src/main/java/com/newsnack/www/newsnackserver/common/handler/NewSnackControllerAdvice.java b/src/main/java/com/newsnack/www/newsnackserver/common/handler/NewSnackControllerAdvice.java index 03953e3..e7ec2d3 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/common/handler/NewSnackControllerAdvice.java +++ b/src/main/java/com/newsnack/www/newsnackserver/common/handler/NewSnackControllerAdvice.java @@ -1,5 +1,6 @@ package com.newsnack.www.newsnackserver.common.handler; +import com.newsnack.www.newsnackserver.common.exception.ArticleException; import com.newsnack.www.newsnackserver.common.exception.AuthException; import com.newsnack.www.newsnackserver.common.exception.MemberException; import com.newsnack.www.newsnackserver.common.exception.NewSnackException; @@ -50,12 +51,21 @@ public ResponseEntity> handleMemberException(MemberException return ResponseEntity.status(e.getHttpStatusCode()) .body(NewSnackResponse.error(e.getFailureCode())); } + + @ExceptionHandler(ArticleException.class) + public ResponseEntity> handleMemberException(ArticleException e) { + log.info("handleMemberException() in NewSnackControllerAdvice throw MemberException : {}", e.getMessage()); + return ResponseEntity.status(e.getHttpStatusCode()) + .body(NewSnackResponse.error(e.getFailureCode())); + } + @ExceptionHandler(NewSnackException.class) public ResponseEntity> handleNewSnackException(NewSnackException e) { log.info("handleNewSnackException() in NewSnackControllerAdvice throw NewSnackException : {}", e.getMessage()); return ResponseEntity.status(e.getHttpStatusCode()) .body(NewSnackResponse.error(e.getFailureCode())); } + @ExceptionHandler(Exception.class) public ResponseEntity> handleException(Exception e) { log.error("handleException() in NewSnackControllerAdvice throw Exception : {} ,{}", e.getClass() ,e.getMessage()); From dddae87168d30aead73d4a883ef1009e6e6075ee Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sat, 16 Mar 2024 22:40:25 +0900 Subject: [PATCH 04/15] [feat] create functions for heartcount --- .../www/newsnackserver/domain/article/model/Article.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/domain/article/model/Article.java b/src/main/java/com/newsnack/www/newsnackserver/domain/article/model/Article.java index 52dac4b..b67d880 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/domain/article/model/Article.java +++ b/src/main/java/com/newsnack/www/newsnackserver/domain/article/model/Article.java @@ -30,6 +30,13 @@ public class Article extends BaseTimeEntity { @Enumerated(EnumType.STRING) private LocationCategory locationCategory; - @Formula("(select count(*) from article_heart where article_heart.article_id = id)") private int heartCount; + + public void increaseHeartCount() { + this.heartCount++; + } + + public void decreaseHeartCount() { + this.heartCount--; + } } From 35e9d13d5ca86213d654010e21533f0cf2df0eee Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sat, 16 Mar 2024 23:04:54 +0900 Subject: [PATCH 05/15] [feat] create builder in article heart --- .../domain/articleheart/model/ArticleHeart.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/newsnack/www/newsnackserver/domain/articleheart/model/ArticleHeart.java b/src/main/java/com/newsnack/www/newsnackserver/domain/articleheart/model/ArticleHeart.java index bc86422..71bf078 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/domain/articleheart/model/ArticleHeart.java +++ b/src/main/java/com/newsnack/www/newsnackserver/domain/articleheart/model/ArticleHeart.java @@ -4,6 +4,7 @@ import com.newsnack.www.newsnackserver.domain.member.model.Member; import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -23,4 +24,10 @@ public class ArticleHeart { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "article_id") private Article article; + + @Builder + public ArticleHeart(Member member, Article article) { + this.member = member; + this.article = article; + } } From 710c6e905252297e565cd8bd16116ef9751fd8bd Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sat, 16 Mar 2024 23:06:02 +0900 Subject: [PATCH 06/15] [feat] create logic for article like and cancel api --- .../repository/ArticleHeartJpaRepository.java | 5 +++ .../service/ArticleService.java | 39 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/domain/articleheart/repository/ArticleHeartJpaRepository.java b/src/main/java/com/newsnack/www/newsnackserver/domain/articleheart/repository/ArticleHeartJpaRepository.java index 6faf297..3db87f8 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/domain/articleheart/repository/ArticleHeartJpaRepository.java +++ b/src/main/java/com/newsnack/www/newsnackserver/domain/articleheart/repository/ArticleHeartJpaRepository.java @@ -1,8 +1,13 @@ package com.newsnack.www.newsnackserver.domain.articleheart.repository; +import com.newsnack.www.newsnackserver.domain.article.model.Article; import com.newsnack.www.newsnackserver.domain.articleheart.model.ArticleHeart; +import com.newsnack.www.newsnackserver.domain.member.model.Member; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface ArticleHeartJpaRepository extends JpaRepository { boolean existsByArticleIdAndMemberId(Long articleId, Long memberId); + Optional findByArticleAndMember(Article article, Member member); } diff --git a/src/main/java/com/newsnack/www/newsnackserver/service/ArticleService.java b/src/main/java/com/newsnack/www/newsnackserver/service/ArticleService.java index 4f01fad..92f6cb4 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/service/ArticleService.java +++ b/src/main/java/com/newsnack/www/newsnackserver/service/ArticleService.java @@ -1,14 +1,19 @@ package com.newsnack.www.newsnackserver.service; import com.newsnack.www.newsnackserver.common.code.failure.ArticleFailureCode; +import com.newsnack.www.newsnackserver.common.code.failure.MemberFailureCode; import com.newsnack.www.newsnackserver.common.exception.ArticleException; +import com.newsnack.www.newsnackserver.common.exception.MemberException; import com.newsnack.www.newsnackserver.domain.article.model.Article; +import com.newsnack.www.newsnackserver.domain.articleheart.model.ArticleHeart; import com.newsnack.www.newsnackserver.domain.articleheart.repository.ArticleHeartJpaRepository; +import com.newsnack.www.newsnackserver.domain.member.model.Member; +import com.newsnack.www.newsnackserver.domain.member.repository.MemberJpaRepository; import com.newsnack.www.newsnackserver.dto.response.ArticleIndividualResponse; import com.newsnack.www.newsnackserver.dto.response.ArticleMainPageResponse; import com.newsnack.www.newsnackserver.dto.response.ArticleResponse; import com.newsnack.www.newsnackserver.domain.article.model.LocationCategory; -import com.newsnack.www.newsnackserver.domain.article.model.SearchOrder; +import com.newsnack.www.newsnackserver.controller.parameter.SearchOrder; import com.newsnack.www.newsnackserver.domain.article.model.SectionCategory; import com.newsnack.www.newsnackserver.domain.article.repository.ArticleRepository; import lombok.RequiredArgsConstructor; @@ -28,6 +33,7 @@ public class ArticleService { private final ArticleRepository articleRepository; private final ArticleHeartJpaRepository articleHeartJpaRepository; + private final MemberJpaRepository memberJpaRepository; public List getArticles(SearchOrder order, SectionCategory sectionCategory, LocationCategory locationCategory, Integer page) { @@ -67,4 +73,35 @@ public List getMainArticles() { LocalDateTime oneWeekAgo = LocalDateTime.now().minusDays(7); return articleRepository.findTop5ByCreatedAtAfterOrderByHeartCountDescIdDesc(oneWeekAgo).stream().map(ArticleMainPageResponse::from).collect(Collectors.toList()); } + + @Transactional + public void likeArticle(Long articleId, Long memberId) { + Article article = articleRepository.getReferenceById(articleId); + Member member = memberJpaRepository.getReferenceById(memberId); + articleHeartJpaRepository.findByArticleAndMember(article, member).ifPresentOrElse( + (articleHeart) -> { + throw new ArticleException(ArticleFailureCode.ARTICLE_HEART_ALREADY_EXISTS); + }, + () -> { + articleHeartJpaRepository.save(ArticleHeart.builder().member(member).article(article).build()); + article.increaseHeartCount(); + } + ); + } + + @Transactional + public void cancelArticleLike(Long articleId, Long memberId) { + Article article = articleRepository.getReferenceById(articleId); + Member member = memberJpaRepository.getReferenceById(memberId); + articleHeartJpaRepository.findByArticleAndMember(article, member).ifPresentOrElse( + (articleHeart) -> { + articleHeartJpaRepository.delete(articleHeart); + article.decreaseHeartCount(); + }, + () -> { + throw new ArticleException(ArticleFailureCode.ARTICLE_HEART_NOT_FOUND); + } + ); + } } + From 5c878fafe07fab7b201c83fcd994027b0fdb5fb1 Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sat, 16 Mar 2024 23:06:12 +0900 Subject: [PATCH 07/15] [feat] create article like and cancel api --- .../controller/ArticleController.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/controller/ArticleController.java b/src/main/java/com/newsnack/www/newsnackserver/controller/ArticleController.java index 0da99b8..280b75c 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/controller/ArticleController.java +++ b/src/main/java/com/newsnack/www/newsnackserver/controller/ArticleController.java @@ -1,16 +1,16 @@ package com.newsnack.www.newsnackserver.controller; import com.newsnack.www.newsnackserver.annotation.MemberId; +import com.newsnack.www.newsnackserver.common.exception.ArticleException; import com.newsnack.www.newsnackserver.dto.response.ArticleIndividualResponse; import com.newsnack.www.newsnackserver.dto.response.ArticleMainPageResponse; import com.newsnack.www.newsnackserver.dto.response.ArticleResponse; import com.newsnack.www.newsnackserver.service.ArticleService; import com.newsnack.www.newsnackserver.common.code.failure.ArticleFailureCode; import com.newsnack.www.newsnackserver.common.code.success.ArticleSuccessCode; -import com.newsnack.www.newsnackserver.common.exception.NewSnackException; import com.newsnack.www.newsnackserver.common.response.NewSnackResponse; import com.newsnack.www.newsnackserver.domain.article.model.LocationCategory; -import com.newsnack.www.newsnackserver.domain.article.model.SearchOrder; +import com.newsnack.www.newsnackserver.controller.parameter.SearchOrder; import com.newsnack.www.newsnackserver.domain.article.model.SectionCategory; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -30,7 +30,7 @@ public NewSnackResponse> getArticles(@RequestParam SearchO @RequestParam(required = false)SectionCategory sectionCategory, @RequestParam(required = false)LocationCategory locationCategory) { if (order == null || page < 0 || (sectionCategory != null && locationCategory != null) ) { - throw new NewSnackException(ArticleFailureCode.INVALID_PARAMETER); + throw new ArticleException(ArticleFailureCode.INVALID_PARAMETER); } return NewSnackResponse.success(ArticleSuccessCode.GET_ARTICLES_SUCCESS, articleService.getArticles(order, sectionCategory, locationCategory, page)); } @@ -44,4 +44,15 @@ public NewSnackResponse getArticle(@PathVariable Long public NewSnackResponse> getMainArticles() { return NewSnackResponse.success(ArticleSuccessCode.GET_ARTICLES_SUCCESS, articleService.getMainArticles()); } + + @PostMapping("/{articleId}/likes") + public NewSnackResponse likeArticle(@PathVariable Long articleId, @MemberId Long memberId) { + articleService.likeArticle(articleId, memberId); + return NewSnackResponse.success(ArticleSuccessCode.ARTICLE_LIKE_SUCCESS); + } + @DeleteMapping("/{articleId}/likes") + public NewSnackResponse deleteArticleLike(@PathVariable Long articleId, @MemberId Long memberId) { + articleService.cancelArticleLike(articleId, memberId); + return NewSnackResponse.success(ArticleSuccessCode.ARTICLE_LIKE_CANCEL_SUCCESS); + } } From c04c1053463f4d1dfac18467da9c196cd5a88a8f Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sat, 16 Mar 2024 23:06:52 +0900 Subject: [PATCH 08/15] [refactor] move SearchOrder.java to controller.parameter package --- .../article/model => controller/parameter}/SearchOrder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/com/newsnack/www/newsnackserver/{domain/article/model => controller/parameter}/SearchOrder.java (76%) diff --git a/src/main/java/com/newsnack/www/newsnackserver/domain/article/model/SearchOrder.java b/src/main/java/com/newsnack/www/newsnackserver/controller/parameter/SearchOrder.java similarity index 76% rename from src/main/java/com/newsnack/www/newsnackserver/domain/article/model/SearchOrder.java rename to src/main/java/com/newsnack/www/newsnackserver/controller/parameter/SearchOrder.java index f448a40..ff4e7c4 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/domain/article/model/SearchOrder.java +++ b/src/main/java/com/newsnack/www/newsnackserver/controller/parameter/SearchOrder.java @@ -1,4 +1,4 @@ -package com.newsnack.www.newsnackserver.domain.article.model; +package com.newsnack.www.newsnackserver.controller.parameter; import lombok.Getter; import lombok.RequiredArgsConstructor; From 505244f4e0b76d24d541852d6d43f7b08c18c778 Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sun, 17 Mar 2024 02:36:14 +0900 Subject: [PATCH 09/15] [feat] create comment exception --- .../common/exception/CommentException.java | 18 ++++++++++++++++++ .../handler/NewSnackControllerAdvice.java | 12 ++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/newsnack/www/newsnackserver/common/exception/CommentException.java diff --git a/src/main/java/com/newsnack/www/newsnackserver/common/exception/CommentException.java b/src/main/java/com/newsnack/www/newsnackserver/common/exception/CommentException.java new file mode 100644 index 0000000..3ad6c84 --- /dev/null +++ b/src/main/java/com/newsnack/www/newsnackserver/common/exception/CommentException.java @@ -0,0 +1,18 @@ +package com.newsnack.www.newsnackserver.common.exception; + +import com.newsnack.www.newsnackserver.common.code.failure.FailureCode; +import lombok.Getter; + +@Getter +public class CommentException extends RuntimeException { + private final FailureCode failureCode; + + public CommentException(FailureCode failureCode) { + super("[CommentException] : " + failureCode.getMessage()); + this.failureCode = failureCode; + } + + public int getHttpStatusCode() { + return failureCode.getHttpStatus().value(); + } +} diff --git a/src/main/java/com/newsnack/www/newsnackserver/common/handler/NewSnackControllerAdvice.java b/src/main/java/com/newsnack/www/newsnackserver/common/handler/NewSnackControllerAdvice.java index e7ec2d3..65211b7 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/common/handler/NewSnackControllerAdvice.java +++ b/src/main/java/com/newsnack/www/newsnackserver/common/handler/NewSnackControllerAdvice.java @@ -1,9 +1,6 @@ package com.newsnack.www.newsnackserver.common.handler; -import com.newsnack.www.newsnackserver.common.exception.ArticleException; -import com.newsnack.www.newsnackserver.common.exception.AuthException; -import com.newsnack.www.newsnackserver.common.exception.MemberException; -import com.newsnack.www.newsnackserver.common.exception.NewSnackException; +import com.newsnack.www.newsnackserver.common.exception.*; import com.newsnack.www.newsnackserver.common.response.NewSnackResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -59,6 +56,13 @@ public ResponseEntity> handleMemberException(ArticleExceptio .body(NewSnackResponse.error(e.getFailureCode())); } + @ExceptionHandler(CommentException.class) + public ResponseEntity> handleCommentException(CommentException e) { + log.info("handleCommentException() in NewSnackControllerAdvice throw CommentException : {}", e.getMessage()); + return ResponseEntity.status(e.getHttpStatusCode()) + .body(NewSnackResponse.error(e.getFailureCode())); + } + @ExceptionHandler(NewSnackException.class) public ResponseEntity> handleNewSnackException(NewSnackException e) { log.info("handleNewSnackException() in NewSnackControllerAdvice throw NewSnackException : {}", e.getMessage()); From 8be86165b4943d3df22acf203412794c57ef41a4 Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sun, 17 Mar 2024 02:36:37 +0900 Subject: [PATCH 10/15] [feat] add success and failure code for comment --- .../common/code/failure/CommentFailureCode.java | 7 ++++++- .../common/code/success/CommentSuccessCode.java | 5 +++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/common/code/failure/CommentFailureCode.java b/src/main/java/com/newsnack/www/newsnackserver/common/code/failure/CommentFailureCode.java index ef7ac35..33c5410 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/common/code/failure/CommentFailureCode.java +++ b/src/main/java/com/newsnack/www/newsnackserver/common/code/failure/CommentFailureCode.java @@ -17,7 +17,12 @@ public enum CommentFailureCode implements FailureCode{ /** * 404 Not Found **/ - COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "댓글을 찾을 수 없습니다"); + COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "댓글을 찾을 수 없습니다"), + COMMENT_LIKE_NOT_FOUND(HttpStatus.NOT_FOUND, "댓글 좋아요가 없습니다."), + /** + * 409 Conflict + **/ + COMMENT_HEART_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 좋아요한 댓글입니다"); private final HttpStatus httpStatus; private final String message; diff --git a/src/main/java/com/newsnack/www/newsnackserver/common/code/success/CommentSuccessCode.java b/src/main/java/com/newsnack/www/newsnackserver/common/code/success/CommentSuccessCode.java index 8c5605f..e8ebc7f 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/common/code/success/CommentSuccessCode.java +++ b/src/main/java/com/newsnack/www/newsnackserver/common/code/success/CommentSuccessCode.java @@ -15,12 +15,13 @@ public enum CommentSuccessCode implements SuccessCode{ * 201 Created **/ COMMENT_CREATED(HttpStatus.CREATED, "댓글 작성 성공"), - + COMMENT_LIKE_SUCCESS(HttpStatus.CREATED, "댓글 좋아요 성공"), /** * 204 No Content **/ COMMENT_DELETED_SUCCESS(HttpStatus.NO_CONTENT, "댓글 삭제 성공"), - COMMENT_UPDATED(HttpStatus.NO_CONTENT, "댓글 수정 성공"); + COMMENT_UPDATED(HttpStatus.NO_CONTENT, "댓글 수정 성공"), + COMMENT_LIKE_CANCEL_SUCCESS(HttpStatus.NO_CONTENT, "댓글 좋아요 취소 성공"); private final HttpStatus httpStatus; private final String message; From 388e1577f4133b3d871564e6dfceff55ea67b0fe Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sun, 17 Mar 2024 02:37:18 +0900 Subject: [PATCH 11/15] [feat] add functions for heartcount --- .../www/newsnackserver/domain/comment/model/Comment.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/domain/comment/model/Comment.java b/src/main/java/com/newsnack/www/newsnackserver/domain/comment/model/Comment.java index 0145a29..6d1f3f7 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/domain/comment/model/Comment.java +++ b/src/main/java/com/newsnack/www/newsnackserver/domain/comment/model/Comment.java @@ -36,7 +36,6 @@ public class Comment extends BaseTimeEntity { private String content; - @Formula("(select count(*) from comment_heart where comment_heart.comment_id = id)") private int heartCount; @Builder @@ -46,6 +45,14 @@ public Comment(Article article, Member member, String content) { this.content = content; } + public void increaseHeartCount() { + this.heartCount++; + } + + public void decreaseHeartCount() { + this.heartCount--; + } + public void updateContent(String content) { this.content = content; } From 3ada0091f2aaa270c613414a54681b531731bf88 Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sun, 17 Mar 2024 02:38:28 +0900 Subject: [PATCH 12/15] [feat] create logic for like and cancel like of comment api --- .../commentheart/model/CommentHeart.java | 7 ++++ .../repository/CommentHeartJpaRepository.java | 5 +++ .../service/CommentService.java | 42 +++++++++++++++++-- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/domain/commentheart/model/CommentHeart.java b/src/main/java/com/newsnack/www/newsnackserver/domain/commentheart/model/CommentHeart.java index fa29a72..0eca9da 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/domain/commentheart/model/CommentHeart.java +++ b/src/main/java/com/newsnack/www/newsnackserver/domain/commentheart/model/CommentHeart.java @@ -5,6 +5,7 @@ import com.newsnack.www.newsnackserver.domain.member.model.Member; import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -24,4 +25,10 @@ public class CommentHeart extends BaseTimeEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") private Member member; + + @Builder + public CommentHeart(Comment comment, Member member) { + this.comment = comment; + this.member = member; + } } diff --git a/src/main/java/com/newsnack/www/newsnackserver/domain/commentheart/repository/CommentHeartJpaRepository.java b/src/main/java/com/newsnack/www/newsnackserver/domain/commentheart/repository/CommentHeartJpaRepository.java index 4421f5f..2f660ec 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/domain/commentheart/repository/CommentHeartJpaRepository.java +++ b/src/main/java/com/newsnack/www/newsnackserver/domain/commentheart/repository/CommentHeartJpaRepository.java @@ -1,7 +1,12 @@ package com.newsnack.www.newsnackserver.domain.commentheart.repository; +import com.newsnack.www.newsnackserver.domain.comment.model.Comment; import com.newsnack.www.newsnackserver.domain.commentheart.model.CommentHeart; +import com.newsnack.www.newsnackserver.domain.member.model.Member; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface CommentHeartJpaRepository extends JpaRepository { + Optional findByCommentAndMember(Comment comment, Member member); } diff --git a/src/main/java/com/newsnack/www/newsnackserver/service/CommentService.java b/src/main/java/com/newsnack/www/newsnackserver/service/CommentService.java index e7545dd..239a1bc 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/service/CommentService.java +++ b/src/main/java/com/newsnack/www/newsnackserver/service/CommentService.java @@ -3,12 +3,17 @@ import com.newsnack.www.newsnackserver.common.code.failure.ArticleFailureCode; import com.newsnack.www.newsnackserver.common.code.failure.CommentFailureCode; import com.newsnack.www.newsnackserver.common.code.failure.MemberFailureCode; +import com.newsnack.www.newsnackserver.common.exception.ArticleException; +import com.newsnack.www.newsnackserver.common.exception.CommentException; +import com.newsnack.www.newsnackserver.common.exception.MemberException; import com.newsnack.www.newsnackserver.common.exception.NewSnackException; import com.newsnack.www.newsnackserver.domain.article.model.Article; -import com.newsnack.www.newsnackserver.domain.article.model.SearchOrder; +import com.newsnack.www.newsnackserver.controller.parameter.SearchOrder; import com.newsnack.www.newsnackserver.domain.article.repository.ArticleRepository; import com.newsnack.www.newsnackserver.domain.comment.model.Comment; import com.newsnack.www.newsnackserver.domain.comment.repository.CommentJpaRepository; +import com.newsnack.www.newsnackserver.domain.commentheart.model.CommentHeart; +import com.newsnack.www.newsnackserver.domain.commentheart.repository.CommentHeartJpaRepository; import com.newsnack.www.newsnackserver.domain.member.model.Member; import com.newsnack.www.newsnackserver.domain.member.repository.MemberJpaRepository; import com.newsnack.www.newsnackserver.dto.CommentRequest; @@ -26,12 +31,13 @@ public class CommentService { private final CommentJpaRepository commentJpaRepository; private final ArticleRepository articleRepository; private final MemberJpaRepository memberJpaRepository; + private final CommentHeartJpaRepository commentHeartJpaRepository; @Transactional public void createComment(Long articleId, CommentRequest commentRequest, Long memberId) { - Article article = articleRepository.findById(articleId).orElseThrow(() -> new NewSnackException(ArticleFailureCode.ARTICLE_NOT_FOUND)); - Member member = memberJpaRepository.findById(memberId).orElseThrow(() -> new NewSnackException(MemberFailureCode.MEMBER_NOT_FOUND)); + Article article = articleRepository.findById(articleId).orElseThrow(() -> new ArticleException(ArticleFailureCode.ARTICLE_NOT_FOUND)); + Member member = memberJpaRepository.findById(memberId).orElseThrow(() -> new MemberException(MemberFailureCode.MEMBER_NOT_FOUND)); Comment comment = Comment.builder() .article(article) .member(member) @@ -85,4 +91,34 @@ boolean isLikedByMe(Comment comment, Long memberId) { boolean isMyComment(Comment comment, Long memberId) { return comment.getMember().getId().equals(memberId); } + + @Transactional + public void likeComment(Long commentId, Long memberId) { + Comment comment = commentJpaRepository.getReferenceById(commentId); + Member member = memberJpaRepository.getReferenceById(memberId); + commentHeartJpaRepository.findByCommentAndMember(comment, member).ifPresentOrElse( + (commentHeart) -> { + throw new CommentException(CommentFailureCode.COMMENT_HEART_ALREADY_EXISTS); + }, + () -> { + commentHeartJpaRepository.save(CommentHeart.builder().member(member).comment(comment).build()); + comment.increaseHeartCount(); + } + ); + } + + @Transactional + public void cancelCommentLike(Long commentId, Long memberId) { + Comment comment = commentJpaRepository.getReferenceById(commentId); + Member member = memberJpaRepository.getReferenceById(memberId); + commentHeartJpaRepository.findByCommentAndMember(comment, member).ifPresentOrElse( + (commentHeart) -> { + commentHeartJpaRepository.delete(commentHeart); + comment.decreaseHeartCount(); + }, + () -> { + throw new CommentException(CommentFailureCode.COMMENT_LIKE_NOT_FOUND); + } + ); + } } From f5952fd637f163bdb82edce683152cb3651304bb Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sun, 17 Mar 2024 02:38:46 +0900 Subject: [PATCH 13/15] [feat] create like comment api and cancel like of comment api --- .../newsnackserver/controller/CommentController.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/controller/CommentController.java b/src/main/java/com/newsnack/www/newsnackserver/controller/CommentController.java index 1af67b4..911bf82 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/controller/CommentController.java +++ b/src/main/java/com/newsnack/www/newsnackserver/controller/CommentController.java @@ -3,7 +3,7 @@ import com.newsnack.www.newsnackserver.annotation.MemberId; import com.newsnack.www.newsnackserver.common.code.success.CommentSuccessCode; import com.newsnack.www.newsnackserver.common.response.NewSnackResponse; -import com.newsnack.www.newsnackserver.domain.article.model.SearchOrder; +import com.newsnack.www.newsnackserver.controller.parameter.SearchOrder; import com.newsnack.www.newsnackserver.dto.CommentRequest; import com.newsnack.www.newsnackserver.dto.response.CommentResponse; import com.newsnack.www.newsnackserver.service.CommentService; @@ -46,4 +46,14 @@ public NewSnackResponse deleteComment(@PathVariable Long commentId, @MemberId return NewSnackResponse.success(CommentSuccessCode.COMMENT_DELETED_SUCCESS); } + @PostMapping("/comments/{commentId}/likes")//comment likecount 갱실 분실 문제 해결하기 + public NewSnackResponse likeComment(@PathVariable Long commentId, @MemberId Long memberId) { + commentService.likeComment(commentId, memberId); + return NewSnackResponse.success(CommentSuccessCode.COMMENT_LIKE_SUCCESS); + } + @DeleteMapping("/comments/{commentId}/likes")//나중에 comment에 likecount 넣게 되면 -> comment.likecount++-- 하고 동시성 해결하기. + public NewSnackResponse cancelCommentLike(@PathVariable Long commentId, @MemberId Long memberId) { + commentService.cancelCommentLike(commentId, memberId); + return NewSnackResponse.success(CommentSuccessCode.COMMENT_LIKE_CANCEL_SUCCESS); + } } From 9fa0f9b99e2977e1e4139501107d1ed65adbc262 Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sun, 17 Mar 2024 02:40:28 +0900 Subject: [PATCH 14/15] [refactor] change exception type from newsnackexception to commentexception --- .../newsnack/www/newsnackserver/service/CommentService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/service/CommentService.java b/src/main/java/com/newsnack/www/newsnackserver/service/CommentService.java index 239a1bc..ab49373 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/service/CommentService.java +++ b/src/main/java/com/newsnack/www/newsnackserver/service/CommentService.java @@ -48,7 +48,7 @@ public void createComment(Long articleId, CommentRequest commentRequest, Long me @Transactional public void deleteComment(Long commentId, Long memberId) { - Comment comment = commentJpaRepository.findById(commentId).orElseThrow(() -> new NewSnackException(CommentFailureCode.COMMENT_NOT_FOUND)); + Comment comment = commentJpaRepository.findById(commentId).orElseThrow(() -> new CommentException(CommentFailureCode.COMMENT_NOT_FOUND)); if (!comment.getMember().getId().equals(memberId)) { throw new NewSnackException(CommentFailureCode.DELETE_NOT_AUTHORIZED); } @@ -57,7 +57,7 @@ public void deleteComment(Long commentId, Long memberId) { @Transactional public void updateComment(Long commentId, CommentRequest commentRequest, Long memberId) { - Comment comment = commentJpaRepository.findById(commentId).orElseThrow(() -> new NewSnackException(CommentFailureCode.COMMENT_NOT_FOUND)); + Comment comment = commentJpaRepository.findById(commentId).orElseThrow(() -> new CommentException(CommentFailureCode.COMMENT_NOT_FOUND)); if (!comment.getMember().getId().equals(memberId)) { throw new NewSnackException(CommentFailureCode.UPDATE_NOT_AUTHORIZED); } From 34367cd7cd2081c937fb8532658874c3284d1f53 Mon Sep 17 00:00:00 2001 From: Parkjyun Date: Sun, 17 Mar 2024 02:41:31 +0900 Subject: [PATCH 15/15] [refactor] delete unused import statement --- .../www/newsnackserver/domain/article/model/Article.java | 1 - .../www/newsnackserver/domain/comment/model/Comment.java | 1 - .../com/newsnack/www/newsnackserver/service/ArticleService.java | 2 -- 3 files changed, 4 deletions(-) diff --git a/src/main/java/com/newsnack/www/newsnackserver/domain/article/model/Article.java b/src/main/java/com/newsnack/www/newsnackserver/domain/article/model/Article.java index b67d880..536a660 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/domain/article/model/Article.java +++ b/src/main/java/com/newsnack/www/newsnackserver/domain/article/model/Article.java @@ -5,7 +5,6 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.annotations.Formula; @Entity @Getter diff --git a/src/main/java/com/newsnack/www/newsnackserver/domain/comment/model/Comment.java b/src/main/java/com/newsnack/www/newsnackserver/domain/comment/model/Comment.java index 6d1f3f7..e0b6500 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/domain/comment/model/Comment.java +++ b/src/main/java/com/newsnack/www/newsnackserver/domain/comment/model/Comment.java @@ -9,7 +9,6 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.annotations.Formula; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/newsnack/www/newsnackserver/service/ArticleService.java b/src/main/java/com/newsnack/www/newsnackserver/service/ArticleService.java index 92f6cb4..6d68514 100644 --- a/src/main/java/com/newsnack/www/newsnackserver/service/ArticleService.java +++ b/src/main/java/com/newsnack/www/newsnackserver/service/ArticleService.java @@ -1,9 +1,7 @@ package com.newsnack.www.newsnackserver.service; import com.newsnack.www.newsnackserver.common.code.failure.ArticleFailureCode; -import com.newsnack.www.newsnackserver.common.code.failure.MemberFailureCode; import com.newsnack.www.newsnackserver.common.exception.ArticleException; -import com.newsnack.www.newsnackserver.common.exception.MemberException; import com.newsnack.www.newsnackserver.domain.article.model.Article; import com.newsnack.www.newsnackserver.domain.articleheart.model.ArticleHeart; import com.newsnack.www.newsnackserver.domain.articleheart.repository.ArticleHeartJpaRepository;