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] 응원톡 관리 화면에서의 응원톡 전체 조회 #232

Merged
merged 25 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6b1aa3d
[REFACTOR] CheerTalkResponse.Reported 를 CheerTalkResponse.ForManager …
Jin409 Sep 22, 2024
0ea33a3
[FIX] 게임팀의 아이디로 응원톡을 조회하는 쿼리로 변경 #218
Jin409 Sep 22, 2024
65b2e18
[FEAT] 리그에 해당하는 응원톡 전체 조회 #218
Jin409 Sep 22, 2024
db07b70
[ADD] SecurityConfig 에 엔드포인트 추가 #218
Jin409 Sep 22, 2024
3f8baaf
[FIX] 차단되지 않은 응원톡만 조회하도록 조건 추가 #218
Jin409 Sep 22, 2024
131a077
[TEST] 서비스 테스트 추가 #218
Jin409 Sep 22, 2024
9a436f8
[TEST] 인수 테스트 추가 #218
Jin409 Sep 22, 2024
bea8d9d
Merge branch 'main' of https://github.com/hufscheer/spectator-server …
Jin409 Sep 22, 2024
ea5cc5e
[DOCS] 문서화 추가 #218
Jin409 Sep 22, 2024
8dff26b
[TEST] 랜덤으로 이름의 길이가 지정되어 테스트가 실패하는 오류 수정 #218
Jin409 Sep 22, 2024
3122135
[ADD] 권한 검사가 필요한 엔티티를 추상화하기 위해 ManagedEntity 인터페이스 추가 #218
Jin409 Sep 22, 2024
747f12c
[FEAT] 권한 검사 기능 구현 #218
Jin409 Sep 22, 2024
d1a27e0
[REFACTOR] 중복된 코드 제거 및 PermissionValidator 를 사용하도록 수정 #218
Jin409 Sep 22, 2024
e709e98
[MERGE] 메인 브랜치 변경사항 반영 #218
Jin409 Sep 25, 2024
256769b
[REFACTOR] PermissionValidator 가 권한 검사만 담당하도록 수정 #218
Jin409 Sep 25, 2024
22c7985
[MERGE] 메인 브랜치 변경사항 반영 #218
Jin409 Sep 25, 2024
c4b5c06
[DOCS] 문서화 반영 #218
Jin409 Sep 25, 2024
05f0ef5
[REFACTOR] PermissionValidator 를 빈으로 등록하지 않고 정적메서드로 선언 #218
Jin409 Sep 26, 2024
3b302a4
[REFACTOR] PermissionValidator 를 빈으로 등록하지 않고 정적메서드로 선언 #218
Jin409 Sep 26, 2024
47cfc96
[REFACTOR] 에러 메시지 자세하게 수정 #218
Jin409 Sep 26, 2024
6444400
[FIX] 사용되지 않는 의존성 주입 삭제 #218
Jin409 Sep 26, 2024
fd4fc3e
[TEST] 에러 메시지 수정에 따른 메시지 검증 로직 삭제 #218
Jin409 Sep 26, 2024
f156935
[MERGE] 메인 브랜치 변경 사항 반영 #218
Jin409 Sep 26, 2024
a50e24c
[TEST] 에러 메시지 검증 삭제 #218
Jin409 Sep 26, 2024
d23a876
[DOCS] 문서화 #218
Jin409 Sep 26, 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
4 changes: 4 additions & 0 deletions src/docs/asciidoc/api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ operation::cheer-talk-query-controller-test/응원톡을_조회한다[snippets='

operation::cheer-talk-query-controller-test/리그의_신고된_응원톡을_조회한다[snippets='http-request,query-parameters,path-parameters,http-response,response-fields']

=== 응원톡 관리 화면에서의 응원톡 전체 조회

operation::cheer-talk-query-controller-test/리그의_차단되지_않은_응원톡을_조회한다[snippets='http-request,query-parameters,path-parameters,http-response,response-fields']

== 게임 API

=== 게임 상세 조회
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,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.GET, "/leagues/{leagueId}/cheer-talks/reported"),
mvc.pattern(HttpMethod.GET, "/leagues/{leagueId}/cheer-talks/**"),
mvc.pattern(HttpMethod.POST, "/leagues/{leagueId}/games"),
mvc.pattern(HttpMethod.POST, "/games/*/timelines/**"),
mvc.pattern(HttpMethod.POST, "/games/{gameId}/lineup-players/{lineupPlayerId}/**")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ private void saveGameTeams(Game game, GameRequestDto.Register requestDto) {
game.addTeam(gameTeam1);
game.addTeam(gameTeam2);

leagueTeam1.getLeagueTeamPlayers().stream()
leagueTeam1.getLeagueTeamPlayers()
.forEach(gameTeam1::registerLineup);
leagueTeam2.getLeagueTeamPlayers().stream()
leagueTeam2.getLeagueTeamPlayers()
.forEach(gameTeam2::registerLineup);
Comment on lines -50 to 53
Copy link
Member

Choose a reason for hiding this comment

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

코드 내용과는 별개로... Collection.forEach와 Stream.forEach의 차이

}

Expand Down
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 com.sports.server.common.domain.ManagedEntity;
import com.sports.server.common.exception.CustomException;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
Expand All @@ -30,7 +31,7 @@
@Getter
@Table(name = "games")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Game extends BaseEntity<Game> {
public class Game extends BaseEntity<Game> implements ManagedEntity {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sport_id")
Expand Down Expand Up @@ -192,4 +193,9 @@ private void validateGameTeam(final GameTeam gameTeam) {
throw new CustomException(HttpStatus.BAD_REQUEST, "해당 게임팀은 이 게임에 포함되지 않습니다.");
}
}

@Override
public boolean isManagedBy(Member manager) {
return manager.equals(manager);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.sports.server.command.member.domain.Member;
import com.sports.server.command.organization.domain.Organization;
import com.sports.server.common.domain.BaseEntity;
import com.sports.server.common.domain.ManagedEntity;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
Expand All @@ -28,7 +29,7 @@
@SQLDelete(sql = "UPDATE leagues SET is_deleted = 1 WHERE id = ?")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class League extends BaseEntity<League> {
public class League extends BaseEntity<League> implements ManagedEntity {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "manager_id")
Expand Down Expand Up @@ -86,6 +87,7 @@ public void updateInfo(String name, LocalDateTime startAt, LocalDateTime endAt,
this.maxRound = maxRound;
}

@Override
public boolean isManagedBy(Member manager) {
return this.manager.equals(manager);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.sports.server.command.leagueteam.application;

import com.sports.server.auth.exception.AuthorizationErrorMessages;
import com.sports.server.command.league.domain.League;
import com.sports.server.command.leagueteam.domain.LeagueTeam;
import com.sports.server.command.leagueteam.domain.LeagueTeamPlayer;
Expand All @@ -10,8 +9,8 @@
import com.sports.server.command.leagueteam.dto.LeagueTeamRequest;
import com.sports.server.command.member.domain.Member;
import com.sports.server.common.application.EntityUtils;
import com.sports.server.common.application.PermissionValidator;
import com.sports.server.common.exception.NotFoundException;
import com.sports.server.common.exception.UnauthorizedException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
Expand All @@ -33,7 +32,8 @@ public class LeagueTeamService {
private final EntityUtils entityUtils;

public void register(final Long leagueId, final Member manager, final LeagueTeamRequest.Register request) {
League league = getLeagueAndCheckPermission(leagueId, manager);
League league = entityUtils.getEntity(leagueId, League.class);
PermissionValidator.checkPermission(league, manager);

String imgUrl = changeLogoImageUrlToBeSaved(request.logoImageUrl());
LeagueTeam leagueTeam = request.toEntity(manager, league, imgUrl);
Expand All @@ -46,7 +46,9 @@ public void register(final Long leagueId, final Member manager, final LeagueTeam
}

public void update(Long leagueId, LeagueTeamRequest.Update request, Member manager, Long teamId) {
getLeagueAndCheckPermission(leagueId, manager);
League league = entityUtils.getEntity(leagueId, League.class);
PermissionValidator.checkPermission(league, manager);

LeagueTeam leagueTeam = getLeagueTeam(teamId);

leagueTeam.updateInfo(request.name(), changeLogoImageUrlToBeSaved(request.logoImageUrl()));
Expand All @@ -57,7 +59,9 @@ public void update(Long leagueId, LeagueTeamRequest.Update request, Member manag
}

public void delete(Long leagueId, Member manager, Long teamId) {
League league = getLeagueAndCheckPermission(leagueId, manager);
League league = entityUtils.getEntity(leagueId, League.class);
PermissionValidator.checkPermission(league, manager);

LeagueTeam leagueTeam = entityUtils.getEntity(teamId, LeagueTeam.class);
leagueTeam.isParticipate(league);

Expand Down Expand Up @@ -102,7 +106,8 @@ private void deletePlayers(LeagueTeamRequest.Update request, LeagueTeam leagueTe
}

public void deleteLogoImage(Long leagueId, Member manager, Long teamId) {
getLeagueAndCheckPermission(leagueId, manager);
League league = entityUtils.getEntity(leagueId, League.class);
PermissionValidator.checkPermission(league, manager);

LeagueTeam leagueTeam = entityUtils.getEntity(teamId, LeagueTeam.class);
leagueTeam.deleteLogoImageUrl();
Expand All @@ -115,13 +120,4 @@ private String changeLogoImageUrlToBeSaved(String logoImageUrl) {
return logoImageUrl.replace(originPrefix, replacePrefix);
}

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import com.sports.server.command.timeline.dto.TimelineRequest;
import com.sports.server.command.timeline.mapper.TimelineMapper;
import com.sports.server.common.application.EntityUtils;
import com.sports.server.common.application.PermissionValidator;
import com.sports.server.common.exception.CustomException;
import com.sports.server.common.exception.UnauthorizedException;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand All @@ -19,37 +19,29 @@
@RequiredArgsConstructor
public class TimelineService {
private final TimelineRepository timelineRepository;
private final EntityUtils entityUtils;
private final TimelineMapper timelineMapper;
private final EntityUtils entityUtils;

public void register(Member member, Long gameId, TimelineRequest request) {
Game game = checkPermissionAndGet(gameId, member);
public void register(Member manager, Long gameId, TimelineRequest request) {
Game game = entityUtils.getEntity(gameId, Game.class);
PermissionValidator.checkPermission(game, manager);

Timeline timeline = timelineMapper.toEntity(game, request);
timeline.apply();

timelineRepository.save(timeline);
}

public void deleteTimeline(Member member, Long gameId, Long timelineId) {
Game game = checkPermissionAndGet(gameId, member);
public void deleteTimeline(Member manager, Long gameId, Long timelineId) {
Game game = entityUtils.getEntity(gameId, Game.class);
PermissionValidator.checkPermission(game, manager);

Timeline timeline = getLastTimeline(timelineId, game);
timeline.rollback();

timelineRepository.delete(timeline);
}

private Game checkPermissionAndGet(Long gameId, Member member) {
Game game = entityUtils.getEntity(gameId, Game.class);

if (!game.isMangedBy(member)) {
throw new UnauthorizedException("타임라인 조작 권한이 없습니다.");
}

return game;
}

private Timeline getLastTimeline(Long timelineId, Game game) {
return timelineRepository.findFirstByGameOrderByIdDesc(game)
.filter(t -> t.getId().equals(timelineId))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.sports.server.common.application;

import com.sports.server.command.member.domain.Member;
import com.sports.server.common.domain.ManagedEntity;
import com.sports.server.common.exception.UnauthorizedException;

public class PermissionValidator {

public static <T extends ManagedEntity> void checkPermission(final T entity, final Member manager) {
if (!entity.isManagedBy(manager)) {
throw new UnauthorizedException(
manager.getEmail() + " 은 " + entity.getClass() + " 에 접근할 권한이 존재하지 않습니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.sports.server.common.domain;

import com.sports.server.command.member.domain.Member;

public interface ManagedEntity {
boolean isManagedBy(Member manager);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.sports.server.query.application;

import com.sports.server.auth.exception.AuthorizationErrorMessages;
import com.sports.server.command.cheertalk.domain.CheerTalk;
import com.sports.server.command.league.domain.League;
import com.sports.server.command.member.domain.Member;
import com.sports.server.common.application.EntityUtils;
import com.sports.server.common.application.PermissionValidator;
import com.sports.server.common.dto.PageRequestDto;
import com.sports.server.common.exception.UnauthorizedException;
import com.sports.server.query.dto.response.CheerTalkResponse;
import com.sports.server.query.dto.response.CheerTalkResponse.ForManager;
import com.sports.server.query.repository.CheerTalkDynamicRepository;
import com.sports.server.query.repository.GameQueryRepository;
import java.util.Collections;
Expand Down Expand Up @@ -42,22 +42,35 @@ public List<CheerTalkResponse.ForSpectator> getCheerTalksByGameId(final Long gam
return responses;
}

public List<CheerTalkResponse.Reported> getReportedCheerTalksByLeagueId(final Long leagueId,
final PageRequestDto pageRequest,
final Member manager) {
public List<CheerTalkResponse.ForManager> getReportedCheerTalksByLeagueId(final Long leagueId,
final PageRequestDto pageRequest,
final Member manager) {
League league = entityUtils.getEntity(leagueId, League.class);

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

List<CheerTalk> reportedCheerTalks = cheerTalkDynamicRepository.findReportedCheerTalksByLeagueId(
leagueId, pageRequest.cursor(), pageRequest.size()
);

return reportedCheerTalks.stream()
.map(cheerTalk -> new CheerTalkResponse.Reported(cheerTalk,
gameQueryRepository.findByIdWithLeague(cheerTalk.getGameTeamId()))).toList();
.map(cheerTalk -> new CheerTalkResponse.ForManager(cheerTalk,
gameQueryRepository.findByGameTeamIdWithLeague(cheerTalk.getGameTeamId()))).toList();
}

public List<CheerTalkResponse.ForManager> getUnblockedCheerTalksByLeagueId(Long leagueId,
PageRequestDto pageRequest,
Member manager) {
League league = entityUtils.getEntity(leagueId, League.class);
PermissionValidator.checkPermission(league, manager);

List<CheerTalk> cheerTalks = cheerTalkDynamicRepository.findUnblockedCheerTalksByLeagueId(
leagueId, pageRequest.cursor(), pageRequest.size()
);

return cheerTalks.stream()
.map(cheerTalk -> new ForManager(cheerTalk,
gameQueryRepository.findByGameTeamIdWithLeague(cheerTalk.getGameTeamId()))).toList();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private static String checkCheerTalkIsBlocked(CheerTalk cheerTalk) {
}
}

public record Reported(
public record ForManager(
Long cheerTalkId,
Long gameId,
Long leagueId,
Expand All @@ -41,7 +41,7 @@ public record Reported(
String gameName,
String leagueName
) {
public Reported(CheerTalk cheerTalk, Game game) {
public ForManager(CheerTalk cheerTalk, Game game) {
this(
cheerTalk.getId(),
game.getId(),
Jin409 marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,18 @@ public ResponseEntity<List<CheerTalkResponse.ForSpectator>> getAllCheerTalks(@Pa
}

@GetMapping("/leagues/{leagueId}/cheer-talks/reported")
public ResponseEntity<List<CheerTalkResponse.Reported>> getAllReportedCheerTalks(@PathVariable final Long leagueId,
@ModelAttribute final PageRequestDto pageRequest,
Member member) {
public ResponseEntity<List<CheerTalkResponse.ForManager>> getAllReportedCheerTalks(
@PathVariable final Long leagueId,
@ModelAttribute final PageRequestDto pageRequest,
Member member) {
return ResponseEntity.ok(cheerTalkQueryService.getReportedCheerTalksByLeagueId(leagueId, pageRequest, member));
}

@GetMapping("/leagues/{leagueId}/cheer-talks")
public ResponseEntity<List<CheerTalkResponse.ForManager>> getUnblockedCheerTalksOfLeague(
@PathVariable final Long leagueId,
@ModelAttribute final PageRequestDto pageRequest, Member
member) {
return ResponseEntity.ok(cheerTalkQueryService.getUnblockedCheerTalksByLeagueId(leagueId, pageRequest, member));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ public interface CheerTalkDynamicRepository {
List<CheerTalk> findByGameIdOrderByStartTime(Long gameId, Long cursor, Integer size);

List<CheerTalk> findReportedCheerTalksByLeagueId(Long leagueId, Long cursor, Integer size);

List<CheerTalk> findUnblockedCheerTalksByLeagueId(Long leagueId, Long cursor, Integer size);
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ public List<CheerTalk> findReportedCheerTalksByLeagueId(Long leagueId, Long curs
);
}

@Override
public List<CheerTalk> findUnblockedCheerTalksByLeagueId(Long leagueId, Long cursor, Integer size) {
return applyPagination(
queryFactory.selectFrom(cheerTalk)
.join(gameTeam).on(cheerTalk.gameTeamId.eq(gameTeam.id))
.where(gameTeam.game.league.id.eq(leagueId))
.where(cheerTalk.isBlocked.eq(false)),
cursor,
size
);
}

private List<CheerTalk> applyPagination(JPAQuery<CheerTalk> query, Long cursor, Integer size) {
return query
.where(getPaginationConditions(cursor))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public interface GameQueryRepository extends Repository<Game, Long> {
@Query(
"SELECT g FROM Game g "
+ "JOIN FETCH g.league "
+ "WHERE g.id=:id"
+ "JOIN GameTeam gt ON gt.game = g "
+ "WHERE gt.id = :gameTeamId"
)
Game findByIdWithLeague(@Param("id") Long id);
Game findByGameTeamIdWithLeague(@Param("gameTeamId") Long gameTeamId);
}
Loading
Loading