Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

테크블로그 게시글 좋아요 기능 개발 #127

Merged
merged 9 commits into from
Oct 23, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,23 @@ public ResponseEntity<PagingResponse<PostResponse.Main>> searchPosts(@RequestPar
@RequestParam(value = "size", defaultValue = "20") int size) {
return ResponseEntity.ok().body(postService.searchPostList(keyword, page, size));
}


@PostMapping("/{postId}/like")
@Operation(summary = "게시글 좋아요 API", description = "게시글에 좋아요를 누른다. 이미 좋아요를 누른 게시글이면 예외가 발생한다.")
public ResponseEntity<Void> likePost(@TokenMember JwtMemberDetail jwtMemberDetail,
@PathVariable("postId") Long postId) {
postService.likePost(jwtMemberDetail.getId(), postId);
return ResponseEntity.ok().build();
}

@DeleteMapping("/{postId}/like")
@Operation(summary = "게시글 좋아요 취소 API", description = "게시글에 좋아요를 취소한다. 좋아요를 누르지 않은 게시글이면 예외가 발생한다.")
public ResponseEntity<Void> unlikePost(@TokenMember JwtMemberDetail jwtMemberDetail,
@PathVariable("postId") Long postId) {
postService.unlikePost(jwtMemberDetail.getId(), postId);
return ResponseEntity.ok().build();
}

@GetMapping("trending")
@Operation(summary = "카테고리별 인기글 5개 조회", description = "category가 null이면 전제를 조회한다.")
public ResponseEntity<List<PostResponse.Main>> getTrendingPosts(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static Main from(Post post) {
.category(post.getCategory().name())
.createAt(post.getPublishedAt())
.likeCount(post.getLikeCount())
.commentCount(post.getCommentList().size())
.commentCount(post.getCommentCount())
.sharedCount(post.getSharedCount())
.build();

Expand All @@ -70,8 +70,9 @@ public static class Detail {
private int sharedCount;
private boolean canDelete;
private boolean canModify;
private boolean isLiked;

public static Detail from(Post post, AccessModel access) {
public static Detail from(Post post, AccessModel access, boolean isLiked) {
int length = Math.min(post.getContent().length(), 20);
return Detail.builder()
.id(post.getId())
Expand All @@ -83,10 +84,11 @@ public static Detail from(Post post, AccessModel access) {
.authorName(post.getMember().getName())
.createAt(post.getPublishedAt())
.likeCount(post.getLikeCount())
.commentCount(post.getCommentList().size())
.commentCount(post.getCommentCount())
.sharedCount(post.getSharedCount())
.canDelete(access.canDelete())
.canModify(access.canModify())
.isLiked(isLiked)
.build();

}
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/gdsc_knu/official_homepage/entity/post/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,20 @@ public void update(PostRequest.Update postRequest) {
public boolean isSaved() {
return this.status.equals(PostStatus.SAVED);
}

public void addCommentCount() {
this.commentCount++;
}

public void subtractCommentCount(int deleteCount) {
this.commentCount =- deleteCount;
}

public void addLikeCount() {
this.likeCount++;
}

public void subtractLikeCount() {
this.likeCount--;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.gdsc_knu.official_homepage.entity.post;

import com.gdsc_knu.official_homepage.entity.Member;
import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class PostLike {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
private Post post;

@ManyToOne(fetch = FetchType.LAZY)
private Member member;

public static PostLike from(Post post, Member member) {
return PostLike.builder()
.post(post)
.member(member)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public enum ErrorCode {
// Post
POST_NOT_FOUND(404, HttpStatus.NOT_FOUND, "게시글을 찾을 수 없습니다."),
POST_FORBIDDEN(403, HttpStatus.FORBIDDEN, "게시글을 수정할 수 있는 권한이 없습니다."),
POST_ALREADY_LIKED(409, HttpStatus.CONFLICT, "이미 좋아요를 누른 게시글입니다."),
POST_NOT_LIKED(404, HttpStatus.NOT_FOUND, "좋아요를 누르지 않은 게시글입니다."),

// Comment
COMMENT_NOT_FOUND(404, HttpStatus.NOT_FOUND, "댓글을 찾을 수 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.gdsc_knu.official_homepage.repository;

import com.gdsc_knu.official_homepage.entity.post.PostLike;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface PostLikeRepository extends JpaRepository<PostLike, Long> {
Optional<PostLike> findByMemberIdAndPostId(Long memberId, Long postId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public void createComment(Long memberId, Long postId, CommentRequest.Create requ
Comment parent = getParentComment(request.getGroupId());
Comment comment = Comment.from(request.getContent(), member, post, parent);
commentRepository.save(comment);
post.addCommentCount();
}

private Comment getParentComment(Long parentId) {
Expand Down Expand Up @@ -91,6 +92,8 @@ public void deleteComment(Long memberId, Long commentId) {
if (!comment.getAuthor().getId().equals(memberId)) {
throw new CustomException(ErrorCode.COMMENT_FORBIDDEN);
}
int deleteCount = 1 + comment.getReplies().size();
commentRepository.delete(comment);
comment.getPost().subtractCommentCount(deleteCount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ public interface PostService {
PagingResponse<PostResponse.Main> searchPostList(String keyword, int page, int size);

List<PostResponse.Main> getTrendingPosts(Category category, int size);

void likePost(Long memberId, Long postId);

void unlikePost(Long memberId, Long postId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import com.gdsc_knu.official_homepage.entity.Member;
import com.gdsc_knu.official_homepage.entity.enumeration.Role;
import com.gdsc_knu.official_homepage.entity.post.Post;
import com.gdsc_knu.official_homepage.entity.post.PostLike;
import com.gdsc_knu.official_homepage.entity.post.enumeration.Category;
import com.gdsc_knu.official_homepage.entity.post.enumeration.PostStatus;
import com.gdsc_knu.official_homepage.exception.CustomException;
import com.gdsc_knu.official_homepage.exception.ErrorCode;
import com.gdsc_knu.official_homepage.repository.MemberRepository;
import com.gdsc_knu.official_homepage.repository.PostLikeRepository;
import com.gdsc_knu.official_homepage.repository.PostRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -31,6 +33,7 @@
public class PostServiceImpl implements PostService {
private final PostRepository postRepository;
private final MemberRepository memberRepository;
private final PostLikeRepository postLikeRepository;

/**
* 게시글 작성, 회원만 작성 가능
Expand Down Expand Up @@ -62,7 +65,8 @@ public PostResponse.Detail getPost(Long memberId, Long postId) {
throw new CustomException(ErrorCode.POST_NOT_FOUND);
}
AccessModel access = getPostAccess(memberId, post.getMember().getId());
return PostResponse.Detail.from(post, access);
boolean isLiked = memberId != 0L && postLikeRepository.findByMemberIdAndPostId(memberId, postId).isPresent();
return PostResponse.Detail.from(post, access, isLiked);
}

/**
Expand Down Expand Up @@ -142,6 +146,30 @@ public void deletePost(Long memberId, Long postId) {
postRepository.delete(post);
}

@Override
public void likePost(Long memberId, Long postId) {
postLikeRepository.findByMemberIdAndPostId(memberId, postId)
.ifPresent(postLike -> {
throw new CustomException(ErrorCode.POST_ALREADY_LIKED);
});
Post post = postRepository.findById(postId)
.orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
postLikeRepository.save(PostLike.from(post, member));
post.addLikeCount();
}

@Override
public void unlikePost(Long memberId, Long postId) {
PostLike postLike = postLikeRepository.findByMemberIdAndPostId(memberId, postId)
.orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_LIKED));
postLikeRepository.delete(postLike);
Post post = postRepository.findById(postId)
.orElseThrow(() -> new CustomException(ErrorCode.POST_NOT_FOUND));
post.subtractLikeCount();
}

@Override
@Transactional(readOnly = true)
@Cacheable(value = "trending-post", key = "'Is '+#category", unless="#result.size()<5")
Expand Down
Loading