diff --git a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureApiPresentation.java b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureApiPresentation.java index 9f4bd5bf..69fc27b8 100644 --- a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureApiPresentation.java +++ b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureApiPresentation.java @@ -1,9 +1,12 @@ package com.plzgraduate.myongjigraduatebe.lecture.api; +import java.util.List; + import javax.validation.constraints.Size; import org.springframework.web.bind.annotation.RequestParam; +import com.plzgraduate.myongjigraduatebe.core.meta.LoginUser; import com.plzgraduate.myongjigraduatebe.lecture.api.dto.response.SearchLectureResponse; import io.swagger.v3.oas.annotations.Parameter; @@ -12,7 +15,8 @@ @Tag(name = "SearchLecture", description = "type과 keyword를 통해 과목정보를 검색하는 API") public interface SearchLectureApiPresentation { - SearchLectureResponse searchLecture( + List searchLecture( + @LoginUser Long userId, @Parameter(name = "type", description = "과목명 또는 과목코드") @RequestParam(defaultValue = "name") String type, @Parameter(name = "keyword", description = "검색어 2자리 이상") @RequestParam @Size(min = 2, message = "검색어를 2자리 이상 입력해주세요.") String keyword ); diff --git a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureController.java b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureController.java index 706f006c..ac8e31a6 100644 --- a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureController.java +++ b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureController.java @@ -1,5 +1,8 @@ package com.plzgraduate.myongjigraduatebe.lecture.api; +import java.util.List; +import java.util.stream.Collectors; + import javax.validation.constraints.Size; import org.springframework.validation.annotation.Validated; @@ -7,9 +10,11 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import com.plzgraduate.myongjigraduatebe.core.meta.LoginUser; import com.plzgraduate.myongjigraduatebe.core.meta.WebAdapter; import com.plzgraduate.myongjigraduatebe.lecture.api.dto.response.SearchLectureResponse; import com.plzgraduate.myongjigraduatebe.lecture.application.usecase.SearchLectureUseCase; +import com.plzgraduate.myongjigraduatebe.lecture.application.usecase.dto.SearchedLectureDto; import lombok.RequiredArgsConstructor; @@ -22,10 +27,13 @@ public class SearchLectureController implements SearchLectureApiPresentation { private final SearchLectureUseCase searchLectureUseCase; @GetMapping - public SearchLectureResponse searchLecture( + public List searchLecture( + @LoginUser Long userId, @RequestParam(defaultValue = "name") String type, @RequestParam @Size(min = 2, message = "검색어를 2자리 이상 입력해주세요.") String keyword ) { - return SearchLectureResponse.from(searchLectureUseCase.searchLectures(type, keyword)); + return searchLectureUseCase.searchLectures(userId, type, keyword).stream() + .map(SearchLectureResponse::from) + .collect(Collectors.toList()); } } diff --git a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/dto/response/SearchLectureResponse.java b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/dto/response/SearchLectureResponse.java index bebebabc..628d71cc 100644 --- a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/dto/response/SearchLectureResponse.java +++ b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/api/dto/response/SearchLectureResponse.java @@ -1,28 +1,46 @@ package com.plzgraduate.myongjigraduatebe.lecture.api.dto.response; -import java.util.List; -import java.util.stream.Collectors; - -import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture; +import com.plzgraduate.myongjigraduatebe.lecture.application.usecase.dto.SearchedLectureDto; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Getter; -import lombok.NoArgsConstructor; @Getter -@NoArgsConstructor public class SearchLectureResponse { - private List lectures; + @Schema(name = "id", example = "18") + private final Long id; + @Schema(name = "lectureCode", example = "KMA02137") + private final String lectureCode; + @Schema(name = "name", example = "4차산업혁명시대의진로선택") + private final String name; + @Schema(name = "credit", example = "2") + private final int credit; + @Schema(name = "isRevoked", example = "false") + private final boolean revoked; + @Schema(name = "isAddable", example = "true") + private final boolean taken; @Builder - private SearchLectureResponse(List lectures) { - this.lectures = lectures; + private SearchLectureResponse(Long id, String lectureCode, String name, int credit, boolean revoked, + boolean taken) { + this.id = id; + this.lectureCode = lectureCode; + this.name = name; + this.credit = credit; + this.revoked = revoked; + this.taken = taken; } - public static SearchLectureResponse from(List lectures) { + public static SearchLectureResponse from(SearchedLectureDto searchedLectureDto) { return SearchLectureResponse.builder() - .lectures(lectures.stream().map(LectureResponse::of).collect(Collectors.toList())) + .id(searchedLectureDto.getLecture().getId()) + .lectureCode(searchedLectureDto.getLecture().getLectureCode()) + .name(searchedLectureDto.getLecture().getName()) + .credit(searchedLectureDto.getLecture().getCredit()) + .revoked(searchedLectureDto.getLecture().getIsRevoked() != 0) + .taken(searchedLectureDto.isAddable()) .build(); } } diff --git a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/service/SearchLectureService.java b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/service/SearchLectureService.java index a5704c53..08f2b5e6 100644 --- a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/service/SearchLectureService.java +++ b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/service/SearchLectureService.java @@ -1,22 +1,41 @@ package com.plzgraduate.myongjigraduatebe.lecture.application.service; import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.transaction.annotation.Transactional; import com.plzgraduate.myongjigraduatebe.core.meta.UseCase; import com.plzgraduate.myongjigraduatebe.lecture.application.port.SearchLecturePort; import com.plzgraduate.myongjigraduatebe.lecture.application.usecase.SearchLectureUseCase; +import com.plzgraduate.myongjigraduatebe.lecture.application.usecase.dto.SearchedLectureDto; import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture; +import com.plzgraduate.myongjigraduatebe.takenlecture.application.usecase.find.FindTakenLectureUseCase; +import com.plzgraduate.myongjigraduatebe.takenlecture.domain.model.TakenLecture; +import com.plzgraduate.myongjigraduatebe.takenlecture.domain.model.TakenLectureInventory; import lombok.RequiredArgsConstructor; @UseCase +@Transactional(readOnly = true) @RequiredArgsConstructor public class SearchLectureService implements SearchLectureUseCase { private final SearchLecturePort searchLecturePort; + private final FindTakenLectureUseCase findTakenLectureUseCase; @Override - public List searchLectures(String type, String keyword) { - return searchLecturePort.searchLectureByNameOrCode(type, keyword); + public List searchLectures(Long userId, String type, String keyword) { + List searchedLectures = searchLecturePort.searchLectureByNameOrCode(type, keyword); + TakenLectureInventory takenLectureInventory = findTakenLectureUseCase.findTakenLectures(userId); + + List takenLectures = takenLectureInventory.getTakenLectures().stream() + .map(TakenLecture::getLecture) + .collect(Collectors.toList()); + + return searchedLectures.stream() + .map(searchedLecture -> SearchedLectureDto.of(takenLectures.contains(searchedLecture), + searchedLecture)) + .collect(Collectors.toList()); } } diff --git a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/usecase/SearchLectureUseCase.java b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/usecase/SearchLectureUseCase.java index 28746cfc..6bb2a4bd 100644 --- a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/usecase/SearchLectureUseCase.java +++ b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/usecase/SearchLectureUseCase.java @@ -2,8 +2,8 @@ import java.util.List; -import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture; +import com.plzgraduate.myongjigraduatebe.lecture.application.usecase.dto.SearchedLectureDto; public interface SearchLectureUseCase { - List searchLectures(String type, String keyword); + List searchLectures(Long userId, String type, String keyword); } diff --git a/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/usecase/dto/SearchedLectureDto.java b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/usecase/dto/SearchedLectureDto.java new file mode 100644 index 00000000..c6d12033 --- /dev/null +++ b/src/main/java/com/plzgraduate/myongjigraduatebe/lecture/application/usecase/dto/SearchedLectureDto.java @@ -0,0 +1,25 @@ +package com.plzgraduate.myongjigraduatebe.lecture.application.usecase.dto; + +import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture; + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class SearchedLectureDto { + + private final boolean addable; + private final Lecture lecture; + + @Builder + private SearchedLectureDto(boolean addable, Lecture lecture) { + this.addable = addable; + this.lecture = lecture; + } + + public static SearchedLectureDto of(boolean addable, Lecture lecture) { + return SearchedLectureDto.builder() + .addable(addable) + .lecture(lecture).build(); + } +} diff --git a/src/test/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureControllerTest.java b/src/test/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureControllerTest.java index 53cb1462..279bb55a 100644 --- a/src/test/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureControllerTest.java +++ b/src/test/java/com/plzgraduate/myongjigraduatebe/lecture/api/SearchLectureControllerTest.java @@ -2,6 +2,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.times; @@ -15,6 +16,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import com.plzgraduate.myongjigraduatebe.lecture.application.usecase.dto.SearchedLectureDto; import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture; import com.plzgraduate.myongjigraduatebe.support.WebAdaptorTestSupport; import com.plzgraduate.myongjigraduatebe.support.WithMockAuthenticationUser; @@ -26,10 +28,8 @@ class SearchLectureControllerTest extends WebAdaptorTestSupport { @Test void searchLecture() throws Exception { //given - List searchedLectures = List.of( - Lecture.builder().id(1L).build() - ); - given(searchLectureUseCase.searchLectures(any(), any())).willReturn(searchedLectures); + List searchedLectures = List.of(SearchedLectureDto.of(true, Lecture.from("code"))); + given(searchLectureUseCase.searchLectures(anyLong(), any(), any())).willReturn(searchedLectures); //when //then mockMvc.perform(get("/api/v1/lectures") @@ -37,9 +37,10 @@ void searchLecture() throws Exception { .param("keyword", "기초")) .andDo(print()) .andExpect(status().isOk()) - .andExpect(jsonPath("$.lectures", hasSize(1))); + .andExpect(jsonPath("$", hasSize(searchedLectures.size()))) + .andExpect(jsonPath("$.[0].taken").value(true)); - then(searchLectureUseCase).should(times(1)).searchLectures(any(), any()); + then(searchLectureUseCase).should(times(1)).searchLectures(anyLong(), any(), any()); } @WithMockAuthenticationUser diff --git a/src/test/java/com/plzgraduate/myongjigraduatebe/lecture/application/service/search/SearchLectureServiceTest.java b/src/test/java/com/plzgraduate/myongjigraduatebe/lecture/application/service/search/SearchLectureServiceTest.java index ae4f914c..390c3ee9 100644 --- a/src/test/java/com/plzgraduate/myongjigraduatebe/lecture/application/service/search/SearchLectureServiceTest.java +++ b/src/test/java/com/plzgraduate/myongjigraduatebe/lecture/application/service/search/SearchLectureServiceTest.java @@ -5,6 +5,7 @@ import static org.mockito.BDDMockito.given; import java.util.List; +import java.util.Set; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -15,37 +16,50 @@ import com.plzgraduate.myongjigraduatebe.lecture.application.port.SearchLecturePort; import com.plzgraduate.myongjigraduatebe.lecture.application.service.SearchLectureService; +import com.plzgraduate.myongjigraduatebe.lecture.application.usecase.dto.SearchedLectureDto; import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture; +import com.plzgraduate.myongjigraduatebe.takenlecture.application.usecase.find.FindTakenLectureUseCase; +import com.plzgraduate.myongjigraduatebe.takenlecture.domain.model.TakenLecture; +import com.plzgraduate.myongjigraduatebe.takenlecture.domain.model.TakenLectureInventory; @ExtendWith(MockitoExtension.class) class SearchLectureServiceTest { @Mock private SearchLecturePort searchLecturePort; + @Mock + private FindTakenLectureUseCase findTakenLectureUseCase; @InjectMocks private SearchLectureService searchLectureService; @DisplayName("과목을 검색한다.") @Test void searchLectures() { + Long userId = 1L; String type = "name"; String keyword = "기초"; - List lectures = List.of( - createLecture(1L, "code1", "기초웹프로그래밍", 3, 0), - createLecture(2L, "code2", "앱과웹기초", 2, 1) + Lecture takenLecture = createLecture(1L, "code1", "기초웹프로그래밍", 3, 0); + Lecture nonTakenLecture = createLecture(2L, "code2", "앱과웹기초", 2, 1); + List lectures = List.of(takenLecture, nonTakenLecture); + TakenLectureInventory takenLectureInventory = TakenLectureInventory.from( + Set.of(TakenLecture.builder() + .lecture(Lecture.from("code1")) + .build()) ); given(searchLecturePort.searchLectureByNameOrCode("name", "기초")) .willReturn(lectures); + given(findTakenLectureUseCase.findTakenLectures(userId)).willReturn(takenLectureInventory); //when - List searchedLectures = searchLectureService.searchLectures(type, keyword); + List searchedLectureDtos = searchLectureService.searchLectures(userId, type, + keyword); //then - assertThat(searchedLectures) + assertThat(searchedLectureDtos) .hasSize(2) - .extracting("id", "lectureCode", "name", "credit", "isRevoked") + .extracting("addable", "lecture") .containsExactlyInAnyOrder( - tuple(1L, "code1", "기초웹프로그래밍", 3, 0), - tuple(2L, "code2", "앱과웹기초", 2, 1) + tuple(true, takenLecture), + tuple(false, nonTakenLecture) ); }