From 3b762d30500c9c666aa2387c7e17776dd335dfc6 Mon Sep 17 00:00:00 2001 From: hojooo Date: Wed, 16 Apr 2025 12:43:45 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=EC=A0=84=EC=A0=81=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../com/example/moim/club/entity/Club.java | 3 + .../example/moim/club/entity/UserClub.java | 6 + .../moim/global/exception/ResponseCode.java | 9 +- .../com/example/moim/match/entity/Match.java | 3 +- .../example/moim/match/entity/MatchUser.java | 8 +- .../match/service/MatchAggregateService.java | 4 + .../controller/StatisticController.java | 21 +++ .../moim/statistic/dto/StatisticDTO.java | 46 ++++++ .../moim/statistic/entity/Statistic.java | 153 ++++++++++++++++++ .../example/moim/statistic/entity/Tier.java | 19 +++ .../advice/StatisticControllerAdvice.java | 25 +++ .../repository/StatisticRepository.java | 29 ++++ .../statistic/service/StatisticService.java | 74 +++++++++ .../example/moim/match/MatchServiceTest.java | 2 - 15 files changed, 397 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/example/moim/statistic/controller/StatisticController.java create mode 100644 src/main/java/com/example/moim/statistic/dto/StatisticDTO.java create mode 100644 src/main/java/com/example/moim/statistic/entity/Statistic.java create mode 100644 src/main/java/com/example/moim/statistic/entity/Tier.java create mode 100644 src/main/java/com/example/moim/statistic/exception/advice/StatisticControllerAdvice.java create mode 100644 src/main/java/com/example/moim/statistic/repository/StatisticRepository.java create mode 100644 src/main/java/com/example/moim/statistic/service/StatisticService.java diff --git a/build.gradle b/build.gradle index f017ab5..fb146ed 100644 --- a/build.gradle +++ b/build.gradle @@ -51,7 +51,7 @@ dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' compileOnly 'org.projectlombok:lombok' -// runtimeOnly 'com.mysql:mysql-connector-j' + runtimeOnly 'com.mysql:mysql-connector-j' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/com/example/moim/club/entity/Club.java b/src/main/java/com/example/moim/club/entity/Club.java index f175777..cc9b42e 100644 --- a/src/main/java/com/example/moim/club/entity/Club.java +++ b/src/main/java/com/example/moim/club/entity/Club.java @@ -8,6 +8,7 @@ import com.example.moim.global.exception.ResponseCode; import com.example.moim.global.util.TextUtils; import com.example.moim.match.entity.Match; +import com.example.moim.statistic.entity.Statistic; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; @@ -80,6 +81,8 @@ public static Club createClub(ClubInput clubInput, String profileImgPath) { club.mainUniformColor = clubInput.getMainUniformColor(); club.subUniformColor = clubInput.getSubUniformColor(); club.memberCount = 1; + + Statistic.createStatistic(club); return club; } diff --git a/src/main/java/com/example/moim/club/entity/UserClub.java b/src/main/java/com/example/moim/club/entity/UserClub.java index 7d11357..2f3096f 100644 --- a/src/main/java/com/example/moim/club/entity/UserClub.java +++ b/src/main/java/com/example/moim/club/entity/UserClub.java @@ -28,6 +28,8 @@ public class UserClub extends BaseEntity { private Integer scheduleCount; private Integer matchCount; + private int score; + public static UserClub createLeaderUserClub(User user, Club club) { UserClub userClub = new UserClub(); userClub.user = user; @@ -59,4 +61,8 @@ public static UserClub createUserClub(User user, Club club) { public void changeUserClub(ClubRole clubRole) { this.clubRole = clubRole; } + + public void updateScore(int score) { + this.score += score; + } } diff --git a/src/main/java/com/example/moim/global/exception/ResponseCode.java b/src/main/java/com/example/moim/global/exception/ResponseCode.java index 86b9f9d..e218943 100644 --- a/src/main/java/com/example/moim/global/exception/ResponseCode.java +++ b/src/main/java/com/example/moim/global/exception/ResponseCode.java @@ -37,10 +37,15 @@ public enum ResponseCode { MATCH_APPLICATION_NOT_FOUND(HttpStatus.BAD_REQUEST, "MATCH4006", "올바른 매치가 아닙니다."), MATCH_TIME_OUT(HttpStatus.BAD_REQUEST, "MATCH4007", "매치가 종료된 후 48시간이 지났습니다."), MATCH_NOT_CONFIRMED(HttpStatus.BAD_REQUEST, "MATCH4008", "확정된 매치가 아닙니다."), + MATCH_DUPLICATED(HttpStatus.BAD_REQUEST, "MATCH4009", "해당 시간에 다른 매치가 존재합니다."), // MatchUser Error - MATCH_USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MATCH4005", "가입된 모임이 없습니다."), - MATCH_CANNOT_CANCEL(HttpStatus.BAD_REQUEST, "MATCH4006", "매치를 취소할 수 없습니다."), + MATCH_USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MATCHUSER4001", "가입된 모임이 없습니다."), + MATCH_CANNOT_CANCEL(HttpStatus.BAD_REQUEST, "MATCHUSER4002", "매치를 취소할 수 없습니다."), + MATCH_USER_NOT_ATTENDANCE(HttpStatus.BAD_REQUEST, "MATCHUSER4003", "매치에 참여한 클럽 소속이 아닙니다"), + + // Statistic Error + STATISTIC_NOT_FOUND(HttpStatus.NOT_FOUND, "STATISTIC4001", "전적을 찾을 수 없습니다."), // Token Error ACCESS_TOKEN_NOT_FOUND(HttpStatus.BAD_REQUEST, "TOKEN4001", "헤더에 토큰 값이 없습니다"), diff --git a/src/main/java/com/example/moim/match/entity/Match.java b/src/main/java/com/example/moim/match/entity/Match.java index ab81b01..79367cc 100644 --- a/src/main/java/com/example/moim/match/entity/Match.java +++ b/src/main/java/com/example/moim/match/entity/Match.java @@ -164,7 +164,8 @@ public void setMatchScore(int homeScore, int awayScore) { public void timeDuplicationCheck(LocalDateTime startTime, LocalDateTime endTime) { if ((startTime.isBefore(this.startTime) && endTime.isBefore(this.startTime)) || (startTime.isAfter(this.endTime) && endTime.isAfter(this.endTime))) { - throw new MatchRecordExpireException("해당 시간대에 다른 매치 일정이 있습니다"); +// throw new MatchRecordExpireException("해당 시간대에 다른 매치 일정이 있습니다"); + throw new MatchControllerAdvice(ResponseCode.MATCH_DUPLICATED); } } } \ No newline at end of file diff --git a/src/main/java/com/example/moim/match/entity/MatchUser.java b/src/main/java/com/example/moim/match/entity/MatchUser.java index d563019..61be6fd 100644 --- a/src/main/java/com/example/moim/match/entity/MatchUser.java +++ b/src/main/java/com/example/moim/match/entity/MatchUser.java @@ -1,9 +1,12 @@ package com.example.moim.match.entity; import com.example.moim.club.entity.Club; +import com.example.moim.global.exception.ResponseCode; +import com.example.moim.match.exception.advice.MatchControllerAdvice; import com.example.moim.schedule.entity.ScheduleVote; import com.example.moim.club.entity.UserClub; import com.example.moim.match.dto.MatchRecordInput; +import com.example.moim.statistic.entity.Statistic; import com.example.moim.user.entity.User; import jakarta.persistence.*; import lombok.Getter; @@ -25,6 +28,7 @@ public class MatchUser { private Club club; private int score; + private String season; public static MatchUser createMatchUser(Match match, ScheduleVote scheduleVote) { MatchUser matchUser = new MatchUser(); @@ -32,6 +36,7 @@ public static MatchUser createMatchUser(Match match, ScheduleVote scheduleVote) matchUser.user = scheduleVote.getUser(); matchUser.club = findUserClubInMatch(match, scheduleVote.getUser()); matchUser.score = 0; + matchUser.season = Statistic.getCurrentSeason(); return matchUser; } @@ -48,6 +53,7 @@ private static Club findUserClubInMatch(Match match, User user) { } } - throw new RuntimeException("해당 유저는 매치에 참여한 클럽 소속이 아닙니다."); +// throw new RuntimeException("해당 유저는 매치에 참여한 클럽 소속이 아닙니다."); + throw new MatchControllerAdvice(ResponseCode.MATCH_USER_NOT_ATTENDANCE); } } diff --git a/src/main/java/com/example/moim/match/service/MatchAggregateService.java b/src/main/java/com/example/moim/match/service/MatchAggregateService.java index 376e8a2..a6577a0 100644 --- a/src/main/java/com/example/moim/match/service/MatchAggregateService.java +++ b/src/main/java/com/example/moim/match/service/MatchAggregateService.java @@ -4,6 +4,7 @@ import com.example.moim.match.entity.MatchUser; import com.example.moim.match.repository.MatchRepository; import com.example.moim.match.repository.MatchUserRepository; +import com.example.moim.statistic.service.StatisticService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; @@ -19,6 +20,7 @@ public class MatchAggregateService { private final MatchRepository matchRepository; private final MatchUserRepository matchUserRepository; + private final StatisticService statisticService; //한시간마다 지금시간-48< 매치 끝나는시간 <지금시간이면 집계 @Scheduled(fixedRate = 1, timeUnit = TimeUnit.HOURS) @@ -43,6 +45,8 @@ public void aggregateMatchScore() { } log.info("Match ID: {}, homeScore: {}, awayScore: {}", match.getId(), homeScore, awayScore); match.setMatchScore(homeScore, awayScore); + + statisticService.updateStatistic(match); } } diff --git a/src/main/java/com/example/moim/statistic/controller/StatisticController.java b/src/main/java/com/example/moim/statistic/controller/StatisticController.java new file mode 100644 index 0000000..7f02b8d --- /dev/null +++ b/src/main/java/com/example/moim/statistic/controller/StatisticController.java @@ -0,0 +1,21 @@ +package com.example.moim.statistic.controller; + +import com.example.moim.global.exception.BaseResponse; +import com.example.moim.global.exception.ResponseCode; +import com.example.moim.statistic.dto.StatisticDTO; +import com.example.moim.statistic.service.StatisticService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +public class StatisticController { + private StatisticService statisticService; + + // 전적 조회 + @GetMapping("/statistic/{clubId}") + public BaseResponse getStatistic(@PathVariable Long clubId, + @RequestBody StatisticDTO.StatisticRequest request) { + return BaseResponse.onSuccess(statisticService.getStatistic(clubId, request.getTargetSeason()), ResponseCode.OK); + } +} diff --git a/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java b/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java new file mode 100644 index 0000000..82cf170 --- /dev/null +++ b/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java @@ -0,0 +1,46 @@ +package com.example.moim.statistic.dto; + +import com.example.moim.statistic.entity.Statistic; +import lombok.AllArgsConstructor; +import lombok.Data; + +public class StatisticDTO { + @Data + @AllArgsConstructor + public static class StatisticRequest { + private String targetSeason; + } + + @Data + @AllArgsConstructor + public static class StatisticResponse { + private String season; + private String rank; + private int point; + private float winRate; + private int winCount; + private int defeatCount; + private int drawCount; + private String mvpName; + private int mvpGoalCount; + + public StatisticResponse(Statistic statistic) { + this.season = statistic.getSeason(); + this.point = statistic.getPoint(); + this.winRate = statistic.getWinRate(); + this.winCount = statistic.getWinCount(); + this.defeatCount = statistic.getDefeatCount(); + this.drawCount = statistic.getDrawCount(); + this.mvpName = statistic.getMvpName(); + this.mvpGoalCount = statistic.getMvpScore(); + this.rank = statistic.getTier().toString(); + } + } + + @Data + @AllArgsConstructor + public static class mvpDTO { + private String name; + private Long goalCount; + } +} diff --git a/src/main/java/com/example/moim/statistic/entity/Statistic.java b/src/main/java/com/example/moim/statistic/entity/Statistic.java new file mode 100644 index 0000000..3f93713 --- /dev/null +++ b/src/main/java/com/example/moim/statistic/entity/Statistic.java @@ -0,0 +1,153 @@ +package com.example.moim.statistic.entity; + +import com.example.moim.club.entity.Club; +import com.example.moim.club.entity.UserClub; +import com.example.moim.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.Getter; + +import java.time.LocalDate; + +import static com.example.moim.statistic.entity.Tier.ROOKIE; + +@Entity +@Getter +public class Statistic extends BaseEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "club_id") + private Club club; + + private String season; // -년 전반기 / 후반기 + private Tier tier; + private int point; + + private float winRate; // 소수점 한 자리 수까지 표기 + private int winCount; + private int drawCount; + private int defeatCount; + + private int winStreak; + private int defeatStreak; + + private int mvpScore; + private String mvpName; + + public static String getCurrentSeason() { + LocalDate now = LocalDate.now(); + int year = now.getYear(); + int month = now.getMonthValue(); + String half = (month <= 6) ? "상반기" : "하반기"; + + return year + " " + half + "전적"; + } + + // 모임 만들때 기본적으로 하나 생성, 반기별로 하나씩 생성 + public static Statistic createStatistic(Club club) { + Statistic statistic = new Statistic(); + + statistic.club = club; + statistic.season = Statistic.getCurrentSeason(); + statistic.tier = ROOKIE; + statistic.point = 305; + statistic.winRate = 0f; + statistic.winCount = 0; + statistic.drawCount = 0; + statistic.defeatCount = 0; + statistic.winStreak = 0; + statistic.defeatStreak = 0; + statistic.mvpScore = 0; + statistic.mvpName = null; + + return statistic; + } + + public void updateStatistic(int homeScore, int awayScore, int opponentRank, String mvpName, int mvpScore) { + int currentMatches = this.winCount + this.drawCount + this.defeatCount; // 배치고사 단계: 3경기 미만, 이후 경기부터는 정식 랭크 적용 + int pointsChange = 0; + + int ourRankLevel = this.tier.getLevel(); + int diff = opponentRank - ourRankLevel; + + if (homeScore > awayScore) { + this.winCount++; + this.winStreak += this.winStreak + 1; + this.defeatStreak = 0; + + if (this.tier == ROOKIE) { + pointsChange += 125; + } else { + pointsChange += 128; + // 3연승 이상이면 연승 보너스: 현재 연승 수 × 10점을 추가 + if (this.winStreak >= 3) { + pointsChange += this.winStreak * 10; + } + // 랭크 차이 보너스: 우리 팀의 랭크와 상대팀의 랭크 차이에 따라 추가 점수를 부여 + if (diff == 2) { + pointsChange += 100; + } else if (diff >= 3) { + pointsChange += 150; + } + } + } else if (homeScore < awayScore) { + this.defeatCount++; + this.defeatStreak += this.defeatStreak + 1; + this.winStreak = 0; + + if (this.tier == ROOKIE) { + pointsChange -= 75; + } else { + // 기본 패배 페널티는 -72점 + // 단, 연패 완화 로직: 3연패 시에는 -30점, 4연패 이상이면 페널티가 0점 + if (this.defeatStreak == 3) { + pointsChange += -30; + } else if (this.defeatStreak >= 4) { + pointsChange += 0; + } else { + pointsChange += -72; + } + } + } else { + this.drawCount++; + this.winStreak = 0; + this.defeatStreak = 0; + + if (this.tier == ROOKIE) { + pointsChange += 50; + } else { + pointsChange += 47; + } + } + + this.point += pointsChange; + + boolean isRookie = (currentMatches < 3); + if (!isRookie) { + if (this.point >= 1600) { + this.tier = Tier.M1; + } else if (this.point >= 1300) { + this.tier = Tier.M2; + } else if (this.point >= 875) { + this.tier = Tier.M3; + } else if (this.point >= 500) { + this.tier = Tier.M4; + } else { + this.tier = Tier.M5; + } + } + + this.mvpName = mvpName; + this.mvpScore = mvpScore; + + // 총 경기수 계산 후 승률 업데이트 (승률 = (승리수/총경기수)*100) + int totalMatches = this.winCount + this.drawCount + this.defeatCount; + this.winRate = totalMatches > 0 ? ((float) this.winCount / totalMatches) * 100 : 0; + } + + public void updateMvp(UserClub userClub) { + this.mvpName = userClub.getUser().getName(); + this.mvpScore = userClub.getScore(); + } +} diff --git a/src/main/java/com/example/moim/statistic/entity/Tier.java b/src/main/java/com/example/moim/statistic/entity/Tier.java new file mode 100644 index 0000000..0ddaf16 --- /dev/null +++ b/src/main/java/com/example/moim/statistic/entity/Tier.java @@ -0,0 +1,19 @@ +package com.example.moim.statistic.entity; + +import lombok.Getter; + +@Getter +public enum Tier { + M1(1), + M2(2), + M3(3), + M4(4), + M5(5), + ROOKIE(0); + + private final int level; + + Tier(int level) { + this.level = level; + } +} diff --git a/src/main/java/com/example/moim/statistic/exception/advice/StatisticControllerAdvice.java b/src/main/java/com/example/moim/statistic/exception/advice/StatisticControllerAdvice.java new file mode 100644 index 0000000..7bc4877 --- /dev/null +++ b/src/main/java/com/example/moim/statistic/exception/advice/StatisticControllerAdvice.java @@ -0,0 +1,25 @@ +package com.example.moim.statistic.exception.advice; + +import com.example.moim.global.exception.GeneralException; +import com.example.moim.global.exception.ResponseCode; + +public class StatisticControllerAdvice extends GeneralException { + + public StatisticControllerAdvice(ResponseCode responseCode) { + super(responseCode); + } +// @ExceptionHandler +// public ResponseEntity MethodArgumentNotValidExHandle(MethodArgumentNotValidException e) { +// return new ResponseEntity<>(new ErrorResult(e.getBindingResult().getFieldError().getDefaultMessage()), HttpStatus.BAD_REQUEST); +// } +// +// @ExceptionHandler +// public ResponseEntity MatchSaveExHandle(MatchPermissionException e) { +// return new ResponseEntity<>(new ErrorResult(e.getMessage()), HttpStatus.UNAUTHORIZED); +// } +// +// @ExceptionHandler +// public ResponseEntity MatchRecordExpireExHandle(MatchRecordExpireException e) { +// return new ResponseEntity<>(new ErrorResult(e.getMessage()), HttpStatus.BAD_REQUEST); +// } +} diff --git a/src/main/java/com/example/moim/statistic/repository/StatisticRepository.java b/src/main/java/com/example/moim/statistic/repository/StatisticRepository.java new file mode 100644 index 0000000..d85a0ec --- /dev/null +++ b/src/main/java/com/example/moim/statistic/repository/StatisticRepository.java @@ -0,0 +1,29 @@ +package com.example.moim.statistic.repository; + +import com.example.moim.club.entity.Club; +import com.example.moim.statistic.dto.StatisticDTO; +import com.example.moim.statistic.entity.Statistic; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface StatisticRepository extends JpaRepository { + // 현재 시즌 mvp 유저 찾기(이름, 누적 점수) + @Query(""" + SELECT new com.example.moim.statistic.dto.StatisticDTO$mvpDTO(mu.user.name, SUM(mu.score)) + FROM MatchUser mu + WHERE mu.club = :club AND mu.season = :season + GROUP BY mu.user + ORDER BY SUM(mu.score) DESC + """) + List findTopScorerByClub(@Param("club") Club club, @Param("season") String season); + + // 모임과 시즌으로 전적 찾기 + Optional findByClubAndSeason(Club club, String season); +} diff --git a/src/main/java/com/example/moim/statistic/service/StatisticService.java b/src/main/java/com/example/moim/statistic/service/StatisticService.java new file mode 100644 index 0000000..64b34e5 --- /dev/null +++ b/src/main/java/com/example/moim/statistic/service/StatisticService.java @@ -0,0 +1,74 @@ +package com.example.moim.statistic.service; + +import com.example.moim.club.entity.Club; +import com.example.moim.club.exception.advice.ClubControllerAdvice; +import com.example.moim.club.repository.ClubRepository; +import com.example.moim.global.exception.ResponseCode; +import com.example.moim.match.entity.Match; +import com.example.moim.statistic.dto.StatisticDTO; +import com.example.moim.statistic.entity.Statistic; +import com.example.moim.statistic.exception.advice.StatisticControllerAdvice; +import com.example.moim.statistic.repository.StatisticRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class StatisticService { + private final StatisticRepository statisticRepository; + private final ClubRepository clubRepository; + + // 전적 생성(새로운 시즌) + // 매년 1월 1일과 7월 1일 + // 스케쥴 말고 배치 처리 고려해보기 + @Scheduled(cron = "0 0 0 1 1,7 ?") + @Transactional + public void createNewSeasonStatistics() { + String currentSeason = Statistic.getCurrentSeason(); + List clubs = clubRepository.findAll(); + + for (Club club : clubs) { + // 현재 시즌에 해당하는 Statistic이 존재하는지 확인하는 로직 + Optional existing = statisticRepository.findByClubAndSeason(club, currentSeason); + if (existing.isEmpty()) { + Statistic newStatistic = Statistic.createStatistic(club); + statisticRepository.save(newStatistic); + } + } + } + + // 전적 업데이트 로직 + public void updateStatistic(Match match) { + String currentSeason = Statistic.getCurrentSeason(); + Statistic homeStatistic = statisticRepository.findByClubAndSeason(match.getHomeClub(), currentSeason) + .orElseThrow(); + Statistic awayStatistic = statisticRepository.findByClubAndSeason(match.getAwayClub(), currentSeason) + .orElseThrow(); + int homeRankLevel = homeStatistic.getTier().getLevel(); + int awayRankLevel = awayStatistic.getTier().getLevel(); + + // 1명만 가져오게 페이징 + StatisticDTO.mvpDTO homeMVPResult = statisticRepository.findTopScorerByClub(match.getHomeClub(), currentSeason).getFirst(); + StatisticDTO.mvpDTO awayMVPResult = statisticRepository.findTopScorerByClub(match.getHomeClub(), currentSeason).getFirst(); + + homeStatistic.updateStatistic(match.getHomeScore(), match.getAwayScore(), awayRankLevel, homeMVPResult.getName(), homeMVPResult.getGoalCount().intValue()); + awayStatistic.updateStatistic(match.getAwayScore(), match.getHomeScore(), homeRankLevel, awayMVPResult.getName(), awayMVPResult.getGoalCount().intValue()); + } + + // 전적 조회 + public StatisticDTO.StatisticResponse getStatistic(Long clubId, String targetSeason) { +// String currentSeason = Statistic.getCurrentSeason(); + Club club = clubRepository.findById(clubId) + .orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_NOT_FOUND)); + Statistic statistic = statisticRepository.findByClubAndSeason(club, targetSeason) + .orElseThrow(() -> new StatisticControllerAdvice(ResponseCode.STATISTIC_NOT_FOUND)); + + return new StatisticDTO.StatisticResponse(statistic); + } +} diff --git a/src/test/java/com/example/moim/match/MatchServiceTest.java b/src/test/java/com/example/moim/match/MatchServiceTest.java index c25c035..b503fcb 100644 --- a/src/test/java/com/example/moim/match/MatchServiceTest.java +++ b/src/test/java/com/example/moim/match/MatchServiceTest.java @@ -8,8 +8,6 @@ import com.example.moim.global.exception.ResponseCode; import com.example.moim.match.dto.*; import com.example.moim.match.entity.*; -import com.example.moim.match.exception.MatchPermissionException; -import com.example.moim.match.exception.MatchRecordExpireException; import com.example.moim.match.exception.advice.MatchControllerAdvice; import com.example.moim.match.repository.MatchApplicationRepository; import com.example.moim.match.repository.MatchRepository; From 9c6485749cc3c7ada8a94e713e9b82f31fd47749 Mon Sep 17 00:00:00 2001 From: hojooo Date: Sat, 19 Apr 2025 20:58:05 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20matchuser=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/moim/match/entity/MatchUser.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/moim/match/entity/MatchUser.java b/src/main/java/com/example/moim/match/entity/MatchUser.java index 61be6fd..6f9b0a9 100644 --- a/src/main/java/com/example/moim/match/entity/MatchUser.java +++ b/src/main/java/com/example/moim/match/entity/MatchUser.java @@ -34,7 +34,8 @@ public static MatchUser createMatchUser(Match match, ScheduleVote scheduleVote) MatchUser matchUser = new MatchUser(); matchUser.match = match; matchUser.user = scheduleVote.getUser(); - matchUser.club = findUserClubInMatch(match, scheduleVote.getUser()); + matchUser.club = scheduleVote.getSchedule().getClub(); +// matchUser.club = findUserClubInMatch(match, scheduleVote.getUser()); matchUser.score = 0; matchUser.season = Statistic.getCurrentSeason(); @@ -45,9 +46,11 @@ public void recordScore(MatchRecordInput matchRecordInput) { this.score = matchRecordInput.getScore(); } + // 이거 뭐지 private static Club findUserClubInMatch(Match match, User user) { for (UserClub userClub : user.getUserClub()) { Club myClub = userClub.getClub(); + if (myClub.equals(match.getHomeClub()) || myClub.equals(match.getAwayClub())) { return myClub; } From eeb4db591bf9998871cfe76807c1aa7b280a9f58 Mon Sep 17 00:00:00 2001 From: hojooo Date: Thu, 29 May 2025 23:44:19 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=EC=A0=84=EC=A0=81=20=ED=92=8B?= =?UTF-8?q?=EC=82=B4,=20=EC=B6=95=EA=B5=AC,=20=ED=86=B5=ED=95=A9=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=82=98=EB=88=84=EC=96=B4=EC=84=9C=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/moim/club/entity/Club.java | 4 +- .../example/moim/global/enums/SportsType.java | 2 +- .../controller/StatisticController.java | 4 +- .../moim/statistic/dto/StatisticDTO.java | 1 + .../moim/statistic/entity/Statistic.java | 5 +- .../repository/StatisticRepository.java | 26 ++++++-- .../statistic/service/StatisticService.java | 61 +++++++++++++++---- 7 files changed, 82 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/example/moim/club/entity/Club.java b/src/main/java/com/example/moim/club/entity/Club.java index cc9b42e..fe929fb 100644 --- a/src/main/java/com/example/moim/club/entity/Club.java +++ b/src/main/java/com/example/moim/club/entity/Club.java @@ -82,7 +82,9 @@ public static Club createClub(ClubInput clubInput, String profileImgPath) { club.subUniformColor = clubInput.getSubUniformColor(); club.memberCount = 1; - Statistic.createStatistic(club); + Statistic.createStatistic(club, SportsType.OVERALL); + Statistic.createStatistic(club, SportsType.FUTSAL); + Statistic.createStatistic(club, SportsType.SOCCER); return club; } diff --git a/src/main/java/com/example/moim/global/enums/SportsType.java b/src/main/java/com/example/moim/global/enums/SportsType.java index d2ebd52..598ffff 100644 --- a/src/main/java/com/example/moim/global/enums/SportsType.java +++ b/src/main/java/com/example/moim/global/enums/SportsType.java @@ -4,7 +4,7 @@ import java.util.Optional; public enum SportsType { - SOCCER("축구"), FUTSAL("풋살"); + SOCCER("축구"), FUTSAL("풋살"), OVERALL("전체"); private final String koreanName; SportsType(String koreanName) { diff --git a/src/main/java/com/example/moim/statistic/controller/StatisticController.java b/src/main/java/com/example/moim/statistic/controller/StatisticController.java index 7f02b8d..76f3fbe 100644 --- a/src/main/java/com/example/moim/statistic/controller/StatisticController.java +++ b/src/main/java/com/example/moim/statistic/controller/StatisticController.java @@ -12,10 +12,10 @@ public class StatisticController { private StatisticService statisticService; - // 전적 조회 + // 전적 조회(전적 메인) @GetMapping("/statistic/{clubId}") public BaseResponse getStatistic(@PathVariable Long clubId, @RequestBody StatisticDTO.StatisticRequest request) { - return BaseResponse.onSuccess(statisticService.getStatistic(clubId, request.getTargetSeason()), ResponseCode.OK); + return BaseResponse.onSuccess(statisticService.getStatistic(clubId, request.getTargetSeason(), request.getTargetType()), ResponseCode.OK); } } diff --git a/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java b/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java index 82cf170..56fe2b0 100644 --- a/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java +++ b/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java @@ -9,6 +9,7 @@ public class StatisticDTO { @AllArgsConstructor public static class StatisticRequest { private String targetSeason; + private String targetType; } @Data diff --git a/src/main/java/com/example/moim/statistic/entity/Statistic.java b/src/main/java/com/example/moim/statistic/entity/Statistic.java index 3f93713..26e071e 100644 --- a/src/main/java/com/example/moim/statistic/entity/Statistic.java +++ b/src/main/java/com/example/moim/statistic/entity/Statistic.java @@ -3,6 +3,7 @@ import com.example.moim.club.entity.Club; import com.example.moim.club.entity.UserClub; import com.example.moim.global.entity.BaseEntity; +import com.example.moim.global.enums.SportsType; import jakarta.persistence.*; import lombok.Getter; @@ -20,6 +21,7 @@ public class Statistic extends BaseEntity { @JoinColumn(name = "club_id") private Club club; + private SportsType sportsType; private String season; // -년 전반기 / 후반기 private Tier tier; private int point; @@ -45,10 +47,11 @@ public static String getCurrentSeason() { } // 모임 만들때 기본적으로 하나 생성, 반기별로 하나씩 생성 - public static Statistic createStatistic(Club club) { + public static Statistic createStatistic(Club club, SportsType sportsType) { Statistic statistic = new Statistic(); statistic.club = club; + statistic.sportsType = sportsType; statistic.season = Statistic.getCurrentSeason(); statistic.tier = ROOKIE; statistic.point = 305; diff --git a/src/main/java/com/example/moim/statistic/repository/StatisticRepository.java b/src/main/java/com/example/moim/statistic/repository/StatisticRepository.java index d85a0ec..f76c498 100644 --- a/src/main/java/com/example/moim/statistic/repository/StatisticRepository.java +++ b/src/main/java/com/example/moim/statistic/repository/StatisticRepository.java @@ -1,6 +1,7 @@ package com.example.moim.statistic.repository; import com.example.moim.club.entity.Club; +import com.example.moim.global.enums.SportsType; import com.example.moim.statistic.dto.StatisticDTO; import com.example.moim.statistic.entity.Statistic; import org.springframework.data.domain.Pageable; @@ -14,16 +15,33 @@ @Repository public interface StatisticRepository extends JpaRepository { - // 현재 시즌 mvp 유저 찾기(이름, 누적 점수) + // 현재 시즌 통합 mvp 유저 찾기(이름, 누적 점수) +// @Query(""" +// SELECT new com.example.moim.statistic.dto.StatisticDTO$mvpDTO(mu.user.name, SUM(mu.score)) +// FROM MatchUser mu +// WHERE mu.club = :club AND mu.season = :season +// GROUP BY mu.user +// ORDER BY SUM(mu.score) DESC +// """) +// List findTopScorerByClub(@Param("club") Club club, @Param("season") String season); + + // 현재 시즌 종목별 mvp 유저 찾기(이름, 누적 점수) @Query(""" SELECT new com.example.moim.statistic.dto.StatisticDTO$mvpDTO(mu.user.name, SUM(mu.score)) FROM MatchUser mu - WHERE mu.club = :club AND mu.season = :season + WHERE mu.club = :club + AND mu.season = :season + AND mu.match.event = :sportsType GROUP BY mu.user ORDER BY SUM(mu.score) DESC """) - List findTopScorerByClub(@Param("club") Club club, @Param("season") String season); + List findTopScorerByClubAndSportsType(@Param("club") Club club, + @Param("season") String season, + @Param("sportsType") SportsType sportsType); - // 모임과 시즌으로 전적 찾기 + // 모임과 시즌으로 전적 찾기(통합) Optional findByClubAndSeason(Club club, String season); + + // 종목별 시즌 전적 + Optional findByClubAndSeasonAndSportsType(Club club, String season, SportsType sportsType); } diff --git a/src/main/java/com/example/moim/statistic/service/StatisticService.java b/src/main/java/com/example/moim/statistic/service/StatisticService.java index 64b34e5..7168886 100644 --- a/src/main/java/com/example/moim/statistic/service/StatisticService.java +++ b/src/main/java/com/example/moim/statistic/service/StatisticService.java @@ -3,6 +3,7 @@ import com.example.moim.club.entity.Club; import com.example.moim.club.exception.advice.ClubControllerAdvice; import com.example.moim.club.repository.ClubRepository; +import com.example.moim.global.enums.SportsType; import com.example.moim.global.exception.ResponseCode; import com.example.moim.match.entity.Match; import com.example.moim.statistic.dto.StatisticDTO; @@ -34,11 +35,15 @@ public void createNewSeasonStatistics() { List clubs = clubRepository.findAll(); for (Club club : clubs) { - // 현재 시즌에 해당하는 Statistic이 존재하는지 확인하는 로직 - Optional existing = statisticRepository.findByClubAndSeason(club, currentSeason); + // 현재 시즌에 해당하는 Statistic이 존재하는지 확인하는 로직(통합, 풋살, 축구) + Optional existing = statisticRepository.findByClubAndSeasonAndSportsType(club, currentSeason, SportsType.OVERALL); if (existing.isEmpty()) { - Statistic newStatistic = Statistic.createStatistic(club); + Statistic newStatistic = Statistic.createStatistic(club, SportsType.OVERALL); + Statistic newFulsalStatistic = Statistic.createStatistic(club, SportsType.FUTSAL); + Statistic newSoccerStatistic = Statistic.createStatistic(club, SportsType.SOCCER); statisticRepository.save(newStatistic); + statisticRepository.save(newFulsalStatistic); + statisticRepository.save(newSoccerStatistic); } } } @@ -46,27 +51,59 @@ public void createNewSeasonStatistics() { // 전적 업데이트 로직 public void updateStatistic(Match match) { String currentSeason = Statistic.getCurrentSeason(); - Statistic homeStatistic = statisticRepository.findByClubAndSeason(match.getHomeClub(), currentSeason) + + // 통합 + Statistic homeStatistic = statisticRepository.findByClubAndSeasonAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL) .orElseThrow(); - Statistic awayStatistic = statisticRepository.findByClubAndSeason(match.getAwayClub(), currentSeason) + Statistic awayStatistic = statisticRepository.findByClubAndSeasonAndSportsType(match.getAwayClub(), currentSeason, SportsType.OVERALL) .orElseThrow(); int homeRankLevel = homeStatistic.getTier().getLevel(); int awayRankLevel = awayStatistic.getTier().getLevel(); - // 1명만 가져오게 페이징 - StatisticDTO.mvpDTO homeMVPResult = statisticRepository.findTopScorerByClub(match.getHomeClub(), currentSeason).getFirst(); - StatisticDTO.mvpDTO awayMVPResult = statisticRepository.findTopScorerByClub(match.getHomeClub(), currentSeason).getFirst(); + StatisticDTO.mvpDTO homeMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); + StatisticDTO.mvpDTO awayMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); + + homeStatistic.updateStatistic(match.getHomeScore(), match.getHomeScore(), homeRankLevel, homeMVPResult.getName(), homeMVPResult.getGoalCount().intValue()); + awayStatistic.updateStatistic(match.getAwayScore(), match.getAwayScore(), awayRankLevel, awayMVPResult.getName(), awayMVPResult.getGoalCount().intValue()); + + if (match.getEvent() == SportsType.FUTSAL) { + // 풋살 + Statistic homeFutsalStatistic = statisticRepository.findByClubAndSeasonAndSportsType(match.getHomeClub(), currentSeason, SportsType.FUTSAL) + .orElseThrow(); + Statistic awayFutsalStatistic = statisticRepository.findByClubAndSeasonAndSportsType(match.getAwayClub(), currentSeason, SportsType.FUTSAL) + .orElseThrow(); + int homeFutsalRankLevel = homeFutsalStatistic.getTier().getLevel(); + int awayFutsalRankLevel = awayFutsalStatistic.getTier().getLevel(); + + StatisticDTO.mvpDTO homeFutsalMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); + StatisticDTO.mvpDTO awayFutsalMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); - homeStatistic.updateStatistic(match.getHomeScore(), match.getAwayScore(), awayRankLevel, homeMVPResult.getName(), homeMVPResult.getGoalCount().intValue()); - awayStatistic.updateStatistic(match.getAwayScore(), match.getHomeScore(), homeRankLevel, awayMVPResult.getName(), awayMVPResult.getGoalCount().intValue()); + homeFutsalStatistic.updateStatistic(match.getHomeScore(), match.getHomeScore(), homeFutsalRankLevel, homeMVPResult.getName(), homeFutsalMVPResult.getGoalCount().intValue()); + awayFutsalStatistic.updateStatistic(match.getAwayScore(), match.getAwayScore(), awayFutsalRankLevel, awayMVPResult.getName(), awayFutsalMVPResult.getGoalCount().intValue()); + } else if (match.getEvent() == SportsType.SOCCER) { + // 축구 + Statistic homeSoccerStatistic = statisticRepository.findByClubAndSeasonAndSportsType(match.getHomeClub(), currentSeason, SportsType.SOCCER) + .orElseThrow(); + Statistic awaySoccerStatistic = statisticRepository.findByClubAndSeasonAndSportsType(match.getAwayClub(), currentSeason, SportsType.SOCCER) + .orElseThrow(); + int homeSoccerRankLevel = homeSoccerStatistic.getTier().getLevel(); + int awaySoccerRankLevel = awaySoccerStatistic.getTier().getLevel(); + + StatisticDTO.mvpDTO homeSoccerMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); + StatisticDTO.mvpDTO awaySoccerMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); + + homeSoccerStatistic.updateStatistic(match.getHomeScore(), match.getAwayScore(), homeSoccerRankLevel, homeSoccerMVPResult.getName(), homeMVPResult.getGoalCount().intValue()); + awaySoccerStatistic.updateStatistic(match.getAwayScore(), match.getHomeScore(), awaySoccerRankLevel, awaySoccerMVPResult.getName(), awayMVPResult.getGoalCount().intValue()); + } } // 전적 조회 - public StatisticDTO.StatisticResponse getStatistic(Long clubId, String targetSeason) { + public StatisticDTO.StatisticResponse getStatistic(Long clubId, String targetSeason, String sportsType) { // String currentSeason = Statistic.getCurrentSeason(); + SportsType targetSportsType = SportsType.valueOf(sportsType); Club club = clubRepository.findById(clubId) .orElseThrow(() -> new ClubControllerAdvice(ResponseCode.CLUB_NOT_FOUND)); - Statistic statistic = statisticRepository.findByClubAndSeason(club, targetSeason) + Statistic statistic = statisticRepository.findByClubAndSeasonAndSportsType(club, targetSeason, targetSportsType) .orElseThrow(() -> new StatisticControllerAdvice(ResponseCode.STATISTIC_NOT_FOUND)); return new StatisticDTO.StatisticResponse(statistic); From c44365815fd7eb75359ed7df037174f53cdcb5d9 Mon Sep 17 00:00:00 2001 From: hojooo Date: Fri, 30 May 2025 14:15:22 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20=EB=A7=A4=EC=B9=98=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=97=90=20=EC=A0=84=EC=A0=81=20mock=20?= =?UTF-8?q?=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../moim/statistic/service/StatisticService.java | 12 ++++++------ .../moim/match/MatchAggregateServiceTest.java | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/example/moim/statistic/service/StatisticService.java b/src/main/java/com/example/moim/statistic/service/StatisticService.java index 7168886..63773b4 100644 --- a/src/main/java/com/example/moim/statistic/service/StatisticService.java +++ b/src/main/java/com/example/moim/statistic/service/StatisticService.java @@ -60,8 +60,8 @@ public void updateStatistic(Match match) { int homeRankLevel = homeStatistic.getTier().getLevel(); int awayRankLevel = awayStatistic.getTier().getLevel(); - StatisticDTO.mvpDTO homeMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); - StatisticDTO.mvpDTO awayMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); + StatisticDTO.mvpDTO homeMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).get(0); + StatisticDTO.mvpDTO awayMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).get(0); homeStatistic.updateStatistic(match.getHomeScore(), match.getHomeScore(), homeRankLevel, homeMVPResult.getName(), homeMVPResult.getGoalCount().intValue()); awayStatistic.updateStatistic(match.getAwayScore(), match.getAwayScore(), awayRankLevel, awayMVPResult.getName(), awayMVPResult.getGoalCount().intValue()); @@ -75,8 +75,8 @@ public void updateStatistic(Match match) { int homeFutsalRankLevel = homeFutsalStatistic.getTier().getLevel(); int awayFutsalRankLevel = awayFutsalStatistic.getTier().getLevel(); - StatisticDTO.mvpDTO homeFutsalMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); - StatisticDTO.mvpDTO awayFutsalMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); + StatisticDTO.mvpDTO homeFutsalMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).get(0); + StatisticDTO.mvpDTO awayFutsalMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).get(0); homeFutsalStatistic.updateStatistic(match.getHomeScore(), match.getHomeScore(), homeFutsalRankLevel, homeMVPResult.getName(), homeFutsalMVPResult.getGoalCount().intValue()); awayFutsalStatistic.updateStatistic(match.getAwayScore(), match.getAwayScore(), awayFutsalRankLevel, awayMVPResult.getName(), awayFutsalMVPResult.getGoalCount().intValue()); @@ -89,8 +89,8 @@ public void updateStatistic(Match match) { int homeSoccerRankLevel = homeSoccerStatistic.getTier().getLevel(); int awaySoccerRankLevel = awaySoccerStatistic.getTier().getLevel(); - StatisticDTO.mvpDTO homeSoccerMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); - StatisticDTO.mvpDTO awaySoccerMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).getFirst(); + StatisticDTO.mvpDTO homeSoccerMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).get(0); + StatisticDTO.mvpDTO awaySoccerMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).get(0); homeSoccerStatistic.updateStatistic(match.getHomeScore(), match.getAwayScore(), homeSoccerRankLevel, homeSoccerMVPResult.getName(), homeMVPResult.getGoalCount().intValue()); awaySoccerStatistic.updateStatistic(match.getAwayScore(), match.getHomeScore(), awaySoccerRankLevel, awaySoccerMVPResult.getName(), awayMVPResult.getGoalCount().intValue()); diff --git a/src/test/java/com/example/moim/match/MatchAggregateServiceTest.java b/src/test/java/com/example/moim/match/MatchAggregateServiceTest.java index 9260fca..e42716a 100644 --- a/src/test/java/com/example/moim/match/MatchAggregateServiceTest.java +++ b/src/test/java/com/example/moim/match/MatchAggregateServiceTest.java @@ -6,6 +6,7 @@ import com.example.moim.match.repository.MatchRepository; import com.example.moim.match.repository.MatchUserRepository; import com.example.moim.match.service.MatchAggregateService; +import com.example.moim.statistic.service.StatisticService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; @@ -30,6 +31,8 @@ class MatchAggregateServiceTest { private MatchRepository matchRepository; @Mock private MatchUserRepository matchUserRepository; + @Mock + private StatisticService statisticService; @InjectMocks private MatchAggregateService matchAggregateService; From 665248e20d23c9a0ae59d8b57a235118df58c639 Mon Sep 17 00:00:00 2001 From: hojooo Date: Mon, 30 Jun 2025 17:55:46 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20=EC=A0=84=EC=A0=81=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/StatisticController.java | 4 +-- .../moim/statistic/dto/StatisticDTO.java | 6 ++-- .../advice/StatisticControllerAdvice.java | 15 +--------- .../statistic/service/StatisticService.java | 29 +++++++++++++++---- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/example/moim/statistic/controller/StatisticController.java b/src/main/java/com/example/moim/statistic/controller/StatisticController.java index 76f3fbe..9a8ff02 100644 --- a/src/main/java/com/example/moim/statistic/controller/StatisticController.java +++ b/src/main/java/com/example/moim/statistic/controller/StatisticController.java @@ -14,8 +14,8 @@ public class StatisticController { // 전적 조회(전적 메인) @GetMapping("/statistic/{clubId}") - public BaseResponse getStatistic(@PathVariable Long clubId, - @RequestBody StatisticDTO.StatisticRequest request) { + public BaseResponse getStatistic(@PathVariable Long clubId, + @RequestBody StatisticDTO.StatisticInput request) { return BaseResponse.onSuccess(statisticService.getStatistic(clubId, request.getTargetSeason(), request.getTargetType()), ResponseCode.OK); } } diff --git a/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java b/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java index 56fe2b0..6cb7fba 100644 --- a/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java +++ b/src/main/java/com/example/moim/statistic/dto/StatisticDTO.java @@ -7,14 +7,14 @@ public class StatisticDTO { @Data @AllArgsConstructor - public static class StatisticRequest { + public static class StatisticInput { private String targetSeason; private String targetType; } @Data @AllArgsConstructor - public static class StatisticResponse { + public static class StatisticOutPut { private String season; private String rank; private int point; @@ -25,7 +25,7 @@ public static class StatisticResponse { private String mvpName; private int mvpGoalCount; - public StatisticResponse(Statistic statistic) { + public StatisticOutPut(Statistic statistic) { this.season = statistic.getSeason(); this.point = statistic.getPoint(); this.winRate = statistic.getWinRate(); diff --git a/src/main/java/com/example/moim/statistic/exception/advice/StatisticControllerAdvice.java b/src/main/java/com/example/moim/statistic/exception/advice/StatisticControllerAdvice.java index 7bc4877..7153ead 100644 --- a/src/main/java/com/example/moim/statistic/exception/advice/StatisticControllerAdvice.java +++ b/src/main/java/com/example/moim/statistic/exception/advice/StatisticControllerAdvice.java @@ -8,18 +8,5 @@ public class StatisticControllerAdvice extends GeneralException { public StatisticControllerAdvice(ResponseCode responseCode) { super(responseCode); } -// @ExceptionHandler -// public ResponseEntity MethodArgumentNotValidExHandle(MethodArgumentNotValidException e) { -// return new ResponseEntity<>(new ErrorResult(e.getBindingResult().getFieldError().getDefaultMessage()), HttpStatus.BAD_REQUEST); -// } -// -// @ExceptionHandler -// public ResponseEntity MatchSaveExHandle(MatchPermissionException e) { -// return new ResponseEntity<>(new ErrorResult(e.getMessage()), HttpStatus.UNAUTHORIZED); -// } -// -// @ExceptionHandler -// public ResponseEntity MatchRecordExpireExHandle(MatchRecordExpireException e) { -// return new ResponseEntity<>(new ErrorResult(e.getMessage()), HttpStatus.BAD_REQUEST); -// } + } diff --git a/src/main/java/com/example/moim/statistic/service/StatisticService.java b/src/main/java/com/example/moim/statistic/service/StatisticService.java index 63773b4..51bd75a 100644 --- a/src/main/java/com/example/moim/statistic/service/StatisticService.java +++ b/src/main/java/com/example/moim/statistic/service/StatisticService.java @@ -54,14 +54,31 @@ public void updateStatistic(Match match) { // 통합 Statistic homeStatistic = statisticRepository.findByClubAndSeasonAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL) - .orElseThrow(); + .orElseThrow(() -> new StatisticControllerAdvice(ResponseCode.STATISTIC_NOT_FOUND)); Statistic awayStatistic = statisticRepository.findByClubAndSeasonAndSportsType(match.getAwayClub(), currentSeason, SportsType.OVERALL) - .orElseThrow(); + .orElseThrow(() -> new StatisticControllerAdvice(ResponseCode.STATISTIC_NOT_FOUND)); int homeRankLevel = homeStatistic.getTier().getLevel(); int awayRankLevel = awayStatistic.getTier().getLevel(); - StatisticDTO.mvpDTO homeMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).get(0); - StatisticDTO.mvpDTO awayMVPResult = statisticRepository.findTopScorerByClubAndSportsType(match.getHomeClub(), currentSeason, SportsType.OVERALL).get(0); + StatisticDTO.mvpDTO homeMVPResult = statisticRepository + .findTopScorerByClubAndSportsType( + match.getHomeClub(), + currentSeason, + SportsType.OVERALL + ) + .stream() + .findFirst() + .orElse(new StatisticDTO.mvpDTO("No Data", 0L)); + + StatisticDTO.mvpDTO awayMVPResult = statisticRepository + .findTopScorerByClubAndSportsType( + match.getAwayClub(), + currentSeason, + SportsType.OVERALL + ) + .stream() + .findFirst() + .orElse(new StatisticDTO.mvpDTO("No Data", 0L)); homeStatistic.updateStatistic(match.getHomeScore(), match.getHomeScore(), homeRankLevel, homeMVPResult.getName(), homeMVPResult.getGoalCount().intValue()); awayStatistic.updateStatistic(match.getAwayScore(), match.getAwayScore(), awayRankLevel, awayMVPResult.getName(), awayMVPResult.getGoalCount().intValue()); @@ -98,7 +115,7 @@ public void updateStatistic(Match match) { } // 전적 조회 - public StatisticDTO.StatisticResponse getStatistic(Long clubId, String targetSeason, String sportsType) { + public StatisticDTO.StatisticOutPut getStatistic(Long clubId, String targetSeason, String sportsType) { // String currentSeason = Statistic.getCurrentSeason(); SportsType targetSportsType = SportsType.valueOf(sportsType); Club club = clubRepository.findById(clubId) @@ -106,6 +123,6 @@ public StatisticDTO.StatisticResponse getStatistic(Long clubId, String targetSea Statistic statistic = statisticRepository.findByClubAndSeasonAndSportsType(club, targetSeason, targetSportsType) .orElseThrow(() -> new StatisticControllerAdvice(ResponseCode.STATISTIC_NOT_FOUND)); - return new StatisticDTO.StatisticResponse(statistic); + return new StatisticDTO.StatisticOutPut(statistic); } }