Merge branch 'dev' into NCBACK-35
# Conflicts: # src/main/java/ru/soune/nocopy/service/file/impl/FileUploadServiceImpl.java
This commit is contained in:
@@ -12,21 +12,17 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import ru.soune.nocopy.entity.file.FileEntity;
|
|
||||||
import ru.soune.nocopy.entity.file.ProtectionStatus;
|
|
||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
|
||||||
import ru.soune.nocopy.util.FileUtil;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Collections;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class ApplicationConfig {
|
public class ApplicationConfig {
|
||||||
|
|
||||||
private final FileEntityRepository fileEntityRepository;
|
// private final FileEntityRepository fileEntityRepository;
|
||||||
|
|
||||||
private final FileUtil fileUtil;
|
// private final FileUtil fileUtil;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
PasswordEncoder passwordEncoder() {
|
PasswordEncoder passwordEncoder() {
|
||||||
@@ -44,11 +40,11 @@ public class ApplicationConfig {
|
|||||||
AudioFilePathProvider audioFilePathProvider,
|
AudioFilePathProvider audioFilePathProvider,
|
||||||
DocumentLocalSearch documentLocalSearch) {
|
DocumentLocalSearch documentLocalSearch) {
|
||||||
|
|
||||||
List<FileProtector.FileInfo> initialFiles = loadProcessingFile();
|
// List<FileProtector.FileInfo> initialFiles = loadProcessingFile();
|
||||||
|
|
||||||
//TODO инициализация файлов при создании
|
//TODO инициализация файлов при создании
|
||||||
return new com.vrt.NoCopyFileService(
|
return new com.vrt.NoCopyFileService(
|
||||||
initialFiles,
|
Collections.emptyList(),
|
||||||
fileProvider,
|
fileProvider,
|
||||||
processingListener,
|
processingListener,
|
||||||
imageUniqueCheck,
|
imageUniqueCheck,
|
||||||
@@ -59,12 +55,12 @@ public class ApplicationConfig {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<FileProtector.FileInfo> loadProcessingFile() {
|
// private List<FileProtector.FileInfo> loadProcessingFile() {
|
||||||
List<FileEntity> byProtectionStatus =
|
// List<FileEntity> byProtectionStatus =
|
||||||
fileEntityRepository.findByProtectionStatus(ProtectionStatus.PROCESSING);
|
// fileEntityRepository.findByProtectionStatus(ProtectionStatus.PROCESSING);
|
||||||
|
//
|
||||||
return byProtectionStatus.stream().map(fileEntity ->
|
// return byProtectionStatus.stream().map(fileEntity ->
|
||||||
fileUtil.createFileInfo(fileEntity, null)).toList();
|
// fileUtil.createFileInfo(fileEntity, null)).toList();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ public class HandlerConfig {
|
|||||||
GlobalSearchHandler globalSearchHandler,
|
GlobalSearchHandler globalSearchHandler,
|
||||||
ViolationNotionHandler violationNotionHandler,
|
ViolationNotionHandler violationNotionHandler,
|
||||||
ComplaintEntityHandler complaintEntityHandler,
|
ComplaintEntityHandler complaintEntityHandler,
|
||||||
UserInfoHandler userInfoHandler
|
UserInfoHandler userInfoHandler,
|
||||||
|
NotificationHandler notificationHandler
|
||||||
) {
|
) {
|
||||||
Map<Integer, RequestHandler> map = new HashMap<>();
|
Map<Integer, RequestHandler> map = new HashMap<>();
|
||||||
map.put(20001, login);
|
map.put(20001, login);
|
||||||
@@ -60,6 +61,7 @@ public class HandlerConfig {
|
|||||||
map.put(30012, violationNotionHandler);
|
map.put(30012, violationNotionHandler);
|
||||||
map.put(30013, complaintEntityHandler);
|
map.put(30013, complaintEntityHandler);
|
||||||
map.put(30014, userInfoHandler);
|
map.put(30014, userInfoHandler);
|
||||||
|
map.put(30015, notificationHandler);
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package ru.soune.nocopy.dto.notification;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ActivateNotificationRequest {
|
||||||
|
private String authToken;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package ru.soune.nocopy.dto.notification;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class ActiveNotificationsResponse {
|
||||||
|
private List<NotificationDto> notifications;
|
||||||
|
private long unreadCount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package ru.soune.nocopy.dto.notification;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class MarkAsReadRequest {
|
||||||
|
private List<Long> notificationIds;
|
||||||
|
private String authToken;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package ru.soune.nocopy.dto.notification;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class MarkAsReadResponse {
|
||||||
|
private boolean success;
|
||||||
|
private int updatedCount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package ru.soune.nocopy.dto.notification;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationStatus;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationType;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class NotificationDto {
|
||||||
|
private Long id;
|
||||||
|
private String message;
|
||||||
|
private NotificationType type;
|
||||||
|
private NotificationStatus status;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package ru.soune.nocopy.dto.notification;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationStatus;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class NotificationListRequest {
|
||||||
|
private int page = 0;
|
||||||
|
private int size = 10;
|
||||||
|
private String sortBy = "createdAt";
|
||||||
|
private String sortDirection = "DESC";
|
||||||
|
private List<NotificationType> types;
|
||||||
|
private List<NotificationStatus> statuses;
|
||||||
|
private String authToken;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package ru.soune.nocopy.dto.notification;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class NotificationListResponse {
|
||||||
|
private List<NotificationDto> notifications;
|
||||||
|
private int currentPage;
|
||||||
|
private int totalPages;
|
||||||
|
private long totalElements;
|
||||||
|
}
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
package ru.soune.nocopy.entity.monitoring;
|
package ru.soune.nocopy.entity.monitoring;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
public enum MonitoringType {
|
public enum MonitoringType {
|
||||||
MONITORING_DAILY, MONITORING_WEEKLY, MONITORING_MONTHLY, NONE
|
MONITORING_DAILY(1), MONITORING_WEEKLY(7), MONITORING_MONTHLY(30), NONE(0);
|
||||||
|
|
||||||
|
private final int days;
|
||||||
|
|
||||||
|
MonitoringType(int days) {
|
||||||
|
this.days = days;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package ru.soune.nocopy.entity.notification;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
import org.springframework.data.annotation.CreatedDate;
|
||||||
|
import org.springframework.data.annotation.LastModifiedDate;
|
||||||
|
import ru.soune.nocopy.entity.user.User;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter @Setter
|
||||||
|
@Builder
|
||||||
|
@Table(name = "notifications", indexes = {
|
||||||
|
@Index(name = "idx_user_status", columnList = "user_id, status"),
|
||||||
|
@Index(name = "idx_user_created", columnList = "user_id, created_at DESC")
|
||||||
|
})
|
||||||
|
public class Notification {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "created_at", updatable = false)
|
||||||
|
@CreatedDate
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@LastModifiedDate
|
||||||
|
@Column(name = "updated_at")
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@Column(name = "message")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "notification_type")
|
||||||
|
private NotificationType notificationType;
|
||||||
|
|
||||||
|
@Column(name = "status")
|
||||||
|
private NotificationStatus status = NotificationStatus.NEW;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "user_id")
|
||||||
|
@JsonIgnore
|
||||||
|
private User user;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package ru.soune.nocopy.entity.notification;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum NotificationMessage {
|
||||||
|
FILE_SEARCH("notification.file.search");
|
||||||
|
|
||||||
|
private final String messageKey;
|
||||||
|
|
||||||
|
NotificationMessage(String messageKey) {
|
||||||
|
this.messageKey = messageKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package ru.soune.nocopy.entity.notification;
|
||||||
|
|
||||||
|
public enum NotificationStatus {
|
||||||
|
NEW, READIED
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package ru.soune.nocopy.entity.notification;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum NotificationType {
|
||||||
|
SEARCH_RESULT("notification.search.result"),
|
||||||
|
MONITORING_RESULT("notification.monitoring.result"),
|
||||||
|
OPERATION_IMPOSSIBLE("notification.operation.impossible"),
|
||||||
|
START_FAILED_NOW("notification.start.failed.now"),
|
||||||
|
START_FAILED_NEXT("notification.start.failed.next"),
|
||||||
|
PAYOUT_RESULT("notification.payout.result"),
|
||||||
|
PAYMENT_RESULT("notification.payment.result"),
|
||||||
|
REFERRAL_DEPOSIT("notification.referral.deposit"),
|
||||||
|
REFERRAL_REGISTERED("notification.referral.registered"),
|
||||||
|
REFERRAL_ACTIVATED("notification.referral.activated"),
|
||||||
|
COMPLAINT_STATUS_CHANGED("notification.complaint.status"),
|
||||||
|
CASE_STATUS_CHANGED("notification.case.status"),
|
||||||
|
INCOMING_MESSAGE("notification.incoming.message"),
|
||||||
|
TARIFF_EXPIRING("notification.tariff.expiring"),
|
||||||
|
TOKEN_NOT_FOUND("notification.token.not.found"),
|
||||||
|
FILE_MODERATION_EVENT("notification.file.moderation"),
|
||||||
|
FILE_ADDED_TO_SYSTEM("notification.file.added"),
|
||||||
|
SEARCH_OPERATION_FAILED("notification.search.operation.filed");
|
||||||
|
|
||||||
|
private final String messageKey;
|
||||||
|
|
||||||
|
NotificationType(String messageKey) {
|
||||||
|
this.messageKey = messageKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,14 +2,21 @@ package ru.soune.nocopy.entity.tarif;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public enum TariffTimeTerm {
|
public enum TariffTimeTerm {
|
||||||
MONTHLY(30),YEAR(365);
|
MONTHLY(30, "MONTHLY"),YEAR(365, "YEAR");
|
||||||
|
|
||||||
private final Integer days;
|
private final Integer days;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
TariffTimeTerm(Integer days) {
|
TariffTimeTerm(Integer days, String name) {
|
||||||
this.days = days;
|
this.days = days;
|
||||||
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<String> validTerms() {
|
||||||
|
return List.of(MONTHLY.name(), YEAR.name());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package ru.soune.nocopy.handler;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
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.file.ActionResponse;
|
||||||
|
import ru.soune.nocopy.service.notification.NotificationService;
|
||||||
|
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||||
|
import ru.soune.nocopy.dto.notification.*;
|
||||||
|
import ru.soune.nocopy.entity.notification.Notification;
|
||||||
|
import ru.soune.nocopy.entity.user.User;
|
||||||
|
import ru.soune.nocopy.service.register.AuthService;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class NotificationHandler implements RequestHandler {
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private final AuthService authService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseResponse handle(BaseRequest request) throws Exception {
|
||||||
|
Map<String, Object> bodyMap = objectMapper.convertValue(request.getMessageBody(),
|
||||||
|
TypeFactory.defaultInstance().constructMapType(Map.class, String.class, Object.class));
|
||||||
|
|
||||||
|
String action = (String) bodyMap.get("action");
|
||||||
|
|
||||||
|
if (action == null) {
|
||||||
|
ActionResponse response = ActionResponse.builder()
|
||||||
|
.action(action)
|
||||||
|
.availableActions(Arrays.asList("list", "active", "markRead"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new BaseResponse(request.getMsgId(),
|
||||||
|
MessageCode.INVALID_FIELD.getCode(),
|
||||||
|
"Action is required",
|
||||||
|
response);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "list": {
|
||||||
|
NotificationListRequest listRequest = objectMapper.convertValue(request.getMessageBody(),
|
||||||
|
NotificationListRequest.class);
|
||||||
|
User user = authService.getUser(listRequest.getAuthToken());
|
||||||
|
|
||||||
|
Page<Notification> page = notificationService.getAllNotifications(
|
||||||
|
user,
|
||||||
|
listRequest.getPage(),
|
||||||
|
listRequest.getSize(),
|
||||||
|
listRequest.getSortBy(),
|
||||||
|
listRequest.getSortDirection(),
|
||||||
|
listRequest.getTypes(),
|
||||||
|
listRequest.getStatuses());
|
||||||
|
|
||||||
|
List<NotificationDto> dtos = page.getContent().stream()
|
||||||
|
.map(this::toDto)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
NotificationListResponse response = NotificationListResponse.builder()
|
||||||
|
.notifications(dtos)
|
||||||
|
.currentPage(page.getNumber())
|
||||||
|
.totalPages(page.getTotalPages())
|
||||||
|
.totalElements(page.getTotalElements())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new BaseResponse(request.getMsgId(),
|
||||||
|
MessageCode.SUCCESS.getCode(),
|
||||||
|
MessageCode.SUCCESS.getDescription(),
|
||||||
|
response);
|
||||||
|
}
|
||||||
|
|
||||||
|
case "active": {
|
||||||
|
ActivateNotificationRequest activateRequest = objectMapper.convertValue(request.getMessageBody(),
|
||||||
|
ActivateNotificationRequest.class);
|
||||||
|
User user = authService.getUser(activateRequest.getAuthToken());
|
||||||
|
|
||||||
|
List<Notification> recent = notificationService.getActiveNotifications(user);
|
||||||
|
|
||||||
|
long unreadCount = notificationService.getUnreadCount(user);
|
||||||
|
|
||||||
|
ActiveNotificationsResponse response = ActiveNotificationsResponse.builder()
|
||||||
|
.notifications(recent.stream().map(this::toDto).collect(Collectors.toList()))
|
||||||
|
.unreadCount(unreadCount)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new BaseResponse(request.getMsgId(),
|
||||||
|
MessageCode.SUCCESS.getCode(),
|
||||||
|
MessageCode.SUCCESS.getDescription(),
|
||||||
|
response);
|
||||||
|
}
|
||||||
|
|
||||||
|
case "markRead": {
|
||||||
|
MarkAsReadRequest markRequest = objectMapper.convertValue(request.getMessageBody(),
|
||||||
|
MarkAsReadRequest.class);
|
||||||
|
User user = authService.getUser(markRequest.getAuthToken());
|
||||||
|
|
||||||
|
int updated = notificationService.markAsRead(user, markRequest.getNotificationIds());
|
||||||
|
|
||||||
|
MarkAsReadResponse response = MarkAsReadResponse.builder()
|
||||||
|
.success(true)
|
||||||
|
.updatedCount(updated)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new BaseResponse(request.getMsgId(),
|
||||||
|
MessageCode.SUCCESS.getCode(),
|
||||||
|
MessageCode.SUCCESS.getDescription(),
|
||||||
|
response);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
ActionResponse response = ActionResponse.builder()
|
||||||
|
.action(action)
|
||||||
|
.availableActions(Arrays.asList("list", "active", "markRead"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return new BaseResponse(request.getMsgId(),
|
||||||
|
MessageCode.INVALID_ACTION.getCode(),
|
||||||
|
"Invalid action: " + action,
|
||||||
|
response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private NotificationDto toDto(Notification notification) {
|
||||||
|
return NotificationDto.builder()
|
||||||
|
.id(notification.getId())
|
||||||
|
.message(notification.getMessage())
|
||||||
|
.type(notification.getNotificationType())
|
||||||
|
.status(notification.getStatus())
|
||||||
|
.createdAt(notification.getCreatedAt())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -123,6 +123,16 @@ public class TariffHandler implements RequestHandler {
|
|||||||
private BaseResponse handleGetTariffs(BaseRequest request, TariffRequest tariffRequest) {
|
private BaseResponse handleGetTariffs(BaseRequest request, TariffRequest tariffRequest) {
|
||||||
AuthToken authToken = authTokenRepository.findByToken(tariffRequest.getUserToken()).orElseThrow();
|
AuthToken authToken = authTokenRepository.findByToken(tariffRequest.getUserToken()).orElseThrow();
|
||||||
User user = authToken.getUser();
|
User user = authToken.getUser();
|
||||||
|
|
||||||
|
if (tariffRequest.getTariffTerm() == null || !TariffTimeTerm.validTerms().contains(
|
||||||
|
tariffRequest.getTariffTerm())) {
|
||||||
|
return new BaseResponse(
|
||||||
|
request.getMsgId(),
|
||||||
|
MessageCode.SUCCESS.getCode(),
|
||||||
|
"Not valid tariff_term",
|
||||||
|
Map.of("Valid term", TariffTimeTerm.validTerms()));
|
||||||
|
}
|
||||||
|
|
||||||
List<TariffDTO> allTariffs = tariffService.getTariffByAccountTypeAndTariffTerm(user,
|
List<TariffDTO> allTariffs = tariffService.getTariffByAccountTypeAndTariffTerm(user,
|
||||||
TariffTimeTerm.valueOf(tariffRequest.getTariffTerm().toUpperCase()));
|
TariffTimeTerm.valueOf(tariffRequest.getTariffTerm().toUpperCase()));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package ru.soune.nocopy.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import ru.soune.nocopy.entity.notification.Notification;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationStatus;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationType;
|
||||||
|
import ru.soune.nocopy.entity.user.User;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface NotificationRepository extends JpaRepository<Notification, Long> {
|
||||||
|
Page<Notification> findByUser(User user, Pageable pageable);
|
||||||
|
|
||||||
|
Page<Notification> findByUserAndNotificationTypeIn(User user, List<NotificationType> types, Pageable pageable);
|
||||||
|
|
||||||
|
Page<Notification> findByUserAndStatusIn(User user, List<NotificationStatus> statuses, Pageable pageable);
|
||||||
|
|
||||||
|
Page<Notification> findByUserAndNotificationTypeInAndStatusIn(User user, List<NotificationType> types,
|
||||||
|
List<NotificationStatus> statuses,
|
||||||
|
Pageable pageable);
|
||||||
|
|
||||||
|
List<Notification> findTop5ByUserAndStatusOrderByCreatedAtDesc(User user, NotificationStatus status);
|
||||||
|
|
||||||
|
long countByUserAndStatus(User user, NotificationStatus status);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("UPDATE Notification n SET n.status = :newStatus WHERE n.id IN :ids AND n.user = :user AND " +
|
||||||
|
"n.status = :oldStatus")
|
||||||
|
int updateStatus(@Param("ids") List<Long> ids,
|
||||||
|
@Param("user") User user,
|
||||||
|
@Param("oldStatus") NotificationStatus oldStatus,
|
||||||
|
@Param("newStatus") NotificationStatus newStatus);
|
||||||
|
|
||||||
|
Page<Notification> getNotificationsByUser(User user, Pageable pageable);
|
||||||
|
}
|
||||||
@@ -7,11 +7,16 @@ import org.springframework.stereotype.Component;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
|
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationType;
|
||||||
import ru.soune.nocopy.repository.FileMonitoringRepository;
|
import ru.soune.nocopy.repository.FileMonitoringRepository;
|
||||||
import ru.soune.nocopy.service.monitoring.MonitoringSearchService;
|
import ru.soune.nocopy.service.monitoring.MonitoringSearchService;
|
||||||
|
import ru.soune.nocopy.service.notification.NotificationService;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@@ -22,6 +27,8 @@ public class MonitoringScheduler {
|
|||||||
|
|
||||||
private final MonitoringSearchService monitoringSearchService;
|
private final MonitoringSearchService monitoringSearchService;
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
@Scheduled(cron = "0 1 2 * * *", zone = "Europe/Moscow")
|
@Scheduled(cron = "0 1 2 * * *", zone = "Europe/Moscow")
|
||||||
@Transactional
|
@Transactional
|
||||||
public void processScheduledMonitoring() {
|
public void processScheduledMonitoring() {
|
||||||
@@ -29,6 +36,8 @@ public class MonitoringScheduler {
|
|||||||
|
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
List<FileMonitoringEntity> readyForRun = monitoringRepository.findReadyForRun(now);
|
List<FileMonitoringEntity> readyForRun = monitoringRepository.findReadyForRun(now);
|
||||||
|
Map<Long, List<FileMonitoringEntity>> groupingFilesMonitoring = readyForRun.stream()
|
||||||
|
.collect(Collectors.groupingBy(FileMonitoringEntity::getUserId));
|
||||||
|
|
||||||
log.info("Found {} files ready for monitoring", readyForRun.size());
|
log.info("Found {} files ready for monitoring", readyForRun.size());
|
||||||
|
|
||||||
@@ -43,5 +52,10 @@ public class MonitoringScheduler {
|
|||||||
monitoringRepository.save(monitoring);
|
monitoringRepository.save(monitoring);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<Long> usersForNotifications = groupingFilesMonitoring.keySet();
|
||||||
|
for (Long userId : usersForNotifications) {
|
||||||
|
notificationService.addNotification(NotificationType.MONITORING_RESULT, userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
package ru.soune.nocopy.service.complaint;
|
package ru.soune.nocopy.service.complaint;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
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;
|
||||||
@@ -13,12 +11,14 @@ import ru.soune.nocopy.dto.complaint.ComplaintResponse;
|
|||||||
import ru.soune.nocopy.entity.complaint.ComplaintEntity;
|
import ru.soune.nocopy.entity.complaint.ComplaintEntity;
|
||||||
import ru.soune.nocopy.entity.complaint.ComplaintStatus;
|
import ru.soune.nocopy.entity.complaint.ComplaintStatus;
|
||||||
import ru.soune.nocopy.entity.file.FileStatus;
|
import ru.soune.nocopy.entity.file.FileStatus;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationType;
|
||||||
import ru.soune.nocopy.entity.violation.Violation;
|
import ru.soune.nocopy.entity.violation.Violation;
|
||||||
import ru.soune.nocopy.exception.ComplaintNotFoundException;
|
import ru.soune.nocopy.exception.ComplaintNotFoundException;
|
||||||
import ru.soune.nocopy.exception.DuplicateComplaintException;
|
import ru.soune.nocopy.exception.DuplicateComplaintException;
|
||||||
import ru.soune.nocopy.exception.ViolationNotFoundException;
|
import ru.soune.nocopy.exception.ViolationNotFoundException;
|
||||||
import ru.soune.nocopy.repository.ComplaintEntityRepository;
|
import ru.soune.nocopy.repository.ComplaintEntityRepository;
|
||||||
import ru.soune.nocopy.repository.ViolationRepository;
|
import ru.soune.nocopy.repository.ViolationRepository;
|
||||||
|
import ru.soune.nocopy.service.notification.NotificationService;
|
||||||
import ru.soune.nocopy.service.violation.ViolationService;
|
import ru.soune.nocopy.service.violation.ViolationService;
|
||||||
import ru.soune.nocopy.service.violation.ViolationStatus;
|
import ru.soune.nocopy.service.violation.ViolationStatus;
|
||||||
|
|
||||||
@@ -37,6 +37,8 @@ public class ComplaintEntityService {
|
|||||||
|
|
||||||
private final ViolationService violationService;
|
private final ViolationService violationService;
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
public ComplaintResponse createComplaint(ComplaintRequest request) {
|
public ComplaintResponse createComplaint(ComplaintRequest request) {
|
||||||
@@ -101,7 +103,12 @@ public class ComplaintEntityService {
|
|||||||
}
|
}
|
||||||
if (request.getComplaintText() != null) complaint.setComplaintText(request.getComplaintText());
|
if (request.getComplaintText() != null) complaint.setComplaintText(request.getComplaintText());
|
||||||
if (request.getEmail() != null) complaint.setEmail(request.getEmail());
|
if (request.getEmail() != null) complaint.setEmail(request.getEmail());
|
||||||
if (request.getStatus() != null) complaint.setStatus(ComplaintStatus.valueOf(request.getStatus()));
|
if (request.getStatus() != null) {
|
||||||
|
complaint.setStatus(ComplaintStatus.valueOf(request.getStatus()));
|
||||||
|
|
||||||
|
notificationService.addNotification(NotificationType.COMPLAINT_STATUS_CHANGED, complaint.getViolation()
|
||||||
|
.getFileEntity().getUserId(), request.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
return mapToResponse(complaintRepository.save(complaint));
|
return mapToResponse(complaintRepository.save(complaint));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,11 +82,11 @@ public class FileEntityService {
|
|||||||
.map(User::getId)
|
.map(User::getId)
|
||||||
.toList()) {
|
.toList()) {
|
||||||
allFiles.addAll(fileEntityRepository.findByUserIdAndStatusIn(uId,
|
allFiles.addAll(fileEntityRepository.findByUserIdAndStatusIn(uId,
|
||||||
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED)));
|
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED, FileStatus.MODERATION)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
allFiles = fileEntityRepository.findByUserIdAndStatusIn(userId,
|
allFiles = fileEntityRepository.findByUserIdAndStatusIn(userId,
|
||||||
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED));
|
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED, FileStatus.MODERATION));
|
||||||
}
|
}
|
||||||
|
|
||||||
return allFiles;
|
return allFiles;
|
||||||
@@ -102,12 +102,12 @@ public class FileEntityService {
|
|||||||
.map(User::getId)
|
.map(User::getId)
|
||||||
.toList()) {
|
.toList()) {
|
||||||
allFiles.addAll(fileEntityRepository.findByUserIdAndStatusIn(uId,
|
allFiles.addAll(fileEntityRepository.findByUserIdAndStatusIn(uId,
|
||||||
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED)));
|
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED, FileStatus.MODERATION)));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
allFiles = fileEntityRepository.findByUserIdAndStatusIn(userId,
|
allFiles = fileEntityRepository.findByUserIdAndStatusIn(userId,
|
||||||
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED));
|
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED, FileStatus.MODERATION));
|
||||||
}
|
}
|
||||||
|
|
||||||
int start = (page - 1) * pageSize;
|
int start = (page - 1) * pageSize;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import ru.soune.nocopy.dto.file.UploadProgressResponse;
|
import ru.soune.nocopy.dto.file.UploadProgressResponse;
|
||||||
import ru.soune.nocopy.entity.file.*;
|
import ru.soune.nocopy.entity.file.*;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationType;
|
||||||
import ru.soune.nocopy.exception.*;
|
import ru.soune.nocopy.exception.*;
|
||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||||
import ru.soune.nocopy.repository.FileUploadSessionRepository;
|
import ru.soune.nocopy.repository.FileUploadSessionRepository;
|
||||||
@@ -29,6 +30,7 @@ import ru.soune.nocopy.service.file.FileEntityService;
|
|||||||
import ru.soune.nocopy.service.file.FileUploadService;
|
import ru.soune.nocopy.service.file.FileUploadService;
|
||||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||||
import ru.soune.nocopy.service.tariff.TariffConstants;
|
import ru.soune.nocopy.service.tariff.TariffConstants;
|
||||||
|
import ru.soune.nocopy.service.notification.NotificationService;
|
||||||
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
||||||
import ru.soune.nocopy.util.FileUtil;
|
import ru.soune.nocopy.util.FileUtil;
|
||||||
|
|
||||||
@@ -87,6 +89,8 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
|
|
||||||
private final TariffInfoService tariffInfoService;
|
private final TariffInfoService tariffInfoService;
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
private final CloudStorageService cloudStorageService;
|
private final CloudStorageService cloudStorageService;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@@ -374,6 +378,8 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
session.setStatus(UploadStatus.COMPLETED);
|
session.setStatus(UploadStatus.COMPLETED);
|
||||||
session.setFilePath(finalFilePath);
|
session.setFilePath(finalFilePath);
|
||||||
|
|
||||||
|
notificationService.addNotification(NotificationType.FILE_ADDED_TO_SYSTEM, session.getUserId());
|
||||||
|
|
||||||
FileStatus status = findSimilar == 0 ? FileStatus.MODERATION: FileStatus.TEMP;
|
FileStatus status = findSimilar == 0 ? FileStatus.MODERATION: FileStatus.TEMP;
|
||||||
|
|
||||||
if (status == FileStatus.TEMP) {
|
if (status == FileStatus.TEMP) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import ru.soune.nocopy.dto.file.*;
|
import ru.soune.nocopy.dto.file.*;
|
||||||
import ru.soune.nocopy.entity.file.*;
|
import ru.soune.nocopy.entity.file.*;
|
||||||
import ru.soune.nocopy.entity.file.moderation.ModerationLog;
|
import ru.soune.nocopy.entity.file.moderation.ModerationLog;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationType;
|
||||||
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
||||||
|
|
||||||
import ru.soune.nocopy.exception.InvalidAppealException;
|
import ru.soune.nocopy.exception.InvalidAppealException;
|
||||||
@@ -18,6 +19,7 @@ import ru.soune.nocopy.repository.FileAppealRepository;
|
|||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||||
import ru.soune.nocopy.repository.ModerationLogRepository;
|
import ru.soune.nocopy.repository.ModerationLogRepository;
|
||||||
import ru.soune.nocopy.service.file.FileEntityService;
|
import ru.soune.nocopy.service.file.FileEntityService;
|
||||||
|
import ru.soune.nocopy.service.notification.NotificationService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -35,6 +37,8 @@ public class ModerationService {
|
|||||||
|
|
||||||
private final FileEntityService fileEntityService;
|
private final FileEntityService fileEntityService;
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
private static final List<FileStatus> MODERATION_NEEDED_STATUSES = Arrays.asList(
|
private static final List<FileStatus> MODERATION_NEEDED_STATUSES = Arrays.asList(
|
||||||
FileStatus.MODERATION,
|
FileStatus.MODERATION,
|
||||||
FileStatus.BLOCKED
|
FileStatus.BLOCKED
|
||||||
@@ -84,6 +88,8 @@ public class ModerationService {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notificationService.addNotification(NotificationType.FILE_MODERATION_EVENT, file.getUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package ru.soune.nocopy.service.monitoring;
|
|||||||
import jakarta.mail.MessagingException;
|
import jakarta.mail.MessagingException;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import ru.soune.nocopy.configuration.search.SearchProperties;
|
import ru.soune.nocopy.configuration.search.SearchProperties;
|
||||||
@@ -10,6 +11,8 @@ 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.tarif.TariffInfo;
|
import ru.soune.nocopy.entity.tarif.TariffInfo;
|
||||||
import ru.soune.nocopy.entity.tarif.TariffType;
|
import ru.soune.nocopy.entity.tarif.TariffType;
|
||||||
import ru.soune.nocopy.entity.user.User;
|
import ru.soune.nocopy.entity.user.User;
|
||||||
@@ -17,6 +20,7 @@ import ru.soune.nocopy.exception.TariffNotFoundException;
|
|||||||
import ru.soune.nocopy.repository.FileMonitoringRepository;
|
import ru.soune.nocopy.repository.FileMonitoringRepository;
|
||||||
import ru.soune.nocopy.repository.UserRepository;
|
import ru.soune.nocopy.repository.UserRepository;
|
||||||
import ru.soune.nocopy.service.mail.EmailService;
|
import ru.soune.nocopy.service.mail.EmailService;
|
||||||
|
import ru.soune.nocopy.service.notification.NotificationService;
|
||||||
import ru.soune.nocopy.service.search.SearchImageService;
|
import ru.soune.nocopy.service.search.SearchImageService;
|
||||||
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
||||||
import ru.soune.nocopy.service.tariff.TariffService;
|
import ru.soune.nocopy.service.tariff.TariffService;
|
||||||
@@ -48,6 +52,10 @@ public class MonitoringSearchService {
|
|||||||
|
|
||||||
private final SearchProperties searchProperties;
|
private final SearchProperties searchProperties;
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
|
private final MessageSource messageSource;
|
||||||
|
|
||||||
@Transactional(noRollbackFor = TariffNotFoundException.class)
|
@Transactional(noRollbackFor = TariffNotFoundException.class)
|
||||||
public void processFileSearch(FileMonitoringEntity monitoring) throws MessagingException, IOException {
|
public void processFileSearch(FileMonitoringEntity monitoring) throws MessagingException, IOException {
|
||||||
monitoring.setLastRun(LocalDateTime.now());
|
monitoring.setLastRun(LocalDateTime.now());
|
||||||
@@ -123,7 +131,8 @@ public class MonitoringSearchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
monitoring.setLastRunStatus("SUCCESS");
|
monitoring.setLastRunStatus("SUCCESS");
|
||||||
|
updateNextRun(monitoring);
|
||||||
|
monitoringRepository.save(monitoring);
|
||||||
if (uniqueResults.isEmpty()) {
|
if (uniqueResults.isEmpty()) {
|
||||||
log.info("No results found for monitoring file {}", monitoring.getFile().getId());
|
log.info("No results found for monitoring file {}", monitoring.getFile().getId());
|
||||||
}
|
}
|
||||||
@@ -134,15 +143,24 @@ public class MonitoringSearchService {
|
|||||||
int currentTokens = activeTariffInfo.getTokens() + activeTariffInfo.getBoughtTokens();
|
int currentTokens = activeTariffInfo.getTokens() + activeTariffInfo.getBoughtTokens();
|
||||||
|
|
||||||
emailService.sendTokensNotFoundEmail(user, currentTokens, tariffMonitoring.getTokens());
|
emailService.sendTokensNotFoundEmail(user, currentTokens, tariffMonitoring.getTokens());
|
||||||
|
|
||||||
monitoring.setLastRunStatus("ERROR: " + e.getMessage());
|
monitoring.setLastRunStatus("ERROR: " + e.getMessage());
|
||||||
|
|
||||||
updateNextRun(monitoring);
|
updateNextRun(monitoring);
|
||||||
|
|
||||||
monitoringRepository.save(monitoring);
|
monitoringRepository.save(monitoring);
|
||||||
|
|
||||||
|
notificationService.addNotification(NotificationType.TOKEN_NOT_FOUND, user.getId(),
|
||||||
|
NotificationMessage.FILE_SEARCH.getMessageKey());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
User user = userRepository.findById(monitoring.getUserId()).orElseThrow();
|
||||||
log.error("Error processing monitoring search", e);
|
log.error("Error processing monitoring search", e);
|
||||||
monitoring.setLastRunStatus("ERROR: " + e.getMessage());
|
monitoring.setLastRunStatus("ERROR: " + e.getMessage());
|
||||||
updateNextRun(monitoring);
|
updateNextRun(monitoring);
|
||||||
|
|
||||||
monitoringRepository.save(monitoring);
|
monitoringRepository.save(monitoring);
|
||||||
|
|
||||||
|
notificationService.addNotification(NotificationType.SEARCH_OPERATION_FAILED, user.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,115 @@ package ru.soune.nocopy.service.notification;
|
|||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.MessageSource;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.soune.nocopy.entity.notification.Notification;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationStatus;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationType;
|
||||||
|
import ru.soune.nocopy.entity.user.User;
|
||||||
|
import ru.soune.nocopy.repository.NotificationRepository;
|
||||||
|
import ru.soune.nocopy.repository.UserRepository;
|
||||||
|
import ru.soune.nocopy.service.user.UserService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class NotificationService {
|
public class NotificationService {
|
||||||
|
|
||||||
|
private final NotificationRepository notificationRepository;
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
private final MessageSource messageSource;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void addNotification(NotificationType notificationType, long userId, Object... args) {
|
||||||
|
Locale locale = Locale.forLanguageTag("ru");
|
||||||
|
|
||||||
|
String message = messageSource.getMessage(notificationType.getMessageKey(), args, locale);
|
||||||
|
|
||||||
|
Notification notification = Notification.builder()
|
||||||
|
.notificationType(notificationType)
|
||||||
|
.user(userRepository.findById(userId).orElseThrow())
|
||||||
|
.status(NotificationStatus.NEW)
|
||||||
|
.message(message)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
notificationRepository.save(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteNotification(Notification notification) {
|
||||||
|
notificationRepository.delete(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Получение всех нотификаций с сортировкой
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Page<Notification> allUserNotifications(User user, int page, int size, String propertyToSort) {
|
||||||
|
Pageable pageable = PageRequest.of(page, size, Sort.by(propertyToSort).descending());
|
||||||
|
|
||||||
|
return notificationRepository.getNotificationsByUser(user, pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<Notification> getAllNotifications(User user,
|
||||||
|
int page,
|
||||||
|
int size,
|
||||||
|
String sortBy,
|
||||||
|
String sortDirection,
|
||||||
|
List<NotificationType> types,
|
||||||
|
List<NotificationStatus> statuses) {
|
||||||
|
Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), sortBy);
|
||||||
|
PageRequest pageable = PageRequest.of(page, size, sort);
|
||||||
|
|
||||||
|
boolean hasTypes = types != null && !types.isEmpty();
|
||||||
|
boolean hasStatuses = statuses != null && !statuses.isEmpty();
|
||||||
|
|
||||||
|
if (hasTypes && hasStatuses) {
|
||||||
|
return notificationRepository.findByUserAndNotificationTypeInAndStatusIn(user, types, statuses, pageable);
|
||||||
|
} else if (hasTypes) {
|
||||||
|
return notificationRepository.findByUserAndNotificationTypeIn(user, types, pageable);
|
||||||
|
} else if (hasStatuses) {
|
||||||
|
return notificationRepository.findByUserAndStatusIn(user, statuses, pageable);
|
||||||
|
} else {
|
||||||
|
return notificationRepository.findByUser(user, pageable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 5 последних непрочитанных уведомлений.
|
||||||
|
*/
|
||||||
|
public List<Notification> getActiveNotifications(User user) {
|
||||||
|
return notificationRepository.findTop5ByUserAndStatusOrderByCreatedAtDesc(user, NotificationStatus.NEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Общее количество непрочитанных уведомлений.
|
||||||
|
*/
|
||||||
|
public long getUnreadCount(User user) {
|
||||||
|
return notificationRepository.countByUserAndStatus(user, NotificationStatus.NEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Пометить выбранные уведомления как прочитанные.
|
||||||
|
* @return количество обновлённых записей
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public int markAsRead(User user, List<Long> notificationIds) {
|
||||||
|
if (notificationIds == null || notificationIds.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return notificationRepository.updateStatus(notificationIds, user, NotificationStatus.NEW,
|
||||||
|
NotificationStatus.READIED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.soune.ReferralRepo;
|
||||||
import ru.soune.ReferralService;
|
import ru.soune.ReferralService;
|
||||||
import ru.soune.nocopy.client.YooKassaClient;
|
import ru.soune.nocopy.client.YooKassaClient;
|
||||||
import ru.soune.nocopy.dto.payment.PaymentMethodDTO;
|
import ru.soune.nocopy.dto.payment.PaymentMethodDTO;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationType;
|
||||||
import ru.soune.nocopy.entity.payment.Payment;
|
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;
|
||||||
@@ -19,6 +21,7 @@ import ru.soune.nocopy.exception.PaymentNotFoundException;
|
|||||||
import ru.soune.nocopy.exception.TariffNotFoundException;
|
import ru.soune.nocopy.exception.TariffNotFoundException;
|
||||||
import ru.soune.nocopy.exception.UserNotFoundException;
|
import ru.soune.nocopy.exception.UserNotFoundException;
|
||||||
import ru.soune.nocopy.repository.*;
|
import ru.soune.nocopy.repository.*;
|
||||||
|
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;
|
||||||
@@ -46,6 +49,10 @@ public class PaymentService {
|
|||||||
|
|
||||||
private final YooKassaClient yooKassaClient;
|
private final YooKassaClient yooKassaClient;
|
||||||
|
|
||||||
|
private final ReferralRepo referralRepo;
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Payment createPayment(String email, Long tariffId, String operationType, String paymentUuid) {
|
public Payment createPayment(String email, Long tariffId, String operationType, String paymentUuid) {
|
||||||
User user = userRepository.findByEmail(email);
|
User user = userRepository.findByEmail(email);
|
||||||
@@ -158,15 +165,19 @@ public class PaymentService {
|
|||||||
savePaymentMethod(payment.getUser(), paymentMethod);
|
savePaymentMethod(payment.getUser(), paymentMethod);
|
||||||
|
|
||||||
referralService.onUserAccountRefill(payment.getUser().getId(), intPrice);
|
referralService.onUserAccountRefill(payment.getUser().getId(), intPrice);
|
||||||
|
|
||||||
|
// long inviterIdForUser = referralRepo.getInviterIdForUser(payment.getUser().getId());
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
Map<String, Object> cancellationDetails = (Map<String, Object>) object.get("cancellation_details");
|
Map<String, Object> cancellationDetails = (Map<String, Object>) object.get("cancellation_details");
|
||||||
if (cancellationDetails != null) {
|
|
||||||
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);
|
||||||
} 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);
|
||||||
@@ -175,10 +186,11 @@ public class PaymentService {
|
|||||||
payment.setStatus(PaymentStatus.FAILED);
|
payment.setStatus(PaymentStatus.FAILED);
|
||||||
|
|
||||||
Map<String, Object> cancellationDetails = (Map<String, Object>) object.get("cancellation_details");
|
Map<String, Object> cancellationDetails = (Map<String, Object>) object.get("cancellation_details");
|
||||||
if (cancellationDetails != null) {
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
paymentRepository.save(payment);
|
paymentRepository.save(payment);
|
||||||
|
|||||||
@@ -11,9 +11,11 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import ru.soune.Referral;
|
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.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;
|
||||||
|
import ru.soune.nocopy.service.notification.NotificationService;
|
||||||
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
@@ -31,6 +33,8 @@ public class ReferralRepoImpl implements ReferralRepo {
|
|||||||
|
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull String getUserReferralLink(long userId) {
|
public @NotNull String getUserReferralLink(long userId) {
|
||||||
ru.soune.nocopy.entity.referral.Referral referral =
|
ru.soune.nocopy.entity.referral.Referral referral =
|
||||||
@@ -150,6 +154,8 @@ public class ReferralRepoImpl implements ReferralRepo {
|
|||||||
@Override
|
@Override
|
||||||
public void createReferralEntity(@NotNull Referral entity) {
|
public void createReferralEntity(@NotNull Referral entity) {
|
||||||
ru.soune.nocopy.entity.referral.Referral referral = new ru.soune.nocopy.entity.referral.Referral();
|
ru.soune.nocopy.entity.referral.Referral referral = new ru.soune.nocopy.entity.referral.Referral();
|
||||||
|
Long inviter = entity.getInviter();
|
||||||
|
|
||||||
referral.setUserId(entity.getUserId());
|
referral.setUserId(entity.getUserId());
|
||||||
referral.setReferralLink(entity.getReferralLink());
|
referral.setReferralLink(entity.getReferralLink());
|
||||||
referral.setInviterId(entity.getInviter());
|
referral.setInviterId(entity.getInviter());
|
||||||
@@ -160,6 +166,10 @@ public class ReferralRepoImpl implements ReferralRepo {
|
|||||||
referral.setActive(entity.getActive());
|
referral.setActive(entity.getActive());
|
||||||
|
|
||||||
referralRepository.save(referral);
|
referralRepository.save(referral);
|
||||||
|
|
||||||
|
if (inviter != null){
|
||||||
|
notificationService.addNotification(NotificationType.REFERRAL_REGISTERED, inviter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -106,6 +106,26 @@ public class AuthService {
|
|||||||
return authToken.getUser().getId();
|
return authToken.getUser().getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public User getUser(String token) {
|
||||||
|
if (token == null || token.isBlank()) {
|
||||||
|
throw new NotFoundAuthToken("Token is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.startsWith("Bearer ")) {
|
||||||
|
token = token.replace("Bearer ", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthToken authToken = authTokenRepository.findByToken(token)
|
||||||
|
.orElseThrow(() -> new NotFoundAuthToken("Token not found"));
|
||||||
|
authToken.setLastUsedAt(LocalDateTime.now());
|
||||||
|
authToken.setExpiresAt(LocalDateTime.now().plusHours(1));
|
||||||
|
|
||||||
|
authTokenRepository.save(authToken);
|
||||||
|
|
||||||
|
return authToken.getUser();
|
||||||
|
}
|
||||||
|
|
||||||
public AuthToken login(LoginRequest request) {
|
public AuthToken login(LoginRequest request) {
|
||||||
User user = userRepository.findByEmail(request.getEmail());
|
User user = userRepository.findByEmail(request.getEmail());
|
||||||
|
|
||||||
@@ -161,7 +181,7 @@ public class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public AuthToken getAuthToken(String token) {
|
public AuthToken getAuthToken(String token) {
|
||||||
return authTokenRepository.findByToken(token).get();
|
return authTokenRepository.findByToken(token).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthToken genereateAuthToken(User user) {
|
private AuthToken genereateAuthToken(User user) {
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import ru.soune.nocopy.configuration.search.SearchProperties;
|
import ru.soune.nocopy.configuration.search.SearchProperties;
|
||||||
import ru.soune.nocopy.dto.file.YandexSearchResponse;
|
import ru.soune.nocopy.dto.file.YandexSearchResponse;
|
||||||
import ru.soune.nocopy.entity.file.FileEntity;
|
import ru.soune.nocopy.entity.file.FileEntity;
|
||||||
|
import ru.soune.nocopy.entity.notification.NotificationType;
|
||||||
import ru.soune.nocopy.entity.search.GlobalSearchResult;
|
import ru.soune.nocopy.entity.search.GlobalSearchResult;
|
||||||
import ru.soune.nocopy.entity.search.GlobalSearchTask;
|
import ru.soune.nocopy.entity.search.GlobalSearchTask;
|
||||||
import ru.soune.nocopy.entity.search.SearchStatus;
|
import ru.soune.nocopy.entity.search.SearchStatus;
|
||||||
|
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||||
import ru.soune.nocopy.repository.GlobalSearchResultRepository;
|
import ru.soune.nocopy.repository.GlobalSearchResultRepository;
|
||||||
import ru.soune.nocopy.repository.GlobalSearchTaskRepository;
|
import ru.soune.nocopy.repository.GlobalSearchTaskRepository;
|
||||||
|
import ru.soune.nocopy.service.notification.NotificationService;
|
||||||
import ru.soune.nocopy.service.tariff.TariffConstants;
|
import ru.soune.nocopy.service.tariff.TariffConstants;
|
||||||
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
||||||
import ru.soune.nocopy.service.violation.ViolationService;
|
import ru.soune.nocopy.service.violation.ViolationService;
|
||||||
@@ -41,31 +44,38 @@ public class GlobalSearchAsyncProcessor {
|
|||||||
|
|
||||||
private final SearchProperties searchProperties;
|
private final SearchProperties searchProperties;
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
|
private final FileEntityRepository fileEntityRepository;
|
||||||
|
|
||||||
@Async
|
@Async
|
||||||
@Transactional
|
@Transactional
|
||||||
public void processFilesAsync(String taskId, List<FileEntity> filesToProcess, Long userId) {
|
public void processFilesAsync(String taskId, List<String> filesToProcess, Long userId) {
|
||||||
|
GlobalSearchTask task = globalSearchTaskRepository.findById(taskId).orElseThrow();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (int i = 0; i < filesToProcess.size(); i++) {
|
for (int i = 0; i < filesToProcess.size(); i++) {
|
||||||
FileEntity file = filesToProcess.get(i);
|
String uuid = filesToProcess.get(i);
|
||||||
|
FileEntity file = fileEntityRepository.findById(uuid)
|
||||||
|
.orElseThrow(() -> new RuntimeException("File not found: " + uuid));
|
||||||
|
|
||||||
tariffInfoService.writeOffTokens(userId, TariffConstants.TOKEN_VALUE_FOR_SEARCH);
|
tariffInfoService.writeOffTokens(userId, TariffConstants.TOKEN_VALUE_FOR_SEARCH);
|
||||||
GlobalSearchResult result = processFile(file, taskId, userId);
|
GlobalSearchResult result = processFile(file, taskId, userId);
|
||||||
globalSearchResultRepository.save(result);
|
globalSearchResultRepository.save(result);
|
||||||
|
|
||||||
GlobalSearchTask task = globalSearchTaskRepository.findById(taskId).orElseThrow();
|
|
||||||
task.setProcessedFiles(i + 1);
|
task.setProcessedFiles(i + 1);
|
||||||
task.setUpdatedAt(LocalDateTime.now());
|
task.setUpdatedAt(LocalDateTime.now());
|
||||||
globalSearchTaskRepository.save(task);
|
globalSearchTaskRepository.save(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalSearchTask task = globalSearchTaskRepository.findById(taskId).orElseThrow();
|
|
||||||
task.setStatus(SearchStatus.COMPLETED.name());
|
task.setStatus(SearchStatus.COMPLETED.name());
|
||||||
task.setUpdatedAt(LocalDateTime.now());
|
task.setUpdatedAt(LocalDateTime.now());
|
||||||
globalSearchTaskRepository.save(task);
|
globalSearchTaskRepository.save(task);
|
||||||
|
|
||||||
|
notificationService.addNotification(NotificationType.SEARCH_RESULT, 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);
|
||||||
|
|
||||||
GlobalSearchTask task = globalSearchTaskRepository.findById(taskId).orElseThrow();
|
|
||||||
task.setStatus(SearchStatus.FAILED.name());
|
task.setStatus(SearchStatus.FAILED.name());
|
||||||
task.setUpdatedAt(LocalDateTime.now());
|
task.setUpdatedAt(LocalDateTime.now());
|
||||||
globalSearchTaskRepository.save(task);
|
globalSearchTaskRepository.save(task);
|
||||||
|
|||||||
@@ -46,7 +46,9 @@ public class GlobalSearchService {
|
|||||||
|
|
||||||
globalSearchTaskRepository.save(task);
|
globalSearchTaskRepository.save(task);
|
||||||
|
|
||||||
asyncProcessor.processFilesAsync(task.getTaskId(), filesToProcess, userId);
|
List<String> fileIds = filesToProcess.stream().map(FileEntity::getId).toList();
|
||||||
|
|
||||||
|
asyncProcessor.processFilesAsync(task.getTaskId(), fileIds, userId);
|
||||||
|
|
||||||
return task.getTaskId();
|
return task.getTaskId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
notification.search.result=Результат поиска готов
|
||||||
|
notification.monitoring.result=Результат мониторинга доступен
|
||||||
|
notification.operation.impossible=Операция невозможна
|
||||||
|
notification.start.failed.now=Запуск не удался
|
||||||
|
notification.start.failed.next=Запуск не удался
|
||||||
|
notification.payout.result=Результат выплаты {0}
|
||||||
|
notification.payment.result=Результат оплаты {0}
|
||||||
|
notification.referral.deposit=Реферальный депозит
|
||||||
|
notification.referral.registered=Новый реферал зарегистрирован
|
||||||
|
notification.referral.activated=Реферал активирован
|
||||||
|
notification.complaint.status=Статус жалобы изменён: {0}
|
||||||
|
notification.case.status=Статус дела изменён
|
||||||
|
notification.incoming.message=Новое сообщение
|
||||||
|
notification.tariff.expiring=Тариф скоро истекает
|
||||||
|
notification.token.not.found=Недостаточно токенов для операции: {0}
|
||||||
|
notification.file.moderation=Файл проходит модерацию
|
||||||
|
notification.file.added=Файл {0} добавлен в систему
|
||||||
|
notification.file.search=Поиск файла
|
||||||
|
notification.search.operation.filed=Поиск завершился непредвиденной ошибкой
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
notification.search.result=Search result is ready
|
||||||
|
notification.monitoring.result=Monitoring result is available
|
||||||
|
notification.operation.impossible=Operation impossible
|
||||||
|
notification.start.failed.now=Start failed
|
||||||
|
notification.start.failed.next=Start failed
|
||||||
|
notification.payout.result=Payout result
|
||||||
|
notification.referral.deposit=Referral deposit
|
||||||
|
notification.referral.registered=New referral registered
|
||||||
|
notification.referral.activated=Referral activated
|
||||||
|
notification.complaint.status=Complaint status changed
|
||||||
|
notification.case.status=Case status changed
|
||||||
|
notification.incoming.message=New message
|
||||||
|
notification.tariff.expiring=Tariff expiring soon
|
||||||
|
notification.file.moderation=File is being moderated
|
||||||
|
notification.file.added=File {0} added to the system
|
||||||
Reference in New Issue
Block a user