From dc4fae9b2e3c52cff146d84131ebdc3d537075fd Mon Sep 17 00:00:00 2001 From: nuyh Date: Wed, 1 Nov 2023 11:03:18 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20ID=EB=A1=9C=20?= =?UTF-8?q?=EB=8B=B5=EB=B3=80=20=EA=B0=9C=EC=88=98=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1609 --- .../domain/repository/KeywordRepository.java | 39 ++++++------ .../dto/KeywordIdAndDoneQuizCount.java | 7 +++ .../dto/KeywordIdAndTotalQuizCount.java | 7 +++ .../repository/KeywordRepositoryTest.java | 61 +++---------------- 4 files changed, 40 insertions(+), 74 deletions(-) create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndDoneQuizCount.java create mode 100644 backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndTotalQuizCount.java diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java index db4207efe..8b5e412cd 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java @@ -1,35 +1,30 @@ package wooteco.prolog.roadmap.domain.repository; -import org.springframework.data.jpa.repository.EntityGraph; 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.domain.Keyword; +import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndDoneQuizCount; +import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndTotalQuizCount; import java.util.List; -import java.util.Optional; -import java.util.Set; - -import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.FETCH; public interface KeywordRepository extends JpaRepository { - @EntityGraph(attributePaths = "recommendedPosts", type = FETCH) - Optional findById(final Long id); + @Query("SELECT k.id AS keywordId, COUNT (q.id) as totalQuizCount " + + "FROM Keyword k " + + "JOIN Quiz q ON q.keyword.id = k.id " + + "GROUP BY k.id") + List findTotalQuizCount(); - @EntityGraph(attributePaths = "recommendedPosts", type = FETCH) - List findAll(); + @Query("SELECT k.id AS keywordId, COUNT (e.id) AS doneQuizCount " + + "FROM Keyword k " + + "JOIN Quiz q ON k.id = q.keyword.id " + + "JOIN EssayAnswer e ON e.quiz.id = q.id " + + "WHERE e.member.id = :memberId " + + "GROUP BY k.id ") + List findDoneQuizCountByMemberId(@Param("memberId") Long memberId); - @Query("SELECT k FROM Keyword k " - + "LEFT JOIN FETCH k.children c " - + "LEFT JOIN FETCH k.recommendedPosts " - + "LEFT JOIN FETCH k.parent p " - + "LEFT JOIN FETCH p.recommendedPosts " - + "LEFT JOIN FETCH c.recommendedPosts " - + "LEFT JOIN FETCH c.children lc " - + "LEFT JOIN FETCH lc.recommendedPosts " - + "LEFT JOIN FETCH lc.children " - + "WHERE k.id = :keywordId ORDER BY k.seq") Keyword findFetchByIdOrderBySeq(@Param("keywordId") Long keywordId); @Query("SELECT k FROM Keyword k " @@ -42,5 +37,9 @@ public interface KeywordRepository extends JpaRepository { + "WHERE k.parent IS NULL") List newFindByParentIsNull(); - List findBySessionIdIn(final Set sessionIds); + @Query("SELECT k FROM Keyword k " + + "JOIN Session s ON s.id = k.sessionId " + + "LEFT JOIN FETCH RecommendedPost r ON r.keyword.id = k.id " + + "WHERE s.curriculumId = :curriculumId ") + List findAllByCurriculumId(Long curriculumId); } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndDoneQuizCount.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndDoneQuizCount.java new file mode 100644 index 000000000..d25aef114 --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndDoneQuizCount.java @@ -0,0 +1,7 @@ +package wooteco.prolog.roadmap.domain.repository.dto; + +public interface KeywordIdAndDoneQuizCount { + long getKeywordId(); + + int getDoneQuizCount(); +} diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndTotalQuizCount.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndTotalQuizCount.java new file mode 100644 index 000000000..47836c550 --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndTotalQuizCount.java @@ -0,0 +1,7 @@ +package wooteco.prolog.roadmap.domain.repository.dto; + +public interface KeywordIdAndTotalQuizCount { + long getKeywordId(); + + int getTotalQuizCount(); +} diff --git a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java index 47c67b86b..104a08410 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java @@ -1,12 +1,5 @@ package wooteco.prolog.roadmap.repository; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import javax.persistence.EntityManager; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -18,6 +11,13 @@ import wooteco.prolog.session.domain.repository.SessionRepository; import wooteco.support.utils.RepositoryTest; +import javax.persistence.EntityManager; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + @RepositoryTest class KeywordRepositoryTest { @@ -114,53 +114,6 @@ class KeywordRepositoryTest { assertThat(extractParentKeyword.size()).isEqualTo(1); } - @Test - @DisplayName("세션 ID 리스트로 키워드 리스트를 조회한다") - void findBySessionIdIn() { - //given - final Curriculum curriculum = curriculumRepository.save(new Curriculum("커리큘럼1")); - - final Session session1 = sessionRepository.save(new Session(curriculum.getId(), "세션1")); - final Session session2 = sessionRepository.save(new Session(curriculum.getId(), "세션2")); - final Session session3 = sessionRepository.save(new Session(curriculum.getId(), "세션3")); - final Session session4 = sessionRepository.save(new Session(curriculum.getId(), "세션4")); - final Session session5 = sessionRepository.save(new Session(curriculum.getId(), "세션5")); - - final Keyword keyword1 = keywordRepository.save( - Keyword.createKeyword("자바1", "자바 설명1", 1, 5, session1.getId(), null)); - final Keyword keyword2 = keywordRepository.save( - Keyword.createKeyword("자바2", "자바 설명2", 2, 5, session1.getId(), keyword1)); - final Keyword keyword3 = keywordRepository.save( - Keyword.createKeyword("자바3", "자바 설명3", 3, 5, session1.getId(), null)); - final Keyword keyword4 = keywordRepository.save( - Keyword.createKeyword("자바4", "자바 설명4", 4, 5, session1.getId(), keyword3)); - keywordRepository.save( - Keyword.createKeyword("자바5", "자바 설명5", 5, 5, session2.getId(), null)); - keywordRepository.save( - Keyword.createKeyword("자바6", "자바 설명6", 6, 5, session2.getId(), keyword1)); - keywordRepository.save( - Keyword.createKeyword("자바7", "자바 설명7", 7, 5, session2.getId(), null)); - keywordRepository.save( - Keyword.createKeyword("자바8", "자바 설명8", 8, 5, session3.getId(), keyword2)); - final Keyword keyword9 = keywordRepository.save( - Keyword.createKeyword("자바9", "자바 설명9", 9, 5, session4.getId(), keyword2)); - final Keyword keyword10 = keywordRepository.save( - Keyword.createKeyword("자바10", "자바 설명10", 10, 5, session5.getId(), null)); - - final HashSet sessionIds = new HashSet<>( - Arrays.asList(session1.getId(), session4.getId(), session5.getId()) - ); - - //when - final List keywords = keywordRepository.findBySessionIdIn(sessionIds); - - //then - assertThat(keywords) - .usingRecursiveComparison() - .ignoringFields("id", "parent.id") - .isEqualTo(Arrays.asList(keyword1, keyword2, keyword3, keyword4, keyword9, keyword10)); - } - @Test @DisplayName("newFindParentIsNull() : 모든 상위 키워드들을 조회할 수 있다.") void newFindParentIsNull() { From 11e87dcb4bef662f23a2106e6811a729b2f4d0f6 Mon Sep 17 00:00:00 2001 From: nuyh Date: Wed, 1 Nov 2023 11:27:34 +0900 Subject: [PATCH 2/9] =?UTF-8?q?test:=20=EC=9C=A0=EC=A0=80=20ID=EB=A1=9C=20?= =?UTF-8?q?=EB=8B=B5=EB=B3=80=20=EA=B0=9C=EC=88=98=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1609 --- .../repository/KeywordRepositoryTest.java | 99 +++++++++++++++++-- 1 file changed, 90 insertions(+), 9 deletions(-) diff --git a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java index 104a08410..bbc791549 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java @@ -3,10 +3,19 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import wooteco.prolog.member.domain.Member; +import wooteco.prolog.member.domain.Role; +import wooteco.prolog.member.domain.repository.MemberRepository; 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.roadmap.domain.repository.dto.KeywordIdAndDoneQuizCount; +import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndTotalQuizCount; import wooteco.prolog.session.domain.Session; import wooteco.prolog.session.domain.repository.SessionRepository; import wooteco.support.utils.RepositoryTest; @@ -16,6 +25,7 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.junit.jupiter.api.Assertions.assertAll; @RepositoryTest @@ -26,16 +36,20 @@ class KeywordRepositoryTest { @Autowired private SessionRepository sessionRepository; @Autowired + private EssayAnswerRepository essayAnswerRepository; + @Autowired + private MemberRepository memberRepository; + @Autowired private EntityManager em; @Autowired private CurriculumRepository curriculumRepository; + @Autowired + private QuizRepository quizRepository; @Test void 부모_키워드와_1뎁스까지의_자식_키워드를_함께_조회할_수_있다() { // given - Session session = new Session("테스트 세션"); - sessionRepository.save(session); - Long sessionId = session.getId(); + Long sessionId = createSession(); Keyword keywordParent = createKeywordParent( Keyword.createKeyword("자바", "자바에 대한 설명", 1, 1, sessionId, null)); @@ -58,9 +72,7 @@ class KeywordRepositoryTest { @Test void 부모_키워드와_2뎁스까지의_자식_키워드를_함께_조회할_수_있다() { // given - Session session = new Session("테스트 세션"); - sessionRepository.save(session); - Long sessionId = session.getId(); + Long sessionId = createSession(); Keyword parent = createKeywordParent( Keyword.createKeyword("자바", "자바에 대한 설명", 1, 1, sessionId, null)); @@ -93,9 +105,7 @@ class KeywordRepositoryTest { @Test void 최상위_키워드만_조회한다() { // given - Session session = new Session("테스트 세션"); - sessionRepository.save(session); - Long sessionId = session.getId(); + Long sessionId = createSession(); Keyword keywordParent = createKeywordParent( Keyword.createKeyword("자바", "자바에 대한 설명", 1, 1, sessionId, null)); @@ -157,6 +167,77 @@ void newFindParentIsNull() { .isEqualTo(Arrays.asList(keyword1, keyword3, keyword5, keyword7, keyword10)); } + @Test + @DisplayName("각 키워드별 퀴즈 개수를 조회할 수 있다") + void findTotalQuizCount() { + //given + final Long sessionId = createSession(); + + final Keyword parent = createKeywordParent( + Keyword.createKeyword("자바", "자바에 대한 설명", 1, 1, sessionId, null)); + final Keyword child1 = createKeywordChildren( + Keyword.createKeyword("List", "List에 대한 설명", 1, 1, sessionId, parent)); + createKeywordChildren( + Keyword.createKeyword("Set", "Set에 대한 설명", 2, 1, sessionId, parent)); + + quizRepository.save(new Quiz(parent, "자바란 무엇일까요?")); + quizRepository.save(new Quiz(parent, "자바를 왜 쓰죠?")); + quizRepository.save(new Quiz(child1, "리스트보단 배열이 낫지 않나요?")); + + //when + final List totalQuizCounts = keywordRepository.findTotalQuizCount(); + + //then + assertSoftly(softAssertions -> { + softAssertions.assertThat(totalQuizCounts) + .map(KeywordIdAndTotalQuizCount::getKeywordId) + .containsExactlyInAnyOrder(parent.getId(), child1.getId()); + + softAssertions.assertThat(totalQuizCounts) + .map(KeywordIdAndTotalQuizCount::getTotalQuizCount) + .containsExactlyInAnyOrder(1, 2); + }); + } + + @Test + @DisplayName("회원의 id로 각 키워드별 완료한 답변 개수를 조회할 수 있다") + void findDoneQuizCountByMemberId() { + //given + final Long sessionId = createSession(); + + final Keyword parent = createKeywordParent( + Keyword.createKeyword("자바", "자바에 대한 설명", 1, 1, sessionId, null)); + final Keyword child1 = createKeywordChildren( + Keyword.createKeyword("List", "List에 대한 설명", 1, 1, sessionId, parent)); + + final Quiz quiz1 = quizRepository.save(new Quiz(parent, "자바란 무엇일까요?")); + final Quiz quiz2 = quizRepository.save(new Quiz(parent, "코틀린은 뭘까요?")); + quizRepository.save(new Quiz(child1, "리스트보단 배열이 낫지 않나요?")); + + final Member member = memberRepository.save(new Member("username1", "nickname1", Role.CREW, 1L, "imageUrl1")); + essayAnswerRepository.save(new EssayAnswer(quiz1, "쓰라고 해서요ㅠ", member)); + essayAnswerRepository.save(new EssayAnswer(quiz2, "쓰라고 해서요ㅠ", member)); + + //when + final List doneQuizCounts = keywordRepository.findDoneQuizCountByMemberId(member.getId()); + + //then + assertSoftly(softAssertions -> { + softAssertions.assertThat(doneQuizCounts) + .map(KeywordIdAndDoneQuizCount::getKeywordId) + .containsExactly(parent.getId()); + softAssertions.assertThat(doneQuizCounts) + .map(KeywordIdAndDoneQuizCount::getDoneQuizCount) + .containsExactly(2); + }); + } + + private Long createSession() { + Session session = new Session("테스트 세션"); + sessionRepository.save(session); + return session.getId(); + } + private Keyword createKeywordParent(final Keyword keyword) { return keywordRepository.save(keyword); } From bc41ccb853dd19b511a1a37f7aa3d246ee29a759 Mon Sep 17 00:00:00 2001 From: nuyh Date: Wed, 1 Nov 2023 11:38:08 +0900 Subject: [PATCH 3/9] =?UTF-8?q?test:=20=EC=BB=A4=EB=A6=AC=ED=81=98?= =?UTF-8?q?=EB=9F=BC=EC=9D=98=20=EB=AA=A8=EB=93=A0=20=ED=82=A4=EC=9B=8C?= =?UTF-8?q?=EB=93=9C=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1609 --- .../repository/KeywordRepositoryTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java index bbc791549..133c685a6 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java @@ -232,12 +232,42 @@ void findDoneQuizCountByMemberId() { }); } + @Test + @DisplayName("커리큘럼 ID에 해당하는 모든 키워드들을 조회할 수 있다") + void findAllByCurriculumId() { + //given + final Long session1 = createSessionWithCurriculumId(1L); + final Long session2 = createSessionWithCurriculumId(1L); + + final Keyword parent1 = createKeywordParent( + Keyword.createKeyword("자바", "자바에 대한 설명", 1, 1, session1, null)); + createKeywordChildren( + Keyword.createKeyword("List", "List에 대한 설명", 1, 1, session1, parent1)); + createKeywordChildren( + Keyword.createKeyword("List", "List에 대한 설명", 1, 1, session1, parent1)); + + createKeywordParent( + Keyword.createKeyword("자바", "자바에 대한 설명", 1, 1, session2, null)); + + //when + final List keywords = keywordRepository.findAllByCurriculumId(1L); + + //then + assertThat(keywords).hasSize(4); + } + private Long createSession() { Session session = new Session("테스트 세션"); sessionRepository.save(session); return session.getId(); } + private Long createSessionWithCurriculumId(final Long curriculumId) { + Session session = new Session(curriculumId, "테스트 세션"); + sessionRepository.save(session); + return session.getId(); + } + private Keyword createKeywordParent(final Keyword keyword) { return keywordRepository.save(keyword); } From 5faa0fb7cbf678c5b5a2d365097e25c3fea7883e Mon Sep 17 00:00:00 2001 From: nuyh Date: Wed, 1 Nov 2023 16:05:36 +0900 Subject: [PATCH 4/9] =?UTF-8?q?refactor:=20=EC=BB=A4=EB=A6=AC=ED=81=98?= =?UTF-8?q?=EB=9F=BC=20ID=EC=9D=98=20=EB=AA=A8=EB=93=A0=20=ED=82=A4?= =?UTF-8?q?=EC=9B=8C=EB=93=9C=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20JPQL=20=EC=82=AC=EC=9A=A9=ED=95=B4?= =?UTF-8?q?=EC=84=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1609 --- .../roadmap/application/KeywordService.java | 15 +- .../roadmap/application/RoadMapService.java | 116 +++------- .../application/dto/KeywordResponse.java | 30 +-- .../application/dto/KeywordsResponse.java | 22 +- .../prolog/roadmap/domain/Keyword.java | 17 +- .../domain/repository/QuizRepository.java | 3 +- .../RecommendedPostServiceTest.java | 7 +- .../application/RoadMapServiceTest.java | 204 ++++++++++++++---- 8 files changed, 235 insertions(+), 179 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java index b7dd4b696..cb89cbb07 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java @@ -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; @@ -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 { @@ -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; } @@ -83,14 +84,14 @@ public KeywordsResponse findSessionIncludeRootKeywords(final Long sessionId) { List keywords = keywordRepository.findBySessionIdAndParentIsNull(sessionId); - return KeywordsResponse.createResponse(keywords); + return KeywordsResponse.of(keywords); } @Transactional(readOnly = true) public KeywordsResponse newFindSessionIncludeRootKeywords() { List keywords = keywordRepository.newFindByParentIsNull(); - return KeywordsResponse.createResponse(keywords); + return KeywordsResponse.of(keywords); } public void updateKeyword( diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java index 96215d86e..236259aef 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java @@ -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 keywords = keywordRepository.findAllByCurriculumId(curriculumId); + final Map totalQuizCounts = getTotalQuizCounts(); + final Map doneQuizCounts = getDoneQuizCounts(memberId); - final List keywordsInCurriculum = getKeywordsInCurriculum(curriculum); + final KeywordsResponse keywordsResponse = KeywordsResponse.of(keywords); + keywordsResponse.setProgress(totalQuizCounts, doneQuizCounts); - final Map> quizzesInKeywords = quizRepository.findAll().stream() - .collect(groupingBy(Quiz::getKeyword, toSet())); - - return createResponsesWithProgress(keywordsInCurriculum, quizzesInKeywords, getDoneQuizzes(memberId)); - } - - private Set getDoneQuizzes(final Long memberId) { - return essayAnswerRepository.findAllByMemberId(memberId).stream() - .map(EssayAnswer::getQuiz) - .collect(toSet()); + return keywordsResponse; } - private List getKeywordsInCurriculum(final Curriculum curriculum) { - final Set sessionIds = sessionRepository.findAllByCurriculumId(curriculum.getId()) - .stream() - .map(Session::getId) - .collect(toSet()); - - return keywordRepository.findBySessionIdIn(sessionIds); - } + private Map getTotalQuizCounts() { + final Map totalQuizCounts = new HashMap<>(); - private KeywordsResponse createResponsesWithProgress(final List keywords, - final Map> quizzesPerKeyword, - final Set doneQuizzes) { - final List 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> quizzesPerKeyword, - final Set doneQuizzes) { - final int totalQuizCount = quizzesPerKeyword.getOrDefault(keyword, new HashSet<>()).size(); - final int doneQuizCount = getDoneQuizCount( - quizzesPerKeyword.getOrDefault(keyword, new HashSet<>()), doneQuizzes); + private Map getDoneQuizCounts(final Long memberId) { + final Map doneQuizCounts = new HashMap<>(); + if (isNull(memberId)) { + return doneQuizCounts; + } - final List 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 quizzes, final Set doneQuizzes) { - quizzes.retainAll(doneQuizzes); - return quizzes.size(); - } + for (KeywordIdAndDoneQuizCount doneQuizCount : keywordRepository.findDoneQuizCountByMemberId(memberId)) { + doneQuizCounts.put(doneQuizCount.getKeywordId(), doneQuizCount.getDoneQuizCount()); + } - private List createChildrenWithProgress(final Set children, - final Map> quizzesPerKeyword, - final Set userAnswers) { - return children.stream() - .map(child -> createResponseWithProgress(child, quizzesPerKeyword, userAnswers)) - .sorted(Comparator.comparing(KeywordResponse::getKeywordId)) - .collect(Collectors.toList()); + return doneQuizCounts; } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java index aa94ea5d2..adb464f8e 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java @@ -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; @@ -56,12 +55,6 @@ public static KeywordResponse createResponse(final Keyword keyword) { null); } - private static List 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(), @@ -76,11 +69,22 @@ public static KeywordResponse createWithAllChildResponse(final Keyword keyword) createChildren(keyword.getChildren())); } + private static List createRecommendedPostResponses(final Keyword keyword) { + return keyword.getRecommendedPosts().stream() + .map(RecommendedPostResponse::from) + .collect(Collectors.toList()); + } + private static List createChildren(final Set children) { - List 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 totalQuizCounts, final Map doneQuizCounts) { + totalQuizCount = totalQuizCounts.getOrDefault(keywordId, 0); + doneQuizCount = doneQuizCounts.getOrDefault(keywordId, 0); + + childrenKeywords.forEach(child -> child.setProgress(totalQuizCounts, doneQuizCounts)); } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java index d904b67a3..360e1e574 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java @@ -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 { @@ -17,18 +19,16 @@ public KeywordsResponse(final List data) { this.data = data; } - public static KeywordsResponse createResponse(final List keywords) { - List keywordsResponse = keywords.stream() - .map(KeywordResponse::createResponse) - .collect(Collectors.toList()); - return new KeywordsResponse(keywordsResponse); - } - - public static KeywordsResponse createResponseWithChildren(final List keywords) { - List keywordsResponse = keywords.stream() + public static KeywordsResponse of(final List keywords) { + final List keywordsResponse = keywords.stream() .filter(Keyword::isRoot) .map(KeywordResponse::createWithAllChildResponse) .collect(Collectors.toList()); + return new KeywordsResponse(keywordsResponse); } + + public void setProgress(final Map totalQuizCounts, final Map doneQuizCounts) { + data.forEach(response -> response.setProgress(totalQuizCounts, doneQuizCounts)); + } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java index 9fc136b14..6259312d2 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/Keyword.java @@ -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; @@ -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 recommendedPosts = new HashSet<>(); @@ -50,8 +60,7 @@ public class Keyword { private Set 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 children) { + final int importance, final Long sessionId, final Keyword parent, final Set children) { validateSeq(seq); this.id = id; this.name = name; @@ -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, diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/QuizRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/QuizRepository.java index 6ecdf58a1..944abf921 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/QuizRepository.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/QuizRepository.java @@ -1,10 +1,11 @@ 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 wooteco.prolog.roadmap.domain.Quiz; +import java.util.List; + public interface QuizRepository extends JpaRepository { @Query("SELECT q FROM Quiz q" diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java index 5f3932c58..1a5417144 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RecommendedPostServiceTest.java @@ -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 { @@ -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(); } } diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java index 45d584adb..287ac7226 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java @@ -1,92 +1,202 @@ package wooteco.prolog.roadmap.application; +import org.assertj.core.api.AbstractListAssert; +import org.assertj.core.api.ObjectAssert; +import org.assertj.core.api.ProxyableListAssert; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import wooteco.prolog.common.DataInitializer; import wooteco.prolog.member.domain.Member; import wooteco.prolog.member.domain.Role; +import wooteco.prolog.member.domain.repository.MemberRepository; import wooteco.prolog.roadmap.application.dto.KeywordResponse; import wooteco.prolog.roadmap.application.dto.KeywordsResponse; -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 java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Optional; +import java.util.function.Consumer; +import static java.util.Collections.emptySet; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.SoftAssertions.assertSoftly; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.when; -@ExtendWith(MockitoExtension.class) +@SpringBootTest class RoadMapServiceTest { - @Mock - private CurriculumRepository curriculumRepository; - @Mock + @Autowired private SessionRepository sessionRepository; - @Mock + @Autowired private KeywordRepository keywordRepository; - @Mock + @Autowired private QuizRepository quizRepository; - @Mock + @Autowired private EssayAnswerRepository essayAnswerRepository; - @InjectMocks + @Autowired + private MemberRepository memberRepository; + + @Autowired private RoadMapService roadMapService; + @Autowired + private DataInitializer dataInitializer; + + @AfterEach + void truncate() { + dataInitializer.execute(); + } + @Test - @DisplayName("curriculumId가 주어지면 해당 커리큘럼의 키워드들을 학습 진도와 함께 전부 조회할 수 있다.") + @DisplayName("커리큘럼 ID에 해당하는 모든 키워드를 진도율과 함께 조회할 수 있다 - 비회원") void findAllKeywordsWithProgress() { //given - final Curriculum curriculum = new Curriculum(1L, "커리큘럼1"); - final Session session = new Session(1L, curriculum.getId(), "세션1"); - final List sessions = Arrays.asList(session); - final Keyword keyword = new Keyword(1L, "자바1", "자바 설명1", 1, 5, session.getId(), - null, Collections.emptySet()); - final Quiz quiz = new Quiz(1L, keyword, "자바8을 왜 쓰나요?"); - final Member member = new Member(1L, "연어", "참치", Role.CREW, 1L, "image"); - final EssayAnswer essayAnswer = new EssayAnswer(quiz, "쓰라고 해서요ㅠㅠ", member); + final Long curriculumId = 1L; + final Long session1 = createSession(curriculumId); + final Long session2 = createSession(curriculumId); + + final Keyword root1 = createParentKeyword("스트림", "스트림 API 설명", session1); + final Keyword root2 = createParentKeyword("컬렉션", "컬렉션 API 설명", session2); + + final Keyword oneDepthChild = createChildKeyword(root1, "map()", "스트림 api의 map"); + final Keyword twoDepthChild = createChildKeyword(oneDepthChild, "map()의 파라미터", "파라미터 설명"); + + createQuiz(oneDepthChild, "map을 왜 쓰나요?"); + createQuiz(root2, "컬렉션을 왜 쓰나요?"); + + //when + final KeywordsResponse response = roadMapService.findAllKeywordsWithProgress(curriculumId, null); + + //then + assertSoftly(softAssertions -> { + final ProxyableListAssert roots = softAssertions.assertThat(response.getData()); + final AbstractListAssert, KeywordResponse, ObjectAssert> oneDepthChildren + = roots.flatMap(KeywordResponse::getChildrenKeywords); + final AbstractListAssert, KeywordResponse, ObjectAssert> twoDepthChildren + = oneDepthChildren.flatMap(KeywordResponse::getChildrenKeywords); + + roots.hasSize(2) + .satisfies(containsKeywordIds(root1.getId(), root2.getId())) + .satisfies(containsTotalQuizCounts(0, 1)) + .satisfies(containsDoneQuizCounts(0, 0)); + + oneDepthChildren.hasSize(1) + .satisfies(containsKeywordIds(oneDepthChild.getId())) + .satisfies(containsTotalQuizCounts(1)) + .satisfies(containsDoneQuizCounts(0)); + + twoDepthChildren.hasSize(1) + .satisfies(containsKeywordIds(twoDepthChild.getId())) + .satisfies(containsTotalQuizCounts(0)) + .satisfies(containsDoneQuizCounts(0)); + }); + } + + @Test + @DisplayName("커리큘럼 ID에 해당하는 모든 키워드를 진도율과 함께 조회할 수 있다 - 회원") + void findAllKeywordsWithProgress_login() { + //given + final Long curriculumId = 1L; + final Long session1 = createSession(curriculumId); + final Long session2 = createSession(curriculumId); - when(curriculumRepository.findById(anyLong())) - .thenReturn(Optional.of(curriculum)); + final Keyword root1 = createParentKeyword("스트림", "스트림 API 설명", session1); + final Keyword root2 = createParentKeyword("컬렉션", "컬렉션 API 설명", session2); - when(sessionRepository.findAllByCurriculumId(anyLong())) - .thenReturn(sessions); + final Keyword oneDepthChild = createChildKeyword(root1, "map()", "스트림 api의 map"); + final Keyword twoDepthChild = createChildKeyword(oneDepthChild, "map()의 파라미터", "파라미터 설명"); - when(keywordRepository.findBySessionIdIn(any())) - .thenReturn(Arrays.asList(keyword)); + final Quiz quiz1 = createQuiz(root2, "컬렉션을 왜 쓰나요?"); + final Quiz quiz2 = createQuiz(twoDepthChild, "파라미터의 종류는?"); + createQuiz(oneDepthChild, "map을 왜 쓰나요?"); - when(essayAnswerRepository.findAllByMemberId(1L)) - .thenReturn(new HashSet<>(Arrays.asList(essayAnswer))); + final Member member = createMember(); - when(quizRepository.findAll()) - .thenReturn(Arrays.asList(quiz)); + createEssayAnswer(quiz1, member, "배열을 쓸 수는 없으니까요"); + createEssayAnswer(quiz2, member, "Function 입니다"); //when - final KeywordsResponse actual = - roadMapService.findAllKeywordsWithProgress(curriculum.getId(), 1L); + final KeywordsResponse response = roadMapService.findAllKeywordsWithProgress(curriculumId, member.getId()); //then - final List responses = actual.getData(); - assertSoftly(soft -> { - assertThat(responses).hasSize(1); - assertThat(responses.get(0).getDoneQuizCount()).isOne(); - assertThat(responses.get(0).getTotalQuizCount()).isOne(); + assertSoftly(softAssertions -> { + final ProxyableListAssert roots = softAssertions.assertThat(response.getData()); + final AbstractListAssert, KeywordResponse, ObjectAssert> oneDepthChildren + = roots.flatMap(KeywordResponse::getChildrenKeywords); + final AbstractListAssert, KeywordResponse, ObjectAssert> twoDepthChildren + = oneDepthChildren.flatMap(KeywordResponse::getChildrenKeywords); + + roots.hasSize(2) + .satisfies(containsKeywordIds(root1.getId(), root2.getId())) + .satisfies(containsTotalQuizCounts(0, 1)) + .satisfies(containsDoneQuizCounts(0, 1)); + + oneDepthChildren.hasSize(1) + .satisfies(containsKeywordIds(oneDepthChild.getId())) + .satisfies(containsTotalQuizCounts(1)) + .satisfies(containsDoneQuizCounts(0)); + + twoDepthChildren.hasSize(1) + .satisfies(containsKeywordIds(twoDepthChild.getId())) + .satisfies(containsTotalQuizCounts(1)) + .satisfies(containsDoneQuizCounts(1)); }); } + + private Consumer> containsDoneQuizCounts(final Integer... expected) { + return keywords -> assertThat(keywords) + .map(KeywordResponse::getDoneQuizCount) + .containsExactlyInAnyOrder(expected); + } + + private Consumer> containsTotalQuizCounts(final Integer... expected) { + return keywords -> assertThat(keywords) + .map(KeywordResponse::getTotalQuizCount) + .containsExactlyInAnyOrder(expected); + } + + private Consumer> containsKeywordIds(final Long... expected) { + return keywords -> assertThat(keywords) + .map(KeywordResponse::getKeywordId) + .containsExactlyInAnyOrder(expected); + } + + private Long createSession(final Long curriculumId) { + Session session = new Session(curriculumId, "테스트 세션"); + sessionRepository.save(session); + return session.getId(); + } + + private Keyword createParentKeyword(final String name, final String description, final Long sessionId) { + final Keyword keyword = new Keyword(null, name, description, 1, 1, sessionId, null, emptySet()); + return keywordRepository.save(keyword); + } + + private Keyword createChildKeyword(final Keyword parent, final String name, final String description) { + final Keyword keyword = new Keyword(null, name, description, 1, 1, parent.getSessionId(), parent, emptySet()); + return keywordRepository.save(keyword); + } + + private Quiz createQuiz(final Keyword keyword, final String question) { + final Quiz quiz = new Quiz(keyword, question); + return quizRepository.save(quiz); + } + + private Member createMember() { + final Member member = new Member("id", "연어", Role.CREW, 1L, "image"); + return memberRepository.save(member); + } + + private EssayAnswer createEssayAnswer(final Quiz quiz, final Member member, final String answer) { + final EssayAnswer essayAnswer = new EssayAnswer(quiz, answer, member); + return essayAnswerRepository.save(essayAnswer); + } } From 746ee8c6fc2ec9f58b4fc52407212186265764b7 Mon Sep 17 00:00:00 2001 From: nuyh Date: Wed, 1 Nov 2023 16:20:49 +0900 Subject: [PATCH 5/9] =?UTF-8?q?refactor:=20Dto=20->=20Map=20=EB=B3=80?= =?UTF-8?q?=ED=99=98=20=EB=A1=9C=EC=A7=81=20Stream=20API=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1609 --- .../roadmap/application/RoadMapService.java | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java index 236259aef..450d6be28 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java @@ -9,11 +9,10 @@ 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 static java.util.Objects.isNull; +import static java.util.stream.Collectors.toMap; @RequiredArgsConstructor @Transactional(readOnly = true) @@ -34,25 +33,18 @@ public KeywordsResponse findAllKeywordsWithProgress(final Long curriculumId, fin } private Map getTotalQuizCounts() { - final Map totalQuizCounts = new HashMap<>(); - - for (KeywordIdAndTotalQuizCount totalQuizCount : keywordRepository.findTotalQuizCount()) { - totalQuizCounts.put(totalQuizCount.getKeywordId(), totalQuizCount.getTotalQuizCount()); - } - - return totalQuizCounts; + return keywordRepository.findTotalQuizCount().stream() + .collect( + toMap( + KeywordIdAndTotalQuizCount::getKeywordId, + KeywordIdAndTotalQuizCount::getTotalQuizCount)); } private Map getDoneQuizCounts(final Long memberId) { - final Map doneQuizCounts = new HashMap<>(); - if (isNull(memberId)) { - return doneQuizCounts; - } - - for (KeywordIdAndDoneQuizCount doneQuizCount : keywordRepository.findDoneQuizCountByMemberId(memberId)) { - doneQuizCounts.put(doneQuizCount.getKeywordId(), doneQuizCount.getDoneQuizCount()); - } - - return doneQuizCounts; + return keywordRepository.findDoneQuizCountByMemberId(memberId).stream() + .collect( + toMap( + KeywordIdAndDoneQuizCount::getKeywordId, + KeywordIdAndDoneQuizCount::getDoneQuizCount)); } } From 4eac034617be5e654b3be22d8e3589d252e42218 Mon Sep 17 00:00:00 2001 From: nuyh Date: Wed, 1 Nov 2023 16:34:06 +0900 Subject: [PATCH 6/9] =?UTF-8?q?refactor:=20=ED=82=A4=EC=9B=8C=EB=93=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20=EC=B6=94=EC=B2=9C=20=ED=8F=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20fetch=20join=20->=20batch=20size=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1609 --- .../prolog/roadmap/domain/repository/KeywordRepository.java | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java index 8b5e412cd..99806ca93 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java @@ -39,7 +39,6 @@ public interface KeywordRepository extends JpaRepository { @Query("SELECT k FROM Keyword k " + "JOIN Session s ON s.id = k.sessionId " + - "LEFT JOIN FETCH RecommendedPost r ON r.keyword.id = k.id " + "WHERE s.curriculumId = :curriculumId ") List findAllByCurriculumId(Long curriculumId); } From c730607034c4c3fef0a9c069205910be4ac02c2e Mon Sep 17 00:00:00 2001 From: nuyh Date: Wed, 1 Nov 2023 17:40:19 +0900 Subject: [PATCH 7/9] =?UTF-8?q?style:=20doneQuizCount=20->=20answeredQuizC?= =?UTF-8?q?ount=EB=A1=9C=20=EC=9A=A9=EC=96=B4=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1609 --- .../prolog/roadmap/application/RoadMapService.java | 14 +++++++------- .../roadmap/application/dto/KeywordResponse.java | 6 +++--- .../roadmap/application/dto/KeywordsResponse.java | 4 ++-- .../domain/repository/KeywordRepository.java | 6 +++--- ...unt.java => KeywordIdAndAnsweredQuizCount.java} | 4 ++-- .../roadmap/application/RoadMapServiceTest.java | 14 +++++++------- .../roadmap/repository/KeywordRepositoryTest.java | 10 +++++----- 7 files changed, 29 insertions(+), 29 deletions(-) rename backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/{KeywordIdAndDoneQuizCount.java => KeywordIdAndAnsweredQuizCount.java} (50%) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java index 450d6be28..fbfae1d22 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java @@ -6,7 +6,7 @@ import wooteco.prolog.roadmap.application.dto.KeywordsResponse; import wooteco.prolog.roadmap.domain.Keyword; import wooteco.prolog.roadmap.domain.repository.KeywordRepository; -import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndDoneQuizCount; +import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndAnsweredQuizCount; import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndTotalQuizCount; import java.util.List; @@ -24,10 +24,10 @@ public class RoadMapService { public KeywordsResponse findAllKeywordsWithProgress(final Long curriculumId, final Long memberId) { final List keywords = keywordRepository.findAllByCurriculumId(curriculumId); final Map totalQuizCounts = getTotalQuizCounts(); - final Map doneQuizCounts = getDoneQuizCounts(memberId); + final Map answeredQuizCounts = getAnsweredQuizCounts(memberId); final KeywordsResponse keywordsResponse = KeywordsResponse.of(keywords); - keywordsResponse.setProgress(totalQuizCounts, doneQuizCounts); + keywordsResponse.setProgress(totalQuizCounts, answeredQuizCounts); return keywordsResponse; } @@ -40,11 +40,11 @@ private Map getTotalQuizCounts() { KeywordIdAndTotalQuizCount::getTotalQuizCount)); } - private Map getDoneQuizCounts(final Long memberId) { - return keywordRepository.findDoneQuizCountByMemberId(memberId).stream() + private Map getAnsweredQuizCounts(final Long memberId) { + return keywordRepository.findAnsweredQuizCountByMemberId(memberId).stream() .collect( toMap( - KeywordIdAndDoneQuizCount::getKeywordId, - KeywordIdAndDoneQuizCount::getDoneQuizCount)); + KeywordIdAndAnsweredQuizCount::getKeywordId, + KeywordIdAndAnsweredQuizCount::getAnsweredQuizCount)); } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java index adb464f8e..0255414ec 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java @@ -81,10 +81,10 @@ private static List createChildren(final Set children) .collect(Collectors.toList()); } - public void setProgress(final Map totalQuizCounts, final Map doneQuizCounts) { + public void setProgress(final Map totalQuizCounts, final Map answeredQuizCounts) { totalQuizCount = totalQuizCounts.getOrDefault(keywordId, 0); - doneQuizCount = doneQuizCounts.getOrDefault(keywordId, 0); + doneQuizCount = answeredQuizCounts.getOrDefault(keywordId, 0); - childrenKeywords.forEach(child -> child.setProgress(totalQuizCounts, doneQuizCounts)); + childrenKeywords.forEach(child -> child.setProgress(totalQuizCounts, answeredQuizCounts)); } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java index 360e1e574..46e8952ed 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java @@ -28,7 +28,7 @@ public static KeywordsResponse of(final List keywords) { return new KeywordsResponse(keywordsResponse); } - public void setProgress(final Map totalQuizCounts, final Map doneQuizCounts) { - data.forEach(response -> response.setProgress(totalQuizCounts, doneQuizCounts)); + public void setProgress(final Map totalQuizCounts, final Map answeredQuizCounts) { + data.forEach(response -> response.setProgress(totalQuizCounts, answeredQuizCounts)); } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java index 99806ca93..c9f96348b 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java @@ -4,7 +4,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import wooteco.prolog.roadmap.domain.Keyword; -import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndDoneQuizCount; +import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndAnsweredQuizCount; import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndTotalQuizCount; import java.util.List; @@ -17,13 +17,13 @@ public interface KeywordRepository extends JpaRepository { "GROUP BY k.id") List findTotalQuizCount(); - @Query("SELECT k.id AS keywordId, COUNT (e.id) AS doneQuizCount " + + @Query("SELECT k.id AS keywordId, COUNT (e.id) AS answeredQuizCount " + "FROM Keyword k " + "JOIN Quiz q ON k.id = q.keyword.id " + "JOIN EssayAnswer e ON e.quiz.id = q.id " + "WHERE e.member.id = :memberId " + "GROUP BY k.id ") - List findDoneQuizCountByMemberId(@Param("memberId") Long memberId); + List findAnsweredQuizCountByMemberId(@Param("memberId") Long memberId); Keyword findFetchByIdOrderBySeq(@Param("keywordId") Long keywordId); diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndDoneQuizCount.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndAnsweredQuizCount.java similarity index 50% rename from backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndDoneQuizCount.java rename to backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndAnsweredQuizCount.java index d25aef114..63009ab0e 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndDoneQuizCount.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/dto/KeywordIdAndAnsweredQuizCount.java @@ -1,7 +1,7 @@ package wooteco.prolog.roadmap.domain.repository.dto; -public interface KeywordIdAndDoneQuizCount { +public interface KeywordIdAndAnsweredQuizCount { long getKeywordId(); - int getDoneQuizCount(); + int getAnsweredQuizCount(); } diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java index 287ac7226..85c05b86a 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java @@ -86,17 +86,17 @@ void findAllKeywordsWithProgress() { roots.hasSize(2) .satisfies(containsKeywordIds(root1.getId(), root2.getId())) .satisfies(containsTotalQuizCounts(0, 1)) - .satisfies(containsDoneQuizCounts(0, 0)); + .satisfies(containsAnsweredQuizCounts(0, 0)); oneDepthChildren.hasSize(1) .satisfies(containsKeywordIds(oneDepthChild.getId())) .satisfies(containsTotalQuizCounts(1)) - .satisfies(containsDoneQuizCounts(0)); + .satisfies(containsAnsweredQuizCounts(0)); twoDepthChildren.hasSize(1) .satisfies(containsKeywordIds(twoDepthChild.getId())) .satisfies(containsTotalQuizCounts(0)) - .satisfies(containsDoneQuizCounts(0)); + .satisfies(containsAnsweredQuizCounts(0)); }); } @@ -137,21 +137,21 @@ void findAllKeywordsWithProgress_login() { roots.hasSize(2) .satisfies(containsKeywordIds(root1.getId(), root2.getId())) .satisfies(containsTotalQuizCounts(0, 1)) - .satisfies(containsDoneQuizCounts(0, 1)); + .satisfies(containsAnsweredQuizCounts(0, 1)); oneDepthChildren.hasSize(1) .satisfies(containsKeywordIds(oneDepthChild.getId())) .satisfies(containsTotalQuizCounts(1)) - .satisfies(containsDoneQuizCounts(0)); + .satisfies(containsAnsweredQuizCounts(0)); twoDepthChildren.hasSize(1) .satisfies(containsKeywordIds(twoDepthChild.getId())) .satisfies(containsTotalQuizCounts(1)) - .satisfies(containsDoneQuizCounts(1)); + .satisfies(containsAnsweredQuizCounts(1)); }); } - private Consumer> containsDoneQuizCounts(final Integer... expected) { + private Consumer> containsAnsweredQuizCounts(final Integer... expected) { return keywords -> assertThat(keywords) .map(KeywordResponse::getDoneQuizCount) .containsExactlyInAnyOrder(expected); diff --git a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java index 133c685a6..4f018ebce 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java @@ -14,7 +14,7 @@ import wooteco.prolog.roadmap.domain.repository.EssayAnswerRepository; import wooteco.prolog.roadmap.domain.repository.KeywordRepository; import wooteco.prolog.roadmap.domain.repository.QuizRepository; -import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndDoneQuizCount; +import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndAnsweredQuizCount; import wooteco.prolog.roadmap.domain.repository.dto.KeywordIdAndTotalQuizCount; import wooteco.prolog.session.domain.Session; import wooteco.prolog.session.domain.repository.SessionRepository; @@ -121,7 +121,7 @@ class KeywordRepositoryTest { sessionId); // then - assertThat(extractParentKeyword.size()).isEqualTo(1); + assertThat(extractParentKeyword).hasSize(1); } @Test @@ -219,15 +219,15 @@ void findDoneQuizCountByMemberId() { essayAnswerRepository.save(new EssayAnswer(quiz2, "쓰라고 해서요ㅠ", member)); //when - final List doneQuizCounts = keywordRepository.findDoneQuizCountByMemberId(member.getId()); + final List doneQuizCounts = keywordRepository.findAnsweredQuizCountByMemberId(member.getId()); //then assertSoftly(softAssertions -> { softAssertions.assertThat(doneQuizCounts) - .map(KeywordIdAndDoneQuizCount::getKeywordId) + .map(KeywordIdAndAnsweredQuizCount::getKeywordId) .containsExactly(parent.getId()); softAssertions.assertThat(doneQuizCounts) - .map(KeywordIdAndDoneQuizCount::getDoneQuizCount) + .map(KeywordIdAndAnsweredQuizCount::getAnsweredQuizCount) .containsExactly(2); }); } From d94a493dbabcb6810096fede4e3ee82a7a68fbd6 Mon Sep 17 00:00:00 2001 From: nuyh Date: Wed, 22 Nov 2023 16:33:55 +0900 Subject: [PATCH 8/9] =?UTF-8?q?style:=20=EC=A0=95=EC=A0=81=20=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1609 --- .../wooteco/prolog/roadmap/application/KeywordService.java | 4 ++-- .../wooteco/prolog/roadmap/application/RoadMapService.java | 2 +- .../prolog/roadmap/application/dto/KeywordsResponse.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java index cb89cbb07..c5d687c4f 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java @@ -84,14 +84,14 @@ public KeywordsResponse findSessionIncludeRootKeywords(final Long sessionId) { List keywords = keywordRepository.findBySessionIdAndParentIsNull(sessionId); - return KeywordsResponse.of(keywords); + return KeywordsResponse.from(keywords); } @Transactional(readOnly = true) public KeywordsResponse newFindSessionIncludeRootKeywords() { List keywords = keywordRepository.newFindByParentIsNull(); - return KeywordsResponse.of(keywords); + return KeywordsResponse.from(keywords); } public void updateKeyword( diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java index fbfae1d22..d5514c3f6 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java @@ -26,7 +26,7 @@ public KeywordsResponse findAllKeywordsWithProgress(final Long curriculumId, fin final Map totalQuizCounts = getTotalQuizCounts(); final Map answeredQuizCounts = getAnsweredQuizCounts(memberId); - final KeywordsResponse keywordsResponse = KeywordsResponse.of(keywords); + final KeywordsResponse keywordsResponse = KeywordsResponse.from(keywords); keywordsResponse.setProgress(totalQuizCounts, answeredQuizCounts); return keywordsResponse; diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java index 46e8952ed..f4f602c00 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java @@ -19,7 +19,7 @@ public KeywordsResponse(final List data) { this.data = data; } - public static KeywordsResponse of(final List keywords) { + public static KeywordsResponse from(final List keywords) { final List keywordsResponse = keywords.stream() .filter(Keyword::isRoot) .map(KeywordResponse::createWithAllChildResponse) From e9cc339e3c2f52a437cf3d78e00d0db674d33e5d Mon Sep 17 00:00:00 2001 From: nuyh Date: Wed, 22 Nov 2023 17:30:51 +0900 Subject: [PATCH 9/9] =?UTF-8?q?refactor:=20=EC=A7=84=EB=8F=84=EC=9C=A8=20s?= =?UTF-8?q?etter=20=EC=A0=9C=EA=B1=B0=20=ED=9B=84=20dto=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=EC=97=90=EC=84=9C=20=EB=A7=A4=ED=95=91=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1609 --- .../roadmap/application/KeywordService.java | 9 +++--- .../roadmap/application/RoadMapService.java | 5 +-- .../application/dto/KeywordResponse.java | 32 +++++++++---------- .../application/dto/KeywordsResponse.java | 10 +++--- .../application/RoadMapServiceTest.java | 2 +- 5 files changed, 26 insertions(+), 32 deletions(-) diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java index c5d687c4f..b76335c4c 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/KeywordService.java @@ -13,6 +13,7 @@ import java.util.List; +import static java.util.Collections.emptyMap; import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_KEYWORD_NOT_FOUND_EXCEPTION; import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_SESSION_NOT_FOUND_EXCEPTION; @@ -66,7 +67,7 @@ public KeywordResponse findKeywordWithAllChild(final Long sessionId, final Long Keyword keyword = keywordRepository.findFetchByIdOrderBySeq(keywordId); - return KeywordResponse.createWithAllChildResponse(keyword); + return KeywordResponse.createWithAllChildResponse(keyword, emptyMap(), emptyMap()); } @Transactional(readOnly = true) @@ -75,7 +76,7 @@ public KeywordResponse newFindKeywordWithAllChild(final Long keywordId) { Keyword keyword = keywordRepository.findFetchByIdOrderBySeq(keywordId); - return KeywordResponse.createWithAllChildResponse(keyword); + return KeywordResponse.createWithAllChildResponse(keyword, emptyMap(), emptyMap()); } @Transactional(readOnly = true) @@ -84,14 +85,14 @@ public KeywordsResponse findSessionIncludeRootKeywords(final Long sessionId) { List keywords = keywordRepository.findBySessionIdAndParentIsNull(sessionId); - return KeywordsResponse.from(keywords); + return KeywordsResponse.of(keywords, emptyMap(), emptyMap()); } @Transactional(readOnly = true) public KeywordsResponse newFindSessionIncludeRootKeywords() { List keywords = keywordRepository.newFindByParentIsNull(); - return KeywordsResponse.from(keywords); + return KeywordsResponse.of(keywords, emptyMap(), emptyMap()); } public void updateKeyword( diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java index d5514c3f6..bff726073 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java @@ -26,10 +26,7 @@ public KeywordsResponse findAllKeywordsWithProgress(final Long curriculumId, fin final Map totalQuizCounts = getTotalQuizCounts(); final Map answeredQuizCounts = getAnsweredQuizCounts(memberId); - final KeywordsResponse keywordsResponse = KeywordsResponse.from(keywords); - keywordsResponse.setProgress(totalQuizCounts, answeredQuizCounts); - - return keywordsResponse; + return KeywordsResponse.of(keywords, totalQuizCounts, answeredQuizCounts); } private Map getTotalQuizCounts() { diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java index 0255414ec..683bf3e49 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordResponse.java @@ -5,6 +5,7 @@ import lombok.NoArgsConstructor; import wooteco.prolog.roadmap.domain.Keyword; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -20,14 +21,14 @@ public class KeywordResponse { private int order; private int importance; private int totalQuizCount; - private int doneQuizCount; + private int answeredQuizCount; private Long parentKeywordId; private List recommendedPosts; private List childrenKeywords; public KeywordResponse(final Long keywordId, final String name, final String description, final int order, final int importance, final int totalQuizCount, - final int doneQuizCount, final Long parentKeywordId, + final int answeredQuizCount, final Long parentKeywordId, final List recommendedPosts, final List childrenKeywords) { this.keywordId = keywordId; @@ -36,7 +37,7 @@ public KeywordResponse(final Long keywordId, final String name, final String des this.order = order; this.importance = importance; this.totalQuizCount = totalQuizCount; - this.doneQuizCount = doneQuizCount; + this.answeredQuizCount = answeredQuizCount; this.parentKeywordId = parentKeywordId; this.recommendedPosts = recommendedPosts; this.childrenKeywords = childrenKeywords; @@ -52,21 +53,23 @@ public static KeywordResponse createResponse(final Keyword keyword) { 0, 0, keyword.getParentIdOrNull(), createRecommendedPostResponses(keyword), - null); + Collections.emptyList()); } - public static KeywordResponse createWithAllChildResponse(final Keyword keyword) { + public static KeywordResponse createWithAllChildResponse(final Keyword keyword, + final Map totalQuizCounts, + final Map answeredQuizCounts) { return new KeywordResponse( keyword.getId(), keyword.getName(), keyword.getDescription(), keyword.getSeq(), keyword.getImportance(), - 0, - 0, + totalQuizCounts.getOrDefault(keyword.getId(), 0), + answeredQuizCounts.getOrDefault(keyword.getId(), 0), keyword.getParentIdOrNull(), createRecommendedPostResponses(keyword), - createChildren(keyword.getChildren())); + createChildren(keyword.getChildren(), totalQuizCounts, answeredQuizCounts)); } private static List createRecommendedPostResponses(final Keyword keyword) { @@ -75,16 +78,11 @@ private static List createRecommendedPostResponses(fina .collect(Collectors.toList()); } - private static List createChildren(final Set children) { + private static List createChildren(final Set children, + final Map totalQuizCounts, + final Map answeredQuizCounts) { return children.stream() - .map(KeywordResponse::createWithAllChildResponse) + .map(keyword -> createWithAllChildResponse(keyword, totalQuizCounts, answeredQuizCounts)) .collect(Collectors.toList()); } - - public void setProgress(final Map totalQuizCounts, final Map answeredQuizCounts) { - totalQuizCount = totalQuizCounts.getOrDefault(keywordId, 0); - doneQuizCount = answeredQuizCounts.getOrDefault(keywordId, 0); - - childrenKeywords.forEach(child -> child.setProgress(totalQuizCounts, answeredQuizCounts)); - } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java index f4f602c00..c5927f0a1 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java @@ -19,16 +19,14 @@ public KeywordsResponse(final List data) { this.data = data; } - public static KeywordsResponse from(final List keywords) { + public static KeywordsResponse of(final List keywords, + final Map totalQuizCounts, + final Map answeredQuizCounts) { final List keywordsResponse = keywords.stream() .filter(Keyword::isRoot) - .map(KeywordResponse::createWithAllChildResponse) + .map(rootKeyword -> KeywordResponse.createWithAllChildResponse(rootKeyword, totalQuizCounts, answeredQuizCounts)) .collect(Collectors.toList()); return new KeywordsResponse(keywordsResponse); } - - public void setProgress(final Map totalQuizCounts, final Map answeredQuizCounts) { - data.forEach(response -> response.setProgress(totalQuizCounts, answeredQuizCounts)); - } } diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java index 85c05b86a..fac37a2ff 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java @@ -153,7 +153,7 @@ void findAllKeywordsWithProgress_login() { private Consumer> containsAnsweredQuizCounts(final Integer... expected) { return keywords -> assertThat(keywords) - .map(KeywordResponse::getDoneQuizCount) + .map(KeywordResponse::getAnsweredQuizCount) .containsExactlyInAnyOrder(expected); }