Skip to content

Commit

Permalink
refactor: 커리큘럼 ID의 모든 키워드 가져오는 로직 JPQL 사용해서 리팩토링
Browse files Browse the repository at this point in the history
  • Loading branch information
nuyh99 committed Nov 23, 2023
1 parent d6cf99f commit 4e1ad8e
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 179 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package wooteco.prolog.roadmap.application;

import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION;
import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_SESSION_NOT_FOUND_EXCEPTION;

import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import wooteco.prolog.common.exception.BadRequestException;
Expand All @@ -15,6 +11,11 @@
import wooteco.prolog.roadmap.domain.repository.KeywordRepository;
import wooteco.prolog.session.domain.repository.SessionRepository;

import java.util.List;

import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION;
import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_SESSION_NOT_FOUND_EXCEPTION;

@Transactional
@Service
public class KeywordService {
Expand All @@ -23,7 +24,7 @@ public class KeywordService {
private final KeywordRepository keywordRepository;

public KeywordService(final SessionRepository sessionRepository,
final KeywordRepository keywordRepository) {
final KeywordRepository keywordRepository) {
this.sessionRepository = sessionRepository;
this.keywordRepository = keywordRepository;
}
Expand Down Expand Up @@ -83,14 +84,14 @@ public KeywordsResponse findSessionIncludeRootKeywords(final Long sessionId) {

List<Keyword> keywords = keywordRepository.findBySessionIdAndParentIsNull(sessionId);

return KeywordsResponse.createResponse(keywords);
return KeywordsResponse.of(keywords);
}

@Transactional(readOnly = true)
public KeywordsResponse newFindSessionIncludeRootKeywords() {
List<Keyword> keywords = keywordRepository.newFindByParentIsNull();

return KeywordsResponse.createResponse(keywords);
return KeywordsResponse.of(keywords);
}

public void updateKeyword(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,122 +1,58 @@
package wooteco.prolog.roadmap.application;

import java.util.Comparator;
import java.util.HashSet;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import wooteco.prolog.common.exception.BadRequestException;
import wooteco.prolog.roadmap.application.dto.KeywordResponse;
import wooteco.prolog.roadmap.application.dto.KeywordsResponse;
import wooteco.prolog.roadmap.application.dto.RecommendedPostResponse;
import wooteco.prolog.roadmap.domain.Curriculum;
import wooteco.prolog.roadmap.domain.EssayAnswer;
import wooteco.prolog.roadmap.domain.Keyword;
import wooteco.prolog.roadmap.domain.Quiz;
import wooteco.prolog.roadmap.domain.repository.CurriculumRepository;
import wooteco.prolog.roadmap.domain.repository.EssayAnswerRepository;
import wooteco.prolog.roadmap.domain.repository.KeywordRepository;
import wooteco.prolog.roadmap.domain.repository.QuizRepository;
import wooteco.prolog.session.domain.Session;
import wooteco.prolog.session.domain.repository.SessionRepository;
import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndDoneQuizCount;
import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndTotalQuizCount;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static wooteco.prolog.common.exception.BadRequestCode.CURRICULUM_NOT_FOUND_EXCEPTION;
import static java.util.Objects.isNull;

@RequiredArgsConstructor
@Transactional
@Transactional(readOnly = true)
@Service
public class RoadMapService {

private final CurriculumRepository curriculumRepository;
private final SessionRepository sessionRepository;
private final KeywordRepository keywordRepository;
private final QuizRepository quizRepository;
private final EssayAnswerRepository essayAnswerRepository;

@Transactional(readOnly = true)
public KeywordsResponse findAllKeywordsWithProgress(final Long curriculumId, final Long memberId) {
final Curriculum curriculum = curriculumRepository.findById(curriculumId)
.orElseThrow(() -> new BadRequestException(CURRICULUM_NOT_FOUND_EXCEPTION));
final List<Keyword> keywords = keywordRepository.findAllByCurriculumId(curriculumId);
final Map<Long, Integer> totalQuizCounts = getTotalQuizCounts();
final Map<Long, Integer> doneQuizCounts = getDoneQuizCounts(memberId);

final List<Keyword> keywordsInCurriculum = getKeywordsInCurriculum(curriculum);
final KeywordsResponse keywordsResponse = KeywordsResponse.of(keywords);
keywordsResponse.setProgress(totalQuizCounts, doneQuizCounts);

final Map<Keyword, Set<Quiz>> quizzesInKeywords = quizRepository.findAll().stream()
.collect(groupingBy(Quiz::getKeyword, toSet()));

return createResponsesWithProgress(keywordsInCurriculum, quizzesInKeywords, getDoneQuizzes(memberId));
}

private Set<Quiz> getDoneQuizzes(final Long memberId) {
return essayAnswerRepository.findAllByMemberId(memberId).stream()
.map(EssayAnswer::getQuiz)
.collect(toSet());
return keywordsResponse;
}

private List<Keyword> getKeywordsInCurriculum(final Curriculum curriculum) {
final Set<Long> sessionIds = sessionRepository.findAllByCurriculumId(curriculum.getId())
.stream()
.map(Session::getId)
.collect(toSet());

return keywordRepository.findBySessionIdIn(sessionIds);
}
private Map<Long, Integer> getTotalQuizCounts() {
final Map<Long, Integer> totalQuizCounts = new HashMap<>();

private KeywordsResponse createResponsesWithProgress(final List<Keyword> keywords,
final Map<Keyword, Set<Quiz>> quizzesPerKeyword,
final Set<Quiz> doneQuizzes) {
final List<KeywordResponse> keywordResponses = keywords.stream()
.filter(Keyword::isRoot)
.map(keyword -> createResponseWithProgress(keyword, quizzesPerKeyword, doneQuizzes))
.sorted(Comparator.comparing(KeywordResponse::getKeywordId))
.collect(toList());
for (KeywordIdAndTotalQuizCount totalQuizCount : keywordRepository.findTotalQuizCount()) {
totalQuizCounts.put(totalQuizCount.getKeywordId(), totalQuizCount.getTotalQuizCount());
}

return new KeywordsResponse(keywordResponses);
return totalQuizCounts;
}

private KeywordResponse createResponseWithProgress(final Keyword keyword,
final Map<Keyword, Set<Quiz>> quizzesPerKeyword,
final Set<Quiz> doneQuizzes) {
final int totalQuizCount = quizzesPerKeyword.getOrDefault(keyword, new HashSet<>()).size();
final int doneQuizCount = getDoneQuizCount(
quizzesPerKeyword.getOrDefault(keyword, new HashSet<>()), doneQuizzes);
private Map<Long, Integer> getDoneQuizCounts(final Long memberId) {
final Map<Long, Integer> doneQuizCounts = new HashMap<>();
if (isNull(memberId)) {
return doneQuizCounts;
}

final List<RecommendedPostResponse> recommendedPostResponses = keyword.getRecommendedPosts().stream()
.map(RecommendedPostResponse::from)
.collect(toList());

return new KeywordResponse(
keyword.getId(),
keyword.getName(),
keyword.getDescription(),
keyword.getSeq(),
keyword.getImportance(),
totalQuizCount,
doneQuizCount,
keyword.getParentIdOrNull(),
recommendedPostResponses,
createChildrenWithProgress(keyword.getChildren(), quizzesPerKeyword, doneQuizzes)
);
}

private int getDoneQuizCount(final Set<Quiz> quizzes, final Set<Quiz> doneQuizzes) {
quizzes.retainAll(doneQuizzes);
return quizzes.size();
}
for (KeywordIdAndDoneQuizCount doneQuizCount : keywordRepository.findDoneQuizCountByMemberId(memberId)) {
doneQuizCounts.put(doneQuizCount.getKeywordId(), doneQuizCount.getDoneQuizCount());
}

private List<KeywordResponse> createChildrenWithProgress(final Set<Keyword> children,
final Map<Keyword, Set<Quiz>> quizzesPerKeyword,
final Set<Quiz> userAnswers) {
return children.stream()
.map(child -> createResponseWithProgress(child, quizzesPerKeyword, userAnswers))
.sorted(Comparator.comparing(KeywordResponse::getKeywordId))
.collect(Collectors.toList());
return doneQuizCounts;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package wooteco.prolog.roadmap.application.dto;

import java.util.ArrayList;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import wooteco.prolog.roadmap.domain.Keyword;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -56,12 +55,6 @@ public static KeywordResponse createResponse(final Keyword keyword) {
null);
}

private static List<RecommendedPostResponse> createRecommendedPostResponses(final Keyword keyword) {
return keyword.getRecommendedPosts().stream()
.map(RecommendedPostResponse::from)
.collect(Collectors.toList());
}

public static KeywordResponse createWithAllChildResponse(final Keyword keyword) {
return new KeywordResponse(
keyword.getId(),
Expand All @@ -76,11 +69,22 @@ public static KeywordResponse createWithAllChildResponse(final Keyword keyword)
createChildren(keyword.getChildren()));
}

private static List<RecommendedPostResponse> createRecommendedPostResponses(final Keyword keyword) {
return keyword.getRecommendedPosts().stream()
.map(RecommendedPostResponse::from)
.collect(Collectors.toList());
}

private static List<KeywordResponse> createChildren(final Set<Keyword> children) {
List<KeywordResponse> keywords = new ArrayList<>();
for (Keyword keyword : children) {
keywords.add(createWithAllChildResponse(keyword));
}
return keywords;
return children.stream()
.map(KeywordResponse::createWithAllChildResponse)
.collect(Collectors.toList());
}

public void setProgress(final Map<Long, Integer> totalQuizCounts, final Map<Long, Integer> doneQuizCounts) {
totalQuizCount = totalQuizCounts.getOrDefault(keywordId, 0);
doneQuizCount = doneQuizCounts.getOrDefault(keywordId, 0);

childrenKeywords.forEach(child -> child.setProgress(totalQuizCounts, doneQuizCounts));
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package wooteco.prolog.roadmap.application.dto;

import java.util.List;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import wooteco.prolog.roadmap.domain.Keyword;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class KeywordsResponse {
Expand All @@ -17,18 +19,16 @@ public KeywordsResponse(final List<KeywordResponse> data) {
this.data = data;
}

public static KeywordsResponse createResponse(final List<Keyword> keywords) {
List<KeywordResponse> keywordsResponse = keywords.stream()
.map(KeywordResponse::createResponse)
.collect(Collectors.toList());
return new KeywordsResponse(keywordsResponse);
}

public static KeywordsResponse createResponseWithChildren(final List<Keyword> keywords) {
List<KeywordResponse> keywordsResponse = keywords.stream()
public static KeywordsResponse of(final List<Keyword> keywords) {
final List<KeywordResponse> keywordsResponse = keywords.stream()
.filter(Keyword::isRoot)
.map(KeywordResponse::createWithAllChildResponse)
.collect(Collectors.toList());

return new KeywordsResponse(keywordsResponse);
}

public void setProgress(final Map<Long, Integer> totalQuizCounts, final Map<Long, Integer> doneQuizCounts) {
data.forEach(response -> response.setProgress(totalQuizCounts, doneQuizCounts));
}
}
17 changes: 13 additions & 4 deletions backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@
import org.hibernate.annotations.BatchSize;
import wooteco.prolog.common.exception.BadRequestException;

import javax.persistence.*;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
Expand Down Expand Up @@ -38,6 +47,7 @@ public class Keyword {
@Column(name = "session_id", nullable = false)
private Long sessionId;

@BatchSize(size = 1000)
@OneToMany(mappedBy = "keyword", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<RecommendedPost> recommendedPosts = new HashSet<>();

Expand All @@ -50,8 +60,7 @@ public class Keyword {
private Set<Keyword> children = new HashSet<>();

public Keyword(final Long id, final String name, final String description, final int seq,
final int importance,
final Long sessionId, final Keyword parent, final Set<Keyword> children) {
final int importance, final Long sessionId, final Keyword parent, final Set<Keyword> children) {
validateSeq(seq);
this.id = id;
this.name = name;
Expand All @@ -69,7 +78,7 @@ public static Keyword createKeyword(final String name,
final int importance,
final Long sessionId,
final Keyword parent) {
return new Keyword(null, name, description, seq, importance, sessionId, parent, null);
return new Keyword(null, name, description, seq, importance, sessionId, parent, new HashSet<>());
}

public void update(final String name, final String description, final int seq,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package wooteco.prolog.roadmap.domain.repository;

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import wooteco.prolog.roadmap.application.dto.CurriculumQuizResponse;
import wooteco.prolog.roadmap.domain.Quiz;

import java.util.List;

public interface QuizRepository extends JpaRepository<Quiz, Long> {

@Query("SELECT q FROM Quiz q"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import wooteco.prolog.session.domain.repository.SessionRepository;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.SoftAssertions.assertSoftly;

@SpringBootTest
class RecommendedPostServiceTest {
Expand Down Expand Up @@ -90,10 +89,6 @@ void delete() {
recommendedPostService.delete(recommendedPostId);

//then
assertSoftly(softAssertions -> {
assertThat(recommendedPostRepository.findAll()).hasSize(0);
assertThat(keywordRepository.findById(keyword.getId()).get().getRecommendedPosts())
.isEmpty();
});
assertThat(recommendedPostRepository.existsById(recommendedPostId)).isFalse();
}
}
Loading

0 comments on commit 4e1ad8e

Please sign in to comment.