diff --git a/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java b/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java index 3f40875..a845008 100644 --- a/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java +++ b/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java @@ -45,7 +45,8 @@ public class HandlerConfig { StatisticProtectedFilesHandler statisticProtectedFilesHandler, StatisticViolationHandler statisticViolationHandler, StatisticSubscriberHandler statisticSubscriberHandler, - StatisticTokenHandler statisticTokenHandler + StatisticTokenHandler statisticTokenHandler, + StatisticIncomeHandler statisticIncomeHandler ) { Map map = new HashMap<>(); @@ -83,6 +84,7 @@ public class HandlerConfig { map.put(30023, statisticViolationHandler); map.put(30024, statisticSubscriberHandler); map.put(30025, statisticTokenHandler); + map.put(30026, statisticIncomeHandler); return map; } diff --git a/src/main/java/ru/soune/nocopy/dto/statistic/IncomeStatisticRequest.java b/src/main/java/ru/soune/nocopy/dto/statistic/IncomeStatisticRequest.java new file mode 100644 index 0000000..1cbe6a9 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/statistic/IncomeStatisticRequest.java @@ -0,0 +1,6 @@ +package ru.soune.nocopy.dto.statistic; +import lombok.Data; + +@Data +public class IncomeStatisticRequest { +} diff --git a/src/main/java/ru/soune/nocopy/dto/statistic/IncomeStatisticResponse.java b/src/main/java/ru/soune/nocopy/dto/statistic/IncomeStatisticResponse.java new file mode 100644 index 0000000..12fea04 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/statistic/IncomeStatisticResponse.java @@ -0,0 +1,40 @@ +package ru.soune.nocopy.dto.statistic; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class IncomeStatisticResponse { + + @JsonProperty("total_income") + private Long totalIncome; + + @JsonProperty("total_available") + private Long totalAvailable; + + @JsonProperty("total_hold") + private Long totalHold; + + @JsonProperty("income_per_user") + private Percentiles incomePerUser; + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Percentiles { + @JsonProperty("p5") + private Long p5; + + @JsonProperty("p50") + private Long p50; + + @JsonProperty("p95") + private Long p95; + } +} diff --git a/src/main/java/ru/soune/nocopy/handler/StatisticIncomeHandler.java b/src/main/java/ru/soune/nocopy/handler/StatisticIncomeHandler.java new file mode 100644 index 0000000..13d0e5c --- /dev/null +++ b/src/main/java/ru/soune/nocopy/handler/StatisticIncomeHandler.java @@ -0,0 +1,30 @@ +package ru.soune.nocopy.handler; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import ru.soune.nocopy.dto.BaseRequest; +import ru.soune.nocopy.dto.BaseResponse; +import ru.soune.nocopy.dto.MessageCode; +import ru.soune.nocopy.dto.statistic.IncomeStatisticResponse; +import ru.soune.nocopy.service.statistic.IncomeStatisticService; + +@Component +@RequiredArgsConstructor +@Slf4j +public class StatisticIncomeHandler implements RequestHandler { + + private final IncomeStatisticService statisticService; + + @Override + public BaseResponse handle(BaseRequest request) throws Exception { + IncomeStatisticResponse statistics = statisticService.getIncomeStatistics(); + + return new BaseResponse( + request.getMsgId(), + MessageCode.SUCCESS.getCode(), + MessageCode.SUCCESS.getDescription(), + statistics + ); + } +} diff --git a/src/main/java/ru/soune/nocopy/repository/ReferralJpaRepository.java b/src/main/java/ru/soune/nocopy/repository/ReferralJpaRepository.java index c90eba9..232bfeb 100644 --- a/src/main/java/ru/soune/nocopy/repository/ReferralJpaRepository.java +++ b/src/main/java/ru/soune/nocopy/repository/ReferralJpaRepository.java @@ -43,4 +43,22 @@ public interface ReferralJpaRepository extends JpaRepository { @Query("SELECT COUNT(r) FROM Referral r WHERE r.inviterId = :userId") int countTotalInvitees(@Param("userId") Long userId); + + @Query("SELECT COALESCE(SUM(r.totalIncome), 0) FROM Referral r") + Long getTotalIncome(); + + @Query("SELECT COALESCE(SUM(r.availableIncome), 0) FROM Referral r") + Long getTotalAvailableIncome(); + + @Query("SELECT COALESCE(SUM(r.holdBalance), 0) FROM Referral r") + Long getTotalHoldBalance(); + + @Query(""" + SELECT + r.userId, + r.totalIncome + FROM Referral r + WHERE r.totalIncome > 0 + """) + List getIncomePerUser(); } diff --git a/src/main/java/ru/soune/nocopy/service/statistic/IncomeStatisticService.java b/src/main/java/ru/soune/nocopy/service/statistic/IncomeStatisticService.java new file mode 100644 index 0000000..d21cd9d --- /dev/null +++ b/src/main/java/ru/soune/nocopy/service/statistic/IncomeStatisticService.java @@ -0,0 +1,55 @@ +package ru.soune.nocopy.service.statistic; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.soune.nocopy.dto.statistic.IncomeStatisticResponse; +import ru.soune.nocopy.repository.ReferralJpaRepository; +import ru.soune.nocopy.util.PercentileCalculator; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Slf4j +public class IncomeStatisticService { + private final ReferralJpaRepository referralRepository; + + private static final int PERCENTILE_5 = 5; + + private static final int PERCENTILE_50 = 50; + + private static final int PERCENTILE_95 = 95; + + public IncomeStatisticResponse getIncomeStatistics() { + Long totalIncome = referralRepository.getTotalIncome(); + Long totalAvailable = referralRepository.getTotalAvailableIncome(); + Long totalHold = referralRepository.getTotalHoldBalance(); + + List incomeData = referralRepository.getIncomePerUser(); + List incomeValues = extractIncomeValues(incomeData); + + PercentileCalculator.PercentilesResult incomePercentiles = + PercentileCalculator.calculatePercentiles(incomeValues, PERCENTILE_5, PERCENTILE_50, + PERCENTILE_95); + + return IncomeStatisticResponse.builder() + .totalIncome(totalIncome != null ? totalIncome : 0L) + .totalAvailable(totalAvailable != null ? totalAvailable : 0L) + .totalHold(totalHold != null ? totalHold : 0L) + .incomePerUser(new IncomeStatisticResponse.Percentiles( + incomePercentiles.getP5() != null ? incomePercentiles.getP5() : 0L, + incomePercentiles.getP50() != null ? incomePercentiles.getP50() : 0L, + incomePercentiles.getP95() != null ? incomePercentiles.getP95() : 0L + )) + .build(); + } + + private List extractIncomeValues(List data) { + return data.stream() + .map(row -> row[1] != null ? ((Number) row[1]).longValue() : 0L) + .filter(value -> value > 0) + .collect(Collectors.toList()); + } +}