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] 타임라인 선수 교체 정보 관련 기능 추가 #116

Merged
merged 17 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion src/docs/asciidoc/api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ operation::league-query-controller-test/리그의_모든_리그팀을_조회한

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

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

== 스포츠 API

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sports.server.query.application.timeline;

import com.sports.server.query.dto.response.RecordResponse;

import java.util.List;

public interface RecordQueryService {

List<RecordResponse> findByGameId(Long gameId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.sports.server.query.application.timeline;

import com.sports.server.query.dto.mapper.RecordMapper;
import com.sports.server.query.dto.response.RecordResponse;
import com.sports.server.query.repository.ReplacementRecordQueryRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ReplacementRecordQueryService implements RecordQueryService {

private final ReplacementRecordQueryRepository replacementRecordQueryRepository;
private final RecordMapper recordMapper;

@Override
public List<RecordResponse> findByGameId(Long gameId) {
return replacementRecordQueryRepository.findByGameId(gameId)
.stream()
.map(recordMapper::toRecordResponse)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.sports.server.query.application.timeline;

import com.sports.server.command.game.domain.GameTeam;
import com.sports.server.command.record.domain.ScoreRecord;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.stream.Collectors.toMap;

@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class ScoreHistory {

private final Map<ScoreRecord, ScoreSnapshot> snapshots;

public static ScoreHistory of(List<ScoreRecord> scoreRecords, List<GameTeam> gameTeams) {
Map<ScoreRecord, ScoreSnapshot> snapshots = new HashMap<>();
Map<GameTeam, Integer> scores = initializeScores(gameTeams);
scoreRecords.forEach(record -> {
applyScore(scores, record);
ScoreSnapshot snapshot = generateSnapshot(scores, gameTeams);
snapshots.put(record, snapshot);
});
return new ScoreHistory(snapshots);
}

private static Map<GameTeam, Integer> initializeScores(List<GameTeam> gameTeams) {
return gameTeams.stream()
.collect(toMap(gameTeam -> gameTeam, gameTeam -> 0));
}

private static void applyScore(Map<GameTeam, Integer> scores, ScoreRecord record) {
GameTeam gameTeam = record.getRecord().getGameTeam();
int score = record.getScore();
scores.put(gameTeam, scores.get(gameTeam) + score);
}

private static ScoreSnapshot generateSnapshot(Map<GameTeam, Integer> scores,
List<GameTeam> gameTeams) {
Map<GameTeam, Integer> snapshot = new HashMap<>();
gameTeams.forEach(team -> snapshot.put(team, scores.get(team)));
return new ScoreSnapshot(snapshot);
}

public List<ScoreRecord> getScoreRecordsOrderByTimeDesc() {
return snapshots.keySet().stream()
.sorted((r1, r2) -> Integer.compare(r2.getRecord().getRecordedAt(), r1.getRecord().getRecordedAt()))
.toList();
}

public ScoreSnapshot getSnapshot(ScoreRecord scoreRecord) {
return snapshots.get(scoreRecord);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.sports.server.query.application.timeline;

import com.sports.server.query.dto.mapper.RecordMapper;
import com.sports.server.query.dto.response.RecordResponse;
import com.sports.server.query.repository.GameTeamQueryRepository;
import com.sports.server.query.repository.ScoreRecordQueryRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ScoreRecordQueryService implements RecordQueryService {

private final ScoreRecordQueryRepository scoreRecordQueryRepository;
private final GameTeamQueryRepository gameTeamQueryRepository;
private final RecordMapper recordMapper;

@Override
public List<RecordResponse> findByGameId(Long gameId) {
ScoreHistory scoreHistory = ScoreHistory.of(
scoreRecordQueryRepository.findByGameId(gameId),
gameTeamQueryRepository.findAllByGameWithTeam(gameId)
);
return scoreHistory.getScoreRecordsOrderByTimeDesc()
.stream()
.map(record -> recordMapper.toRecordResponse(record, scoreHistory.getSnapshot(record)))
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.sports.server.query.application.timeline;

import com.sports.server.command.game.domain.GameTeam;

import java.util.Comparator;
import java.util.List;
import java.util.Map;


public class ScoreSnapshot {

private final Map<GameTeam, Integer> values;

public ScoreSnapshot(Map<GameTeam, Integer> values) {
this.values = values;
}

public Integer getScore(GameTeam team) {
return values.get(team);
}

public List<GameTeam> getTeamsOrderById() {
return values.keySet()
.stream()
.sorted(Comparator.comparingLong(GameTeam::getId))
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.sports.server.query.application.timeline;

import com.sports.server.command.sport.domain.Quarter;
import com.sports.server.query.dto.response.RecordResponse;
import com.sports.server.query.dto.response.TimelineResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;

import static java.util.Comparator.comparingInt;
import static java.util.Comparator.comparingLong;
import static java.util.stream.Collectors.groupingBy;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class TimelineQueryService {

private final List<RecordQueryService> recordQueryServices;

public List<TimelineResponse> getTimeline(final Long gameId) {
Map<Quarter, List<RecordResponse>> records = getRecordsGroupByQuarter(gameId);
return records.keySet()
.stream()
.sorted(comparingLong(Quarter::getId).reversed())
.map(quarter -> new TimelineResponse(
quarter.getName(),
records.get(quarter)
)).toList();

}

private Map<Quarter, List<RecordResponse>> getRecordsGroupByQuarter(Long gameId) {
return recordQueryServices.stream()
.flatMap(recordQueryService -> recordQueryService.findByGameId(gameId).stream())
.sorted(comparingInt(RecordResponse::recordedAt).reversed())
.collect(groupingBy(RecordResponse::quarter));
}
}
71 changes: 71 additions & 0 deletions src/main/java/com/sports/server/query/dto/mapper/RecordMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.sports.server.query.dto.mapper;

import com.sports.server.command.game.domain.GameTeam;
import com.sports.server.command.leagueteam.LeagueTeam;
import com.sports.server.command.record.domain.Record;
import com.sports.server.command.record.domain.ReplacementRecord;
import com.sports.server.command.record.domain.ScoreRecord;
import com.sports.server.query.application.timeline.ScoreSnapshot;
import com.sports.server.query.dto.response.RecordResponse;
import com.sports.server.query.dto.response.ReplacementRecordResponse;
import com.sports.server.query.dto.response.ScoreRecordResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;


@Component
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class RecordMapper {

public RecordResponse toRecordResponse(ReplacementRecord replacementRecord) {
Record record = replacementRecord.getRecord();
LeagueTeam team = record.getGameTeam().getLeagueTeam();
return new RecordResponse(
record.getRecordedQuarter(),
record.getRecordType().name(),
record.getRecordedAt(),
replacementRecord.getOriginLineupPlayer().getName(),
team.getName(),
team.getLogoImageUrl(),
null,
new ReplacementRecordResponse(replacementRecord.getReplacedLineupPlayer().getName())
);
}

public RecordResponse toRecordResponse(ScoreRecord scoreRecord, ScoreSnapshot snapshot) {
Record record = scoreRecord.getRecord();
int score = scoreRecord.getScore();
LeagueTeam team = record.getGameTeam().getLeagueTeam();
return new RecordResponse(
record.getRecordedQuarter(),
record.getRecordType().name(),
record.getRecordedAt(),
scoreRecord.getLineupPlayer().getName(),
team.getName(),
team.getLogoImageUrl(),
new ScoreRecordResponse(score, toSnapshotResponses(snapshot)),
null
);
}

private List<ScoreRecordResponse.Snapshot> toSnapshotResponses(ScoreSnapshot snapshot) {
return snapshot.getTeamsOrderById()
.stream()
.map(gameTeam -> toSnapshotResponse(snapshot, gameTeam))
.toList();
}

private ScoreRecordResponse.Snapshot toSnapshotResponse(ScoreSnapshot snapshot,
GameTeam gameTeam) {
LeagueTeam leagueTeam = gameTeam.getLeagueTeam();
return new ScoreRecordResponse.Snapshot(
leagueTeam.getName(),
leagueTeam.getLogoImageUrl(),
snapshot.getScore(gameTeam)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.sports.server.query.dto.response;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sports.server.command.leagueteam.LeagueTeam;
import com.sports.server.command.record.domain.Record;
import com.sports.server.command.record.domain.ScoreRecord;
import com.sports.server.command.sport.domain.Quarter;

public record RecordResponse(
@JsonIgnore
Quarter quarter,
String type,
Integer recordedAt,
String playerName,
String teamName,
String teamImageUrl,
ScoreRecordResponse scoreRecord,
ReplacementRecordResponse replacementRecord
) {

public static RecordResponse from(ScoreRecord scoreRecord, ScoreRecordResponse scoreRecordResponse) {
Record record = scoreRecord.getRecord();
LeagueTeam team = record.getGameTeam().getLeagueTeam();
return new RecordResponse(
record.getRecordedQuarter(),
record.getRecordType().name(),
record.getRecordedAt(),
scoreRecord.getLineupPlayer().getName(),
team.getName(),
team.getLogoImageUrl(),
scoreRecordResponse,
null
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sports.server.query.dto.response;

public record ReplacementRecordResponse(
String replacedPlayerName
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.sports.server.query.dto.response;

import java.util.List;

public record ScoreRecordResponse(
Integer score,
List<Snapshot> snapshot
) {

public record Snapshot(
String teamName,
String teamImageUrl,
Integer score
) {
}
}
Loading
Loading