diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java index 37b99f167..1d872ed94 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java @@ -42,8 +42,9 @@ public class BookQueryController { public BaseResponse showBookSearchList( @Parameter(description = "검색 키워드", example = "해리포터") @RequestParam final String keyword, @Parameter(description = "페이지 번호 (1부터 시작)", example = "1") @RequestParam final int page, + @Parameter(description = "사용자가 검색어 입력을 '확정'했는지 여부 (입력 중: false, 입력 확정: true)", example = "false") @RequestParam(name = "isFinalized") final boolean isFinalized, @Parameter(hidden = true) @UserId final Long userId) { - return BaseResponse.ok(BookSearchListResponse.of(bookSearchUseCase.searchBooks(keyword, page,userId), page)); + return BaseResponse.ok(BookSearchListResponse.of(bookSearchUseCase.searchBooks(keyword, page, userId, isFinalized), page)); } //책 상세검색 결과 조회 diff --git a/src/main/java/konkuk/thip/book/application/port/in/BookSearchUseCase.java b/src/main/java/konkuk/thip/book/application/port/in/BookSearchUseCase.java index 462f32d75..4f7cdfed9 100644 --- a/src/main/java/konkuk/thip/book/application/port/in/BookSearchUseCase.java +++ b/src/main/java/konkuk/thip/book/application/port/in/BookSearchUseCase.java @@ -5,6 +5,6 @@ public interface BookSearchUseCase { - NaverBookParseResult searchBooks(String keyword, int page,Long userId); - BookDetailSearchResult searchDetailBooks(String isbn,Long userId); + NaverBookParseResult searchBooks(String keyword, int page, Long userId, boolean isFinalized); + BookDetailSearchResult searchDetailBooks(String isbn, Long userId); } diff --git a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java index d1826a244..3858d06aa 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java @@ -12,7 +12,7 @@ import konkuk.thip.common.exception.BusinessException; import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.recentSearch.application.port.out.RecentSearchCommandPort; -import konkuk.thip.recentSearch.domain.RecentSearch; +import konkuk.thip.recentSearch.application.service.manager.RecentSearchCreateManager; import konkuk.thip.room.application.port.out.RoomQueryPort; import konkuk.thip.user.application.port.out.UserCommandPort; import konkuk.thip.user.application.port.out.UserQueryPort; @@ -43,9 +43,11 @@ public class BookSearchService implements BookSearchUseCase { private final UserCommandPort userCommandPort; private final BookRedisCommandPort bookRedisCommandPort; + private final RecentSearchCreateManager recentSearchCreateManager; + @Override @Transactional - public NaverBookParseResult searchBooks(String keyword, int page, Long userId) { + public NaverBookParseResult searchBooks(String keyword, int page, Long userId, boolean isFinalized) { if (keyword == null || keyword.isBlank()) { throw new BusinessException(BOOK_KEYWORD_REQUIRED); @@ -65,13 +67,8 @@ public NaverBookParseResult searchBooks(String keyword, int page, Long userId) { throw new BusinessException(BOOK_SEARCH_PAGE_OUT_OF_RANGE); } - //최근검색어 추가 - RecentSearch recentSearch = RecentSearch.builder() - .searchTerm(keyword) - .type(BOOK_SEARCH) - .userId(userId) - .build(); - recentSearchCommandPort.save(recentSearch); + // 최근 검색어 저장 + recentSearchCreateManager.saveRecentSearchByUser(userId, keyword, BOOK_SEARCH, isFinalized); return result; } diff --git a/src/main/java/konkuk/thip/recentSearch/application/service/manager/RecentSearchCreateManager.java b/src/main/java/konkuk/thip/recentSearch/application/service/manager/RecentSearchCreateManager.java index e74484e01..3c41bf10b 100644 --- a/src/main/java/konkuk/thip/recentSearch/application/service/manager/RecentSearchCreateManager.java +++ b/src/main/java/konkuk/thip/recentSearch/application/service/manager/RecentSearchCreateManager.java @@ -14,13 +14,16 @@ public class RecentSearchCreateManager { private final RecentSearchCommandPort recentSearchCommandPort; private final RecentSearchQueryPort recentSearchQueryPort; - public void saveRecentSearchByUser(Long userId, String keyword, RecentSearchType type) { + public void saveRecentSearchByUser(Long userId, String keyword, RecentSearchType type, boolean isFinalized) { + // 검색완료일 경우에 최근검색어 추가 + if (isFinalized) { + // 동일 조건 (userId + keyword + type) 검색 기록이 이미 있는지 확인 + recentSearchQueryPort.findRecentSearchByKeywordAndUserId(keyword, userId, type) + .ifPresentOrElse( + recentSearchCommandPort::touch, // 있으면 modifiedAt 갱신 + () -> recentSearchCommandPort.save(RecentSearch.withoutId(keyword, type, userId)) // 없으면 새로 저장 + ); + } - // 동일 조건 (userId + keyword + type) 검색 기록이 이미 있는지 확인 - recentSearchQueryPort.findRecentSearchByKeywordAndUserId(keyword, userId, type) - .ifPresentOrElse( - recentSearchCommandPort::touch, // 있으면 modifiedAt 갱신 - () -> recentSearchCommandPort.save(RecentSearch.withoutId(keyword, type, userId)) // 없으면 새로 저장 - ); } } diff --git a/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java b/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java index e1b55a560..bf75c21d4 100644 --- a/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java +++ b/src/main/java/konkuk/thip/room/adapter/in/web/RoomQueryController.java @@ -33,7 +33,7 @@ public class RoomQueryController { @Operation( summary = "방 검색", - description = "키워드, 카테고리, 정렬 방식, 페이지 번호를 기준으로 방을 검색합니다." + description = "키워드, 카테고리, 정렬 방식, 페이지 번호를 기준으로 방을 검색합니다. 아직 수정중입니다. 일단 request param 만 수정한 상태입니다." ) @ExceptionDescription(ROOM_SEARCH) @GetMapping("/rooms/search") @@ -41,9 +41,11 @@ public BaseResponse searchRooms( @Parameter(description = "검색 키워드 (책 이름 or 방 이름", example = "해리") @RequestParam(value = "keyword", required = false, defaultValue = "") final String keyword, @Parameter(description = "모임방 카테고리", example = "문학") @RequestParam(value = "category", required = false, defaultValue = "") final String category, @Parameter(description = "정렬 방식 (마감 임박 : deadline, 신청 인원 : memberCount)", example = "deadline") @RequestParam("sort") final String sort, - @Parameter(description = "페이지 번호", example = "1") @RequestParam("page") final int page + @Parameter(description = "사용자가 검색어 입력을 '확정'했는지 여부 (입력 중: false, 입력 확정: true)", example = "false") @RequestParam(name = "isFinalized") final boolean isFinalized, + @Parameter(description = "페이지 번호", example = "1") @RequestParam("page") final int page, + @Parameter(hidden = true) @UserId final Long userId ) { - return BaseResponse.ok(roomSearchUseCase.searchRoom(keyword, category, sort, page)); + return BaseResponse.ok(roomSearchUseCase.searchRoom(keyword, category, sort, page, isFinalized, userId)); } @Operation( diff --git a/src/main/java/konkuk/thip/room/application/port/in/RoomSearchUseCase.java b/src/main/java/konkuk/thip/room/application/port/in/RoomSearchUseCase.java index d7ae4c94c..d4231a8c0 100644 --- a/src/main/java/konkuk/thip/room/application/port/in/RoomSearchUseCase.java +++ b/src/main/java/konkuk/thip/room/application/port/in/RoomSearchUseCase.java @@ -4,5 +4,5 @@ public interface RoomSearchUseCase { - RoomSearchResponse searchRoom(String keyword, String category, String sort, int page); + RoomSearchResponse searchRoom(String keyword, String category, String sort, int page, boolean isFinalized, Long userId); } diff --git a/src/main/java/konkuk/thip/room/application/service/RoomSearchService.java b/src/main/java/konkuk/thip/room/application/service/RoomSearchService.java index 488dd9df1..9b5b8626a 100644 --- a/src/main/java/konkuk/thip/room/application/service/RoomSearchService.java +++ b/src/main/java/konkuk/thip/room/application/service/RoomSearchService.java @@ -1,11 +1,13 @@ package konkuk.thip.room.application.service; import konkuk.thip.common.exception.BusinessException; +import konkuk.thip.recentSearch.adapter.out.jpa.RecentSearchType; +import konkuk.thip.recentSearch.application.service.manager.RecentSearchCreateManager; import konkuk.thip.room.adapter.in.web.response.RoomSearchResponse; -import konkuk.thip.room.domain.Category; import konkuk.thip.room.adapter.out.persistence.RoomSearchSortParam; import konkuk.thip.room.application.port.in.RoomSearchUseCase; import konkuk.thip.room.application.port.out.RoomQueryPort; +import konkuk.thip.room.domain.Category; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -14,7 +16,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static konkuk.thip.common.exception.code.ErrorCode.CATEGORY_NOT_FOUND; import static konkuk.thip.common.exception.code.ErrorCode.INVALID_ROOM_SEARCH_SORT; @Service @@ -25,9 +26,11 @@ public class RoomSearchService implements RoomSearchUseCase { private final RoomQueryPort roomQueryPort; + private final RecentSearchCreateManager recentSearchCreateManager; + @Override - @Transactional(readOnly = true) - public RoomSearchResponse searchRoom(String keyword, String category, String sort, int page) { + @Transactional // <- 최근 검색 저장으로 인한 트랜잭션 + public RoomSearchResponse searchRoom(String keyword, String category, String sort, int page, boolean isFinalized, Long userId) { // 1. validation String sortVal = validateSort(sort); String categoryVal = validateCategory(category); @@ -39,6 +42,9 @@ public RoomSearchResponse searchRoom(String keyword, String category, String sor // 3. 방 검색 Page result = roomQueryPort.searchRoom(keyword, categoryVal, pageable); + // TODO 검색 완료일 경우, 최근 검색어로 저장되도록 + recentSearchCreateManager.saveRecentSearchByUser(userId, keyword, RecentSearchType.ROOM_SEARCH, isFinalized); + // 4. response 구성 return new RoomSearchResponse( result.getContent(), diff --git a/src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java b/src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java index de90725d8..c411e3107 100644 --- a/src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java +++ b/src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java @@ -120,9 +120,10 @@ public BaseResponse checkIsFollowing( @GetMapping("/users") public BaseResponse showSearchUsers( @Parameter(description = "검색어", example = "thip") @RequestParam @NotBlank(message = "검색어는 필수입니다.") final String keyword, + @Parameter(description = "사용자가 검색어 입력을 '확정'했는지 여부 (입력 중: false, 입력 확정: true)", example = "false") @RequestParam(name = "isFinalized") final boolean isFinalized, @Parameter(description = "단일 검색 결과 페이지 크기 (1~30) / default : 30", example = "30") @RequestParam(required = false, defaultValue = "30") @Min(1) @Max(30) final Integer size, @Parameter(hidden = true) @UserId final Long userId) { - return BaseResponse.ok(userSearchUsecase.searchUsers(UserSearchQuery.of(keyword, userId, size))); + return BaseResponse.ok(userSearchUsecase.searchUsers(UserSearchQuery.of(keyword, userId, size, isFinalized))); } @Operation( diff --git a/src/main/java/konkuk/thip/user/application/port/in/dto/UserSearchQuery.java b/src/main/java/konkuk/thip/user/application/port/in/dto/UserSearchQuery.java index 17806ef8c..8fe66190e 100644 --- a/src/main/java/konkuk/thip/user/application/port/in/dto/UserSearchQuery.java +++ b/src/main/java/konkuk/thip/user/application/port/in/dto/UserSearchQuery.java @@ -3,9 +3,10 @@ public record UserSearchQuery( String keyword, Long userId, - Integer size + Integer size, + boolean isFinalized ) { - public static UserSearchQuery of(String keyword, Long userId, Integer size) { - return new UserSearchQuery(keyword, userId, size); + public static UserSearchQuery of(String keyword, Long userId, Integer size, boolean isFinalized) { + return new UserSearchQuery(keyword, userId, size, isFinalized); } } diff --git a/src/main/java/konkuk/thip/user/application/service/UserSearchService.java b/src/main/java/konkuk/thip/user/application/service/UserSearchService.java index e2aed557e..e9c9a6267 100644 --- a/src/main/java/konkuk/thip/user/application/service/UserSearchService.java +++ b/src/main/java/konkuk/thip/user/application/service/UserSearchService.java @@ -29,9 +29,8 @@ public UserSearchResponse searchUsers(UserSearchQuery userSearchQuery) { userSearchQuery.size() )); - // 최근 검색어 저장 - recentSearchCreateManager.saveRecentSearchByUser(userSearchQuery.userId(), userSearchQuery.keyword(), RecentSearchType.USER_SEARCH); - + recentSearchCreateManager.saveRecentSearchByUser(userSearchQuery.userId(), userSearchQuery.keyword(), RecentSearchType.USER_SEARCH, userSearchQuery.isFinalized()); + return UserSearchResponse.of(userDtoList); } } diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java index 0c38d9537..52467e26f 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java @@ -94,6 +94,7 @@ void searchBooks_success() throws Exception { .header("Authorization", "Bearer " + testToken) .param("keyword", keyword) .param("page", String.valueOf(page)) + .param("isFinalized", String.valueOf(false)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.isSuccess").value(true)) @@ -115,6 +116,7 @@ void searchBooks_pageOutOfRange() throws Exception { .header("Authorization", "Bearer " + testToken) .param("keyword", keyword) .param("page", String.valueOf(page)) + .param("isFinalized", String.valueOf(false)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.isSuccess").value(false)) @@ -136,6 +138,7 @@ void searchBooks_keywordMissing_badRequest() throws Exception { .header("Authorization", "Bearer " + testToken) .param("keyword", keyword) .param("page", String.valueOf(page)) + .param("isFinalized", String.valueOf(false)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code").value(ErrorCode.BOOK_KEYWORD_REQUIRED.getCode())) @@ -155,6 +158,7 @@ void searchBooks_pageInvalid_badRequest() throws Exception { .header("Authorization", "Bearer " + testToken) .param("keyword", keyword) .param("page", String.valueOf(page)) + .param("isFinalized", String.valueOf(false)) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code").value(ErrorCode.BOOK_PAGE_NUMBER_INVALID.getCode())) @@ -175,6 +179,7 @@ void searchBooks_savesRecentSearch() throws Exception { .header("Authorization", "Bearer " + testToken) .param("keyword", keyword) .param("page", String.valueOf(page)) + .param("isFinalized", String.valueOf(true)) // 검색 완료일 경우 : 최근검색어로 저장된다 .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); diff --git a/src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java b/src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java index 20a7733c7..380357b1f 100644 --- a/src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java +++ b/src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java @@ -184,6 +184,7 @@ void search_keyword_and_sort_deadline() throws Exception { .requestAttr("userId", 1L) .param("keyword", "과학") .param("sort", "deadline") + .param("isFinalized", String.valueOf(false)) .param("page", "1")); //then @@ -233,6 +234,7 @@ void search_keyword_and_sort_member_count() throws Exception { .requestAttr("userId", 1L) .param("keyword", "과학") .param("sort", "memberCount") + .param("isFinalized", String.valueOf(false)) .param("page", "1")); //then @@ -285,6 +287,7 @@ void search_category_and_sort_deadline() throws Exception { .requestAttr("userId", 1L) .param("category", "과학·IT") .param("sort", "deadline") + .param("isFinalized", String.valueOf(false)) .param("page", "1")); //then @@ -324,6 +327,7 @@ void search_sort_deadline() throws Exception { ResultActions result = mockMvc.perform(get("/rooms/search") .requestAttr("userId", 1L) .param("sort", "deadline") + .param("isFinalized", String.valueOf(false)) .param("page", "1")); //then @@ -366,6 +370,7 @@ void search_keyword_and_category() throws Exception { .param("keyword", "과학") .param("category", Category.SCIENCE_IT.getValue()) .param("sort", "deadline") + .param("isFinalized", String.valueOf(false)) .param("page", "1")); // then diff --git a/src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java b/src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java index 88d0f2fc8..fc563c0c0 100644 --- a/src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java +++ b/src/test/java/konkuk/thip/user/adapter/in/web/UserSearchApiTest.java @@ -72,7 +72,7 @@ void searchUsersAndSaveRecentSearch() throws Exception { .param("keyword", keyword) .requestAttr("userId", currentUserId) .param("size", "10") - + .param("isFinalized", String.valueOf(true)) // 검색 완료 -> 최근 검색어 저장 ); // then: 검색 결과 검증 @@ -91,4 +91,4 @@ void searchUsersAndSaveRecentSearch() throws Exception { assertEquals(RecentSearchType.USER_SEARCH, saved.getType()); assertEquals(currentUserId, saved.getUserJpaEntity().getUserId()); } -} \ No newline at end of file +}