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.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import ru.soune.nocopy.dto.*; import ru.soune.nocopy.dto.file.*; import ru.soune.nocopy.entity.file.*; import ru.soune.nocopy.entity.file.moderation.ModerationLog; import ru.soune.nocopy.entity.file.permission.PermissionType; import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity; import ru.soune.nocopy.exception.*; import ru.soune.nocopy.repository.*; import ru.soune.nocopy.service.file.cloud.CloudStorageService; import ru.soune.nocopy.service.file.moderation.ModerationService; import ru.soune.nocopy.service.file.permission.FileAccessService; import ru.soune.nocopy.service.register.AuthService; import ru.soune.nocopy.service.file.FileEntityService; import ru.soune.nocopy.service.file.FileStatsService; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDate; import java.util.*; import java.util.stream.Collectors; @Slf4j @Component @RequiredArgsConstructor public class FileEntityHandler implements RequestHandler { private final FileEntityService fileEntityService; private final AuthService authService; private final FileStatsService fileStatsService; private final ObjectMapper objectMapper; private final FileEntityRepository fileEntityRepository; private final FileMonitoringRepository fileMonitoringRepository; private final CloudStorageService cloudStorageService; private final ModerationService moderationService; private final FileAppealRepository fileAppealRepository; private final ModerationLogRepository moderationLogRepository; private final ImageHashRepository imageHashRepository; private final FileAccessService fileAccessService; @Override public BaseResponse handle(BaseRequest request) { try { FileEntityRequest fileRequest = objectMapper.convertValue( request.getMessageBody(), FileEntityRequest.class); String action = fileRequest.getAction(); switch (action) { case "file_info": return handleGetFileInfo(request, fileRequest); case "user_files_info": return handleGetFilesUserInfo(request, fileRequest); case "file_by_session": return handleGetFileBySession(request, fileRequest); case "user_files": return handleGetUserFiles(request, fileRequest); case "search_files": return handleSearchFiles(request, fileRequest); case "storage_usage": return handleGetStorageUsage(request, fileRequest); case "check_protected": return handleProtectFile(request, fileRequest); case "delete_file": return handleDeleteFile(request, fileRequest); case "delete_files": return handleDeleteFiles(request, fileRequest); case "submit_appeal": return handleSubmitAppeal(request, fileRequest); case "user_appeals": return handleGetUserAppeals(request, fileRequest); case "change_permission": return handleChangePermission(request, fileRequest); default: ActionResponse response = ActionResponse.builder() .action(action) .availableActions(Arrays.asList( "file_info", "file_by_session", "user_files", "search_files", "storage_usage", "delete_file", "user_files_info")) .build(); return new BaseResponse(request.getMsgId(), MessageCode.INVALID_ACTION.getCode(), "Invalid action: " + action, response); } } catch (Exception e) { log.error("Error in FileEntityHandler", e); return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), "Handler error: " + e.getMessage(), null); } } private BaseResponse handleGetFilesUserInfo(BaseRequest request, FileEntityRequest fileRequest) { Long userId = authService.useUserAuthToken(fileRequest.getToken()); FileInfoUserResponse userFileStats = fileStatsService.getUserFileStats(userId); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), userFileStats); } private BaseResponse handleGetFileInfo(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId(), request.getVersion()); if (!fileInfo.getUserId().equals(userId)) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), "Access denied", null); } return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), fileInfo); } catch (NotFoundAuthToken e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), "Authentication required", null); } catch (Exception e) { log.error("Error getting file info", e); return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), "Failed to get file info: " + e.getMessage(), null); } } private BaseResponse handleChangePermission(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId(), request.getVersion()); if (!fileInfo.getUserId().equals(userId)) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), "Access denied", null); } Map permissions = fileRequest.getPermissions(); if (!PermissionType.areAllValid(permissions)) { return new BaseResponse(request.getMsgId(), MessageCode.PERMISSION_NOT_VALID.getCode(), MessageCode.PERMISSION_NOT_VALID.getDescription(), null); } for (Map.Entry entry: permissions.entrySet()) { try { fileAccessService.changePermission(fileInfo.getId(), PermissionType.valueOf(entry.getKey()), entry.getValue()); } catch (FileEntityNotFoundException e) { return new BaseResponse(request.getMsgId(), MessageCode.FILE_NOT_FOUND.getCode(), MessageCode.FILE_NOT_FOUND.getDescription(), null); } } return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), fileInfo); } catch (NotFoundAuthToken e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), "Authentication required", null); } catch (Exception e) { log.error("Error getting file info", e); return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), "Failed to get file info: " + e.getMessage(), null); } } private BaseResponse handleGetFileBySession(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); FileEntityResponse fileInfo = fileEntityService.getByUploadSessionId(fileRequest.getUploadSessionId(), request.getVersion()); if (!fileInfo.getUserId().equals(userId)) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), "Access denied", null); } return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), fileInfo); } catch (NotFoundAuthToken e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), "Authentication required", null); } catch (Exception e) { log.error("Error getting file by session", e); return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), "Failed to get file: " + e.getMessage(), null); } } private BaseResponse handleGetUserFiles(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; FileResponse files = fileEntityService.getUserFiles(userId, page, pageSize, request.getVersion()); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), files); } catch (NotFoundAuthToken e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), "Authentication required", null); } catch (Exception e) { log.error("Error getting user files", e); return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), "Failed to get 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()); FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000, request.getVersion()); String searchQuery = fileRequest.getQuery() != null ? fileRequest.getQuery().toLowerCase().trim() : ""; String[] searchTerms = searchQuery.split("\\s+"); String filterType = fileRequest.getType(); String dateFilter = fileRequest.getDateFilter(); List filteredFiles = allFiles.getFiles().stream() .filter(f -> matchesSearch(f, searchTerms, searchQuery, filterType)) .filter(f -> matchesDateFilter(f, dateFilter)) .collect(Collectors.toList()); Comparator comparator = getComparator(sortBy, sortOrder); filteredFiles.sort(comparator); int start = (page - 1) * pageSize; int end = Math.min(start + pageSize, filteredFiles.size()); 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(paginatedFiles) .totalCount(filteredFiles.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 searching files", e); return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), "Failed to search files: " + e.getMessage(), null); } } private boolean matchesDateFilter(FileEntityResponse file, String dateFilter) { if (dateFilter == null || dateFilter.isEmpty()) { return true; } LocalDate fileDate = file.getCreatedAt().toLocalDate(); LocalDate now = LocalDate.now(); switch (dateFilter.toLowerCase()) { case "today": return fileDate.equals(now); case "week": return fileDate.isAfter(now.minusWeeks(1)) && !fileDate.isAfter(now); case "month": return fileDate.isAfter(now.minusMonths(1)) && !fileDate.isAfter(now); default: return true; } } private boolean matchesSearch(FileEntityResponse file, String[] searchTerms, String fullQuery, String type) { String fileName = getSafeString(file.getFileName()); String originalFileName = getSafeString(file.getOriginalFileName()); String mimeType = getSafeString(file.getMimeType()); String fileFormat = getSafeString(file.getFileFormat()); String fullFileName = fileName + " " + originalFileName; boolean matchesType = true; if (type != null && !type.isEmpty()) { matchesType = matchesMimeType(mimeType, type); } if (!matchesType) { return false; } if (fullQuery != null && !fullQuery.isEmpty()) { 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; } if (mimeType.toLowerCase().contains(fullQuery)) { return true; } if (!fileFormat.isEmpty() && !fileName.toLowerCase().endsWith("." + fileFormat.toLowerCase())) { String fileNameWithExtension = fileName + "." + fileFormat; if (fileNameWithExtension.toLowerCase().contains(fullQuery)) { return true; } } return false; } return true; } private boolean matchesMimeType(String mimeType, String filterType) { if (filterType == null || filterType.isEmpty()) return true; String mimeTypeLower = mimeType.toLowerCase(); String filterTypeLower = filterType.toLowerCase().trim(); return mimeTypeLower.equals(filterTypeLower); } 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 "fileType": comparator = Comparator.comparing( FileEntityResponse::getMimeType); 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; case "monitoring": Map monitoringOrder = Map.of( "MONITORING_DAILY", 1, "MONITORING_WEEKLY", 2, "MONITORING_MONTHLY", 3 ); comparator = Comparator.comparing( response -> monitoringOrder.getOrDefault(response.getMonitoring(), Integer.MAX_VALUE), 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()); long usage = fileEntityService.getUserStorageUsed(userId); StorageUsageResponse response = StorageUsageResponse.builder() .userId(userId) .storageUsed(usage) .formattedStorageUsed(formatFileSize(usage)) .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 storage usage", e); return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), "Failed to get storage usage: " + e.getMessage(), null); } } private BaseResponse handleProtectFile(BaseRequest request, FileEntityRequest fileRequest) { String fileId = fileRequest.getFileId(); Optional optionalFileEntity = fileEntityRepository.findById(fileId); if (optionalFileEntity.isEmpty()) { return new BaseResponse(request.getMsgId(), MessageCode.FILE_NOT_FOUND.getCode(), "File not found exception: " + fileRequest.getFileId(), null); } FileEntity fileEntity = optionalFileEntity.get(); ProtectionStatus protectionStatus = fileEntity.getProtectionStatus(); Integer code = protectionStatus == ProtectionStatus.PROTECTED ? MessageCode.FILE_IS_PROTECTED.getCode(): MessageCode.FILE_IS_NOT_PROTECTED.getCode(); String message = protectionStatus == ProtectionStatus.PROTECTED ? MessageCode.FILE_IS_PROTECTED.getDescription(): MessageCode.FILE_IS_NOT_PROTECTED.getDescription(); return new BaseResponse(request.getMsgId(), code, message, fileEntityService.getById(fileId, request.getVersion())); } @Transactional public BaseResponse handleDeleteFiles(BaseRequest request, FileEntityRequest fileRequest) { List fileIds = fileRequest.getFileIds(); List deletedIds = new ArrayList<>(); List failedIds = new ArrayList<>(); for (String fileId : fileIds) { try { FileEntity fileEntity = fileEntityRepository.findById(fileId) .orElseThrow(() -> new FileEntityNotFoundException(fileId)); deleteFileIfExists(fileEntity.getFilePath()); deleteFileIfExists(fileEntity.getProtectedFilePath()); deleteFileIfExists(fileEntity.getThumbnailPath()); deleteFileIfExists(fileEntity.getMediumPath()); deletedIds.add(fileId); log.info("Physical files deleted for entity: {}", fileId); } catch (Exception e) { failedIds.add(fileId); log.error("Failed to delete files for entity: {}", fileId, e); } } if (!deletedIds.isEmpty()) { fileMonitoringRepository.deleteAllByFileIdIn(deletedIds); moderationLogRepository.deleteAllByFileIdIn(deletedIds); imageHashRepository.deleteAllByFileIdIn(deletedIds); fileAppealRepository.deleteAllByFileIdIn(deletedIds); fileEntityRepository.deleteAllByIdIn(deletedIds); log.info("Deleted {} entities from database", deletedIds.size()); } return new BaseResponse( request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), failedIds ); } private void deleteFileIfExists(String filePath) { if (filePath != null && !filePath.isEmpty()) { try { Path path = Paths.get(filePath); boolean deleted = Files.deleteIfExists(path); if (deleted) { log.debug("Deleted file: {}", filePath); } } catch (IOException e) { log.warn("Failed to delete file: {}", filePath, e); throw new RuntimeException("Failed to delete file: " + filePath, e); } } } private BaseResponse handleDeleteFile(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); String fileId = fileRequest.getFileId(); FileEntityResponse fileInfo = fileEntityService.getById(fileId, request.getVersion()); DeleteFileResponse response; if (!fileInfo.getUserId().equals(userId)) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), "Access denied", null); } FileEntity fileEntity = fileEntityRepository.findById(fileId) .orElseThrow(() -> new FileEntityNotFoundException(fileId)); int fullDelete = fileRequest.getFullDelete() == null ? 0 : fileRequest.getFullDelete(); if (fileEntity.getStatus().equals(FileStatus.DELETED) || fullDelete == 1) { cloudStorageService.deleteFromStorageDisk(fileEntity); response = DeleteFileResponse.builder() .fileId(fileRequest.getFileId()) .message("File deleted from disk") .build(); } else { fileEntityService.softDeleteFileWithHash(fileEntity); response = DeleteFileResponse.builder() .fileId(fileRequest.getFileId()) .message("File marked as deleted") .build(); } return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), "File marked as deleted", response); } catch (NotFoundAuthToken e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), "Authentication required", null); } catch (Exception e) { log.error("Error deleting file", e); return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), "Failed to delete file: " + e.getMessage(), null); } } private BaseResponse handleModerateFile(BaseRequest request, FileEntityRequest fileRequest) { try { //TODO добавить проверку на админа // Long moderatorId = authService.useAdminAuthToken(fileRequest.getToken()); ModerationActionRequest moderationRequest = ModerationActionRequest.builder() .fileId(fileRequest.getFileId()) .newStatus(fileRequest.getFileStatus()) .comment(fileRequest.getComment()) .build(); //TODO set moderator ID moderationService.moderateFile(moderationRequest, 0L); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS_MODERATION.getCode(), MessageCode.SUCCESS_MODERATION.getDescription(), FileEntityResponse.builder().id(fileRequest.getFileId()).build()); } catch (SecurityException e) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), MessageCode.ACCESS_DENIED.getDescription(), FileEntityResponse.builder().id(fileRequest.getFileId()).build()); } catch (FileEntityNotFoundException e) { return new BaseResponse(request.getMsgId(), MessageCode.FILE_NOT_FOUND.getCode(), MessageCode.FILE_NOT_FOUND.getDescription(), FileEntityResponse.builder() .id(fileRequest.getFileId()).build()); } catch (IllegalStateException e) { return new BaseResponse(request.getMsgId(), MessageCode.ILLEGAL_STATE.getCode(), MessageCode.ILLEGAL_STATE.getDescription(), FileEntityResponse.builder() .status(fileRequest.getFileStatus()).build()); } } private BaseResponse handleSubmitAppeal(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); AppealRequest appealRequest = AppealRequest.builder() .fileId(fileRequest.getFileId()) .appealReason(fileRequest.getAppealReason()) .additionalInfo(fileRequest.getAdditionalInfo()) .build(); AppealResponse response = moderationService.submitAppeal(appealRequest, userId); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), "Appeal submitted successfully", response); } catch (InvalidAppealException e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), e.getMessage(), null); } catch (Exception e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "Failed to submit appeal: " + e.getMessage(), null); } } private BaseResponse handleGetUserAppeals(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; Page appeals = moderationService.getUserAppeals(userId, page - 1, pageSize); Map response = Map.of( "appeals", appeals.getContent(), "totalCount", appeals.getTotalElements(), "totalPages", appeals.getTotalPages(), "currentPage", page, "pageSize", pageSize); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), response); } catch (SecurityException e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), "Authentication required", null); } catch (Exception e) { log.error("Error getting user appeals", e); return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "Failed to get appeals: " + e.getMessage(), null); } } private BaseResponse handleGetFileModerationInfo(BaseRequest request, FileEntityRequest fileRequest) { try { // TODO // authService.useAdminAuthToken(fileRequest.getToken()); String fileId = fileRequest.getFileId(); if (fileId == null) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "fileId is required", null); } FileModerationInfo moderationInfo = moderationService.getFileModerationInfo(fileId); FileEntity file = fileEntityRepository.findById(fileId) .orElseThrow(() -> new FileEntityNotFoundException(fileId)); Map fileInfoMap = new HashMap<>(); fileInfoMap.put("id", file.getId()); fileInfoMap.put("fileName", file.getOriginalFileName()); fileInfoMap.put("storedFileName", file.getStoredFileName()); fileInfoMap.put("fileSize", file.getFileSize()); fileInfoMap.put("formattedSize", fileEntityService.formatFileSize(file.getFileSize())); fileInfoMap.put("mimeType", file.getMimeType()); fileInfoMap.put("fileExtension", file.getFileExtension()); fileInfoMap.put("checksum", file.getChecksum()); fileInfoMap.put("status", file.getStatus() != null ? file.getStatus().name() : null); fileInfoMap.put("createdAt", file.getCreatedAt()); fileInfoMap.put("updatedAt", file.getUpdatedAt()); Map response = new HashMap<>(); response.put("fileInfo", fileInfoMap); response.put("moderationInfo", moderationInfo != null ? moderationInfo : new HashMap<>()); response.put("moderationHistory", getModerationHistory(fileId)); response.put("appeals", getFileAppeals(fileId)); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), response); } catch (SecurityException e) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), "Admin access required", null); } catch (FileEntityNotFoundException e) { return new BaseResponse(request.getMsgId(), MessageCode.FILE_NOT_FOUND.getCode(), e.getMessage(), null); } catch (Exception e) { log.error("Error getting file moderation info", e); return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "Failed to get file info: " + e.getMessage(), null); } } private List> getModerationHistory(String fileId) { List logs = moderationLogRepository.findByFileIdOrderByCreatedAtDesc(fileId); if (logs == null || logs.isEmpty()) { return Collections.emptyList(); } return logs.stream() .map(log -> { Map logMap = new HashMap<>(); logMap.put("id", log.getId()); logMap.put("moderatorId", log.getModeratorId()); logMap.put("oldStatus", log.getOldStatus() != null ? log.getOldStatus().name() : null); logMap.put("newStatus", log.getNewStatus() != null ? log.getNewStatus().name() : null); logMap.put("reason", log.getReason()); logMap.put("comment", log.getComment()); logMap.put("createdAt", log.getCreatedAt()); return logMap; }) .collect(Collectors.toList()); } private List> getFileAppeals(String fileId) { List appeals = fileAppealRepository.findByFileId(fileId); if (appeals == null || appeals.isEmpty()) { return Collections.emptyList(); } return appeals.stream() .map(appeal -> { Map appealMap = new HashMap<>(); appealMap.put("id", appeal.getId()); appealMap.put("userId", appeal.getUserId()); appealMap.put("appealReason", appeal.getAppealReason()); appealMap.put("additionalInfo", appeal.getAdditionalInfo()); appealMap.put("status", appeal.getStatus() != null ? appeal.getStatus().name() : null); appealMap.put("adminComment", appeal.getAdminComment()); appealMap.put("moderatorId", appeal.getModeratorId()); appealMap.put("createdAt", appeal.getCreatedAt()); appealMap.put("resolvedAt", appeal.getResolvedAt()); return appealMap; }) .collect(Collectors.toList()); } private String formatFileSize(long size) { if (size < 1024) return size + " B"; int exp = (int) (Math.log(size) / Math.log(1024)); String pre = "KMGTPE".charAt(exp-1) + ""; return String.format("%.1f %sB", size / Math.pow(1024, exp), pre); } }