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

[FEAT] 게임 진행 타임라인 내부 로직 구현 #237

Merged
merged 15 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from 10 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
33 changes: 33 additions & 0 deletions src/main/java/com/sports/server/command/game/domain/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.sports.server.command.league.domain.League;
import com.sports.server.command.league.domain.Round;
import com.sports.server.command.member.domain.Member;
import com.sports.server.command.sport.domain.Quarter;
import com.sports.server.command.sport.domain.Sport;
import com.sports.server.common.domain.BaseEntity;
import com.sports.server.common.exception.CustomException;
Expand Down Expand Up @@ -142,4 +143,36 @@ private void validateGameTeam(final GameTeam gameTeam) {
throw new CustomException(HttpStatus.BAD_REQUEST, "해당 게임팀은 이 게임에 포함되지 않습니다.");
}
}

public void updateState(GameState state) {
this.state = state;
}

public void play() {
this.state = GameState.PLAYING;
updateQuarter(sport.getAfterStartQuarter());
}

public void end() {
this.state = GameState.FINISHED;
updateQuarter(sport.getEndQuarter());
}

public void updateQuarter(Quarter quarter) {
this.gameQuarter = quarter.getName();
this.quarterChangedAt = LocalDateTime.now();
}

public void updateQuarter(Quarter quarter, LocalDateTime changedAt) {
this.gameQuarter = quarter.getName();
this.quarterChangedAt = changedAt;
}

public Quarter getQuarter() {
return sport.getQuarters()
.stream()
.filter(quarter -> quarter.getName().equals(gameQuarter))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("해당 쿼터가 존재하지 않습니다."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@ public static GameState from(final String value) {
.findFirst().orElseThrow(
() -> new CustomException(HttpStatus.BAD_REQUEST, GameErrorMessages.STATE_NOT_FOUND_EXCEPTION));
}

}
11 changes: 11 additions & 0 deletions src/main/java/com/sports/server/command/sport/domain/Quarter.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,15 @@ public class Quarter extends BaseEntity<Quarter> {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sports_id")
private Sport sport;

@Column(name = "_order", nullable = false)
private Integer order = 0;

public boolean isOrder(Integer order) {
return this.order.equals(order);
}

public boolean isPreviousThan(Quarter other) {
return this.order < other.order;
}
}
23 changes: 23 additions & 0 deletions src/main/java/com/sports/server/command/sport/domain/Sport.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,40 @@
import com.sports.server.common.domain.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

@Entity
@Table(name = "sports")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Sport extends BaseEntity<Sport> {

private static Integer AFTER_START_QUARTER_ORDER = 2;

@Column(name = "name", nullable = false)
private String name;

@OneToMany(mappedBy = "sport")
private List<Quarter> quarters = new ArrayList<>();

public Quarter getAfterStartQuarter() {
return quarters.stream()
.filter(quarter -> quarter.isOrder(AFTER_START_QUARTER_ORDER))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("경기 시작 후의 쿼터가 존재하지 않습니다."));
}

public Quarter getEndQuarter() {
return quarters.stream()
.max(Comparator.comparing(Quarter::getOrder))
.orElseThrow(() -> new IllegalArgumentException("최종 쿼터가 존재하지 않습니다."));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 예외가 던져지는 상황은 Quarter 가 하나도 없을 때 아닌가요?!
그냥 쿼터가 존재하지 않는다고 해도 되지 않을런지... getAfterStartQuarter() 도 마찬가지로!!

}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package com.sports.server.command.timeline.domain;

import com.sports.server.command.game.domain.Game;
import com.sports.server.command.game.domain.GameState;
import com.sports.server.command.sport.domain.Quarter;
import com.sports.server.common.exception.CustomException;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpStatus;

import java.time.LocalDateTime;

@Entity
@DiscriminatorValue("GAME_PROGRESS")
Expand All @@ -17,6 +22,12 @@ public class GameProgressTimeline extends Timeline {
@Column(name = "game_progress_type")
private GameProgressType gameProgressType;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "previous_quarter_id")
private Quarter previousQuarter;

private LocalDateTime previousQuarterChangedAt;

public GameProgressTimeline(
Game game,
Quarter quarter,
Expand All @@ -25,6 +36,16 @@ public GameProgressTimeline(
) {
super(game, quarter, recordedAt);
this.gameProgressType = gameProgressType;
this.previousQuarter = game.getQuarter();
this.previousQuarterChangedAt = game.getQuarterChangedAt();

validateQuarter();
}

private void validateQuarter() {
if (recordedQuarter.isPreviousThan(previousQuarter)) {
throw new CustomException(HttpStatus.BAD_REQUEST, "이전 쿼터로의 진행은 불가능합니다.");
}
}

@Override
Expand All @@ -34,11 +55,29 @@ public TimelineType getType() {

@Override
public void apply() {
// TODO Game 상태 변경
if (gameProgressType == GameProgressType.GAME_START) {
game.play();
}

if (gameProgressType == GameProgressType.QUARTER_START) {
game.updateQuarter(recordedQuarter);
}

if (gameProgressType == GameProgressType.GAME_END) {
game.end();
}
}

@Override
public void rollback() {
// TODO 게임 상태 변경 타임라인 생성 로직 구현 이후 구현 예정
game.updateQuarter(previousQuarter, previousQuarterChangedAt);

if (gameProgressType == GameProgressType.GAME_START) {
game.updateState(GameState.SCHEDULED);
}

if (gameProgressType == GameProgressType.GAME_END) {
game.updateState(GameState.PLAYING);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public List<TimelineResponse> getTimelines(final Long gameId) {

return timelines.keySet()
.stream()
.sorted(comparingLong(Quarter::getId).reversed())
.sorted(comparingLong(Quarter::getOrder).reversed().thenComparing(Quarter::getId).reversed())
.map(quarter -> TimelineResponse.of(
quarter.getName(),
timelines.get(quarter)
Expand Down
8 changes: 8 additions & 0 deletions src/main/resources/db/migration/V18__add_quarter_order.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ALTER TABLE quarters ADD COLUMN _order INT DEFAULT 0;

ALTER TABLE timelines ADD COLUMN previous_quarter_id BIGINT NULL;

ALTER TABLE timelines
ADD CONSTRAINT FK_TIMELINES_ON_PREV_QUARTER FOREIGN KEY (previous_quarter_id) REFERENCES quarters (id);

ALTER TABLE timelines ADD COLUMN previous_quarter_changed_at DATETIME NULL;
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
package com.sports.server.command.timeline.application;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertAll;

import com.sports.server.command.member.domain.Member;
import com.sports.server.command.member.domain.MemberRepository;
import com.sports.server.command.timeline.TimelineFixtureRepository;
import com.sports.server.command.timeline.domain.ReplacementTimeline;
import com.sports.server.command.timeline.domain.ScoreTimeline;
import com.sports.server.command.timeline.domain.*;
import com.sports.server.command.timeline.dto.TimelineRequest;
import com.sports.server.common.exception.CustomException;
import com.sports.server.support.ServiceTest;
Expand All @@ -21,6 +16,10 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.jdbc.Sql;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertAll;

@Sql(scripts = "/timeline-fixture.sql")
class TimelineServiceTest extends ServiceTest {
@Autowired
Expand Down Expand Up @@ -190,6 +189,27 @@ class CreateReplacementTest {
}
}

@Nested
@DisplayName("게임 진행 타임라인을")
class GameProgressTimelineTest {
@Test
void 생성한다() {
// given
TimelineRequest.RegisterProgress request = new TimelineRequest.RegisterProgress(
10,
3L,
GameProgressType.QUARTER_START
);

// when
timelineService.register(manager, gameId, request);

// then
Timeline actual = timelineFixtureRepository.findAllLatest(gameId).get(0);
assertThat(actual).isInstanceOf(GameProgressTimeline.class);
}
}

@DisplayName("타임라인을 삭제할 때")
@Nested
class DeleteTest {
Expand Down
Loading
Loading