Files
no-copy/src/main/java/ru/soune/nocopy/service/violation/ViolationService.java
T

266 lines
12 KiB
Java
Raw Normal View History

2026-03-10 22:47:43 +07:00
package ru.soune.nocopy.service.violation;
import lombok.AllArgsConstructor;
2026-03-11 15:42:06 +07:00
import org.apache.commons.codec.digest.DigestUtils;
2026-04-28 13:30:00 +07:00
import org.springframework.data.domain.*;
2026-03-10 22:47:43 +07:00
import org.springframework.stereotype.Service;
2026-03-26 14:34:34 +07:00
import org.springframework.transaction.annotation.Transactional;
2026-03-10 22:47:43 +07:00
import ru.soune.nocopy.dto.file.YandexSearchResponse;
2026-03-13 13:55:10 +07:00
import ru.soune.nocopy.dto.violation.FileViolationSummaryDTO;
2026-03-12 00:21:19 +07:00
import ru.soune.nocopy.dto.violation.ViolationStatisticsResponse;
2026-03-10 22:47:43 +07:00
import ru.soune.nocopy.entity.file.FileEntity;
2026-04-28 13:30:00 +07:00
import ru.soune.nocopy.entity.file.FileStatus;
2026-03-10 22:47:43 +07:00
import ru.soune.nocopy.entity.search.GlobalSearchResult;
import ru.soune.nocopy.entity.violation.Violation;
2026-04-28 13:30:00 +07:00
import ru.soune.nocopy.repository.ComplaintEntityRepository;
2026-03-12 00:21:19 +07:00
import ru.soune.nocopy.repository.FileEntityRepository;
2026-04-28 13:30:00 +07:00
import ru.soune.nocopy.repository.LawCaseRepository;
2026-03-10 22:47:43 +07:00
import ru.soune.nocopy.repository.ViolationRepository;
2026-03-12 00:41:04 +07:00
import ru.soune.nocopy.service.file.FileEntityService;
2026-03-10 22:47:43 +07:00
import java.time.LocalDateTime;
2026-03-13 14:14:37 +07:00
import java.util.*;
2026-03-10 22:47:43 +07:00
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
public class ViolationService {
private final ViolationRepository violationRepository;
2026-03-12 00:21:19 +07:00
private final FileEntityRepository fileEntityRepository;
2026-03-12 00:41:04 +07:00
private final FileEntityService fileEntityService;
2026-04-28 13:30:00 +07:00
private final ComplaintEntityRepository complaintRepository;
private final LawCaseRepository lawCaseRepository;
2026-03-10 22:47:43 +07:00
public void processViolation(YandexSearchResponse.ImageResult imageResult, FileEntity file,
GlobalSearchResult result) {
String url = imageResult.getUrl();
2026-03-11 15:42:06 +07:00
String urlHash = DigestUtils.sha256Hex(url);
2026-03-10 22:47:43 +07:00
2026-03-11 15:42:06 +07:00
if (!violationRepository.existsByUrlHash(urlHash)) {
2026-03-10 22:47:43 +07:00
Violation violation = new Violation();
if (result != null) {
violation.setGlobalSearchResult(result);
}
violation.setHost(imageResult.getHost());
violation.setUrl(url);
2026-03-11 15:42:06 +07:00
violation.setUrlHash(urlHash);
2026-03-10 22:47:43 +07:00
violation.setPageUrl(imageResult.getPageUrl());
violation.setPageTitle(imageResult.getPageTitle());
violation.setFileEntity(file);
2026-04-28 09:06:38 +07:00
violation.setCreatedDate(LocalDateTime.now());
2026-03-26 14:34:34 +07:00
violation.setStatus(ViolationStatus.NEW.name());
2026-03-10 22:47:43 +07:00
violationRepository.save(violation);
}
}
2026-04-22 12:16:37 +07:00
public Violation getById(Long violationId) {
return violationRepository.findById(violationId).orElse(null);
}
2026-04-28 13:30:00 +07:00
public Page<FileViolationSummaryDTO> getFileViolationsSummary(Long userId, String fileName, String sortDirection, Integer page,
Integer size, LocalDateTime start, LocalDateTime end) {
List<FileEntity> userFiles = fileName == null || fileName.isEmpty() ? fileEntityService.getAllUserFiles(userId)
: fileEntityService.getAllUserFilesWithFileName(userId, fileName,
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED, FileStatus.MODERATION));
2026-03-13 13:48:03 +07:00
2026-04-28 13:30:00 +07:00
Sort sort = Sort.by(sortDirection.equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC,
"createdDate");
Pageable pageable = PageRequest.of(page, size, sort);
Page<Violation> violations = violationRepository.findByFileEntityInAndStatusNotAndCreatedDateBetween(
userFiles, ViolationStatus.NOT_OWNER_FILE.name(), start, end, pageable);
Map<String, List<Violation>> violationsByFileId = violations.getContent().stream()
2026-03-13 13:48:03 +07:00
.collect(Collectors.groupingBy(v -> v.getFileEntity().getId()));
2026-04-28 13:30:00 +07:00
List<FileViolationSummaryDTO> dtoList = violationsByFileId.entrySet().stream()
.map(entry -> {
List<Violation> fileViolations = entry.getValue();
FileEntity file = fileViolations.get(0).getFileEntity();
2026-03-13 14:14:37 +07:00
2026-03-13 13:55:10 +07:00
LocalDateTime latestDate = fileViolations.stream()
.map(Violation::getCreatedDate)
.max(LocalDateTime::compareTo)
.orElse(null);
2026-03-13 13:48:03 +07:00
2026-03-13 13:55:10 +07:00
return FileViolationSummaryDTO.builder()
.fileId(file.getId())
.supportId(file.getSupportId())
.fileName(file.getOriginalFileName())
.fileSize(file.getFileSize())
.mimeType(file.getMimeType())
.status(file.getStatus())
.createdAt(file.getCreatedAt())
.violationCount(fileViolations.size())
.latestViolationDate(latestDate)
.build();
})
.collect(Collectors.toList());
2026-04-28 13:30:00 +07:00
return new PageImpl<>(dtoList, pageable, violations.getTotalElements());
2026-03-13 13:48:03 +07:00
}
2026-03-26 14:34:34 +07:00
@Transactional
public void changeStatus(ViolationStatus status, Long violationId) {
Violation violation = violationRepository.findById(violationId).orElseThrow();
violation.setStatus(status.name());
violationRepository.save(violation);
}
2026-03-18 17:07:44 +07:00
public Page<Violation> getViolationsByFiles(List<FileEntity> files, int page, int size, String sortDirection) {
2026-04-24 13:11:49 +07:00
Sort sort = Sort.by(sortDirection.equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC,
"createdDate");
2026-03-18 17:07:44 +07:00
Pageable pageable = PageRequest.of(page, size, sort);
2026-04-24 13:11:49 +07:00
return violationRepository.findByFileEntityInAndStatusNot(files, ViolationStatus.NOT_OWNER_FILE.name(), pageable);
2026-03-18 17:07:44 +07:00
}
public Page<Violation> getViolationsByFilesAndStatus(List<FileEntity> files, String status, int page, int size, String sortDirection) {
2026-04-24 13:11:49 +07:00
Sort sort = Sort.by(sortDirection.equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC,
"createdDate");
2026-03-18 17:07:44 +07:00
Pageable pageable = PageRequest.of(page, size, sort);
2026-04-24 13:11:49 +07:00
2026-03-18 17:07:44 +07:00
return violationRepository.findByFileEntityInAndStatus(files, status, pageable);
}
public Page<Violation> getViolationsByFilesAndDateRange(List<FileEntity> files, LocalDateTime startDate, LocalDateTime endDate,
int page, int size, String sortDirection) {
2026-04-24 13:11:49 +07:00
Sort sort = Sort.by(sortDirection.equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC,
"createdDate");
2026-03-18 17:07:44 +07:00
Pageable pageable = PageRequest.of(page, size, sort);
2026-04-24 13:11:49 +07:00
return violationRepository.findByFileEntityInAndStatusNotAndCreatedDateBetween(files,
ViolationStatus.NOT_OWNER_FILE.name(), startDate, endDate, pageable);
2026-03-18 17:07:44 +07:00
}
2026-03-18 17:55:14 +07:00
public Map<String, Long> getGroupedViolations(List<FileEntity> files, String groupBy, String status,
LocalDateTime startDate, LocalDateTime endDate,
String sortDirection) {
List<Violation> violations;
if (status != null && !status.isEmpty() && startDate != null && endDate != null) {
2026-04-24 13:24:50 +07:00
violations = violationRepository.findByFileEntityInAndStatusAndCreatedDateBetween(files, status,
startDate, endDate);
2026-03-18 17:55:14 +07:00
} else if (status != null && !status.isEmpty()) {
2026-03-18 18:21:33 +07:00
violations = violationRepository.findByFileEntityInAndStatus(files, status);
2026-03-18 17:55:14 +07:00
} else if (startDate != null && endDate != null) {
2026-04-24 13:24:50 +07:00
violations = violationRepository.findByFileEntityInAndCreatedDateBetweenAndStatusNot(files, startDate,
endDate, ViolationStatus.NOT_OWNER_FILE.name());
2026-03-18 17:55:14 +07:00
} else {
2026-04-24 13:24:50 +07:00
violations = violationRepository.findByFileEntityInAndStatusNot(files,
ViolationStatus.NOT_OWNER_FILE.name());
2026-03-18 17:55:14 +07:00
}
2026-03-18 18:21:33 +07:00
Map<String, Long> grouped = violations.stream()
2026-03-18 17:55:14 +07:00
.collect(Collectors.groupingBy(
v -> extractGroupKey(v.getPageUrl(), groupBy),
Collectors.counting()));
2026-03-18 18:21:33 +07:00
return grouped.entrySet().stream()
.sorted((e1, e2) -> {
if (sortDirection.equalsIgnoreCase("desc")) {
return e2.getValue().compareTo(e1.getValue());
} else {
return e1.getValue().compareTo(e2.getValue());
}
})
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new));
2026-03-18 17:07:44 +07:00
}
2026-03-12 00:21:19 +07:00
public ViolationStatisticsResponse getViolationStatistics(Long userId, String fileId,
LocalDateTime startDate, LocalDateTime endDate) {
List<FileEntity> userFiles;
if (fileId != null && !fileId.isEmpty()) {
FileEntity file = fileEntityRepository.findById(fileId)
.orElseThrow(() -> new IllegalArgumentException("File not found with id: " + fileId));
if (!file.getUserId().equals(userId)) {
throw new IllegalArgumentException("File does not belong to user");
}
userFiles = List.of(file);
} else {
2026-03-12 02:52:38 +07:00
userFiles = fileEntityService.getAllUserFiles(userId);
2026-03-12 00:21:19 +07:00
}
if (userFiles.isEmpty()) {
return ViolationStatisticsResponse.builder()
.totalViolations(0)
.newViolations(0)
.inProgressViolations(0)
.resolvedViolations(0)
.build();
}
List<Violation> violations;
if (startDate != null && endDate != null) {
2026-04-24 13:36:30 +07:00
violations = violationRepository.findByFileEntityInAndCreatedDateBetweenAndStatusNot(userFiles,
startDate, endDate, ViolationStatus.NOT_OWNER_FILE.name());
2026-03-12 00:21:19 +07:00
} else {
2026-04-24 13:36:30 +07:00
violations = violationRepository.findByFileEntityInAndStatusNot(userFiles,
ViolationStatus.NOT_OWNER_FILE.name());
2026-03-12 00:21:19 +07:00
}
2026-03-26 14:42:38 +07:00
Set<String> inProgressStatuses = Set.of(
ViolationStatus.LEGAL_IN_WORK.name(),
ViolationStatus.COMPLAINT_IN_WORK.name(),
ViolationStatus.COMPLAINT_AND_LEGAL_IN_WORK.name()
);
2026-03-12 00:21:19 +07:00
long newViolations = violations.stream()
2026-03-26 14:42:38 +07:00
.filter(v -> v.getStatus().equals(ViolationStatus.NEW.name()))
2026-03-12 00:21:19 +07:00
.count();
long inProgressViolations = violations.stream()
2026-03-26 14:42:38 +07:00
.filter(v -> inProgressStatuses.contains(v.getStatus()))
2026-03-12 00:21:19 +07:00
.count();
long resolvedViolations = violations.stream()
2026-03-26 14:42:38 +07:00
.filter(v -> v.getStatus().equals(ViolationStatus.AUTHORIZED_USE.name()))
2026-03-12 00:21:19 +07:00
.count();
2026-04-28 13:30:00 +07:00
List<Long> violationIds = violations.stream().map(Violation::getId).toList();
int totalComplaints = complaintRepository.countComplaintByViolations(violationIds);
int totalLawCases = lawCaseRepository.countLawCaseByViolationIds(violationIds);
2026-03-12 00:21:19 +07:00
return ViolationStatisticsResponse.builder()
.totalViolations(violations.size())
.newViolations(newViolations)
.inProgressViolations(inProgressViolations)
.resolvedViolations(resolvedViolations)
2026-04-28 13:30:00 +07:00
.totalComplaints(totalComplaints)
.totalLawCases(totalLawCases)
2026-03-12 00:21:19 +07:00
.build();
}
2026-03-13 13:48:03 +07:00
private String extractGroupKey(String url, String groupBy) {
try {
String domain = url.replaceAll("https?://(www\\.)?", "").split("/")[0];
if ("tld".equalsIgnoreCase(groupBy)) {
int lastDot = domain.lastIndexOf('.');
return lastDot > 0 ? domain.substring(lastDot + 1) : domain;
}
return domain;
} catch (Exception e) {
return "unknown";
}
}
2026-03-10 22:47:43 +07:00
}