From 54607fe6a286571237b4910ea497bc6e089cc76f Mon Sep 17 00:00:00 2001 From: sangeun Date: Wed, 29 May 2024 17:56:44 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat=20:=20=EC=98=A4=EB=8A=98=EC=9D=98=20?= =?UTF-8?q?=EC=9D=B8=EA=B8=B0=20=ED=99=9C=EB=8F=99=20API=20=EA=B5=AC?= =?UTF-8?q?=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../record/repository/RecordRepository.java | 5 ++- .../domain/report/api/TrendController.java | 11 ++++++ .../TrendPopularActivitiesService.java | 36 +++++++++++++++++++ .../report/converter/TrendConverter.java | 11 ++++++ .../TrendPopularActivitiesResponseDto.java | 25 +++++++++++++ 5 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hobak/happinessql/domain/report/application/TrendPopularActivitiesService.java create mode 100644 src/main/java/com/hobak/happinessql/domain/report/dto/TrendPopularActivitiesResponseDto.java diff --git a/src/main/java/com/hobak/happinessql/domain/record/repository/RecordRepository.java b/src/main/java/com/hobak/happinessql/domain/record/repository/RecordRepository.java index f3e3b16..a3f9459 100644 --- a/src/main/java/com/hobak/happinessql/domain/record/repository/RecordRepository.java +++ b/src/main/java/com/hobak/happinessql/domain/record/repository/RecordRepository.java @@ -5,7 +5,8 @@ import org.springframework.data.domain.Page; 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 java.time.LocalDateTime; import java.util.List; @@ -14,4 +15,6 @@ public interface RecordRepository extends JpaRepository { Page findByRecordIdLessThanAndUserOrderByRecordIdDesc(Long recordId, User user, Pageable pageRequest); List findAllByCreatedAtBetweenAndUser(LocalDateTime startOfMonth, LocalDateTime endOfMonth, User user); List findAllByUser(User user); + @Query("SELECT r.activity, COUNT(r) as count FROM Record r WHERE r.createdAt >= :time GROUP BY r.activity ORDER BY count DESC limit 3") + List findPopularActivities(@Param("time")LocalDateTime time); } diff --git a/src/main/java/com/hobak/happinessql/domain/report/api/TrendController.java b/src/main/java/com/hobak/happinessql/domain/report/api/TrendController.java index a9aa928..b81c221 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/api/TrendController.java +++ b/src/main/java/com/hobak/happinessql/domain/report/api/TrendController.java @@ -1,7 +1,9 @@ package com.hobak.happinessql.domain.report.api; import com.hobak.happinessql.domain.report.application.TrendHappinessService; +import com.hobak.happinessql.domain.report.application.TrendPopularActivitiesService; import com.hobak.happinessql.domain.report.dto.TrendHappinessResponseDto; +import com.hobak.happinessql.domain.report.dto.TrendPopularActivitiesResponseDto; import com.hobak.happinessql.global.response.DataResponseDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -10,16 +12,25 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @Tag(name="Trend", description = "행복 트렌드 관련 REST API에 대한 명세를 제공합니다.") @RestController @RequiredArgsConstructor @RequestMapping("api/trend") public class TrendController { private final TrendHappinessService trendHappinessService; + private final TrendPopularActivitiesService trendPopularActivitiesService; @Operation(summary = "대한민국 평균 행복지수", description = "전체 유저의 평균 행복지수와 그에 따른 수준을 판단합니다.") @GetMapping("/happiness") public DataResponseDto getHappiness() { TrendHappinessResponseDto responseDto = trendHappinessService.getTrendHappiness(); return DataResponseDto.of(responseDto, "대한민국 평균 행복지수를 성공적으로 조회했습니다."); } + @Operation(summary = "오늘의 인기 활동 top3", description = "오늘 많이 기록된 활동의 이름과 기록 횟수를 조회합니다.") + @GetMapping("/popular") + public DataResponseDto> getPopular() { + List responseDto = trendPopularActivitiesService.getPopularActivities(); + return DataResponseDto.of(responseDto, "오늘의 인기 활동을 성공적으로 조회했습니다."); + } } diff --git a/src/main/java/com/hobak/happinessql/domain/report/application/TrendPopularActivitiesService.java b/src/main/java/com/hobak/happinessql/domain/report/application/TrendPopularActivitiesService.java new file mode 100644 index 0000000..7158227 --- /dev/null +++ b/src/main/java/com/hobak/happinessql/domain/report/application/TrendPopularActivitiesService.java @@ -0,0 +1,36 @@ +package com.hobak.happinessql.domain.report.application; + +import com.hobak.happinessql.domain.activity.domain.Activity; +import com.hobak.happinessql.domain.record.repository.RecordRepository; +import com.hobak.happinessql.domain.report.converter.TrendConverter; +import com.hobak.happinessql.domain.report.dto.TrendPopularActivitiesResponseDto; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class TrendPopularActivitiesService { + private final RecordRepository recordRepository; + public List getPopularActivities(){ + LocalDate today = LocalDate.now(); + List results = recordRepository.findPopularActivities(today.atStartOfDay()); + List popularActivities = results.stream() + .map(result -> (Activity) result[0]) + .toList(); + List times = results.stream() + .map(result -> ((Number) result[1]).longValue()) + .toList(); + int ranking = 1; + List responseDtos = new ArrayList<>(); + for(int i = 0; i Date: Wed, 29 May 2024 21:00:33 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix=20:=20TimePeriod=20enum=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20TimeOfDay=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/ReportSummaryService.java | 6 +++--- ...er.java => TimeOfDayHappinessAnalyzer.java} | 18 +++++++++--------- .../report/converter/ReportConverter.java | 6 +++--- .../domain/{TimePeriod.java => TimeOfDay.java} | 8 ++++---- .../report/dto/ReportSummaryResponseDto.java | 10 +++++----- 5 files changed, 24 insertions(+), 24 deletions(-) rename src/main/java/com/hobak/happinessql/domain/report/application/{TimePeriodHappinessAnalyzer.java => TimeOfDayHappinessAnalyzer.java} (65%) rename src/main/java/com/hobak/happinessql/domain/report/domain/{TimePeriod.java => TimeOfDay.java} (84%) diff --git a/src/main/java/com/hobak/happinessql/domain/report/application/ReportSummaryService.java b/src/main/java/com/hobak/happinessql/domain/report/application/ReportSummaryService.java index a79745e..f572aa0 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/application/ReportSummaryService.java +++ b/src/main/java/com/hobak/happinessql/domain/report/application/ReportSummaryService.java @@ -3,7 +3,7 @@ import com.hobak.happinessql.domain.record.domain.Record; import com.hobak.happinessql.domain.record.repository.RecordRepository; import com.hobak.happinessql.domain.report.converter.ReportConverter; -import com.hobak.happinessql.domain.report.domain.TimePeriod; +import com.hobak.happinessql.domain.report.domain.TimeOfDay; import com.hobak.happinessql.domain.report.dto.ReportSummaryResponseDto; import com.hobak.happinessql.domain.user.domain.User; import lombok.RequiredArgsConstructor; @@ -45,9 +45,9 @@ public ReportSummaryResponseDto getMonthlySummary(User user) { private ReportSummaryResponseDto generateReportSummary(List records) { String location = LocationHappinessAnalyzer.getHappiestLocation(records); - TimePeriod timePeriod = TimePeriodHappinessAnalyzer.getHappiestTimePeriod(records); + TimeOfDay timeOfDay = TimeOfDayHappinessAnalyzer.getHappiestTimeOfDay(records); String activity = ActivityHappinessAnalyzer.getHappiestActivity(records); - return ReportConverter.toReportSummaryResponseDto(timePeriod, location, activity); + return ReportConverter.toReportSummaryResponseDto(timeOfDay, location, activity); } } diff --git a/src/main/java/com/hobak/happinessql/domain/report/application/TimePeriodHappinessAnalyzer.java b/src/main/java/com/hobak/happinessql/domain/report/application/TimeOfDayHappinessAnalyzer.java similarity index 65% rename from src/main/java/com/hobak/happinessql/domain/report/application/TimePeriodHappinessAnalyzer.java rename to src/main/java/com/hobak/happinessql/domain/report/application/TimeOfDayHappinessAnalyzer.java index 5b7d259..a65b37d 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/application/TimePeriodHappinessAnalyzer.java +++ b/src/main/java/com/hobak/happinessql/domain/report/application/TimeOfDayHappinessAnalyzer.java @@ -1,31 +1,31 @@ package com.hobak.happinessql.domain.report.application; import com.hobak.happinessql.domain.record.domain.Record; -import com.hobak.happinessql.domain.report.domain.TimePeriod; +import com.hobak.happinessql.domain.report.domain.TimeOfDay; import java.util.*; import java.util.stream.Collectors; -public class TimePeriodHappinessAnalyzer { +public class TimeOfDayHappinessAnalyzer { - public static TimePeriod getHappiestTimePeriod(List records) { + public static TimeOfDay getHappiestTimeOfDay(List records) { if (records == null || records.isEmpty()) { return null; } - Map> timePeriodHappinessMap = new HashMap<>(); + Map> timeOfDayHappinessMap = new HashMap<>(); // 시간대별 행복도를 매핑 for (Record record : records) { - TimePeriod timePeriod = TimePeriod.of(record.getCreatedAt().getHour()); + TimeOfDay timeOfDay = TimeOfDay.of(record.getCreatedAt().getHour()); Integer happiness = record.getHappiness(); - timePeriodHappinessMap.computeIfAbsent(timePeriod, k -> new ArrayList<>()).add(happiness); + timeOfDayHappinessMap.computeIfAbsent(timeOfDay, k -> new ArrayList<>()).add(happiness); } // 평균 행복도 계산 - Map averageHappinessMap = timePeriodHappinessMap.entrySet().stream() + Map averageHappinessMap = timeOfDayHappinessMap.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().stream().mapToInt(Integer::intValue).average().orElse(0))); @@ -35,7 +35,7 @@ public static TimePeriod getHappiestTimePeriod(List records) { .orElse(Double.MIN_VALUE); // 최대 평균 행복도를 가진 시간대 후보들 필터링 - List candidates = averageHappinessMap.entrySet().stream() + List candidates = averageHappinessMap.entrySet().stream() .filter(entry -> entry.getValue() == maxAverageHappiness) .map(Map.Entry::getKey) .toList(); @@ -46,7 +46,7 @@ public static TimePeriod getHappiestTimePeriod(List records) { // 후보 중 빈도수가 가장 높은 시간대 찾기, 동일 빈도시 랜덤 선택 return candidates.stream() - .max(Comparator.comparingInt(tp -> timePeriodHappinessMap.get(tp).size())) + .max(Comparator.comparingInt(tp -> timeOfDayHappinessMap.get(tp).size())) .orElseGet(() -> candidates.get(new Random().nextInt(candidates.size()))); } } diff --git a/src/main/java/com/hobak/happinessql/domain/report/converter/ReportConverter.java b/src/main/java/com/hobak/happinessql/domain/report/converter/ReportConverter.java index 4a6d5fd..d37a25d 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/converter/ReportConverter.java +++ b/src/main/java/com/hobak/happinessql/domain/report/converter/ReportConverter.java @@ -1,6 +1,6 @@ package com.hobak.happinessql.domain.report.converter; -import com.hobak.happinessql.domain.report.domain.TimePeriod; +import com.hobak.happinessql.domain.report.domain.TimeOfDay; import com.hobak.happinessql.domain.report.dto.ActivityHappinessDto; import com.hobak.happinessql.domain.report.dto.LocationHappinessDto; import com.hobak.happinessql.domain.report.dto.ReportGraphResponseDto; @@ -9,11 +9,11 @@ import java.util.ArrayList; public class ReportConverter { - public static ReportSummaryResponseDto toReportSummaryResponseDto(TimePeriod timePeriod, String location, String activity) { + public static ReportSummaryResponseDto toReportSummaryResponseDto(TimeOfDay timeOfDay, String location, String activity) { return ReportSummaryResponseDto.builder() .activity(activity) .location(location) - .timePeriod(timePeriod) + .timeOfDay(timeOfDay) .build(); } diff --git a/src/main/java/com/hobak/happinessql/domain/report/domain/TimePeriod.java b/src/main/java/com/hobak/happinessql/domain/report/domain/TimeOfDay.java similarity index 84% rename from src/main/java/com/hobak/happinessql/domain/report/domain/TimePeriod.java rename to src/main/java/com/hobak/happinessql/domain/report/domain/TimeOfDay.java index 79d1f98..bd7e845 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/domain/TimePeriod.java +++ b/src/main/java/com/hobak/happinessql/domain/report/domain/TimeOfDay.java @@ -5,7 +5,7 @@ import lombok.AllArgsConstructor; @AllArgsConstructor -public enum TimePeriod { +public enum TimeOfDay { DAWN("새벽"), MORNING("아침"), AFTERNOON("낮"), @@ -14,7 +14,7 @@ public enum TimePeriod { private final String viewName; - public static TimePeriod of(int hour) { + public static TimeOfDay of(int hour) { if (hour >= 0 && hour < 5) { return DAWN; } else if (hour >= 5 && hour < 9) { @@ -29,8 +29,8 @@ public static TimePeriod of(int hour) { } @JsonCreator - public static TimePeriod from(String value) { - for (TimePeriod status : TimePeriod.values()) { + public static TimeOfDay from(String value) { + for (TimeOfDay status : TimeOfDay.values()) { if (status.getViewName().equals(value)) { return status; } diff --git a/src/main/java/com/hobak/happinessql/domain/report/dto/ReportSummaryResponseDto.java b/src/main/java/com/hobak/happinessql/domain/report/dto/ReportSummaryResponseDto.java index cab3aa2..b4015ed 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/dto/ReportSummaryResponseDto.java +++ b/src/main/java/com/hobak/happinessql/domain/report/dto/ReportSummaryResponseDto.java @@ -1,7 +1,7 @@ package com.hobak.happinessql.domain.report.dto; import com.fasterxml.jackson.annotation.JsonProperty; -import com.hobak.happinessql.domain.report.domain.TimePeriod; +import com.hobak.happinessql.domain.report.domain.TimeOfDay; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -12,16 +12,16 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ReportSummaryResponseDto { - @JsonProperty("time_period") - private TimePeriod timePeriod; + @JsonProperty("time_of_day") + private TimeOfDay timeOfDay; private String location; private String activity; @Builder - public ReportSummaryResponseDto(TimePeriod timePeriod, String location, String activity) { - this.timePeriod = timePeriod; + public ReportSummaryResponseDto(TimeOfDay timeOfDay, String location, String activity) { + this.timeOfDay = timeOfDay; this.location = location; this.activity = activity; } From d1788aea630edc4ae48ebb8d6aa1ef2ffae74cda Mon Sep 17 00:00:00 2001 From: yel-m Date: Wed, 29 May 2024 22:05:21 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat=20:=20=EC=A0=84=EC=B2=B4,=20=EC=97=B0?= =?UTF-8?q?=EA=B0=84,=20=EC=9B=94=EA=B0=84=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EB=8C=80=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EB=9E=AD=ED=82=B9=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/report/api/ReportController.java | 35 +++-- .../ReportTimeOfPeriodRankingService.java | 42 ++++++ .../TimeOfDayHappinessAnalyzer.java | 121 ++++++++++++++---- .../report/converter/ReportConverter.java | 12 +- .../report/dto/TimeOfDayHappinessDto.java | 25 ++++ 5 files changed, 197 insertions(+), 38 deletions(-) create mode 100644 src/main/java/com/hobak/happinessql/domain/report/application/ReportTimeOfPeriodRankingService.java create mode 100644 src/main/java/com/hobak/happinessql/domain/report/dto/TimeOfDayHappinessDto.java diff --git a/src/main/java/com/hobak/happinessql/domain/report/api/ReportController.java b/src/main/java/com/hobak/happinessql/domain/report/api/ReportController.java index 3ba73a0..0987cb4 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/api/ReportController.java +++ b/src/main/java/com/hobak/happinessql/domain/report/api/ReportController.java @@ -1,14 +1,8 @@ package com.hobak.happinessql.domain.report.api; -import com.hobak.happinessql.domain.report.application.ReportActivityRankingService; -import com.hobak.happinessql.domain.report.application.ReportGraphService; -import com.hobak.happinessql.domain.report.application.ReportLocationRankingService; -import com.hobak.happinessql.domain.report.application.ReportSummaryService; -import com.hobak.happinessql.domain.report.dto.ActivityHappinessDto; -import com.hobak.happinessql.domain.report.dto.LocationHappinessDto; -import com.hobak.happinessql.domain.report.dto.ReportGraphResponseDto; -import com.hobak.happinessql.domain.report.dto.ReportSummaryResponseDto; +import com.hobak.happinessql.domain.report.application.*; +import com.hobak.happinessql.domain.report.dto.*; import com.hobak.happinessql.domain.user.application.UserFindService; import com.hobak.happinessql.domain.user.domain.User; import com.hobak.happinessql.global.response.DataResponseDto; @@ -33,6 +27,7 @@ public class ReportController { private final ReportSummaryService reportSummaryService; private final ReportActivityRankingService reportActivityRankingService; private final ReportLocationRankingService reportLocationRankingService; + private final ReportTimeOfPeriodRankingService reportTimeOfPeriodRankingService; private final ReportGraphService reportGraphService; @Operation(summary = "[전체] 행복 종합 리포트", description = "전체 기간에서 언제, 어디에서, 무엇을 할 때 행복했는지에 대한 종합적인 리포트를 제공합니다.") @GetMapping("/all/summary") @@ -144,4 +139,28 @@ public DataResponseDto> getMonthlyActivityRankings(@A return DataResponseDto.of(responseDto, "월간 활동 행복도 순위를 성공적으로 조회했습니다."); } + @Operation(summary = "[전체] 시간대 행복도 순위", description = "전체 기록에서 시간대 행복도 순위를 제공합니다.") + @GetMapping("/all/ranking/time") + public DataResponseDto> getAllTimeOfDayRankings(@AuthenticationPrincipal UserDetails userDetails) { + User user = userFindService.findByUserDetails(userDetails); + List responseDto = reportTimeOfPeriodRankingService.getAllTimeOfDayRankings(user); + return DataResponseDto.of(responseDto, "전체 활동 행복도 순위를 성공적으로 조회했습니다."); + } + + @Operation(summary = "[연간] 시간대 행복도 순위", description = "이번 해 시간대 행복도 순위를 제공합니다.") + @GetMapping("/year/ranking/time") + public DataResponseDto> getYearlyTimeOfDayRankings(@AuthenticationPrincipal UserDetails userDetails) { + User user = userFindService.findByUserDetails(userDetails); + List responseDto = reportTimeOfPeriodRankingService.getYearlyTimeOfDayRankings(user); + return DataResponseDto.of(responseDto, "연간 활동 행복도 순위를 성공적으로 조회했습니다."); + } + + @Operation(summary = "[월간] 시간대 행복도 순위", description = "이번 달 시간대 행복도 순위를 제공합니다.") + @GetMapping("/month/ranking/time") + public DataResponseDto> getMonthlyTimeOfDayRankings(@AuthenticationPrincipal UserDetails userDetails) { + User user = userFindService.findByUserDetails(userDetails); + List responseDto = reportTimeOfPeriodRankingService.getMonthlyTimeOfDayRankings(user); + return DataResponseDto.of(responseDto, "월간 활동 행복도 순위를 성공적으로 조회했습니다."); + } + } diff --git a/src/main/java/com/hobak/happinessql/domain/report/application/ReportTimeOfPeriodRankingService.java b/src/main/java/com/hobak/happinessql/domain/report/application/ReportTimeOfPeriodRankingService.java new file mode 100644 index 0000000..49bc1d5 --- /dev/null +++ b/src/main/java/com/hobak/happinessql/domain/report/application/ReportTimeOfPeriodRankingService.java @@ -0,0 +1,42 @@ +package com.hobak.happinessql.domain.report.application; + +import com.hobak.happinessql.domain.record.domain.Record; +import com.hobak.happinessql.domain.record.repository.RecordRepository; +import com.hobak.happinessql.domain.report.dto.TimeOfDayHappinessDto; +import com.hobak.happinessql.domain.user.domain.User; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ReportTimeOfPeriodRankingService { + + private final RecordRepository recordRepository; + + public List getAllTimeOfDayRankings(User user) { + List records = recordRepository.findAllByUser(user); + return TimeOfDayHappinessAnalyzer.getTimeOfDayRankings(records); + } + + public List getYearlyTimeOfDayRankings(User user) { + int currentYear = LocalDate.now().getYear(); + LocalDateTime startOfYear = LocalDateTime.of(currentYear, 1, 1, 0, 0); + LocalDateTime endOfYear = LocalDateTime.of(currentYear, 12, 31, 23, 59, 59); + List records = recordRepository.findAllByCreatedAtBetweenAndUser(startOfYear, endOfYear, user); + return TimeOfDayHappinessAnalyzer.getTimeOfDayRankings(records); + } + + public List getMonthlyTimeOfDayRankings(User user) { + LocalDate today = LocalDate.now(); + LocalDate startOfMonth = today.withDayOfMonth(1); + LocalDate endOfMonth = today.withDayOfMonth(today.lengthOfMonth()); + LocalDateTime startOfMonthDateTime = startOfMonth.atStartOfDay(); + LocalDateTime endOfMonthDateTime = endOfMonth.atTime(23, 59, 59); + List records = recordRepository.findAllByCreatedAtBetweenAndUser(startOfMonthDateTime, endOfMonthDateTime, user); + return TimeOfDayHappinessAnalyzer.getTimeOfDayRankings(records); + } +} diff --git a/src/main/java/com/hobak/happinessql/domain/report/application/TimeOfDayHappinessAnalyzer.java b/src/main/java/com/hobak/happinessql/domain/report/application/TimeOfDayHappinessAnalyzer.java index a65b37d..fe31faf 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/application/TimeOfDayHappinessAnalyzer.java +++ b/src/main/java/com/hobak/happinessql/domain/report/application/TimeOfDayHappinessAnalyzer.java @@ -1,7 +1,9 @@ package com.hobak.happinessql.domain.report.application; import com.hobak.happinessql.domain.record.domain.Record; +import com.hobak.happinessql.domain.report.converter.ReportConverter; import com.hobak.happinessql.domain.report.domain.TimeOfDay; +import com.hobak.happinessql.domain.report.dto.TimeOfDayHappinessDto; import java.util.*; import java.util.stream.Collectors; @@ -9,44 +11,111 @@ public class TimeOfDayHappinessAnalyzer { public static TimeOfDay getHappiestTimeOfDay(List records) { - if (records == null || records.isEmpty()) { return null; } - Map> timeOfDayHappinessMap = new HashMap<>(); + // 시간대를 기준으로 Record 그룹화 + Map> timeOfDayRecordsMap = groupRecordsByTimeOfDay(records); + + // 시간대별 평균 행복도와 빈도 계산 + Map timeOfDayAverageHappiness = calculateTimeOfDayAverageHappiness(timeOfDayRecordsMap); + Map timeOfDayFrequency = calculateTimeOfDayFrequency(timeOfDayRecordsMap); + + // 평균 행복도가 가장 높은 시간대 찾기 + double maxHappiness = Collections.max(timeOfDayAverageHappiness.values()); - // 시간대별 행복도를 매핑 - for (Record record : records) { - TimeOfDay timeOfDay = TimeOfDay.of(record.getCreatedAt().getHour()); - Integer happiness = record.getHappiness(); + // 평균 행복도가 가장 높은 시간대들 중 빈도가 가장 높은 시간대 찾기 + Optional happiestTimeOfDay = findHappiestTimeOfDay(timeOfDayAverageHappiness, timeOfDayFrequency, maxHappiness); - timeOfDayHappinessMap.computeIfAbsent(timeOfDay, k -> new ArrayList<>()).add(happiness); + // 평균 행복도와 빈도가 같은 시간대가 여러 개인 경우, 랜덤으로 선택 + List happiestTimesOfDay = findTimesOfDayWithMaxHappiness(timeOfDayAverageHappiness, maxHappiness); + + if (happiestTimesOfDay.size() > 1) { + Collections.shuffle(happiestTimesOfDay); + return happiestTimesOfDay.get(0); + } else { + return happiestTimeOfDay.orElse(null); } + } - // 평균 행복도 계산 - Map averageHappinessMap = timeOfDayHappinessMap.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, - entry -> entry.getValue().stream().mapToInt(Integer::intValue).average().orElse(0))); + public static List getTimeOfDayRankings(List records) { + List timeOfDayRankings = new ArrayList<>(); - // 최대 평균 행복도를 가진 시간대 찾기 - double maxAverageHappiness = averageHappinessMap.values().stream() - .max(Double::compare) - .orElse(Double.MIN_VALUE); + // 시간대별 평균 행복도와 빈도 계산 + Map> timeOfDayRecordsMap = groupRecordsByTimeOfDay(records); + Map timeOfDayAverageHappiness = calculateTimeOfDayAverageHappiness(timeOfDayRecordsMap); + Map timeOfDayFrequency = calculateTimeOfDayFrequency(timeOfDayRecordsMap); - // 최대 평균 행복도를 가진 시간대 후보들 필터링 - List candidates = averageHappinessMap.entrySet().stream() - .filter(entry -> entry.getValue() == maxAverageHappiness) - .map(Map.Entry::getKey) - .toList(); + // 평균 행복도와 빈도를 기준으로 시간대들을 정렬 + List sortedTimesOfDay = sortTimesOfDay(timeOfDayAverageHappiness, timeOfDayFrequency); - if (candidates.isEmpty()) { - return null; + // 상위 N개의 시간대 선정 + for (int i = 0; i < sortedTimesOfDay.size(); i++) { + TimeOfDay timeOfDay = sortedTimesOfDay.get(i); + TimeOfDayHappinessDto timeOfDayDto = ReportConverter.toTimeOfDayHappinessDto(i + 1, timeOfDay); + timeOfDayRankings.add(timeOfDayDto); } - // 후보 중 빈도수가 가장 높은 시간대 찾기, 동일 빈도시 랜덤 선택 - return candidates.stream() - .max(Comparator.comparingInt(tp -> timeOfDayHappinessMap.get(tp).size())) - .orElseGet(() -> candidates.get(new Random().nextInt(candidates.size()))); + return timeOfDayRankings; + } + + private static Map> groupRecordsByTimeOfDay(List records) { + return records.stream() + .collect(Collectors.groupingBy(record -> TimeOfDay.of(record.getCreatedAt().getHour()))); + } + + private static Map calculateTimeOfDayAverageHappiness(Map> timeOfDayRecordsMap) { + Map timeOfDayAverageHappiness = new HashMap<>(); + timeOfDayRecordsMap.forEach((timeOfDay, recordList) -> { + double averageHappiness = recordList.stream() + .mapToInt(Record::getHappiness) + .average() + .orElse(Double.NaN); + timeOfDayAverageHappiness.put(timeOfDay, averageHappiness); + }); + return timeOfDayAverageHappiness; + } + + private static Map calculateTimeOfDayFrequency(Map> timeOfDayRecordsMap) { + Map timeOfDayFrequency = new HashMap<>(); + timeOfDayRecordsMap.forEach((timeOfDay, recordList) -> { + timeOfDayFrequency.put(timeOfDay, recordList.size()); + }); + return timeOfDayFrequency; + } + + private static Optional findHappiestTimeOfDay(Map timeOfDayAverageHappiness, + Map timeOfDayFrequency, + double maxHappiness) { + return timeOfDayAverageHappiness.entrySet().stream() + .filter(entry -> entry.getValue() == maxHappiness) + .max(Comparator.comparing(entry -> timeOfDayFrequency.get(entry.getKey()))) + .map(Map.Entry::getKey); + } + + private static List findTimesOfDayWithMaxHappiness(Map timeOfDayAverageHappiness, + double maxHappiness) { + return timeOfDayAverageHappiness.entrySet().stream() + .filter(entry -> entry.getValue() == maxHappiness) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + } + + private static List sortTimesOfDay(Map timeOfDayAverageHappiness, + Map timeOfDayFrequency) { + return timeOfDayAverageHappiness.entrySet().stream() + .sorted((entry1, entry2) -> { + int compare = Double.compare(entry2.getValue(), entry1.getValue()); + if (compare == 0) { + compare = Integer.compare(timeOfDayFrequency.get(entry2.getKey()), timeOfDayFrequency.get(entry1.getKey())); + } + if (compare == 0) { + compare = entry1.getKey().name().compareTo(entry2.getKey().name()); + } + return compare; + }) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); } } diff --git a/src/main/java/com/hobak/happinessql/domain/report/converter/ReportConverter.java b/src/main/java/com/hobak/happinessql/domain/report/converter/ReportConverter.java index d37a25d..558a9f3 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/converter/ReportConverter.java +++ b/src/main/java/com/hobak/happinessql/domain/report/converter/ReportConverter.java @@ -1,10 +1,7 @@ package com.hobak.happinessql.domain.report.converter; import com.hobak.happinessql.domain.report.domain.TimeOfDay; -import com.hobak.happinessql.domain.report.dto.ActivityHappinessDto; -import com.hobak.happinessql.domain.report.dto.LocationHappinessDto; -import com.hobak.happinessql.domain.report.dto.ReportGraphResponseDto; -import com.hobak.happinessql.domain.report.dto.ReportSummaryResponseDto; +import com.hobak.happinessql.domain.report.dto.*; import java.util.ArrayList; @@ -37,4 +34,11 @@ public static ReportGraphResponseDto toReportGraphResponseDto(ArrayList .happiness(happiness) .build(); } + + public static TimeOfDayHappinessDto toTimeOfDayHappinessDto(int ranking, TimeOfDay timeOfDay) { + return TimeOfDayHappinessDto.builder() + .ranking(ranking) + .timeOfDay(timeOfDay) + .build(); + } } diff --git a/src/main/java/com/hobak/happinessql/domain/report/dto/TimeOfDayHappinessDto.java b/src/main/java/com/hobak/happinessql/domain/report/dto/TimeOfDayHappinessDto.java new file mode 100644 index 0000000..5039725 --- /dev/null +++ b/src/main/java/com/hobak/happinessql/domain/report/dto/TimeOfDayHappinessDto.java @@ -0,0 +1,25 @@ +package com.hobak.happinessql.domain.report.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.hobak.happinessql.domain.report.domain.TimeOfDay; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class TimeOfDayHappinessDto { + + private int ranking; + + @JsonProperty("time_of_day") + private TimeOfDay timeOfDay; + + @Builder + public TimeOfDayHappinessDto(int ranking, TimeOfDay timeOfDay) { + this.ranking = ranking; + this.timeOfDay = timeOfDay; + } +} From e372d244c81dae1e57c15aed54e751e06e7c9ca8 Mon Sep 17 00:00:00 2001 From: yel-m Date: Wed, 29 May 2024 22:26:36 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix=20:=20responseBody=EC=97=90=20=EB=93=A4?= =?UTF-8?q?=EC=96=B4=EA=B0=88=20message=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../happinessql/domain/report/api/ReportController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hobak/happinessql/domain/report/api/ReportController.java b/src/main/java/com/hobak/happinessql/domain/report/api/ReportController.java index 0987cb4..3bdfb34 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/api/ReportController.java +++ b/src/main/java/com/hobak/happinessql/domain/report/api/ReportController.java @@ -144,7 +144,7 @@ public DataResponseDto> getMonthlyActivityRankings(@A public DataResponseDto> getAllTimeOfDayRankings(@AuthenticationPrincipal UserDetails userDetails) { User user = userFindService.findByUserDetails(userDetails); List responseDto = reportTimeOfPeriodRankingService.getAllTimeOfDayRankings(user); - return DataResponseDto.of(responseDto, "전체 활동 행복도 순위를 성공적으로 조회했습니다."); + return DataResponseDto.of(responseDto, "전체 시간대 행복도 순위를 성공적으로 조회했습니다."); } @Operation(summary = "[연간] 시간대 행복도 순위", description = "이번 해 시간대 행복도 순위를 제공합니다.") @@ -152,7 +152,7 @@ public DataResponseDto> getAllTimeOfDayRankings(@Aut public DataResponseDto> getYearlyTimeOfDayRankings(@AuthenticationPrincipal UserDetails userDetails) { User user = userFindService.findByUserDetails(userDetails); List responseDto = reportTimeOfPeriodRankingService.getYearlyTimeOfDayRankings(user); - return DataResponseDto.of(responseDto, "연간 활동 행복도 순위를 성공적으로 조회했습니다."); + return DataResponseDto.of(responseDto, "연간 시간대 행복도 순위를 성공적으로 조회했습니다."); } @Operation(summary = "[월간] 시간대 행복도 순위", description = "이번 달 시간대 행복도 순위를 제공합니다.") @@ -160,7 +160,7 @@ public DataResponseDto> getYearlyTimeOfDayRankings(@ public DataResponseDto> getMonthlyTimeOfDayRankings(@AuthenticationPrincipal UserDetails userDetails) { User user = userFindService.findByUserDetails(userDetails); List responseDto = reportTimeOfPeriodRankingService.getMonthlyTimeOfDayRankings(user); - return DataResponseDto.of(responseDto, "월간 활동 행복도 순위를 성공적으로 조회했습니다."); + return DataResponseDto.of(responseDto, "월간 시간대 행복도 순위를 성공적으로 조회했습니다."); } } From 84268cccb2aff548db0ef9347aead16e79e5a7e9 Mon Sep 17 00:00:00 2001 From: sangeun Date: Thu, 30 May 2024 00:05:35 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix=20:=20=EC=9D=91=EB=8B=B5=EC=97=90=20act?= =?UTF-8?q?ivityId=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/application/TrendPopularActivitiesService.java | 2 +- .../happinessql/domain/report/converter/TrendConverter.java | 3 +-- .../domain/report/dto/TrendPopularActivitiesResponseDto.java | 4 +--- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/hobak/happinessql/domain/report/application/TrendPopularActivitiesService.java b/src/main/java/com/hobak/happinessql/domain/report/application/TrendPopularActivitiesService.java index 7158227..777bd94 100644 --- a/src/main/java/com/hobak/happinessql/domain/report/application/TrendPopularActivitiesService.java +++ b/src/main/java/com/hobak/happinessql/domain/report/application/TrendPopularActivitiesService.java @@ -29,7 +29,7 @@ public List getPopularActivities(){ for(int i = 0; i