diff --git a/src/docs/asciidoc/api/tech-article-comment/tech-article-comment-modify.adoc b/src/docs/asciidoc/api/tech-article-comment/tech-article-comment-modify.adoc index 438af5f7..524b44e0 100644 --- a/src/docs/asciidoc/api/tech-article-comment/tech-article-comment-modify.adoc +++ b/src/docs/asciidoc/api/tech-article-comment/tech-article-comment-modify.adoc @@ -2,8 +2,10 @@ == 기술블로그 댓글 수정 API(PATCH: /devdevdev/api/v1/articles/{techArticleId}/comments/{techCommentId}) * 기술블로그 댓글을 수정한다. -* 회원 본인이 작성한 기술블로그 댓글을 수정할 수 있다. -* 삭제된 댓글을 수정할 수 없다. +** 회원인 경우 토큰을 `Authorization` 헤더에 포함시켜야 한다. +** 익명 회원인 경우 `Anonymous-Member-Id` 헤더에 익명 회원 아이디를 포함시켜야 한다. +* 회원 또는 익명회원 본인이 작성한 기술블로그 댓글/답글 만 수정 할 수 있다. +* 삭제된 댓글/답글을 수정할 수 없다. === 정상 요청/응답 diff --git a/src/docs/asciidoc/api/tech-article-comment/tech-article-reply-register.adoc b/src/docs/asciidoc/api/tech-article-comment/tech-article-reply-register.adoc index 44e8d65c..7fa986cc 100644 --- a/src/docs/asciidoc/api/tech-article-comment/tech-article-reply-register.adoc +++ b/src/docs/asciidoc/api/tech-article-comment/tech-article-reply-register.adoc @@ -2,7 +2,8 @@ == 기술블로그 답글 작성 API(POST: /devdevdev/api/v1/articles/{techArticleId}/comments/{originParentTechCommentId}/{parentTechCommentId} * 회원은 기술블로그에 댓글에 답글을 작성할 수 있다. -* 익명회원은 답글을 작성할 수 없다. +** 회원인 경우 토큰을 `Authorization` 헤더에 포함시켜야 한다. +** 익명 회원인 경우 `Anonymous-Member-Id` 헤더에 익명 회원 아이디를 포함시켜야 한다. * 삭제된 댓글에는 답글을 작성할 수 없다. * 최초 댓글에 대한 답글을 작성할 경우 `techCommentOriginParentId` 값과 `techParentCommentId` 값이 동일하다. @@ -40,5 +41,6 @@ include::{snippets}/register-tech-article-reply/response-fields.adoc[] * `회원을 찾을 수 없습니다.`: 회원 정보가 없을 경우 * `익명 회원은 사용할 수 없는 기능 입니다.`: 익명 회원이 사용할 수 없는 기능일 경우 * `존재하지 않는 기술블로그입니다.`: 기술블로그가 존재하지 않는 경우 +* `익명 사용자가 아닙니다. 잘못된 메소드 호출 입니다.`: 회원이 익명 회원 메소드를 호출한 경우 include::{snippets}/register-tech-article-reply-null-exception/response-body.adoc[] diff --git a/src/main/java/com/dreamypatisiel/devdevdev/domain/entity/TechComment.java b/src/main/java/com/dreamypatisiel/devdevdev/domain/entity/TechComment.java index 84121cc8..ef878e4a 100644 --- a/src/main/java/com/dreamypatisiel/devdevdev/domain/entity/TechComment.java +++ b/src/main/java/com/dreamypatisiel/devdevdev/domain/entity/TechComment.java @@ -152,11 +152,32 @@ public static TechComment createRepliedTechCommentByMember(CommentContents conte .build(); } + public static TechComment createRepliedTechCommentByAnonymousMember(CommentContents contents, + AnonymousMember createdAnonymousBy, + TechArticle techArticle, TechComment originParent, + TechComment parent) { + return TechComment.builder() + .contents(contents) + .createdAnonymousBy(createdAnonymousBy) + .techArticle(techArticle) + .blameTotalCount(Count.defaultCount()) + .recommendTotalCount(Count.defaultCount()) + .replyTotalCount(Count.defaultCount()) + .originParent(originParent) + .parent(parent) + .build(); + } + public void changeDeletedAt(LocalDateTime deletedAt, Member deletedBy) { this.deletedAt = deletedAt; this.deletedBy = deletedBy; } + public void changeDeletedAt(LocalDateTime deletedAt, AnonymousMember deletedAnonymousBy) { + this.deletedAt = deletedAt; + this.deletedAnonymousBy = deletedAnonymousBy; + } + public void modifyCommentContents(CommentContents contents, LocalDateTime contentsLastModifiedAt) { this.contents = contents; this.contentsLastModifiedAt = contentsLastModifiedAt; diff --git a/src/main/java/com/dreamypatisiel/devdevdev/domain/repository/techArticle/TechCommentRepository.java b/src/main/java/com/dreamypatisiel/devdevdev/domain/repository/techArticle/TechCommentRepository.java index 620bd09c..7951ede7 100644 --- a/src/main/java/com/dreamypatisiel/devdevdev/domain/repository/techArticle/TechCommentRepository.java +++ b/src/main/java/com/dreamypatisiel/devdevdev/domain/repository/techArticle/TechCommentRepository.java @@ -1,5 +1,6 @@ package com.dreamypatisiel.devdevdev.domain.repository.techArticle; +import com.dreamypatisiel.devdevdev.domain.entity.AnonymousMember; import com.dreamypatisiel.devdevdev.domain.entity.TechComment; import com.dreamypatisiel.devdevdev.domain.repository.techArticle.custom.TechCommentRepositoryCustom; import java.util.List; @@ -16,6 +17,9 @@ public interface TechCommentRepository extends JpaRepository, Optional findByIdAndTechArticleIdAndCreatedByIdAndDeletedAtIsNull(Long id, Long techArticleId, Long createdById); + Optional findByIdAndTechArticleIdAndCreatedAnonymousByAndDeletedAtIsNull(Long id, Long techArticleId, + AnonymousMember createdAnonymousBy); + Optional findByIdAndTechArticleIdAndDeletedAtIsNull(Long id, Long techArticleId); @EntityGraph(attributePaths = {"createdBy", "deletedBy", "createdAnonymousBy", "deletedAnonymousBy", "techArticle"}) diff --git a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/dto/TechCommentDto.java b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/dto/TechCommentDto.java index 7b9613e0..b8559632 100644 --- a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/dto/TechCommentDto.java +++ b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/dto/TechCommentDto.java @@ -1,5 +1,6 @@ package com.dreamypatisiel.devdevdev.domain.service.techArticle.dto; +import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.ModifyTechCommentRequest; import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.RegisterTechCommentRequest; import lombok.Data; @@ -15,4 +16,12 @@ public static TechCommentDto createRegisterCommentDto(RegisterTechCommentRequest techCommentDto.setAnonymousMemberId(anonymousMemberId); return techCommentDto; } + + public static TechCommentDto createModifyCommentDto(ModifyTechCommentRequest modifyTechCommentRequest, + String anonymousMemberId) { + TechCommentDto techCommentDto = new TechCommentDto(); + techCommentDto.setContents(modifyTechCommentRequest.getContents()); + techCommentDto.setAnonymousMemberId(anonymousMemberId); + return techCommentDto; + } } diff --git a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentService.java b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentService.java index 9ce43a5d..1c5bc16e 100644 --- a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentService.java +++ b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentService.java @@ -2,19 +2,18 @@ import static com.dreamypatisiel.devdevdev.domain.exception.GuestExceptionMessage.INVALID_ANONYMOUS_CAN_NOT_USE_THIS_FUNCTION_MESSAGE; +import com.dreamypatisiel.devdevdev.domain.policy.TechArticlePopularScorePolicy; import com.dreamypatisiel.devdevdev.domain.policy.TechBestCommentsPolicy; import com.dreamypatisiel.devdevdev.domain.repository.techArticle.TechCommentRepository; import com.dreamypatisiel.devdevdev.domain.repository.techArticle.TechCommentSort; -import com.dreamypatisiel.devdevdev.domain.service.member.AnonymousMemberService; import com.dreamypatisiel.devdevdev.domain.service.techArticle.dto.TechCommentDto; import com.dreamypatisiel.devdevdev.global.utils.AuthenticationMemberUtils; import com.dreamypatisiel.devdevdev.web.dto.SliceCommentCustom; -import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.ModifyTechCommentRequest; -import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.RegisterTechCommentRequest; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentRecommendResponse; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentResponse; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentsResponse; import java.util.List; +import javax.annotation.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; @@ -25,13 +24,10 @@ @Transactional(readOnly = true) public class GuestTechCommentService extends TechCommentCommonService implements TechCommentService { - private final AnonymousMemberService anonymousMemberService; - public GuestTechCommentService(TechCommentRepository techCommentRepository, TechBestCommentsPolicy techBestCommentsPolicy, - AnonymousMemberService anonymousMemberService) { - super(techCommentRepository, techBestCommentsPolicy); - this.anonymousMemberService = anonymousMemberService; + TechArticlePopularScorePolicy techArticlePopularScorePolicy) { + super(techCommentRepository, techBestCommentsPolicy, techArticlePopularScorePolicy); } @Override @@ -44,20 +40,19 @@ public TechCommentResponse registerMainTechComment(Long techArticleId, @Override public TechCommentResponse registerRepliedTechComment(Long techArticleId, Long originParentTechCommentId, Long parentTechCommentId, - RegisterTechCommentRequest registerRepliedTechCommentRequest, + TechCommentDto registerRepliedTechCommentRequest, Authentication authentication) { throw new AccessDeniedException(INVALID_ANONYMOUS_CAN_NOT_USE_THIS_FUNCTION_MESSAGE); } @Override - public TechCommentResponse modifyTechComment(Long techArticleId, Long techCommentId, - ModifyTechCommentRequest modifyTechCommentRequest, + public TechCommentResponse modifyTechComment(Long techArticleId, Long techCommentId, TechCommentDto modifyTechCommentDto, Authentication authentication) { throw new AccessDeniedException(INVALID_ANONYMOUS_CAN_NOT_USE_THIS_FUNCTION_MESSAGE); } @Override - public TechCommentResponse deleteTechComment(Long techArticleId, Long techCommentId, + public TechCommentResponse deleteTechComment(Long techArticleId, Long techCommentId, @Nullable String anonymousMemberId, Authentication authentication) { throw new AccessDeniedException(INVALID_ANONYMOUS_CAN_NOT_USE_THIS_FUNCTION_MESSAGE); } diff --git a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentServiceV2.java b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentServiceV2.java index a243f8df..c507b674 100644 --- a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentServiceV2.java +++ b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentServiceV2.java @@ -1,21 +1,23 @@ package com.dreamypatisiel.devdevdev.domain.service.techArticle.techComment; import static com.dreamypatisiel.devdevdev.domain.exception.GuestExceptionMessage.INVALID_ANONYMOUS_CAN_NOT_USE_THIS_FUNCTION_MESSAGE; +import static com.dreamypatisiel.devdevdev.domain.exception.TechArticleExceptionMessage.INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE; import com.dreamypatisiel.devdevdev.domain.entity.AnonymousMember; import com.dreamypatisiel.devdevdev.domain.entity.TechArticle; import com.dreamypatisiel.devdevdev.domain.entity.TechComment; import com.dreamypatisiel.devdevdev.domain.entity.embedded.CommentContents; +import com.dreamypatisiel.devdevdev.domain.policy.TechArticlePopularScorePolicy; import com.dreamypatisiel.devdevdev.domain.policy.TechBestCommentsPolicy; import com.dreamypatisiel.devdevdev.domain.repository.techArticle.TechCommentRepository; import com.dreamypatisiel.devdevdev.domain.repository.techArticle.TechCommentSort; import com.dreamypatisiel.devdevdev.domain.service.member.AnonymousMemberService; import com.dreamypatisiel.devdevdev.domain.service.techArticle.dto.TechCommentDto; import com.dreamypatisiel.devdevdev.domain.service.techArticle.techArticle.TechArticleCommonService; +import com.dreamypatisiel.devdevdev.exception.NotFoundException; +import com.dreamypatisiel.devdevdev.global.common.TimeProvider; import com.dreamypatisiel.devdevdev.global.utils.AuthenticationMemberUtils; import com.dreamypatisiel.devdevdev.web.dto.SliceCommentCustom; -import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.ModifyTechCommentRequest; -import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.RegisterTechCommentRequest; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentRecommendResponse; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentResponse; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentsResponse; @@ -30,30 +32,32 @@ @Transactional(readOnly = true) public class GuestTechCommentServiceV2 extends TechCommentCommonService implements TechCommentService { + private final TimeProvider timeProvider; + private final AnonymousMemberService anonymousMemberService; - private final TechCommentCommonService techCommentCommonService; private final TechArticleCommonService techArticleCommonService; - public GuestTechCommentServiceV2(TechCommentRepository techCommentRepository, TechBestCommentsPolicy techBestCommentsPolicy, + public GuestTechCommentServiceV2(TimeProvider timeProvider, TechCommentRepository techCommentRepository, + TechBestCommentsPolicy techBestCommentsPolicy, AnonymousMemberService anonymousMemberService, - TechCommentCommonService techCommentCommonService, + TechArticlePopularScorePolicy techArticlePopularScorePolicy, TechArticleCommonService techArticleCommonService) { - super(techCommentRepository, techBestCommentsPolicy); + super(techCommentRepository, techBestCommentsPolicy, techArticlePopularScorePolicy); + this.timeProvider = timeProvider; this.anonymousMemberService = anonymousMemberService; - this.techCommentCommonService = techCommentCommonService; this.techArticleCommonService = techArticleCommonService; } @Override @Transactional - public TechCommentResponse registerMainTechComment(Long techArticleId, TechCommentDto techCommentDto, + public TechCommentResponse registerMainTechComment(Long techArticleId, TechCommentDto registerTechCommentDto, Authentication authentication) { // 익명 회원인지 검증 AuthenticationMemberUtils.validateAnonymousMethodCall(authentication); - String anonymousMemberId = techCommentDto.getAnonymousMemberId(); - String contents = techCommentDto.getContents(); + String anonymousMemberId = registerTechCommentDto.getAnonymousMemberId(); + String contents = registerTechCommentDto.getContents(); // 회원 조회 또는 생성 AnonymousMember findAnonymousMember = anonymousMemberService.findOrCreateAnonymousMember(anonymousMemberId); @@ -74,24 +78,91 @@ public TechCommentResponse registerMainTechComment(Long techArticleId, TechComme } @Override + @Transactional public TechCommentResponse registerRepliedTechComment(Long techArticleId, Long originParentTechCommentId, Long parentTechCommentId, - RegisterTechCommentRequest registerRepliedTechCommentRequest, + TechCommentDto registerRepliedTechCommentDto, Authentication authentication) { - throw new AccessDeniedException(INVALID_ANONYMOUS_CAN_NOT_USE_THIS_FUNCTION_MESSAGE); + // 익명 회원인지 검증 + AuthenticationMemberUtils.validateAnonymousMethodCall(authentication); + + String anonymousMemberId = registerRepliedTechCommentDto.getAnonymousMemberId(); + String contents = registerRepliedTechCommentDto.getContents(); + + // 회원 조회 또는 생성 + AnonymousMember findAnonymousMember = anonymousMemberService.findOrCreateAnonymousMember(anonymousMemberId); + + // 답글 대상의 기술블로그 댓글 조회 + TechComment findParentTechComment = techCommentRepository.findWithTechArticleByIdAndTechArticleId( + parentTechCommentId, techArticleId) + .orElseThrow(() -> new NotFoundException(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE)); + + // 답글 엔티티 생성 및 저장 + TechComment findOriginParentTechComment = super.getAndValidateOriginParentTechComment(originParentTechCommentId, + findParentTechComment); + TechArticle findTechArticle = findParentTechComment.getTechArticle(); + + TechComment repliedTechComment = TechComment.createRepliedTechCommentByAnonymousMember(new CommentContents(contents), + findAnonymousMember, findTechArticle, findOriginParentTechComment, findParentTechComment); + techCommentRepository.save(repliedTechComment); + + // 아티클의 댓글수 증가 + findTechArticle.incrementCommentCount(); + findTechArticle.changePopularScore(techArticlePopularScorePolicy); + + // origin 댓글의 답글수 증가 + findOriginParentTechComment.incrementReplyTotalCount(); + + // 데이터 가공 + return new TechCommentResponse(repliedTechComment.getId()); } @Override - public TechCommentResponse modifyTechComment(Long techArticleId, Long techCommentId, - ModifyTechCommentRequest modifyTechCommentRequest, + @Transactional + public TechCommentResponse modifyTechComment(Long techArticleId, Long techCommentId, TechCommentDto modifyTechCommentDto, Authentication authentication) { - throw new AccessDeniedException(INVALID_ANONYMOUS_CAN_NOT_USE_THIS_FUNCTION_MESSAGE); + // 익명 회원인지 검증 + AuthenticationMemberUtils.validateAnonymousMethodCall(authentication); + + String contents = modifyTechCommentDto.getContents(); + String anonymousMemberId = modifyTechCommentDto.getAnonymousMemberId(); + + // 회원 조회 또는 생성 + AnonymousMember findAnonymousMember = anonymousMemberService.findOrCreateAnonymousMember(anonymousMemberId); + + // 기술블로그 댓글 조회 + TechComment findTechComment = techCommentRepository.findByIdAndTechArticleIdAndCreatedAnonymousByAndDeletedAtIsNull( + techCommentId, techArticleId, findAnonymousMember) + .orElseThrow(() -> new NotFoundException(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE)); + + // 댓글 수정 + findTechComment.modifyCommentContents(new CommentContents(contents), timeProvider.getLocalDateTimeNow()); + + // 데이터 가공 + return new TechCommentResponse(findTechComment.getId()); } @Override - public TechCommentResponse deleteTechComment(Long techArticleId, Long techCommentId, + @Transactional + public TechCommentResponse deleteTechComment(Long techArticleId, Long techCommentId, String anonymousMemberId, Authentication authentication) { - throw new AccessDeniedException(INVALID_ANONYMOUS_CAN_NOT_USE_THIS_FUNCTION_MESSAGE); + + // 익명 회원인지 검증 + AuthenticationMemberUtils.validateAnonymousMethodCall(authentication); + + // 익명회원 조회 또는 생성 + AnonymousMember findAnonymousMember = anonymousMemberService.findOrCreateAnonymousMember(anonymousMemberId); + + // 기술블로그 댓글 조회 + TechComment findTechComment = techCommentRepository.findByIdAndTechArticleIdAndCreatedAnonymousByAndDeletedAtIsNull( + techCommentId, techArticleId, findAnonymousMember) + .orElseThrow(() -> new NotFoundException(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE)); + + // 소프트 삭제 + findTechComment.changeDeletedAt(timeProvider.getLocalDateTimeNow(), findAnonymousMember); + + // 데이터 가공 + return new TechCommentResponse(findTechComment.getId()); } /** diff --git a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/MemberTechCommentService.java b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/MemberTechCommentService.java index cb5266f7..fc3e36a6 100644 --- a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/MemberTechCommentService.java +++ b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/MemberTechCommentService.java @@ -1,9 +1,7 @@ package com.dreamypatisiel.devdevdev.domain.service.techArticle.techComment; import static com.dreamypatisiel.devdevdev.domain.exception.TechArticleExceptionMessage.INVALID_CAN_NOT_RECOMMEND_DELETED_TECH_COMMENT_MESSAGE; -import static com.dreamypatisiel.devdevdev.domain.exception.TechArticleExceptionMessage.INVALID_CAN_NOT_REPLY_DELETED_TECH_COMMENT_MESSAGE; import static com.dreamypatisiel.devdevdev.domain.exception.TechArticleExceptionMessage.INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE; -import static com.dreamypatisiel.devdevdev.domain.service.techArticle.techArticle.TechArticleCommonService.validateIsDeletedTechComment; import com.dreamypatisiel.devdevdev.domain.entity.Member; import com.dreamypatisiel.devdevdev.domain.entity.TechArticle; @@ -21,13 +19,12 @@ import com.dreamypatisiel.devdevdev.global.common.MemberProvider; import com.dreamypatisiel.devdevdev.global.common.TimeProvider; import com.dreamypatisiel.devdevdev.web.dto.SliceCommentCustom; -import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.ModifyTechCommentRequest; -import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.RegisterTechCommentRequest; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentRecommendResponse; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentResponse; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentsResponse; import java.util.List; import java.util.Optional; +import javax.annotation.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; @@ -40,7 +37,6 @@ public class MemberTechCommentService extends TechCommentCommonService implement private final TechArticleCommonService techArticleCommonService; private final MemberProvider memberProvider; private final TimeProvider timeProvider; - private final TechArticlePopularScorePolicy techArticlePopularScorePolicy; private final TechCommentRecommendRepository techCommentRecommendRepository; public MemberTechCommentService(TechCommentRepository techCommentRepository, @@ -49,11 +45,10 @@ public MemberTechCommentService(TechCommentRepository techCommentRepository, TechArticlePopularScorePolicy techArticlePopularScorePolicy, TechCommentRecommendRepository techCommentRecommendRepository, TechBestCommentsPolicy techBestCommentsPolicy) { - super(techCommentRepository, techBestCommentsPolicy); + super(techCommentRepository, techBestCommentsPolicy, techArticlePopularScorePolicy); this.techArticleCommonService = techArticleCommonService; this.memberProvider = memberProvider; this.timeProvider = timeProvider; - this.techArticlePopularScorePolicy = techArticlePopularScorePolicy; this.techCommentRecommendRepository = techCommentRecommendRepository; } @@ -95,7 +90,7 @@ public TechCommentResponse registerMainTechComment(Long techArticleId, public TechCommentResponse registerRepliedTechComment(Long techArticleId, Long originParentTechCommentId, Long parentTechCommentId, - RegisterTechCommentRequest registerRepliedTechCommentRequest, + TechCommentDto requestedRepliedTechCommentDto, Authentication authentication) { // 회원 조회 Member findMember = memberProvider.getMemberByAuthentication(authentication); @@ -106,11 +101,11 @@ public TechCommentResponse registerRepliedTechComment(Long techArticleId, .orElseThrow(() -> new NotFoundException(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE)); // 답글 엔티티 생성 및 저장 - TechComment findOriginParentTechComment = getAndValidateOriginParentTechComment(originParentTechCommentId, + TechComment findOriginParentTechComment = super.getAndValidateOriginParentTechComment(originParentTechCommentId, findParentTechComment); TechArticle findTechArticle = findParentTechComment.getTechArticle(); - String contents = registerRepliedTechCommentRequest.getContents(); + String contents = requestedRepliedTechCommentDto.getContents(); TechComment repliedTechComment = TechComment.createRepliedTechCommentByMember(new CommentContents(contents), findMember, findTechArticle, findOriginParentTechComment, findParentTechComment); techCommentRepository.save(repliedTechComment); @@ -126,41 +121,14 @@ public TechCommentResponse registerRepliedTechComment(Long techArticleId, return new TechCommentResponse(repliedTechComment.getId()); } - /** - * @Note: 답글 대상의 댓글을 조회하고, 답글 대상의 댓글이 최초 댓글이면 답글 대상으로 반환한다. - * @Author: 유소영 - * @Since: 2024.09.06 - */ - private TechComment getAndValidateOriginParentTechComment(Long originParentTechCommentId, - TechComment parentTechComment) { - - // 삭제된 댓글에는 답글 작성 불가 - validateIsDeletedTechComment(parentTechComment, INVALID_CAN_NOT_REPLY_DELETED_TECH_COMMENT_MESSAGE, null); - - // 답글 대상의 댓글이 최초 댓글이면 답글 대상으로 반환 - if (parentTechComment.isEqualsId(originParentTechCommentId)) { - return parentTechComment; - } - - // 답글 대상의 댓글의 메인 댓글 조회 - TechComment findOriginParentTechComment = techCommentRepository.findById(originParentTechCommentId) - .orElseThrow(() -> new NotFoundException(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE)); - - // 최초 댓글이 삭제 상태이면 답글 작성 불가 - validateIsDeletedTechComment(findOriginParentTechComment, INVALID_CAN_NOT_REPLY_DELETED_TECH_COMMENT_MESSAGE, - null); - - return findOriginParentTechComment; - } - /** * @Note: 기술블로그 댓글을 수정한다. 단, 본인이 작성한 댓글만 수정할 수 있다. * @Author: 유소영 * @Since: 2024.08.11 */ + @Override @Transactional - public TechCommentResponse modifyTechComment(Long techArticleId, Long techCommentId, - ModifyTechCommentRequest modifyTechCommentRequest, + public TechCommentResponse modifyTechComment(Long techArticleId, Long techCommentId, TechCommentDto modifyTechCommentDto, Authentication authentication) { // 회원 조회 Member findMember = memberProvider.getMemberByAuthentication(authentication); @@ -171,7 +139,7 @@ public TechCommentResponse modifyTechComment(Long techArticleId, Long techCommen .orElseThrow(() -> new NotFoundException(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE)); // 댓글 수정 - String contents = modifyTechCommentRequest.getContents(); + String contents = modifyTechCommentDto.getContents(); findTechComment.modifyCommentContents(new CommentContents(contents), timeProvider.getLocalDateTimeNow()); // 데이터 가공 @@ -183,10 +151,9 @@ public TechCommentResponse modifyTechComment(Long techArticleId, Long techCommen * @Author: 유소영 * @Since: 2024.08.13 */ - @Transactional - public TechCommentResponse deleteTechComment(Long techArticleId, Long techCommentId, + @Override + public TechCommentResponse deleteTechComment(Long techArticleId, Long techCommentId, @Nullable String anonymousMemberId, Authentication authentication) { - // 회원 조회 Member findMember = memberProvider.getMemberByAuthentication(authentication); diff --git a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/TechCommentCommonService.java b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/TechCommentCommonService.java index acfc6caa..a8753f63 100644 --- a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/TechCommentCommonService.java +++ b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/TechCommentCommonService.java @@ -1,12 +1,18 @@ package com.dreamypatisiel.devdevdev.domain.service.techArticle.techComment; +import static com.dreamypatisiel.devdevdev.domain.exception.TechArticleExceptionMessage.INVALID_CAN_NOT_REPLY_DELETED_TECH_COMMENT_MESSAGE; +import static com.dreamypatisiel.devdevdev.domain.exception.TechArticleExceptionMessage.INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE; +import static com.dreamypatisiel.devdevdev.domain.service.techArticle.techArticle.TechArticleCommonService.validateIsDeletedTechComment; + import com.dreamypatisiel.devdevdev.domain.entity.AnonymousMember; import com.dreamypatisiel.devdevdev.domain.entity.BasicTime; import com.dreamypatisiel.devdevdev.domain.entity.Member; import com.dreamypatisiel.devdevdev.domain.entity.TechComment; +import com.dreamypatisiel.devdevdev.domain.policy.TechArticlePopularScorePolicy; import com.dreamypatisiel.devdevdev.domain.policy.TechBestCommentsPolicy; import com.dreamypatisiel.devdevdev.domain.repository.techArticle.TechCommentRepository; import com.dreamypatisiel.devdevdev.domain.repository.techArticle.TechCommentSort; +import com.dreamypatisiel.devdevdev.exception.NotFoundException; import com.dreamypatisiel.devdevdev.web.dto.SliceCommentCustom; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentsResponse; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechRepliedCommentsResponse; @@ -31,6 +37,7 @@ public class TechCommentCommonService { protected final TechCommentRepository techCommentRepository; protected final TechBestCommentsPolicy techBestCommentsPolicy; + protected final TechArticlePopularScorePolicy techArticlePopularScorePolicy; /** * @Note: 정렬 조건에 따라 커서 방식으로 기술블로그 댓글 목록을 조회한다. @@ -144,4 +151,30 @@ protected List findTechBestComments(int size, Long techArt techBestCommentReplies)) .toList(); } + + /** + * @Note: 답글 대상의 댓글을 조회하고, 답글 대상의 댓글이 최초 댓글이면 답글 대상으로 반환한다. + * @Author: 유소영 + * @Since: 2024.09.06 + */ + protected TechComment getAndValidateOriginParentTechComment(Long originParentTechCommentId, TechComment parentTechComment) { + + // 삭제된 댓글에는 답글 작성 불가 + validateIsDeletedTechComment(parentTechComment, INVALID_CAN_NOT_REPLY_DELETED_TECH_COMMENT_MESSAGE, null); + + // 답글 대상의 댓글이 최초 댓글이면 답글 대상으로 반환 + if (parentTechComment.isEqualsId(originParentTechCommentId)) { + return parentTechComment; + } + + // 답글 대상의 댓글의 메인 댓글 조회 + TechComment findOriginParentTechComment = techCommentRepository.findById(originParentTechCommentId) + .orElseThrow(() -> new NotFoundException(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE)); + + // 최초 댓글이 삭제 상태이면 답글 작성 불가 + validateIsDeletedTechComment(findOriginParentTechComment, INVALID_CAN_NOT_REPLY_DELETED_TECH_COMMENT_MESSAGE, + null); + + return findOriginParentTechComment; + } } diff --git a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/TechCommentService.java b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/TechCommentService.java index 23980e2c..d151c4ef 100644 --- a/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/TechCommentService.java +++ b/src/main/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/TechCommentService.java @@ -3,40 +3,37 @@ import com.dreamypatisiel.devdevdev.domain.repository.techArticle.TechCommentSort; import com.dreamypatisiel.devdevdev.domain.service.techArticle.dto.TechCommentDto; import com.dreamypatisiel.devdevdev.web.dto.SliceCommentCustom; -import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.ModifyTechCommentRequest; -import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.RegisterTechCommentRequest; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentRecommendResponse; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentResponse; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentsResponse; import java.util.List; +import javax.annotation.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.security.core.Authentication; public interface TechCommentService { TechCommentResponse registerMainTechComment(Long techArticleId, - TechCommentDto techCommentDto, + TechCommentDto registerTechCommentDto, Authentication authentication); TechCommentResponse registerRepliedTechComment(Long techArticleId, Long originParentTechCommentId, Long parentTechCommentId, - RegisterTechCommentRequest registerRepliedTechCommentRequest, + TechCommentDto registerRepliedTechCommentDto, Authentication authentication); - TechCommentResponse modifyTechComment(Long techArticleId, Long techCommentId, - ModifyTechCommentRequest modifyTechCommentRequest, + TechCommentResponse modifyTechComment(Long techArticleId, Long techCommentId, TechCommentDto modifyTechCommentDto, Authentication authentication); - TechCommentResponse deleteTechComment(Long techArticleId, Long techCommentId, Authentication authentication); + TechCommentResponse deleteTechComment(Long techArticleId, Long techCommentId, @Nullable String anonymousMemberId, + Authentication authentication); SliceCommentCustom getTechComments(Long techArticleId, Long techCommentId, TechCommentSort techCommentSort, Pageable pageable, - String anonymousMemberId, - Authentication authentication); + String anonymousMemberId, Authentication authentication); - TechCommentRecommendResponse recommendTechComment(Long techArticleId, Long techCommentId, - Authentication authentication); + TechCommentRecommendResponse recommendTechComment(Long techArticleId, Long techCommentId, Authentication authentication); List findTechBestComments(int size, Long techArticleId, String anonymousMemberId, Authentication authentication); diff --git a/src/main/java/com/dreamypatisiel/devdevdev/web/controller/techArticle/TechArticleCommentController.java b/src/main/java/com/dreamypatisiel/devdevdev/web/controller/techArticle/TechArticleCommentController.java index 476fb84d..382547a8 100644 --- a/src/main/java/com/dreamypatisiel/devdevdev/web/controller/techArticle/TechArticleCommentController.java +++ b/src/main/java/com/dreamypatisiel/devdevdev/web/controller/techArticle/TechArticleCommentController.java @@ -61,7 +61,7 @@ public ResponseEntity> registerMainTechCommen return ResponseEntity.ok(BasicResponse.success(response)); } - @Operation(summary = "기술블로그 답글 작성") + @Operation(summary = "기술블로그 답글 작성", description = "기술블로그 답글을 작성할 수 있습니다.") @PostMapping("/articles/{techArticleId}/comments/{originParentTechCommentId}/{parentTechCommentId}") public ResponseEntity> registerRepliedTechComment( @PathVariable Long techArticleId, @@ -70,15 +70,18 @@ public ResponseEntity> registerRepliedTechCom @RequestBody @Validated RegisterTechCommentRequest registerRepliedTechCommentRequest) { Authentication authentication = AuthenticationMemberUtils.getAuthentication(); + String anonymousMemberId = HttpRequestUtils.getHeaderValue(HEADER_ANONYMOUS_MEMBER_ID); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechCommentRequest, + anonymousMemberId); TechCommentService techCommentService = techArticleServiceStrategy.getTechCommentService(); TechCommentResponse response = techCommentService.registerRepliedTechComment(techArticleId, - originParentTechCommentId, parentTechCommentId, registerRepliedTechCommentRequest, authentication); + originParentTechCommentId, parentTechCommentId, registerRepliedCommentDto, authentication); return ResponseEntity.ok(BasicResponse.success(response)); } - @Operation(summary = "기술블로그 댓글/답글 수정") + @Operation(summary = "기술블로그 댓글/답글 수정", description = "기술블로그 댓글/답글을 수정할 수 있습니다.") @PatchMapping("/articles/{techArticleId}/comments/{techCommentId}") public ResponseEntity> modifyTechComment( @PathVariable Long techArticleId, @@ -86,10 +89,13 @@ public ResponseEntity> modifyTechComment( @RequestBody @Validated ModifyTechCommentRequest modifyTechCommentRequest) { Authentication authentication = AuthenticationMemberUtils.getAuthentication(); + String anonymousMemberId = HttpRequestUtils.getHeaderValue(HEADER_ANONYMOUS_MEMBER_ID); + TechCommentDto modifyTechCommentDto = TechCommentDto.createModifyCommentDto(modifyTechCommentRequest, + anonymousMemberId); TechCommentService techCommentService = techArticleServiceStrategy.getTechCommentService(); TechCommentResponse response = techCommentService.modifyTechComment(techArticleId, techCommentId, - modifyTechCommentRequest, authentication); + modifyTechCommentDto, authentication); return ResponseEntity.ok(BasicResponse.success(response)); } @@ -101,10 +107,11 @@ public ResponseEntity> deleteTechComment( @PathVariable Long techCommentId) { Authentication authentication = AuthenticationMemberUtils.getAuthentication(); + String anonymousMemberId = HttpRequestUtils.getHeaderValue(HEADER_ANONYMOUS_MEMBER_ID); TechCommentService techCommentService = techArticleServiceStrategy.getTechCommentService(); TechCommentResponse response = techCommentService.deleteTechComment(techArticleId, techCommentId, - authentication); + anonymousMemberId, authentication); return ResponseEntity.ok(BasicResponse.success(response)); } diff --git a/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/GuestTechCommentServiceTest.java b/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/GuestTechCommentServiceTest.java index da5c0118..ac0d746c 100644 --- a/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/GuestTechCommentServiceTest.java +++ b/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/GuestTechCommentServiceTest.java @@ -138,8 +138,7 @@ void registerRepliedTechComment() { companyRepository.save(company); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -149,10 +148,11 @@ void registerRepliedTechComment() { Long parentTechCommentId = parentTechComment.getId(); RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, null); // when // then assertThatThrownBy(() -> guestTechCommentService.registerRepliedTechComment( - techArticleId, parentTechCommentId, parentTechCommentId, registerRepliedTechComment, authentication)) + techArticleId, parentTechCommentId, parentTechCommentId, registerCommentDto, authentication)) .isInstanceOf(AccessDeniedException.class) .hasMessage(INVALID_ANONYMOUS_CAN_NOT_USE_THIS_FUNCTION_MESSAGE); } diff --git a/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/MemberTechCommentServiceTest.java b/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/MemberTechCommentServiceTest.java index 718a0927..f47eb2c2 100644 --- a/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/MemberTechCommentServiceTest.java +++ b/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/MemberTechCommentServiceTest.java @@ -235,8 +235,7 @@ void modifyTechComment() { companyRepository.save(company); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -245,13 +244,14 @@ void modifyTechComment() { Long techCommentId = techComment.getId(); ModifyTechCommentRequest modifyTechCommentRequest = new ModifyTechCommentRequest("댓글 수정입니다."); + TechCommentDto modifyCommentDto = TechCommentDto.createModifyCommentDto(modifyTechCommentRequest, null); LocalDateTime modifiedDateTime = LocalDateTime.of(2024, 10, 6, 0, 0, 0); when(timeProvider.getLocalDateTimeNow()).thenReturn(modifiedDateTime); // when TechCommentResponse techCommentResponse = memberTechCommentService.modifyTechComment( - techArticleId, techCommentId, modifyTechCommentRequest, authentication); + techArticleId, techCommentId, modifyCommentDto, authentication); em.flush(); // then @@ -285,10 +285,11 @@ void modifyTechCommentNotFoundMemberException() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); ModifyTechCommentRequest modifyTechCommentRequest = new ModifyTechCommentRequest("댓글 수정입니다."); + TechCommentDto modifyCommentDto = TechCommentDto.createModifyCommentDto(modifyTechCommentRequest, null); // when // then assertThatThrownBy( - () -> memberTechCommentService.modifyTechComment(0L, 0L, modifyTechCommentRequest, + () -> memberTechCommentService.modifyTechComment(0L, 0L, modifyCommentDto, authentication)) .isInstanceOf(MemberException.class) .hasMessage(INVALID_MEMBER_NOT_FOUND_MESSAGE); @@ -319,10 +320,11 @@ void modifyTechCommentNotFoundTechArticleCommentException() { Long techArticleId = techArticle.getId(); ModifyTechCommentRequest modifyTechCommentRequest = new ModifyTechCommentRequest("댓글 수정입니다."); + TechCommentDto modifyCommentDto = TechCommentDto.createModifyCommentDto(modifyTechCommentRequest, null); // when // then assertThatThrownBy( - () -> memberTechCommentService.modifyTechComment(techArticleId, 0L, modifyTechCommentRequest, + () -> memberTechCommentService.modifyTechComment(techArticleId, 0L, modifyCommentDto, authentication)) .isInstanceOf(NotFoundException.class) .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); @@ -348,8 +350,7 @@ void modifyTechCommentAlreadyDeletedException() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -362,11 +363,11 @@ void modifyTechCommentAlreadyDeletedException() { em.flush(); ModifyTechCommentRequest modifyTechCommentRequest = new ModifyTechCommentRequest("댓글 수정"); + TechCommentDto modifyCommentDto = TechCommentDto.createModifyCommentDto(modifyTechCommentRequest, null); // when // then - assertThatThrownBy( - () -> memberTechCommentService.modifyTechComment(techArticleId, techCommentId, modifyTechCommentRequest, - authentication)) + assertThatThrownBy(() -> memberTechCommentService.modifyTechComment(techArticleId, techCommentId, modifyCommentDto, + authentication)) .isInstanceOf(NotFoundException.class) .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); } @@ -390,8 +391,7 @@ void deleteTechComment() { companyRepository.save(company); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -405,7 +405,7 @@ void deleteTechComment() { em.flush(); // when - memberTechCommentService.deleteTechComment(techArticleId, techCommentId, authentication); + memberTechCommentService.deleteTechComment(techArticleId, techCommentId, null, authentication); // then TechComment findTechComment = techCommentRepository.findById(techCommentId).get(); @@ -437,8 +437,7 @@ void deleteTechCommentAlreadyDeletedException() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -452,7 +451,7 @@ void deleteTechCommentAlreadyDeletedException() { // when // then assertThatThrownBy( - () -> memberTechCommentService.deleteTechComment(techArticleId, techCommentId, authentication)) + () -> memberTechCommentService.deleteTechComment(techArticleId, techCommentId, null, authentication)) .isInstanceOf(NotFoundException.class) .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); } @@ -477,14 +476,13 @@ void deleteTechCommentNotFoundException() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); // when // then assertThatThrownBy( - () -> memberTechCommentService.deleteTechComment(techArticleId, 0L, authentication)) + () -> memberTechCommentService.deleteTechComment(techArticleId, 0L, null, authentication)) .isInstanceOf(NotFoundException.class) .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); } @@ -513,8 +511,7 @@ void deleteTechCommentAdmin() { companyRepository.save(company); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -528,7 +525,7 @@ void deleteTechCommentAdmin() { em.flush(); // when - memberTechCommentService.deleteTechComment(techArticleId, techCommentId, authentication); + memberTechCommentService.deleteTechComment(techArticleId, techCommentId, null, authentication); // then TechComment findTechComment = techCommentRepository.findById(techCommentId).get(); @@ -564,8 +561,7 @@ void deleteTechCommentNotByMemberException() { companyRepository.save(company); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -575,7 +571,7 @@ void deleteTechCommentNotByMemberException() { // when // then assertThatThrownBy( - () -> memberTechCommentService.deleteTechComment(techArticleId, techCommentId, authentication)) + () -> memberTechCommentService.deleteTechComment(techArticleId, techCommentId, null, authentication)) .isInstanceOf(NotFoundException.class) .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); } @@ -594,8 +590,7 @@ void deleteTechCommentNotFoundMemberException() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // when // then - assertThatThrownBy( - () -> memberTechCommentService.deleteTechComment(0L, 0L, authentication)) + assertThatThrownBy(() -> memberTechCommentService.deleteTechComment(0L, 0L, null, authentication)) .isInstanceOf(MemberException.class) .hasMessage(INVALID_MEMBER_NOT_FOUND_MESSAGE); } @@ -619,8 +614,7 @@ void registerRepliedTechComment() { companyRepository.save(company); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -630,10 +624,11 @@ void registerRepliedTechComment() { Long parentTechCommentId = parentTechComment.getId(); RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, null); // when TechCommentResponse techCommentResponse = memberTechCommentService.registerRepliedTechComment( - techArticleId, parentTechCommentId, parentTechCommentId, registerRepliedTechComment, authentication); + techArticleId, parentTechCommentId, parentTechCommentId, registerRepliedCommentDto, authentication); em.flush(); // then @@ -695,10 +690,11 @@ void registerRepliedTechCommentToRepliedTechComment() { Long parentTechCommentId = parentTechComment.getId(); RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, null); // when TechCommentResponse techCommentResponse = memberTechCommentService.registerRepliedTechComment( - techArticleId, originParentTechCommentId, parentTechCommentId, registerRepliedTechComment, + techArticleId, originParentTechCommentId, parentTechCommentId, registerRepliedCommentDto, authentication); em.flush(); @@ -754,11 +750,12 @@ void registerRepliedTechCommentNotFoundTechCommentException() { Long techCommentId = techComment.getId() + 1; RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, null); // when // then assertThatThrownBy( () -> memberTechCommentService.registerRepliedTechComment(techArticleId, techCommentId, techCommentId, - registerRepliedTechComment, authentication)) + registerRepliedCommentDto, authentication)) .isInstanceOf(NotFoundException.class) .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); } @@ -798,11 +795,12 @@ void registerRepliedTechCommentDeletedTechCommentException() { em.clear(); RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, null); // when // then assertThatThrownBy( () -> memberTechCommentService.registerRepliedTechComment(techArticleId, techCommentId, techCommentId, - registerRepliedTechComment, authentication)) + registerRepliedCommentDto, authentication)) .isInstanceOf(IllegalArgumentException.class) .hasMessage(INVALID_CAN_NOT_REPLY_DELETED_TECH_COMMENT_MESSAGE); } @@ -824,11 +822,12 @@ void registerRepliedTechCommentNotFoundMemberException() { em.clear(); RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, null); // when // then assertThatThrownBy( () -> memberTechCommentService.registerRepliedTechComment(0L, 0L, 0L, - registerRepliedTechComment, authentication)) + registerRepliedCommentDto, authentication)) .isInstanceOf(MemberException.class) .hasMessage(INVALID_MEMBER_NOT_FOUND_MESSAGE); } diff --git a/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentServiceV2Test.java b/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentServiceV2Test.java index d329be10..c345159d 100644 --- a/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentServiceV2Test.java +++ b/src/test/java/com/dreamypatisiel/devdevdev/domain/service/techArticle/techComment/GuestTechCommentServiceV2Test.java @@ -1,5 +1,7 @@ package com.dreamypatisiel.devdevdev.domain.service.techArticle.techComment; +import static com.dreamypatisiel.devdevdev.domain.exception.TechArticleExceptionMessage.INVALID_CAN_NOT_REPLY_DELETED_TECH_COMMENT_MESSAGE; +import static com.dreamypatisiel.devdevdev.domain.exception.TechArticleExceptionMessage.INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE; import static com.dreamypatisiel.devdevdev.domain.exception.TechArticleExceptionMessage.NOT_FOUND_TECH_ARTICLE_MESSAGE; import static com.dreamypatisiel.devdevdev.domain.service.techArticle.TechTestUtils.createCompany; import static com.dreamypatisiel.devdevdev.domain.service.techArticle.TechTestUtils.createMainTechComment; @@ -39,6 +41,7 @@ import com.dreamypatisiel.devdevdev.global.security.oauth2.model.UserPrincipal; import com.dreamypatisiel.devdevdev.global.utils.AuthenticationMemberUtils; import com.dreamypatisiel.devdevdev.web.dto.SliceCommentCustom; +import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.ModifyTechCommentRequest; import com.dreamypatisiel.devdevdev.web.dto.request.techArticle.RegisterTechCommentRequest; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentResponse; import com.dreamypatisiel.devdevdev.web.dto.response.techArticle.TechCommentsResponse; @@ -52,6 +55,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.security.core.Authentication; @@ -78,7 +82,7 @@ class GuestTechCommentServiceV2Test { TechCommentRecommendRepository techCommentRecommendRepository; @Autowired AnonymousMemberRepository anonymousMemberRepository; - @Autowired + @MockBean TimeProvider timeProvider; @Autowired EntityManager em; @@ -1498,4 +1502,547 @@ void registerTechCommentIllegalStateException() { .isInstanceOf(IllegalStateException.class) .hasMessage(INVALID_METHODS_CALL_MESSAGE); } + + @Test + @DisplayName("익명회원은 기술블로그 댓글에 답글을 작성할 수 있다.") + void registerRepliedTechComment() { + // given + // 회원 생성 + SocialMemberDto socialMemberDto = createSocialDto(userId, name, nickname, password, email, socialType, role); + Member member = Member.createMemberBy(socialMemberDto); + memberRepository.save(member); + + // 익명회원 생성 + AnonymousMember anonymousMember = AnonymousMember.create("anonymousMemberId", "익명으로 개발하는 댑댑이"); + anonymousMemberRepository.save(anonymousMember); + + // 익명회원 목킹 + Authentication authentication = mock(Authentication.class); + when(authentication.getPrincipal()).thenReturn(AuthenticationMemberUtils.ANONYMOUS_USER); + + Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", + "https://example.com"); + companyRepository.save(company); + + TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); + techArticleRepository.save(techArticle); + Long techArticleId = techArticle.getId(); + + TechComment parentTechComment = TechComment.createMainTechCommentByMember(new CommentContents("댓글입니다."), member, + techArticle); + techCommentRepository.save(parentTechComment); + Long parentTechCommentId = parentTechComment.getId(); + + RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, + anonymousMember.getAnonymousMemberId()); + + // when + TechCommentResponse techCommentResponse = guestTechCommentServiceV2.registerRepliedTechComment( + techArticleId, parentTechCommentId, parentTechCommentId, registerRepliedCommentDto, authentication); + + // then + assertThat(techCommentResponse.getTechCommentId()).isNotNull(); + + TechComment findRepliedTechComment = techCommentRepository.findById(techCommentResponse.getTechCommentId()) + .get(); + + assertAll( + // 답글 생성 확인 + () -> assertThat(findRepliedTechComment.getContents().getCommentContents()).isEqualTo("답글입니다."), + () -> assertThat(findRepliedTechComment.getBlameTotalCount().getCount()).isEqualTo(0L), + () -> assertThat(findRepliedTechComment.getRecommendTotalCount().getCount()).isEqualTo(0L), + () -> assertThat(findRepliedTechComment.getReplyTotalCount().getCount()).isEqualTo(0L), + () -> assertThat(findRepliedTechComment.getCreatedAnonymousBy().getId()).isEqualTo(anonymousMember.getId()), + () -> assertThat(findRepliedTechComment.getParent().getId()).isEqualTo(parentTechCommentId), + () -> assertThat(findRepliedTechComment.getOriginParent().getId()).isEqualTo(parentTechCommentId), + // 최상단 댓글의 답글 수 증가 확인 + () -> assertThat(findRepliedTechComment.getOriginParent().getReplyTotalCount().getCount()).isEqualTo( + 1L), + // 기술블로그 댓글 수 증가 확인 + () -> assertThat(findRepliedTechComment.getTechArticle().getCommentTotalCount().getCount()).isEqualTo( + 2L) + ); + } + + @Test + @DisplayName("익명회원은 기술블로그 댓글의 답글에 답글을 작성할 수 있다.") + void registerRepliedTechCommentToRepliedTechComment() { + // given + // 회원 생성 + SocialMemberDto socialMemberDto = createSocialDto(userId, name, nickname, password, email, socialType, role); + Member member = Member.createMemberBy(socialMemberDto); + memberRepository.save(member); + + // 익명회원 생성 + AnonymousMember anonymousMember = AnonymousMember.create("anonymousMemberId", "익명으로 개발하는 댑댑이"); + anonymousMemberRepository.save(anonymousMember); + + // 익명회원 목킹 + Authentication authentication = mock(Authentication.class); + when(authentication.getPrincipal()).thenReturn(AuthenticationMemberUtils.ANONYMOUS_USER); + + // 회사 생성 + Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", + "https://example.com"); + companyRepository.save(company); + + // 기술블로그 생성 + TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), + new Count(1L), new Count(1L), new Count(2L), new Count(1L), null, company); + techArticleRepository.save(techArticle); + Long techArticleId = techArticle.getId(); + + // 댓글 생성 + TechComment originParentTechComment = TechComment.createMainTechCommentByMember(new CommentContents("댓글입니다."), member, + techArticle); + techCommentRepository.save(originParentTechComment); + Long originParentTechCommentId = originParentTechComment.getId(); + + // 답글 생성 + TechComment parentTechComment = TechComment.createRepliedTechCommentByAnonymousMember(new CommentContents("답글입니다."), + anonymousMember, techArticle, originParentTechComment, originParentTechComment); + techCommentRepository.save(parentTechComment); + Long parentTechCommentId = parentTechComment.getId(); + + RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, + anonymousMember.getAnonymousMemberId()); + + // when + TechCommentResponse techCommentResponse = guestTechCommentServiceV2.registerRepliedTechComment(techArticleId, + originParentTechCommentId, parentTechCommentId, registerRepliedCommentDto, authentication); + + // then + assertThat(techCommentResponse.getTechCommentId()).isNotNull(); + + TechComment findRepliedTechComment = techCommentRepository.findById(techCommentResponse.getTechCommentId()) + .get(); + + assertAll( + () -> assertThat(findRepliedTechComment.getContents().getCommentContents()).isEqualTo("답글입니다."), + () -> assertThat(findRepliedTechComment.getBlameTotalCount().getCount()).isEqualTo(0L), + () -> assertThat(findRepliedTechComment.getRecommendTotalCount().getCount()).isEqualTo(0L), + () -> assertThat(findRepliedTechComment.getReplyTotalCount().getCount()).isEqualTo(0L), + () -> assertThat(findRepliedTechComment.getCreatedAnonymousBy().getId()).isEqualTo(anonymousMember.getId()), + () -> assertThat(findRepliedTechComment.getParent().getId()).isEqualTo(parentTechCommentId), + () -> assertThat(findRepliedTechComment.getOriginParent().getId()).isEqualTo(originParentTechCommentId), + // 최상단 댓글의 답글 수 증가 확인 + () -> assertThat(findRepliedTechComment.getOriginParent().getReplyTotalCount().getCount()).isEqualTo( + 1L), + // 기술블로그 댓글 수 증가 확인 + () -> assertThat(findRepliedTechComment.getTechArticle().getCommentTotalCount().getCount()).isEqualTo( + 3L) + ); + } + + @Test + @DisplayName("익명회원이 기술블로그 댓글에 답글을 작성할 때 존재하지 않는 댓글에 답글을 작성하면 예외가 발생한다.") + void registerRepliedTechCommentNotFoundTechCommentException() { + // given + SocialMemberDto socialMemberDto = createSocialDto(userId, name, nickname, password, email, socialType, role); + Member member = Member.createMemberBy(socialMemberDto); + memberRepository.save(member); + + // 익명회원 생성 + AnonymousMember anonymousMember = AnonymousMember.create("anonymousMemberId", "익명으로 개발하는 댑댑이"); + anonymousMemberRepository.save(anonymousMember); + + // 익명회원 목킹 + Authentication authentication = mock(Authentication.class); + when(authentication.getPrincipal()).thenReturn(AuthenticationMemberUtils.ANONYMOUS_USER); + + // 회사 생성 + Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", + "https://example.com"); + companyRepository.save(company); + + // 기술블로그 생성 + TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); + techArticleRepository.save(techArticle); + Long techArticleId = techArticle.getId(); + + // 댓글 생성 + TechComment techComment = TechComment.createMainTechCommentByMember(new CommentContents("댓글입니다."), member, techArticle); + techCommentRepository.save(techComment); + Long invalidTechCommentId = techComment.getId() + 1; + + // 답글 등록 요청 생성 + RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, + anonymousMember.getAnonymousMemberId()); + + // when // then + assertThatThrownBy( + () -> guestTechCommentServiceV2.registerRepliedTechComment(techArticleId, invalidTechCommentId, + invalidTechCommentId, registerRepliedCommentDto, authentication)) + .isInstanceOf(NotFoundException.class) + .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); + } + + @Test + @DisplayName("익명회원이 기술블로그 댓글에 답글을 작성할 때 삭제된 댓글에 답글을 작성하면 예외가 발생한다.") + void registerRepliedTechCommentDeletedTechCommentException() { + // given + SocialMemberDto socialMemberDto = createSocialDto(userId, name, nickname, password, email, socialType, role); + Member member = Member.createMemberBy(socialMemberDto); + memberRepository.save(member); + + // 익명회원 생성 + AnonymousMember anonymousMember = AnonymousMember.create("anonymousMemberId", "익명으로 개발하는 댑댑이"); + anonymousMemberRepository.save(anonymousMember); + + // 익명회원 목킹 + Authentication authentication = mock(Authentication.class); + when(authentication.getPrincipal()).thenReturn(AuthenticationMemberUtils.ANONYMOUS_USER); + + // 회사 생성 + Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", + "https://example.com"); + companyRepository.save(company); + + // 기술블로그 생성 + TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); + techArticleRepository.save(techArticle); + Long techArticleId = techArticle.getId(); + + // 삭제 상태의 댓글 생성 + TechComment techComment = TechComment.createMainTechCommentByMember(new CommentContents("댓글입니다."), member, techArticle); + techCommentRepository.save(techComment); + Long techCommentId = techComment.getId(); + + LocalDateTime deletedAt = LocalDateTime.of(2024, 10, 6, 0, 0, 0); + techComment.changeDeletedAt(deletedAt, member); + + em.flush(); + em.clear(); + + RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, + anonymousMember.getAnonymousMemberId()); + + // when // then + assertThatThrownBy( + () -> guestTechCommentServiceV2.registerRepliedTechComment(techArticleId, techCommentId, techCommentId, + registerRepliedCommentDto, authentication)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage(INVALID_CAN_NOT_REPLY_DELETED_TECH_COMMENT_MESSAGE); + } + + @Test + @DisplayName("회원이 익명회원 전용 기술블로그 댓글에 답글을 작성하면 예외가 발생한다.") + void registerRepliedTechCommentIllegalStateException() { + // given + SocialMemberDto socialMemberDto = createSocialDto(userId, name, nickname, password, email, socialType, role); + Member member = Member.createMemberBy(socialMemberDto); + + UserPrincipal userPrincipal = UserPrincipal.createByMember(member); + SecurityContext context = SecurityContextHolder.getContext(); + context.setAuthentication(new OAuth2AuthenticationToken(userPrincipal, userPrincipal.getAuthorities(), + userPrincipal.getSocialType().name())); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + em.flush(); + em.clear(); + + RegisterTechCommentRequest registerRepliedTechComment = new RegisterTechCommentRequest("답글입니다."); + TechCommentDto registerRepliedCommentDto = TechCommentDto.createRegisterCommentDto(registerRepliedTechComment, null); + + // when // then + assertThatThrownBy( + () -> guestTechCommentServiceV2.registerRepliedTechComment(0L, 0L, 0L, + registerRepliedCommentDto, authentication)) + .isInstanceOf(IllegalStateException.class) + .hasMessage(INVALID_METHODS_CALL_MESSAGE); + } + + @Test + @DisplayName("익명회원은 본인이 작성한 삭제되지 않은 댓글을 수정할 수 있다. 수정시 편집된 시각이 갱신된다.") + void modifyTechComment() { + // given + // 익명회원 생성 + AnonymousMember anonymousMember = AnonymousMember.create("anonymousMemberId", "익명으로 개발하는 댑댑이"); + anonymousMemberRepository.save(anonymousMember); + + // 익명회원 목킹 + Authentication authentication = mock(Authentication.class); + when(authentication.getPrincipal()).thenReturn(AuthenticationMemberUtils.ANONYMOUS_USER); + + // 회사 생성 + Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", + "https://example.com"); + companyRepository.save(company); + + // 기술블로그 생성 + TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); + techArticleRepository.save(techArticle); + Long techArticleId = techArticle.getId(); + + // 댓글 생성 + TechComment techComment = TechComment.createMainTechCommentByAnonymousMember(new CommentContents("댓글입니다"), + anonymousMember, techArticle); + techCommentRepository.save(techComment); + Long techCommentId = techComment.getId(); + + ModifyTechCommentRequest modifyTechCommentRequest = new ModifyTechCommentRequest("댓글 수정입니다."); + TechCommentDto modifyCommentDto = TechCommentDto.createModifyCommentDto(modifyTechCommentRequest, + anonymousMember.getAnonymousMemberId()); + + LocalDateTime modifiedDateTime = LocalDateTime.of(2024, 10, 6, 0, 0, 0); + when(timeProvider.getLocalDateTimeNow()).thenReturn(modifiedDateTime); + + // when + TechCommentResponse techCommentResponse = guestTechCommentServiceV2.modifyTechComment( + techArticleId, techCommentId, modifyCommentDto, authentication); + em.flush(); + + // then + assertThat(techCommentResponse.getTechCommentId()).isNotNull(); + + TechComment findTechComment = techCommentRepository.findById(techCommentResponse.getTechCommentId()) + .get(); + + assertAll( + () -> assertThat(findTechComment.getContents().getCommentContents()).isEqualTo("댓글 수정입니다."), + () -> assertThat(findTechComment.getBlameTotalCount().getCount()).isEqualTo(0L), + () -> assertThat(findTechComment.getRecommendTotalCount().getCount()).isEqualTo(0L), + () -> assertThat(findTechComment.getCreatedAnonymousBy().getId()).isEqualTo(anonymousMember.getId()), + () -> assertThat(findTechComment.getTechArticle().getId()).isEqualTo(techArticleId), + () -> assertThat(findTechComment.getId()).isEqualTo(techCommentId), + () -> assertThat(findTechComment.getContentsLastModifiedAt()).isEqualTo(modifiedDateTime) + ); + } + + @Test + @DisplayName("회원이 기술블로그 댓글을 수정할 때 익명회원 전용 기술블로그 댓글 수정 메소드를 호출하면 예외가 발생한다.") + void modifyTechCommentNotFoundMemberException() { + // given + SocialMemberDto socialMemberDto = createSocialDto(userId, name, nickname, password, email, socialType, role); + Member member = Member.createMemberBy(socialMemberDto); + + UserPrincipal userPrincipal = UserPrincipal.createByMember(member); + SecurityContext context = SecurityContextHolder.getContext(); + context.setAuthentication(new OAuth2AuthenticationToken(userPrincipal, userPrincipal.getAuthorities(), + userPrincipal.getSocialType().name())); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + ModifyTechCommentRequest modifyTechCommentRequest = new ModifyTechCommentRequest("댓글 수정입니다."); + TechCommentDto modifyCommentDto = TechCommentDto.createModifyCommentDto(modifyTechCommentRequest, null); + + // when // then + assertThatThrownBy(() -> guestTechCommentServiceV2.modifyTechComment(0L, 0L, modifyCommentDto, authentication)) + .isInstanceOf(IllegalStateException.class) + .hasMessage(INVALID_METHODS_CALL_MESSAGE); + } + + @Test + @DisplayName("회원이 기술블로그 댓글을 수정할 때 댓글이 존재하지 않으면 예외가 발생한다.") + void modifyTechCommentNotFoundTechArticleCommentException() { + // given + // 익명회원 생성 + AnonymousMember anonymousMember = AnonymousMember.create("anonymousMemberId", "익명으로 개발하는 댑댑이"); + anonymousMemberRepository.save(anonymousMember); + + // 익명회원 목킹 + Authentication authentication = mock(Authentication.class); + when(authentication.getPrincipal()).thenReturn(AuthenticationMemberUtils.ANONYMOUS_USER); + + // 회사 생성 + Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", + "https://example.com"); + companyRepository.save(company); + + // 기술블로그 생성 + TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); + techArticleRepository.save(techArticle); + Long techArticleId = techArticle.getId(); + + ModifyTechCommentRequest modifyTechCommentRequest = new ModifyTechCommentRequest("댓글 수정입니다."); + TechCommentDto modifyCommentDto = TechCommentDto.createModifyCommentDto(modifyTechCommentRequest, + anonymousMember.getAnonymousMemberId()); + + // when // then + assertThatThrownBy(() -> guestTechCommentServiceV2.modifyTechComment(techArticleId, 0L, modifyCommentDto, authentication)) + .isInstanceOf(NotFoundException.class) + .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); + } + + @Test + @DisplayName("익명회원이 기술블로그 댓글을 수정할 때, 이미 삭제된 댓글이라면 예외가 발생한다.") + void modifyTechCommentAlreadyDeletedException() { + // given + // 익명회원 생성 + AnonymousMember anonymousMember = AnonymousMember.create("anonymousMemberId", "익명으로 개발하는 댑댑이"); + anonymousMemberRepository.save(anonymousMember); + + // 익명회원 목킹 + Authentication authentication = mock(Authentication.class); + when(authentication.getPrincipal()).thenReturn(AuthenticationMemberUtils.ANONYMOUS_USER); + + // 회사 생성 + Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", + "https://example.com"); + companyRepository.save(company); + + TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); + techArticleRepository.save(techArticle); + Long techArticleId = techArticle.getId(); + + TechComment techComment = TechComment.createMainTechCommentByAnonymousMember(new CommentContents("댓글입니다"), + anonymousMember, + techArticle); + techCommentRepository.save(techComment); + Long techCommentId = techComment.getId(); + + LocalDateTime deletedAt = LocalDateTime.of(2024, 10, 6, 0, 0, 0); + techComment.changeDeletedAt(deletedAt, anonymousMember); + em.flush(); + + ModifyTechCommentRequest modifyTechCommentRequest = new ModifyTechCommentRequest("댓글 수정"); + TechCommentDto modifyCommentDto = TechCommentDto.createModifyCommentDto(modifyTechCommentRequest, + anonymousMember.getAnonymousMemberId()); + + // when // then + assertThatThrownBy( + () -> guestTechCommentServiceV2.modifyTechComment(techArticleId, techCommentId, modifyCommentDto, + authentication)) + .isInstanceOf(NotFoundException.class) + .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); + } + + @Test + @DisplayName("익명회원은 본인이 작성한, 아직 삭제되지 않은 댓글을 삭제할 수 있다.") + void deleteTechComment() { + // given + // 익명회원 생성 + AnonymousMember anonymousMember = AnonymousMember.create("anonymousMemberId", "익명으로 개발하는 댑댑이"); + anonymousMemberRepository.save(anonymousMember); + + // 익명회원 목킹 + Authentication authentication = mock(Authentication.class); + when(authentication.getPrincipal()).thenReturn(AuthenticationMemberUtils.ANONYMOUS_USER); + + Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", + "https://example.com"); + companyRepository.save(company); + + TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); + techArticleRepository.save(techArticle); + Long techArticleId = techArticle.getId(); + + TechComment techComment = TechComment.createMainTechCommentByAnonymousMember(new CommentContents("댓글입니다"), + anonymousMember, techArticle); + techCommentRepository.save(techComment); + Long techCommentId = techComment.getId(); + + LocalDateTime deletedAt = LocalDateTime.of(2024, 10, 6, 0, 0, 0); + when(timeProvider.getLocalDateTimeNow()).thenReturn(deletedAt); + + em.flush(); + + // when + guestTechCommentServiceV2.deleteTechComment(techArticleId, techCommentId, anonymousMember.getAnonymousMemberId(), + authentication); + + // then + TechComment findTechComment = techCommentRepository.findById(techCommentId).get(); + + assertAll( + () -> assertThat(findTechComment.getDeletedAt()).isNotNull(), + () -> assertThat(findTechComment.getDeletedAnonymousBy().getId()).isEqualTo(anonymousMember.getId()), + () -> assertThat(findTechComment.getDeletedAt()).isEqualTo(deletedAt) + ); + } + + @Test + @DisplayName("익명회원이 댓글을 삭제할 때, 이미 삭제된 댓글이라면 예외가 발생한다.") + void deleteTechCommentAlreadyDeletedException() { + // given + // 익명회원 생성 + AnonymousMember anonymousMember = AnonymousMember.create("anonymousMemberId", "익명으로 개발하는 댑댑이"); + anonymousMemberRepository.save(anonymousMember); + + // 익명회원 목킹 + Authentication authentication = mock(Authentication.class); + when(authentication.getPrincipal()).thenReturn(AuthenticationMemberUtils.ANONYMOUS_USER); + + Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", + "https://example.com"); + companyRepository.save(company); + + TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); + techArticleRepository.save(techArticle); + Long techArticleId = techArticle.getId(); + + TechComment techComment = TechComment.createMainTechCommentByAnonymousMember(new CommentContents("댓글입니다"), + anonymousMember, techArticle); + techCommentRepository.save(techComment); + Long techCommentId = techComment.getId(); + + LocalDateTime deletedAt = LocalDateTime.of(2024, 10, 6, 0, 0, 0); + techComment.changeDeletedAt(deletedAt, anonymousMember); + em.flush(); + + // when // then + assertThatThrownBy(() -> guestTechCommentServiceV2.deleteTechComment(techArticleId, techCommentId, + anonymousMember.getAnonymousMemberId(), authentication)) + .isInstanceOf(NotFoundException.class) + .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); + } + + @Test + @DisplayName("익명회원이 댓글을 삭제할 때, 댓글이 존재하지 않으면 예외가 발생한다.") + void deleteTechCommentNotFoundException() { + // given + Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", + "https://example.com"); + companyRepository.save(company); + + // 익명회원 생성 + AnonymousMember anonymousMember = AnonymousMember.create("anonymousMemberId", "익명으로 개발하는 댑댑이"); + anonymousMemberRepository.save(anonymousMember); + + // 익명회원 목킹 + Authentication authentication = mock(Authentication.class); + when(authentication.getPrincipal()).thenReturn(AuthenticationMemberUtils.ANONYMOUS_USER); + + TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); + techArticleRepository.save(techArticle); + Long techArticleId = techArticle.getId(); + + // when // then + assertThatThrownBy( + () -> guestTechCommentServiceV2.deleteTechComment(techArticleId, 0L, anonymousMember.getAnonymousMemberId(), + authentication)) + .isInstanceOf(NotFoundException.class) + .hasMessage(INVALID_NOT_FOUND_TECH_COMMENT_MESSAGE); + } + + @Test + @DisplayName("댓글을 삭제할 때 회원이 익명회원 전용 댓글 삭제 메소드를 호출하면 예외가 발생한다.") + void deleteTechCommentIllegalStateException() { + // given + SocialMemberDto socialMemberDto = createSocialDto(userId, name, nickname, password, email, socialType, role); + Member member = Member.createMemberBy(socialMemberDto); + + UserPrincipal userPrincipal = UserPrincipal.createByMember(member); + SecurityContext context = SecurityContextHolder.getContext(); + context.setAuthentication(new OAuth2AuthenticationToken(userPrincipal, userPrincipal.getAuthorities(), + userPrincipal.getSocialType().name())); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + // when // then + assertThatThrownBy(() -> guestTechCommentServiceV2.deleteTechComment(0L, 0L, null, authentication)) + .isInstanceOf(IllegalStateException.class) + .hasMessage(INVALID_METHODS_CALL_MESSAGE); + } } \ No newline at end of file diff --git a/src/test/java/com/dreamypatisiel/devdevdev/web/controller/techArticle/TechArticleCommentControllerTest.java b/src/test/java/com/dreamypatisiel/devdevdev/web/controller/techArticle/TechArticleCommentControllerTest.java index ff59b145..8f61b87d 100644 --- a/src/test/java/com/dreamypatisiel/devdevdev/web/controller/techArticle/TechArticleCommentControllerTest.java +++ b/src/test/java/com/dreamypatisiel/devdevdev/web/controller/techArticle/TechArticleCommentControllerTest.java @@ -514,8 +514,7 @@ void registerRepliedTechComment() throws Exception { companyRepository.save(company); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -569,8 +568,7 @@ void registerRepliedTechCommentContentsIsNullException(String contents) throws E companyRepository.save(company); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); TechArticle savedTechArticle = techArticleRepository.save(techArticle); Long techArticleId = savedTechArticle.getId(); @@ -619,8 +617,7 @@ void getTechComments() throws Exception { companyRepository.save(company); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(12L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(12L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); diff --git a/src/test/java/com/dreamypatisiel/devdevdev/web/docs/TechArticleCommentControllerDocsTest.java b/src/test/java/com/dreamypatisiel/devdevdev/web/docs/TechArticleCommentControllerDocsTest.java index 674c2e1c..3d192657 100644 --- a/src/test/java/com/dreamypatisiel/devdevdev/web/docs/TechArticleCommentControllerDocsTest.java +++ b/src/test/java/com/dreamypatisiel/devdevdev/web/docs/TechArticleCommentControllerDocsTest.java @@ -353,8 +353,7 @@ void modifyTechComment() throws Exception { memberRepository.save(member); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -418,8 +417,7 @@ void modifyTechCommentContentsIsNullException(String contents) throws Exception memberRepository.save(member); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -474,8 +472,7 @@ void modifyTechCommentNotFoundException() throws Exception { memberRepository.save(member); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -525,8 +522,7 @@ void deleteTechComment() throws Exception { memberRepository.save(member); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -583,8 +579,7 @@ void deleteTechCommentNotFoundException() throws Exception { memberRepository.save(member); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId(); @@ -620,7 +615,7 @@ void deleteTechCommentNotFoundException() throws Exception { } @Test - @DisplayName("회원은 기술블로그 댓글에 답글을 작성할 수 있다.") + @DisplayName("기술블로그 댓글에 답글을 작성할 수 있다.") void registerTechReply() throws Exception { // given Company company = createCompany("꿈빛 파티시엘", "https://example.com/company.png", "https://example.com", @@ -628,8 +623,7 @@ void registerTechReply() throws Exception { companyRepository.save(company); TechArticle techArticle = TechArticle.createTechArticle(new Title("기술블로그 제목"), new Url("https://example.com"), - new Count(1L), - new Count(1L), new Count(1L), new Count(1L), null, company); + new Count(1L), new Count(1L), new Count(1L), new Count(1L), null, company); techArticleRepository.save(techArticle); Long techArticleId = techArticle.getId();