Skip to content

Commit

Permalink
Merge pull request #116 from hufscheer/feat/#107-timeline
Browse files Browse the repository at this point in the history
[FEAT] 타임라인 선수 교체 정보 관련 기능 추가
  • Loading branch information
ldk980130 authored Feb 27, 2024
2 parents 14d3a8f + d849c15 commit 1a15a82
Show file tree
Hide file tree
Showing 23 changed files with 720 additions and 179 deletions.
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

0 comments on commit 1a15a82

Please sign in to comment.