Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import GDGoC.team_24.domain.quiz.converter.QuizConverter;
import GDGoC.team_24.domain.quiz.domain.Quiz;
import GDGoC.team_24.domain.quiz.dto.CustomPage;
import GDGoC.team_24.domain.quiz.dto.QuizRequestDto;
import GDGoC.team_24.domain.quiz.dto.QuizResponseDto;
import GDGoC.team_24.domain.quiz.service.QuizService;
Expand Down Expand Up @@ -38,9 +39,9 @@ public ApiResponse<Boolean> answerQuiz(@RequestBody QuizRequestDto.answerQuiz re
}


@Operation(summary = "퀴즈 조회 API", description = "지금까지 푼 퀴즈를 페이지네이션으로 조회합니다.")
@Operation(summary = "풀었던 퀴즈 조회 API", description = "지금까지 푼 퀴즈를 페이지네이션으로 조회합니다.")
@GetMapping("/{userId}/answerlist")
public ApiResponse<Page<QuizResponseDto.quizList>> listQuiz(
public ApiResponse<CustomPage<QuizResponseDto.quizList>> listQuiz(
@PathVariable Long userId,
Pageable pageable
) {
Expand All @@ -54,9 +55,9 @@ public ApiResponse<Boolean> noAnswerQuiz (@PathVariable Long userId){
return ApiResponse.onSuccess(quizService.noAnswerQuiz(userId));
}

@Operation(summary = "안푼 퀴즈 조회 API", description = "지금까지 안푼 퀴즈를 페이지네이션으로 조회합니다.")
@Operation(summary = "안 푼 퀴즈 조회 API", description = "다시 풀 수 있는 퀴즈만 반환합니다.")
@GetMapping("/{userId}/noanswerlist")
public ApiResponse<Page<QuizResponseDto.quizList>> listNoQuiz(
public ApiResponse<CustomPage<QuizResponseDto.quizList>> listNoQuiz(
@PathVariable Long userId,
Pageable pageable
) {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/GDGoC/team_24/domain/quiz/domain/Quiz.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;

@Entity
@Getter
@Setter
Expand All @@ -27,6 +29,8 @@ public class Quiz extends BaseEntity {

private Long quizAnswer; //퀴즈 답변

private LocalDateTime nextRetryTime;

@ManyToOne(fetch = FetchType.LAZY)
private User user;

Expand Down
19 changes: 19 additions & 0 deletions src/main/java/GDGoC/team_24/domain/quiz/dto/CustomPage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package GDGoC.team_24.domain.quiz.dto;

import lombok.*;
import java.util.List;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CustomPage<T> {
private List<T> content; // 페이징된 실제 데이터
private int pageNumber; // 현재 페이지 번호
private int pageSize; // 페이지 크기
private long totalElements; // 전체 요소 수
private int totalPages; // 전체 페이지 수
private boolean isLast; // 마지막 페이지 여부
private boolean isFirst; // 첫 번째 페이지 여부
}
16 changes: 7 additions & 9 deletions src/main/java/GDGoC/team_24/domain/quiz/dto/QuizResponseDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,16 @@ public static class QuizResponseDTO{
private LocalDateTime createdAt;
}

@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class quizList {
private Long id;
private String question; // 퀴즈
private List<QuizOptionDto> options; // 선택지 목록
private Long answer; // 정답 번호
private Long quizAnswer; // 유저의 답변 번호
private boolean isCorrect; // 맞/틀 여부
private String question;
private List<QuizOptionDto> options;
private Long answer;
private Long quizAnswer;
private boolean isCorrect;
private LocalDateTime solvedAt; // 푼 날짜 추가
}

// 선택지 DTO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,31 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.Optional;
import java.util.List;

@Repository
public interface QuizRepository extends JpaRepository<Quiz, Long> {
Page<Quiz> findByUserAndIsCompleted(User user, Boolean isCompleted, Pageable pageable);
List<Quiz> findByUser (User user);
boolean existsByUserAndIsCompleted(User user, Boolean isCompleted);
@Query("SELECT q FROM Quiz q " +
"WHERE q.user = :user AND q.isCompleted = true")
Page<Quiz> findSolvedQuizzes(@Param("user") User user, Pageable pageable);
@Query("SELECT q FROM Quiz q " +
"WHERE q.user = :user " +
"AND q.isCompleted = false " +
"AND (q.nextRetryTime IS NULL OR q.nextRetryTime <= CURRENT_TIMESTAMP)")
Page<Quiz> findUnsolvedQuizzes(@Param("user") User user, Pageable pageable);
@Query("SELECT CASE WHEN COUNT(q) > 0 THEN TRUE ELSE FALSE END " +
"FROM Quiz q WHERE q.user = :user AND q.isCompleted = :isCompleted " +
"AND (q.nextRetryTime IS NULL OR q.nextRetryTime <= CURRENT_TIMESTAMP)")
boolean existsByUserAndIsCompletedAndNextRetryTimeBefore(
@Param("user") User user,
@Param("isCompleted") Boolean isCompleted);

}
113 changes: 69 additions & 44 deletions src/main/java/GDGoC/team_24/domain/quiz/service/QuizService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import GDGoC.team_24.domain.family.repository.FamilyRepository;
import GDGoC.team_24.domain.quiz.domain.Quiz;
import GDGoC.team_24.domain.quiz.domain.QuizOption;
import GDGoC.team_24.domain.quiz.dto.CustomPage;
import GDGoC.team_24.domain.quiz.dto.QuizRequestDto;
import GDGoC.team_24.domain.quiz.dto.QuizResponseDto;
import GDGoC.team_24.domain.quiz.repository.QuizOptionRepository;
Expand All @@ -17,6 +18,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -30,20 +32,17 @@ public class QuizService {
private final UserRepository userRepository;

public Quiz makeQuiz(QuizRequestDto.createQuiz request, Long familyId) {
// 퀴즈 생성
Family family = familyRepository.findById(familyId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

User user = family.getUser(); // 중복 조회 제거

Quiz newQuiz = new Quiz();
newQuiz.setQuestion(request.getQuestion());
newQuiz.setAnswer(request.getAnswer());
newQuiz.setCompleted(false);
newQuiz.setUser(user); // User 설정

// Family 및 User 조회
Family family = familyRepository.findById(familyId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));
User user = userRepository.findById(family.getUser().getId())
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));
newQuiz.setUser(user);

// 퀴즈 저장
Quiz savedQuiz = quizRepository.save(newQuiz);

// 선택지 저장
Expand All @@ -60,66 +59,92 @@ public Quiz makeQuiz(QuizRequestDto.createQuiz request, Long familyId) {
return savedQuiz;
}


public Boolean answerQuiz(QuizRequestDto.answerQuiz request) {
Quiz quiz = quizRepository.findById(request.getQuizId())
.orElseThrow(() -> new GeneralException(ErrorStatus.QUIZ_NOT_FOUND));

// 정답 비교
// 객관식 정답 비교
boolean isCorrect = quiz.getAnswer().equals(request.getAnswer());
quiz.setCorrect(isCorrect);
quiz.setCompleted(true); // 퀴즈 완료 상태로 변경
quiz.setQuizAnswer(request.getAnswer()); // 사용자 답변 저장
quizRepository.save(quiz);
quiz.setCompleted(true); // 퀴즈 완료 처리
quiz.setQuizAnswer(request.getAnswer()); // 사용자 입력 저장

if (!isCorrect) {
// 틀린 경우: 하루 뒤에 다시 풀 수 있도록 설정
quiz.setNextRetryTime(LocalDateTime.now().plusDays(1));
} else {
// 맞은 경우: 7일 뒤에 다시 풀 수 있도록 설정
quiz.setNextRetryTime(LocalDateTime.now().plusDays(7));
}

quizRepository.save(quiz);
return isCorrect;
}



public Page<QuizResponseDto.quizList> readAllQuizzes(Long userId, Boolean solve, Pageable pageable) {
// User 조회
public CustomPage<QuizResponseDto.quizList> readAllQuizzes(Long userId, Boolean solve, Pageable pageable) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

// User가 푼/안 푼 퀴즈를 페이지네이션으로 조회
Page<Quiz> quizzes = quizRepository.findByUserAndIsCompleted(user, solve, pageable);
Page<Quiz> quizzes = solve
? quizRepository.findSolvedQuizzes(user, pageable) // 풀었던 퀴즈 조회
: quizRepository.findUnsolvedQuizzes(user, pageable); // 안 푼 퀴즈 조회

if (quizzes.isEmpty()) {
throw new GeneralException(ErrorStatus.QUIZ_NOT_FOUND);
}

// 각 퀴즈와 해당 선택지 정보를 DTO로 변환
return quizzes.map(quiz -> {
// 선택지 조회
List<QuizOption> options = quizOptionRepository.findByQuiz(quiz);

// 선택지를 DTO로 변환
List<QuizResponseDto.QuizOptionDto> optionDtos = options.stream()
.map(option -> QuizResponseDto.QuizOptionDto.builder()
.number(option.getNumber())
.text(option.getText())
.build())
.toList();

// 퀴즈와 선택지 정보를 DTO로 변환
return QuizResponseDto.quizList.builder()
.id(quiz.getId())
.question(quiz.getQuestion())
.options(optionDtos)
.answer(quiz.getAnswer())
.quizAnswer(quiz.getQuizAnswer())
.isCorrect(quiz.isCorrect())
.build();
});
// `Page` 객체를 `CustomPage`로 변환
List<QuizResponseDto.quizList> quizList = quizzes.getContent().stream()
.map(quiz -> {
// 각 퀴즈의 선택지만 조회
List<QuizOption> options = quizOptionRepository.findByQuiz(quiz);

// 선택지 DTO로 변환
List<QuizResponseDto.QuizOptionDto> optionDtos = options.stream()
.map(option -> QuizResponseDto.QuizOptionDto.builder()
.number(option.getNumber())
.text(option.getText())
.build())
.toList();

// `solvedAt` 값을 퀴즈 상태에 따라 설정
LocalDateTime solvedAt = quiz.isCompleted() ? quiz.getUpdatedAt() : null;

// 퀴즈 DTO로 변환
return QuizResponseDto.quizList.builder()
.id(quiz.getId())
.question(quiz.getQuestion())
.options(optionDtos)
.answer(quiz.getAnswer())
.quizAnswer(quiz.getQuizAnswer())
.isCorrect(quiz.isCorrect())
.solvedAt(solvedAt) // 상태에 따라 `solvedAt` 값 설정
.build();
})
.toList();

return CustomPage.<QuizResponseDto.quizList>builder()
.content(quizList)
.pageNumber(quizzes.getNumber())
.pageSize(quizzes.getSize())
.totalElements(quizzes.getTotalElements())
.totalPages(quizzes.getTotalPages())
.isLast(quizzes.isLast())
.isFirst(quizzes.isFirst())
.build();
}



public Boolean noAnswerQuiz(Long userId) {
// User 조회
User user = userRepository.findById(userId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

// 안 푼 퀴즈 존재 여부 확인
return quizRepository.existsByUserAndIsCompleted(user, false);
// 다음 풀 수 있는 퀴즈 존재 여부 확인
return quizRepository.existsByUserAndIsCompletedAndNextRetryTimeBefore(user, false);
}


}
Loading