This commit is contained in:
@@ -43,7 +43,8 @@ public class HandlerConfig {
|
||||
StatisticTariffInfoFileHandler statisticTariffInfoFileHandler,
|
||||
StatisticUserDynamicHandler statisticUserDynamicHandler,
|
||||
StatisticProtectedFilesHandler statisticProtectedFilesHandler,
|
||||
StatisticViolationHandler statisticViolationHandler
|
||||
StatisticViolationHandler statisticViolationHandler,
|
||||
StatisticSubscriberHandler statisticSubscriberHandler
|
||||
|
||||
) {
|
||||
Map<Integer, RequestHandler> map = new HashMap<>();
|
||||
@@ -79,6 +80,7 @@ public class HandlerConfig {
|
||||
map.put(30021, statisticUserDynamicHandler);
|
||||
map.put(30022, statisticProtectedFilesHandler);
|
||||
map.put(30023, statisticViolationHandler);
|
||||
map.put(30024, statisticSubscriberHandler);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.soune.nocopy.dto.statistic;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SubscriberStatisticRequest {
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
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 SubscriberStatisticResponse {
|
||||
|
||||
@JsonProperty("monthly")
|
||||
private PeriodStatistics monthly;
|
||||
|
||||
@JsonProperty("yearly")
|
||||
private PeriodStatistics yearly;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class PeriodStatistics {
|
||||
|
||||
@JsonProperty("current_active_subscriptions")
|
||||
private Long currentActiveSubscriptions;
|
||||
|
||||
@JsonProperty("first_time_subscriptions")
|
||||
private Long firstTimeSubscriptions;
|
||||
|
||||
@JsonProperty("renewals")
|
||||
private Long renewals;
|
||||
|
||||
@JsonProperty("upgrades")
|
||||
private Long upgrades;
|
||||
|
||||
@JsonProperty("downgrades")
|
||||
private Long downgrades;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public class TariffInfo {
|
||||
private String id;
|
||||
|
||||
@Column(name = "status", nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TariffStatus status;
|
||||
|
||||
@Column(name = "start_tariff")
|
||||
|
||||
@@ -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.SubscriberStatisticResponse;
|
||||
import ru.soune.nocopy.service.statistic.SubscriberStatisticService;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class StatisticSubscriberHandler implements RequestHandler {
|
||||
private final SubscriberStatisticService statisticService;
|
||||
|
||||
@Override
|
||||
public BaseResponse handle(BaseRequest request) throws Exception {
|
||||
SubscriberStatisticResponse statistics = statisticService.getSubscriberStatistics();
|
||||
|
||||
return new BaseResponse(
|
||||
request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(),
|
||||
statistics
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -50,4 +50,20 @@ public interface TariffInfoRepository extends JpaRepository<TariffInfo, String>
|
||||
ORDER BY user_count DESC
|
||||
""", nativeQuery = true)
|
||||
List<Object[]> getTariffStatisticsNative(@Param("activeOnly") boolean activeOnly);
|
||||
|
||||
@Query("""
|
||||
SELECT COUNT(ti.id)
|
||||
FROM TariffInfo ti
|
||||
WHERE ti.endTariff >= CURRENT_TIMESTAMP
|
||||
AND (:term IS NULL OR ti.tariff.tariffTerm = :term)
|
||||
""")
|
||||
Long getCurrentActiveSubscriptions(@Param("term") String term);
|
||||
|
||||
@Query("""
|
||||
SELECT ti
|
||||
FROM TariffInfo ti
|
||||
WHERE (:term IS NULL OR ti.tariff.tariffTerm = :term)
|
||||
ORDER BY ti.startTariff
|
||||
""")
|
||||
List<TariffInfo> getAllTariffInfosForAnalysis(@Param("term") String term);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ package ru.soune.nocopy.repository;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import ru.soune.nocopy.entity.tarif.TariffInfo;
|
||||
import ru.soune.nocopy.entity.user.User;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
User findByEmail(String email);
|
||||
@@ -40,4 +42,7 @@ public interface UserRepository extends JpaRepository<User, Long> {
|
||||
AND created_at < DATE_TRUNC('month', NOW())
|
||||
""", nativeQuery = true)
|
||||
Long getNewUsersPreviousMonth();
|
||||
|
||||
@Query("SELECT u FROM User u WHERE u.personalTariffInfo.id = :tariffInfoId")
|
||||
Optional<User> findByPersonalTariffInfoId(@Param("tariffInfoId") String tariffInfoId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
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.SubscriberStatisticResponse;
|
||||
import ru.soune.nocopy.entity.tarif.TariffInfo;
|
||||
import ru.soune.nocopy.entity.user.User;
|
||||
import ru.soune.nocopy.repository.TariffInfoRepository;
|
||||
import ru.soune.nocopy.repository.UserRepository;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class SubscriberStatisticService {
|
||||
private final TariffInfoRepository tariffInfoRepository;
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
private static final String TERM_MONTHLY = "MONTHLY";
|
||||
|
||||
private static final String TERM_YEARLY = "YEAR";
|
||||
|
||||
public SubscriberStatisticResponse getSubscriberStatistics() {
|
||||
return SubscriberStatisticResponse.builder()
|
||||
.monthly(buildPeriodStatistics(TERM_MONTHLY))
|
||||
.yearly(buildPeriodStatistics(TERM_YEARLY))
|
||||
.build();
|
||||
}
|
||||
|
||||
private SubscriberStatisticResponse.PeriodStatistics buildPeriodStatistics(String term) {
|
||||
Long currentActive = tariffInfoRepository.getCurrentActiveSubscriptions(term);
|
||||
|
||||
List<TariffInfo> allTariffs = tariffInfoRepository.getAllTariffInfosForAnalysis(term);
|
||||
|
||||
Map<Long, List<TariffInfo>> tariffsByUser = groupTariffsByUser(allTariffs);
|
||||
|
||||
Long firstTime = countFirstTimeSubscriptions(tariffsByUser);
|
||||
Long renewals = countRenewals(tariffsByUser);
|
||||
UpDownGradeResult gradeResult = analyzeUpgradesAndDowngrades(tariffsByUser);
|
||||
|
||||
return SubscriberStatisticResponse.PeriodStatistics.builder()
|
||||
.currentActiveSubscriptions(currentActive != null ? currentActive : 0L)
|
||||
.firstTimeSubscriptions(firstTime)
|
||||
.renewals(renewals)
|
||||
.upgrades(gradeResult.upgrades)
|
||||
.downgrades(gradeResult.downgrades)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Map<Long, List<TariffInfo>> groupTariffsByUser(List<TariffInfo> tariffs) {
|
||||
Map<Long, List<TariffInfo>> result = new HashMap<>();
|
||||
|
||||
for (TariffInfo ti : tariffs) {
|
||||
Optional<User> userOpt = userRepository.findByPersonalTariffInfoId(ti.getId());
|
||||
if (userOpt.isPresent()) {
|
||||
Long userId = userOpt.get().getId();
|
||||
result.computeIfAbsent(userId, k -> new ArrayList<>()).add(ti);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Long countFirstTimeSubscriptions(Map<Long, List<TariffInfo>> tariffsByUser) {
|
||||
return (long) tariffsByUser.size();
|
||||
}
|
||||
|
||||
private Long countRenewals(Map<Long, List<TariffInfo>> tariffsByUser) {
|
||||
return tariffsByUser.values().stream()
|
||||
.filter(list -> list.size() > 1)
|
||||
.mapToLong(list -> list.size() - 1)
|
||||
.sum();
|
||||
}
|
||||
|
||||
private UpDownGradeResult analyzeUpgradesAndDowngrades(Map<Long, List<TariffInfo>> tariffsByUser) {
|
||||
long upgrades = 0L;
|
||||
long downgrades = 0L;
|
||||
|
||||
for (List<TariffInfo> userTariffs : tariffsByUser.values()) {
|
||||
if (userTariffs.size() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
userTariffs.sort(Comparator.comparing(TariffInfo::getStartTariff,
|
||||
Comparator.nullsLast(Comparator.naturalOrder())));
|
||||
|
||||
for (int i = 1; i < userTariffs.size(); i++) {
|
||||
TariffInfo previous = userTariffs.get(i - 1);
|
||||
TariffInfo current = userTariffs.get(i);
|
||||
|
||||
if (previous.getTariff() == null || current.getTariff() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double previousPrice = previous.getTariff().getPrice();
|
||||
double currentPrice = current.getTariff().getPrice();
|
||||
|
||||
if (currentPrice > previousPrice) {
|
||||
upgrades++;
|
||||
} else if (currentPrice < previousPrice) {
|
||||
downgrades++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new UpDownGradeResult(upgrades, downgrades);
|
||||
}
|
||||
|
||||
private static class UpDownGradeResult {
|
||||
final Long upgrades;
|
||||
final Long downgrades;
|
||||
|
||||
UpDownGradeResult(Long upgrades, Long downgrades) {
|
||||
this.upgrades = upgrades;
|
||||
this.downgrades = downgrades;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user