This commit is contained in:
@@ -44,7 +44,8 @@ public class HandlerConfig {
|
|||||||
StatisticUserDynamicHandler statisticUserDynamicHandler,
|
StatisticUserDynamicHandler statisticUserDynamicHandler,
|
||||||
StatisticProtectedFilesHandler statisticProtectedFilesHandler,
|
StatisticProtectedFilesHandler statisticProtectedFilesHandler,
|
||||||
StatisticViolationHandler statisticViolationHandler,
|
StatisticViolationHandler statisticViolationHandler,
|
||||||
StatisticSubscriberHandler statisticSubscriberHandler
|
StatisticSubscriberHandler statisticSubscriberHandler,
|
||||||
|
StatisticTokenHandler statisticTokenHandler
|
||||||
|
|
||||||
) {
|
) {
|
||||||
Map<Integer, RequestHandler> map = new HashMap<>();
|
Map<Integer, RequestHandler> map = new HashMap<>();
|
||||||
@@ -81,6 +82,7 @@ public class HandlerConfig {
|
|||||||
map.put(30022, statisticProtectedFilesHandler);
|
map.put(30022, statisticProtectedFilesHandler);
|
||||||
map.put(30023, statisticViolationHandler);
|
map.put(30023, statisticViolationHandler);
|
||||||
map.put(30024, statisticSubscriberHandler);
|
map.put(30024, statisticSubscriberHandler);
|
||||||
|
map.put(30025, statisticTokenHandler);
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package ru.soune.nocopy.dto.statistic;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TokenStatisticRequest {
|
||||||
|
}
|
||||||
@@ -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 TokenStatisticResponse {
|
||||||
|
|
||||||
|
@JsonProperty("total_bought")
|
||||||
|
private Long totalBought;
|
||||||
|
|
||||||
|
@JsonProperty("total_spent")
|
||||||
|
private Long totalSpent;
|
||||||
|
|
||||||
|
@JsonProperty("bought_per_user")
|
||||||
|
private Percentiles boughtPerUser;
|
||||||
|
|
||||||
|
@JsonProperty("spent_per_user")
|
||||||
|
private Percentiles spentPerUser;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public static class Percentiles {
|
||||||
|
@JsonProperty("p5")
|
||||||
|
private Long p5;
|
||||||
|
|
||||||
|
@JsonProperty("p50")
|
||||||
|
private Long p50;
|
||||||
|
|
||||||
|
@JsonProperty("p95")
|
||||||
|
private Long p95;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
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.TokenStatisticResponse;
|
||||||
|
import ru.soune.nocopy.service.statistic.TokenStatisticService;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class StatisticTokenHandler implements RequestHandler {
|
||||||
|
private final TokenStatisticService statisticService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseResponse handle(BaseRequest request) throws Exception {
|
||||||
|
TokenStatisticResponse statistics = statisticService.getTokenStatistics();
|
||||||
|
|
||||||
|
return new BaseResponse(
|
||||||
|
request.getMsgId(),
|
||||||
|
MessageCode.SUCCESS.getCode(),
|
||||||
|
MessageCode.SUCCESS.getDescription(),
|
||||||
|
statistics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package ru.soune.nocopy.repository;
|
package ru.soune.nocopy.repository;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
import ru.soune.nocopy.entity.payment.Payment;
|
import ru.soune.nocopy.entity.payment.Payment;
|
||||||
|
|
||||||
@@ -11,4 +12,23 @@ import java.util.Optional;
|
|||||||
public interface PaymentRepository extends JpaRepository<Payment, String> {
|
public interface PaymentRepository extends JpaRepository<Payment, String> {
|
||||||
Optional<Payment> findByPaymentUuid(String paymentUuid);
|
Optional<Payment> findByPaymentUuid(String paymentUuid);
|
||||||
List<Payment> findByUserId(Long userId);
|
List<Payment> findByUserId(Long userId);
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT COALESCE(SUM(p.tariff.tokens), 0)
|
||||||
|
FROM Payment p
|
||||||
|
WHERE p.operationType = 'TOKEN'
|
||||||
|
AND p.status = 'SUCCEEDED'
|
||||||
|
""")
|
||||||
|
Long getTotalTokensBought();
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT
|
||||||
|
p.user.Id,
|
||||||
|
COALESCE(SUM(p.tariff.tokens), 0)
|
||||||
|
FROM Payment p
|
||||||
|
WHERE p.operationType = 'TOKEN'
|
||||||
|
AND p.status = 'SUCCEEDED'
|
||||||
|
GROUP BY p.user.Id
|
||||||
|
""")
|
||||||
|
List<Object[]> getTokensBoughtPerUser();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,4 +38,19 @@ public interface TokenOperationRepository extends JpaRepository<TokenOperation,
|
|||||||
Long sumSpentByUserAndType(@Param("userId") Long userId, @Param("operationType") OperationType operationType);
|
Long sumSpentByUserAndType(@Param("userId") Long userId, @Param("operationType") OperationType operationType);
|
||||||
|
|
||||||
Long countByUserId(Long userId);
|
Long countByUserId(Long userId);
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT COALESCE(SUM(t.spent), 0)
|
||||||
|
FROM TokenOperation t
|
||||||
|
""")
|
||||||
|
Long getTotalTokensSpent();
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT
|
||||||
|
t.userId,
|
||||||
|
COALESCE(SUM(t.spent), 0)
|
||||||
|
FROM TokenOperation t
|
||||||
|
GROUP BY t.userId
|
||||||
|
""")
|
||||||
|
List<Object[]> getTokensSpentPerUser();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
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.TokenStatisticResponse;
|
||||||
|
import ru.soune.nocopy.repository.PaymentRepository;
|
||||||
|
import ru.soune.nocopy.repository.TokenOperationRepository;
|
||||||
|
import ru.soune.nocopy.util.PercentileCalculator;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class TokenStatisticService {
|
||||||
|
private final PaymentRepository paymentRepository;
|
||||||
|
|
||||||
|
private final TokenOperationRepository tokenOperationRepository;
|
||||||
|
|
||||||
|
private static final int PERCENTILE_5 = 5;
|
||||||
|
|
||||||
|
private static final int PERCENTILE_50 = 50;
|
||||||
|
|
||||||
|
private static final int PERCENTILE_95 = 95;
|
||||||
|
|
||||||
|
public TokenStatisticResponse getTokenStatistics() {
|
||||||
|
Long totalBought = paymentRepository.getTotalTokensBought();
|
||||||
|
Long totalSpent = tokenOperationRepository.getTotalTokensSpent();
|
||||||
|
|
||||||
|
List<Object[]> boughtData = paymentRepository.getTokensBoughtPerUser();
|
||||||
|
List<Long> boughtValues = extractTokenValues(boughtData);
|
||||||
|
|
||||||
|
List<Object[]> spentData = tokenOperationRepository.getTokensSpentPerUser();
|
||||||
|
List<Long> spentValues = extractTokenValues(spentData);
|
||||||
|
|
||||||
|
PercentileCalculator.PercentilesResult boughtPercentiles =
|
||||||
|
PercentileCalculator.calculatePercentiles(boughtValues, PERCENTILE_5, PERCENTILE_50, PERCENTILE_95);
|
||||||
|
PercentileCalculator.PercentilesResult spentPercentiles =
|
||||||
|
PercentileCalculator.calculatePercentiles(spentValues, PERCENTILE_5, PERCENTILE_50, PERCENTILE_95);
|
||||||
|
|
||||||
|
return TokenStatisticResponse.builder()
|
||||||
|
.totalBought(totalBought != null ? totalBought : 0L)
|
||||||
|
.totalSpent(totalSpent != null ? totalSpent : 0L)
|
||||||
|
.boughtPerUser(new TokenStatisticResponse.Percentiles(
|
||||||
|
boughtPercentiles.getP5(),
|
||||||
|
boughtPercentiles.getP50(),
|
||||||
|
boughtPercentiles.getP95()
|
||||||
|
))
|
||||||
|
.spentPerUser(new TokenStatisticResponse.Percentiles(
|
||||||
|
spentPercentiles.getP5(),
|
||||||
|
spentPercentiles.getP50(),
|
||||||
|
spentPercentiles.getP95()
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Long> extractTokenValues(List<Object[]> data) {
|
||||||
|
return data.stream()
|
||||||
|
.map(row -> row[1] != null ? ((Number) row[1]).longValue() : 0L)
|
||||||
|
.filter(value -> value > 0)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user