dev add context for notifcations
Test Workflow / test (push) Has been cancelled

This commit is contained in:
2026-05-13 13:39:02 +07:00
parent 0546f6acd5
commit 48c14d475d
10 changed files with 154 additions and 21 deletions
@@ -42,6 +42,9 @@ public class Notification {
@Column(name = "status") @Column(name = "status")
private NotificationStatus status = NotificationStatus.NEW; private NotificationStatus status = NotificationStatus.NEW;
@Column(name = "context", columnDefinition = "TEXT")
private String context;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id") @JoinColumn(name = "user_id")
@JsonIgnore @JsonIgnore
@@ -1,21 +1,25 @@
package ru.soune.nocopy.handler; package ru.soune.nocopy.handler;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import ru.soune.nocopy.dto.BaseRequest; import ru.soune.nocopy.dto.BaseRequest;
import ru.soune.nocopy.dto.BaseResponse; import ru.soune.nocopy.dto.BaseResponse;
import ru.soune.nocopy.dto.MessageCode; 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.file.ActionResponse;
import ru.soune.nocopy.dto.user.moderation.UserVerificationRequest; import ru.soune.nocopy.dto.user.moderation.UserVerificationRequest;
import ru.soune.nocopy.dto.user.moderation.UserVerificationResponse; 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.notification.NotificationType;
import ru.soune.nocopy.entity.user.ModerationStatus; import ru.soune.nocopy.entity.user.ModerationStatus;
import ru.soune.nocopy.entity.user.moderation.UserVerification; import ru.soune.nocopy.entity.user.moderation.UserVerification;
import ru.soune.nocopy.service.notification.NotificationService; import ru.soune.nocopy.service.notification.NotificationService;
import ru.soune.nocopy.service.user.moderation.UserVerificationService; import ru.soune.nocopy.service.user.moderation.UserVerificationService;
import java.time.format.DateTimeFormatter;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -81,10 +85,7 @@ public class UserVerificationHandler implements RequestHandler {
null); null);
} }
NotificationType notificationType = userVerification.getModerationStatus() == ModerationStatus.VERIFIED? addComplaintNotification(userVerification);
NotificationType.USER_VERIFIED: NotificationType.USER_NOT_VERIFIED;
notificationService.addNotification(notificationType, userVerification.getUserId());
return BaseResponse.builder() return BaseResponse.builder()
.msgId(request.getMsgId()) .msgId(request.getMsgId())
@@ -95,6 +96,18 @@ public class UserVerificationHandler implements RequestHandler {
.build(); .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) { private UserVerificationResponse convertDto(UserVerification userVerification) {
return UserVerificationResponse.builder() return UserVerificationResponse.builder()
@@ -1,5 +1,7 @@
package ru.soune.nocopy.service.complaint; package ru.soune.nocopy.service.complaint;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -117,8 +119,7 @@ public class ComplaintEntityService {
if (request.getStatus() != null) { if (request.getStatus() != null) {
complaint.setStatus(ComplaintStatus.valueOf(request.getStatus())); complaint.setStatus(ComplaintStatus.valueOf(request.getStatus()));
notificationService.addNotification(NotificationType.COMPLAINT_STATUS_CHANGED, complaint.getViolation() addComplaintNotification(complaint, request);
.getFileEntity().getUserId(), request.getStatus());
} }
return mapToResponse(complaintRepository.save(complaint)); return mapToResponse(complaintRepository.save(complaint));
@@ -146,4 +147,17 @@ public class ComplaintEntityService {
.notModerated(entity.getNot_moderated_file() != null ? entity.getNot_moderated_file() : null) .notModerated(entity.getNot_moderated_file() != null ? entity.getNot_moderated_file() : null)
.build(); .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());
}
} }
@@ -1,5 +1,7 @@
package ru.soune.nocopy.service.file.impl; 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.NoCopyFileService;
import com.vrt.fileprotection.FileProtector; import com.vrt.fileprotection.FileProtector;
import com.vrt.fileprotection.NoCopyCheckResult; import com.vrt.fileprotection.NoCopyCheckResult;
@@ -447,7 +449,16 @@ public class FileUploadServiceImpl implements FileUploadService {
tariffInfoService.writeOffTokens(session.getUserId(), costService.protectFileCost(fileType), tariffInfoService.writeOffTokens(session.getUserId(), costService.protectFileCost(fileType),
OperationType.FILE_UPLOAD); 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) { if (status != FileStatus.TEMP) {
@@ -1,5 +1,7 @@
package ru.soune.nocopy.service.file.moderation; package ru.soune.nocopy.service.file.moderation;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; 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);
} }
/** /**
@@ -1,5 +1,7 @@
package ru.soune.nocopy.service.monitoring; package ru.soune.nocopy.service.monitoring;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.mail.MessagingException; import jakarta.mail.MessagingException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; 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.dto.tarriff.TariffDTO;
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity; import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
import ru.soune.nocopy.entity.monitoring.MonitoringType; 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.notification.NotificationType;
import ru.soune.nocopy.entity.tarif.TariffInfo; import ru.soune.nocopy.entity.tarif.TariffInfo;
import ru.soune.nocopy.entity.tarif.TariffType; import ru.soune.nocopy.entity.tarif.TariffType;
@@ -29,8 +30,10 @@ import ru.soune.nocopy.service.violation.ViolationService;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@Slf4j @Slf4j
@@ -97,7 +100,12 @@ public class MonitoringSearchService {
monitoringRepository.save(monitoring); 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 { } else {
log.info("Yandex search is disabled by settings"); log.info("Yandex search is disabled by settings");
@@ -121,7 +129,14 @@ public class MonitoringSearchService {
monitoringRepository.save(monitoring); 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 { } else {
log.info("Google search is disabled by settings"); log.info("Google search is disabled by settings");
@@ -159,8 +174,15 @@ public class MonitoringSearchService {
monitoringRepository.save(monitoring); monitoringRepository.save(monitoring);
notificationService.addNotification(NotificationType.TOKEN_NOT_FOUND, user.getId(), ObjectMapper mapper = new ObjectMapper();
NotificationMessage.FILE_SEARCH.getMessageKey()); 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) { } catch (Exception e) {
log.error("Error processing monitoring search", e); log.error("Error processing monitoring search", e);
monitoring.setLastRunStatus("ERROR: " + e.getMessage()); monitoring.setLastRunStatus("ERROR: " + e.getMessage());
@@ -168,7 +190,14 @@ public class MonitoringSearchService {
monitoringRepository.save(monitoring); 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);
} }
} }
@@ -45,6 +45,20 @@ public class NotificationService {
notificationRepository.save(notification); 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 @Transactional
public void deleteNotification(Notification notification) { public void deleteNotification(Notification notification) {
notificationRepository.delete(notification); notificationRepository.delete(notification);
@@ -1,5 +1,7 @@
package ru.soune.nocopy.service.payment; package ru.soune.nocopy.service.payment;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; 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.PaymentMethod;
import ru.soune.nocopy.entity.payment.PaymentOperationType; import ru.soune.nocopy.entity.payment.PaymentOperationType;
import ru.soune.nocopy.entity.payment.PaymentStatus; 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.Tariff;
import ru.soune.nocopy.entity.tarif.TariffInfo; import ru.soune.nocopy.entity.tarif.TariffInfo;
import ru.soune.nocopy.entity.tarif.TariffStatus; 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 ru.soune.nocopy.service.tariff.TariffInfoService;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -166,9 +170,7 @@ public class PaymentService {
referralService.onUserAccountRefill(payment.getUser().getId(), intPrice); referralService.onUserAccountRefill(payment.getUser().getId(), intPrice);
// long inviterIdForUser = referralRepo.getInviterIdForUser(payment.getUser().getId()); addSearchPaymentNotification(payment, payment.getUser().getId(), null);
// notificationService.addNotification(NotificationType.REFERRAL_ACTIVATED, inviterIdForUser);
} else if ("payment.canceled".equals(eventType)) { } else if ("payment.canceled".equals(eventType)) {
payment.setStatus(PaymentStatus.CANCELED); payment.setStatus(PaymentStatus.CANCELED);
@@ -177,7 +179,7 @@ public class PaymentService {
String reason = (String) cancellationDetails.get("reason"); String reason = (String) cancellationDetails.get("reason");
payment.setCancellationReason(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)) { } else if ("payment.waiting_for_capture".equals(eventType)) {
payment.setStatus(PaymentStatus.WAITING); payment.setStatus(PaymentStatus.WAITING);
payment.setCancellationReason(null); payment.setCancellationReason(null);
@@ -190,12 +192,23 @@ public class PaymentService {
String reason = (String) cancellationDetails.get("reason"); String reason = (String) cancellationDetails.get("reason");
payment.setCancellationReason(reason); payment.setCancellationReason(reason);
notificationService.addNotification(NotificationType.PAYMENT_RESULT, payment.getUser().getId(), reason); addSearchPaymentNotification(payment, payment.getUser().getId(), reason);
} }
paymentRepository.save(payment); 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<Payment> userPayments(String email) { public List<Payment> userPayments(String email) {
User user = userRepository.findByEmail(email); User user = userRepository.findByEmail(email);
@@ -1,5 +1,7 @@
package ru.soune.nocopy.service.referral; package ru.soune.nocopy.service.referral;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -12,6 +14,7 @@ import ru.soune.Referral;
import ru.soune.ReferralInvitee; import ru.soune.ReferralInvitee;
import ru.soune.ReferralRepo; import ru.soune.ReferralRepo;
import ru.soune.nocopy.entity.notification.NotificationType; 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.entity.user.User;
import ru.soune.nocopy.repository.ReferralJpaRepository; import ru.soune.nocopy.repository.ReferralJpaRepository;
import ru.soune.nocopy.repository.UserRepository; import ru.soune.nocopy.repository.UserRepository;
@@ -168,6 +171,12 @@ public class ReferralRepoImpl implements ReferralRepo {
referralRepository.save(referral); referralRepository.save(referral);
if (inviter != null){ 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); notificationService.addNotification(NotificationType.REFERRAL_REGISTERED, inviter);
} }
} }
@@ -1,5 +1,7 @@
package ru.soune.nocopy.service.search; package ru.soune.nocopy.service.search;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
@@ -23,6 +25,7 @@ import ru.soune.nocopy.service.violation.ViolationService;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@@ -71,9 +74,9 @@ public class GlobalSearchAsyncProcessor {
task.setStatus(SearchStatus.COMPLETED.name()); task.setStatus(SearchStatus.COMPLETED.name());
task.setUpdatedAt(LocalDateTime.now()); 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) { } catch (Exception e) {
log.error("Global search failed for task: {}", taskId, e); log.error("Global search failed for task: {}", taskId, e);
@@ -83,6 +86,21 @@ public class GlobalSearchAsyncProcessor {
} }
} }
private void addSearchResultNotification(GlobalSearchTask globalSearchTask, List<String> 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) { private GlobalSearchResult processFile(FileEntity file, String taskId, Long userId) {
GlobalSearchResult result = new GlobalSearchResult(); GlobalSearchResult result = new GlobalSearchResult();
result.setTaskId(taskId); result.setTaskId(taskId);