Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/chung step4 #11

Merged
merged 21 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
package com.c4cometrue.mystorage.common;

import java.time.ZoneOffset;
import java.time.ZonedDateTime;

import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import lombok.Getter;

import java.time.ZoneOffset;
import java.time.ZonedDateTime;

@Getter
@MappedSuperclass
public class MetadataBaseEntity {
@Column(updatable = false)
private ZonedDateTime createdAt;
private ZonedDateTime updatedAt;
@Column(updatable = false)
private ZonedDateTime createdAt;
private ZonedDateTime updatedAt;

@PrePersist
public void prePersist() {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
createdAt = now;
updatedAt = now;
}
@PrePersist
public void prePersist() {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
createdAt = now;
updatedAt = now;
}

@PreUpdate
public void preUpdate() {
updatedAt = ZonedDateTime.now(ZoneOffset.UTC);
}
@PreUpdate
public void preUpdate() {
updatedAt = ZonedDateTime.now(ZoneOffset.UTC);
}
}
42 changes: 23 additions & 19 deletions src/main/java/com/c4cometrue/mystorage/exception/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,37 @@
@Getter
@AllArgsConstructor
public enum ErrorCode {
UNAUTHORIZED_FILE_ACCESS(HttpStatus.FORBIDDEN, "비정상적인 요청입니다."),
UNAUTHORIZED_FILE_ACCESS(HttpStatus.FORBIDDEN, "비정상적인 요청입니다."),

CANNOT_FOUND_FILE(HttpStatus.NOT_FOUND, "해당 파일을 찾을 수 없습니다."),
CANNOT_FOUND_FILE(HttpStatus.NOT_FOUND, "해당 파일을 찾을 수 없습니다."),

FILE_COPY_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "파일 복사 중 오류가 발생했습니다."),
FILE_COPY_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "파일 복사 중 오류가 발생했습니다."),

FILE_DELETE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "파일 삭제 중 오류가 발생했습니다."),
FILE_DELETE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "파일 삭제 중 오류가 발생했습니다."),

DUPLICATE_FILE_NAME(HttpStatus.BAD_REQUEST, "파일 업로드에 중복이 발생 했습니다"),
DUPLICATE_FILE_NAME(HttpStatus.BAD_REQUEST, "파일 업로드에 중복이 발생 했습니다"),

FOLDER_CREATE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "폴더 생성 중 오류가 발생했습니다"),
UNAUTHORIZED_FOLDER_ACCESS(HttpStatus.FORBIDDEN, "비정상적인 요청입니다."),
DUPLICATE_FOLDER_NAME(HttpStatus.BAD_REQUEST, "폴더 업로드에 중복이 발생 했습니다"),
CANNOT_FOUND_FOLDER(HttpStatus.NOT_FOUND, "해당 폴더를 찾을 수 없습니다."),
FOLDER_CREATE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "폴더 생성 중 오류가 발생했습니다"),
UNAUTHORIZED_FOLDER_ACCESS(HttpStatus.FORBIDDEN, "비정상적인 요청입니다."),
DUPLICATE_FOLDER_NAME(HttpStatus.BAD_REQUEST, "폴더 업로드에 중복이 발생 했습니다"),
DUPLICATE_SERVER_FOLDER_NAME(HttpStatus.BAD_REQUEST, "폴더 UUID 중복이 발생 했습니다"),
CANNOT_FOUND_FOLDER(HttpStatus.NOT_FOUND, "해당 폴더를 찾을 수 없습니다."),

DUPLICATE_BASE_PATH(HttpStatus.BAD_REQUEST, "기본 경로 생성에 중복이 발생했습니다"),
DUPLICATE_BASE_PATH(HttpStatus.BAD_REQUEST, "기본 경로 생성에 중복이 발생했습니다"),

MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 맴버를 찾지 못했습니다");
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 맴버를 찾지 못했습니다"),
EXCEEDED_CAPACITY(HttpStatus.INSUFFICIENT_STORAGE, "더 이상 업로드 할 수 없습니다"),
INVALID_OPERATION(HttpStatus.BAD_REQUEST, "사용 중인 공간보다 많은 공간은 해제할 수 없습니다"),
VALIDATION_ERROR(HttpStatus.BAD_REQUEST, "유효하지 않은 요청입니다.");

private final HttpStatus httpStatus;
private final String message;
private final HttpStatus httpStatus;
private final String message;

public ServiceException serviceException() {
return new ServiceException(this.name(), message);
}
public ServiceException serviceException() {
return new ServiceException(this.name(), message);
}

public ServiceException serviceException(String debugMessage, Object... debugMessageArgs) {
return new ServiceException(this.name(), message, String.format(debugMessage, debugMessageArgs));
}
public ServiceException serviceException(String debugMessage, Object... debugMessageArgs) {
return new ServiceException(this.name(), message, String.format(debugMessage, debugMessageArgs));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.c4cometrue.mystorage.exception;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

Expand All @@ -12,4 +13,12 @@ public ResponseEntity<ErrorResponse> handleServiceException(ServiceException ser
ErrorCode errorCode = ErrorCode.valueOf(serviceException.getErrCode());
return new ResponseEntity<>(errorResponse, errorCode.getHttpStatus());
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationExceptions(MethodArgumentNotValidException ex) {
String errorMessage = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
ErrorCode errorCode = ErrorCode.VALIDATION_ERROR;
ErrorResponse errorResponse = new ErrorResponse(errorCode.name(), errorMessage);
return new ResponseEntity<>(errorResponse, errorCode.getHttpStatus());
}
}
14 changes: 7 additions & 7 deletions src/main/java/com/c4cometrue/mystorage/file/FileController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -25,25 +25,25 @@ public class FileController {

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void uploadFile(@Valid @ModelAttribute FileUploadRequest req) {
fileService.uploadFile(req.multipartFile(), req.userId(), req.parentId());
public void uploadFile(@Valid FileUploadRequest req) {
fileService.uploadFile(req.multipartFile(), req.userId(), req.parentId(), req.rootId());
}

@DeleteMapping
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteFile(@Valid FileDeleteRequest request) {
fileService.deleteFile(request.fileId(), request.userId());
public void deleteFile(@RequestBody @Valid FileDeleteRequest request) {
fileService.deleteFile(request.fileId(), request.userId(), request.rootId());
}

@GetMapping
@ResponseStatus(HttpStatus.NO_CONTENT)
public void downloadFile(@Valid FileDownloadRequest request) {
public void downloadFile(@RequestBody @Valid FileDownloadRequest request) {
fileService.downloadFile(request.fileId(), request.userPath(), request.userId());
}

@PostMapping("/move")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void moveFile(@Valid FileMoveReq req) {
public void moveFile(@RequestBody @Valid FileMoveReq req) {
fileService.moveFile(req.fileId(), req.userId(), req.destinationFolderId());
}
}
9 changes: 9 additions & 0 deletions src/main/java/com/c4cometrue/mystorage/file/FileCountReq.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.c4cometrue.mystorage.file;

import jakarta.validation.constraints.NotNull;

public record FileCountReq(
@NotNull long rootId,
@NotNull long userId
) {
}
25 changes: 25 additions & 0 deletions src/main/java/com/c4cometrue/mystorage/file/FileCountRes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.c4cometrue.mystorage.file;

import java.math.BigDecimal;

public record FileCountRes(
Long imageCnt,
Long videoCnt,
Long audioCnt,
Long documentCnt,
Long otherCnt,

BigDecimal imageCapacity,
BigDecimal videoCapacity,
BigDecimal audioCapacity,
BigDecimal documentCapacity,
BigDecimal otherCapacity
) {

public FileCountRes of(Long imageCnt, Long videoCnt, Long audioCnt, Long documentCnt, Long otherCnt,
BigDecimal imageCapacity, BigDecimal videoCapacity, BigDecimal audioCapacity,
BigDecimal documentCapacity, BigDecimal otherCapacity) {
return new FileCountRes(imageCnt, videoCnt, audioCnt, documentCnt, otherCnt, imageCapacity, videoCapacity,
audioCapacity, documentCapacity, otherCapacity);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,50 +13,50 @@
@Service
@RequiredArgsConstructor
public class FileDataHandlerService {
private final FileRepository fileRepository;
@Transactional
public void deleteBy(Long fileId) {
existBy(fileId);
fileRepository.deleteById(fileId);
}

private void existBy(Long fileId) {
if (!fileRepository.existsById(fileId)) {
throw ErrorCode.CANNOT_FOUND_FILE.serviceException("fileId : {}", fileId);
}
}

public FileMetadata findBy(Long fileId, Long userId) {
return fileRepository.findByIdAndUploaderId(fileId, userId)
.orElseThrow(() -> ErrorCode.CANNOT_FOUND_FILE.serviceException("fileId : {}, userId : {}", fileId,
userId));
}

@Transactional
public void persist(FileMetadata fileMetadata) {
fileRepository.save(fileMetadata);
}

public void duplicateBy(Long parentId, Long userId, String fileName) {
if (fileRepository.checkDuplicateFileName(parentId, userId, fileName)) {
throw ErrorCode.DUPLICATE_FILE_NAME.serviceException("fileName : {}", fileName);
}
}

public List<FileMetadata> getFileList(Long parentId, Long cursorId, Long userId, Pageable page) {
return cursorId == null ? fileRepository.findAllByParentIdAndUploaderIdOrderByIdDesc(parentId, userId, page)
: fileRepository.findByParentIdAndUploaderIdAndIdLessThanOrderByIdDesc(parentId, cursorId, userId, page);
}

public Boolean hashNext(Long parentId, Long userId, Long lastIdOfList) {
return fileRepository.existsByParentIdAndUploaderIdAndIdLessThan(parentId, userId, lastIdOfList);
}

public List<FileMetadata> findAllBy(Long parentId) {
return fileRepository.findAllByParentId(parentId);
}

public void deleteAll(List<FileMetadata> fileMetadataList) {
fileRepository.deleteAll(fileMetadataList);
}
private final FileRepository fileRepository;

@Transactional
public void deleteBy(Long fileId) {
existBy(fileId);
fileRepository.deleteById(fileId);
}

private void existBy(Long fileId) {
if (!fileRepository.existsById(fileId)) {
throw ErrorCode.CANNOT_FOUND_FILE.serviceException("fileId : {}", fileId);
}
}

public FileMetadata findBy(Long fileId, Long userId) {
return fileRepository.findByIdAndUploaderId(fileId, userId)
.orElseThrow(() -> ErrorCode.CANNOT_FOUND_FILE.serviceException("fileId : {}, userId : {}", fileId,
userId));
}

public void persist(FileMetadata fileMetadata) {
fileRepository.save(fileMetadata);
}

public void duplicateBy(Long parentId, String fileName) {
if (fileRepository.existsByParentIdAndOriginalFileName(parentId, fileName)) {
throw ErrorCode.DUPLICATE_FILE_NAME.serviceException("fileName : {}", fileName);
}
}

public List<FileMetadata> getFileList(Long parentId, Long cursorId, Long userId, Pageable page) {
return cursorId == null ? fileRepository.findAllByParentIdAndUploaderIdOrderByIdDesc(parentId, userId, page)
: fileRepository.findByParentIdAndUploaderIdAndIdLessThanOrderByIdDesc(parentId, cursorId, userId, page);
}

public Boolean hashNext(Long parentId, Long userId, Long lastIdOfList) {
return fileRepository.existsByParentIdAndUploaderIdAndIdLessThan(parentId, userId, lastIdOfList);
}

public List<FileMetadata> findAllBy(Long parentId) {
return fileRepository.findAllByParentId(parentId);
}

public void deleteAll(List<FileMetadata> fileMetadataList) {
fileRepository.deleteAll(fileMetadataList);
}
}
32 changes: 19 additions & 13 deletions src/main/java/com/c4cometrue/mystorage/file/FileMetadata.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.c4cometrue.mystorage.file;

import java.math.BigDecimal;
import java.util.UUID;

import com.c4cometrue.mystorage.common.MetadataBaseEntity;
import com.c4cometrue.mystorage.common.MetadataType;
import com.c4cometrue.mystorage.util.FileType;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
Expand All @@ -22,7 +23,10 @@
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "file_metadata", indexes = @Index(name = "index_parentId", columnList = "parentId"))
@Table(name = "file_metadata", indexes = {
@Index(name = "index_id_uploaderId", columnList = "id,uploaderId"),
@Index(name = "index_parentId_originalFileName", columnList = "parentId,originalFileName")
})
public class FileMetadata extends MetadataBaseEntity {

@Id
Expand All @@ -37,27 +41,29 @@ public class FileMetadata extends MetadataBaseEntity {
@Column(nullable = false)
private Long uploaderId;
private Long parentId;
private BigDecimal sizeInBytes;

@Column(nullable = false)
@Enumerated(EnumType.STRING)
private MetadataType metadataType;

public static String storedName() {
return UUID.randomUUID().toString();
}

public void changeParentId(Long parentId) {
this.parentId = parentId;
}
private FileType mime;

@Builder
public FileMetadata(String originalFileName, String storedFileName, String filePath, Long uploaderId,
Long parentId) {
Long parentId, FileType mime, BigDecimal sizeInBytes) {
this.originalFileName = originalFileName;
this.storedFileName = storedFileName;
this.filePath = filePath;
this.uploaderId = uploaderId;
this.parentId = parentId;
this.metadataType = MetadataType.FILE;
this.mime = mime;
this.sizeInBytes = sizeInBytes;
}

public static String storedName() {
return UUID.randomUUID().toString();
}

public void changeParentId(Long parentId) {
this.parentId = parentId;
}
}
21 changes: 9 additions & 12 deletions src/main/java/com/c4cometrue/mystorage/file/FileRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,20 @@
import org.springframework.data.jpa.repository.Query;

public interface FileRepository extends JpaRepository<FileMetadata, Long> {
Optional<FileMetadata> findByIdAndUploaderId(Long id, Long uploaderId);
Optional<FileMetadata> findByIdAndUploaderId(Long id, Long uploaderId);

@Query("SELECT CASE WHEN COUNT(m) > 0 " + "THEN TRUE " + "ELSE FALSE END " + "FROM FileMetadata m " + "WHERE "
+ "(m.parentId = :parentId OR (m.parentId IS NULL AND :parentId IS NULL)) " + "AND m.uploaderId = :uploaderId "
+ "AND m.originalFileName = :fileName ")
boolean checkDuplicateFileName(Long parentId, Long uploaderId, String fileName);
boolean existsByParentIdAndOriginalFileName(Long parentId, String fileName);

List<FileMetadata> findByParentIdAndUploaderId(Long parentId, Long userId);
List<FileMetadata> findByParentIdAndUploaderId(Long parentId, Long userId);

Boolean existsByIdAndUploaderId(Long parentId, Long userId);
Boolean existsByIdAndUploaderId(Long parentId, Long userId);

List<FileMetadata> findAllByParentIdAndUploaderIdOrderByIdDesc(Long parentId, Long uploaderId, Pageable page);
List<FileMetadata> findAllByParentIdAndUploaderIdOrderByIdDesc(Long parentId, Long uploaderId, Pageable page);

List<FileMetadata> findByParentIdAndUploaderIdAndIdLessThanOrderByIdDesc(Long parentId, Long userId, Long cursorId,
Pageable pageable);
List<FileMetadata> findByParentIdAndUploaderIdAndIdLessThanOrderByIdDesc(Long parentId, Long userId, Long cursorId,
Pageable pageable);

Boolean existsByParentIdAndUploaderIdAndIdLessThan(Long parentId, Long uploaderId, Long id);
Boolean existsByParentIdAndUploaderIdAndIdLessThan(Long parentId, Long uploaderId, Long id);

List<FileMetadata> findAllByParentId(Long parentId);
List<FileMetadata> findAllByParentId(Long parentId);
}
Loading
Loading