Skip to content

Commit

Permalink
Feature: 교육 결승진출자, 우승자 계산, 재전송 API 구현 (#19)
Browse files Browse the repository at this point in the history
* Chore: update code-deploy-app-name

* Chore: set overwrite

* Chore: 배포 스크립트 분기 로그 추가

* Chore: 브랜치 확인을 위한 경로 이동 스크립트 추가

* Chore: 작업 디렉토리 신뢰를 위한 git 설정

* Chore: jar파일 경로 구체화

* Chore: jar파일 생성 전 build를 선행 작업으로 추가

* Chore: 정상 build 후 JAR파일 이름을 변경하는 방식으로 수정

* Chore: test server jdbc url 수정

* Chore: test server jdbc url 수정

* Chore: Github action에 키를 주입하는 방식 변경

* Chore: application.yml 환경 변수 세팅 단계 추가

* Chore: application.yml 환경 변수 세팅 단계 추가

* docs: Update Feature Issue Template

* docs: Update Fix Issue Template

* docs: PR 템플릿 생성

* docs: PR 템플릿 이모지 제거

* Feature: 교육 결승진출자, 우승자 계산, 재전송 API 구현 (#3)

* feat: 교육 결승진출자 계산, 재전송 API 구현

* feat: 교육 결승진출자 계산, 재전송 API 구현

* feat: kingking member, winner 계산 + 전송 API 구현

* feat: 이미 우승자가 계산됐으면 exception 발생

* refactor: 코드 리뷰를 기반으로 코드 수정

* refactor: 컨트롤러, 서비스 메소드 위치 변경

---------

Co-authored-by: GiHun Nam <52378919+gikhoon@users.noreply.github.com>
  • Loading branch information
Youthhing and gikhoon authored May 30, 2024
1 parent a1356ce commit 2a5c59c
Show file tree
Hide file tree
Showing 15 changed files with 217 additions and 70 deletions.
22 changes: 22 additions & 0 deletions .github/ISSUE_TEMPLATE/feature-issue-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
name: Feature Issue Template
about: Feature 브랜치 생성 전 작성해주세요
title: "[Feature]"
labels: ''
assignees: ''

---

## 🪶 추가할 기능 설명


## ✅ 작업 내용

- [ ] 구현 내용 1
- [ ] 구현 내용 2

## 스크린샷 (선택)

## 🔗 참고한 링크 (선택)

## ETC
28 changes: 28 additions & 0 deletions .github/ISSUE_TEMPLATE/fix-issue-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: Fix Issue Template
about: Fix 브랜치 생성 전 작성해주세요
title: "[Fix]"
labels: ''
assignees: ''

---

## ⚠️ 발생한 에러


## 에러가 발생한 상황

**Given** :
**When** :
**Then** :

## ✅ 수정한 내용

- [ ] 수정 내용 1
- [ ] 수정 내용 2



## 📡 예상 결과

## 🔗 참고한 자료
9 changes: 9 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## 연관된 이슈

이슈링크(url):


## ✅ 작업 내용


## 🗣 ️리뷰 요구 사항
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.cotato.csquiz.api.education.dto.WinnerInfoResponse;
import org.cotato.csquiz.api.quiz.dto.KingMemberInfo;
import org.cotato.csquiz.domain.education.service.EducationService;
import org.cotato.csquiz.domain.education.service.KingMemberService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -30,6 +31,7 @@
public class EducationController {

private final EducationService educationService;
private final KingMemberService kingMemberService;

@GetMapping
public ResponseEntity<List<AllEducationResponse>> findEducationListByGeneration(
Expand Down Expand Up @@ -60,15 +62,29 @@ public ResponseEntity<EducationIdOfQuizResponse> findEducationId(@RequestParam("
return ResponseEntity.ok().body(educationService.findEducationIdOfQuizId(quizId));
}

@GetMapping("/result/kings")
@GetMapping("/kings")
public ResponseEntity<List<KingMemberInfo>> findFinalKingMembers(@RequestParam("educationId") Long educationId) {
log.info("[{} 교육 결승진출자 조회 컨트롤러]", educationId);
return ResponseEntity.ok().body(educationService.findKingMemberInfo(educationId));
return ResponseEntity.ok().body(kingMemberService.findKingMemberInfo(educationId));
}

@GetMapping("/result/winner")
@PostMapping("/kings")
public ResponseEntity<Void> calculateKingMembers(@RequestParam("educationId") Long educationId) {
log.info("[{} 교육 결승진출자 계산하기]", educationId);
kingMemberService.saveKingMember(educationId);
return ResponseEntity.noContent().build();
}

@GetMapping("/winner")
public ResponseEntity<WinnerInfoResponse> findWinner(@RequestParam("educationId") Long educationId) {
log.info("[{} 교육 우승자 조회 컨트롤러]", educationId);
return ResponseEntity.ok().body(educationService.findWinner(educationId));
return ResponseEntity.ok().body(kingMemberService.findWinner(educationId));
}

@PostMapping("/winner")
public ResponseEntity<Void> calculateWinner(@RequestParam("educationId") Long educationId) {
log.info("[{} 교육 우승자 계산]", educationId);
kingMemberService.calculateWinner(educationId);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.springframework.web.bind.annotation.RequestBody;
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;

@RestController
Expand Down Expand Up @@ -68,4 +69,19 @@ public ResponseEntity<Void> stopAllQuiz(@RequestBody @Valid EducationCloseReques
public ResponseEntity<SocketTokenDto> makeSocketToken(@RequestHeader("Authorization") String authorizationHeader) {
return ResponseEntity.ok(socketService.createSocketToken(authorizationHeader));
}


@PostMapping("/kings")
public ResponseEntity<Void> sendKingCommand(@RequestParam("educationId") Long educationId) {
log.info("[{} 교육 결승진출자 재전송하기]", educationId);
socketService.sendKingCommand(educationId);
return ResponseEntity.noContent().build();
}

@PostMapping("/winner")
public ResponseEntity<Void> sendWinnerCommand(@RequestParam("educationId") Long educationId) {
log.info("[{} 교육 결승진출자 재전송하기]", educationId);
socketService.sendWinnerCommand(educationId);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.cotato.csquiz.api.socket.dto;

import org.cotato.csquiz.domain.education.entity.Education;

public record EducationResultResponse(
String command,
Long educationId
) {
public static EducationResultResponse of(String command, Long educationId) {
return new EducationResultResponse(command, educationId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ public record QuizStopResponse(
String command,
Long quizId
) {
public static QuizStopResponse from(String command, Long quizId) {
public static final String STOP_COMMAND = "stop";

public static QuizStopResponse from(Long quizId) {
return new QuizStopResponse(
command,
STOP_COMMAND,
quizId
);
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/org/cotato/csquiz/common/error/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@ public enum ErrorCode {

SUBJECT_INVALID(HttpStatus.BAD_REQUEST, "E-000", "교육 주제는 NULL이거나 비어있을 수 없습니다."),

KING_MEMBER_EXIST(HttpStatus.CONFLICT, "K-301", "이미 킹킹 멤버가 존재합니다"),

ALREADY_REPLY_CORRECT(HttpStatus.BAD_REQUEST, "R-301", "해당 사용자는 이미 정답 처리되었습니다."),

LAST_QUIZ_SCORER_NOT_EXIST(HttpStatus.CONFLICT, "W-201", "아직 마지막 문제 득점자가 없습니다"),
WINNER_EXIST(HttpStatus.CONFLICT, "W-301", "이미 우승자가 존재합니다"),
// 500 오류 -> 서버측에서 처리가 실패한 부분들
WEBSOCKET_SEND_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "S-001", "소캣 메세지 전송 실패"),
IMAGE_PROCESSING_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "S-002", "이미지 처리에 실패했습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import org.cotato.csquiz.api.socket.dto.CsQuizStopResponse;
import org.cotato.csquiz.api.socket.dto.EducationResultResponse;
import org.cotato.csquiz.api.socket.dto.QuizStartResponse;
import org.cotato.csquiz.api.socket.dto.QuizStatusResponse;
import org.cotato.csquiz.api.socket.dto.QuizStopResponse;
Expand Down Expand Up @@ -118,14 +119,23 @@ public void startQuiz(Long quizId) {
}

public void stopQuiz(Quiz quiz) {
String command = "";
if (quiz.getNumber() == 9) {
command = KING_COMMAND;
QuizStopResponse response = QuizStopResponse.from(quiz.getId());
for (WebSocketSession clientSession : CLIENTS.values()) {
sendMessage(clientSession, response);
}
if (quiz.getNumber() == 10) {
command = WINNER_COMMAND;
}

public void sendKingMemberCommand(Long educationId) {
EducationResultResponse response = EducationResultResponse.of(KING_COMMAND, educationId);

for (WebSocketSession clientSession : CLIENTS.values()) {
sendMessage(clientSession, response);
}
QuizStopResponse response = QuizStopResponse.from(command, quiz.getId());
}

public void sendWinnerCommand(Long educationId) {
EducationResultResponse response = EducationResultResponse.of(WINNER_COMMAND, educationId);

for (WebSocketSession clientSession : CLIENTS.values()) {
sendMessage(clientSession, response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@

public interface KingMemberRepository extends JpaRepository<KingMember, Long> {
List<KingMember> findAllByEducation(Education education);

boolean existsByEducation(Education education);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.cotato.csquiz.domain.education.repository;

import org.cotato.csquiz.domain.education.entity.Education;
import org.cotato.csquiz.domain.education.entity.Quiz;
import org.cotato.csquiz.domain.education.enums.QuizStatus;
import java.util.List;
Expand Down Expand Up @@ -29,4 +30,8 @@ public interface QuizRepository extends JpaRepository<Quiz, Long> {
@Modifying
@Query("select q from Quiz q where q.education.id in :educationIds")
List<Quiz> findAllByEducationIdsInQuery(@Param("educationIds") List<Long> educationIds);

Optional<Quiz> findByEducationIdAndNumber(Long educationId, Integer i);

Optional<Quiz> findFirstByEducationOrderByNumberDesc(Education education);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@

public interface WinnerRepository extends JpaRepository<Winner, Long> {
Optional<Winner> findByEducation(Education education);

boolean existsByEducation(Education education);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,8 @@
@Slf4j
public class EducationService {

private final MemberService memberService;
private final EducationRepository educationRepository;
private final KingMemberRepository kingMemberRepository;
private final QuizRepository quizRepository;
private final WinnerRepository winnerRepository;
private final SessionRepository sessionRepository;

@Transactional
Expand Down Expand Up @@ -97,32 +94,6 @@ public List<AllEducationResponse> findEducationListByGeneration(Long generationI
.toList();
}

public List<KingMemberInfo> findKingMemberInfo(Long educationId) {
Education findEducation = educationRepository.findById(educationId)
.orElseThrow(() -> new EntityNotFoundException("해당 교육을 찾을 수 없습니다."));
List<KingMember> kingMembers = kingMemberRepository.findAllByEducation(findEducation);
validateIsEmpty(kingMembers);
return kingMembers.stream()
.map(kingMember -> memberService.findById(kingMember.getMemberId()))
.map(member -> KingMemberInfo.from(member, memberService.findBackFourNumber(member)))
.toList();
}

private void validateIsEmpty(List<KingMember> kingMembers) {
if (kingMembers.isEmpty()) {
throw new EntityNotFoundException("아직 결승 진출자가 결정되지 않았습니다.");
}
}

public WinnerInfoResponse findWinner(Long educationId) {
Education findEducation = educationRepository.findById(educationId)
.orElseThrow(() -> new EntityNotFoundException("해당 교육을 찾을 수 없습니다."));
Winner findWinner = winnerRepository.findByEducation(findEducation)
.orElseThrow(() -> new EntityNotFoundException("해당 교육의 우승자를 찾을 수 없습니다."));
Member findMember = memberService.findById(findWinner.getMemberId());
return WinnerInfoResponse.of(findWinner, findMember, memberService.findBackFourNumber(findMember));
}

public EducationIdOfQuizResponse findEducationIdOfQuizId(Long quizId) {
Quiz quiz = quizRepository.findById(quizId)
.orElseThrow(() -> new EntityNotFoundException("해당 문제를 찾을 수 없습니다."));
Expand Down
Loading

0 comments on commit 2a5c59c

Please sign in to comment.