From f3aa6324c2a9c3f322c45765148dd95971b386eb Mon Sep 17 00:00:00 2001 From: vladp Date: Thu, 18 Dec 2025 01:13:19 +0700 Subject: [PATCH] add fileentity controller --- .../nocopy/configuration/HandlerConfig.java | 28 ++ .../nocopy/controller/ApiController.java | 20 +- .../controller/file/FileEntityController.java | 210 ------------- .../controller/file/FileUploadController.java | 65 ---- .../ru/soune/nocopy/dto/LoginResponse.java | 0 .../java/ru/soune/nocopy/dto/MessageCode.java | 3 +- .../ru/soune/nocopy/dto/RegisterRequest.java | 0 .../nocopy/dto/file/DeleteFileResponse.java | 19 ++ .../nocopy/dto/file/FileEntityRequest.java | 34 ++ .../nocopy/dto/file/FileListResponse.java | 23 +- .../soune/nocopy/dto/file/FileResponse.java | 22 ++ .../nocopy/dto/file/StorageUsageResponse.java | 22 ++ .../exception/UserAlreadyExistsException.java | 0 .../nocopy/handler/FileEntityHandler.java | 292 ++++++++++++++++++ .../service/file/FileEntityService.java | 54 +--- 15 files changed, 447 insertions(+), 345 deletions(-) create mode 100644 src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java delete mode 100644 src/main/java/ru/soune/nocopy/controller/file/FileEntityController.java delete mode 100644 src/main/java/ru/soune/nocopy/controller/file/FileUploadController.java delete mode 100644 src/main/java/ru/soune/nocopy/dto/LoginResponse.java delete mode 100644 src/main/java/ru/soune/nocopy/dto/RegisterRequest.java create mode 100644 src/main/java/ru/soune/nocopy/dto/file/DeleteFileResponse.java create mode 100644 src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java create mode 100644 src/main/java/ru/soune/nocopy/dto/file/FileResponse.java create mode 100644 src/main/java/ru/soune/nocopy/dto/file/StorageUsageResponse.java delete mode 100644 src/main/java/ru/soune/nocopy/exception/UserAlreadyExistsException.java create mode 100644 src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java diff --git a/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java b/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java new file mode 100644 index 0000000..96ae5bb --- /dev/null +++ b/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java @@ -0,0 +1,28 @@ +package ru.soune.nocopy.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import ru.soune.nocopy.handler.*; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class HandlerConfig { + + @Bean + public Map handlers( + RegRequestHandler reg, + LoginRequestHandler login, + FileUploadHandler upload, + FileEntityHandler file + ) { + Map map = new HashMap<>(); + map.put(20001, login); + map.put(20002, reg); + map.put(20004, upload); + map.put(20005, file); + + return map; + } +} diff --git a/src/main/java/ru/soune/nocopy/controller/ApiController.java b/src/main/java/ru/soune/nocopy/controller/ApiController.java index 04e2d29..652fab7 100644 --- a/src/main/java/ru/soune/nocopy/controller/ApiController.java +++ b/src/main/java/ru/soune/nocopy/controller/ApiController.java @@ -1,9 +1,7 @@ package ru.soune.nocopy.controller; -import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationContext; import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; @@ -19,10 +17,7 @@ import ru.soune.nocopy.dto.file.UploadProgress; import ru.soune.nocopy.entity.file.UploadStatus; import ru.soune.nocopy.exception.NotValidFieldException; import ru.soune.nocopy.exception.ValidationException; -import ru.soune.nocopy.handler.FileUploadHandler; -import ru.soune.nocopy.handler.LoginRequestHandler; -import ru.soune.nocopy.handler.RegRequestHandler; -import ru.soune.nocopy.handler.RequestHandler; +import ru.soune.nocopy.handler.*; import ru.soune.nocopy.service.file.FileUploadService; import java.util.HashMap; @@ -35,21 +30,10 @@ import java.util.stream.Collectors; @RequestMapping("/api") @RequiredArgsConstructor public class ApiController { - private final ApplicationContext applicationContext; private final FileUploadService fileUploadService; - private Map handlers = new HashMap<>(); + private final Map handlers; - @PostConstruct - public void init() { - RegRequestHandler regHandler = applicationContext.getBean(RegRequestHandler.class); - LoginRequestHandler loginHandler = applicationContext.getBean(LoginRequestHandler.class); - FileUploadHandler fileUploadHandler = applicationContext.getBean(FileUploadHandler.class); - - handlers.put(20004, fileUploadHandler); - handlers.put(20002, regHandler); - handlers.put(20001, loginHandler); - } @PostMapping("/v{version}/data") public ResponseEntity handlePostRequest(@RequestBody BaseRequest request, diff --git a/src/main/java/ru/soune/nocopy/controller/file/FileEntityController.java b/src/main/java/ru/soune/nocopy/controller/file/FileEntityController.java deleted file mode 100644 index 25d19d1..0000000 --- a/src/main/java/ru/soune/nocopy/controller/file/FileEntityController.java +++ /dev/null @@ -1,210 +0,0 @@ -package ru.soune.nocopy.controller.file; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import ru.soune.nocopy.dto.file.FileApiResponse; -import ru.soune.nocopy.dto.file.FileEntityResponse; -import ru.soune.nocopy.dto.file.FileListResponse; -import ru.soune.nocopy.entity.AuthToken; -import ru.soune.nocopy.exception.NotFoundAuthToken; -import ru.soune.nocopy.repository.AuthTokenRepository; -import ru.soune.nocopy.service.file.FileEntityService; - -@Slf4j -@RestController -@RequestMapping("/api/file-entities") -@RequiredArgsConstructor -public class FileEntityController { - - private final FileEntityService fileEntityService; - private final AuthTokenRepository authTokenRepository; - - /** - * Получить информацию о файле по ID - */ - @GetMapping("/{fileId}") - public ResponseEntity> getFileInfo( - @PathVariable String fileId, - @RequestHeader("Authorization") String tokenHeader) { - - try { - Long userId = getUserIdFromToken(tokenHeader); - FileEntityResponse fileInfo = fileEntityService.getById(fileId); - - if (!fileInfo.getUserId().equals(userId)) { - return ResponseEntity.status(403) - .body(FileApiResponse.error("Access denied")); - } - - return ResponseEntity.ok(FileApiResponse.success(fileInfo)); - - } catch (NotFoundAuthToken e) { - return ResponseEntity.status(401) - .body(FileApiResponse.error("Authentication required")); - } catch (Exception e) { - log.error("Error getting file info", e); - return ResponseEntity.badRequest() - .body(FileApiResponse.error("Failed to get file info: " + e.getMessage())); - } - } - - /** - * Получить информацию о файле по ID сессии загрузки - */ - @GetMapping("/by-session/{uploadSessionId}") - public ResponseEntity> getFileByUploadSession( - @PathVariable String uploadSessionId, - @RequestHeader("Authorization") String tokenHeader) { - - try { - Long userId = getUserIdFromToken(tokenHeader); - FileEntityResponse fileInfo = fileEntityService.getByUploadSessionId(uploadSessionId); - - if (!fileInfo.getUserId().equals(userId)) { - return ResponseEntity.status(403) - .body(FileApiResponse.error("Access denied")); - } - - return ResponseEntity.ok(FileApiResponse.success(fileInfo)); - - } catch (NotFoundAuthToken e) { - return ResponseEntity.status(401) - .body(FileApiResponse.error("Authentication required")); - } catch (Exception e) { - log.error("Error getting file by session", e); - return ResponseEntity.badRequest() - .body(FileApiResponse.error("Failed to get file: " + e.getMessage())); - } - } - - /** - * Получить все файлы пользователя - */ - @GetMapping("/my-files") - public ResponseEntity> getUserFiles( - @RequestHeader("Authorization") String tokenHeader, - @RequestParam(defaultValue = "1") int page, - @RequestParam(defaultValue = "20") int pageSize) { - - try { - Long userId = getUserIdFromToken(tokenHeader); - FileListResponse files = fileEntityService.getUserFiles(userId, page, pageSize); - - return ResponseEntity.ok(FileApiResponse.success(files)); - - } catch (NotFoundAuthToken e) { - return ResponseEntity.status(401) - .body(FileApiResponse.error("Authentication required")); - } catch (Exception e) { - log.error("Error getting user files", e); - return ResponseEntity.badRequest() - .body(FileApiResponse.error("Failed to get files: " + e.getMessage())); - } - } - - /** - * Поиск файлов пользователя - */ - @GetMapping("/search") - public ResponseEntity> searchFiles( - @RequestParam String query, - @RequestHeader("Authorization") String tokenHeader, - @RequestParam(defaultValue = "1") int page, - @RequestParam(defaultValue = "20") int pageSize) { - - try { - Long userId = getUserIdFromToken(tokenHeader); - FileListResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000); - - var filteredFiles = allFiles.getFiles().stream() - .filter(f -> f.getOriginalFileName().toLowerCase().contains(query.toLowerCase())) - .toList(); - - int start = (page - 1) * pageSize; - int end = Math.min(start + pageSize, filteredFiles.size()); - - FileListResponse response = FileListResponse.builder() - .files(filteredFiles.subList(start, Math.min(end, filteredFiles.size()))) - .totalCount(filteredFiles.size()) - .totalSize(allFiles.getTotalSize()) - .formattedTotalSize(allFiles.getFormattedTotalSize()) - .page(page) - .pageSize(pageSize) - .build(); - - return ResponseEntity.ok(FileApiResponse.success(response)); - - } catch (NotFoundAuthToken e) { - return ResponseEntity.status(401) - .body(FileApiResponse.error("Authentication required")); - } catch (Exception e) { - log.error("Error searching files", e); - return ResponseEntity.badRequest() - .body(FileApiResponse.error("Failed to search files: " + e.getMessage())); - } - } - - /** - * Получить общий размер файлов пользователя - */ - @GetMapping("/storage/usage") - public ResponseEntity> getStorageUsage( - @RequestHeader("Authorization") String tokenHeader) { - - try { - Long userId = getUserIdFromToken(tokenHeader); - long usage = fileEntityService.getUserStorageUsed(userId); - - return ResponseEntity.ok(FileApiResponse.success(usage)); - - } catch (NotFoundAuthToken e) { - return ResponseEntity.status(401) - .body(FileApiResponse.error("Authentication required")); - } catch (Exception e) { - log.error("Error getting storage usage", e); - return ResponseEntity.badRequest() - .body(FileApiResponse.error("Failed to get storage usage: " + e.getMessage())); - } - } - - /** - * Пометить файл как удаленный - */ - @DeleteMapping("/{fileId}") - public ResponseEntity> deleteFile( - @PathVariable String fileId, - @RequestHeader("Authorization") String tokenHeader) { - - try { - Long userId = getUserIdFromToken(tokenHeader); - FileEntityResponse fileInfo = fileEntityService.getById(fileId); - - if (!fileInfo.getUserId().equals(userId)) { - return ResponseEntity.status(403) - .body(FileApiResponse.error("Access denied")); - } - - fileEntityService.markAsDeleted(fileId); - - return ResponseEntity.ok(FileApiResponse.success( - "File marked as deleted", null)); - - } catch (NotFoundAuthToken e) { - return ResponseEntity.status(401) - .body(FileApiResponse.error("Authentication required")); - } catch (Exception e) { - log.error("Error deleting file", e); - return ResponseEntity.badRequest() - .body(FileApiResponse.error("Failed to delete file: " + e.getMessage())); - } - } - - private Long getUserIdFromToken(String tokenHeader) { - String token = tokenHeader.replace("Bearer ", ""); - AuthToken authToken = authTokenRepository.findByToken(token) - .orElseThrow(() -> new NotFoundAuthToken("Token not found")); - return authToken.getUser().getId(); - } -} diff --git a/src/main/java/ru/soune/nocopy/controller/file/FileUploadController.java b/src/main/java/ru/soune/nocopy/controller/file/FileUploadController.java deleted file mode 100644 index d4bb6da..0000000 --- a/src/main/java/ru/soune/nocopy/controller/file/FileUploadController.java +++ /dev/null @@ -1,65 +0,0 @@ -package ru.soune.nocopy.controller.file; - -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; -import ru.soune.nocopy.dto.file.FileApiResponse; -import ru.soune.nocopy.dto.file.InitUploadRequest; -import ru.soune.nocopy.dto.file.UploadProgressResponse; -import ru.soune.nocopy.entity.AuthToken; -import ru.soune.nocopy.entity.file.FileType; -import ru.soune.nocopy.entity.file.FileUploadSession; -import ru.soune.nocopy.entity.file.UploadStatus; -import ru.soune.nocopy.exception.FileUploadException; -import ru.soune.nocopy.exception.NotFoundAuthToken; -import ru.soune.nocopy.exception.UploadSessionNotFoundException; -import ru.soune.nocopy.repository.AuthTokenRepository; -import ru.soune.nocopy.repository.FileUploadSessionRepository; -import ru.soune.nocopy.service.file.FileUploadService; - -import java.util.*; - -@Slf4j -@RestController -@RequestMapping("/api/files") -@RequiredArgsConstructor -public class FileUploadController { - - private final FileUploadService fileUploadService; - - @PostMapping("/chunk") - public ResponseEntity> uploadChunk( - @RequestParam("uploadId") String uploadId, - @RequestParam("chunkNumber") Integer chunkNumber, - @RequestParam("chunk") MultipartFile chunk) { - - log.info("Uploading chunk {} for session {}, file size: {} bytes", - chunkNumber, uploadId, chunk.getSize()); - - if (chunk.isEmpty()) { - return ResponseEntity.badRequest() - .body(FileApiResponse.error("Chunk file is empty")); - } - - if (uploadId == null || uploadId.isBlank()) { - return ResponseEntity.badRequest() - .body(FileApiResponse.error("Upload ID is required")); - } - - try { - UploadProgressResponse progress = fileUploadService.uploadChunk( - uploadId, chunkNumber, chunk); - - return ResponseEntity.ok(FileApiResponse.success(progress)); - - } catch (Exception e) { - log.error("Error uploading chunk", e); - return ResponseEntity.badRequest() - .body(FileApiResponse.error(e.getMessage())); - } - } -} diff --git a/src/main/java/ru/soune/nocopy/dto/LoginResponse.java b/src/main/java/ru/soune/nocopy/dto/LoginResponse.java deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/ru/soune/nocopy/dto/MessageCode.java b/src/main/java/ru/soune/nocopy/dto/MessageCode.java index 36cd838..84541c9 100644 --- a/src/main/java/ru/soune/nocopy/dto/MessageCode.java +++ b/src/main/java/ru/soune/nocopy/dto/MessageCode.java @@ -11,7 +11,8 @@ public enum MessageCode { INVALID_JSON_BODY(2, "Invalid fields in JSON object"), INCOMPLETE_UPLOAD(2, "Not load all chunks"), MSG_ID_NOT_FOUND(4, "Message id not found"), - + FILE_ENTITY_ERROR(2, "File entity error"), + ACCESS_DENIED(2, "Access denied"), AUTH_EMAIL_NOT_FOUND(4, "Email not found"), FILE_NOT_FOUND(4, "File not found"), AUTH_PASSWORD_NOT_MATCHES(2, "Password does not match"); diff --git a/src/main/java/ru/soune/nocopy/dto/RegisterRequest.java b/src/main/java/ru/soune/nocopy/dto/RegisterRequest.java deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/ru/soune/nocopy/dto/file/DeleteFileResponse.java b/src/main/java/ru/soune/nocopy/dto/file/DeleteFileResponse.java new file mode 100644 index 0000000..c45163e --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/file/DeleteFileResponse.java @@ -0,0 +1,19 @@ +package ru.soune.nocopy.dto.file; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class DeleteFileResponse { + @JsonProperty("file_id") + private String fileId; + + @JsonProperty("message") + private String message; +} diff --git a/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java b/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java new file mode 100644 index 0000000..4682dac --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/file/FileEntityRequest.java @@ -0,0 +1,34 @@ +package ru.soune.nocopy.dto.file; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class FileEntityRequest { + @JsonProperty("action") + private String action; + + @JsonProperty("file_id") + private String fileId; + + @JsonProperty("upload_session_id") + private String uploadSessionId; + + @JsonProperty("query") + private String query; + + @JsonProperty("page") + private Integer page; + + @JsonProperty("page_size") + private Integer pageSize; + + @JsonProperty("token") + private String token; +} 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 5815a03..bbc5a5f 100644 --- a/src/main/java/ru/soune/nocopy/dto/file/FileListResponse.java +++ b/src/main/java/ru/soune/nocopy/dto/file/FileListResponse.java @@ -1,5 +1,6 @@ package ru.soune.nocopy.dto.file; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -9,14 +10,24 @@ import java.util.List; @Data @Builder -@NoArgsConstructor @AllArgsConstructor +@NoArgsConstructor public class FileListResponse { - + @JsonProperty("files") private List files; - private int totalCount; - private long totalSize; + + @JsonProperty("total_count") + private Integer totalCount; + + @JsonProperty("total_size") + private Long totalSize; + + @JsonProperty("formatted_total_size") private String formattedTotalSize; - private int page; - private int pageSize; + + @JsonProperty("page") + private Integer page; + + @JsonProperty("page_size") + private Integer pageSize; } diff --git a/src/main/java/ru/soune/nocopy/dto/file/FileResponse.java b/src/main/java/ru/soune/nocopy/dto/file/FileResponse.java new file mode 100644 index 0000000..4786748 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/file/FileResponse.java @@ -0,0 +1,22 @@ +package ru.soune.nocopy.dto.file; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FileResponse { + + private List files; + private int totalCount; + private long totalSize; + private String formattedTotalSize; + private int page; + private int pageSize; +} diff --git a/src/main/java/ru/soune/nocopy/dto/file/StorageUsageResponse.java b/src/main/java/ru/soune/nocopy/dto/file/StorageUsageResponse.java new file mode 100644 index 0000000..c4ae5ec --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/file/StorageUsageResponse.java @@ -0,0 +1,22 @@ +package ru.soune.nocopy.dto.file; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class StorageUsageResponse { + @JsonProperty("user_id") + private Long userId; + + @JsonProperty("storage_used") + private Long storageUsed; + + @JsonProperty("formatted_storage_used") + private String formattedStorageUsed; +} diff --git a/src/main/java/ru/soune/nocopy/exception/UserAlreadyExistsException.java b/src/main/java/ru/soune/nocopy/exception/UserAlreadyExistsException.java deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java b/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java new file mode 100644 index 0000000..5c1b3d0 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java @@ -0,0 +1,292 @@ +package ru.soune.nocopy.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import ru.soune.nocopy.dto.*; +import ru.soune.nocopy.dto.file.*; +import ru.soune.nocopy.entity.AuthToken; +import ru.soune.nocopy.exception.NotFoundAuthToken; +import ru.soune.nocopy.repository.AuthTokenRepository; +import ru.soune.nocopy.service.file.FileEntityService; + +import java.util.Arrays; + +@Slf4j +@Component +@RequiredArgsConstructor +public class FileEntityHandler implements RequestHandler { + + private final FileEntityService fileEntityService; + private final AuthTokenRepository authTokenRepository; + private final ObjectMapper objectMapper; + + @Override + public BaseResponse handle(BaseRequest request) { + try { + FileEntityRequest fileRequest = objectMapper.convertValue( + request.getMessageBody(), FileEntityRequest.class); + + String action = fileRequest.getAction(); + + switch (action) { + case "get_file_info": + return handleGetFileInfo(request, fileRequest); + case "get_file_by_session": + return handleGetFileBySession(request, fileRequest); + case "get_user_files": + return handleGetUserFiles(request, fileRequest); + case "search_files": + return handleSearchFiles(request, fileRequest); + case "get_storage_usage": + return handleGetStorageUsage(request, fileRequest); + case "delete_file": + return handleDeleteFile(request, fileRequest); + default: + ActionResponse response = ActionResponse.builder() + .action(action) + .availableActions(Arrays.asList( + "get_file_info", "get_file_by_session", "get_user_files", + "search_files", "get_storage_usage", "delete_file")) + .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 handleGetFileInfo(BaseRequest request, FileEntityRequest fileRequest) { + try { + Long userId = getUserIdFromToken(fileRequest.getToken()); + FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId()); + + 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 handleGetFileBySession(BaseRequest request, FileEntityRequest fileRequest) { + try { + Long userId = getUserIdFromToken(fileRequest.getToken()); + FileEntityResponse fileInfo = fileEntityService.getByUploadSessionId(fileRequest.getUploadSessionId()); + + 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 = getUserIdFromToken(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); + + 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 = getUserIdFromToken(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); + + var filteredFiles = allFiles.getFiles().stream() + .filter(f -> f.getOriginalFileName().toLowerCase().contains(fileRequest.getQuery().toLowerCase())) + .toList(); + + int start = (page - 1) * pageSize; + int end = Math.min(start + pageSize, filteredFiles.size()); + + FileListResponse response = FileListResponse.builder() + .files(filteredFiles.subList(start, Math.min(end, filteredFiles.size()))) + .totalCount(filteredFiles.size()) + .totalSize(allFiles.getTotalSize()) + .formattedTotalSize(allFiles.getFormattedTotalSize()) + .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 handleGetStorageUsage(BaseRequest request, FileEntityRequest fileRequest) { + try { + Long userId = getUserIdFromToken(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 handleDeleteFile(BaseRequest request, FileEntityRequest fileRequest) { + try { + Long userId = getUserIdFromToken(fileRequest.getToken()); + FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId()); + + if (!fileInfo.getUserId().equals(userId)) { + return new BaseResponse(request.getMsgId(), + MessageCode.ACCESS_DENIED.getCode(), + "Access denied", + null); + } + + fileEntityService.markAsDeleted(fileRequest.getFileId()); + + DeleteFileResponse 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 Long getUserIdFromToken(String token) { + if (token == null || token.isBlank()) { + throw new NotFoundAuthToken("Token is required"); + } + + if (token.startsWith("Bearer ")) { + token = token.replace("Bearer ", ""); + } + + AuthToken authToken = authTokenRepository.findByToken(token) + .orElseThrow(() -> new NotFoundAuthToken("Token not found")); + return authToken.getUser().getId(); + } + + 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); + } +} diff --git a/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java b/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java index 5bc39a2..7bd858e 100644 --- a/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java +++ b/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java @@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.soune.nocopy.dto.file.FileEntityResponse; -import ru.soune.nocopy.dto.file.FileListResponse; +import ru.soune.nocopy.dto.file.FileResponse; import ru.soune.nocopy.entity.file.FileEntity; import ru.soune.nocopy.entity.file.FileStatus; import ru.soune.nocopy.entity.file.FileUploadSession; @@ -27,9 +27,6 @@ public class FileEntityService { private final FileEntityRepository fileEntityRepository; - /** - * Создает FileEntity на основе завершенной сессии загрузки - */ @Transactional public FileEntity createFromUploadSession(FileUploadSession session, String checksum) { log.info("Creating FileEntity for upload session: {}", session.getUploadId()); @@ -72,9 +69,7 @@ public class FileEntityService { } } - /** - * Извлекает расширение файла из имени - */ + private String extractFileExtension(String fileName) { int dotIndex = fileName.lastIndexOf('.'); if (dotIndex > 0 && dotIndex < fileName.length() - 1) { @@ -83,9 +78,6 @@ public class FileEntityService { return ""; } - /** - * Получает FileEntity по ID - */ @Transactional(readOnly = true) public FileEntityResponse getById(String fileId) { FileEntity fileEntity = fileEntityRepository.findById(fileId) @@ -94,9 +86,6 @@ public class FileEntityService { return convertToResponse(fileEntity); } - /** - * Получает FileEntity по ID сессии загрузки - */ @Transactional(readOnly = true) public FileEntityResponse getByUploadSessionId(String uploadSessionId) { FileEntity fileEntity = fileEntityRepository.findByUploadSessionId(uploadSessionId) @@ -106,9 +95,6 @@ public class FileEntityService { return convertToResponse(fileEntity); } - /** - * Получает FileEntity по пути файла - */ @Transactional(readOnly = true) public FileEntityResponse getByFilePath(String filePath) { FileEntity fileEntity = fileEntityRepository.findByFilePath(filePath) @@ -117,11 +103,9 @@ public class FileEntityService { return convertToResponse(fileEntity); } - /** - * Получает все файлы пользователя - */ + @Transactional(readOnly = true) - public FileListResponse getAllUserFiles(Long userId) { + public FileResponse getAllUserFiles(Long userId) { List fileEntities = fileEntityRepository.findByUserIdAndStatus( userId, FileStatus.ACTIVE); @@ -133,7 +117,7 @@ public class FileEntityService { .mapToLong(FileEntity::getFileSize) .sum(); - return FileListResponse.builder() + return FileResponse.builder() .files(files) .totalCount(files.size()) .totalSize(totalSize) @@ -143,11 +127,9 @@ public class FileEntityService { .build(); } - /** - * Получает файлы пользователя с пагинацией - */ + @Transactional(readOnly = true) - public FileListResponse getUserFiles(Long userId, int page, int pageSize) { + public FileResponse getUserFiles(Long userId, int page, int pageSize) { List allFiles = fileEntityRepository.findByUserIdAndStatus( userId, FileStatus.ACTIVE); @@ -155,7 +137,7 @@ public class FileEntityService { int end = Math.min(start + pageSize, allFiles.size()); if (start >= allFiles.size()) { - return FileListResponse.builder() + return FileResponse.builder() .files(List.of()) .totalCount(allFiles.size()) .totalSize(0) @@ -175,7 +157,7 @@ public class FileEntityService { .mapToLong(FileEntity::getFileSize) .sum(); - return FileListResponse.builder() + return FileResponse.builder() .files(files) .totalCount(allFiles.size()) .totalSize(totalSize) @@ -185,9 +167,6 @@ public class FileEntityService { .build(); } - /** - * Помечает файл как удаленный (мягкое удаление) - */ @Transactional public void markAsDeleted(String fileId) { FileEntity fileEntity = fileEntityRepository.findById(fileId) @@ -200,18 +179,12 @@ public class FileEntityService { log.info("FileEntity marked as deleted: {}", fileId); } - /** - * Получает общий размер файлов пользователя - */ @Transactional(readOnly = true) public long getUserStorageUsed(Long userId) { Long totalSize = fileEntityRepository.getTotalSizeByUserId(userId); return totalSize != null ? totalSize : 0L; } - /** - * Поиск файлов пользователя по имени - */ @Transactional(readOnly = true) public List searchFiles(Long userId, String query) { List files = fileEntityRepository.searchByFileName(userId, query); @@ -222,9 +195,6 @@ public class FileEntityService { .collect(Collectors.toList()); } - /** - * Проверяет, существует ли файл на диске - */ private boolean checkFileExistsOnDisk(String filePath) { try { return Files.exists(Paths.get(filePath)); @@ -234,9 +204,6 @@ public class FileEntityService { } } - /** - * Конвертирует FileEntity в DTO - */ private FileEntityResponse convertToResponse(FileEntity fileEntity) { boolean existsOnDisk = checkFileExistsOnDisk(fileEntity.getFilePath()); @@ -260,9 +227,6 @@ public class FileEntityService { .build(); } - /** - * Форматирует размер файла в читаемый вид - */ private String formatFileSize(long size) { if (size < 1024) { return size + " B";