Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
== 기술블로그 댓글 수정 API(PATCH: /devdevdev/api/v1/articles/{techArticleId}/comments/{techCommentId})

* 기술블로그 댓글을 수정한다.
* 회원 본인이 작성한 기술블로그 댓글을 수정할 수 있다.
* 삭제된 댓글을 수정할 수 없다.
** 회원인 경우 토큰을 `Authorization` 헤더에 포함시켜야 한다.
** 익명 회원인 경우 `Anonymous-Member-Id` 헤더에 익명 회원 아이디를 포함시켜야 한다.
* 회원 또는 익명회원 본인이 작성한 기술블로그 댓글/답글 만 수정 할 수 있다.
* 삭제된 댓글/답글을 수정할 수 없다.

=== 정상 요청/응답

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
== 기술블로그 답글 작성 API(POST: /devdevdev/api/v1/articles/{techArticleId}/comments/{originParentTechCommentId}/{parentTechCommentId}

* 회원은 기술블로그에 댓글에 답글을 작성할 수 있다.
* 익명회원은 답글을 작성할 수 없다.
** 회원인 경우 토큰을 `Authorization` 헤더에 포함시켜야 한다.
** 익명 회원인 경우 `Anonymous-Member-Id` 헤더에 익명 회원 아이디를 포함시켜야 한다.
* 삭제된 댓글에는 답글을 작성할 수 없다.
* 최초 댓글에 대한 답글을 작성할 경우 `techCommentOriginParentId` 값과 `techParentCommentId` 값이 동일하다.

Expand Down Expand Up @@ -40,5 +41,6 @@ include::{snippets}/register-tech-article-reply/response-fields.adoc[]
* `회원을 찾을 수 없습니다.`: 회원 정보가 없을 경우
* `익명 회원은 사용할 수 없는 기능 입니다.`: 익명 회원이 사용할 수 없는 기능일 경우
* `존재하지 않는 기술블로그입니다.`: 기술블로그가 존재하지 않는 경우
* `익명 사용자가 아닙니다. 잘못된 메소드 호출 입니다.`: 회원이 익명 회원 메소드를 호출한 경우

include::{snippets}/register-tech-article-reply-null-exception/response-body.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,6 +17,9 @@ public interface TechCommentRepository extends JpaRepository<TechComment, Long>,
Optional<TechComment> findByIdAndTechArticleIdAndCreatedByIdAndDeletedAtIsNull(Long id, Long techArticleId,
Long createdById);

Optional<TechComment> findByIdAndTechArticleIdAndCreatedAnonymousByAndDeletedAtIsNull(Long id, Long techArticleId,
AnonymousMember createdAnonymousBy);

Optional<TechComment> findByIdAndTechArticleIdAndDeletedAtIsNull(Long id, Long techArticleId);

@EntityGraph(attributePaths = {"createdBy", "deletedBy", "createdAnonymousBy", "deletedAnonymousBy", "techArticle"})
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
Expand All @@ -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());
}

/**
Expand Down
Loading