Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 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
72 changes: 72 additions & 0 deletions src/main/java/com/opus/opus/docs/asciidoc/contest.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,75 @@ include::{snippets}/get-vote-statistics-fail-not-found/http-request.adoc[]
include::{snippets}/get-vote-statistics-fail-not-found/http-response.adoc[]

====

== 대회 전체 팀 조회

=== `GET`: 대회의 팀 목록 조회 (비회원용)

NOTE: 비회원용 메인 페이지 API입니다. `isLiked`와 `isVoted` 값이 항상 `false` 입니다.

.Path Parameters
include::{snippets}/get-contest-team-summaries-public/path-parameters.adoc[]

.HTTP Request
include::{snippets}/get-contest-team-summaries-public/http-request.adoc[]

.HTTP Response
include::{snippets}/get-contest-team-summaries-public/http-response.adoc[]

.Response Body's Fields
include::{snippets}/get-contest-team-summaries-public/response-fields.adoc[]

=== `GET`: 대회의 팀 목록 조회 (회원용 - 미투표 기간)

NOTE: 회원만 접근 가능한 메인 페이지용 API입니다. 미투표 기간에는 로그인한 사용자의 `isLiked` 여부가 포함됩니다.

.Path Parameters
include::{snippets}/get-contest-team-summaries-member-non-voting/path-parameters.adoc[]

.Request Headers
include::{snippets}/get-contest-team-summaries-member-non-voting/request-headers.adoc[]

.HTTP Request
include::{snippets}/get-contest-team-summaries-member-non-voting/http-request.adoc[]

.HTTP Response
include::{snippets}/get-contest-team-summaries-member-non-voting/http-response.adoc[]

.Response Body's Fields
include::{snippets}/get-contest-team-summaries-member-non-voting/response-fields.adoc[]

=== `GET`: 대회의 팀 목록 조회 (회원용 - 투표 기간)

NOTE: 회원만 접근 가능한 메인 페이지용 API입니다. 투표 기간에는 로그인한 사용자의 `isVoted` 여부가 포함됩니다.

.Path Parameters
include::{snippets}/get-contest-team-summaries-member-voting/path-parameters.adoc[]

.Request Headers
include::{snippets}/get-contest-team-summaries-member-voting/request-headers.adoc[]

.HTTP Request
include::{snippets}/get-contest-team-summaries-member-voting/http-request.adoc[]

.HTTP Response
include::{snippets}/get-contest-team-summaries-member-voting/http-response.adoc[]

.Response Body's Fields
include::{snippets}/get-contest-team-summaries-member-voting/response-fields.adoc[]

==== ⚠️ 실패 케이스

.❌ Case 1: 존재하지 않는 대회 ID

[%collapsible]
====
.HTTP Request
include::{snippets}/get-contest-team-summaries-fail-contest-not-found/http-request.adoc[]

.HTTP Response
include::{snippets}/get-contest-team-summaries-fail-contest-not-found/http-response.adoc[]

.Path Parameters
include::{snippets}/get-contest-team-summaries-fail-contest-not-found/path-parameters.adoc[]
====
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@
import com.opus.opus.modules.contest.application.dto.request.VoteUpdateRequest;
import com.opus.opus.modules.contest.application.dto.response.ContestCurrentResponse;
import com.opus.opus.modules.contest.application.dto.response.ContestCurrentToggleResponse;
import com.opus.opus.modules.contest.application.dto.response.ContestRankingResponse;
import com.opus.opus.modules.contest.application.dto.response.ContestResponse;
import com.opus.opus.modules.contest.application.dto.response.ContestSortResponse;
import com.opus.opus.modules.contest.application.dto.response.ContestVoteLogResponse;
import com.opus.opus.modules.contest.application.dto.response.ContestSubmissionResponse;
import com.opus.opus.modules.contest.application.dto.response.ContestVoteStatisticsResponse;
import com.opus.opus.modules.contest.application.dto.response.ContestVotesLimitResponse;
import com.opus.opus.modules.contest.application.dto.response.TeamSummaryResponse;
import com.opus.opus.modules.contest.application.dto.response.VotePeriodResponse;
import com.opus.opus.modules.member.domain.Member;
import com.opus.opus.modules.team.application.dto.ImageResponse;
import com.opus.opus.modules.team.application.dto.response.MemberVoteCountResponse;
import com.opus.opus.modules.contest.application.dto.response.ContestRankingResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.PositiveOrZero;
import java.util.List;
Expand Down Expand Up @@ -205,4 +206,20 @@ public ResponseEntity<List<ContestSubmissionResponse>> getTeamSubmissions(@PathV
final List<ContestSubmissionResponse> responses = contestQueryService.getTeamSubmissions(contestId);
return ResponseEntity.ok(responses);
}

@GetMapping("/{contestId}/teams")
public ResponseEntity<List<TeamSummaryResponse>> getAllContestTeamSummaries(
@PathVariable final Long contestId,
@LoginMember final Member member
) {
final List<TeamSummaryResponse> responses = contestQueryService.getContestTeamSummaries(contestId, member);
return ResponseEntity.ok(responses);
}

@GetMapping("/{contestId}/teams/public")
public ResponseEntity<List<TeamSummaryResponse>> getAllContestTeamSummariesPublic(
@PathVariable final Long contestId) {
final List<TeamSummaryResponse> responses = contestQueryService.getContestTeamSummariesPublic(contestId);
return ResponseEntity.ok(responses);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static org.springframework.data.domain.Sort.Direction.DESC;

import com.opus.opus.global.util.FileStorageUtil;
import com.opus.opus.modules.contest.application.convenience.ContestAwardConvenience;
import com.opus.opus.modules.contest.application.convenience.ContestCategoryConvenience;
import com.opus.opus.modules.contest.application.convenience.ContestConvenience;
import com.opus.opus.modules.contest.application.convenience.ContestSortConvenience;
Expand All @@ -19,7 +20,10 @@
import com.opus.opus.modules.contest.application.dto.response.ContestVoteStatisticsResponse;
import com.opus.opus.modules.contest.application.dto.response.VotePeriodResponse;
import com.opus.opus.modules.contest.application.dto.response.ContestVotesLimitResponse;
import com.opus.opus.modules.contest.application.dto.response.TeamSummaryResponse;
import com.opus.opus.modules.contest.application.dto.response.VotePeriodResponse;
import com.opus.opus.modules.contest.domain.Contest;
import com.opus.opus.modules.contest.domain.ContestAward;
import com.opus.opus.modules.contest.domain.ContestCategory;
import com.opus.opus.modules.contest.domain.ContestSort;
import com.opus.opus.modules.contest.domain.ContestTrack;
Expand All @@ -32,9 +36,15 @@
import com.opus.opus.modules.member.domain.Member;
import com.opus.opus.modules.team.application.convenience.TeamConvenience;
import com.opus.opus.modules.team.application.convenience.TeamVoteConvenience;
import com.opus.opus.modules.member.domain.Member;
import com.opus.opus.modules.team.application.convenience.TeamConvenience;
import com.opus.opus.modules.team.application.convenience.TeamLikeConvenience;
import com.opus.opus.modules.team.application.convenience.TeamVoteConvenience;
import com.opus.opus.modules.team.application.dto.ImageResponse;
import com.opus.opus.modules.team.domain.Team;
import com.opus.opus.modules.team.domain.TeamVote;
import com.opus.opus.modules.team.domain.Team;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import com.opus.opus.modules.team.application.dto.response.MemberVoteCountResponse;
Expand All @@ -46,6 +56,7 @@

import java.util.stream.Collectors;

import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.antlr.v4.runtime.misc.Pair;
import org.springframework.core.io.Resource;
Expand Down Expand Up @@ -73,7 +84,9 @@ public class ContestQueryService {
private final ContestSortConvenience contestSortConvenience;
private final TeamConvenience teamConvenience;
private final TeamVoteConvenience teamVoteConvenience;
private final TeamLikeConvenience teamLikeConvenience;
private final MemberConvenience memberConvenience;
private final ContestAwardConvenience contestAwardConvenience;
private final FileConvenience fileConvenience;

public ImageResponse getContestBanner(final Long contestId) {
Expand Down Expand Up @@ -215,10 +228,64 @@ private static List<ContestRankingResponse> applyDenseRanking(List<TeamRankingRe
return responseList;
}

public List<TeamSummaryResponse> getContestTeamSummaries(final Long contestId, final Member member) {
final Contest contest = contestConvenience.getValidateExistContest(contestId);
final List<Team> teams = teamConvenience.findAllByContestId(contestId);

final boolean isVotingPeriod = checkVotingPeriod(contest);

final ReactionMaps reactionMaps = getReactionMaps(contestId, member, isVotingPeriod);
final Map<Long, Boolean> voteMap = reactionMaps.voteMap();
final Map<Long, Boolean> likeMap = reactionMaps.likeMap();

final List<ContestAward> teamAwards = contestAwardConvenience.getTeamAwards(teams);

teamConvenience.shuffleTeams(teams, member);

return teams.stream()
.map(team -> TeamSummaryResponse.of(
team,
teamAwards,
likeMap.getOrDefault(team.getId(), false),
voteMap.getOrDefault(team.getId(), false)
))
.toList();
}

public List<TeamSummaryResponse> getContestTeamSummariesPublic(final Long contestId) {
return getContestTeamSummaries(contestId, null);
}

private ReactionMaps getReactionMaps(final Long contestId, final Member member, final boolean isVotingPeriod) {
if (member == null) {
return new ReactionMaps(Map.of(), Map.of());
}

if (isVotingPeriod) {
return new ReactionMaps(
teamVoteConvenience.getVoteMap(contestId, member),
Map.of()
);
} else {
return new ReactionMaps(
Map.of(),
teamLikeConvenience.getLikeMap(contestId, member)
);
}
}

private boolean checkVotingPeriod(final Contest contest) {
final LocalDateTime now = LocalDateTime.now();
return !now.isBefore(contest.getVoteStartAt())
&& !now.isAfter(contest.getVoteEndAt());
}

private void checkImageConverted(final File findFile) {
if (!findFile.getIsWebpConverted()) {
throw new FileException(NOT_WEBP_CONVERTED);
}
}

private record ReactionMaps(Map<Long, Boolean> voteMap, Map<Long, Boolean> likeMap) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import com.opus.opus.modules.contest.domain.ContestAward;
import com.opus.opus.modules.contest.domain.dao.ContestAwardRepository;
import com.opus.opus.modules.contest.exception.ContestAwardException;
import com.opus.opus.modules.team.domain.Team;
import com.opus.opus.modules.team.domain.TeamContestAward;
import com.opus.opus.modules.team.domain.dao.TeamContestAwardRepository;
import java.util.Collections;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
Expand All @@ -14,6 +18,7 @@
public class ContestAwardConvenience {

private final ContestAwardRepository contestAwardRepository;
private final TeamContestAwardRepository teamContestAwardRepository;

public List<ContestAward> findAllById(final List<Long> awardIds) {
final List<ContestAward> contestAwards = contestAwardRepository.findAllById(awardIds);
Expand All @@ -24,4 +29,18 @@ public List<ContestAward> findAllById(final List<Long> awardIds) {

return contestAwards;
}

public List<ContestAward> getTeamAwards(final List<Team> teams) {
final List<Long> teamIds = teams.stream().map(Team::getId).toList();

final List<TeamContestAward> teamAwards = teamContestAwardRepository.findByTeamIdIn(teamIds);

if (teamAwards.isEmpty()) {
return Collections.emptyList();
}

final List<Long> awardIds = teamAwards.stream().map(TeamContestAward::getContestAwardId).distinct().toList();

return findAllById(awardIds);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.opus.opus.modules.contest.application.dto.response;

import com.opus.opus.modules.contest.domain.ContestAward;
import com.opus.opus.modules.team.domain.Team;
import java.util.List;

public record TeamSummaryResponse(
Long teamId,
String teamName,
String projectName,
Boolean isLiked,
Boolean isVoted,
List<AwardInfo> awards
) {
public static TeamSummaryResponse of(
final Team team,
final List<ContestAward> contestAwards,
final Boolean isLiked,
final Boolean isVoted
) {
final List<AwardInfo> awardInfos = contestAwards.stream()
.map(AwardInfo::from)
.toList();

return new TeamSummaryResponse(
team.getId(),
team.getTeamName(),
team.getProjectName(),
isLiked,
isVoted,
awardInfos
);
}

public record AwardInfo(
String awardName,
String awardColor
) {
public static AwardInfo from(final ContestAward contestAward) {
return new AwardInfo(
contestAward.getAwardName(),
contestAward.getAwardColor()
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.opus.opus.modules.team.application.convenience;

import com.opus.opus.modules.contest.application.convenience.ContestAwardConvenience;
import com.opus.opus.modules.contest.domain.ContestAward;
import com.opus.opus.modules.team.domain.Team;
import com.opus.opus.modules.team.domain.TeamContestAward;
import com.opus.opus.modules.team.domain.dao.TeamContestAwardRepository;
import java.util.Collections;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

private final TeamContestAwardRepository teamContestAwardRepository;

private final ContestAwardConvenience contestAwardConvenience;

public List<ContestAward> getTeamAwards(final List<Team> teams) {
final List<Long> teamIds = teams.stream().map(Team::getId).toList();

final List<TeamContestAward> teamAwards = teamContestAwardRepository.findByTeamIdIn(teamIds);

if (teamAwards.isEmpty()) {
return Collections.emptyList();
}

final List<Long> awardIds = teamAwards.stream().map(TeamContestAward::getContestAwardId).distinct().toList();

return contestAwardConvenience.findAllById(awardIds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import static com.opus.opus.modules.team.exception.TeamExceptionType.NOT_FOUND_TEAM;
import static com.opus.opus.modules.team.exception.TeamExceptionType.TRACK_HAS_TEAM;

import com.opus.opus.modules.member.domain.Member;
import com.opus.opus.modules.team.domain.Team;
import com.opus.opus.modules.team.domain.dao.TeamRepository;
import com.opus.opus.modules.team.exception.TeamException;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -43,4 +46,18 @@ public void validateAllTeamsDeletedInTrack(final Long trackId) {
public List<Team> getTeamsOfContest(final Long contestId) {
return teamRepository.findAllByContestId(contestId);
}

public List<Team> findAllByContestId(final Long contestId) {
return teamRepository.findAllByContestId(contestId);
}

public void shuffleTeams(final List<Team> teams, final Member member) {
if (member != null) {
Random seed = new Random(member.getId());
Collections.shuffle(teams, seed);
} else {
Collections.shuffle(teams);
}
}

}
Loading