dev delete config
Test Workflow / test (push) Waiting to run

This commit is contained in:
vladp
2026-02-17 13:57:38 +07:00
parent 8147a6ce49
commit 78698d2873
2 changed files with 235 additions and 127 deletions
@@ -1,5 +1,10 @@
package ru.soune.nocopy.controller; package ru.soune.nocopy.controller;
import com.vrt.NoCopyFileService;
import com.vrt.fileprotection.FileProtector;
import com.vrt.fileprotection.NoCopyCheckResult;
import com.vrt.fileprotection.audio.AudioCheckResult;
import com.vrt.fileprotection.documents.DocumentCheckResult;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
@@ -33,14 +38,12 @@ 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.util.FileUtil; import ru.soune.nocopy.util.FileUtil;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
@@ -152,40 +155,179 @@ public class ApiController {
} }
} }
private final NoCopyFileService noCopyFileService;
// @GetMapping("/v{version}/files/{fileId}/similar")
// public ResponseEntity<BaseResponse> findSimilarFiles(
// @PathVariable("version") int version,
// @PathVariable String fileId,
// @RequestParam(value = "auth_token",required = false) String authToken,
// @RequestParam(required = false) List<String> similarityLevels,
// @PageableDefault(size = 20, sort = "hammingDistance") Pageable pageable) throws Exception {
// SimilarityFilter filter = SimilarityFilter.builder()
// .similarityLevels(similarityLevels)
// .build();
// FileEntity fileEntity = fileEntityRepository.findByFileId(fileId);
// Page<SimilarFileDTO> similarFiles = null;
// List<SimilarFileDTO> allResults = null;
// List<String> errors = new ArrayList<>();
//
// if (fileEntity.getMimeType().equals("image")) {
// similarFiles = fileSimilarityService.findSimilarFiles(fileId, filter, pageable, authToken);
// } else {
// File file = new File(fileEntity.getFilePath());
// FileProtector.Type type = fileEntity.getMimeType().equals("document") ? FileProtector.Type.DOC :
// FileProtector.Type.AUDIO;
//
// NoCopyCheckResult noCopyCheckResult = noCopyFileService.checkFile(file, type);
// FileEntity entity = fileEntityRepository.findByFileId(fileId);
//
// switch (noCopyCheckResult) {
// case DocumentCheckResult.Success success -> {
// FileProtector.FileInfo info = success.getInfo();
// allResults.add(fileSimilarityService.buildDTO(fileEntityRepository.findByFileId(info.getId())));
// log.debug("File processed successfully: {}", entity.getFilePath());
// }
// case AudioCheckResult.Success success -> {
// FileProtector.FileInfo info = success.getInfo();
//
// allResults.add(fileSimilarityService.buildDTO(fileEntityRepository.findByFileId(info.getId())));
// log.debug("File processed successfully: {}", entity.getFilePath());
// }
// case DocumentCheckResult.Failed failed -> {
// String errorMessage = String.format("Document check failed for %s: %s",
// entity.getFilePath(), failed.getMessage());
// log.warn(errorMessage);
// errors.add(errorMessage);
// }
// case AudioCheckResult.Failed failed -> {
// String errorMessage = String.format("Audio check failed for %s: %s",
// entity.getFilePath(), failed.getMessage());
// log.warn(errorMessage);
// errors.add(errorMessage);
// }
// default -> throw new IllegalStateException("Unexpected result type: " +
// noCopyCheckResult.getClass().getSimpleName());
// }
//
// }
//
//
// String messageDesc;
// MessageCode success;
// Map<String, Object> responseData = new HashMap<>();
//
// assert similarFiles != null;
// if (similarFiles.isEmpty()) {
// messageDesc = MessageCode.FILE_NOT_FOUND.getDescription();
// success = MessageCode.SUCCESS;
// responseData.put("content", similarFiles.getContent());
// responseData.put("page", similarFiles.getNumber());
// responseData.put("size", similarFiles.getSize());
// responseData.put("totalElements", similarFiles.getTotalElements());
// responseData.put("totalPages", similarFiles.getTotalPages());
// responseData.put("hasNext", similarFiles.hasNext());
// responseData.put("hasPrevious", similarFiles.hasPrevious());
// } else {
// messageDesc = MessageCode.SIMILAR_FILES_FOUND.getDescription();
// success = MessageCode.SUCCESS;
// responseData.put("content", allResults);
// }
//
//
// return ResponseEntity.ok()
// .body(new BaseResponse(20004, success.getCode(), messageDesc, responseData));
// }
@GetMapping("/v{version}/files/{fileId}/similar") @GetMapping("/v{version}/files/{fileId}/similar")
public ResponseEntity<BaseResponse> findSimilarFiles( public ResponseEntity<BaseResponse> findSimilarFiles(
@PathVariable("version") int version, @PathVariable("version") int version,
@PathVariable String fileId, @PathVariable String fileId,
@RequestParam(value = "auth_token", required = false) String authToken, @RequestParam(value = "auth_token", required = false) String authToken,
@RequestParam(required = false) List<String> similarityLevels, @RequestParam(required = false) List<String> similarityLevels,
@PageableDefault(size = 20, sort = "hammingDistance") Pageable pageable) throws Exception { @PageableDefault(size = 20, sort = "hammingDistance") Pageable pageable) {
SimilarityFilter filter = SimilarityFilter.builder()
.similarityLevels(similarityLevels)
.build();
Page<SimilarFileDTO> similarFiles = fileSimilarityService.findSimilarFiles(fileId, filter, pageable, authToken);
String messageDesc; try {
MessageCode success; FileEntity fileEntity = fileEntityRepository.findByFileId(fileId);
if (fileEntity == null) {
if (similarFiles.isEmpty()) { return ResponseEntity.ok().body(new BaseResponse(0, MessageCode.INVALID_FIELD.getCode(),
messageDesc = MessageCode.FILE_NOT_FOUND.getDescription(); MessageCode.INVALID_FIELD.getDescription(), null));
success = MessageCode.SUCCESS;
} else {
messageDesc = MessageCode.SIMILAR_FILES_FOUND.getDescription();
success = MessageCode.SUCCESS;
} }
Map<String, Object> responseData = new HashMap<>(); BaseResponse response;
responseData.put("content", similarFiles.getContent()); if ("image".equals(fileEntity.getMimeType())) {
responseData.put("page", similarFiles.getNumber()); Page<SimilarFileDTO> similarFilesPage = fileSimilarityService.findSimilarFiles(
responseData.put("size", similarFiles.getSize()); fileId, similarityLevels, pageable, authToken);
responseData.put("totalElements", similarFiles.getTotalElements()); response = buildSuccessResponse(similarFilesPage, MessageCode.SIMILAR_FILES_FOUND);
responseData.put("totalPages", similarFiles.getTotalPages()); } else {
responseData.put("hasNext", similarFiles.hasNext()); List<SimilarFileDTO> results = processNonImageFile(fileEntity, authToken);
responseData.put("hasPrevious", similarFiles.hasPrevious()); response = buildSuccessResponse(results, MessageCode.SIMILAR_FILES_FOUND);
}
return ResponseEntity.ok() return ResponseEntity.ok(response);
.body(new BaseResponse(20004, success.getCode(), messageDesc, responseData));
} catch (Exception e) {
log.error("Error finding similar files: {}", e.getMessage(), e);
return ResponseEntity.ok().body(new BaseResponse(0, MessageCode.INVALID_FIELD.getCode(),
MessageCode.INVALID_FIELD.getDescription(), null));
}
}
private List<SimilarFileDTO> processNonImageFile(FileEntity fileEntity, String authToken) {
List<SimilarFileDTO> results = new ArrayList<>();
File file = new File(fileEntity.getFilePath());
FileProtector.Type type = "document".equals(fileEntity.getMimeType()) ?
FileProtector.Type.DOC : FileProtector.Type.AUDIO;
NoCopyCheckResult checkResult = noCopyFileService.checkFile(file, type);
switch (checkResult) {
case DocumentCheckResult.Success success -> {
FileProtector.FileInfo info = success.getInfo();
FileEntity entity = fileEntityRepository.findByFileId(info.getId());
if (entity != null) {
results.add(fileSimilarityService.buildDTO(entity));
}
}
case AudioCheckResult.Success success -> {
FileProtector.FileInfo info = success.getInfo();
FileEntity entity = fileEntityRepository.findByFileId(info.getId());
if (entity != null) {
results.add(fileSimilarityService.buildDTO(entity));
}
}
case DocumentCheckResult.Failed failed ->
log.warn("Document check failed: {}", failed.getMessage());
case AudioCheckResult.Failed failed ->
log.warn("Audio check failed: {}", failed.getMessage());
default -> log.warn("Unexpected result type: {}", checkResult.getClass().getSimpleName());
}
return results;
}
private BaseResponse buildSuccessResponse(Page<SimilarFileDTO> page, MessageCode messageCode) {
Map<String, Object> responseData = new HashMap<>();
responseData.put("content", page.getContent());
responseData.put("page", page.getNumber());
responseData.put("size", page.getSize());
responseData.put("totalElements", page.getTotalElements());
responseData.put("totalPages", page.getTotalPages());
responseData.put("hasNext", page.hasNext());
responseData.put("hasPrevious", page.hasPrevious());
return new BaseResponse(20004, messageCode.getCode(),
messageCode.getDescription(), responseData);
}
private BaseResponse buildSuccessResponse(List<SimilarFileDTO> list, MessageCode messageCode) {
Map<String, Object> responseData = new HashMap<>();
responseData.put("content", list);
responseData.put("totalElements", list.size());
return new BaseResponse(20004, messageCode.getCode(),
messageCode.getDescription(), responseData);
} }
@GetMapping("/v{version}/files/progress/{uploadId}") @GetMapping("/v{version}/files/progress/{uploadId}")
@@ -46,8 +46,8 @@ public class FileSimilarityService {
private final FileEntityRepository fileEntityRepository; private final FileEntityRepository fileEntityRepository;
@Lazy // @Lazy
private final NoCopyFileService noCopyFileService; // private final NoCopyFileService noCopyFileService;
@Value("${server.baseurl}") @Value("${server.baseurl}")
private String baseUrl; private String baseUrl;
@@ -164,115 +164,81 @@ public class FileSimilarityService {
} }
} }
public Page<SimilarFileDTO> findSimilarFiles(String fileId, SimilarityFilter filter, Pageable pageable, // public Page<SimilarFileDTO> findSimilarFiles(String fileId, SimilarityFilter filter, Pageable pageable,
String authToken) throws Exception { // String authToken) throws Exception {
FileEntity fileEntity = fileEntityRepository.findByFileId(fileId); //
String mimeType = fileEntity.getMimeType(); // var imageHashEntity = hashRepository.findById(fileId)
List<SimilarFileDTO> allResults = new ArrayList<>(); // .orElseThrow(() -> new RuntimeException("Hash not found"));
//
// Long hash64Hi = imageHashEntity.getHash64Hi();
// Long hash64Lo = imageHashEntity.getHash64Lo();
// List<SimilarImageProjection> candidates = authToken.equals("all") ? repository.findCandidates(fileId):
// repository.findCandidatesFromUserFiles(fileId, authTokenRepository.findUserIdByToken(authToken));
//
// List<String> similarityLevels = (filter != null && filter.getSimilarityLevels() != null)
// ? filter.getSimilarityLevels()
// : List.of("DUPLICATE", "SIMILAR", "DIFFERENT");
//
// List<SimilarFileDTO> allResults = candidates.stream()
// .map(c -> createSimilarFileResponse(c, hash64Hi, hash64Lo))
// .filter(response -> similarityLevels.contains(response.getSimilarityLevel()))
// .sorted(Comparator.comparingInt(SimilarFileDTO::getHammingDistance))
// .collect(Collectors.toList());
//
// int total = allResults.size();
// int page = (pageable != null) ? pageable.getPageNumber() : 0;
// int size = (pageable != null) ? pageable.getPageSize() : 20;
//
// int fromIndex = Math.min(page * size, total);
// int toIndex = Math.min(fromIndex + size, total);
//
// List<SimilarFileDTO> pageContent = allResults.subList(fromIndex, toIndex);
//
// return new PageImpl<>(pageContent, pageable, total);
// }
if (mimeType.equals("image")) { public Page<SimilarFileDTO> findSimilarFiles(
String fileId,
List<String> similarityLevels,
Pageable pageable,
String authToken) {
try {
var imageHashEntity = hashRepository.findById(fileId) var imageHashEntity = hashRepository.findById(fileId)
.orElseThrow(() -> new RuntimeException("Hash not found")); .orElseThrow(() -> new RuntimeException("Hash not found for file: " + fileId));
Long hash64Hi = imageHashEntity.getHash64Hi(); List<SimilarImageProjection> candidates;
Long hash64Lo = imageHashEntity.getHash64Lo(); if (authToken == null || "all".equals(authToken)) {
List<SimilarImageProjection> candidates = authToken.equals("all") ? repository.findCandidates(fileId): candidates = repository.findCandidates(fileId);
repository.findCandidatesFromUserFiles(fileId, authTokenRepository.findUserIdByToken(authToken)); } else {
Long userId = authTokenRepository.findUserIdByToken(authToken);
candidates = repository.findCandidatesFromUserFiles(fileId, userId);
}
List<String> similarityLevels = (filter != null && filter.getSimilarityLevels() != null) List<String> levels = (similarityLevels != null && !similarityLevels.isEmpty())
? filter.getSimilarityLevels() ? similarityLevels
: List.of("DUPLICATE", "SIMILAR", "DIFFERENT"); : List.of("DUPLICATE", "SIMILAR", "DIFFERENT");
allResults = candidates.stream() List<SimilarFileDTO> allResults = candidates.stream()
.map(c -> createSimilarFileResponse(c, hash64Hi, hash64Lo)) .map(c -> createSimilarFileResponse(c,
.filter(response -> similarityLevels.contains(response.getSimilarityLevel())) imageHashEntity.getHash64Hi(),
imageHashEntity.getHash64Lo()))
.filter(dto -> levels.contains(dto.getSimilarityLevel()))
.sorted(Comparator.comparingInt(SimilarFileDTO::getHammingDistance)) .sorted(Comparator.comparingInt(SimilarFileDTO::getHammingDistance))
.collect(Collectors.toList()); .collect(Collectors.toList());
} else {
// Long userId = fileEntity.getUserId();
// List<FileEntity> fileEntityList = fileEntityRepository.findByUserIdAndMimeType(userId, mimeType);
// NoCopyCheckResult noCopyCheckResult;
//
// for (FileEntity file : fileEntityList) {
// Path path = Paths.get(file.getFilePath());
// if (mimeType.equals("document")) {
// noCopyCheckResult = (DocumentCheckResult) noCopyFileService.checkFile(
// path.toFile(), FileProtector.Type.valueOf(file.getMimeType().toUpperCase()));
// } else {
// noCopyCheckResult = (AudioCheckResult) noCopyFileService.checkFile(
// path.toFile(), FileProtector.Type.valueOf(file.getMimeType().toUpperCase()));
// }
//
// switch (noCopyCheckResult) {
// case DocumentCheckResult.Success success -> {
// FileProtector.FileInfo info = success.getInfo();
// allResults.add(buildDTO(file));
// }
// case DocumentCheckResult.Failed failed -> {
// String message = failed.getMessage();
// }
// default -> throw new IllegalStateException("Unexpected result");
// }
// }
Long userId = fileEntity.getUserId(); int start = (int) pageable.getOffset();
List<FileEntity> fileEntityList = fileEntityRepository.findByUserIdAndMimeType(userId, mimeType); int end = Math.min((start + pageable.getPageSize()), allResults.size());
List<String> errors = new ArrayList<>(); List<SimilarFileDTO> pageContent = start < allResults.size() ?
allResults.subList(start, end) : Collections.emptyList();
for (FileEntity file : fileEntityList) { return new PageImpl<>(pageContent, pageable, allResults.size());
try {
Path path = Paths.get(file.getFilePath());
FileProtector.Type fileType = file.getMimeType().equals("document") ? FileProtector.Type.DOC:
FileProtector.Type.AUDIO;
NoCopyCheckResult noCopyCheckResult = noCopyFileService.checkFile(path.toFile(), fileType);
switch (noCopyCheckResult) {
case DocumentCheckResult.Success success -> {
FileProtector.FileInfo info = success.getInfo();
allResults.add(buildDTO(file));
log.debug("File processed successfully: {}", file.getFilePath());
}
case AudioCheckResult.Success success -> {
FileProtector.FileInfo info = success.getInfo();
allResults.add(buildDTO(file));
log.debug("File processed successfully: {}", file.getFilePath());
}
case DocumentCheckResult.Failed failed -> {
String errorMessage = String.format("Document check failed for %s: %s",
file.getFilePath(), failed.getMessage());
log.warn(errorMessage);
errors.add(errorMessage);
}
case AudioCheckResult.Failed failed -> {
String errorMessage = String.format("Audio check failed for %s: %s",
file.getFilePath(), failed.getMessage());
log.warn(errorMessage);
errors.add(errorMessage);
}
default -> throw new IllegalStateException("Unexpected result type: " + noCopyCheckResult.getClass().getSimpleName());
}
} catch (Exception e) { } catch (Exception e) {
String errorMessage = String.format("Error processing file %s: %s", log.error("Error finding similar files for fileId: {}", fileId, e);
file.getFilePath(), e.getMessage()); return new PageImpl<>(Collections.emptyList(), pageable, 0);
log.error(errorMessage, e);
errors.add(errorMessage);
} }
} }
}
int total = allResults.size();
int page = (pageable != null) ? pageable.getPageNumber() : 0;
int size = (pageable != null) ? pageable.getPageSize() : 20;
int fromIndex = Math.min(page * size, total);
int toIndex = Math.min(fromIndex + size, total);
List<SimilarFileDTO> pageContent = allResults.subList(fromIndex, toIndex);
return new PageImpl<>(pageContent, pageable, total);
}
private String calculateFileHash(String path) throws Exception { private String calculateFileHash(String path) throws Exception {
@@ -322,7 +288,7 @@ public class FileSimilarityService {
.build(); .build();
} }
private SimilarFileDTO buildDTO(FileEntity fileEntity) { public SimilarFileDTO buildDTO(FileEntity fileEntity) {
return SimilarFileDTO.builder() return SimilarFileDTO.builder()
.fileId(fileEntity.getId()) .fileId(fileEntity.getId())
.ownerId(fileEntity.getUserId()) .ownerId(fileEntity.getUserId())