diff --git a/backend/src/acceptanceTest/java/wooteco/prolog/steps/QuizStepDefinitions.java b/backend/src/acceptanceTest/java/wooteco/prolog/steps/QuizStepDefinitions.java index 8f9dbb121..a5ad8fa6b 100644 --- a/backend/src/acceptanceTest/java/wooteco/prolog/steps/QuizStepDefinitions.java +++ b/backend/src/acceptanceTest/java/wooteco/prolog/steps/QuizStepDefinitions.java @@ -13,13 +13,13 @@ public class QuizStepDefinitions extends AcceptanceSteps { //:todo keyword 와 합친 이후에 작성 예정 @When("퀴즈를 생성하면") - public void 퀴즈를생성하면(Long sessionId, Long keywordId) { + public void 퀴즈를_생성하면(Long sessionId, Long keywordId) { final QuizRequest 수달이_만든_테스트_퀴즈 = new QuizRequest("수달이 만든 테스트 퀴즈"); context.invokeHttpPost("", 수달이_만든_테스트_퀴즈); } @Then("퀴즈가 추가된다") - public void 강의를추가된다() { + public void 퀴즈가_추가된다() { assertThat(context.response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); } } diff --git a/backend/src/documentation/java/wooteco/prolog/docu/QuizDocumentation.java b/backend/src/documentation/java/wooteco/prolog/docu/QuizDocumentation.java index 36dd7141b..833de174a 100644 --- a/backend/src/documentation/java/wooteco/prolog/docu/QuizDocumentation.java +++ b/backend/src/documentation/java/wooteco/prolog/docu/QuizDocumentation.java @@ -39,7 +39,7 @@ public class QuizDocumentation extends NewDocumentation { @Test void Keyword별_Quiz_목록_조회() { - given(quizService.findQuizzesByKeywordId(any())).willReturn(QUIZZES_RESPONSE); + given(quizService.findQuizzesByKeywordId(any(), any())).willReturn(QUIZZES_RESPONSE); given .contentType(MediaType.APPLICATION_JSON_VALUE) @@ -82,6 +82,7 @@ public class QuizDocumentation extends NewDocumentation { ); private static final QuizzesResponse QUIZZES_RESPONSE = new QuizzesResponse(1L, - Arrays.asList(new QuizResponse(1L, "브라운을 위해 낸 퀴즈"), new QuizResponse(1L, "포코를 위해 낸 퀴즈"))); + Arrays.asList(new QuizResponse(1L, "브라운을 위해 낸 퀴즈", true), + new QuizResponse(1L, "포코를 위해 낸 퀴즈", false))); } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/QuizService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/QuizService.java index e1ab45dcc..a4bce8137 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/QuizService.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/QuizService.java @@ -4,6 +4,7 @@ import static wooteco.prolog.common.exception.BadRequestCode.ROADMAP_QUIZ_NOT_FOUND_EXCEPTION; import java.util.List; +import java.util.stream.Collectors; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -13,6 +14,7 @@ import wooteco.prolog.roadmap.application.dto.QuizzesResponse; import wooteco.prolog.roadmap.domain.Keyword; import wooteco.prolog.roadmap.domain.Quiz; +import wooteco.prolog.roadmap.domain.repository.EssayAnswerRepository; import wooteco.prolog.roadmap.domain.repository.KeywordRepository; import wooteco.prolog.roadmap.domain.repository.QuizRepository; @@ -23,6 +25,7 @@ public class QuizService { private final KeywordRepository keywordRepository; private final QuizRepository quizRepository; + private final EssayAnswerRepository essayAnswerRepository; @Transactional public Long createQuiz(Long keywordId, QuizRequest quizRequest) { @@ -32,9 +35,20 @@ public Long createQuiz(Long keywordId, QuizRequest quizRequest) { return quiz.getId(); } - public QuizzesResponse findQuizzesByKeywordId(Long keywordId) { + public QuizzesResponse findQuizzesByKeywordId(Long keywordId, Long memberId) { final List quizzes = quizRepository.findFetchQuizByKeywordId(keywordId); - return QuizzesResponse.of(keywordId, quizzes); + final List quizResponses = quizzes.stream() + .map(quiz -> QuizResponse.of(quiz, isLearning(memberId, quiz.getId()))) + .collect(Collectors.toList()); + return QuizzesResponse.of(keywordId, quizResponses); + } + + private boolean isLearning(Long memberId, Long quizId) { + if (memberId == null) { + return false; + } + return essayAnswerRepository.existsByQuizIdAndMemberId(quizId, + memberId); } @Transactional @@ -52,9 +66,9 @@ public void deleteQuiz(Long quizId) { quizRepository.deleteById(quizId); } - public QuizResponse findById(Long quizId) { + public QuizResponse findById(Long quizId, Long memberId) { final Quiz quiz = quizRepository.findById(quizId) .orElseThrow(() -> new BadRequestException(ROADMAP_QUIZ_NOT_FOUND_EXCEPTION)); - return QuizResponse.of(quiz); + return QuizResponse.of(quiz, isLearning(memberId, quizId)); } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerQuizResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerQuizResponse.java new file mode 100644 index 000000000..a4a717bdb --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerQuizResponse.java @@ -0,0 +1,23 @@ +package wooteco.prolog.roadmap.application.dto; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import wooteco.prolog.roadmap.domain.Quiz; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class EssayAnswerQuizResponse { + + private Long quizId; + private String question; + + public EssayAnswerQuizResponse(Long quizId, String question) { + this.quizId = quizId; + this.question = question; + } + + public static EssayAnswerQuizResponse of(Quiz quiz) { + return new EssayAnswerQuizResponse(quiz.getId(), quiz.getQuestion()); + } +} diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerResponse.java index 5cd670803..ecf280e12 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/EssayAnswerResponse.java @@ -12,7 +12,7 @@ public class EssayAnswerResponse { private Long id; - private QuizResponse quiz; + private EssayAnswerQuizResponse quiz; private String answer; private MemberResponse author; private LocalDateTime createdAt; @@ -22,7 +22,7 @@ public static EssayAnswerResponse of(EssayAnswer essayAnswer) { EssayAnswerResponse response = new EssayAnswerResponse(); response.id = essayAnswer.getId(); - response.quiz = QuizResponse.of(essayAnswer.getQuiz()); + response.quiz = EssayAnswerQuizResponse.of(essayAnswer.getQuiz()); response.answer = essayAnswer.getAnswer(); response.author = MemberResponse.of(essayAnswer.getMember()); response.createdAt = essayAnswer.getCreatedAt(); diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/QuizResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/QuizResponse.java index 654366529..c582ca9ae 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/QuizResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/QuizResponse.java @@ -12,13 +12,15 @@ public class QuizResponse { private Long quizId; private String question; + private Boolean isLearning; - public QuizResponse(Long quizId, String question) { + public QuizResponse(Long quizId, String question, boolean isLearning) { this.quizId = quizId; this.question = question; + this.isLearning = isLearning; } - public static QuizResponse of(Quiz quiz) { - return new QuizResponse(quiz.getId(), quiz.getQuestion()); + public static QuizResponse of(Quiz quiz, boolean isLearning) { + return new QuizResponse(quiz.getId(), quiz.getQuestion(), isLearning); } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/QuizzesResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/QuizzesResponse.java index 2f04e8c27..91af1bd29 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/QuizzesResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/QuizzesResponse.java @@ -1,11 +1,9 @@ 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.Quiz; @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @@ -21,9 +19,7 @@ public QuizzesResponse(Long keywordId, this.data = data; } - public static QuizzesResponse of(Long keywordId, List quizzes) { - final List responses = quizzes.stream().map(QuizResponse::of) - .collect(Collectors.toList()); - return new QuizzesResponse(keywordId, responses); + public static QuizzesResponse of(Long keywordId, List quizzes) { + return new QuizzesResponse(keywordId, quizzes); } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerRepository.java index 441dd2198..990a12ca2 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerRepository.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/EssayAnswerRepository.java @@ -3,10 +3,14 @@ import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import wooteco.prolog.roadmap.domain.EssayAnswer; public interface EssayAnswerRepository extends JpaRepository { + @Query("select ea from EssayAnswer ea where ea.quiz.id = :quizId and ea.member.id = :memberId ") + boolean existsByQuizIdAndMemberId(Long quizId, Long memberId); + Optional findByIdAndMemberId(Long id, Long memberId); List findByQuizIdOrderByIdDesc(Long quizId); diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java index bce13ff0f..1aa42fb9a 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/EssayAnswerController.java @@ -1,8 +1,18 @@ package wooteco.prolog.roadmap.ui; +import static java.util.stream.Collectors.toList; + +import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import wooteco.prolog.login.domain.AuthMemberPrincipal; import wooteco.prolog.login.ui.LoginMember; import wooteco.prolog.roadmap.application.EssayAnswerService; @@ -13,10 +23,6 @@ import wooteco.prolog.roadmap.application.dto.QuizResponse; import wooteco.prolog.roadmap.domain.EssayAnswer; -import java.util.List; - -import static java.util.stream.Collectors.toList; - @RestController @RequestMapping public class EssayAnswerController { @@ -61,8 +67,9 @@ public ResponseEntity deleteEssayAnswerById(@PathVariable Long essayAnswer } @GetMapping("/quizzes/{quizId}") - public ResponseEntity findQuizById(@PathVariable Long quizId) { - return ResponseEntity.ok(quizService.findById(quizId)); + public ResponseEntity findQuizById(@PathVariable Long quizId, + @AuthMemberPrincipal LoginMember member) { + return ResponseEntity.ok(quizService.findById(quizId, member.getId())); } @GetMapping("/quizzes/{quizId}/essay-answers") diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/QuizController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/QuizController.java index 41e597e69..a9e99fb3c 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/ui/QuizController.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/QuizController.java @@ -11,6 +11,8 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import wooteco.prolog.login.domain.AuthMemberPrincipal; +import wooteco.prolog.login.ui.LoginMember; import wooteco.prolog.roadmap.application.QuizService; import wooteco.prolog.roadmap.application.dto.QuizRequest; import wooteco.prolog.roadmap.application.dto.QuizResponse; @@ -35,14 +37,16 @@ public ResponseEntity create(@PathVariable Long sessionId, @PathVariable L } @GetMapping("/{quizId}") - public ResponseEntity findQuizById(@PathVariable Long quizId) { - return ResponseEntity.ok(quizService.findById(quizId)); + public ResponseEntity findQuizById(@PathVariable Long quizId, + @AuthMemberPrincipal LoginMember member) { + return ResponseEntity.ok(quizService.findById(quizId, member.getId())); } @GetMapping public ResponseEntity findQuizzesByKeyword(@PathVariable Long sessionId, - @PathVariable Long keywordId) { - return ResponseEntity.ok(quizService.findQuizzesByKeywordId(keywordId)); + @PathVariable Long keywordId, + @AuthMemberPrincipal LoginMember member) { + return ResponseEntity.ok(quizService.findQuizzesByKeywordId(keywordId, member.getId())); } @PutMapping("/{quizId}") diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/QuizServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/QuizServiceTest.java index 478d1dee1..92337c395 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/application/QuizServiceTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/QuizServiceTest.java @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.Optional; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -25,6 +26,7 @@ import wooteco.prolog.roadmap.application.dto.QuizzesResponse; import wooteco.prolog.roadmap.domain.Keyword; import wooteco.prolog.roadmap.domain.Quiz; +import wooteco.prolog.roadmap.domain.repository.EssayAnswerRepository; import wooteco.prolog.roadmap.domain.repository.KeywordRepository; import wooteco.prolog.roadmap.domain.repository.QuizRepository; @@ -37,6 +39,9 @@ class QuizServiceTest { @Mock private QuizRepository quizRepository; + @Mock + private EssayAnswerRepository essayAnswerRepository; + @InjectMocks QuizService quizService; @@ -96,7 +101,7 @@ void findQuizzesByKeywordId() { //when final QuizzesResponse quizzesByKeywordId = quizService.findQuizzesByKeywordId( - requestKeywordId); + requestKeywordId, null); //then assertAll( @@ -172,7 +177,7 @@ void findById_fail() { .thenReturn(Optional.empty()); //when,then - assertThatThrownBy(() -> quizService.findById(1L)) + assertThatThrownBy(() -> quizService.findById(1L, null)) .isInstanceOf(BadRequestException.class) .hasMessage(ROADMAP_QUIZ_NOT_FOUND_EXCEPTION.getMessage()); } @@ -187,7 +192,7 @@ void findById() { .thenReturn(Optional.of(new Quiz(findQuizId, null, findQuizQuestion))); //when - final QuizResponse quizResponseById = quizService.findById(1L); + final QuizResponse quizResponseById = quizService.findById(1L, null); //then assertAll( @@ -196,4 +201,62 @@ void findById() { ); } + @DisplayName("quizId로 Quiz를 조회할 때 quiz의 답변 여부가 QuizResponse에 포함된다.") + @Nested + class findQuizzesByKeywordId { + + @DisplayName("조회한 Quiz에 대해 답변을 게시한 적이 있으면 isLearning 값이 true이다.") + @Test + void findById_isLearning_true() { + //given + final long findQuizId = 1L; + final String findQuizQuestion = "question"; + when(essayAnswerRepository.existsByQuizIdAndMemberId(anyLong(), anyLong())) + .thenReturn(true); + when(quizRepository.findById(anyLong())) + .thenReturn(Optional.of(new Quiz(findQuizId, null, findQuizQuestion))); + + //when + final QuizResponse quizResponseById = quizService.findById(1L, 1L); + + //then + assertThat(quizResponseById.getIsLearning()).isTrue(); + } + + @DisplayName("조회한 Quiz에 대해 답변을 게시한 적이 없으면 isLearning 값이 false이다.") + @Test + void findById_isLearning_false() { + //given + final long findQuizId = 1L; + final String findQuizQuestion = "question"; + when(essayAnswerRepository.existsByQuizIdAndMemberId(anyLong(), anyLong())) + .thenReturn(false); + when(quizRepository.findById(anyLong())) + .thenReturn(Optional.of(new Quiz(findQuizId, null, findQuizQuestion))); + + //when + final QuizResponse quizResponseById = quizService.findById(1L, 1L); + + //then + assertThat(quizResponseById.getIsLearning()).isFalse(); + } + + @DisplayName("로그인되지 않은 사용자이면 isLearning 값이 false이다.") + @Test + void findById_isLearning_false_anonymous() { + //given + final long findQuizId = 1L; + final String findQuizQuestion = "question"; + when(quizRepository.findById(anyLong())) + .thenReturn(Optional.of(new Quiz(findQuizId, null, findQuizQuestion))); + + //when + final QuizResponse quizResponseById = quizService.findById(1L, null); + + //then + assertThat(quizResponseById.getIsLearning()).isFalse(); + } + + } + }