Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ [Feature] 토너먼트 유저 참가 신청 API #383

Merged
merged 7 commits into from
Dec 14, 2023
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 @@ -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);});
middlefitting marked this conversation as resolved.
Show resolved Hide resolved

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 @@
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));

Check warning on line 94 in src/main/java/com/gg/server/domain/tournament/controller/TournamentController.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/gg/server/domain/tournament/controller/TournamentController.java#L93-L94

Added lines #L93 - L94 were not covered by tests
}
}
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);
SONGS4RI marked this conversation as resolved.
Show resolved Hide resolved

TournamentUserStatus tournamentUserStatus = TournamentUserStatus.BEFORE;
SONGS4RI marked this conversation as resolved.
Show resolved Hide resolved
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
Loading