package ru.soune.nocopy.service.violation; import lombok.AllArgsConstructor; import org.apache.commons.codec.digest.DigestUtils; 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 ru.soune.nocopy.dto.file.YandexSearchResponse; import ru.soune.nocopy.dto.violation.ViolationStatisticsResponse; import ru.soune.nocopy.entity.file.FileEntity; import ru.soune.nocopy.entity.search.GlobalSearchResult; import ru.soune.nocopy.entity.violation.Violation; import ru.soune.nocopy.exception.ViolationNotFoundException; import ru.soune.nocopy.repository.FileEntityRepository; import ru.soune.nocopy.repository.ViolationRepository; import ru.soune.nocopy.service.file.FileEntityService; import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Service @AllArgsConstructor public class ViolationService { private final ViolationRepository violationRepository; private final FileEntityRepository fileEntityRepository; private final FileEntityService fileEntityService; public void processViolation(YandexSearchResponse.ImageResult imageResult, FileEntity file, GlobalSearchResult result) { String url = imageResult.getUrl(); String urlHash = DigestUtils.sha256Hex(url); if (!violationRepository.existsByUrlHash(urlHash)) { Violation violation = new Violation(); if (result != null) { violation.setGlobalSearchResult(result); } violation.setHost(imageResult.getHost()); violation.setUrl(url); violation.setUrlHash(urlHash); violation.setPageUrl(imageResult.getPageUrl()); violation.setPageTitle(imageResult.getPageTitle()); violation.setFileEntity(file); violation.setStatus("CREATED"); violationRepository.save(violation); } } public List violationsByFileId(FileEntity file) { return violationRepository.findByFileEntity(file); } public void changeStatusViolation(Long violationId, String status) { Violation violation = violationRepository.findById(violationId) .orElseThrow(() -> new ViolationNotFoundException("Violation not found with id: " + violationId)); violation.setStatus(status); violationRepository.save(violation); } public Page getViolationsByFile(FileEntity file, int page, int size, String sortDirection) { Sort sort = Sort.by(sortDirection.equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC, "createdDate"); Pageable pageable = PageRequest.of(page, size, sort); return violationRepository.findByFileEntity(file, pageable); } public Page getViolationsByFileAndStatus(FileEntity file, String status, int page, int size, String sortDirection) { Sort sort = Sort.by(sortDirection.equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC, "createdDate"); Pageable pageable = PageRequest.of(page, size, sort); return violationRepository.findByFileEntityAndStatus(file, status, pageable); } public Page getViolationsByFileAndDateRange(FileEntity file, LocalDateTime startDate, LocalDateTime endDate, int page, int size, String sortDirection) { Sort sort = Sort.by(sortDirection.equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC, "createdDate"); Pageable pageable = PageRequest.of(page, size, sort); return violationRepository.findByFileEntityAndCreatedDateBetween(file, startDate, endDate, pageable); } public Map getGroupedViolations(FileEntity file, String groupBy, String status, LocalDateTime startDate, LocalDateTime endDate) { List violations; if (status != null && !status.isEmpty() && startDate != null && endDate != null) { violations = violationRepository.findByFileEntityAndStatusAndCreatedDateBetween(file, status, startDate, endDate); } else if (status != null && !status.isEmpty()) { violations = violationRepository.findByFileEntityAndStatus(file, status); } else if (startDate != null && endDate != null) { violations = violationRepository.findByFileEntityAndCreatedDateBetween(file, startDate, endDate); } else { violations = violationRepository.findByFileEntity(file); } return violations.stream() .collect(Collectors.groupingBy( v -> extractGroupKey(v.getPageUrl(), groupBy), Collectors.counting() )); } 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"; } } public ViolationStatisticsResponse getViolationStatistics(Long userId, String fileId, LocalDateTime startDate, LocalDateTime endDate) { List 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 { userFiles = fileEntityService.getAllUserFiles(userId, 1); } if (userFiles.isEmpty()) { return ViolationStatisticsResponse.builder() .totalViolations(0) .newViolations(0) .inProgressViolations(0) .resolvedViolations(0) .build(); } List violations; if (startDate != null && endDate != null) { violations = violationRepository.findByFileEntityInAndCreatedDateBetween(userFiles, startDate, endDate); } else { violations = violationRepository.findByFileEntityIn(userFiles); } long newViolations = violations.stream() .filter(v -> "CREATED".equalsIgnoreCase(v.getStatus()) || "NEW".equalsIgnoreCase(v.getStatus())) .count(); long inProgressViolations = violations.stream() .filter(v -> "IN_PROGRESS".equalsIgnoreCase(v.getStatus()) || "PROCESSING".equalsIgnoreCase(v.getStatus()) || "IN_WORK".equalsIgnoreCase(v.getStatus())) .count(); long resolvedViolations = violations.stream() .filter(v -> "RESOLVED".equalsIgnoreCase(v.getStatus()) || "COMPLETED".equalsIgnoreCase(v.getStatus()) || "SOLVED".equalsIgnoreCase(v.getStatus()) || "DONE".equalsIgnoreCase(v.getStatus())) .count(); return ViolationStatisticsResponse.builder() .totalViolations(violations.size()) .newViolations(newViolations) .inProgressViolations(inProgressViolations) .resolvedViolations(resolvedViolations) .build(); } }