diff --git a/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java b/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java index 560fec3..50fadd5 100644 --- a/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java +++ b/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java @@ -34,4 +34,10 @@ public class FileEntityRequest { @JsonProperty("token") private String token; + + @JsonProperty("sort_by") + private String sortBy; + + @JsonProperty("sort_order") + private String sortOrder; } diff --git a/src/main/java/ru/soune/nocopy/dto/file/FileListResponse.java b/src/main/java/ru/soune/nocopy/dto/file/FileListResponse.java index bbc5a5f..d8c3a0d 100644 --- a/src/main/java/ru/soune/nocopy/dto/file/FileListResponse.java +++ b/src/main/java/ru/soune/nocopy/dto/file/FileListResponse.java @@ -30,4 +30,10 @@ public class FileListResponse { @JsonProperty("page_size") private Integer pageSize; + + @JsonProperty("sort_by") + private String sortBy; + + @JsonProperty("sort_order") + private String sortOrder; } diff --git a/src/main/java/ru/soune/nocopy/dto/file/SortOrder.java b/src/main/java/ru/soune/nocopy/dto/file/SortOrder.java new file mode 100644 index 0000000..81ba77f --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/file/SortOrder.java @@ -0,0 +1,25 @@ +package ru.soune.nocopy.dto.file; + +import lombok.Getter; + +@Getter +public enum SortOrder { + ASC("asc"), + DESC("desc"); + + private final String value; + + SortOrder(String value) { + this.value = value; + } + + public static SortOrder fromString(String value) { + if (value == null) return ASC; + for (SortOrder order : SortOrder.values()) { + if (order.value.equalsIgnoreCase(value)) { + return order; + } + } + return ASC; + } +} diff --git a/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java b/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java index 3a1f56e..b6e9202 100644 --- a/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java @@ -16,9 +16,8 @@ import ru.soune.nocopy.service.register.AuthService; import ru.soune.nocopy.service.file.FileEntityService; import ru.soune.nocopy.service.file.FileStatsService; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; @Slf4j @Component @@ -183,53 +182,285 @@ public class FileEntityHandler implements RequestHandler { } } +// private BaseResponse handleSearchFiles(BaseRequest request, FileEntityRequest fileRequest) { +// try { +// Long userId = authService.useUserAuthToken(fileRequest.getToken()); +// int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1; +// int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20; +// +// if (fileRequest.getQuery() == null || fileRequest.getQuery().isBlank()) { +// return handleGetUserFiles(request, fileRequest); +// } +// +// FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000, request.getVersion()); +// +// var filteredFiles = allFiles.getFiles().stream() +// .filter(f -> (f.getOriginalFileName() + f.getFileExtension()).toLowerCase() +// .contains(fileRequest.getQuery().toLowerCase())) +// .toList(); +// +// int start = (page - 1) * pageSize; +// int end = Math.min(start + pageSize, filteredFiles.size()); +// List fileEntityResponses = filteredFiles.subList(start, Math.min(end, filteredFiles.size())); +// long sumSize = fileEntityResponses.stream() +// .mapToLong(FileEntityResponse::getFileSize) +// .sum(); +// +// FileListResponse response = FileListResponse.builder() +// .files(fileEntityResponses) +// .totalCount(filteredFiles.size()) +// .totalSize(sumSize) +// .formattedTotalSize(fileEntityService.formatFileSize(sumSize)) +// .page(page) +// .pageSize(pageSize) +// .build(); +// +// return new BaseResponse(request.getMsgId(), +// MessageCode.SUCCESS.getCode(), +// MessageCode.SUCCESS.getDescription(), +// response); +// } catch (NotFoundAuthToken e) { +// return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), +// "Authentication required", null); +// } catch (Exception e) { +// log.error("Error searching files", e); +// return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), +// "Failed to search files: " + e.getMessage(), null); +// } +// } + private BaseResponse handleSearchFiles(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1; int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20; + String sortBy = fileRequest.getSortBy() != null ? fileRequest.getSortBy() : "fileName"; + SortOrder sortOrder = SortOrder.fromString(fileRequest.getSortOrder()); + if (fileRequest.getQuery() == null || fileRequest.getQuery().isBlank()) { - return handleGetUserFiles(request, fileRequest); + return handleGetUserFilesWithSort(request, fileRequest, sortBy, sortOrder); } FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000, request.getVersion()); - var filteredFiles = allFiles.getFiles().stream() - .filter(f -> (f.getOriginalFileName() + f.getFileExtension()).toLowerCase() - .contains(fileRequest.getQuery().toLowerCase())) - .toList(); + String searchQuery = fileRequest.getQuery().toLowerCase().trim(); + String[] searchTerms = searchQuery.split("\\s+"); + + List filteredFiles = allFiles.getFiles().stream() + .filter(f -> matchesSearch(f, searchTerms, searchQuery)) + .collect(Collectors.toList()); + + Comparator comparator = getComparator(sortBy, sortOrder); + filteredFiles.sort(comparator); int start = (page - 1) * pageSize; int end = Math.min(start + pageSize, filteredFiles.size()); - List fileEntityResponses = filteredFiles.subList(start, Math.min(end, filteredFiles.size())); - long sumSize = fileEntityResponses.stream() + + if (start >= filteredFiles.size()) { + return createEmptyResponse(request, page, pageSize, sortBy, sortOrder.getValue()); + } + + List paginatedFiles = filteredFiles.subList(start, end); + long totalSize = paginatedFiles.stream() .mapToLong(FileEntityResponse::getFileSize) .sum(); FileListResponse response = FileListResponse.builder() - .files(fileEntityResponses) + .files(paginatedFiles) .totalCount(filteredFiles.size()) - .totalSize(sumSize) - .formattedTotalSize(fileEntityService.formatFileSize(sumSize)) + .totalSize(totalSize) + .formattedTotalSize(fileEntityService.formatFileSize(totalSize)) .page(page) .pageSize(pageSize) + .sortBy(sortBy) + .sortOrder(sortOrder.getValue()) .build(); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), response); + } catch (NotFoundAuthToken e) { - return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), + return new BaseResponse(request.getMsgId(), + MessageCode.INVALID_TOKEN.getCode(), "Authentication required", null); } catch (Exception e) { log.error("Error searching files", e); - return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), + return new BaseResponse(request.getMsgId(), + MessageCode.FILE_UPLOAD_ERROR.getCode(), "Failed to search files: " + e.getMessage(), null); } } + private boolean matchesSearch(FileEntityResponse file, String[] searchTerms, String fullQuery) { + String fileName = getSafeString(file.getFileName()); + String originalFileName = getSafeString(file.getOriginalFileName()); + String fileFormat = getSafeString(file.getFileFormat()); + String fullFileName = fileName + " " + originalFileName; + + if (fullFileName.toLowerCase().contains(fullQuery)) { + return true; + } + + for (String term : searchTerms) { + if (term.length() < 2) continue; + + if (fullFileName.toLowerCase().contains(term)) { + return true; + } + } + + if (fileFormat.toLowerCase().contains(fullQuery)) { + return true; + } + + String fileNameWithExtension = fileName; + if (!fileFormat.isEmpty() && !fileName.toLowerCase().endsWith("." + fileFormat.toLowerCase())) { + fileNameWithExtension = fileName + "." + fileFormat; + if (fileNameWithExtension.toLowerCase().contains(fullQuery)) { + return true; + } + } + + return false; + } + + private String getSafeString(String value) { + return value != null ? value : ""; + } + + private Comparator getComparator(String sortBy, SortOrder sortOrder) { + Comparator comparator; + + switch (sortBy) { + case "fileSize": + comparator = Comparator.comparingLong( + FileEntityResponse::getFileSize + ); + break; + + case "createdAt": + comparator = Comparator.comparing( + FileEntityResponse::getCreatedAt, + Comparator.nullsLast(Comparator.naturalOrder()) + ); + break; + + case "updatedAt": + comparator = Comparator.comparing( + FileEntityResponse::getUpdatedAt, + Comparator.nullsLast(Comparator.naturalOrder()) + ); + break; + + case "fileName": + comparator = Comparator.comparing( + f -> getSafeString(f.getFileName()).toLowerCase(), + Comparator.nullsLast(Comparator.naturalOrder()) + ); + break; + + case "protectStatus": + comparator = Comparator.comparing( + f -> getSafeString(f.getProtectStatus()).toLowerCase(), + Comparator.nullsLast(Comparator.naturalOrder()) + ); + break; + + case "supportId": + comparator = Comparator.comparing( + FileEntityResponse::getSupportId, + Comparator.nullsLast(Comparator.naturalOrder()) + ); + break; + + default: + comparator = Comparator.comparing( + f -> getSafeString(f.getFileName()).toLowerCase(), + Comparator.nullsLast(Comparator.naturalOrder()) + ); + break; + } + + return sortOrder == SortOrder.DESC ? comparator.reversed() : comparator; + } + + private BaseResponse createEmptyResponse(BaseRequest request, int page, int pageSize, + String sortBy, String sortOrder) { + FileListResponse emptyResponse = FileListResponse.builder() + .files(Collections.emptyList()) + .totalCount(0) + .totalSize(0L) + .formattedTotalSize("0 B") + .page(page) + .pageSize(pageSize) + .sortBy(sortBy) + .sortOrder(sortOrder) + .build(); + + return new BaseResponse(request.getMsgId(), + MessageCode.SUCCESS.getCode(), + MessageCode.SUCCESS.getDescription(), + emptyResponse); + } + + private BaseResponse handleGetUserFilesWithSort(BaseRequest request, FileEntityRequest fileRequest, + String sortBy, SortOrder sortOrder) { + try { + Long userId = authService.useUserAuthToken(fileRequest.getToken()); + int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1; + int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20; + + FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000, request.getVersion()); + + List files = new ArrayList<>(allFiles.getFiles()); + + Comparator comparator = getComparator(sortBy, sortOrder); + files.sort(comparator); + + int start = (page - 1) * pageSize; + int end = Math.min(start + pageSize, files.size()); + + if (start >= files.size()) { + return createEmptyResponse(request, page, pageSize, sortBy, sortOrder.getValue()); + } + + List paginatedFiles = files.subList(start, end); + long totalSize = paginatedFiles.stream() + .mapToLong(FileEntityResponse::getFileSize) + .sum(); + + FileListResponse response = FileListResponse.builder() + .files(paginatedFiles) + .totalCount(files.size()) + .totalSize(totalSize) + .formattedTotalSize(fileEntityService.formatFileSize(totalSize)) + .page(page) + .pageSize(pageSize) + .sortBy(sortBy) + .sortOrder(sortOrder.getValue()) + .build(); + + return new BaseResponse(request.getMsgId(), + MessageCode.SUCCESS.getCode(), + MessageCode.SUCCESS.getDescription(), + response); + + } catch (NotFoundAuthToken e) { + return new BaseResponse(request.getMsgId(), + MessageCode.INVALID_TOKEN.getCode(), + "Authentication required", null); + } catch (Exception e) { + log.error("Error getting user files with sort", e); + return new BaseResponse(request.getMsgId(), + MessageCode.FILE_UPLOAD_ERROR.getCode(), + "Failed to get files: " + e.getMessage(), + null); + } + } + private BaseResponse handleGetStorageUsage(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); diff --git a/src/main/java/ru/soune/nocopy/service/search/SearchImageService.java b/src/main/java/ru/soune/nocopy/service/search/SearchImageService.java index c9ad65f..cb203a0 100644 --- a/src/main/java/ru/soune/nocopy/service/search/SearchImageService.java +++ b/src/main/java/ru/soune/nocopy/service/search/SearchImageService.java @@ -32,11 +32,11 @@ public class SearchImageService { private final OkHttpClient googleHttpClient; public SearchImageService() { - Proxy proxy = new Proxy(Proxy.Type.HTTP, - new InetSocketAddress("193.46.217.94", 3128)); +// Proxy proxy = new Proxy(Proxy.Type.HTTP, +// new InetSocketAddress("193.46.217.94", 3128)); this.yandexHttpClient = new OkHttpClient.Builder() - .proxy(proxy) +// .proxy(proxy) .connectTimeout(35, TimeUnit.SECONDS) .writeTimeout(35, TimeUnit.SECONDS) .readTimeout(35, TimeUnit.SECONDS)