From 48c14d475db891fdb25e3716f502657393a6ecf2 Mon Sep 17 00:00:00 2001 From: backdev-1 Date: Wed, 13 May 2026 13:39:02 +0700 Subject: [PATCH] dev add context for notifcations --- .../entity/notification/Notification.java | 3 ++ .../handler/UserVerificationHandler.java | 21 ++++++++-- .../complaint/ComplaintEntityService.java | 18 +++++++- .../file/impl/FileUploadServiceImpl.java | 13 +++++- .../file/moderation/ModerationService.java | 11 ++++- .../monitoring/MonitoringSearchService.java | 41 ++++++++++++++++--- .../notification/NotificationService.java | 14 +++++++ .../service/payment/PaymentService.java | 23 ++++++++--- .../service/referral/ReferralRepoImpl.java | 9 ++++ .../search/GlobalSearchAsyncProcessor.java | 22 +++++++++- 10 files changed, 154 insertions(+), 21 deletions(-) diff --git a/src/main/java/ru/soune/nocopy/entity/notification/Notification.java b/src/main/java/ru/soune/nocopy/entity/notification/Notification.java index 87b772b..13c66f5 100644 --- a/src/main/java/ru/soune/nocopy/entity/notification/Notification.java +++ b/src/main/java/ru/soune/nocopy/entity/notification/Notification.java @@ -42,6 +42,9 @@ public class Notification { @Column(name = "status") private NotificationStatus status = NotificationStatus.NEW; + @Column(name = "context", columnDefinition = "TEXT") + private String context; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") @JsonIgnore diff --git a/src/main/java/ru/soune/nocopy/handler/UserVerificationHandler.java b/src/main/java/ru/soune/nocopy/handler/UserVerificationHandler.java index a09036d..10a1647 100644 --- a/src/main/java/ru/soune/nocopy/handler/UserVerificationHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/UserVerificationHandler.java @@ -1,21 +1,25 @@ package ru.soune.nocopy.handler; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; 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.complaint.ComplaintRequest; import ru.soune.nocopy.dto.file.ActionResponse; import ru.soune.nocopy.dto.user.moderation.UserVerificationRequest; import ru.soune.nocopy.dto.user.moderation.UserVerificationResponse; +import ru.soune.nocopy.entity.complaint.ComplaintEntity; import ru.soune.nocopy.entity.notification.NotificationType; import ru.soune.nocopy.entity.user.ModerationStatus; import ru.soune.nocopy.entity.user.moderation.UserVerification; import ru.soune.nocopy.service.notification.NotificationService; import ru.soune.nocopy.service.user.moderation.UserVerificationService; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; @@ -81,10 +85,7 @@ public class UserVerificationHandler implements RequestHandler { null); } - NotificationType notificationType = userVerification.getModerationStatus() == ModerationStatus.VERIFIED? - NotificationType.USER_VERIFIED: NotificationType.USER_NOT_VERIFIED; - - notificationService.addNotification(notificationType, userVerification.getUserId()); + addComplaintNotification(userVerification); return BaseResponse.builder() .msgId(request.getMsgId()) @@ -95,6 +96,18 @@ public class UserVerificationHandler implements RequestHandler { .build(); } + private void addComplaintNotification(UserVerification userVerification) { + NotificationType notificationType = userVerification.getModerationStatus() == ModerationStatus.VERIFIED ? + NotificationType.USER_VERIFIED: NotificationType.USER_NOT_VERIFIED; + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + context.put("status", userVerification.getModerationStatus().name()); + context.put("update_date", + userVerification.getUpdatedAt().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))); + + notificationService.addNotification(notificationType, userVerification.getUserId(), context.toString()); + } + private UserVerificationResponse convertDto(UserVerification userVerification) { return UserVerificationResponse.builder() diff --git a/src/main/java/ru/soune/nocopy/service/complaint/ComplaintEntityService.java b/src/main/java/ru/soune/nocopy/service/complaint/ComplaintEntityService.java index c768fb1..fdf0915 100644 --- a/src/main/java/ru/soune/nocopy/service/complaint/ComplaintEntityService.java +++ b/src/main/java/ru/soune/nocopy/service/complaint/ComplaintEntityService.java @@ -1,5 +1,7 @@ package ru.soune.nocopy.service.complaint; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -117,8 +119,7 @@ public class ComplaintEntityService { if (request.getStatus() != null) { complaint.setStatus(ComplaintStatus.valueOf(request.getStatus())); - notificationService.addNotification(NotificationType.COMPLAINT_STATUS_CHANGED, complaint.getViolation() - .getFileEntity().getUserId(), request.getStatus()); + addComplaintNotification(complaint, request); } return mapToResponse(complaintRepository.save(complaint)); @@ -146,4 +147,17 @@ public class ComplaintEntityService { .notModerated(entity.getNot_moderated_file() != null ? entity.getNot_moderated_file() : null) .build(); } + + private void addComplaintNotification(ComplaintEntity complaint, ComplaintRequest request) { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + context.put("new_status", request.getStatus()); + context.put("old_status", complaint.getStatus().name()); + context.put("violation", complaint.getViolation().getId()); + context.put("file", complaint.getViolation().getFileEntity().getOriginalFileName()); + context.put("file_support_id", complaint.getViolation().getFileEntity().getSupportId()); + + notificationService.addNotification(NotificationType.COMPLAINT_STATUS_CHANGED, complaint.getViolation() + .getFileEntity().getUserId(), context.toString()); + } } diff --git a/src/main/java/ru/soune/nocopy/service/file/impl/FileUploadServiceImpl.java b/src/main/java/ru/soune/nocopy/service/file/impl/FileUploadServiceImpl.java index fdcd0d1..6a3288f 100644 --- a/src/main/java/ru/soune/nocopy/service/file/impl/FileUploadServiceImpl.java +++ b/src/main/java/ru/soune/nocopy/service/file/impl/FileUploadServiceImpl.java @@ -1,5 +1,7 @@ package ru.soune.nocopy.service.file.impl; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.vrt.NoCopyFileService; import com.vrt.fileprotection.FileProtector; import com.vrt.fileprotection.NoCopyCheckResult; @@ -447,7 +449,16 @@ public class FileUploadServiceImpl implements FileUploadService { tariffInfoService.writeOffTokens(session.getUserId(), costService.protectFileCost(fileType), OperationType.FILE_UPLOAD); - notificationService.addNotification(NotificationType.FILE_ADDED_TO_SYSTEM, session.getUserId()); + + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + context.put("status", status.name()); + context.put("file_name", fileEntity.getOriginalFileName()); + context.put("file_id", fileEntity.getId()); + context.put("support_id", fileEntity.getSupportId()); + + notificationService.addNotification(NotificationType.FILE_ADDED_TO_SYSTEM, session.getUserId(), + context); } if (status != FileStatus.TEMP) { diff --git a/src/main/java/ru/soune/nocopy/service/file/moderation/ModerationService.java b/src/main/java/ru/soune/nocopy/service/file/moderation/ModerationService.java index 090dc59..a7a6ee9 100644 --- a/src/main/java/ru/soune/nocopy/service/file/moderation/ModerationService.java +++ b/src/main/java/ru/soune/nocopy/service/file/moderation/ModerationService.java @@ -1,5 +1,7 @@ package ru.soune.nocopy.service.file.moderation; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -89,7 +91,14 @@ public class ModerationService { } } - notificationService.addNotification(NotificationType.FILE_MODERATION_EVENT, file.getUserId()); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + context.put("file_name", file.getOriginalFileName()); + context.put("old_status", file.getStatus().name()); + context.put("new_status", request.getNewStatus().name()); + context.put("comment", request.getComment()); + + notificationService.addNotification(NotificationType.FILE_MODERATION_EVENT, file.getUserId(), context); } /** 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 96a64bc..028177d 100644 --- a/src/main/java/ru/soune/nocopy/service/monitoring/MonitoringSearchService.java +++ b/src/main/java/ru/soune/nocopy/service/monitoring/MonitoringSearchService.java @@ -1,5 +1,7 @@ package ru.soune.nocopy.service.monitoring; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.mail.MessagingException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -11,7 +13,6 @@ import ru.soune.nocopy.dto.file.YandexSearchResponse; import ru.soune.nocopy.dto.tarriff.TariffDTO; import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity; import ru.soune.nocopy.entity.monitoring.MonitoringType; -import ru.soune.nocopy.entity.notification.NotificationMessage; import ru.soune.nocopy.entity.notification.NotificationType; import ru.soune.nocopy.entity.tarif.TariffInfo; import ru.soune.nocopy.entity.tarif.TariffType; @@ -29,8 +30,10 @@ import ru.soune.nocopy.service.violation.ViolationService; import java.io.IOException; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeoutException; @Slf4j @@ -97,7 +100,12 @@ public class MonitoringSearchService { monitoringRepository.save(monitoring); - notificationService.addNotification(NotificationType.SEARCH_OPERATION_FAILED, user.getId()); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + context.put("status", NotificationType.SEARCH_OPERATION_FAILED.name()); + context.put("error_message", e.getMessage()); + + notificationService.addNotification(NotificationType.SEARCH_OPERATION_FAILED, user.getId(), context); } } else { log.info("Yandex search is disabled by settings"); @@ -121,7 +129,14 @@ public class MonitoringSearchService { monitoringRepository.save(monitoring); - notificationService.addNotification(NotificationType.SEARCH_OPERATION_FAILED, user.getId()); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + context.put("message", "Error processing monitoring search"); + context.put("reason", e.getMessage()); + context.put("next_run", Objects.requireNonNull(calculateNextRun( + monitoring.getMonitoringType())).format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))); + + notificationService.addNotification(NotificationType.SEARCH_OPERATION_FAILED, user.getId(), context); } } else { log.info("Google search is disabled by settings"); @@ -159,8 +174,15 @@ public class MonitoringSearchService { monitoringRepository.save(monitoring); - notificationService.addNotification(NotificationType.TOKEN_NOT_FOUND, user.getId(), - NotificationMessage.FILE_SEARCH.getMessageKey()); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + context.put("current_tokens", currentTokens); + context.put("need_tokens", tariffMonitoring.getTokens()); + context.put("message", "Not have tokens for search"); + context.put("next_run", Objects.requireNonNull(calculateNextRun( + monitoring.getMonitoringType())).format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))); + + notificationService.addNotification(NotificationType.TOKEN_NOT_FOUND, user.getId(), context); } catch (Exception e) { log.error("Error processing monitoring search", e); monitoring.setLastRunStatus("ERROR: " + e.getMessage()); @@ -168,7 +190,14 @@ public class MonitoringSearchService { monitoringRepository.save(monitoring); - notificationService.addNotification(NotificationType.SEARCH_OPERATION_FAILED, user.getId()); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + context.put("message", "Error processing monitoring search"); + context.put("reason", e.getMessage()); + context.put("next_run", Objects.requireNonNull(calculateNextRun( + monitoring.getMonitoringType())).format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))); + + notificationService.addNotification(NotificationType.SEARCH_OPERATION_FAILED, user.getId(), context); } } diff --git a/src/main/java/ru/soune/nocopy/service/notification/NotificationService.java b/src/main/java/ru/soune/nocopy/service/notification/NotificationService.java index d7567b5..18817fd 100644 --- a/src/main/java/ru/soune/nocopy/service/notification/NotificationService.java +++ b/src/main/java/ru/soune/nocopy/service/notification/NotificationService.java @@ -45,6 +45,20 @@ public class NotificationService { notificationRepository.save(notification); } + @Transactional + public void addNotification(NotificationType notificationType, long userId, String context) { + Notification notification = Notification.builder() + .notificationType(notificationType) + .createdAt(LocalDateTime.now()) + .user(userRepository.findById(userId).orElseThrow()) + .status(NotificationStatus.NEW) + .message(notificationType.getMessageKey()) + .context(context) + .build(); + + notificationRepository.save(notification); + } + @Transactional public void deleteNotification(Notification notification) { notificationRepository.delete(notification); diff --git a/src/main/java/ru/soune/nocopy/service/payment/PaymentService.java b/src/main/java/ru/soune/nocopy/service/payment/PaymentService.java index dac9c50..7cd3ce3 100644 --- a/src/main/java/ru/soune/nocopy/service/payment/PaymentService.java +++ b/src/main/java/ru/soune/nocopy/service/payment/PaymentService.java @@ -1,5 +1,7 @@ package ru.soune.nocopy.service.payment; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -13,6 +15,7 @@ import ru.soune.nocopy.entity.payment.Payment; import ru.soune.nocopy.entity.payment.PaymentMethod; import ru.soune.nocopy.entity.payment.PaymentOperationType; import ru.soune.nocopy.entity.payment.PaymentStatus; +import ru.soune.nocopy.entity.search.GlobalSearchTask; import ru.soune.nocopy.entity.tarif.Tariff; import ru.soune.nocopy.entity.tarif.TariffInfo; import ru.soune.nocopy.entity.tarif.TariffStatus; @@ -25,6 +28,7 @@ import ru.soune.nocopy.service.notification.NotificationService; import ru.soune.nocopy.service.tariff.TariffInfoService; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -166,9 +170,7 @@ public class PaymentService { referralService.onUserAccountRefill(payment.getUser().getId(), intPrice); -// long inviterIdForUser = referralRepo.getInviterIdForUser(payment.getUser().getId()); - -// notificationService.addNotification(NotificationType.REFERRAL_ACTIVATED, inviterIdForUser); + addSearchPaymentNotification(payment, payment.getUser().getId(), null); } else if ("payment.canceled".equals(eventType)) { payment.setStatus(PaymentStatus.CANCELED); @@ -177,7 +179,7 @@ public class PaymentService { String reason = (String) cancellationDetails.get("reason"); payment.setCancellationReason(reason); - notificationService.addNotification(NotificationType.PAYMENT_RESULT, payment.getUser().getId(), reason); + addSearchPaymentNotification(payment, payment.getUser().getId(), reason); } else if ("payment.waiting_for_capture".equals(eventType)) { payment.setStatus(PaymentStatus.WAITING); payment.setCancellationReason(null); @@ -190,12 +192,23 @@ public class PaymentService { String reason = (String) cancellationDetails.get("reason"); payment.setCancellationReason(reason); - notificationService.addNotification(NotificationType.PAYMENT_RESULT, payment.getUser().getId(), reason); + addSearchPaymentNotification(payment, payment.getUser().getId(), reason); } paymentRepository.save(payment); } + private void addSearchPaymentNotification(Payment payment, Long userId, String reason) { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + + context.put("status", payment.getStatus().name()); + context.put("reason", reason); + context.put("amount", payment.getAmount()); + + notificationService.addNotification(NotificationType.PAYMENT_RESULT, userId, context); + } + public List userPayments(String email) { User user = userRepository.findByEmail(email); diff --git a/src/main/java/ru/soune/nocopy/service/referral/ReferralRepoImpl.java b/src/main/java/ru/soune/nocopy/service/referral/ReferralRepoImpl.java index c5493e4..3a24817 100644 --- a/src/main/java/ru/soune/nocopy/service/referral/ReferralRepoImpl.java +++ b/src/main/java/ru/soune/nocopy/service/referral/ReferralRepoImpl.java @@ -1,5 +1,7 @@ package ru.soune.nocopy.service.referral; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; @@ -12,6 +14,7 @@ import ru.soune.Referral; import ru.soune.ReferralInvitee; import ru.soune.ReferralRepo; import ru.soune.nocopy.entity.notification.NotificationType; +import ru.soune.nocopy.entity.payment.Payment; import ru.soune.nocopy.entity.user.User; import ru.soune.nocopy.repository.ReferralJpaRepository; import ru.soune.nocopy.repository.UserRepository; @@ -168,6 +171,12 @@ public class ReferralRepoImpl implements ReferralRepo { referralRepository.save(referral); if (inviter != null){ + User user = userRepository.findById(entity.getUserId()).orElse(null); + + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + context.put("referral_user", user != null ? user.getFullName(): null); + notificationService.addNotification(NotificationType.REFERRAL_REGISTERED, inviter); } } diff --git a/src/main/java/ru/soune/nocopy/service/search/GlobalSearchAsyncProcessor.java b/src/main/java/ru/soune/nocopy/service/search/GlobalSearchAsyncProcessor.java index ac10767..d2ac58a 100644 --- a/src/main/java/ru/soune/nocopy/service/search/GlobalSearchAsyncProcessor.java +++ b/src/main/java/ru/soune/nocopy/service/search/GlobalSearchAsyncProcessor.java @@ -1,5 +1,7 @@ package ru.soune.nocopy.service.search; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; @@ -23,6 +25,7 @@ import ru.soune.nocopy.service.violation.ViolationService; import java.io.IOException; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeoutException; @@ -71,9 +74,9 @@ public class GlobalSearchAsyncProcessor { task.setStatus(SearchStatus.COMPLETED.name()); task.setUpdatedAt(LocalDateTime.now()); - globalSearchTaskRepository.save(task); + GlobalSearchTask save = globalSearchTaskRepository.save(task); - notificationService.addNotification(NotificationType.SEARCH_RESULT, userId); + addSearchResultNotification(save, filesToProcess, userId); } catch (Exception e) { log.error("Global search failed for task: {}", taskId, e); @@ -83,6 +86,21 @@ public class GlobalSearchAsyncProcessor { } } + private void addSearchResultNotification(GlobalSearchTask globalSearchTask, List files, Long userId) { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode context = mapper.createObjectNode(); + + context.put("start_date", + globalSearchTask.getCreatedAt().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))); + context.put("end_date", + LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))); + context.put("search_type", globalSearchTask.getSearchType()); + context.put("files_id", files.toString()); + context.put("count", files.toString()); + + notificationService.addNotification(NotificationType.SEARCH_RESULT, userId, context); + } + private GlobalSearchResult processFile(FileEntity file, String taskId, Long userId) { GlobalSearchResult result = new GlobalSearchResult(); result.setTaskId(taskId);