-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: mypage 명예의 전당 및 사용자 점수 전달 API 개발 (#49)
* feature: 가입 거절시 해당 멤버를 refusedMember 테이블에 추가 기능 구현 * feature: 가입 재승인 API 구현 * feature: refusedMember 테이블에 생성 날짜가 30일이 넘어가면 해당 멤버 member 테이블 refusedMember 테이블에서 삭제 기능 구현 * fix: RefusedMember 테이블 fetch=LAZY 설정 * feature: mypage 개발 임시저장 * feature: 명예의 전당 구현 진행중 * feature: 마이페이지 정답자 명예의 전당 메소드 구현 * feature: 마이페이지 자신의 맞춘 문제 수, 정답 수 계산 구현
- Loading branch information
Showing
9 changed files
with
218 additions
and
0 deletions.
There are no files selected for viewing
33 changes: 33 additions & 0 deletions
33
src/main/java/cotato/csquiz/controller/MyPageController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
19
src/main/java/cotato/csquiz/domain/dto/mypage/HallOfFameInfo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
); | ||
} | ||
} | ||
|
18 changes: 18 additions & 0 deletions
18
src/main/java/cotato/csquiz/domain/dto/mypage/HallOfFameResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/main/java/cotato/csquiz/domain/dto/mypage/MyHallOfFameInfo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |