Skip to content

Commit

Permalink
Feature: mypage 명예의 전당 및 사용자 점수 전달 API 개발 (#49)
Browse files Browse the repository at this point in the history
* feature: 가입 거절시 해당 멤버를 refusedMember 테이블에 추가 기능 구현

* feature: 가입 재승인 API 구현

* feature: refusedMember 테이블에 생성 날짜가 30일이 넘어가면 해당 멤버 member 테이블 refusedMember 테이블에서 삭제 기능 구현

* fix: RefusedMember 테이블 fetch=LAZY 설정

* feature: mypage 개발 임시저장

* feature: 명예의 전당 구현 진행중

* feature: 마이페이지 정답자 명예의 전당 메소드 구현

* feature: 마이페이지 자신의 맞춘 문제 수, 정답 수 계산 구현
  • Loading branch information
gikhoon authored Feb 13, 2024
1 parent 56aadd4 commit 79bb59d
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 0 deletions.
33 changes: 33 additions & 0 deletions src/main/java/cotato/csquiz/controller/MyPageController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cotato.csquiz.controller;

import cotato.csquiz.config.jwt.JwtUtil;
import cotato.csquiz.domain.dto.mypage.HallOfFameResponse;
import cotato.csquiz.service.MyPageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/v1/api/mypage")
public class MyPageController {

private final MyPageService myPageService;
private final JwtUtil jwtUtil;

@GetMapping("/hall-of-fame")
public ResponseEntity<?> getHallOfFame(@RequestParam("generationId") Long generationId,
@RequestHeader("Authorization") String authorizationHeader) {
log.info("교육에 등록된 전체 퀴즈 조회 컨트롤러");
String accessToken = jwtUtil.getBearer(authorizationHeader);
String email = jwtUtil.getEmail(accessToken);
HallOfFameResponse response = myPageService.getHallOfFame(generationId, email);
return ResponseEntity.ok(response);
}
}
19 changes: 19 additions & 0 deletions src/main/java/cotato/csquiz/domain/dto/mypage/HallOfFameInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cotato.csquiz.domain.dto.mypage;

import cotato.csquiz.domain.entity.Member;
import lombok.Lombok;

public record HallOfFameInfo(
Long memberId,
String name,
long count
) {
public static HallOfFameInfo from(Member member, long count) {
return new HallOfFameInfo(
member.getId(),
member.getName(),
count
);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package cotato.csquiz.domain.dto.mypage;

import java.util.List;

public record HallOfFameResponse(
List<HallOfFameInfo> scorerInfo,
List<HallOfFameInfo> answerInfo,
MyHallOfFameInfo myInfo
) {
public static HallOfFameResponse from(
List<HallOfFameInfo> scorerInfo, List<HallOfFameInfo> answerInfo, MyHallOfFameInfo myInfo) {
return new HallOfFameResponse(
scorerInfo,
answerInfo,
myInfo
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cotato.csquiz.domain.dto.mypage;

import cotato.csquiz.domain.entity.Member;

public record MyHallOfFameInfo(
Long memberId,
long scorerCount,
long answerCount
) {
public static MyHallOfFameInfo from(Member member, long scorerCount, long answerCount) {
return new MyHallOfFameInfo(
member.getId(),
scorerCount,
answerCount
);
}
}
1 change: 1 addition & 0 deletions src/main/java/cotato/csquiz/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public enum ErrorCode {
RANDOM_NUMBER_GENERATE_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "랜덤 숫자 생성 실패"),
JWT_EXPIRED(HttpStatus.UNAUTHORIZED, "accessToken이 만료되었습니다."),
SESSION_NOT_FOUND(HttpStatus.NOT_FOUND, "세션이 존재하지 않습니다."),
NO_GENERATION_EXIST(HttpStatus.CONFLICT, "오늘에 해당하는 기수가 없습니다"),
ANSWER_VALIDATION_FAULT(HttpStatus.BAD_REQUEST,"사용될 수 없는 정답");

private final HttpStatus httpStatus;
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/cotato/csquiz/repository/QuizRepository.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cotato.csquiz.repository;

import cotato.csquiz.domain.entity.Generation;
import cotato.csquiz.domain.entity.Quiz;
import java.util.List;

Expand All @@ -15,4 +16,6 @@ public interface QuizRepository extends JpaRepository<Quiz, Long> {
List<Quiz> findByStatus(QuizStatus status);

List<Quiz> findByStart(QuizStatus quizStatus);

List<Quiz> findByGeneration(Generation generation);
}
4 changes: 4 additions & 0 deletions src/main/java/cotato/csquiz/repository/RecordRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import org.springframework.data.jpa.repository.JpaRepository;

public interface RecordRepository extends JpaRepository<Record, Long> {
List<Record> findAllByQuizAndIsCorrect(Quiz quiz, boolean isCorrect);

List<Record> findAllByQuizAndIsCorrectAndMember(Quiz quiz, boolean isCorrect, Member member);

List<Record> findAllByQuizAndReply(Quiz quiz, String answer);

List<Record> findAllByQuizAndMemberAndIsCorrect(Quiz quiz, Member member, boolean isCorrect);
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/cotato/csquiz/repository/ScorerRepository.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package cotato.csquiz.repository;

import cotato.csquiz.domain.entity.Member;
import cotato.csquiz.domain.entity.Quiz;
import cotato.csquiz.domain.entity.Scorer;
import java.util.Collection;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ScorerRepository extends JpaRepository<Scorer, Long> {
List<Scorer> findAllByQuiz(Quiz quiz);

List<Scorer> findAllByQuizAndMember(Quiz quiz, Member member);

Scorer findByQuiz(Quiz quiz);
}
116 changes: 116 additions & 0 deletions src/main/java/cotato/csquiz/service/MyPageService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package cotato.csquiz.service;

import cotato.csquiz.domain.dto.mypage.HallOfFameInfo;
import cotato.csquiz.domain.dto.mypage.HallOfFameResponse;
import cotato.csquiz.domain.dto.mypage.MyHallOfFameInfo;
import cotato.csquiz.domain.entity.Generation;
import cotato.csquiz.domain.entity.Member;
import cotato.csquiz.domain.entity.Quiz;
import cotato.csquiz.domain.entity.Record;
import cotato.csquiz.domain.entity.Scorer;
import cotato.csquiz.exception.AppException;
import cotato.csquiz.exception.ErrorCode;
import cotato.csquiz.repository.GenerationRepository;
import cotato.csquiz.repository.MemberRepository;
import cotato.csquiz.repository.QuizRepository;
import cotato.csquiz.repository.RecordRepository;
import cotato.csquiz.repository.ScorerRepository;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class MyPageService {

private final GenerationRepository generationRepository;
private final QuizRepository quizRepository;
private final ScorerRepository scorerRepository;
private final RecordRepository recordRepository;
private final MemberRepository memberRepository;
private static final int SHOW_PEOPLE_COUNT = 5;

public HallOfFameResponse getHallOfFame(Long generationId, String email) {
Generation generation = generationRepository.findById(generationId).orElseThrow(() ->
new AppException(ErrorCode.GENERATION_NOT_FOUND)
);
List<Quiz> quizzes = quizRepository.findByGeneration(generation);
List<HallOfFameInfo> scorerHallOfFame = makeScorerHallOfFame(quizzes);
List<HallOfFameInfo> answerHallOfFame = makeAnswerHallOfFame(quizzes);
MyHallOfFameInfo myHallOfFameInfo = makeMyHallOfFameInfo(email, quizzes);
return HallOfFameResponse.from(scorerHallOfFame, answerHallOfFame, myHallOfFameInfo);
}

private MyHallOfFameInfo makeMyHallOfFameInfo(String email, List<Quiz> quizzes) {
Member member = memberRepository.findByEmail(email)
.orElseThrow(() -> new AppException(ErrorCode.MEMBER_NOT_FOUND));
long scorerCount = countMyScorer(member,quizzes);
long answerCount = countMyAnswer(member,quizzes);
return MyHallOfFameInfo.from(member, scorerCount, answerCount);
}

private long countMyScorer(Member member, List<Quiz> quizzes) {
List<Scorer> memberScorers = quizzes.stream()
.flatMap(quiz -> scorerRepository.findAllByQuizAndMember(quiz, member).stream())
.toList();
return memberScorers.size();
}

private long countMyAnswer(Member member, List<Quiz> quizzes) {
List<Record> memberRecords = quizzes.stream()
.flatMap(quiz -> recordRepository.findAllByQuizAndIsCorrectAndMember(quiz, true, member).stream())
.toList();
return memberRecords.size();
}

private List<HallOfFameInfo> makeAnswerHallOfFame(List<Quiz> quizzes) {
List<Record> recordList = findRecordByQuizzes(quizzes);

Map<Member, Long> countByMember = recordList.stream()
.collect(Collectors.groupingBy(Record::getMember, Collectors.counting()));
List<Entry<Member, Long>> sorted5MemberEntry = sorted5MemberEntry(countByMember);

return sorted5MemberEntry.stream()
.map(entry -> HallOfFameInfo.from(entry.getKey(), entry.getValue()))
.toList();
}

private List<HallOfFameInfo> makeScorerHallOfFame(List<Quiz> quizzes) {
List<Scorer> scorerList = findScorerByQuizzes(quizzes);

Map<Member, Long> countByMember = scorerList.stream()
.collect(Collectors.groupingBy(Scorer::getMember, Collectors.counting()));
List<Map.Entry<Member, Long>> sorted5MemberEntry = sorted5MemberEntry(countByMember);

return sorted5MemberEntry.stream()
.map(entry -> HallOfFameInfo.from(entry.getKey(), entry.getValue()))
.toList();
}

private List<Scorer> findScorerByQuizzes(List<Quiz> quizzes) {
return quizzes.stream()
.flatMap(quiz -> scorerRepository.findAllByQuiz(quiz).stream())
.toList();
}

private List<Record> findRecordByQuizzes(List<Quiz> quizzes) {
return quizzes.stream()
.flatMap(quiz -> recordRepository.findAllByQuizAndIsCorrect(quiz, true).stream())
.toList();
}

private static List<Entry<Member, Long>> sorted5MemberEntry(Map<Member, Long> countByMember) {
return countByMember.entrySet().stream()
.sorted(Entry.comparingByValue(Comparator.reverseOrder()))
.limit(SHOW_PEOPLE_COUNT)
.toList();
}
}

0 comments on commit 79bb59d

Please sign in to comment.