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] 게임 등록 기능 구현 #187

Merged
merged 23 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
35ee5b0
[FEAT] 게임 등록 기능 구현 #183
Jin409 Aug 7, 2024
ff4ce9a
[CHORE] 게임 등록이 인증 인가를 거치도록 추가 #183
Jin409 Aug 7, 2024
51555a1
[TEST] 인수 테스트 추가 #183
Jin409 Aug 7, 2024
856110f
[FIX] 누락된 @RequestBody 추가 #183
Jin409 Aug 7, 2024
92b77ea
[REFACTOR] 코드 가독성 개선 #183
Jin409 Aug 7, 2024
6528741
[TEST] 서비스 테스트 추가 #183
Jin409 Aug 7, 2024
d4edf80
[DEL] LeagueTeamPlayerFixtureRepository 를 test/support/fixture 로 이동 #183
Jin409 Aug 7, 2024
52303c8
[DOCS] 문서화 #183
Jin409 Aug 7, 2024
9c1828d
[TEST] 스포츠 fixture 추가에 따른 테스트 수정 #183
Jin409 Aug 7, 2024
623fb7e
Merge branch 'main' of https://github.com/hufscheer/spectator-server …
Jin409 Aug 7, 2024
59a1c2d
[DOCS] 문서화 #183
Jin409 Aug 7, 2024
40e72c6
[REFACTOR] 라인업선수 생성 캡슐화 #183
Jin409 Aug 7, 2024
bb78cf0
[REFACTOR] 하나의 메서드가 한가지의 기능만 하도록 수정 #183
Jin409 Aug 7, 2024
3a9f994
[MERGE] 메인 브랜치 변경 사항 반영 #183
Jin409 Aug 8, 2024
9aa1bc4
[REFACTOR] assertj 를 사용하도록 리팩토링 #183
Jin409 Aug 8, 2024
f1bd003
[TEST] 누락된 @Transactional 어노테이션 추가 #183
Jin409 Aug 8, 2024
6880dfc
[MERGE] 메인 브랜치와 머지 #183
Jin409 Aug 10, 2024
6caadbc
[REFACTOR] 가독성 향상 #183
Jin409 Aug 10, 2024
930f5a3
[DEL] 사용되지 않는 어노테이션 삭제 #183
Jin409 Aug 10, 2024
90504b6
[FIX] 오류 수정 #183
Jin409 Aug 12, 2024
db01700
[TEST] 테스트 분리 #183
Jin409 Aug 12, 2024
22ded1c
[MERGE] 메인 브랜치 반영 #183
Jin409 Aug 17, 2024
e1a1d9c
Merge branch 'main' of https://github.com/hufscheer/spectator-server …
Jin409 Aug 21, 2024
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
7 changes: 7 additions & 0 deletions src/docs/asciidoc/api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ operation::game-query-controller-test/응원_횟수를_조회한다[snippets='ht

operation::game-query-controller-test/라인업을_조회한다[snippets='http-request,path-parameters,http-response,response-fields']

=== 게임 등록

operation::game-controller-test/경기를_등록한다[snippets='http-request,path-parameters,http-response']

== 라인업 API

=== 라인업 선수 선발로 변경
Expand Down Expand Up @@ -97,16 +101,19 @@ operation::league-query-controller-test/리그를_하나_조회한다[snippets='
== 타임라인 API

=== 득점 타임라인 생성

operation::timeline-controller-test/득점_타임라인을_생성한다[snippets='http-request,path-parameters,http-response']

=== 교체 타임라인 생성

operation::timeline-controller-test/교체_타임라인을_생성한다[snippets='http-request,path-parameters,http-response']

=== 게임의 타임라인 조회

operation::timeline-query-controller-test/타임라인을_조회한다[snippets='http-request,path-parameters,http-response,response-fields']

=== 타임라인 삭제

operation::timeline-controller-test/타임라인을_삭제한다[snippets='http-request,request-cookies,path-parameters,http-response']

== 스포츠 API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc
mvc.pattern(HttpMethod.PUT, "/leagues/{leagueId}/teams/{teamId}"),
mvc.pattern(HttpMethod.DELETE, "/leagues/{leagueId}/teams/{teamId}"),
mvc.pattern(HttpMethod.POST, "/leagues/{leagueId}/teams/{teamId}/delete-logo"),
mvc.pattern(HttpMethod.POST, "/leagues/{leagueId}/games"),
mvc.pattern(HttpMethod.POST, "/games/*/timelines/**")
)
.authenticated()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.sports.server.command.game.application;

import com.sports.server.auth.exception.AuthorizationErrorMessages;
import com.sports.server.command.game.domain.Game;
import com.sports.server.command.game.domain.GameRepository;
import com.sports.server.command.game.domain.GameTeam;
import com.sports.server.command.game.dto.GameRequestDto;
import com.sports.server.command.league.domain.League;
import com.sports.server.command.leagueteam.domain.LeagueTeam;
import com.sports.server.command.member.domain.Member;
import com.sports.server.command.sport.domain.Sport;
import com.sports.server.command.sport.domain.SportRepository;
import com.sports.server.common.application.EntityUtils;
import com.sports.server.common.exception.NotFoundException;
import com.sports.server.common.exception.UnauthorizedException;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class GameService {

private final EntityUtils entityUtils;
private final GameRepository gameRepository;
private final SportRepository sportRepository;
private static final String NAME_OF_SPORT = "축구";

@Transactional
public void register(final Long leagueId,
final GameRequestDto.Register requestDto,
final Member manager) {
Game game = saveGame(leagueId, manager, requestDto);

List<LeagueTeam> leagueTeams = List.of(getLeagueTeam(requestDto.idOfTeam1()),
getLeagueTeam(requestDto.idOfTeam2()));

List<GameTeam> gameTeams = saveGameTeams(requestDto, game, leagueTeams);
copyPlayers(gameTeams, leagueTeams);

gameRepository.save(game);
}

private List<GameTeam> saveGameTeams(GameRequestDto.Register requestDto, Game game, List<LeagueTeam> leagueTeams) {
GameTeam gameTeam1 = createGameTeam(game, leagueTeams.get(0));
GameTeam gameTeam2 = createGameTeam(game, leagueTeams.get(1));

game.addTeam(gameTeam1);
game.addTeam(gameTeam2);

return List.of(gameTeam1, gameTeam2);
}

private void copyPlayers(List<GameTeam> gameTeams, List<LeagueTeam> leagueTeams) {
copyPlayersToLineup(gameTeams.get(0), leagueTeams.get(0));
copyPlayersToLineup(gameTeams.get(1), leagueTeams.get(1));
}

private LeagueTeam getLeagueTeam(Long teamId) {
return entityUtils.getEntity(teamId, LeagueTeam.class);
}

private GameTeam createGameTeam(Game game, LeagueTeam leagueTeam) {
return new GameTeam(game, leagueTeam);
}

private void copyPlayersToLineup(GameTeam gameTeam, LeagueTeam leagueTeam) {
leagueTeam.getLeagueTeamPlayers().stream()
.forEach(gameTeam::registerLineup);
}

private Game saveGame(Long leagueId, Member manager, GameRequestDto.Register requestDto) {
Sport sport = getSport(NAME_OF_SPORT);
League league = getLeagueAndCheckPermission(leagueId, manager);
return requestDto.toEntity(sport, manager, league);
}

private Sport getSport(String sportName) {
return sportRepository.findByName(sportName)
.orElseThrow(() -> new NotFoundException("해당 이름을 가진 스포츠가 존재하지 않습니다."));
}

private League getLeagueAndCheckPermission(final Long leagueId, final Member manager) {
League league = entityUtils.getEntity(leagueId, League.class);

if (!league.isManagedBy(manager)) {
throw new UnauthorizedException(AuthorizationErrorMessages.PERMISSION_DENIED);
}

return league;
}
}
16 changes: 15 additions & 1 deletion src/main/java/com/sports/server/command/game/domain/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.sports.server.command.member.domain.Member;
import com.sports.server.command.sport.domain.Sport;
import com.sports.server.common.domain.BaseEntity;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
Expand Down Expand Up @@ -40,7 +41,7 @@ public class Game extends BaseEntity<Game> {
@JoinColumn(name = "league_id")
private League league;

@OneToMany(mappedBy = "game")
@OneToMany(mappedBy = "game", cascade = CascadeType.ALL, orphanRemoval = true)
private List<GameTeam> teams = new ArrayList<>();

@Column(name = "name", nullable = false)
Expand Down Expand Up @@ -110,4 +111,17 @@ public void cancelScore(LineupPlayer scorer) {

scoredTeam.cancelScore();
}

public Game(Sport sport, Member manager, League league, String name, LocalDateTime startTime,
String videoId, String gameQuarter, GameState state, Round round) {
this.sport = sport;
this.manager = manager;
this.league = league;
this.name = name;
this.startTime = startTime;
this.videoId = videoId;
this.gameQuarter = gameQuarter;
this.state = state;
this.round = round;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.sports.server.command.game.domain;

import org.springframework.data.repository.Repository;

public interface GameRepository extends Repository<Game, Long> {
Game save(Game game);
}
30 changes: 24 additions & 6 deletions src/main/java/com/sports/server/command/game/domain/GameTeam.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
package com.sports.server.command.game.domain;

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

import com.sports.server.command.leagueteam.domain.LeagueTeam;
import com.sports.server.command.leagueteam.domain.LeagueTeamPlayer;
import com.sports.server.common.domain.BaseEntity;
import com.sports.server.common.exception.CustomException;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import org.springframework.http.HttpStatus;

@Entity
Expand All @@ -39,7 +38,7 @@ public class GameTeam extends BaseEntity<GameTeam> {
@JoinColumn(name = "league_team_id")
private LeagueTeam leagueTeam;

@OneToMany(mappedBy = "gameTeam")
@OneToMany(mappedBy = "gameTeam", cascade = CascadeType.ALL, orphanRemoval = true)
private List<LineupPlayer> lineupPlayers = new ArrayList<>();

@Column(name = "cheer_count", nullable = false)
Expand Down Expand Up @@ -84,6 +83,25 @@ public void cancelScore() {
this.score -= SCORE_VALUE;
}
}

public GameTeam(Game game, LeagueTeam leagueTeam) {
this.game = game;
this.leagueTeam = leagueTeam;
this.cheerCount = 0;
this.score = 0;
}

public void registerLineup(LeagueTeamPlayer player) {
LineupPlayer lineupPlayer = new LineupPlayer(
this,
player.getId(),
player.getName(),
player.getNumber(),
false,
LineupPlayerState.CANDIDATE);

this.lineupPlayers.add(lineupPlayer);
}
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package com.sports.server.command.game.domain;

import static com.sports.server.command.game.domain.LineupPlayerState.*;

import org.springframework.http.HttpStatus;
import static com.sports.server.command.game.domain.LineupPlayerState.CANDIDATE;
import static com.sports.server.command.game.domain.LineupPlayerState.STARTER;

import com.sports.server.common.domain.BaseEntity;
import com.sports.server.common.exception.CustomException;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
Expand All @@ -15,11 +13,11 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.Objects;
import org.springframework.http.HttpStatus;

@Entity
@Getter
Expand Down Expand Up @@ -71,4 +69,14 @@ public boolean isSameTeam(LineupPlayer other) {
public boolean isInTeam(GameTeam team) {
return Objects.equals(this.gameTeam, team);
}

public LineupPlayer(GameTeam gameTeam, Long leagueTeamPlayerId, String name, int number,
boolean isCaptain, LineupPlayerState state) {
this.gameTeam = gameTeam;
this.leagueTeamPlayerId = leagueTeamPlayerId;
this.name = name;
this.number = number;
this.isCaptain = isCaptain;
this.state = state;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.sports.server.command.game.dto;

import com.sports.server.command.game.domain.Game;
import com.sports.server.command.game.domain.GameState;
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.Sport;
import java.time.LocalDateTime;

public class GameRequestDto {
public record Register(
String name,
String round,
String quarter,
String state,
LocalDateTime startTime,
Long idOfTeam1,
Long idOfTeam2,
String videoId
) {
public Game toEntity(Sport sport, Member manager, League league) {
return new Game(sport, manager, league, name, startTime, videoId, quarter, GameState.from(state),
Round.from(round));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.sports.server.command.game.presentation;

import com.sports.server.command.game.application.GameService;
import com.sports.server.command.game.application.GameTeamService;
import com.sports.server.command.game.application.LineupPlayerService;
import com.sports.server.command.game.dto.CheerCountUpdateRequest;
import com.sports.server.command.game.dto.GameRequestDto;
import com.sports.server.command.member.domain.Member;
import jakarta.validation.Valid;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PatchMapping;
Expand All @@ -15,30 +19,39 @@

@RestController
@RequiredArgsConstructor
@RequestMapping("/games")
@RequestMapping
Jin409 marked this conversation as resolved.
Show resolved Hide resolved
public class GameController {

private final GameTeamService gameTeamService;
private final LineupPlayerService lineupPlayerService;
private final GameTeamService gameTeamService;
private final LineupPlayerService lineupPlayerService;
private final GameService gameService;

@PostMapping("/{gameId}/cheer")
public ResponseEntity<Void> updateCheerCount(@PathVariable final Long gameId,
@RequestBody @Valid CheerCountUpdateRequest cheerRequestDto) {
gameTeamService.updateCheerCount(gameId, cheerRequestDto);
return ResponseEntity.ok().build();
}
@PostMapping("/games/{gameId}/cheer")
public ResponseEntity<Void> updateCheerCount(@PathVariable final Long gameId,
@RequestBody @Valid CheerCountUpdateRequest cheerRequestDto) {
Jin409 marked this conversation as resolved.
Show resolved Hide resolved
gameTeamService.updateCheerCount(gameId, cheerRequestDto);
return ResponseEntity.ok().build();
}

@PatchMapping("/{gameId}/lineup-players/{lineupPlayerId}/starter")
public ResponseEntity<Void> changePlayerStateToStarter(@PathVariable final Long gameId,
@PathVariable final Long lineupPlayerId) {
lineupPlayerService.changePlayerStateToStarter(gameId, lineupPlayerId);
return ResponseEntity.ok().build();
}
@PatchMapping("/games/{gameId}/lineup-players/{lineupPlayerId}/starter")
public ResponseEntity<Void> changePlayerStateToStarter(@PathVariable final Long gameId,
@PathVariable final Long lineupPlayerId) {
lineupPlayerService.changePlayerStateToStarter(gameId, lineupPlayerId);
return ResponseEntity.ok().build();
}

@PatchMapping("/{gameId}/lineup-players/{lineupPlayerId}/candidate")
public ResponseEntity<Void> changePlayerStateToCandidate(@PathVariable final Long gameId,
@PathVariable final Long lineupPlayerId) {
lineupPlayerService.changePlayerStateToCandidate(gameId, lineupPlayerId);
return ResponseEntity.ok().build();
}
@PatchMapping("/games/{gameId}/lineup-players/{lineupPlayerId}/candidate")
public ResponseEntity<Void> changePlayerStateToCandidate(@PathVariable final Long gameId,
@PathVariable final Long lineupPlayerId) {
lineupPlayerService.changePlayerStateToCandidate(gameId, lineupPlayerId);
return ResponseEntity.ok().build();
}

@PostMapping("/leagues/{leagueId}/games")
public ResponseEntity<Void> registerGame(@PathVariable final Long leagueId,
@RequestBody final GameRequestDto.Register requestDto,
final Member member) {
gameService.register(leagueId, requestDto, member);
return ResponseEntity.created(URI.create("")).build();
}
Jin409 marked this conversation as resolved.
Show resolved Hide resolved
}

This file was deleted.

Loading
Loading