dev add stats for search check
Test Workflow / test (push) Successful in 3s

This commit is contained in:
vladp
2026-02-05 18:55:54 +07:00
parent adc978bea8
commit f90b8a0bce
15 changed files with 261 additions and 53 deletions
@@ -11,6 +11,7 @@ import ru.soune.nocopy.entity.user.ProtectedFileCheck;
import ru.soune.nocopy.entity.user.User;
import ru.soune.nocopy.exception.RetryableException;
import ru.soune.nocopy.repository.ProtectedFileCheckRepository;
import ru.soune.nocopy.repository.UserRepository;
@Service
@RequiredArgsConstructor
@@ -19,22 +20,25 @@ public class CheckCounterService {
private final ProtectedFileCheckRepository protectedFileCheckRepository;
@Transactional
public void incrementCheckCount(Long userId) {
try {
ProtectedFileCheck check = protectedFileCheckRepository.findByUser_Id(userId)
.orElseThrow(NullPointerException::new);
private final UserRepository userRepository;
boolean success = check.incrementCheck();
@Transactional
public void incrementCheckCount(Long userId, String mimeType) {
try {
ProtectedFileCheck check = protectedFileCheckRepository.findByUser_Id(userId).orElse(null);
User user = userRepository.findById(userId).orElse(null);
if (check == null) {
check = initProtectCheckLimit(user,0);
}
boolean success = check.writeCheck(mimeType);
if (success) {
protectedFileCheckRepository.save(check);
log.info("Check count incremented for user {}: count={}, limit={}",
userId, check.getCountChecked(), check.getLimitCheck());
CheckIncrementResult.success(check.getCountChecked(), check.getLimitCheck());
} else {
log.warn("Check limit exceeded for user {}", userId);
CheckIncrementResult.limitExceeded(check.getLimitCheck());
log.info("Check count incremented for user {}: count={}",
userId, check.getCountChecked());
CheckIncrementResult.success(check.getCountChecked());
}
} catch (ObjectOptimisticLockingFailureException e) {
@@ -51,27 +55,23 @@ public class CheckCounterService {
return CheckStatus.builder()
.userId(userId)
.countChecked(check.getCountChecked())
.remainingLimit(check.getLimitCheck())
.lastCheckAt(check.getLastCheckAt())
.build();
}
public void initProtectCheckLimit(User user, Integer checked, Integer limit) {
public ProtectedFileCheck initProtectCheckLimit(User user, Integer checked) {
ProtectedFileCheck check = ProtectedFileCheck.builder()
.user(user)
.limitCheck(limit)
.countChecked(checked)
.build();
protectedFileCheckRepository.save(check);
return protectedFileCheckRepository.save(check);
}
@Transactional
public ProtectedFileCheck updateLimit(User user, Integer limit) {
ProtectedFileCheck check = ProtectedFileCheck.builder()
.user(user)
.limitCheck(limit)
.build();
return protectedFileCheckRepository.save(check);
@@ -5,13 +5,17 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ru.soune.nocopy.entity.user.ProtectedFileCheck;
import ru.soune.nocopy.repository.ProtectedFileCheckRepository;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
@Slf4j
@Component
@@ -24,10 +28,14 @@ public class FileCleanupService {
@Value("${file.storage.temp-ttl-hours}")
private int tempTtlHours;
private final ProtectedFileCheckRepository protectedFileCheckRepository;
@Scheduled(cron = "0 0 3 * * *")
public void cleanupExpiredFiles() {
log.info("Starting cleanup of expired temporary files");
cleanupOldPeriods();
Path tempDir = Paths.get(basePath, "temp");
if (!Files.exists(tempDir)) {
return;
@@ -77,4 +85,17 @@ public class FileCleanupService {
log.error("Error during cleanup", e);
}
}
private void cleanupOldPeriods() {
LocalDate thirtyDaysAgo = LocalDate.now().minusDays(30);
List<ProtectedFileCheck> checks = protectedFileCheckRepository.findByPeriodStartDateBefore(thirtyDaysAgo);
for (ProtectedFileCheck check : checks) {
check.cleanupOldData();
}
protectedFileCheckRepository.saveAll(checks);
log.info("Cleaned up {} old protection usage records", checks.size());
}
}
@@ -0,0 +1,46 @@
package ru.soune.nocopy.service.file;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.soune.nocopy.dto.file.FileTypeStatsDto;
import ru.soune.nocopy.entity.user.ProtectedFileCheck;
import ru.soune.nocopy.repository.ProtectedFileCheckRepository;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@Slf4j
public class ProtectionsLimitService {
private final ProtectedFileCheckRepository protectedFileCheckRepository;
@Transactional(readOnly = true)
public Map<String, Object> getFileTypeStats(Long userId) {
ProtectedFileCheck protection = protectedFileCheckRepository
.findByUserId(userId);
Map<String, Integer> checksByType = protection.getUsageByType();
int totalChecks = protection.getPeriodCount();
if (totalChecks == 0) {
return Map.of();
}
Map<String, Object> collect = checksByType.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> FileTypeStatsDto.builder()
.fileType(entry.getKey())
.count(entry.getValue())
.build()
));
collect.put("allCountForPeriod", totalChecks);
return collect;
}
}
@@ -331,8 +331,11 @@ public class FileUploadServiceImpl implements FileUploadService {
completeFileProcessingAsync(session, status);
if (status != FileStatus.TEMP) {
tariffInfoService.writeOffTokens(session.getUserId(), TariffConstants.TOKEN_VALUE);
String fileType = session.getFileType();
tariffInfoService.writeOffTokens(session.getUserId(), protectCost(fileType));
}
} else {
sessionRepository.save(session);
@@ -369,6 +372,23 @@ public class FileUploadServiceImpl implements FileUploadService {
}
}
private int protectCost(String fileType) {
int cost = 0;
switch (fileType) {
case "video":
cost = TariffConstants.VIDEO_TOKEN_VALUE;
case "image":
cost = TariffConstants.IMAGE_TOKEN_VALUE;
case "audio":
cost = TariffConstants.AUDIO_TOKEN_VALUE;
case "pdf":
cost = TariffConstants.PDF_TOKEN_VALUE;
}
return cost;
}
private String assembleFileSynchronously(FileUploadSession session) throws IOException {
Path finalFilePath = prepareFinalFile(session);
log.info("file path: " + finalFilePath);
@@ -76,7 +76,7 @@ public class AuthService {
AuthToken authToken = genereateAuthToken(savedUser);
checkCounterService.initProtectCheckLimit(user, 0, 10);
checkCounterService.initProtectCheckLimit(user, 0);
return authTokenRepository.save(authToken);
}
@@ -13,6 +13,9 @@ public class TariffConstants {
public static final Integer PRO_TOKENS = 300;
public static final Integer ENTERPRISE_TOKENS = 400;
public static final Integer TOKEN_VALUE = 3;
public static final Integer IMAGE_TOKEN_VALUE = 3;
public static final Integer VIDEO_TOKEN_VALUE = 16;
public static final Integer AUDIO_TOKEN_VALUE = 8;
public static final Integer PDF_TOKEN_VALUE = 6;
public static final Integer TOKEN_VALUE_FOR_SEARCH = 5;
}
@@ -54,7 +54,7 @@ public class TariffInfoService {
Integer tokens = activeTariffInfo.getTokens();
if (tokens - value > 0) {
activeTariffInfo.setTokens(activeTariffInfo.getTokens() - TariffConstants.TOKEN_VALUE);
activeTariffInfo.setTokens(activeTariffInfo.getTokens() - value);
tariffInfoRepository.save(activeTariffInfo);
} else {