From e66c85b836718100df68fcce28637d368e6e2458 Mon Sep 17 00:00:00 2001 From: msk226 Date: Mon, 3 Feb 2025 13:52:56 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=20[SPOT-171][REFACTOR]=20=EC=B6=94?= =?UTF-8?q?=EC=B2=9C=20=EC=8A=A4=ED=84=B0=EB=94=94=20=EB=82=B4=EB=B6=80=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../querydsl/StudyRepositoryCustom.java | 1 + .../impl/StudyRepositoryCustomImpl.java | 13 +++++- .../service/study/StudyQueryServiceImpl.java | 44 +++++++++++++++++-- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/example/spot/repository/querydsl/StudyRepositoryCustom.java b/src/main/java/com/example/spot/repository/querydsl/StudyRepositoryCustom.java index c2a19271..f165eb86 100644 --- a/src/main/java/com/example/spot/repository/querydsl/StudyRepositoryCustom.java +++ b/src/main/java/com/example/spot/repository/querydsl/StudyRepositoryCustom.java @@ -21,6 +21,7 @@ public interface StudyRepositoryCustom { List findByStudyTheme(List studyThemes); List findByStudyThemeAndNotInIds(List studyThemes, List studyIds); + List findByRegionStudyAndNotInIds(List regionStudies, List studyIds); // 모집중 스터디 조회 List findRecruitingStudyByConditions(Map search, StudySortBy sortBy, Pageable pageable); diff --git a/src/main/java/com/example/spot/repository/querydsl/impl/StudyRepositoryCustomImpl.java b/src/main/java/com/example/spot/repository/querydsl/impl/StudyRepositoryCustomImpl.java index c119f808..92d4bec8 100644 --- a/src/main/java/com/example/spot/repository/querydsl/impl/StudyRepositoryCustomImpl.java +++ b/src/main/java/com/example/spot/repository/querydsl/impl/StudyRepositoryCustomImpl.java @@ -76,7 +76,18 @@ public List findByStudyThemeAndNotInIds(List studyThemes, return queryFactory.selectFrom(study) .where(study.studyThemes.any().in(studyThemes)) .where(study.id.notIn(studyIds)) - .orderBy(study.createdAt.desc()) + .orderBy(study.hitNum.desc()) + .offset(0) + .limit(3) + .fetch(); + } + + @Override + public List findByRegionStudyAndNotInIds(List regionStudies, List studyIds) { + return queryFactory.selectFrom(study) + .where(study.regionStudies.any().in(regionStudies)) + .where(study.id.notIn(studyIds)) + .orderBy(study.heartCount.desc()) .offset(0) .limit(3) .fetch(); diff --git a/src/main/java/com/example/spot/service/study/StudyQueryServiceImpl.java b/src/main/java/com/example/spot/service/study/StudyQueryServiceImpl.java index 0748c48d..82df5415 100644 --- a/src/main/java/com/example/spot/service/study/StudyQueryServiceImpl.java +++ b/src/main/java/com/example/spot/service/study/StudyQueryServiceImpl.java @@ -47,12 +47,15 @@ import com.example.spot.web.dto.study.response.StudyScheduleResponseDTO.StudyScheduleDTO; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; @@ -254,33 +257,66 @@ public StudyPreviewDTO findRecommendStudies(Long memberId) { // 회원 관심사 조회 List memberThemes = memberThemeRepository.findAllByMemberId(memberId); + List preferredRegions = preferredRegionRepository.findAllByMemberId(memberId); // 회원 관심사가 없을 경우 if (memberThemes.isEmpty()) throw new MemberHandler(ErrorStatus._STUDY_THEME_IS_INVALID); - // MemberId로 회원 관심사 전체 조회 + if (preferredRegions.isEmpty()) + throw new MemberHandler(ErrorStatus._STUDY_REGION_IS_INVALID); + + // MemberId로 회원 관심사 및 관심 지역 전체 조회 List themes = memberThemes.stream() .map(MemberTheme::getTheme) .toList(); + List regions = preferredRegions.stream() + .map(PreferredRegion::getRegion) + .toList(); + // 회원 관심사로 스터디 테마 조회 List studyThemes = themes.stream() .flatMap(theme -> studyThemeRepository.findAllByTheme(theme).stream()) .toList(); + // 회원 관심 지역으로 스터디 지역 조회 + List regionStudies = regions.stream() + .flatMap(region -> regionStudyRepository.findAllByRegion(region).stream()) + .toList(); + // 해당 관심사에 해당하는 스터디가 존재하지 않을 경우 if (studyThemes.isEmpty()) throw new StudyHandler(ErrorStatus._STUDY_THEME_NOT_EXIST); + // 해당 관심 지역에 해당하는 스터디가 존재하지 않을 경우 + if (regionStudies.isEmpty()) + throw new StudyHandler(ErrorStatus._STUDY_REGION_NOT_EXIST); + // 회원 관심사로 추천 스터디 조회 - List studies = studyRepository.findByStudyThemeAndNotInIds(studyThemes, memberOngoingStudyIds); + List preferThemeStudies = studyRepository.findByStudyThemeAndNotInIds(studyThemes, memberOngoingStudyIds); + + // 회원 관심 지역으로 추천 스터디 조회 + List preferRegionStudies = studyRepository.findByRegionStudyAndNotInIds(regionStudies, memberOngoingStudyIds); // 추천 스터디가 없을 경우 - if (studies.isEmpty()) + if (preferRegionStudies.isEmpty() || preferThemeStudies.isEmpty()) throw new StudyHandler(ErrorStatus._STUDY_IS_NOT_MATCH); - return getDTOs(studies, Pageable.unpaged(), studies.size(), memberId); + // 두 리스트를 합쳐서 중복 제거 + Set combinedStudies = new HashSet<>(preferThemeStudies); + combinedStudies.addAll(preferRegionStudies); + + // 리스트 변환 + List studyList = new ArrayList<>(combinedStudies); + + // 랜덤으로 최대 3개 선택 + Collections.shuffle(studyList); + List selectedStudies = studyList.stream() + .limit(3) + .collect(Collectors.toList()); + + return getDTOs(selectedStudies, Pageable.unpaged(), selectedStudies.size(), memberId); } From dc7f3e67a7d5d1108e9a483629b586622878752e Mon Sep 17 00:00:00 2001 From: msk226 Date: Mon, 3 Feb 2025 13:53:23 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=20[SPOT-171][TEST]=20=EB=B0=94=EB=80=90=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EC=97=90=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/study/StudyQueryServiceTest.java | 62 +++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/example/spot/service/study/StudyQueryServiceTest.java b/src/test/java/com/example/spot/service/study/StudyQueryServiceTest.java index 60929b8b..102a70db 100644 --- a/src/test/java/com/example/spot/service/study/StudyQueryServiceTest.java +++ b/src/test/java/com/example/spot/service/study/StudyQueryServiceTest.java @@ -394,12 +394,17 @@ void findRecommendStudies() { // Mock the memberThemeRepository to return a list of MemberTheme when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); when(studyThemeRepository.findAllByTheme(theme1)).thenReturn(List.of(studyTheme1)); when(studyThemeRepository.findAllByTheme(theme2)).thenReturn(List.of(studyTheme2)); + when(regionStudyRepository.findAllByRegion(region1)).thenReturn(List.of(regionStudy1)); + when(regionStudyRepository.findAllByRegion(region2)).thenReturn(List.of(regionStudy2)); + // Mocking the studyRepository to return studies based on the study themes when(studyRepository.findByStudyThemeAndNotInIds(anyList(), anyList())).thenReturn(List.of(study1, study2)); + when(studyRepository.findByRegionStudyAndNotInIds(anyList(), anyList())).thenReturn(List.of(study1, study2)); when(memberRepository.existsById(member.getId())).thenReturn(true); @@ -422,13 +427,17 @@ void findRecommendStudiesOnFail() { // given when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); - + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); when(studyThemeRepository.findAllByTheme(theme1)).thenReturn(List.of(studyTheme1)); when(studyThemeRepository.findAllByTheme(theme2)).thenReturn(List.of(studyTheme2)); + when(regionStudyRepository.findAllByRegion(region1)).thenReturn(List.of(regionStudy1)); + when(regionStudyRepository.findAllByRegion(region2)).thenReturn(List.of(regionStudy2)); + // Mocking the studyRepository to return studies based on the study themes when(studyRepository.findByStudyThemeAndNotInIds(anyList(), anyList())).thenReturn(List.of()); + when(studyRepository.findByRegionStudyAndNotInIds(anyList(), anyList())).thenReturn(List.of()); when(memberRepository.existsById(member.getId())).thenReturn(true); @@ -443,18 +452,46 @@ void findRecommendStudiesOnFail() { } @Test - @DisplayName("추천 스터디 조회 - 회원의 관심 테마에_해당하는_스터디가 없는 경우") + @DisplayName("추천 스터디 조회 - 회원의 관심 테마에 해당하는 스터디가 없는 경우") void 추천_스터디_조회_시_회원의_관심_테마에_해당하는_스터디가_없는_경우() { // given when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); when(studyThemeRepository.findAllByTheme(theme1)).thenReturn(List.of()); when(studyThemeRepository.findAllByTheme(theme2)).thenReturn(List.of()); + when(regionStudyRepository.findAllByRegion(region1)).thenReturn(List.of(regionStudy1)); + when(regionStudyRepository.findAllByRegion(region2)).thenReturn(List.of(regionStudy2)); + + // when & then + assertThrows(StudyHandler.class, () -> { + studyQueryService.findRecommendStudies(member.getId()); + }); + + verify(memberThemeRepository).findAllByMemberId(member.getId()); + verify(studyThemeRepository, times(1)).findAllByTheme(theme1); + verify(studyThemeRepository, times(1)).findAllByTheme(theme2); + } + + @Test + @DisplayName("추천 스터디 조회 - 회원의 관심 지역에 해당하는 스터디가 없는 경우") + void 추천_스터디_조회_시_회원의_관심_지역에_해당하는_스터디가_없는_경우() { + // given + when(memberThemeRepository.findAllByMemberId(member.getId())).thenReturn(List.of(memberTheme1, memberTheme2)); + when(preferredRegionRepository.findAllByMemberId(member.getId())).thenReturn(List.of(preferredRegion1, preferredRegion2)); + + when(studyThemeRepository.findAllByTheme(theme1)).thenReturn(List.of(studyTheme1)); + when(studyThemeRepository.findAllByTheme(theme2)).thenReturn(List.of(studyTheme2)); + + when(regionStudyRepository.findAllByRegion(region1)).thenReturn(List.of()); + when(regionStudyRepository.findAllByRegion(region2)).thenReturn(List.of()); + // when & then assertThrows(StudyHandler.class, () -> { studyQueryService.findRecommendStudies(member.getId()); }); + verify(memberThemeRepository).findAllByMemberId(member.getId()); verify(studyThemeRepository, times(1)).findAllByTheme(theme1); verify(studyThemeRepository, times(1)).findAllByTheme(theme2); @@ -468,9 +505,9 @@ void findRecommendStudiesOnInvalidUser() { Member member = getMember(); // when & then - when(memberRepository.findById(member.getId())).thenReturn(Optional.empty()); + when(memberRepository.existsById(member.getId())).thenReturn(false); - assertThrows(StudyHandler.class, () -> { + assertThrows(MemberHandler.class, () -> { studyQueryService.findRecommendStudies(member.getId()); }); } @@ -492,6 +529,23 @@ void findRecommendStudiesOnNoInterest() { }); } + @Test + @DisplayName("추천 스터디 조회 - 사용자의 관심 지역이 없는 경우") + void findRecommendStudiesOnNoRegion() { + // given + Member member = getMember(); + Long memberId = member.getId(); + + // Mock the preferredRegionRepository to return an empty list + when(preferredRegionRepository.findAllByMemberId(memberId)).thenReturn(List.of()); + + // when & then + assertThrows(MemberHandler.class, () -> { + studyQueryService.findRecommendStudies(memberId); + }); + + } + /* -------------------------------------------------------- 관심 Best 스터디 조회 ------------------------------------------------------------------------*/ @Test @DisplayName("관심 Best 스터디 조회 - 성공")