From 3ed38dacd18090604f6f6352ba584996531368c9 Mon Sep 17 00:00:00 2001 From: vladp Date: Thu, 5 Mar 2026 14:27:14 +0700 Subject: [PATCH] dev add check tokens for monitoring --- .../handler/ImageFoundRequestHandler.java | 8 +- .../nocopy/scheduler/MonitoringScheduler.java | 1 + .../nocopy/service/mail/EmailService.java | 41 ++++- .../monitoring/MonitoringSearchService.java | 35 +++- .../notification/NotificationService.java | 12 ++ .../service/tariff/TariffInfoService.java | 6 +- src/main/resources/application.yaml | 2 +- .../resources/templates/token-not-found.html | 171 ++++++++++++++++++ 8 files changed, 262 insertions(+), 14 deletions(-) create mode 100644 src/main/java/ru/soune/nocopy/service/notification/NotificationService.java create mode 100644 src/main/resources/templates/token-not-found.html diff --git a/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java index bf0194b..81182da 100644 --- a/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java @@ -90,10 +90,10 @@ public class ImageFoundRequestHandler implements RequestHandler { allUniqueImages = searchImageService.removeDuplicateUrls(allYandexImages, allGoogleImages); log.info("allUniqueImages OK: {} images", allUniqueImages.size()); - if (!allUniqueImages.isEmpty()) { - tariffInfoService.writeOffTokens(fileEntity.getUserId(), TariffConstants.TOKEN_VALUE_FOR_SEARCH); - checkCounterService.incrementCheckCount(fileEntity.getUserId(), fileEntity.getMimeType()); - } +// if (!allUniqueImages.isEmpty()) { + tariffInfoService.writeOffTokens(fileEntity.getUserId(), TariffConstants.TOKEN_VALUE_FOR_SEARCH); + checkCounterService.incrementCheckCount(fileEntity.getUserId(), fileEntity.getMimeType()); +// } int page = imageSearchRequest.getPage() != null ? imageSearchRequest.getPage() : 1; int pageSize = 5; diff --git a/src/main/java/ru/soune/nocopy/scheduler/MonitoringScheduler.java b/src/main/java/ru/soune/nocopy/scheduler/MonitoringScheduler.java index 442bcbe..4a92b57 100644 --- a/src/main/java/ru/soune/nocopy/scheduler/MonitoringScheduler.java +++ b/src/main/java/ru/soune/nocopy/scheduler/MonitoringScheduler.java @@ -9,6 +9,7 @@ import org.springframework.transaction.annotation.Transactional; import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity; import ru.soune.nocopy.repository.FileMonitoringRepository; import ru.soune.nocopy.service.monitoring.MonitoringSearchService; +import ru.soune.nocopy.service.tariff.TariffInfoService; import java.time.LocalDateTime; import java.util.List; diff --git a/src/main/java/ru/soune/nocopy/service/mail/EmailService.java b/src/main/java/ru/soune/nocopy/service/mail/EmailService.java index eee6e16..c34212d 100644 --- a/src/main/java/ru/soune/nocopy/service/mail/EmailService.java +++ b/src/main/java/ru/soune/nocopy/service/mail/EmailService.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.time.LocalDateTime; +import java.util.Map; @Service @RequiredArgsConstructor @@ -47,20 +48,34 @@ public class EmailService { @Value("${server.front.port}") private String frontPort; - public void sendVerificationEmail(User user, String code) throws MessagingException, IOException { + public void sendEmail(String from, String sendTo, String subject, String body) throws MessagingException { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); - helper.setFrom("noreply@no-copy.ru"); - helper.setTo(user.getEmail()); - helper.setSubject(standartSubject); - String htmlContent = loadAndProcessTemplate(user.getFullName(), code, - "templates/email-verification.html"); - helper.setText(htmlContent, true); + helper.setFrom(from); + helper.setTo(sendTo); + helper.setSubject(subject); + helper.setText(body, true); mailSender.send(message); } + public void sendVerificationEmail(User user, String code) throws MessagingException, IOException { + String htmlContent = loadAndProcessTemplate(Map.of("username", user.getFullName(), "code", code), + "templates/email-verification.html"); + sendEmail("noreply@no-copy.ru", user.getEmail(), standartAdressFrom, htmlContent); + } + + public void sendTokensNotFoundEmail(User user, Integer currentTokens, Integer missingTokens) + throws MessagingException, IOException { + String htmlContent = loadAndProcessTemplate(Map.of("username", user.getFullName(), + "current_tokens", String.valueOf(currentTokens), "missing_tokens", + String.valueOf(missingTokens), "link_to_token_board", baseUrl + "/pages/payment"), + "templates/token-not-found.html"); + + sendEmail("noreply@no-copy.ru", user.getEmail(), standartAdressFrom, htmlContent); + } + @Transactional public EmailVerificationToken createEmailVerificationToken(AuthToken authToken) { EmailVerificationToken existingToken = emailVerificationTokenRepository @@ -96,6 +111,18 @@ public class EmailService { .replace("{{code}}", safeCode); } + private String loadAndProcessTemplate(Map fieldsForTemplate, String templatePath) throws IOException { + ClassPathResource resource = new ClassPathResource(templatePath); + String template = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + + for (Map.Entry entry : fieldsForTemplate.entrySet()) { + template = template.replace("{{" + entry.getKey() + "}}", escapeHtml(entry.getValue())); + } + + return template; + } + + private String escapeHtml(String text) { if (text == null) return ""; return text diff --git a/src/main/java/ru/soune/nocopy/service/monitoring/MonitoringSearchService.java b/src/main/java/ru/soune/nocopy/service/monitoring/MonitoringSearchService.java index 8796bce..9fa44d8 100644 --- a/src/main/java/ru/soune/nocopy/service/monitoring/MonitoringSearchService.java +++ b/src/main/java/ru/soune/nocopy/service/monitoring/MonitoringSearchService.java @@ -1,19 +1,30 @@ package ru.soune.nocopy.service.monitoring; +import jakarta.mail.MessagingException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.soune.nocopy.dto.file.YandexSearchResponse; +import ru.soune.nocopy.dto.tarriff.TariffDTO; import ru.soune.nocopy.entity.file.*; import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity; import ru.soune.nocopy.entity.monitoring.FoundViolationEntity; import ru.soune.nocopy.entity.monitoring.MonitoringType; import ru.soune.nocopy.entity.monitoring.SearchEngine; +import ru.soune.nocopy.entity.tarif.TariffInfo; +import ru.soune.nocopy.entity.tarif.TariffType; +import ru.soune.nocopy.entity.user.User; +import ru.soune.nocopy.exception.TariffNotFoundException; import ru.soune.nocopy.repository.FileMonitoringRepository; import ru.soune.nocopy.repository.FoundViolationRepository; +import ru.soune.nocopy.repository.UserRepository; +import ru.soune.nocopy.service.mail.EmailService; import ru.soune.nocopy.service.search.SearchImageService; +import ru.soune.nocopy.service.tariff.TariffInfoService; +import ru.soune.nocopy.service.tariff.TariffService; + import java.io.IOException; import java.time.LocalDateTime; import java.util.List; @@ -30,8 +41,16 @@ public class MonitoringSearchService { private final FileMonitoringRepository monitoringRepository; + private final TariffInfoService tariffInfoService; + + private final TariffService tariffService; + + private final EmailService emailService; + + private final UserRepository userRepository; + @Transactional - public void processFileSearch(FileMonitoringEntity monitoring) { + public void processFileSearch(FileMonitoringEntity monitoring) throws MessagingException, IOException { String searchSessionId = UUID.randomUUID().toString(); log.info("Starting search session: {} for file: {}", searchSessionId, monitoring.getFile().getId()); @@ -39,7 +58,12 @@ public class MonitoringSearchService { monitoring.setLastRunStatus("IN_PROGRESS"); monitoringRepository.save(monitoring); + MonitoringType monitoringType = monitoring.getMonitoringType(); + TariffDTO tariffMonitoring = tariffService.getTariffByType(TariffType.valueOf(monitoringType.name())); + try { + tariffInfoService.writeOffTokens(monitoring.getUserId(), tariffMonitoring.getTokens()); + String yandexResponse = searchImageService.searchReverseByPublicUrl( monitoring.getFile(), "yandex_reverse_image", "visual_matches"); @@ -62,7 +86,16 @@ public class MonitoringSearchService { } monitoring.setLastRunStatus("SUCCESS - Found " + savedCount + " violations"); + } catch (TariffNotFoundException e) { + User user = userRepository.findById(monitoring.getUserId()).orElseThrow(); + TariffInfo activeTariffInfo = user.getActiveTariffInfo(); + int currentTokens = activeTariffInfo.getTokens() + activeTariffInfo.getBoughtTokens(); + emailService.sendTokensNotFoundEmail(user, currentTokens, tariffMonitoring.getTokens()); + monitoring.setLastRunStatus("ERROR: " + e.getMessage()); + updateNextRun(monitoring); + + monitoringRepository.save(monitoring); } catch (Exception e) { log.error("Search failed for session: {}", searchSessionId, e); monitoring.setLastRunStatus("ERROR: " + e.getMessage()); diff --git a/src/main/java/ru/soune/nocopy/service/notification/NotificationService.java b/src/main/java/ru/soune/nocopy/service/notification/NotificationService.java new file mode 100644 index 0000000..5bb807a --- /dev/null +++ b/src/main/java/ru/soune/nocopy/service/notification/NotificationService.java @@ -0,0 +1,12 @@ +package ru.soune.nocopy.service.notification; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class NotificationService { + +} diff --git a/src/main/java/ru/soune/nocopy/service/tariff/TariffInfoService.java b/src/main/java/ru/soune/nocopy/service/tariff/TariffInfoService.java index ac28249..8e2054c 100644 --- a/src/main/java/ru/soune/nocopy/service/tariff/TariffInfoService.java +++ b/src/main/java/ru/soune/nocopy/service/tariff/TariffInfoService.java @@ -39,7 +39,7 @@ public class TariffInfoService { return tariffInfoRepository.save(tariffInfo); } - + @Transactional public void writeOffTokens(long userId, Integer value) { User user = userRepository.findById(userId).orElseThrow(); @@ -52,6 +52,10 @@ public class TariffInfoService { activeTariffInfo = user.getCompany().getTariffInfo(); } + writeOffTokensForTariff(activeTariffInfo, value); + } + + public void writeOffTokensForTariff(TariffInfo activeTariffInfo, Integer value) { Integer tokens = activeTariffInfo.getTokens(); Integer boughtTokens = activeTariffInfo.getBoughtTokens(); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 9d480a2..9b1d602 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -104,7 +104,7 @@ yandex: search-url: ${YANDEX_SEARCH_URL:https://searchapi.api.cloud.yandex.net/v2/image/search_by_image} searchapi: - api-key: ${SEARCHAPI_API_KEY:qQ19qn3JeRtqm9aasEgUFvGc} + api-key: ${SEARCHAPI_API_KEY:5jyYZC8jSaxhZTwjMUhwtAXi} reverse-image-url: "https://searchapi.io/api/v1/search" app: diff --git a/src/main/resources/templates/token-not-found.html b/src/main/resources/templates/token-not-found.html new file mode 100644 index 0000000..8cb9674 --- /dev/null +++ b/src/main/resources/templates/token-not-found.html @@ -0,0 +1,171 @@ + + + + + + Недостаточно токенов + + + +
+
+ +

Недостаточно токенов

+
+ +
+

+ Здравствуйте, {{username}}! +

+ +

При попытке провести мониторинг файлов, токенов для автоматического мониторинга не достаточно.

+ +
+

Ваш текущий баланс:

+

{{current_tokens}} токенов

+

Не хватает: {{missing_tokens}} токенов

+
+ + +
+ + +
+ + \ No newline at end of file