diff --git a/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java b/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java index dbb0477..343fd03 100644 --- a/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java +++ b/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java @@ -38,7 +38,9 @@ public class HandlerConfig { NotificationHandler notificationHandler, UserVerificationHandler userVerificationHandler, LawCaseHandler lawCaseHandler, - TokenOperationHandler tokenOperationHandler + TokenOperationHandler tokenOperationHandler, + StatisticUserFilesHandler statisticUserHandler, + StatisticTariffInfoFileHandler statisticTariffInfoFileHandler ) { Map map = new HashMap<>(); map.put(20001, login); @@ -68,6 +70,8 @@ public class HandlerConfig { map.put(30016, userVerificationHandler); map.put(30017, lawCaseHandler); map.put(30018, tokenOperationHandler); + map.put(30019, statisticUserHandler); + map.put(30020, statisticTariffInfoFileHandler); return map; } diff --git a/src/main/java/ru/soune/nocopy/dto/statistic/TariffStatisticFullResponse.java b/src/main/java/ru/soune/nocopy/dto/statistic/TariffStatisticFullResponse.java new file mode 100644 index 0000000..7ee685a --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/statistic/TariffStatisticFullResponse.java @@ -0,0 +1,20 @@ +package ru.soune.nocopy.dto.statistic; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TariffStatisticFullResponse { + + @JsonProperty("tariffs") + private List tariffs; + + @JsonProperty("total_users") + private Long totalUsers; +} diff --git a/src/main/java/ru/soune/nocopy/dto/statistic/TariffStatisticRequest.java b/src/main/java/ru/soune/nocopy/dto/statistic/TariffStatisticRequest.java new file mode 100644 index 0000000..3b2d036 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/statistic/TariffStatisticRequest.java @@ -0,0 +1,14 @@ +package ru.soune.nocopy.dto.statistic; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class TariffStatisticRequest { + + @JsonProperty("company_only") + private Boolean companyOnly = false; + + @JsonProperty("active_only") + private Boolean activeOnly = true; +} diff --git a/src/main/java/ru/soune/nocopy/dto/statistic/TariffStatisticResponse.java b/src/main/java/ru/soune/nocopy/dto/statistic/TariffStatisticResponse.java new file mode 100644 index 0000000..7b7ebe8 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/statistic/TariffStatisticResponse.java @@ -0,0 +1,21 @@ +package ru.soune.nocopy.dto.statistic; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TariffStatisticResponse { + + @JsonProperty("tariff_name") + private String tariffName; + + @JsonProperty("user_count") + private Long userCount; + + @JsonProperty("usage_percent") + private Double usagePercent; +} diff --git a/src/main/java/ru/soune/nocopy/dto/statistic/UserFilesStatisticRequest.java b/src/main/java/ru/soune/nocopy/dto/statistic/UserFilesStatisticRequest.java new file mode 100644 index 0000000..ad2bc38 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/statistic/UserFilesStatisticRequest.java @@ -0,0 +1,13 @@ +package ru.soune.nocopy.dto.statistic; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class UserFilesStatisticRequest { + @JsonProperty("top") + int topUsers; + + @JsonProperty("type") + String fileMimeType; +} diff --git a/src/main/java/ru/soune/nocopy/dto/statistic/UserFilesStatisticResponse.java b/src/main/java/ru/soune/nocopy/dto/statistic/UserFilesStatisticResponse.java new file mode 100644 index 0000000..82c2ec5 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/statistic/UserFilesStatisticResponse.java @@ -0,0 +1,19 @@ +package ru.soune.nocopy.dto.statistic; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class UserFilesStatisticResponse { + @JsonProperty("full_name") + private final String userName; + + @JsonProperty("user_id") + private final long userId; + + @JsonProperty("count") + private final int countFiles; + + @JsonProperty("files_size") + private final long fileSize; +} diff --git a/src/main/java/ru/soune/nocopy/handler/StatisticTariffInfoFileHandler.java b/src/main/java/ru/soune/nocopy/handler/StatisticTariffInfoFileHandler.java new file mode 100644 index 0000000..9e025cb --- /dev/null +++ b/src/main/java/ru/soune/nocopy/handler/StatisticTariffInfoFileHandler.java @@ -0,0 +1,51 @@ +package ru.soune.nocopy.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.AllArgsConstructor; +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.TariffStatisticFullResponse; +import ru.soune.nocopy.dto.statistic.TariffStatisticRequest; +import ru.soune.nocopy.dto.statistic.TariffStatisticResponse; +import ru.soune.nocopy.repository.TariffInfoRepository; + +import java.util.List; + +@Component +@AllArgsConstructor +public class StatisticTariffInfoFileHandler implements RequestHandler { + + private ObjectMapper objectMapper; + + private TariffInfoRepository tariffInfoRepository; + + @Override + public BaseResponse handle(BaseRequest request) throws Exception { + TariffStatisticRequest tariffRequest = + objectMapper.convertValue(request.getMessageBody(), TariffStatisticRequest.class); + + boolean activeOnly = tariffRequest.getActiveOnly() != null ? tariffRequest.getActiveOnly() : true; + List results = tariffInfoRepository.getTariffStatisticsNative(activeOnly); + + List tariffs = results.stream() + .map(row -> new TariffStatisticResponse( + (String) row[0], + ((Number) row[1]).longValue(), + ((Number) row[2]).doubleValue() + )) + .toList(); + + Long totalUsers = tariffInfoRepository.getTotalUsersCount(activeOnly); + + TariffStatisticFullResponse statistics = new TariffStatisticFullResponse( + tariffs, totalUsers != null ? totalUsers : 0L); + + return new BaseResponse( + request.getMsgId(), + MessageCode.SUCCESS.getCode(), + MessageCode.SUCCESS.getDescription(), + statistics); + } +} diff --git a/src/main/java/ru/soune/nocopy/handler/StatisticUserFilesHandler.java b/src/main/java/ru/soune/nocopy/handler/StatisticUserFilesHandler.java new file mode 100644 index 0000000..6eca048 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/handler/StatisticUserFilesHandler.java @@ -0,0 +1,38 @@ +package ru.soune.nocopy.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +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.UserFilesStatisticRequest; +import ru.soune.nocopy.dto.statistic.UserFilesStatisticResponse; +import ru.soune.nocopy.repository.FileEntityRepository; + +import java.util.List; + +@Component +@RequiredArgsConstructor +@Slf4j +public class StatisticUserFilesHandler implements RequestHandler { + + private final ObjectMapper objectMapper; + + private final FileEntityRepository fileRepository; + + @Override + public BaseResponse handle(BaseRequest request) throws Exception { + UserFilesStatisticRequest userFilesStatisticRequest = + objectMapper.convertValue(request.getMessageBody(), UserFilesStatisticRequest.class); + + int topUsers = userFilesStatisticRequest.getTopUsers(); + String fileMimeType = userFilesStatisticRequest.getFileMimeType(); + + List files = fileRepository.findTopUsers(topUsers, fileMimeType); + + return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), + MessageCode.SUCCESS.getDescription(), files); + } +} diff --git a/src/main/java/ru/soune/nocopy/repository/FileEntityRepository.java b/src/main/java/ru/soune/nocopy/repository/FileEntityRepository.java index b4efb96..d0cdce6 100644 --- a/src/main/java/ru/soune/nocopy/repository/FileEntityRepository.java +++ b/src/main/java/ru/soune/nocopy/repository/FileEntityRepository.java @@ -6,6 +6,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import ru.soune.nocopy.dto.statistic.UserFilesStatisticResponse; import ru.soune.nocopy.entity.file.FileEntity; import ru.soune.nocopy.entity.file.FileStatus; import ru.soune.nocopy.entity.file.ProtectionStatus; @@ -95,4 +96,24 @@ public interface FileEntityRepository extends JpaRepository Long sumFileSize( @Param("userIds") List userIds, @Param("statuses") List statuses); + + @Query(""" + SELECT new ru.soune.nocopy.dto.statistic.UserFilesStatisticResponse( + u.fullName, + u.Id, + CAST(COUNT(f.id) AS int), + COALESCE(SUM(f.fileSize), 0L) + ) + FROM FileEntity f + JOIN User u ON f.userId = u.Id + WHERE + (:mimeType IS NULL OR :mimeType = '' OR f.mimeType LIKE CONCAT(:mimeType, '%')) + GROUP BY u.Id, u.fullName + ORDER BY COUNT(f.id) DESC + LIMIT :topUsers + """) + List findTopUsers( + @Param("topUsers") int topUsers, + @Param("mimeType") String mimeType + ); } diff --git a/src/main/java/ru/soune/nocopy/repository/TariffInfoRepository.java b/src/main/java/ru/soune/nocopy/repository/TariffInfoRepository.java index 6982080..ccc0061 100644 --- a/src/main/java/ru/soune/nocopy/repository/TariffInfoRepository.java +++ b/src/main/java/ru/soune/nocopy/repository/TariffInfoRepository.java @@ -13,4 +13,40 @@ import java.util.List; public interface TariffInfoRepository extends JpaRepository { @Query("SELECT t FROM TariffInfo t WHERE t.autoRenewal = true AND t.endTariff <= :date") List findForAutoRenewal(@Param("date") LocalDateTime date); + + @Query(""" + SELECT COUNT(u.Id) + FROM User u + WHERE (:activeOnly = false OR u.isActive = true) + AND (:companyOnly = false OR u.company IS NOT NULL) + """) + Long getTotalUsersCount( + @Param("activeOnly") boolean activeOnly, + @Param("companyOnly") boolean companyOnly + ); + + @Query(""" + SELECT COUNT(u.Id) + FROM User u + WHERE (:activeOnly = false OR u.isActive = true) + """) + Long getTotalUsersCount(@Param("activeOnly") boolean activeOnly); + + @Query(value = """ + SELECT + COALESCE(t.tariff_name, 'NULL') as tariff_name, + COUNT(u.id) as user_count, + (COUNT(u.id) * 100.0) / NULLIF( + (SELECT COUNT(*) FROM users u2 + WHERE :activeOnly = false OR u2.is_active = true + ), 0 + ) as usage_percent + FROM users u + LEFT JOIN tariff_info pti ON u.personal_tariff_info_id = pti.id + LEFT JOIN tariff t ON pti.tariff_id = t.id + WHERE :activeOnly = false OR u.is_active = true + GROUP BY t.id, t.tariff_name + ORDER BY user_count DESC + """, nativeQuery = true) + List getTariffStatisticsNative(@Param("activeOnly") boolean activeOnly); }