Skip to content

Commit

Permalink
✨ [Feature] 토너먼트 유저 참가 신청 API (#383)
Browse files Browse the repository at this point in the history
  • Loading branch information
SONGS4RI authored Dec 14, 2023
1 parent 9d14ae2 commit 617ffbf
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ public class TournamentAdminAddUserResponseDto {
private String intraId;

@NotNull
private boolean isJoined;
private Boolean isJoined;

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public class TournamentAdminService {
/***
* 토너먼트 생성 Method
* @param tournamentAdminCreateRequestDto 토너먼트 생성에 필요한 데이터
* @throws TournamentTitleConflictException 토너먼트의 제목이 겹칠 때
* @throws InvalidParameterException 토너먼트 시간으로 부적합 할 때
* @throws TournamentConflictException 업데이트 하고자 하는 토너먼트의 시간이 겹칠 때
* @return 새로 생성된 tournament
*/
@Transactional
Expand All @@ -66,6 +69,7 @@ public Tournament createTournament(TournamentAdminCreateRequestDto tournamentAdm
* @param requestDto 요청한 Dto
* @throws TournamentNotFoundException 찾을 수 없는 토너먼트 일 때
* @throws TournamentUpdateException 업데이트 할 수 없는 토너먼트 일 때
* @throws InvalidParameterException 토너먼트 시간으로 부적합 할 때
*/
@Transactional
public Tournament updateTournamentInfo(Long tournamentId, TournamentAdminUpdateRequestDto requestDto) {
Expand Down Expand Up @@ -128,7 +132,8 @@ public TournamentAdminAddUserResponseDto addTournamentUser(Long tournamentId, To

List<TournamentUser> tournamentList = targetTournament.getTournamentUsers();
tournamentList.stream().filter(tu->tu.getUser().getIntraId().equals(targetUser.getIntraId()))
.findAny().ifPresent(a->{throw new TournamentConflictException("user is already participant", ErrorCode.TOURNAMENT_CONFLICT);});
.findAny()
.ifPresent(a->{throw new TournamentConflictException("user is already participant", ErrorCode.TOURNAMENT_CONFLICT);});

TournamentUser tournamentUser = new TournamentUser(targetUser, targetTournament,
tournamentList.size() < Tournament.ALLOWED_JOINED_NUMBER, LocalDateTime.now());
Expand All @@ -147,6 +152,9 @@ public TournamentAdminAddUserResponseDto addTournamentUser(Long tournamentId, To
* <p>삭제하고자 하는 유저가 참가자이고, 현재 대기자가 있다면 참가신청이 빠른 대기자를 참가자로 변경해준다.</p>
* @param tournamentId 타겟 토너먼트 id
* @param userId 타겟 유저 id
* @throws TournamentNotFoundException 타겟 토너먼트 없음
* @throws TournamentUpdateException 이미 시작했거나 종료된 토너먼트
* @throws UserNotFoundException 유저 없음 || 토너먼트 신청자가 아님
*/
@Transactional
public void deleteTournamentUser(Long tournamentId, Long userId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.gg.server.domain.user.dto.UserDto;
import com.gg.server.global.utils.argumentresolver.Login;
import io.swagger.v3.oas.annotations.Parameter;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.checkerframework.checker.index.qual.Positive;
import org.springframework.data.domain.PageRequest;
Expand Down Expand Up @@ -78,4 +79,18 @@ ResponseEntity<TournamentUserRegistrationResponseDto> cancelTournamentUserRegist
public ResponseEntity<TournamentGameListResponseDto> getTournamentGames(@PathVariable @Positive Long tournamentId){
return ResponseEntity.ok().body(tournamentService.getTournamentGames(tournamentId));
}

/**
* <p>토너먼트 참가 신청</p>
* <p>토너먼트 최대 인원보다 이미 많이 신청했다면 대기자로 들어간다</p>
* @param tournamentId 타겟 토너먼트 id
* @param user 신청 유저(본인)
* @return
*/
@PostMapping("/{tournamentId}/users")
ResponseEntity<TournamentUserRegistrationResponseDto> registerTournamentUser(@PathVariable Long tournamentId, @Parameter(hidden = true) @Login UserDto user) {

return ResponseEntity.created(URI.create("/pingpong/tournaments/"+tournamentId+"/users"))
.body(tournamentService.registerTournamentUser(tournamentId, user));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gg.server.domain.tournament.data;

import com.gg.server.domain.user.data.User;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -15,4 +16,6 @@ public interface TournamentUserRepository extends JpaRepository<TournamentUser,
List<TournamentUser> findAllByTournamentId(Long tournamentId);

Optional<TournamentUser> findByTournamentIdAndUserId(Long tournamentId, Long userId);

List<TournamentUser> findAllByUser(User user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import com.gg.server.domain.tournament.dto.TournamentGameResDto;
import com.gg.server.domain.tournament.dto.TournamentListResponseDto;
import com.gg.server.domain.tournament.dto.TournamentResponseDto;
import com.gg.server.domain.tournament.exception.TournamentConflictException;
import com.gg.server.domain.tournament.exception.TournamentNotFoundException;
import com.gg.server.domain.tournament.type.TournamentRound;
import com.gg.server.domain.tournament.exception.TournamentNotFoundException;
import com.gg.server.domain.tournament.type.TournamentStatus;
Expand All @@ -33,16 +35,16 @@
import com.gg.server.domain.user.dto.UserImageDto;
import com.gg.server.domain.user.exception.UserNotFoundException;
import com.gg.server.global.exception.ErrorCode;
import java.util.Optional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -106,12 +108,14 @@ public TournamentResponseDto getTournament(long tournamentId) {
* <p>유저 해당 토너먼트 참여 여부 확인 매서드</p>
* @param tournamentId 타겟 토너먼트
* @param user 해당 유저
* @return
* @return TournamentUserRegistrationResponseDto [ BEFORE || WAIT || PLAYER ]
* @throws TournamentNotFoundException 타겟 토너먼트 없음
* @throws UserNotFoundException 유저 없음
*/
public TournamentUserRegistrationResponseDto getUserStatusInTournament(Long tournamentId, UserDto user) {
Tournament targetTournament = tournamentRepository.findById(tournamentId).orElseThrow(() ->
new TournamentNotFoundException("target tournament not found", ErrorCode.TOURNAMENT_NOT_FOUND));
User loginUser = userRepository.findById(user.getId()).orElseThrow(UserNotFoundException::new);

TournamentUserStatus tournamentUserStatus = TournamentUserStatus.BEFORE;
Optional<TournamentUser> tournamentUser = tournamentUserRepository.findByTournamentIdAndUserId(tournamentId, user.getId());
if (tournamentUser.isPresent()) {
Expand All @@ -120,6 +124,34 @@ public TournamentUserRegistrationResponseDto getUserStatusInTournament(Long tour
return new TournamentUserRegistrationResponseDto(tournamentUserStatus);
}

/**
* <p>토너먼트 참가 신청 매서드</p>
* <p>이미 신청한 토너먼트 중 BEFORE || LIVE인 경우가 존재한다면 신청 불가능 하다.</p>
* @param tournamentId 타겟 토너먼트 Id
* @param user 신청 유저(로그인한 본인)
* @return TournamentUserRegistrationResponseDto [ WAIT || PLAYER ]
* @throws TournamentNotFoundException 타겟 토너먼트 없음
* @throws UserNotFoundException 유저 없음
* @throws TournamentConflictException 이미 신청한 토너먼트 존재(BEFORE || LIVE인 토너먼트)
*/
@Transactional
public TournamentUserRegistrationResponseDto registerTournamentUser(Long tournamentId, UserDto user) {
Tournament targetTournament = tournamentRepository.findById(tournamentId).orElseThrow(() ->
new TournamentNotFoundException("target tournament not found", ErrorCode.TOURNAMENT_NOT_FOUND));
User loginUser = userRepository.findById(user.getId()).orElseThrow(UserNotFoundException::new);

List<TournamentUser> tournamentUserList = targetTournament.getTournamentUsers();
tournamentUserRepository.findAllByUser(loginUser).stream()
.filter(tu->tu.getTournament().getStatus().equals(TournamentStatus.BEFORE) || tu.getTournament().getStatus().equals(TournamentStatus.LIVE))
.findAny()
.ifPresent(a->{throw new TournamentConflictException("이미 신청한 토너먼트가 존재합니다.", ErrorCode.TOURNAMENT_CONFLICT);});
TournamentUser tournamentUser = new TournamentUser(loginUser, targetTournament,
tournamentUserList.size() < Tournament.ALLOWED_JOINED_NUMBER, LocalDateTime.now());
targetTournament.addTournamentUser(tournamentUser);
TournamentUserStatus tournamentUserStatus = tournamentUser.getIsJoined() ? TournamentUserStatus.PLAYER : TournamentUserStatus.WAIT;
return new TournamentUserRegistrationResponseDto(tournamentUserStatus);
}

/**
* <p>유저 토너먼트 참가 신청 취소 매서드</p>
* <p>참가자가 WAIT 이거나 PLAYER 로 해당 토너먼트에 신청을 한 상태일때만 취소해 준다.</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@IntegrationTest
Expand Down Expand Up @@ -433,4 +434,96 @@ void tournamentNotFound() throws Exception {
}

}

@Nested
@DisplayName("토너먼트_유저_참가_신청")
class RegisterTournamentUserTest {
@BeforeEach
void beforeEach() {
tester = testDataUtils.createNewUser("findControllerTester", "findControllerTester", RacketType.DUAL, SnsType.SLACK, RoleType.ADMIN);
accessToken = tokenProvider.createToken(tester.getId());
}

@Test
@DisplayName("유저_참가_신청(참가자)_성공")
void successPlayer() throws Exception {
// given
Tournament tournament = testDataUtils.createTournament(LocalDateTime.now(), LocalDateTime.now(), TournamentStatus.BEFORE);
String url = "/pingpong/tournaments/" + tournament.getId() + "/users";
String expected = "{\"status\":\"PLAYER\"}";

// when
String contentAsString = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken))
.andExpect(status().isCreated())
.andReturn().getResponse().getContentAsString();

// then
if (expected.compareTo(contentAsString) != 0) {
throw new CustomRuntimeException("상태 오류", ErrorCode.BAD_REQUEST);
}
}

@Test
@DisplayName("유저_참가_신청(대기자)_성공")
void successWait() throws Exception {
// given
int maxTournamentUser = 8;
Tournament tournament = testDataUtils.createTournament(LocalDateTime.now(), LocalDateTime.now(), TournamentStatus.BEFORE);
for (int i=0; i<maxTournamentUser; i++) {
testDataUtils.createTournamentUser(testDataUtils.createNewUser("testUser" + i), tournament, true);
}
String url = "/pingpong/tournaments/" + tournament.getId() + "/users";
String expected = "{\"status\":\"WAIT\"}";

// when
String contentAsString = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken))
.andExpect(status().isCreated())
.andReturn().getResponse().getContentAsString();

// then
if (expected.compareTo(contentAsString) != 0) {
throw new CustomRuntimeException("상태 오류", ErrorCode.BAD_REQUEST);
}
}

@Test
@DisplayName("토너먼트_없음")
void tournamentNotFound() throws Exception {
// given
String url = "/pingpong/tournaments/" + 9999 + "/users";

// when, then
String contentAsString = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken))
.andExpect(status().isNotFound())
.andReturn().getResponse().getContentAsString();

System.out.println(contentAsString);
}

@Test
@DisplayName("이미_신청한_토너먼트_존재")
void conflictRegisteration() throws Exception {
// given
Tournament tournament1 = testDataUtils.createTournament(LocalDateTime.now(), LocalDateTime.now(), TournamentStatus.BEFORE);
Tournament tournament2 = testDataUtils.createTournament(LocalDateTime.now(), LocalDateTime.now(), TournamentStatus.BEFORE);
testDataUtils.createTournamentUser(tester, tournament1, false);
String url = "/pingpong/tournaments/" + tournament2.getId() + "/users";

// when, then
String contentAsString = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON)
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken))
.andExpect(status().isConflict())
.andReturn().getResponse().getContentAsString();

System.out.println(contentAsString);
}

}
}
Loading

0 comments on commit 617ffbf

Please sign in to comment.