Skip to content

Commit

Permalink
[feat #45] 게시글 상세 조회 응답 필드 추가 (#46)
Browse files Browse the repository at this point in the history
* [feat] : 질문글 등록 응답 dto 생성

* [feat] : 상세 조회 응답 dto에 추천수, 북마크수 필드 추가

* [feat] : 상세 조회 DTO, 등록 DTO 분리

* [feat] : 북마크수, 추천수 구하는 비즈니스 로직 추가

* [refactor] : fixture 메서드명 수정

* [feat] : 북마크수, 추천수 구하는 비즈니스 로직 테스트

* [feat] : API별 DTO 분리 반영

* [test] : dto 응답 필드 추가 반영

* [style] : 코드 리포멧팅

* [fix] : DTO mapper 함수명 수정

* [feat] : 엔티티 필드명 수정
  • Loading branch information
hyun2371 authored Aug 16, 2024
1 parent 6f47134 commit 061f022
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ public class InteractionCount extends TimeBaseEntity {
@Column(name = "question_post_id")
private Long questionPostId;

@Column(name = "total_count", nullable = false)
private int totalCount;
@Column(name = "count", nullable = false)
private int count;

@Enumerated(EnumType.STRING)
@Column(name = "type")
private InteractionType type;

private InteractionCount(InteractionType type, Long questionPostId) {
this.totalCount = 1;
this.count = 1;
this.type = type;
this.questionPostId = questionPostId;
}
Expand All @@ -43,11 +43,11 @@ public static InteractionCount of(InteractionType type, Long questionPostId) {
return new InteractionCount(type, questionPostId);
}

public int increaseTotalCount() {
return ++totalCount;
public int increaseCount() {
return ++count;
}

public int decreaseTotalCount() {
return --totalCount;
public int decreaseCount() {
return --count;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ public InteractionResponse activateInteraction(
Long memberId,
InteractionType type // 북마크, 추천
) {
int totalCount;
int count;
if (!interactionRepository.existsByQuestionPostIdAndMemberIdAndType // 상호 작용 존재x -> 저장
(questionPostId, memberId, type)
) {
totalCount = createInteraction(questionPostId, memberId, type);
count = createInteraction(questionPostId, memberId, type);
} else { // 존재 -> 값 업데이트
totalCount = updateInteractionAndCount(questionPostId, memberId, type, true);
count = updateInteractionAndCount(questionPostId, memberId, type, true);
}
return InteractionMapper.toPostInteractionResponse(
totalCount, type
count, type
);
}

Expand All @@ -52,9 +52,9 @@ public InteractionResponse inactivateInteraction(
Long memberId,
InteractionType type
) {
int totalCount = updateInteractionAndCount(questionPostId, memberId, type, false);
int count = updateInteractionAndCount(questionPostId, memberId, type, false);
return InteractionMapper.toPostInteractionResponse(
totalCount, type
count, type
);
}

Expand All @@ -73,7 +73,7 @@ private int createInteraction(
() -> interactionCountRepository
.save(InteractionMapper.toPostInteractionCount(questionPostId, type)) // 생성 시 count 1로 초기화
)
.getTotalCount();
.getCount();
}

private void validateIfPostExistsAndNotQuestioner(
Expand All @@ -93,18 +93,18 @@ private int updateInteractionAndCount(
InteractionType type,
boolean isActivate
) {
int totalCount;
int count;
Interaction interaction = getPostInteraction(questionPostId, memberId, type);
InteractionCount interactionCount = getPostInteractionCount(questionPostId, type);

if (isActivate) { //활성화
interaction.updateIsInteractedTrue();
totalCount = interactionCount.increaseTotalCount();
count = interactionCount.increaseCount();
} else { // 비활성화
interaction.updateIsInteractedFalse();
totalCount = interactionCount.decreaseTotalCount();
count = interactionCount.decreaseCount();
}
return totalCount;
return count;
}

private Interaction getPostInteraction(Long questionPostId, Long memberId, InteractionType type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.dnd.gongmuin.question_post.dto.request.RegisterQuestionPostRequest;
import com.dnd.gongmuin.question_post.dto.response.QuestionPostDetailResponse;
import com.dnd.gongmuin.question_post.dto.response.QuestionPostSimpleResponse;
import com.dnd.gongmuin.question_post.dto.response.RegisterQuestionPostResponse;
import com.dnd.gongmuin.question_post.service.QuestionPostService;

import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -36,11 +37,11 @@ public class QuestionPostController {
@Operation(summary = "질문글 등록 API", description = "질문글을 등록한다")
@ApiResponse(useReturnTypeSchema = true)
@PostMapping
public ResponseEntity<QuestionPostDetailResponse> registerQuestionPost(
public ResponseEntity<RegisterQuestionPostResponse> registerQuestionPost(
@Valid @RequestBody RegisterQuestionPostRequest request,
@AuthenticationPrincipal Member member
) {
QuestionPostDetailResponse response = questionPostService.registerQuestionPost(request, member);
RegisterQuestionPostResponse response = questionPostService.registerQuestionPost(request, member);
return ResponseEntity.ok(response);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.dnd.gongmuin.question_post.dto.response.MemberInfo;
import com.dnd.gongmuin.question_post.dto.response.QuestionPostDetailResponse;
import com.dnd.gongmuin.question_post.dto.response.QuestionPostSimpleResponse;
import com.dnd.gongmuin.question_post.dto.response.RegisterQuestionPostResponse;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
Expand All @@ -25,9 +26,36 @@ public static QuestionPost toQuestionPost(RegisterQuestionPostRequest request, M
return QuestionPost.of(request.title(), request.content(), request.reward(), jobGroup, images, member);
}

public static QuestionPostDetailResponse toQuestionPostDetailResponse(QuestionPost questionPost) {
public static QuestionPostDetailResponse toQuestionPostDetailResponse(
QuestionPost questionPost,
int recommendCount,
int savedCount
) {
Member member = questionPost.getMember();
return new QuestionPostDetailResponse(
questionPost.getId(),
questionPost.getTitle(),
questionPost.getContent(),
questionPost.getImages().stream()
.map(QuestionPostImage::getImageUrl).toList(),
questionPost.getReward(),
questionPost.getJobGroup().getLabel(),
new MemberInfo(
member.getId(),
member.getNickname(),
member.getJobGroup().getLabel()
),
recommendCount,
savedCount,
questionPost.getCreatedAt().toString()
);
}

public static RegisterQuestionPostResponse toQuestionPostDetailResponse(
QuestionPost questionPost
) {
Member member = questionPost.getMember();
return new RegisterQuestionPostResponse(
questionPost.getId(),
questionPost.getTitle(),
questionPost.getContent(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public record QuestionPostDetailResponse(
int reward,
String targetJobGroup,
MemberInfo memberInfo,
int recommendCount,
int savedCount,
String createdAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.dnd.gongmuin.question_post.dto.response;

import java.util.List;

public record RegisterQuestionPostResponse(
Long questionPostId,
String title,
String content,
List<String> imageUrls,
int reward,
String targetJobGroup,
MemberInfo memberInfo,
String createdAt
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
import com.dnd.gongmuin.common.exception.runtime.ValidationException;
import com.dnd.gongmuin.member.domain.Member;
import com.dnd.gongmuin.member.exception.MemberErrorCode;
import com.dnd.gongmuin.post_interaction.domain.InteractionCount;
import com.dnd.gongmuin.post_interaction.domain.InteractionType;
import com.dnd.gongmuin.post_interaction.repository.InteractionCountRepository;
import com.dnd.gongmuin.question_post.domain.QuestionPost;
import com.dnd.gongmuin.question_post.dto.QuestionPostMapper;
import com.dnd.gongmuin.question_post.dto.request.QuestionPostSearchCondition;
import com.dnd.gongmuin.question_post.dto.request.RegisterQuestionPostRequest;
import com.dnd.gongmuin.question_post.dto.response.QuestionPostDetailResponse;
import com.dnd.gongmuin.question_post.dto.response.QuestionPostSimpleResponse;
import com.dnd.gongmuin.question_post.dto.response.RegisterQuestionPostResponse;
import com.dnd.gongmuin.question_post.exception.QuestionPostErrorCode;
import com.dnd.gongmuin.question_post.repository.QuestionPostQueryRepository;
import com.dnd.gongmuin.question_post.repository.QuestionPostRepository;
Expand All @@ -30,20 +34,31 @@ public class QuestionPostService {
private final QuestionPostRepository questionPostRepository;
private final QuestionPostQueryRepository questionPostQueryRepository;

private final InteractionCountRepository interactionCountRepository;

@Transactional
public QuestionPostDetailResponse registerQuestionPost(RegisterQuestionPostRequest request, Member member) {
public RegisterQuestionPostResponse registerQuestionPost(
RegisterQuestionPostRequest request,
Member member
) {
if (member.getCredit() < request.reward()) {
throw new ValidationException(MemberErrorCode.NOT_ENOUGH_CREDIT);
}
QuestionPost questionPost = QuestionPostMapper.toQuestionPost(request, member);
return QuestionPostMapper.toQuestionPostDetailResponse(questionPostRepository.save(questionPost));
return QuestionPostMapper.toQuestionPostDetailResponse(
questionPostRepository.save(questionPost)
);
}

@Transactional(readOnly = true)
public QuestionPostDetailResponse getQuestionPostById(Long questionPostId) {
QuestionPost questionPost = questionPostRepository.findById(questionPostId)
.orElseThrow(() -> new NotFoundException(QuestionPostErrorCode.NOT_FOUND_QUESTION_POST));
return QuestionPostMapper.toQuestionPostDetailResponse(questionPost);
return QuestionPostMapper.toQuestionPostDetailResponse(
questionPost,
getCountByType(questionPostId, InteractionType.RECOMMEND),
getCountByType(questionPostId, InteractionType.SAVED)
);
}

@Transactional(readOnly = true)
Expand All @@ -56,4 +71,11 @@ public PageResponse<QuestionPostSimpleResponse> searchQuestionPost(
.map(QuestionPostMapper::toQuestionPostSimpleResponse);
return PageMapper.toPageResponse(responsePage);
}

private int getCountByType(Long questionPostId, InteractionType type) {
return interactionCountRepository
.findByQuestionPostIdAndType(questionPostId, type)
.map(InteractionCount::getCount)
.orElse(0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class InteractionCountFixture {

public static InteractionCount postInteractionCount(
public static InteractionCount interactionCount(
InteractionType type,
Long questionPostId
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class InteractionFixture {

public static Interaction postInteraction(
public static Interaction interaction(
InteractionType type,
Long memberId,
Long questionPostId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ void activateInteraction_old() throws Exception {
QuestionPost questionPost = questionPostRepository.save(
QuestionPostFixture.questionPost(questioner)
);
interactionRepository.save(InteractionFixture.postInteraction(InteractionType.RECOMMEND,
interactionRepository.save(InteractionFixture.interaction(InteractionType.RECOMMEND,
loginMember.getId(), questionPost.getId()));
interactionCountRepository.save(
InteractionCountFixture.postInteractionCount(InteractionType.RECOMMEND, questionPost.getId())
InteractionCountFixture.interactionCount(InteractionType.RECOMMEND, questionPost.getId())
);

mockMvc.perform(post("/api/question-posts/{questionPostId}/inactivated", questionPost.getId())
Expand All @@ -84,10 +84,10 @@ void inactivateInteraction() throws Exception {
QuestionPost questionPost = questionPostRepository.save(
QuestionPostFixture.questionPost(questioner)
);
interactionRepository.save(InteractionFixture.postInteraction(InteractionType.RECOMMEND,
interactionRepository.save(InteractionFixture.interaction(InteractionType.RECOMMEND,
loginMember.getId(), questionPost.getId()));
interactionCountRepository.save(
InteractionCountFixture.postInteractionCount(InteractionType.RECOMMEND, questionPost.getId())
InteractionCountFixture.interactionCount(InteractionType.RECOMMEND, questionPost.getId())
);

mockMvc.perform(post("/api/question-posts/{questionPostId}/inactivated", questionPost.getId())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ void activateInteraction_update() {
//given
InteractionType type = InteractionType.RECOMMEND;
QuestionPost questionPost = QuestionPostFixture.questionPost(1L, questioner);
Interaction interaction = InteractionFixture.postInteraction(type, interactor.getId(),
Interaction interaction = InteractionFixture.interaction(type, interactor.getId(),
questionPost.getId());
InteractionCount interactionCount = InteractionCountFixture.postInteractionCount(type,
InteractionCount interactionCount = InteractionCountFixture.interactionCount(type,
interactor.getId());
interaction.updateIsInteractedFalse();
interactionCount.decreaseTotalCount();
interactionCount.decreaseCount();

given(interactionRepository.existsByQuestionPostIdAndMemberIdAndType(
questionPost.getId(), interactor.getId(), type
Expand Down Expand Up @@ -150,9 +150,9 @@ void inactivateInteraction() {
//given
InteractionType type = InteractionType.RECOMMEND;
QuestionPost questionPost = QuestionPostFixture.questionPost(1L, questioner);
Interaction interaction = InteractionFixture.postInteraction(type, interactor.getId(),
Interaction interaction = InteractionFixture.interaction(type, interactor.getId(),
questionPost.getId());
InteractionCount interactionCount = InteractionCountFixture.postInteractionCount(type,
InteractionCount interactionCount = InteractionCountFixture.interactionCount(type,
interactor.getId());

given(interactionRepository.findByQuestionPostIdAndMemberIdAndType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ void getQuestionPostById() throws Exception {
.andExpect(jsonPath("$.targetJobGroup").value(questionPost.getJobGroup().getLabel()))
.andExpect(jsonPath("$.memberInfo.memberId").value(questionPost.getMember().getId()))
.andExpect(jsonPath("$.memberInfo.nickname").value(questionPost.getMember().getNickname()))
.andExpect(jsonPath("$.memberInfo.memberJobGroup").value(questionPost.getMember().getJobGroup().getLabel())
.andExpect(jsonPath("$.memberInfo.memberJobGroup").value(questionPost.getMember().getJobGroup().getLabel()))
.andExpect(jsonPath("$.recommendCount").value(0))
.andExpect(jsonPath("$.savedCount").value(0)
);
}

Expand Down
Loading

0 comments on commit 061f022

Please sign in to comment.