Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,5 @@

import java.time.LocalDateTime;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class MatchCreateRequestDto {
private Long createdBy;
private LocalDateTime matchDateTime;
private String location;
private Integer maxPlayers;
}
public record MatchCreateRequestDto(
Long createdBy, LocalDateTime matchDateTime, String location, Integer maxPlayers) {}
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,28 @@
import com.nextcloudlab.kickytime.match.entity.MatchStatus;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "๊ฒฝ๊ธฐ ๊ฐœ์„ค ์š”์ฒญ ๋ฐ์ดํ„ฐ")
public class MatchResponseDto {

@Schema(description = "๊ฒฝ๊ธฐ ID", example = "1", required = true)
private Long id;

@Schema(
description = "๊ฒฝ๊ธฐ ์ƒํƒœ",
example = "OPEN, FULL, CLOSED, CANCELED",
required = false,
defaultValue = "OPEN")
private MatchStatus matchStatus;

@Schema(description = "๊ฒฝ๊ธฐ ์ผ์‹œ", example = "2025-08-14T19:00:00", required = true)
private LocalDateTime matchDateTime;

@Schema(description = "๊ฒฝ๊ธฐ ์žฅ์†Œ", example = "์„œ์šธํŠน๋ณ„์‹œ ์ถ•๊ตฌ์žฅ", required = true)
private String location;

@Schema(description = "์ตœ๋Œ€ ์ฐธ๊ฐ€์ž ์ˆ˜", example = "10", required = true)
private Integer maxPlayers;

private Integer currentParticipants;

public MatchResponseDto(Match match) {
this.id = match.getId();
this.matchStatus = match.getMatchStatus();
this.matchDateTime = match.getMatchDateTime();
this.location = match.getLocation();
this.maxPlayers = match.getMaxPlayers();
this.currentParticipants = match.getCurrentParticipantCount();
public record MatchResponseDto(
@Schema(description = "๊ฒฝ๊ธฐ ID", example = "1", required = true) Long id,
@Schema(
description = "๊ฒฝ๊ธฐ ์ƒํƒœ",
example = "OPEN, FULL, CLOSED, CANCELED",
required = false,
defaultValue = "OPEN")
MatchStatus matchStatus,
@Schema(description = "๊ฒฝ๊ธฐ ์ผ์‹œ", example = "2025-08-14T19:00:00", required = true)
LocalDateTime matchDateTime,
@Schema(description = "๊ฒฝ๊ธฐ ์žฅ์†Œ", example = "์„œ์šธํŠน๋ณ„์‹œ ์ถ•๊ตฌ์žฅ", required = true) String location,
@Schema(description = "์ตœ๋Œ€ ์ฐธ๊ฐ€์ž ์ˆ˜", example = "10", required = true) Integer maxPlayers,
Integer currentParticipants) {
public static MatchResponseDto from(Match match) {
return new MatchResponseDto(
match.getId(),
match.getMatchStatus(),
match.getMatchDateTime(),
match.getLocation(),
match.getMaxPlayers(),
match.getCurrentParticipantCount());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import java.util.ArrayList;
import java.util.List;

import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import com.nextcloudlab.kickytime.common.entity.BaseEntity;
import com.nextcloudlab.kickytime.user.entity.User;

Expand All @@ -21,7 +19,6 @@
@NoArgsConstructor
@AllArgsConstructor
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "matches")
public class Match extends BaseEntity {
@Id
Expand All @@ -32,7 +29,6 @@ public class Match extends BaseEntity {
@Column(nullable = false)
private MatchStatus matchStatus;

// @Future(message = "๊ฒฝ๊ธฐ ์ผ์‹œ๋Š” ํ˜„์žฌ๋ณด๋‹ค ๋ฏธ๋ž˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")
@Column(nullable = false)
private LocalDateTime matchDateTime;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ public enum MatchStatus {
OPEN, // ๋ชจ์ง‘ ์ค‘ (์ •์› ๋ฏธ๋‹ฌ)
FULL, // ๋ชจ์ง‘ ๋งˆ๊ฐ (์ •์› ์ถฉ์กฑ)
CLOSED, // ๊ฒฝ๊ธฐ ์ข…๋ฃŒ
CANCELLED // ๊ฒฝ๊ธฐ ์ทจ์†Œ
CANCELED // ๊ฒฝ๊ธฐ ์ทจ์†Œ
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ private boolean isUpcoming(MyMatchesResponse.MatchInfo match) {

private boolean isCompleted(MyMatchesResponse.MatchInfo match) {
return match.matchStatus() == MatchStatus.CLOSED
|| match.matchStatus() == MatchStatus.CANCELLED;
|| match.matchStatus() == MatchStatus.CANCELED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ public MatchService(
public List<MatchResponseDto> getAllMatches() {
List<Match> matches = matchRepository.findAllByOrderByMatchDateTimeDesc();

return matches.stream().map(MatchResponseDto::new).collect(Collectors.toList());
return matches.stream().map(MatchResponseDto::from).collect(Collectors.toList());
}

// ๊ฒฝ๊ธฐ ๊ฐœ์„ค
public void createMatch(MatchCreateRequestDto requestDto) {
User user =
userRepository
.findById(requestDto.getCreatedBy())
.findById(requestDto.createdBy())
.orElseThrow(() -> new IllegalArgumentException("์‚ฌ์šฉ์ž๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."));

if (user.getRole() != RoleEnum.ADMIN) {
Expand All @@ -58,9 +58,9 @@ public void createMatch(MatchCreateRequestDto requestDto) {

Match match = new Match();
match.setMatchStatus(MatchStatus.OPEN);
match.setMatchDateTime(requestDto.getMatchDateTime());
match.setLocation(requestDto.getLocation());
match.setMaxPlayers(requestDto.getMaxPlayers());
match.setMatchDateTime(requestDto.matchDateTime());
match.setLocation(requestDto.location());
match.setMaxPlayers(requestDto.maxPlayers());
match.setCreatedBy(user);

matchRepository.save(match);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void setUp() {
createMatchInfo(1L, MatchStatus.OPEN),
createMatchInfo(2L, MatchStatus.FULL),
createMatchInfo(3L, MatchStatus.CLOSED),
createMatchInfo(4L, MatchStatus.CANCELLED),
createMatchInfo(4L, MatchStatus.CANCELED),
createMatchInfo(5L, MatchStatus.OPEN),
createMatchInfo(6L, MatchStatus.CLOSED));

Expand All @@ -51,7 +51,7 @@ void setUp() {
Arrays.asList(
createMatchInfoWithDetails(3L, MatchStatus.OPEN, "์„œ์šธ OOํ’‹์‚ด์žฅ A์ฝ”ํŠธ", 10),
createMatchInfoWithDetails(4L, MatchStatus.CLOSED, "๋ถ€์‚ฐ XXํ’‹์‚ดํŒŒํฌ B์ฝ”ํŠธ", 8),
createMatchInfoWithDetails(5L, MatchStatus.CANCELLED, "์ธ์ฒœ YY์Šคํƒ€๋””์›€ C์ฝ”ํŠธ", 12));
createMatchInfoWithDetails(5L, MatchStatus.CANCELED, "์ธ์ฒœ YY์Šคํƒ€๋””์›€ C์ฝ”ํŠธ", 12));
}

@Test
Expand All @@ -71,7 +71,7 @@ void getMyParticipantWithAllStatusesShouldReturnCorrectCounts() {
MyMatchesResponse.Summary summary = result.summary();
assertThat(summary.totalCount()).isEqualTo(6L);
assertThat(summary.upcomingCount()).isEqualTo(3L); // OPEN(2) + FULL(1)
assertThat(summary.completedCount()).isEqualTo(3L); // CLOSED(2) + CANCELLED(1)
assertThat(summary.completedCount()).isEqualTo(3L); // CLOSED(2) + CANCELED(1)
}

@Test
Expand Down Expand Up @@ -135,15 +135,15 @@ void getMyParticipantWithOnlyClosedMatchesShouldReturnCorrectCounts() {
}

@Test
@DisplayName("CANCELLED ์ƒํƒœ๋งŒ ์žˆ๋Š” ๊ฒฝ์šฐ")
void getMyParticipantWithOnlyCancelledMatchesShouldReturnCorrectCounts() {
@DisplayName("CANCELED ์ƒํƒœ๋งŒ ์žˆ๋Š” ๊ฒฝ์šฐ")
void getMyParticipantWithOnlyCanceledMatchesShouldReturnCorrectCounts() {
// given
List<MyMatchesResponse.MatchInfo> cancelledMatches =
List<MyMatchesResponse.MatchInfo> canceledMatches =
Arrays.asList(
createMatchInfo(1L, MatchStatus.CANCELLED),
createMatchInfo(2L, MatchStatus.CANCELLED));
createMatchInfo(1L, MatchStatus.CANCELED),
createMatchInfo(2L, MatchStatus.CANCELED));
when(matchParticipantRepository.findMatchParticipantByUserId(testCognitoSub))
.thenReturn(cancelledMatches);
.thenReturn(canceledMatches);

// when
MyMatchesResponse result = matchParticipantService.getMyParticipant(testCognitoSub);
Expand Down Expand Up @@ -208,7 +208,7 @@ void getMyParticipantShouldReturnExactMatchList() {
@Test
@DisplayName("์‹ค์ œ ๋ฐ์ดํ„ฐ ์‹œ๋‚˜๋ฆฌ์˜ค - SQL ๊ธฐ๋ฐ˜ ๋งค์น˜ ์ฐธ๊ฐ€์ž ์กฐํšŒ")
void getMyParticipantRealWorldScenarioShouldReturnCorrectCounts() {
// given - user_id 3์ด ์ฐธ๊ฐ€ํ•œ ๋งค์น˜๋“ค (OPEN, CLOSED, CANCELLED)
// given - user_id 3์ด ์ฐธ๊ฐ€ํ•œ ๋งค์น˜๋“ค (OPEN, CLOSED, CANCELED)
when(matchParticipantRepository.findMatchParticipantByUserId(testCognitoSub))
.thenReturn(realWorldMatches);

Expand All @@ -222,7 +222,7 @@ void getMyParticipantRealWorldScenarioShouldReturnCorrectCounts() {
MyMatchesResponse.Summary summary = result.summary();
assertThat(summary.totalCount()).isEqualTo(3L);
assertThat(summary.upcomingCount()).isEqualTo(1L); // OPEN(1)
assertThat(summary.completedCount()).isEqualTo(2L); // CLOSED(1) + CANCELLED(1)
assertThat(summary.completedCount()).isEqualTo(2L); // CLOSED(1) + CANCELED(1)

// ๋งค์น˜ ์ƒ์„ธ ์ •๋ณด ๊ฒ€์ฆ
List<MyMatchesResponse.MatchInfo> matches = result.matches();
Expand All @@ -240,7 +240,7 @@ void getMyParticipantLocationBasedMatchesShouldHandleCorrectly() {
createMatchInfoWithDetails(1L, MatchStatus.OPEN, "์„œ์šธ OOํ’‹์‚ด์žฅ A์ฝ”ํŠธ", 10),
createMatchInfoWithDetails(2L, MatchStatus.FULL, "์„œ์šธ OOํ’‹์‚ด์žฅ B์ฝ”ํŠธ", 8),
createMatchInfoWithDetails(3L, MatchStatus.CLOSED, "๋ถ€์‚ฐ XXํ’‹์‚ดํŒŒํฌ A์ฝ”ํŠธ", 12),
createMatchInfoWithDetails(4L, MatchStatus.CANCELLED, "์ธ์ฒœ YY์Šคํƒ€๋””์›€ C์ฝ”ํŠธ", 16));
createMatchInfoWithDetails(4L, MatchStatus.CANCELED, "์ธ์ฒœ YY์Šคํƒ€๋””์›€ C์ฝ”ํŠธ", 16));

when(matchParticipantRepository.findMatchParticipantByUserId(testCognitoSub))
.thenReturn(locationMatches);
Expand All @@ -251,7 +251,7 @@ void getMyParticipantLocationBasedMatchesShouldHandleCorrectly() {
// then
assertThat(result.summary().totalCount()).isEqualTo(4L);
assertThat(result.summary().upcomingCount()).isEqualTo(2L); // OPEN + FULL
assertThat(result.summary().completedCount()).isEqualTo(2L); // CLOSED + CANCELLED
assertThat(result.summary().completedCount()).isEqualTo(2L); // CLOSED + CANCELED

// ์„œ์šธ ์ง€์—ญ ๋งค์น˜๊ฐ€ 2๊ฐœ์ธ์ง€ ํ™•์ธ
long seoulMatches =
Expand Down Expand Up @@ -286,13 +286,13 @@ void getMyParticipantMixedMaxPlayersMatchesShouldCalculateCorrectly() {
@Test
@DisplayName("์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ๋งค์น˜ ์ƒํƒœ ๊ฒ€์ฆ - ๊ณผ๊ฑฐ/๋ฏธ๋ž˜ ๋งค์น˜")
void getMyParticipantTimeBasedMatchesShouldProcessCorrectly() {
// given - ์‹œ๊ฐ„ ๊ด€๋ จ ํŠน์„ฑ์„ ๊ฐ€์ง„ ๋งค์น˜๋“ค (๊ณผ๊ฑฐ ๋‚ ์งœ๋Š” CLOSED/CANCELLED, ๋ฏธ๋ž˜ ๋‚ ์งœ๋Š” OPEN/FULL)
// given - ์‹œ๊ฐ„ ๊ด€๋ จ ํŠน์„ฑ์„ ๊ฐ€์ง„ ๋งค์น˜๋“ค (๊ณผ๊ฑฐ ๋‚ ์งœ๋Š” CLOSED/CANCELED, ๋ฏธ๋ž˜ ๋‚ ์งœ๋Š” OPEN/FULL)
List<MyMatchesResponse.MatchInfo> timeBasedMatches =
Arrays.asList(
createMatchInfoWithDetails(
1L, MatchStatus.CLOSED, "๊ณผ๊ฑฐ ๋งค์น˜ 1", 10), // ๊ณผ๊ฑฐ - ์ข…๋ฃŒ๋จ
createMatchInfoWithDetails(
2L, MatchStatus.CANCELLED, "์ทจ์†Œ๋œ ๋งค์น˜", 8), // ๊ณผ๊ฑฐ - ์ทจ์†Œ๋จ
2L, MatchStatus.CANCELED, "์ทจ์†Œ๋œ ๋งค์น˜", 8), // ๊ณผ๊ฑฐ - ์ทจ์†Œ๋จ
createMatchInfoWithDetails(3L, MatchStatus.OPEN, "๋ฏธ๋ž˜ ๋งค์น˜ 1", 12), // ๋ฏธ๋ž˜ - ๋ชจ์ง‘์ค‘
createMatchInfoWithDetails(
4L, MatchStatus.FULL, "๋ฏธ๋ž˜ ๋งค์น˜ 2", 10), // ๋ฏธ๋ž˜ - ๋ชจ์ง‘์™„๋ฃŒ
Expand All @@ -308,7 +308,7 @@ void getMyParticipantTimeBasedMatchesShouldProcessCorrectly() {
// then
assertThat(result.summary().totalCount()).isEqualTo(5L);
assertThat(result.summary().upcomingCount()).isEqualTo(3L); // OPEN(2) + FULL(1)
assertThat(result.summary().completedCount()).isEqualTo(2L); // CLOSED(1) + CANCELLED(1)
assertThat(result.summary().completedCount()).isEqualTo(2L); // CLOSED(1) + CANCELED(1)
}

// ํ…Œ์ŠคํŠธ ํ—ฌํผ ๋ฉ”์„œ๋“œ
Expand All @@ -332,7 +332,7 @@ private MyMatchesResponse.MatchInfo createMatchInfoWithDetails(
LocalDateTime matchTime =
switch (status) {
case OPEN, FULL -> baseTime.plusDays(2); // ๋ฏธ๋ž˜ ๋งค์น˜
case CLOSED, CANCELLED -> baseTime.minusDays(1); // ๊ณผ๊ฑฐ ๋งค์น˜
case CLOSED, CANCELED -> baseTime.minusDays(1); // ๊ณผ๊ฑฐ ๋งค์น˜
};

return new MyMatchesResponse.MatchInfo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,8 @@ void setUp() {
testMatch.setCreatedBy(adminUser);

// ๊ฒฝ๊ธฐ ์ƒ์„ฑ ์š”์ฒญ DTO
createRequestDto = new MatchCreateRequestDto();
createRequestDto.setCreatedBy(1L);
createRequestDto.setMatchDateTime(LocalDateTime.now().plusDays(1));
createRequestDto.setLocation("์„œ์šธ ๊ฐ•๋‚จ๊ตฌ");
createRequestDto.setMaxPlayers(10);
createRequestDto =
new MatchCreateRequestDto(1L, LocalDateTime.now().plusDays(1), "์„œ์šธ ๊ฐ•๋‚จ๊ตฌ", 10);
}

@Test
Expand All @@ -89,8 +86,8 @@ void getAllMatchesSuccess() {

// then
assertThat(result).hasSize(1);
assertThat(result.get(0).getId()).isEqualTo(testMatch.getId());
assertThat(result.get(0).getLocation()).isEqualTo(testMatch.getLocation());
assertThat(result.get(0).id()).isEqualTo(testMatch.getId());
assertThat(result.get(0).location()).isEqualTo(testMatch.getLocation());
verify(matchRepository).findAllByOrderByMatchDateTimeDesc();
}

Expand Down Expand Up @@ -124,11 +121,15 @@ void createMatchUserNotFound() {
@Test
@DisplayName("๊ฒฝ๊ธฐ ๊ฐœ์„ค - ๊ด€๋ฆฌ์ž ๊ถŒํ•œ ์—†์Œ ์˜ˆ์™ธ")
void createMatchNotAdminUser() {
// given
createRequestDto.setCreatedBy(2L);
// createRequestDto๋Š” setter๊ฐ€ ์—†์œผ๋ฏ€๋กœ ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
createRequestDto =
new MatchCreateRequestDto(
2L,
createRequestDto.matchDateTime(),
createRequestDto.location(),
createRequestDto.maxPlayers());
given(userRepository.findById(2L)).willReturn(Optional.of(regularUser));

// when & then
assertThatThrownBy(() -> matchService.createMatch(createRequestDto))
.isInstanceOf(IllegalStateException.class)
.hasMessage("๊ด€๋ฆฌ์ž ๊ถŒํ•œ์ด ์žˆ๋Š” ์‚ฌ์šฉ์ž๋งŒ ๊ฒฝ๊ธฐ๋ฅผ ๊ฐœ์„คํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.");
Expand Down
Loading