Skip to content

Commit

Permalink
Merge pull request #129 from Me1tingPot/feature/#39
Browse files Browse the repository at this point in the history
โœจ [Feature] CommentํŽ˜์ด์ง€๋„ค์ด์…˜
  • Loading branch information
moonyaeyoon committed Jul 24, 2024
2 parents 220617e + b70bf3b commit 0bd7558
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@ public ResponseEntity<ResponseData<CommentResponseDTO.CreateCommentResultDTO>> u
}
}

@Operation(summary = "๋Œ“๊ธ€ ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ")
@GetMapping("/list/{postId}")
public ResponseEntity<ResponseData<CommentResponseDTO.CommentsListDTO>> getCommentsList ( @CurrentUser Account account, @PathVariable Long postId,
@RequestParam(required = false) Long cursor,
@RequestParam(defaultValue = "10") int pageSize){
try{
return ResponseData.toResponseEntity(ResponseCode.READ_COMMENTS_LIST_SUCCESS, commentService.getCommentsList(account,postId,cursor,pageSize));
}catch (NoSuchElementException e) {
return ResponseData.toResponseEntity(ResponseCode.COMMENT_UPDATE_FAIL, null);
}
}


}


Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import meltingpot.server.domain.entity.post.Post;

import java.util.List;
import java.util.stream.Collectors;

@RequiredArgsConstructor
public class CommentConverter {
Expand Down Expand Up @@ -38,22 +39,25 @@ public static CommentResponseDTO.CreateCommentResultDTO toCreateCommentResult(St
public static CommentResponseDTO.CommentDetailDTO toCommentDetailDTO(Comment comment){
return CommentResponseDTO.CommentDetailDTO.builder()
.commentId(comment.getId())
.parentId(comment.getParent() != null ? comment.getParent().getId() : null)
.userId(comment.getAccount().getId())
.content(comment.getContent())
.name(comment.getAccount().getName())
.isAnonymous(comment.getIsAnonymous())
.updatedAt(comment.getUpdatedAt())
.children(comment.getChildren() != null ? toCommentDetailDTOList(comment.getChildren()) : null)
.build();
}

public static CommentResponseDTO.ParentCommentDTO toParentCommentDTO(Comment parentComment,List<CommentResponseDTO.CommentDetailDTO>childrenComments){
return CommentResponseDTO.ParentCommentDTO.builder()
.parentComment(toCommentDetailDTO(parentComment))
.childrenComments(childrenComments)
.build();
public static List<CommentResponseDTO.CommentDetailDTO> toCommentDetailDTOList(List<Comment> comments) {
return comments.stream()
.map(CommentConverter::toCommentDetailDTO)
.collect(Collectors.toList());
}

public static CommentResponseDTO.CommentsListDTO toCommentsListDTO(List<CommentResponseDTO.ParentCommentDTO> parentComments, Long nextCursor, Boolean isLast){
public static CommentResponseDTO.CommentsListDTO toCommentsListDTO(List<CommentResponseDTO.CommentDetailDTO> comments, Long nextCursor, Boolean isLast){
return CommentResponseDTO.CommentsListDTO.builder()
.parentComments(parentComments)
.comments(comments)
.nextCursor(nextCursor)
.isLast(isLast)
.build();
Expand Down
25 changes: 15 additions & 10 deletions src/main/java/meltingpot/server/comment/dto/CommentResponseDTO.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package meltingpot.server.comment.dto;

import lombok.*;
import meltingpot.server.domain.entity.comment.Comment;

import java.time.LocalDateTime;
import java.util.List;
Expand All @@ -25,32 +26,36 @@ public static class CreateCommentResultDTO{
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public static class CommentDetailDTO {
private Long commentId;
private Long parentId;
private Long userId;
private String content;
private String name;
private Boolean isAnonymous;
private String imageUrl;
private LocalDateTime updatedAt;
private List<CommentDetailDTO> children;
}


@Builder
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public static class ParentCommentDTO{
private CommentDetailDTO parentComment;
private List<CommentDetailDTO> childrenComments;
}

@Builder
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public static class CommentsListDTO{
private List<ParentCommentDTO> parentComments;
private List<CommentDetailDTO> comments;
private Long nextCursor;
private Boolean isLast;
}

public class CommentQueueItem {
public Comment parent;
public List<Comment> children;

public CommentQueueItem(Comment parent, List<Comment> children) {
this.parent = parent;
this.children = children;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public interface CommentService {
CommentResponseDTO.CreateCommentResultDTO createChildComment (CommentRequestDTO.CreateCommentDTO createCommentDTO, Account account, Long commentId);

CommentResponseDTO.CreateCommentResultDTO updateComment (CommentRequestDTO.CreateCommentDTO updateCommentDTO,Account account, Long commentId);

CommentResponseDTO.CommentsListDTO getCommentsList(Account account, Long postId, Long cursor, int pageSize);
}
154 changes: 100 additions & 54 deletions src/main/java/meltingpot/server/comment/service/CommentServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
import meltingpot.server.domain.repository.CommentRepository;
import meltingpot.server.domain.repository.PostRepository;
import meltingpot.server.util.r2.FileService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Collections;
import java.util.*;
import java.util.stream.Collectors;

import static meltingpot.server.comment.converter.CommentConverter.toComment;
import static meltingpot.server.comment.converter.CommentConverter.toCreateCommentResult;
import static meltingpot.server.comment.converter.CommentConverter.*;
import static meltingpot.server.comment.converter.CommentImageConverter.toCommentImage;

@Service
Expand All @@ -38,8 +38,7 @@ public class CommentServiceImpl implements CommentService {


@Override
//
public CommentResponseDTO.CreateCommentResultDTO createComment (CommentRequestDTO.CreateCommentDTO createCommentDTO, Account account, Long postId) {
public CommentResponseDTO.CreateCommentResultDTO createComment(CommentRequestDTO.CreateCommentDTO createCommentDTO, Account account, Long postId) {
Post post = findPostById(postId);
Comment comment = toComment(createCommentDTO, account, post);
return processCommentCreation(createCommentDTO, account, comment);
Expand All @@ -52,58 +51,92 @@ public CommentResponseDTO.CreateCommentResultDTO createChildComment(CommentReque
return processCommentCreation(createCommentDTO, account, childComment);
}

// @Override
// public CommentResponseDTO.CreateCommentResultDTO updateComment (CommentRequestDTO.CreateCommentDTO updateCommentDTO, Account account,Long commentId){
// Comment comment = findCommentById(commentId);
// Post post = comment.getPost();
// String newImageKey = updateCommentDTO.getImageKey();
// CommentImage oldCommentImage = comment.getCommentImage();
// if (newImageKey == null || newImageKey.isEmpty()) {
// if (oldCommentImage != null) {
// commentImageRepository.delete(oldCommentImage);
// comment.setCommentImage(null);
// }
// } else {
// comment = toComment(updateCommentDTO,account,post);
// }
// return processCommentCreation(updateCommentDTO, account, comment);
// }
@Override
public CommentResponseDTO.CreateCommentResultDTO updateComment(CommentRequestDTO.CreateCommentDTO updateCommentDTO, Account account, Long commentId) {
// ๋Œ“๊ธ€์„ ID๋กœ ์ฐพ๊ธฐ
Comment comment = findCommentById(commentId);

// ๋Œ“๊ธ€ ๋‚ด์šฉ ์—…๋ฐ์ดํŠธ
comment.setContent(updateCommentDTO.getContent());

// ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€ ํ‚ค์™€ ๊ธฐ์กด ๋Œ“๊ธ€ ์ด๋ฏธ์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ
String newImageKey = updateCommentDTO.getImageKey();
CommentImage oldCommentImage = comment.getCommentImage();

if (newImageKey == null || newImageKey.isEmpty()) {
// ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€ ํ‚ค๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ๊ธฐ์กด ์ด๋ฏธ์ง€ ์‚ญ์ œ
if (oldCommentImage != null) {
commentImageRepository.delete(oldCommentImage);
comment.setCommentImage(null);
}
} else {
// ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€ ํ‚ค๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์ด๋ฏธ์ง€ ์—…๋ฐ์ดํŠธ ๋˜๋Š” ์ƒ์„ฑ
if (oldCommentImage != null) {
oldCommentImage.setImageKey(newImageKey);
commentImageRepository.save(oldCommentImage);
@Override
public CommentResponseDTO.CreateCommentResultDTO updateComment(CommentRequestDTO.CreateCommentDTO updateCommentDTO, Account account, Long commentId) {
// ๋Œ“๊ธ€์„ ID๋กœ ์ฐพ๊ธฐ
Comment comment = findCommentById(commentId);

// ๋Œ“๊ธ€ ๋‚ด์šฉ ์—…๋ฐ์ดํŠธ
comment.setContent(updateCommentDTO.getContent());

// ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€ ํ‚ค์™€ ๊ธฐ์กด ๋Œ“๊ธ€ ์ด๋ฏธ์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ
String newImageKey = updateCommentDTO.getImageKey();
CommentImage oldCommentImage = comment.getCommentImage();

if (newImageKey == null || newImageKey.isEmpty()) {
// ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€ ํ‚ค๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ๊ธฐ์กด ์ด๋ฏธ์ง€ ์‚ญ์ œ
if (oldCommentImage != null) {
commentImageRepository.delete(oldCommentImage);
comment.setCommentImage(null);
}
} else {
CommentImage newCommentImage = toCommentImage(updateCommentDTO, account, comment);
comment.setCommentImage(newCommentImage);
// ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€ ํ‚ค๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ์ด๋ฏธ์ง€ ์—…๋ฐ์ดํŠธ ๋˜๋Š” ์ƒ์„ฑ
if (oldCommentImage != null) {
oldCommentImage.setImageKey(newImageKey);
commentImageRepository.save(oldCommentImage);
} else {
CommentImage newCommentImage = toCommentImage(updateCommentDTO, account, comment);
comment.setCommentImage(newCommentImage);
}
}


// ๋Œ“๊ธ€ ์ €์žฅ
commentRepository.save(comment);

// ๊ฒฐ๊ณผ DTO ์ƒ์„ฑ ๋ฐ ๋ฐ˜ํ™˜
String commentImgUrl = newImageKey != null && !newImageKey.isEmpty() ? fileService.getCdnUrl("comment", newImageKey) : null;
return toCreateCommentResult(commentImgUrl, comment);
}

// ๋Œ“๊ธ€ ์ €์žฅ
commentRepository.save(comment);

// ๊ฒฐ๊ณผ DTO ์ƒ์„ฑ ๋ฐ ๋ฐ˜ํ™˜
String commentImgUrl = newImageKey != null && !newImageKey.isEmpty() ? fileService.getCdnUrl("comment", newImageKey) : null;
return toCreateCommentResult(commentImgUrl, comment);
}
public CommentResponseDTO.CommentsListDTO getCommentsList(Account account, Long postId, Long cursor, int pageSize) {
// Retrieve the parent comment based on cursor
Comment parentComment = getParentComment(cursor, postId);

// If no parent comment found, throw an exception or handle accordingly
if (parentComment == null) {
throw new NoSuchElementException("No parent comment found for the given cursor.");
}

// Retrieve child comments for the parent comment
List<Comment> childComments = getChildComments(parentComment, pageSize - 1); // -1 for the parent comment

// Determine the next cursor and if it's the last page
Long nextCursor = determineNextCursor(parentComment, childComments, pageSize);
boolean isLast = nextCursor == null;

// Convert parent comment to DTO
CommentResponseDTO.CommentDetailDTO parentCommentDTO = CommentConverter.toCommentDetailDTO(parentComment);

// Convert child comments to DTOs
List<CommentResponseDTO.CommentDetailDTO> childCommentDTOs = CommentConverter.toCommentDetailDTOList(childComments);

// Add the parent comment DTO at the beginning of the list
childCommentDTOs.add(0, parentCommentDTO);

// Return the final DTO list with next cursor and isLast flag
return CommentResponseDTO.CommentsListDTO.builder()
.comments(childCommentDTOs)
.nextCursor(nextCursor)
.isLast(isLast)
.build();
}


private Comment getParentComment(Long cursor, Long postId) {
if (cursor == null) {
// Fetch the first parent comment if no cursor is provided
return commentRepository.findFirstByPostIdAndParentIsNull(postId).orElse(null);
} else {
// Fetch the parent comment based on the cursor
return commentRepository.findById(cursor).orElse(null);
}
}

private List<Comment> getChildComments(Comment parentComment, int pageSize) {
return commentRepository.findByParent(parentComment, PageRequest.of(0, pageSize));
}

private CommentResponseDTO.CreateCommentResultDTO processCommentCreation(CommentRequestDTO.CreateCommentDTO createCommentDTO, Account account, Comment comment) {
String commentImgUrl = null;
Expand All @@ -120,7 +153,7 @@ private CommentResponseDTO.CreateCommentResultDTO processCommentCreation(Comment

private Comment findCommentById(Long commentId) {
return commentRepository.findById(commentId)
.orElseThrow(() -> new RuntimeException("๋Œ“๊ธ€์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."));
.orElseThrow(() -> new RuntimeException("๋Œ“๊ธ€์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."));
}


Expand All @@ -134,4 +167,17 @@ private Post findPostById(Long postId) {
.orElseThrow(() -> new RuntimeException("๊ฒŒ์‹œ๋ฌผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."));
}

private Long determineNextCursor(Comment parentComment, List<Comment> childComments, int pageSize) {
if (childComments.size() < pageSize - 1) {
// ์ž์‹ ๋Œ“๊ธ€์˜ ์ˆ˜๊ฐ€ ํŽ˜์ด์ง€ ํฌ๊ธฐ๋ณด๋‹ค ์ ์œผ๋ฉด, ๋‹ค์Œ ๋ถ€๋ชจ ๋Œ“๊ธ€์„ ๊ฐ€์ ธ์˜จ๋‹ค.
Optional<Comment> nextParentComment = commentRepository.findNextParentComment(parentComment.getId(), parentComment.getPost().getId());
return nextParentComment.map(Comment::getId).orElse(null);
} else {
// ๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฐ€์ ธ์˜จ ์ž์‹ ๋Œ“๊ธ€์˜ ID๋ฅผ ๋‹ค์Œ ์ปค์„œ๋กœ ์„ค์ •ํ•œ๋‹ค.
return childComments.get(childComments.size() - 1).getId();
}
}
}



Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.persistence.*;
import lombok.*;
import meltingpot.server.comment.dto.CommentResponseDTO;
import meltingpot.server.domain.entity.Account;
import meltingpot.server.domain.entity.post.Post;
import meltingpot.server.domain.entity.common.BaseEntity;
Expand Down Expand Up @@ -77,4 +78,11 @@ public CommentImage getCommentImage() {
public void setContent(String content) {
this.content = content;
}

public Comment getParent() { return parent;}


public List<Comment> getChildren() {
return children;
}
}
4 changes: 4 additions & 0 deletions src/main/java/meltingpot/server/domain/entity/post/Post.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ public class Post extends BaseEntity {
@OneToMany(mappedBy = "post")
private List<Report> reports = new ArrayList<>();

public List<PostImage> getPostImages() {
return postImages;
}

public void setPostImages(List<PostImage> postImages) {
this.postImages = postImages.stream()
.map(postImage -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,22 @@ public interface CommentRepository extends JpaRepository<Comment, Long> {

Optional<Comment> findById(Long id);

@Query("SELECT c FROM Comment c WHERE c.post.id = :postId AND c.parent IS NULL AND c.id < :cursor ORDER BY c.id DESC")
List<Comment> findParentCommentsByPostIdAndCursor(@Param("postId") Long postId, @Param("cursor") Long cursor, Pageable pageable);
@Query("SELECT c FROM Comment c WHERE c.post.id = :postId AND c.id < :cursor ORDER BY c.id DESC")
List<Comment> findParentComments(Long postId, Long cursor, Pageable pageable);

@Query("SELECT c FROM Comment c WHERE c.parent.id = :parentId ORDER BY c.id ASC")
List<Comment> findChildCommentsByParentId(@Param("parentId") Long parentId, Pageable pageable);
// ์ž์‹ ๋Œ“๊ธ€์„ ์กฐํšŒํ•˜๋Š” ๋ฉ”์„œ๋“œ
@Query("SELECT c FROM Comment c WHERE c.parent.id = :parentId")
List<Comment> findChildrenComments(Long parentId);

// ๋ถ€๋ชจ๊ฐ€ ์—†๋Š” ์ฒซ ๋ฒˆ์งธ ๋Œ“๊ธ€์„ ์ฐพ๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ (๊ฐ€์žฅ ์ƒ์œ„ ๋Œ“๊ธ€)
Optional<Comment> findFirstByPostIdAndParentIsNull(Long postId);

// ํŠน์ • ๋ถ€๋ชจ ๋Œ“๊ธ€์˜ ์ž์‹ ๋Œ“๊ธ€์„ ํŽ˜์ด์ง€๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์„œ๋“œ
List<Comment> findByParent(Comment parent, Pageable pageable);

// ๋‹ค์Œ ๋ถ€๋ชจ ๋Œ“๊ธ€์„ ์ฐพ๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ
@Query("SELECT c FROM Comment c WHERE c.post.id = :postId AND c.parent IS NULL AND c.id > :parentId ORDER BY c.id ASC")
Optional<Comment> findNextParentComment(Long parentId, Long postId);

Slice<Comment> findAllByAccountAndDeletedAtIsNullOrderByIdDesc(Account account, Pageable pageable);
}
Loading

0 comments on commit 0bd7558

Please sign in to comment.