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

feat: 댓글/답글 CRD API 구현 #54

Merged
merged 8 commits into from
Feb 4, 2024
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.listywave.auth.application.domain;

import static com.listywave.common.exception.ErrorCode.REQUIRED_ACCESS_TOKEN;
import static java.util.concurrent.TimeUnit.MINUTES;

import com.listywave.common.exception.CustomException;
import io.jsonwebtoken.Jwts;
import java.time.Instant;
import java.util.Date;
Expand All @@ -28,6 +30,10 @@ public String createToken(Long userId) {
}

public Long read(String token) {
if (token.isBlank()) {
throw new CustomException(REQUIRED_ACCESS_TOKEN);
}

String subject = Jwts.parser()
.verifyWith(key)
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public enum ErrorCode {
INVALID_COUNT(HttpStatus.BAD_REQUEST, "선택한 아이템 및 라벨의 개수가 올바르지 않습니다."),
NOT_FOUND(HttpStatus.BAD_REQUEST, "대상이 존재하지 않습니다."),

REQUIRED_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "인증 정보가 필요합니다."),
INVALID_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 AccessToken 입니다. 다시 로그인해주세요."),

// list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.listywave.common.BaseEntity;
import com.listywave.list.application.vo.Content;
import com.listywave.user.application.domain.User;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
Expand Down Expand Up @@ -30,4 +31,34 @@ public class Comment extends BaseEntity {
@Embedded
private Content content;

@Column(nullable = false, length = 5)
private Boolean isDeleted;

public static Comment create(Lists list, User user, String content) {
return new Comment(list, user, new Content(content), false);
}

public void softDelete() {
this.isDeleted = true;
}

public String getContent() {
return content.getValue();
}

public boolean isDeleted() {
return this.isDeleted;
}

public Long getUserId() {
return user.getId();
}

public String getUserNickname() {
return user.getNickname();
}

public String getUserProfileImageUrl() {
return user.getProfileImageUrl();
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/listywave/list/application/domain/Reply.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.listywave.common.BaseEntity;
import com.listywave.list.application.vo.Content;
import com.listywave.user.application.domain.User;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
Expand All @@ -22,6 +23,30 @@ public class Reply extends BaseEntity {
@JoinColumn(name = "comment_id")
private Comment comment;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

@Embedded
private Content content;

public Long getCommentId() {
return comment.getId();
}

public String getContent() {
return content.getValue();
}

public Long getUserId() {
return user.getId();
}

public String getUserNickname() {
return user.getNickname();
}

public String getUserProfileImageUrl() {
return user.getProfileImageUrl();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.listywave.list.application.dto;

public record ReplyDeleteCommand(
Long listId,
Long commentId,
Long replyId,
String accessToken
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.listywave.list.application.dto.response;

import com.listywave.list.application.domain.Comment;
import com.listywave.user.application.domain.User;
import java.time.LocalDateTime;
import lombok.Builder;

@Builder
public record CommentCreateResponse(
Long id,
Long userId,
String userNickname,
String userProfileImageUrl,
String content,
LocalDateTime createdDate,
LocalDateTime updatedDate
) {

public static CommentCreateResponse of(Comment comment, User user) {
return CommentCreateResponse.builder()
.id(comment.getId())
.userId(user.getId())
.userNickname(user.getNickname())
.userProfileImageUrl(user.getProfileImageUrl())
.content(comment.getContent())
.createdDate(comment.getCreatedDate())
.updatedDate(comment.getUpdatedDate())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.listywave.list.application.dto.response;

import com.listywave.list.application.domain.Comment;
import com.listywave.list.application.domain.Reply;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import lombok.Builder;

@Builder
public record CommentFindResponse(
Long totalCount,
Long cursorId,
boolean hasNext,
List<CommentResponse> comments
) {

public static CommentFindResponse from(
Long totalCount,
Long cursorId,
boolean hasNext,
Map<Comment, List<Reply>> comments
) {
return CommentFindResponse.builder()
.totalCount(totalCount)
.cursorId(cursorId)
.hasNext(hasNext)
.comments(CommentResponse.toList(comments))
.build();
}
}

@Builder
record CommentResponse(
Long id,
Long userId,
String userNickname,
String userProfileImageUrl,
String content,
LocalDateTime createdDate,
LocalDateTime updatedDate,
boolean isDeleted,
List<ReplyResponse> replies
) {

public static List<CommentResponse> toList(Map<Comment, List<Reply>> comments) {
return comments.keySet().stream()
.map(comment -> CommentResponse.of(comment, comments.get(comment)))
.toList();
}

public static CommentResponse of(Comment comment, List<Reply> replies) {
return CommentResponse.builder()
.id(comment.getId())
.userId(comment.getUserId())
.userNickname(comment.getUserNickname())
.userProfileImageUrl(comment.getUserProfileImageUrl())
.content(comment.getContent())
.createdDate(comment.getCreatedDate())
.updatedDate(comment.getUpdatedDate())
.isDeleted(comment.isDeleted())
.replies(ReplyResponse.toList(replies))
.build();
}
}

@Builder
record ReplyResponse(
Long id,
Long commentId,
Long userId,
String userNickname,
String userProfileImageUrl,
String content,
LocalDateTime createdDate,
LocalDateTime updatedDate
) {

public static List<ReplyResponse> toList(List<Reply> replies) {
return replies.stream()
.map(ReplyResponse::of)
.toList();
}

public static ReplyResponse of(Reply reply) {
return ReplyResponse.builder()
.id(reply.getId())
.commentId(reply.getCommentId())
.userId(reply.getUserId())
.userNickname(reply.getUserNickname())
.userProfileImageUrl(reply.getUserProfileImageUrl())
.content(reply.getContent())
.createdDate(reply.getCreatedDate())
.updatedDate(reply.getUpdatedDate())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.listywave.list.application.dto.response;

import com.listywave.list.application.domain.Comment;
import com.listywave.list.application.domain.Reply;
import com.listywave.user.application.domain.User;
import java.time.LocalDateTime;
import lombok.Builder;

@Builder
public record ReplyCreateResponse(
Long id,
Long commentId,
Long userId,
String userNickname,
String userProfileImageUrl,
String content,
LocalDateTime createdDate,
LocalDateTime updatedDate
) {

public static ReplyCreateResponse of(Reply reply, Comment comment, User user) {
return ReplyCreateResponse.builder()
.id(reply.getId())
.commentId(comment.getId())
.userId(user.getId())
.userNickname(user.getNickname())
.userProfileImageUrl(user.getProfileImageUrl())
.content(reply.getContent())
.createdDate(reply.getCreatedDate())
.updatedDate(reply.getUpdatedDate())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.listywave.list.application.service;

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;

import com.listywave.auth.application.domain.JwtManager;
import com.listywave.list.application.domain.Comment;
import com.listywave.list.application.domain.Lists;
import com.listywave.list.application.domain.Reply;
import com.listywave.list.application.dto.response.CommentCreateResponse;
import com.listywave.list.application.dto.response.CommentFindResponse;
import com.listywave.list.repository.CommentRepository;
import com.listywave.list.repository.ListRepository;
import com.listywave.list.repository.ReplyRepository;
import com.listywave.user.application.domain.User;
import com.listywave.user.repository.UserRepository;
import jakarta.transaction.Transactional;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@Transactional
@RequiredArgsConstructor
public class CommentService {

private final JwtManager jwtManager;
private final ListRepository listRepository;
private final UserRepository userRepository;
private final ReplyRepository replyRepository;
private final CommentRepository commentRepository;

public CommentCreateResponse create(Long listId, String accessToken, String content) {
Long userId = jwtManager.read(accessToken);
User user = userRepository.getById(userId);
Lists list = listRepository.getById(listId);

kdkdhoho marked this conversation as resolved.
Show resolved Hide resolved
Comment comment = Comment.create(list, user, content);
Comment saved = commentRepository.save(comment);

return CommentCreateResponse.of(saved, user);
}

public CommentFindResponse getComments(Long listId, int size, Long cursorId) {
Lists list = listRepository.getById(listId);
Long totalCount = commentRepository.countByList(list);

List<Comment> comments = commentRepository.getComments(listId, size, cursorId);

boolean hasNext = false;
if (comments.size() > size) {
hasNext = true;
comments.remove(comments.size() - 1);
}
Long cursorIdOfResult = comments.get(comments.size() - 1).getId();

Map<Comment, List<Reply>> result = comments.stream()
.collect(toMap(
identity(),
replyRepository::getAllByComment,
(exists, newValue) -> exists,
LinkedHashMap::new
));
return CommentFindResponse.from(totalCount, cursorIdOfResult, hasNext, result);
}

public void delete(Long listId, String accessToken, Long commentId) {

Long userId = jwtManager.read(accessToken);
userRepository.getById(userId);
listRepository.getById(listId);

Comment comment = commentRepository.getById(commentId);
if (replyRepository.existsByComment(comment)) {
comment.softDelete();
return;
}
commentRepository.delete(comment);
kdkdhoho marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading
Loading