dev add statistic files protected
Test Workflow / test (push) Successful in 2s

This commit is contained in:
vladp
2026-04-16 00:54:44 +07:00
parent db3dcb8166
commit bbf8e3ee3d
7 changed files with 179 additions and 1 deletions
@@ -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());
}
}