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 @@ -4,9 +4,13 @@
import darkoverload.itzip.feature.csQuiz.controller.request.QuizCreatedRequest;
import darkoverload.itzip.feature.csQuiz.controller.request.QuizPointRequest;
import darkoverload.itzip.feature.csQuiz.controller.response.QuizCategoryDetailResponse;
import darkoverload.itzip.feature.csQuiz.controller.response.QuizRankingResponse;
import darkoverload.itzip.feature.csQuiz.controller.response.QuizRankingResponses;
import darkoverload.itzip.feature.csQuiz.controller.response.QuizScoreResponse;
import darkoverload.itzip.feature.csQuiz.entity.QuizCategory;
import darkoverload.itzip.feature.csQuiz.entity.UserQuizStatus;
import darkoverload.itzip.feature.csQuiz.service.QuizService;
import darkoverload.itzip.feature.csQuiz.service.sub.quizscore.QuizScoreService;
import darkoverload.itzip.feature.jwt.infrastructure.CustomUserDetails;
import darkoverload.itzip.global.config.response.code.CommonExceptionCode;
import darkoverload.itzip.global.config.response.code.CommonResponseCode;
Expand All @@ -18,6 +22,7 @@
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.RequestBody;
import lombok.RequiredArgsConstructor;
Expand All @@ -31,7 +36,10 @@
@RequestMapping("cs-quiz")
@RequiredArgsConstructor
public class CsQuizController {

private final QuizService quizService;
private final QuizScoreService quizScoreService;

/**
* 주어진 카테고리 ID에 해당하는 카테고리 정보를 조회하는 메서드
*
Expand Down Expand Up @@ -111,4 +119,39 @@ public String createQuiz(
quizService.createQuiz(quizCreatedRequest, customUserDetails);
return "문제를 생성했습니다.";
}
}

@Operation(
summary = "퀴즈 점수 조회",
description = "현재 인증된 사용자의 퀴즈 점수를 조회하여 반환합니다."
)
@ResponseCodeAnnotation(CommonResponseCode.SUCCESS)
@ExceptionCodeAnnotations({
CommonExceptionCode.NOT_FOUND_USER,
CommonExceptionCode.NOT_FOUND_QUIZ_SCORE
})
@GetMapping("/score")
public QuizScoreResponse getScore(@AuthenticationPrincipal final CustomUserDetails customUserDetails) {
return QuizScoreResponse.from(
quizScoreService.findQuizScoreById(customUserDetails)
);
}

@Operation(
summary = "퀴즈 랭킹 조회",
description = "퀴즈 점수를 기준으로 상위 6명의 랭킹 정보를 조회하여 반환합니다."
)
@ResponseCodeAnnotation(CommonResponseCode.SUCCESS)
@ExceptionCodeAnnotations({
CommonExceptionCode.NOT_FOUND_QUIZ_SCORE_RANKING,
CommonExceptionCode.QUIZ_PROCESSING_ERROR
})
@GetMapping("/rankings")
public QuizRankingResponses getRankings() {
return QuizRankingResponses.of(
quizScoreService.getTop6Ranking().getRankings().stream()
.map(QuizRankingResponse::from)
.toList()
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import darkoverload.itzip.feature.csQuiz.entity.SortBy;
import darkoverload.itzip.feature.csQuiz.controller.response.QuizDetailResponse;
import darkoverload.itzip.feature.csQuiz.service.QuizService;
import darkoverload.itzip.feature.jwt.infrastructure.CustomUserDetails;
import darkoverload.itzip.global.config.response.code.CommonExceptionCode;
import darkoverload.itzip.global.config.response.code.CommonResponseCode;
import darkoverload.itzip.global.config.swagger.ExceptionCodeAnnotations;
Expand All @@ -14,9 +15,9 @@
import lombok.RequiredArgsConstructor;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.PagedModel;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;


//퀴즈(복수) 관련 엔드포인트 모음
@Tag(name = "Computer Science Quiz", description = "퀴즈(복수) 관련 엔드포인트 모음")
@RestController
Expand All @@ -31,7 +32,6 @@ public class CsQuizzesController {
* @param difficulty 퀴즈 난이도 (선택 사항)
* @param categoryId 카테고리 ID (선택 사항)
* @param sortBy 정렬 기준 (기본값: NEWEST)
* @param userId 사용자 ID (기본값: 0)
* @param inUserSolved 사용자가 푼 퀴즈 포함 여부 (기본값: false)
* @param page 페이지 번호 (기본값: 0)
* @param size 페이지 크기 (기본값: 10)
Expand All @@ -46,19 +46,20 @@ public class CsQuizzesController {
@ExceptionCodeAnnotations({CommonExceptionCode.NOT_FOUND_USER})
@GetMapping("/search")
public PagedModel<EntityModel<QuizDetailResponse>> getFilteredAndSortedQuizzes(
@AuthenticationPrincipal CustomUserDetails userDetails,
@Parameter(description = "퀴즈 난이도 입력칸 1~3") @RequestParam(required = false) Integer difficulty,
@Parameter(description = "카테고리 식별값 입력칸") @RequestParam(required = false) Long categoryId,
@Parameter(description = "NEWEST 새로운 순, OLDEST 오래된 순, RECOMMENED 추천순 아무것도 없으면 새로운순") @RequestParam(required = false, defaultValue = "NEWEST") SortBy sortBy,
@Parameter(description = "사용자 ID") @RequestParam(required = false) Long userId,
@Parameter(description = "사용자가 푼 문제를 포함 하는지 true면 포함 false면 미포함") @RequestParam(required = false, defaultValue = "false") boolean inUserSolved,
@Parameter(description = "문제 페이지 0부터 시작") @RequestParam(defaultValue = "0") int page,
@Parameter(description = "가져올 문제 수") @RequestParam(defaultValue = "10") int size,
@Parameter(description = "검색할 단어") @RequestParam(required = false) String keyword) {
@Parameter(description = "검색할 단어") @RequestParam(required = false) String keyword
) {
QuizQueryRequest quizQueryRequest = QuizQueryRequest.builder()
.difficulty(difficulty)
.categoryId(categoryId)
.sortBy(sortBy)
.userId(userId)
.email(userDetails.getEmail())
.inUserSolved(inUserSolved)
.page(page)
.size(size)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class QuizQueryRequest {
//NEWEST, OLDEST
private SortBy sortBy;
//사용자 ID
private Long userId;
private String email;
//사용자가 푼문제를 포함하는지 ture면 포함 false면 미포함
private boolean inUserSolved;
//문제 페이지 0부터 시작
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package darkoverload.itzip.feature.csQuiz.controller.response;

import darkoverload.itzip.feature.csQuiz.entity.QuizRanking;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@Getter
@Schema(description = "퀴즈 랭킹 응답 DTO로 사용자의 순위, 이름, 그리고 점수를 포함합니다.")
public class QuizRankingResponse {

@Schema(description = "사용자의 순위", example = "1")
private final int rank;

@Schema(description = "사용자 이름", example = "홍길동")
private final String name;

@Schema(description = "퀴즈 점수", example = "100")
private final int score;

public QuizRankingResponse(final int rank, final String name, final int score) {
this.rank = rank;
this.name = name;
this.score = score;
}

public static QuizRankingResponse from(final QuizRanking ranking) {
return new QuizRankingResponse(ranking.getRank(), ranking.getName(), ranking.getScore());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package darkoverload.itzip.feature.csQuiz.controller.response;

import lombok.Getter;

import java.util.Collections;
import java.util.List;

@Getter
public class QuizRankingResponses {

private final List<QuizRankingResponse> responses;

private QuizRankingResponses(final List<QuizRankingResponse> responses) {
this.responses = Collections.unmodifiableList(responses);
}

public static QuizRankingResponses of(final List<QuizRankingResponse> responses) {
return new QuizRankingResponses(responses);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package darkoverload.itzip.feature.csQuiz.controller.response;

import darkoverload.itzip.feature.csQuiz.entity.QuizScore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@Getter
@Schema(description = "퀴즈 점수 응답 DTO로 해당 객체는 사용자의 닉네임과 현재 퀴즈 점수를 포함합니다.")
public class QuizScoreResponse {

@Schema(description = "사용자 닉네임", example = "홍길동")
private final String name;

@Schema(description = "사용자의 퀴즈 점수", example = "120")
private final int score;

public QuizScoreResponse(final String name, final int score) {
this.name = name;
this.score = score;
}

public static QuizScoreResponse from(final QuizScore score) {
return new QuizScoreResponse(score.getUser().getNickname(), score.getScore());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package darkoverload.itzip.feature.csQuiz.entity;

import darkoverload.itzip.global.config.response.code.CommonExceptionCode;
import darkoverload.itzip.global.config.response.exception.RestApiException;
import lombok.Getter;

import java.util.Objects;

@Getter
public class QuizRanking {

private final int rank;
private final String name;
private final int score;

private QuizRanking(final int rank, final String name, final int score) {
checkNameNotNull(name);
this.rank = rank;
this.name = name;
this.score = score;
}

private void checkNameNotNull(final String name) {
if (Objects.isNull(name)) {
throw new RestApiException(CommonExceptionCode.QUIZ_PROCESSING_ERROR);
}
}

public static QuizRanking create(final int rank, final QuizScore score) {
return new QuizRanking(rank, score.getUser().getNickname(), score.getScore());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package darkoverload.itzip.feature.csQuiz.entity;

import java.util.Collections;
import java.util.List;

public class QuizRankings {

private final List<QuizRanking> rankings;

private QuizRankings(final List<QuizRanking> rankings) {
this.rankings = Collections.unmodifiableList(rankings);
}

public static QuizRankings of(final List<QuizRanking> rankings) {
return new QuizRankings(rankings);
}

public List<QuizRanking> getRankings() {
return rankings;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package darkoverload.itzip.feature.csQuiz.entity;

import darkoverload.itzip.feature.user.entity.UserEntity;
import darkoverload.itzip.global.config.response.code.CommonExceptionCode;
import darkoverload.itzip.global.config.response.exception.RestApiException;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.Objects;

@Getter
@Entity
@Table(name = "quiz_scores")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class QuizScore {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@OneToOne
@JoinColumn(name = "user_id")
private UserEntity user;

private Integer score;

private QuizScore(final UserEntity user) {
notNullParameters(user);
this.user = user;
this.score = 0;
}

private void notNullParameters(final UserEntity user) {
if (Objects.isNull(user)) {
throw new RestApiException(CommonExceptionCode.QUIZ_PROCESSING_ERROR);
}
}

public static QuizScore create(final UserEntity user) {
return new QuizScore(user);
}

public void incrementScore(final Integer score) {
this.score += score;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package darkoverload.itzip.feature.csQuiz.repository.quizscore;

import darkoverload.itzip.feature.csQuiz.entity.QuizScore;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface QuizScoreRepository extends JpaRepository<QuizScore, Long> {

Optional<List<QuizScore>> findTop6ByOrderByScoreDesc();

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import darkoverload.itzip.feature.csQuiz.service.sub.quiz.CheckAnswer;
import darkoverload.itzip.feature.csQuiz.service.sub.quiz.CreateQuiz;
import darkoverload.itzip.feature.csQuiz.service.sub.quiz.GivenPointToQuiz;
import darkoverload.itzip.feature.csQuiz.service.sub.quizCategory.FindQuizCategory;
import darkoverload.itzip.feature.csQuiz.service.sub.quizcategory.FindQuizCategory;
import darkoverload.itzip.feature.csQuiz.service.sub.quizzes.FindQiuzQuery;

//컨트롤러가 사용할 service계층 진입점 상속을 통해서 서브 시스템들을 받아온다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@
import darkoverload.itzip.feature.csQuiz.controller.response.QuizDetailResponse;
import darkoverload.itzip.feature.csQuiz.entity.QuizCategory;
import darkoverload.itzip.feature.csQuiz.entity.UserQuizStatus;
import darkoverload.itzip.feature.csQuiz.repository.quiz.QuizRepository;
import darkoverload.itzip.feature.csQuiz.service.sub.quiz.CheckAnswer;
import darkoverload.itzip.feature.csQuiz.service.sub.quiz.CreateQuiz;
import darkoverload.itzip.feature.csQuiz.service.sub.quiz.GivenPointToQuiz;
import darkoverload.itzip.feature.csQuiz.service.sub.quizCategory.FindQuizCategory;
import darkoverload.itzip.feature.csQuiz.service.sub.quizcategory.FindQuizCategory;
import darkoverload.itzip.feature.csQuiz.service.sub.quizzes.FindQiuzQuery;
import darkoverload.itzip.feature.jwt.infrastructure.CustomUserDetails;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -42,7 +41,6 @@ public class QuizServiceImpl implements QuizService {

@Qualifier("createQuizImpl")
private final CreateQuiz createQuiz;
private final QuizRepository quizRepository;

/**
* 주어진 필터와 정렬 기준, 사용자 정보를 기반으로 퀴즈 목록을 조회하는 메서드
Expand Down
Loading
Loading