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

모임 댓글 기능 구현 #169

Merged
merged 21 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions backend/src/main/java/mouda/backend/comment/domain/Comment.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,87 @@
package mouda.backend.comment.domain;

import java.time.LocalDateTime;

import org.springframework.http.HttpStatus;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import mouda.backend.comment.exception.CommentErrorMessage;
import mouda.backend.comment.exception.CommentException;
import mouda.backend.member.domain.Member;
import mouda.backend.moim.domain.Moim;
import mouda.backend.moim.exception.MoimErrorMessage;
import mouda.backend.moim.exception.MoimException;

@Entity
@Getter
@NoArgsConstructor
public class Comment {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String content;

@ManyToOne
@JoinColumn(name = "moim_id")
private Moim moim;

@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거는 @Column(nullable = false) 를 안해줘도 되나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요한 것 같아요@ManyToOne가 있어 @JoinColum(nullable = false)로 하겠습니다~!


@Column(nullable = false)
private LocalDateTime createdAt;

private Long parentId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

널이 가능한 컬럼과 아닌 컬럼이 나뉘어 있는데 제가 볼 때는 모든 정보다 다 null 이면 안되는 값 같아보여서요!


@Builder
public Comment(String content, Moim moim, Member member, LocalDateTime createdAt, Long parentId) {
validateContent(content);
validateMoim(moim);
validateMember(member);
this.content = content;
this.moim = moim;
this.member = member;
this.createdAt = createdAt;
this.parentId = parentId;
}

private void validateContent(String content) {
if (content == null || content.isBlank()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

content.isBlank() 에 null check가 포함되어있는 걸로 아는데요 😁

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

정보 공유용~

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헤헷콩 감사합니다

throw new CommentException(HttpStatus.BAD_REQUEST, CommentErrorMessage.CONTENT_NOT_FOUND);
}
}

private void validateMoim(Moim moim) {
if (moim == null) {
throw new MoimException(HttpStatus.NOT_FOUND, MoimErrorMessage.NOT_FOUND);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

댓글 저장 시 잘못된 모임을 입력해서 에러가 발생했는데, MoimException을 발생시키는 게 적절할까요? 고민해봅시다!


private void validateMember(Member member) {
if (member == null) {
throw new CommentException(HttpStatus.NOT_FOUND, CommentErrorMessage.MEMBER_NOT_FOUND);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기에서는 회원이 없을 때 CommentException이 발생하는데, 위와 컨벤션이 맞지 않는 것 같네요.

}
}

public boolean isParent() {
return parentId == null;
}

public boolean isChild() {
return parentId != null;
}

public String getAuthorNickname() {
return member.getNickname();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package mouda.backend.comment.dto.request;

import java.time.LocalDateTime;

import jakarta.validation.constraints.NotNull;
import mouda.backend.comment.domain.Comment;
import mouda.backend.member.domain.Member;
import mouda.backend.moim.domain.Moim;

public record CommentCreateRequest(
Long parentId,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

파라미터 요거 개행하나요?

@NotNull
String content
) {
public Comment toEntity(Moim moim, Member member) {
return Comment.builder()
.content(content)
.moim(moim)
.member(member)
.createdAt(LocalDateTime.now())
.parentId(parentId)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package mouda.backend.comment.dto.response;

import java.time.LocalDateTime;

import com.fasterxml.jackson.annotation.JsonFormat;

import lombok.Builder;
import mouda.backend.comment.domain.Comment;

@Builder
public record ChildCommentResponse(
Long commentId,

String nickname,

String content,

@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
LocalDateTime dateTime
Comment on lines +18 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

데이터베이스에서는 date time으로 따로 저장되어있는데
여기는 한번에 보내네요! 이유가 있을까요?

LocalDateTime으로 보내면 상돌이 설정한 JacksonConfig가 적용되지 않는군요 .. 한번에 JacksonConfig에서 포맷팅할 수 없을까요?

) {
public static ChildCommentResponse toResponse(Comment comment) {
return ChildCommentResponse.builder()
.commentId(comment.getId())
.nickname(comment.getAuthorNickname())
.content(comment.getContent())
.dateTime(comment.getCreatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package mouda.backend.comment.dto.response;

import java.time.LocalDateTime;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonFormat;

import lombok.Builder;
import mouda.backend.comment.domain.Comment;

@Builder
public record CommentResponse(
Long commentId,

String nickname,

String content,

@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
LocalDateTime dateTime,

List<ChildCommentResponse> childs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋ? child의 복수형은 children 아닌가요?

) {
public static CommentResponse toResponse(Comment parentComment, List<ChildCommentResponse> childComments) {
return CommentResponse.builder()
.commentId(parentComment.getId())
.nickname(parentComment.getAuthorNickname())
.content(parentComment.getContent())
.dateTime(parentComment.getCreatedAt())
.childs(childComments)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mouda.backend.comment.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum CommentErrorMessage {

CONTENT_NOT_FOUND("댓글 내용이 존재하지 않습니다."),
MEMBER_NOT_FOUND("작성자가 존재하지 않습니다."),
PARENT_NOT_FOUND("부모 댓글이 존재하지 않습니다."),
;

private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package mouda.backend.comment.exception;

import org.springframework.http.HttpStatus;

import mouda.backend.exception.MoudaException;

public class CommentException extends MoudaException {

public CommentException(HttpStatus httpStatus, CommentErrorMessage commentErrorMessage) {
super(httpStatus, commentErrorMessage.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package mouda.backend.comment.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import mouda.backend.comment.domain.Comment;

public interface CommentRepository extends JpaRepository<Comment, Long> {

List<Comment> findAllByMoimIdOrderByCreatedAt(long id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import mouda.backend.comment.dto.request.CommentCreateRequest;
import mouda.backend.common.RestResponse;
import mouda.backend.config.argumentresolver.LoginMember;
import mouda.backend.member.domain.Member;
import mouda.backend.moim.domain.Moim;
import mouda.backend.moim.dto.request.MoimCreateRequest;
import mouda.backend.moim.dto.request.MoimJoinRequest;
Expand Down Expand Up @@ -65,4 +68,13 @@ public ResponseEntity<Void> deleteMoim(@PathVariable Long moimId) {

return ResponseEntity.ok().build();
}

@Override
@PostMapping("/{moimId}")
public ResponseEntity<Void> createComment(@LoginMember Member member, @PathVariable Long moimId,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

파라미터 3개 이상이면 줄바꿈 해야할 것 같습니다! 컨벤션에 적혀있더라구요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감삼당 👍

@RequestBody CommentCreateRequest commentCreateRequest) {
moimService.createComment(member, moimId, commentCreateRequest);

return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import mouda.backend.comment.dto.request.CommentCreateRequest;
import mouda.backend.common.RestResponse;
import mouda.backend.config.argumentresolver.LoginMember;
import mouda.backend.member.domain.Member;
import mouda.backend.moim.dto.request.MoimCreateRequest;
import mouda.backend.moim.dto.request.MoimJoinRequest;
import mouda.backend.moim.dto.response.MoimDetailsFindResponse;
Expand Down Expand Up @@ -44,4 +47,11 @@ public interface MoimSwagger {
@ApiResponse(responseCode = "200", description = "모임 삭제 성공!"),
})
ResponseEntity<Void> deleteMoim(@PathVariable Long moimId);

@Operation(summary = "댓글 작성", description = "해당하는 id의 모임에 댓글을 생성한다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "댓글 생성 성공!")
})
ResponseEntity<Void> createComment(@LoginMember Member member, @PathVariable Long moimId,
@RequestBody CommentCreateRequest commentCreateRequest);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.List;

import lombok.Builder;
import mouda.backend.comment.dto.response.CommentResponse;
import mouda.backend.moim.domain.Moim;

@Builder
Expand All @@ -17,10 +18,12 @@ public record MoimDetailsFindResponse(
int maxPeople,
String authorNickname,
String description,
List<String> participants
List<String> participants,
List<CommentResponse> comments
) {

public static MoimDetailsFindResponse toResponse(Moim moim, List<String> participants) {
public static MoimDetailsFindResponse toResponse(Moim moim, List<String> participants,
List<CommentResponse> comments) {
return MoimDetailsFindResponse.builder()
.title(moim.getTitle())
.date(moim.getDate())
Expand All @@ -30,6 +33,7 @@ public static MoimDetailsFindResponse toResponse(Moim moim, List<String> partici
.maxPeople(moim.getMaxPeople())
.description(moim.getDescription())
.participants(participants)
.comments(comments)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
package mouda.backend.moim.service;

import static java.util.stream.Collectors.*;

import java.util.List;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import mouda.backend.comment.domain.Comment;
import mouda.backend.comment.dto.request.CommentCreateRequest;
import mouda.backend.comment.dto.response.ChildCommentResponse;
import mouda.backend.comment.dto.response.CommentResponse;
import mouda.backend.comment.exception.CommentErrorMessage;
import mouda.backend.comment.exception.CommentException;
import mouda.backend.comment.repository.CommentRepository;
import mouda.backend.member.domain.Member;
import mouda.backend.member.repository.MemberRepository;
import mouda.backend.moim.domain.Moim;
Expand All @@ -28,6 +38,8 @@ public class MoimService {

private final MemberRepository memberRepository;

private final CommentRepository commentRepository;

public Moim createMoim(MoimCreateRequest moimCreateRequest) {
Member author = new Member(moimCreateRequest.authorNickname());
Moim moim = moimRepository.save(moimCreateRequest.toEntity());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

builder 로 생성하지 않고 생성자를 선택하신 이유가 있나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호라

Expand Down Expand Up @@ -59,7 +71,23 @@ public MoimDetailsFindResponse findMoimDetails(long id) {
.map(Member::getNickname)
.toList();

return MoimDetailsFindResponse.toResponse(moim, participants);
List<Comment> comments = commentRepository.findAllByMoimIdOrderByCreatedAt(id);
Map<Long, List<Comment>> childComments = comments.stream()
.filter(Comment::isChild)
.collect(groupingBy(Comment::getParentId));

List<CommentResponse> commentResponses = comments.stream()
.filter(Comment::isParent)
.map(comment -> CommentResponse.toResponse(comment, findChildComments(comment, childComments)))
.toList();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분이 무슨 역할을 담당하는 지 이해하기 힘든 것 같아요. 메서드 분리해서 가독성을 높여주면 좋겠어용


return MoimDetailsFindResponse.toResponse(moim, participants, commentResponses);
}

private List<ChildCommentResponse> findChildComments(Comment comment, Map<Long, List<Comment>> childComments) {
return childComments.getOrDefault(comment.getId(), List.of()).stream()
.map(ChildCommentResponse::toResponse)
.toList();
}

public void joinMoim(MoimJoinRequest moimJoinRequest) {
Expand All @@ -84,4 +112,17 @@ public void deleteMoim(long id) {

moimRepository.delete(moim);
}

public void createComment(Member member, Long moimId, CommentCreateRequest commentCreateRequest) {
Moim moim = moimRepository.findById(moimId).orElseThrow(
() -> new MoimException(HttpStatus.NOT_FOUND, MoimErrorMessage.NOT_FOUND)
);

Long parentId = commentCreateRequest.parentId();
if (parentId != null && !commentRepository.existsById(parentId)) {
throw new CommentException(HttpStatus.BAD_REQUEST, CommentErrorMessage.PARENT_NOT_FOUND);
}

commentRepository.save(commentCreateRequest.toEntity(moim, member));
}
}
Loading