From 8f0472ff414fc5637c7296dce6ca33a158d5b37b Mon Sep 17 00:00:00 2001 From: Jihwan Jung Date: Thu, 25 Apr 2024 16:21:02 +0900 Subject: [PATCH] =?UTF-8?q?#25=20feat:=20quiz=20=EC=83=9D=EC=84=B1,=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/capstone/club/domain/Club.java | 5 ++ .../capstone/member/domain/Member.java | 7 +- .../quiz/controller/QuizController.java | 34 +++++++ .../controller/dto/CreateQuizRequest.java | 14 +++ .../quiz/controller/dto/QuizResponse.java | 25 ++++++ .../project/capstone/quiz/domain/Quiz.java | 2 + .../capstone/quiz/domain/QuizRepository.java | 9 ++ .../capstone/quiz/domain/QuizType.java | 6 +- .../quiz/exception/QuizException.java | 10 +++ .../quiz/exception/QuizExceptionType.java | 32 +++++++ .../capstone/quiz/service/QuizService.java | 89 +++++++++++++++++++ 11 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 backend/src/main/java/com/project/capstone/quiz/controller/QuizController.java create mode 100644 backend/src/main/java/com/project/capstone/quiz/controller/dto/CreateQuizRequest.java create mode 100644 backend/src/main/java/com/project/capstone/quiz/controller/dto/QuizResponse.java create mode 100644 backend/src/main/java/com/project/capstone/quiz/domain/QuizRepository.java create mode 100644 backend/src/main/java/com/project/capstone/quiz/exception/QuizException.java create mode 100644 backend/src/main/java/com/project/capstone/quiz/exception/QuizExceptionType.java create mode 100644 backend/src/main/java/com/project/capstone/quiz/service/QuizService.java diff --git a/backend/src/main/java/com/project/capstone/club/domain/Club.java b/backend/src/main/java/com/project/capstone/club/domain/Club.java index fc63b9b0d6..deb76b3cd4 100644 --- a/backend/src/main/java/com/project/capstone/club/domain/Club.java +++ b/backend/src/main/java/com/project/capstone/club/domain/Club.java @@ -6,6 +6,7 @@ import com.project.capstone.memberclub.domain.MemberClub; import com.project.capstone.content.domain.Content; import com.project.capstone.post.domain.Post; +import com.project.capstone.quiz.domain.Quiz; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; @@ -54,6 +55,10 @@ public class Club { @OneToMany(mappedBy = "club") private List contents = new ArrayList<>(); + @JsonManagedReference + @OneToMany(mappedBy = "club") + private List quizzes = new ArrayList<>(); + @ManyToOne private Book book; diff --git a/backend/src/main/java/com/project/capstone/member/domain/Member.java b/backend/src/main/java/com/project/capstone/member/domain/Member.java index 74fa70fe69..5f926c1ed1 100644 --- a/backend/src/main/java/com/project/capstone/member/domain/Member.java +++ b/backend/src/main/java/com/project/capstone/member/domain/Member.java @@ -6,6 +6,7 @@ import com.project.capstone.memberclub.domain.MemberClub; import com.project.capstone.content.domain.Content; import com.project.capstone.post.domain.Post; +import com.project.capstone.quiz.domain.Quiz; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; @@ -55,8 +56,12 @@ public class Member { @OneToMany(mappedBy = "member") private List contents = new ArrayList<>(); + @JsonManagedReference + @OneToMany(mappedBy = "member") + private List quizzes = new ArrayList<>(); + public Member(SignupRequest request) { this(null, request.email(), request.name(), request.age(), request.gender(), null, - new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } } diff --git a/backend/src/main/java/com/project/capstone/quiz/controller/QuizController.java b/backend/src/main/java/com/project/capstone/quiz/controller/QuizController.java new file mode 100644 index 0000000000..d8c9b0bd36 --- /dev/null +++ b/backend/src/main/java/com/project/capstone/quiz/controller/QuizController.java @@ -0,0 +1,34 @@ +package com.project.capstone.quiz.controller; + + +import com.project.capstone.auth.domain.PrincipalDetails; +import com.project.capstone.quiz.controller.dto.CreateQuizRequest; +import com.project.capstone.quiz.controller.dto.QuizResponse; +import com.project.capstone.quiz.service.QuizService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/quiz") +public class QuizController { + private final QuizService quizService; + + // 퀴즈 생성 + @PostMapping("/create") + public ResponseEntity createQuiz(@AuthenticationPrincipal PrincipalDetails details, + @RequestBody CreateQuizRequest request, + @RequestParam Long bookId, @RequestParam(required = false) Long clubId) { + quizService.createQuiz(details.getUserId(), request, bookId, clubId); + return ResponseEntity.ok().body("퀴즈 생성 완료"); + } + + // 단건 조회 + @GetMapping("/{id}") + public ResponseEntity getQuiz(@PathVariable Long id) { + QuizResponse quizResponse = quizService.getQuiz(id); + return ResponseEntity.ok().body(quizResponse); + } +} diff --git a/backend/src/main/java/com/project/capstone/quiz/controller/dto/CreateQuizRequest.java b/backend/src/main/java/com/project/capstone/quiz/controller/dto/CreateQuizRequest.java new file mode 100644 index 0000000000..15b091bc78 --- /dev/null +++ b/backend/src/main/java/com/project/capstone/quiz/controller/dto/CreateQuizRequest.java @@ -0,0 +1,14 @@ +package com.project.capstone.quiz.controller.dto; + +import com.project.capstone.quiz.domain.QuizType; + +public record CreateQuizRequest( + QuizType type, + String description, + String answer, + String example1, + String example2, + String example3, + String example4 +) { +} diff --git a/backend/src/main/java/com/project/capstone/quiz/controller/dto/QuizResponse.java b/backend/src/main/java/com/project/capstone/quiz/controller/dto/QuizResponse.java new file mode 100644 index 0000000000..13a7484279 --- /dev/null +++ b/backend/src/main/java/com/project/capstone/quiz/controller/dto/QuizResponse.java @@ -0,0 +1,25 @@ +package com.project.capstone.quiz.controller.dto; + +import com.project.capstone.quiz.domain.Quiz; +import com.project.capstone.quiz.domain.QuizType; + +import java.util.UUID; + +public record QuizResponse ( + Long id, + UUID memberId, + Long bookId, + Long clubId, + QuizType type, + String description, + String answer, + String example1, + String example2, + String example3, + String example4 +) { + public QuizResponse(Quiz quiz) { + this(quiz.getId(), quiz.getMember().getId(), quiz.getBook().getId(), quiz.getClub() == null ? null : quiz.getClub().getId(), quiz.getType(), + quiz.getDescription(), quiz.getAnswer(), quiz.getExample1(), quiz.getExample2(), quiz.getExample3(), quiz.getExample4()); + } +} diff --git a/backend/src/main/java/com/project/capstone/quiz/domain/Quiz.java b/backend/src/main/java/com/project/capstone/quiz/domain/Quiz.java index 912b2cee1d..d3849b80e8 100644 --- a/backend/src/main/java/com/project/capstone/quiz/domain/Quiz.java +++ b/backend/src/main/java/com/project/capstone/quiz/domain/Quiz.java @@ -21,6 +21,8 @@ public class Quiz { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @Enumerated(EnumType.STRING) private QuizType type; private String description; private String answer; diff --git a/backend/src/main/java/com/project/capstone/quiz/domain/QuizRepository.java b/backend/src/main/java/com/project/capstone/quiz/domain/QuizRepository.java new file mode 100644 index 0000000000..7d0568b8bc --- /dev/null +++ b/backend/src/main/java/com/project/capstone/quiz/domain/QuizRepository.java @@ -0,0 +1,9 @@ +package com.project.capstone.quiz.domain; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface QuizRepository extends JpaRepository { + Optional findQuizById(Long id); +} diff --git a/backend/src/main/java/com/project/capstone/quiz/domain/QuizType.java b/backend/src/main/java/com/project/capstone/quiz/domain/QuizType.java index a484b6155c..3b7ea7f7d6 100644 --- a/backend/src/main/java/com/project/capstone/quiz/domain/QuizType.java +++ b/backend/src/main/java/com/project/capstone/quiz/domain/QuizType.java @@ -8,14 +8,14 @@ @AllArgsConstructor @Getter public enum QuizType { - Multiple_Choice("객관식"), - Short_Answer("단답식"), + MultipleChoice("객관식"), + ShortAnswer("단답식"), OX("OX") ; private final String type; - @JsonCreator + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public static QuizType from(String type) { for (QuizType quizType : QuizType.values()) { if (quizType.getType().equals(type)) { diff --git a/backend/src/main/java/com/project/capstone/quiz/exception/QuizException.java b/backend/src/main/java/com/project/capstone/quiz/exception/QuizException.java new file mode 100644 index 0000000000..c5fc2a5d13 --- /dev/null +++ b/backend/src/main/java/com/project/capstone/quiz/exception/QuizException.java @@ -0,0 +1,10 @@ +package com.project.capstone.quiz.exception; + +import com.project.capstone.common.exception.BaseException; +import com.project.capstone.common.exception.ExceptionType; + +public class QuizException extends BaseException { + public QuizException(ExceptionType exceptionType) { + super(exceptionType); + } +} diff --git a/backend/src/main/java/com/project/capstone/quiz/exception/QuizExceptionType.java b/backend/src/main/java/com/project/capstone/quiz/exception/QuizExceptionType.java new file mode 100644 index 0000000000..96b7fd32b2 --- /dev/null +++ b/backend/src/main/java/com/project/capstone/quiz/exception/QuizExceptionType.java @@ -0,0 +1,32 @@ +package com.project.capstone.quiz.exception; + +import com.project.capstone.common.exception.ExceptionType; +import lombok.AllArgsConstructor; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.NOT_FOUND; + +@AllArgsConstructor +public enum QuizExceptionType implements ExceptionType { + QUIZ_NOT_FOUND(NOT_FOUND, 701, "해당 퀴즈를 찾을 수 없습니다.") + ; + + private final HttpStatus status; + private final int exceptionCode; + private final String message; + + @Override + public HttpStatus httpStatus() { + return status; + } + + @Override + public int exceptionCode() { + return exceptionCode; + } + + @Override + public String message() { + return message; + } +} diff --git a/backend/src/main/java/com/project/capstone/quiz/service/QuizService.java b/backend/src/main/java/com/project/capstone/quiz/service/QuizService.java new file mode 100644 index 0000000000..4579b9d9cd --- /dev/null +++ b/backend/src/main/java/com/project/capstone/quiz/service/QuizService.java @@ -0,0 +1,89 @@ +package com.project.capstone.quiz.service; + +import com.project.capstone.book.domain.Book; +import com.project.capstone.book.domain.BookRepository; +import com.project.capstone.book.exception.BookException; +import com.project.capstone.book.exception.BookExceptionType; +import com.project.capstone.club.domain.Club; +import com.project.capstone.club.domain.ClubRepository; +import com.project.capstone.club.exception.ClubException; +import com.project.capstone.club.exception.ClubExceptionType; +import com.project.capstone.member.domain.Member; +import com.project.capstone.member.domain.MemberRepository; +import com.project.capstone.member.exception.MemberException; +import com.project.capstone.member.exception.MemberExceptionType; +import com.project.capstone.quiz.controller.dto.CreateQuizRequest; +import com.project.capstone.quiz.controller.dto.QuizResponse; +import com.project.capstone.quiz.domain.Quiz; +import com.project.capstone.quiz.domain.QuizRepository; +import com.project.capstone.quiz.exception.QuizException; +import com.project.capstone.quiz.exception.QuizExceptionType; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +import static com.project.capstone.book.exception.BookExceptionType.BOOK_NOT_FOUND; +import static com.project.capstone.club.exception.ClubExceptionType.CLUB_NOT_FOUND; +import static com.project.capstone.member.exception.MemberExceptionType.MEMBER_NOT_FOUND; +import static com.project.capstone.quiz.exception.QuizExceptionType.QUIZ_NOT_FOUND; + +@RequiredArgsConstructor +@Service +@Slf4j +public class QuizService { + + private final QuizRepository quizRepository; + private final MemberRepository memberRepository; + private final BookRepository bookRepository; + private final ClubRepository clubRepository; + + public void createQuiz(String userId, CreateQuizRequest request, Long bookId, Long clubId) { + Member member = memberRepository.findMemberById(UUID.fromString(userId)).orElseThrow( + () -> new MemberException(MEMBER_NOT_FOUND) + ); + + Book book = bookRepository.findBookById(bookId).orElseThrow( + () -> new BookException(BOOK_NOT_FOUND) + ); + Club club; + if (clubId == null) { + club = null; + } + else { + club = clubRepository.findClubById(clubId).orElseThrow( + ()-> new ClubException(CLUB_NOT_FOUND) + ); + } + + Quiz saved = quizRepository.save( + Quiz.builder() + .type(request.type()) + .description(request.description()) + .answer(request.answer()) + .example1(request.example1()) + .example2(request.example2()) + .example3(request.example3()) + .example4(request.example4()) + .member(member) + .book(book) + .club(club) + .build() + ); + + member.getQuizzes().add(saved); + book.getQuizzes().add(saved); + if (club != null) { + club.getQuizzes().add(saved); + } + } + + + public QuizResponse getQuiz(Long id) { + Quiz quiz = quizRepository.findQuizById(id).orElseThrow( + () -> new QuizException(QUIZ_NOT_FOUND) + ); + return new QuizResponse(quiz); + } +}