From d37a29408fab48f1263b9e91b17016e6833bc177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=8A=B9=EC=9A=B0?= Date: Sat, 17 Aug 2024 14:22:52 +0900 Subject: [PATCH 1/2] [REFACTOR]: member - comment add fk + ddl --- .../mvp/deplog/domain/comment/domain/Comment.java | 11 +++++++++-- .../db/migration/V7__add_fk_comment_member.sql | 7 +++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/db/migration/V7__add_fk_comment_member.sql diff --git a/src/main/java/mvp/deplog/domain/comment/domain/Comment.java b/src/main/java/mvp/deplog/domain/comment/domain/Comment.java index 359f705..34c1ae3 100644 --- a/src/main/java/mvp/deplog/domain/comment/domain/Comment.java +++ b/src/main/java/mvp/deplog/domain/comment/domain/Comment.java @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import mvp.deplog.domain.common.BaseEntity; +import mvp.deplog.domain.member.domain.Member; import mvp.deplog.domain.post.domain.Post; @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -27,6 +28,10 @@ public class Comment extends BaseEntity { @JoinColumn(name = "post_id", nullable = false) private Post post; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + @Column(name = "content", columnDefinition = "TEXT", nullable = false) private String content; @@ -41,9 +46,10 @@ public class Comment extends BaseEntity { private int depth; @Builder(builderMethodName = "commentBuilder", builderClassName = "CommentBuilder") - public Comment(Post post, String content, String nickname, String avatarImage) { + public Comment(Post post, Member member, String content, String nickname, String avatarImage) { this.parentComment = null; this.post = post; + this.member = member; this.content = content; this.nickname = nickname; this.avatarImage = avatarImage; @@ -51,9 +57,10 @@ public Comment(Post post, String content, String nickname, String avatarImage) { } @Builder(builderMethodName = "replyBuilder", builderClassName = "ReplyBuilder") - public Comment(Comment parentComment, Post post, String content, String nickname, String avatarImage) { + public Comment(Comment parentComment, Post post, Member member, String content, String nickname, String avatarImage) { this.parentComment = parentComment; this.post = post; + this.member = member; this.content = content; this.nickname = nickname; this.avatarImage = avatarImage; diff --git a/src/main/resources/db/migration/V7__add_fk_comment_member.sql b/src/main/resources/db/migration/V7__add_fk_comment_member.sql new file mode 100644 index 0000000..c120eab --- /dev/null +++ b/src/main/resources/db/migration/V7__add_fk_comment_member.sql @@ -0,0 +1,7 @@ +ALTER TABLE comment + ADD COLUMN member_id BIGINT NULL; + +ALTER TABLE comment + ADD CONSTRAINT fk_comment_to_member + FOREIGN KEY (member_id) + REFERENCES member (id); \ No newline at end of file From 8fe2acfdae53fc2c7e7d60b088d6384c0c71aa9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=8A=B9=EC=9A=B0?= Date: Sat, 17 Aug 2024 17:01:25 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[FEAT]:=20=ED=9A=8C=EC=9B=90/=EB=B9=84?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=20=EB=8C=93=EA=B8=80/=EB=8C=80=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20fk=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...=> CreateAnonymousCommentServiceImpl.java} | 14 +++-- ...a => CreateAnonymousReplyServiceImpl.java} | 17 ++--- .../application/CreateCommentService.java | 5 +- .../CreateCommentServiceFactory.java | 5 +- .../CreateMemberCommentServiceImpl.java | 59 ++++++++++++++++++ .../CreateMemberReplyServiceImpl.java | 62 +++++++++++++++++++ .../deplog/domain/comment/domain/Comment.java | 27 +++++++- .../comment/presentation/CommentApi.java | 6 +- .../presentation/CommentController.java | 8 ++- 9 files changed, 180 insertions(+), 23 deletions(-) rename src/main/java/mvp/deplog/domain/comment/application/{CreateCommentServiceImpl.java => CreateAnonymousCommentServiceImpl.java} (70%) rename src/main/java/mvp/deplog/domain/comment/application/{CreateReplyServiceImpl.java => CreateAnonymousReplyServiceImpl.java} (72%) create mode 100644 src/main/java/mvp/deplog/domain/comment/application/CreateMemberCommentServiceImpl.java create mode 100644 src/main/java/mvp/deplog/domain/comment/application/CreateMemberReplyServiceImpl.java diff --git a/src/main/java/mvp/deplog/domain/comment/application/CreateCommentServiceImpl.java b/src/main/java/mvp/deplog/domain/comment/application/CreateAnonymousCommentServiceImpl.java similarity index 70% rename from src/main/java/mvp/deplog/domain/comment/application/CreateCommentServiceImpl.java rename to src/main/java/mvp/deplog/domain/comment/application/CreateAnonymousCommentServiceImpl.java index f819bfd..e2cb21d 100644 --- a/src/main/java/mvp/deplog/domain/comment/application/CreateCommentServiceImpl.java +++ b/src/main/java/mvp/deplog/domain/comment/application/CreateAnonymousCommentServiceImpl.java @@ -4,38 +4,40 @@ import mvp.deplog.domain.comment.domain.Comment; import mvp.deplog.domain.comment.domain.repository.CommentRepository; import mvp.deplog.domain.comment.dto.request.CreateCommentReq; +import mvp.deplog.domain.member.domain.Member; +import mvp.deplog.domain.member.domain.Role; import mvp.deplog.domain.post.domain.Post; import mvp.deplog.domain.post.domain.repository.PostRepository; import mvp.deplog.global.common.Message; import mvp.deplog.global.common.SuccessResponse; +import mvp.deplog.global.security.UserDetailsImpl; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Transactional(readOnly = true) @Service -public class CreateCommentServiceImpl implements CreateCommentService { +public class CreateAnonymousCommentServiceImpl implements CreateCommentService { private final PostRepository postRepository; private final CommentRepository commentRepository; @Override - public boolean supports(Long parentCommentId) { - return parentCommentId == null; + public boolean supports(UserDetailsImpl userDetails, Long parentCommentId) { + return (userDetails == null || userDetails.getMember().getRole().equals(Role.APPLICANT)) && parentCommentId == null; } @Override @Transactional - public SuccessResponse createComment(CreateCommentReq createCommentReq) { + public SuccessResponse createComment(UserDetailsImpl userDetails, CreateCommentReq createCommentReq) { Long postId = createCommentReq.getPostId(); Post post = postRepository.findById(postId) .orElseThrow(() -> new IllegalArgumentException("해당 아이디의 게시글을 찾을 수 없습니다: " + postId)); - Comment comment = Comment.commentBuilder() + Comment comment = Comment.anonymousCommentBuilder() .post(post) .content(createCommentReq.getContent()) .nickname(createCommentReq.getNickname()) - .avatarImage(createCommentReq.getAvatarImage()) .build(); commentRepository.save(comment); diff --git a/src/main/java/mvp/deplog/domain/comment/application/CreateReplyServiceImpl.java b/src/main/java/mvp/deplog/domain/comment/application/CreateAnonymousReplyServiceImpl.java similarity index 72% rename from src/main/java/mvp/deplog/domain/comment/application/CreateReplyServiceImpl.java rename to src/main/java/mvp/deplog/domain/comment/application/CreateAnonymousReplyServiceImpl.java index 75d2a5d..635da0d 100644 --- a/src/main/java/mvp/deplog/domain/comment/application/CreateReplyServiceImpl.java +++ b/src/main/java/mvp/deplog/domain/comment/application/CreateAnonymousReplyServiceImpl.java @@ -4,29 +4,33 @@ import mvp.deplog.domain.comment.domain.Comment; import mvp.deplog.domain.comment.domain.repository.CommentRepository; import mvp.deplog.domain.comment.dto.request.CreateCommentReq; +import mvp.deplog.domain.member.domain.Member; +import mvp.deplog.domain.member.domain.Role; +import mvp.deplog.domain.member.domain.repository.MemberRepository; import mvp.deplog.domain.post.domain.Post; import mvp.deplog.domain.post.domain.repository.PostRepository; import mvp.deplog.global.common.Message; import mvp.deplog.global.common.SuccessResponse; +import mvp.deplog.global.security.UserDetailsImpl; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Transactional(readOnly = true) @Service -public class CreateReplyServiceImpl implements CreateCommentService { +public class CreateAnonymousReplyServiceImpl implements CreateCommentService { private final PostRepository postRepository; private final CommentRepository commentRepository; @Override - public boolean supports(Long parentCommentId) { - return parentCommentId != null; + public boolean supports(UserDetailsImpl userDetails, Long parentCommentId) { + return (userDetails == null || userDetails.getMember().getRole().equals(Role.APPLICANT)) && parentCommentId != null; } @Override @Transactional - public SuccessResponse createComment(CreateCommentReq createCommentReq) { + public SuccessResponse createComment(UserDetailsImpl userDetails, CreateCommentReq createCommentReq) { Long postId = createCommentReq.getPostId(); Post post = postRepository.findById(postId) .orElseThrow(() -> new IllegalArgumentException("해당 아이디의 게시글을 찾을 수 없습니다: " + postId)); @@ -34,12 +38,11 @@ public SuccessResponse createComment(CreateCommentReq createCommentReq) Comment parentComment = commentRepository.findById(createCommentReq.getParentCommentId()) .orElseThrow(() -> new IllegalArgumentException("해당 아이디의 댓글을 찾을 수 없습니다: " + createCommentReq.getParentCommentId())); - Comment reply = Comment.replyBuilder() - .post(post) + Comment reply = Comment.anonymousReplyBuilder() .parentComment(parentComment) + .post(post) .content(createCommentReq.getContent()) .nickname(createCommentReq.getNickname()) - .avatarImage(createCommentReq.getAvatarImage()) .build(); commentRepository.save(reply); diff --git a/src/main/java/mvp/deplog/domain/comment/application/CreateCommentService.java b/src/main/java/mvp/deplog/domain/comment/application/CreateCommentService.java index c7d1fcf..340331a 100644 --- a/src/main/java/mvp/deplog/domain/comment/application/CreateCommentService.java +++ b/src/main/java/mvp/deplog/domain/comment/application/CreateCommentService.java @@ -3,10 +3,11 @@ import mvp.deplog.domain.comment.dto.request.CreateCommentReq; import mvp.deplog.global.common.Message; import mvp.deplog.global.common.SuccessResponse; +import mvp.deplog.global.security.UserDetailsImpl; public interface CreateCommentService { - boolean supports(Long parentCommentId); + boolean supports(UserDetailsImpl userDetails, Long parentCommentId); - SuccessResponse createComment(CreateCommentReq createCommentReq); + SuccessResponse createComment(UserDetailsImpl userDetails, CreateCommentReq createCommentReq); } diff --git a/src/main/java/mvp/deplog/domain/comment/application/CreateCommentServiceFactory.java b/src/main/java/mvp/deplog/domain/comment/application/CreateCommentServiceFactory.java index 7164370..595d3d7 100644 --- a/src/main/java/mvp/deplog/domain/comment/application/CreateCommentServiceFactory.java +++ b/src/main/java/mvp/deplog/domain/comment/application/CreateCommentServiceFactory.java @@ -1,6 +1,7 @@ package mvp.deplog.domain.comment.application; import lombok.RequiredArgsConstructor; +import mvp.deplog.global.security.UserDetailsImpl; import org.springframework.stereotype.Component; import java.util.List; @@ -11,9 +12,9 @@ public class CreateCommentServiceFactory { private final List createCommentServiceList; - public CreateCommentService find(Long parentCommentId) { + public CreateCommentService find(UserDetailsImpl userDetails, Long parentCommentId) { return createCommentServiceList.stream() - .filter(v -> v.supports(parentCommentId)) + .filter(v -> v.supports(userDetails, parentCommentId)) .findFirst() .orElseThrow(); } diff --git a/src/main/java/mvp/deplog/domain/comment/application/CreateMemberCommentServiceImpl.java b/src/main/java/mvp/deplog/domain/comment/application/CreateMemberCommentServiceImpl.java new file mode 100644 index 0000000..447032a --- /dev/null +++ b/src/main/java/mvp/deplog/domain/comment/application/CreateMemberCommentServiceImpl.java @@ -0,0 +1,59 @@ +package mvp.deplog.domain.comment.application; + +import lombok.RequiredArgsConstructor; +import mvp.deplog.domain.comment.domain.Comment; +import mvp.deplog.domain.comment.domain.repository.CommentRepository; +import mvp.deplog.domain.comment.dto.request.CreateCommentReq; +import mvp.deplog.domain.member.domain.Member; +import mvp.deplog.domain.member.domain.Role; +import mvp.deplog.domain.member.domain.repository.MemberRepository; +import mvp.deplog.domain.post.domain.Post; +import mvp.deplog.domain.post.domain.repository.PostRepository; +import mvp.deplog.global.common.Message; +import mvp.deplog.global.common.SuccessResponse; +import mvp.deplog.global.security.UserDetailsImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Service +public class CreateMemberCommentServiceImpl implements CreateCommentService { + + private final PostRepository postRepository; + private final CommentRepository commentRepository; + private final MemberRepository memberRepository; + + @Override + public boolean supports(UserDetailsImpl userDetails, Long parentCommentId) { + return (userDetails != null && !userDetails.getMember().getRole().equals(Role.APPLICANT)) && parentCommentId == null; + } + + + @Override + @Transactional + public SuccessResponse createComment(UserDetailsImpl userDetails, CreateCommentReq createCommentReq) { + Long postId = createCommentReq.getPostId(); + Post post = postRepository.findById(postId) + .orElseThrow(() -> new IllegalArgumentException("해당 아이디의 게시글을 찾을 수 없습니다: " + postId)); + + Member member = memberRepository.findById(userDetails.getMember().getId()) + .orElseThrow(() -> new IllegalArgumentException("해당되는 아이디의 회원이 존재하지 않습니다.")); + + Comment comment = Comment.memberCommentBuilder() + .post(post) + .member(member) + .content(createCommentReq.getContent()) + .nickname(createCommentReq.getNickname()) + .avatarImage(createCommentReq.getAvatarImage()) + .build(); + + commentRepository.save(comment); + + Message message = Message.builder() + .message("댓글 작성이 완료되었습니다.") + .build(); + + return SuccessResponse.of(message); + } +} diff --git a/src/main/java/mvp/deplog/domain/comment/application/CreateMemberReplyServiceImpl.java b/src/main/java/mvp/deplog/domain/comment/application/CreateMemberReplyServiceImpl.java new file mode 100644 index 0000000..57e538c --- /dev/null +++ b/src/main/java/mvp/deplog/domain/comment/application/CreateMemberReplyServiceImpl.java @@ -0,0 +1,62 @@ +package mvp.deplog.domain.comment.application; + +import lombok.RequiredArgsConstructor; +import mvp.deplog.domain.comment.domain.Comment; +import mvp.deplog.domain.comment.domain.repository.CommentRepository; +import mvp.deplog.domain.comment.dto.request.CreateCommentReq; +import mvp.deplog.domain.member.domain.Member; +import mvp.deplog.domain.member.domain.Role; +import mvp.deplog.domain.member.domain.repository.MemberRepository; +import mvp.deplog.domain.post.domain.Post; +import mvp.deplog.domain.post.domain.repository.PostRepository; +import mvp.deplog.global.common.Message; +import mvp.deplog.global.common.SuccessResponse; +import mvp.deplog.global.security.UserDetailsImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Service +public class CreateMemberReplyServiceImpl implements CreateCommentService { + + private final PostRepository postRepository; + private final CommentRepository commentRepository; + private final MemberRepository memberRepository; + + @Override + public boolean supports(UserDetailsImpl userDetails, Long parentCommentId) { + return (userDetails != null && !userDetails.getMember().getRole().equals(Role.APPLICANT)) && parentCommentId != null; + } + + @Override + @Transactional + public SuccessResponse createComment(UserDetailsImpl userDetails, CreateCommentReq createCommentReq) { + Long postId = createCommentReq.getPostId(); + Post post = postRepository.findById(postId) + .orElseThrow(() -> new IllegalArgumentException("해당 아이디의 게시글을 찾을 수 없습니다: " + postId)); + + Comment parentComment = commentRepository.findById(createCommentReq.getParentCommentId()) + .orElseThrow(() -> new IllegalArgumentException("해당 아이디의 댓글을 찾을 수 없습니다: " + createCommentReq.getParentCommentId())); + + Member member = memberRepository.findById(userDetails.getMember().getId()) + .orElseThrow(() -> new IllegalArgumentException("해당되는 아이디의 회원이 존재하지 않습니다.")); + + Comment reply = Comment.memberReplyBuilder() + .parentComment(parentComment) + .post(post) + .member(member) + .content(createCommentReq.getContent()) + .nickname(createCommentReq.getNickname()) + .avatarImage(createCommentReq.getAvatarImage()) + .build(); + + commentRepository.save(reply); + + Message message = Message.builder() + .message("대댓글 작성이 완료되었습니다.") + .build(); + + return SuccessResponse.of(message); + } +} diff --git a/src/main/java/mvp/deplog/domain/comment/domain/Comment.java b/src/main/java/mvp/deplog/domain/comment/domain/Comment.java index 34c1ae3..33aa333 100644 --- a/src/main/java/mvp/deplog/domain/comment/domain/Comment.java +++ b/src/main/java/mvp/deplog/domain/comment/domain/Comment.java @@ -45,7 +45,7 @@ public class Comment extends BaseEntity { @Min(value = 1) private int depth; - @Builder(builderMethodName = "commentBuilder", builderClassName = "CommentBuilder") + @Builder(builderMethodName = "memberCommentBuilder", builderClassName = "MemberCommentBuilder") public Comment(Post post, Member member, String content, String nickname, String avatarImage) { this.parentComment = null; this.post = post; @@ -56,7 +56,18 @@ public Comment(Post post, Member member, String content, String nickname, String this.depth = 1; } - @Builder(builderMethodName = "replyBuilder", builderClassName = "ReplyBuilder") + @Builder(builderMethodName = "anonymousCommentBuilder", builderClassName = "AnonymousCommentBuilder") + public Comment(Post post, String content, String nickname) { + this.parentComment = null; + this.post = post; + this.member = null; + this.content = content; + this.nickname = nickname; + this.avatarImage = null; + this.depth = 1; + } + + @Builder(builderMethodName = "memberReplyBuilder", builderClassName = "MemberReplyBuilder") public Comment(Comment parentComment, Post post, Member member, String content, String nickname, String avatarImage) { this.parentComment = parentComment; this.post = post; @@ -66,4 +77,16 @@ public Comment(Comment parentComment, Post post, Member member, String content, this.avatarImage = avatarImage; this.depth = parentComment.getDepth() + 1; } + + @Builder(builderMethodName = "anonymousReplyBuilder", builderClassName = "AnonymousReplyBuilder") + public Comment(Comment parentComment, Post post, String content, String nickname) { + this.parentComment = parentComment; + this.post = post; + this.member = null; + this.content = content; + this.nickname = nickname; + this.avatarImage = null; + this.depth = parentComment.getDepth() + 1; + } } + diff --git a/src/main/java/mvp/deplog/domain/comment/presentation/CommentApi.java b/src/main/java/mvp/deplog/domain/comment/presentation/CommentApi.java index a108698..6fd3995 100644 --- a/src/main/java/mvp/deplog/domain/comment/presentation/CommentApi.java +++ b/src/main/java/mvp/deplog/domain/comment/presentation/CommentApi.java @@ -13,7 +13,9 @@ import mvp.deplog.global.common.Message; import mvp.deplog.global.common.SuccessResponse; import mvp.deplog.global.exception.ErrorResponse; +import mvp.deplog.global.security.UserDetailsImpl; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -37,7 +39,9 @@ public interface CommentApi { }) @PostMapping ResponseEntity> createComment( - @Parameter(description = "Schemas의 CommentReq를 참고해주세요.", required = true) @RequestBody CreateCommentReq createCommentReq); + @Parameter(description = "Access Token을 입력해주세요.", required = true) @AuthenticationPrincipal UserDetailsImpl userDetails, + @Parameter(description = "Schemas의 CommentReq를 참고해주세요.", required = true) @RequestBody CreateCommentReq createCommentReq + ); @Operation(summary = "댓글 목록 조회 API", description = "해당 게시글을 댓글 목록을 조회합니다.") @ApiResponses(value = { diff --git a/src/main/java/mvp/deplog/domain/comment/presentation/CommentController.java b/src/main/java/mvp/deplog/domain/comment/presentation/CommentController.java index c48c0d2..28a97a9 100644 --- a/src/main/java/mvp/deplog/domain/comment/presentation/CommentController.java +++ b/src/main/java/mvp/deplog/domain/comment/presentation/CommentController.java @@ -8,8 +8,10 @@ import mvp.deplog.domain.comment.dto.response.CommentListRes; import mvp.deplog.global.common.Message; import mvp.deplog.global.common.SuccessResponse; +import mvp.deplog.global.security.UserDetailsImpl; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -24,11 +26,11 @@ public class CommentController implements CommentApi { @Override @PostMapping - public ResponseEntity> createComment(@RequestBody CreateCommentReq createCommentReq) { - CreateCommentService createCommentService = createCommentServiceFactory.find(createCommentReq.getParentCommentId()); + public ResponseEntity> createComment(@AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody CreateCommentReq createCommentReq) { + CreateCommentService createCommentService = createCommentServiceFactory.find(userDetails, createCommentReq.getParentCommentId()); return ResponseEntity .status(HttpStatus.CREATED) - .body(createCommentService.createComment(createCommentReq)); + .body(createCommentService.createComment(userDetails, createCommentReq)); } @Override