From 905528f53d871cdda154e8e0b1ea14fc6b7af721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=99=ED=98=B8?= <66300965+kdkdhoho@users.noreply.github.com> Date: Tue, 5 Nov 2024 22:03:52 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#326)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(BaseEntity): id로 동등성 비교할 수 있도록 equals&hashCode 재정의 (#325) * refactor: ImageService 리팩터링 (#325) * feat: 공지 이미지 업로드 API 구현 (#325) * refactor: BaseEntity의 equals&hashCode 제거 (#325) --- .../domain/ImageFileExtension.java | 20 +- .../image/application/domain/ImageType.java | 1 + .../response/ItemPresignedUrlResponse.java | 13 - .../ListItemPresignedUrlResponse.java | 11 + .../UserPresignedUrlCreateResponse.java | 12 + .../response/UserPresignedUrlResponse.java | 15 - .../application/service/ImageService.java | 451 +++++++----------- .../controller/ImageController.java | 64 +-- .../dto/request/ListImagesCreateRequest.java | 16 + .../dto/request/ListsImagesCreateRequest.java | 10 - .../list/application/domain/item/Item.java | 4 +- .../application/domain/NoticeContent.java | 6 +- ...NoticeImagePresignedUrlCreateResponse.java | 11 + .../dto/OrderAndExtensionDto.java} | 6 +- .../repository/NoticeContentRepository.java | 11 + .../application/domain/item/ItemTest.java | 2 +- 16 files changed, 282 insertions(+), 371 deletions(-) delete mode 100644 src/main/java/com/listywave/image/application/dto/response/ItemPresignedUrlResponse.java create mode 100644 src/main/java/com/listywave/image/application/dto/response/ListItemPresignedUrlResponse.java create mode 100644 src/main/java/com/listywave/image/application/dto/response/UserPresignedUrlCreateResponse.java delete mode 100644 src/main/java/com/listywave/image/application/dto/response/UserPresignedUrlResponse.java create mode 100644 src/main/java/com/listywave/image/presentation/dto/request/ListImagesCreateRequest.java delete mode 100644 src/main/java/com/listywave/image/presentation/dto/request/ListsImagesCreateRequest.java create mode 100644 src/main/java/com/listywave/notice/application/dto/NoticeImagePresignedUrlCreateResponse.java rename src/main/java/com/listywave/{image/application/dto/ExtensionRanks.java => notice/application/dto/OrderAndExtensionDto.java} (52%) create mode 100644 src/main/java/com/listywave/notice/repository/NoticeContentRepository.java diff --git a/src/main/java/com/listywave/image/application/domain/ImageFileExtension.java b/src/main/java/com/listywave/image/application/domain/ImageFileExtension.java index 40d199a9..560d8434 100644 --- a/src/main/java/com/listywave/image/application/domain/ImageFileExtension.java +++ b/src/main/java/com/listywave/image/application/domain/ImageFileExtension.java @@ -1,31 +1,27 @@ package com.listywave.image.application.domain; +import static com.listywave.common.exception.ErrorCode.RESOURCE_NOT_FOUND; + import com.fasterxml.jackson.annotation.JsonCreator; import com.listywave.common.exception.CustomException; -import com.listywave.common.exception.ErrorCode; import java.util.Arrays; import lombok.AllArgsConstructor; import lombok.Getter; -// TODO: 정수씨 이 쪽 나중에 테스트 부탁해요~ -// TODO: ImageService 쪽에서 많이 쓰이고 있어서 쉽게 리팩터링 못하겠네요.. -// TODO: uploadExtension 필드 제거하고 `fromString`을 `ofName` 로 바꿔주세요! @Getter @AllArgsConstructor public enum ImageFileExtension { - JPEG("jpeg"), - JPG("jpg"), - PNG("png"), + JPEG, + JPG, + PNG, ; - private final String uploadExtension; - @JsonCreator - public static ImageFileExtension fromString(String key) { + public static ImageFileExtension ofName(String value) { return Arrays.stream(ImageFileExtension.values()) - .filter(extensionType -> extensionType.name().equalsIgnoreCase(key)) + .filter(extensionType -> extensionType.name().equalsIgnoreCase(value)) .findFirst() - .orElseThrow(() -> new CustomException(ErrorCode.RESOURCE_NOT_FOUND, "해당 이미지 확장자가 존재하지 않습니다.")); + .orElseThrow(() -> new CustomException(RESOURCE_NOT_FOUND, "지원하지 않는 이미지 확장자입니다.")); } } diff --git a/src/main/java/com/listywave/image/application/domain/ImageType.java b/src/main/java/com/listywave/image/application/domain/ImageType.java index c6b820bc..a4d88036 100644 --- a/src/main/java/com/listywave/image/application/domain/ImageType.java +++ b/src/main/java/com/listywave/image/application/domain/ImageType.java @@ -10,5 +10,6 @@ public enum ImageType { LISTS_ITEM, USER_PROFILE, USER_BACKGROUND, + NOTICE, ; } diff --git a/src/main/java/com/listywave/image/application/dto/response/ItemPresignedUrlResponse.java b/src/main/java/com/listywave/image/application/dto/response/ItemPresignedUrlResponse.java deleted file mode 100644 index 10b613b4..00000000 --- a/src/main/java/com/listywave/image/application/dto/response/ItemPresignedUrlResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.listywave.image.application.dto.response; - -import lombok.Builder; - -@Builder -public record ItemPresignedUrlResponse(int rank, String presignedUrl) { - public static ItemPresignedUrlResponse of(int rank, String presignedUrl) { - return ItemPresignedUrlResponse.builder() - .rank(rank) - .presignedUrl(presignedUrl) - .build(); - } -} diff --git a/src/main/java/com/listywave/image/application/dto/response/ListItemPresignedUrlResponse.java b/src/main/java/com/listywave/image/application/dto/response/ListItemPresignedUrlResponse.java new file mode 100644 index 00000000..44ed424e --- /dev/null +++ b/src/main/java/com/listywave/image/application/dto/response/ListItemPresignedUrlResponse.java @@ -0,0 +1,11 @@ +package com.listywave.image.application.dto.response; + +public record ListItemPresignedUrlResponse( + int rank, + String presignedUrl +) { + + public static ListItemPresignedUrlResponse from(int rank, String presignedUrl) { + return new ListItemPresignedUrlResponse(rank, presignedUrl); + } +} diff --git a/src/main/java/com/listywave/image/application/dto/response/UserPresignedUrlCreateResponse.java b/src/main/java/com/listywave/image/application/dto/response/UserPresignedUrlCreateResponse.java new file mode 100644 index 00000000..3afa6e24 --- /dev/null +++ b/src/main/java/com/listywave/image/application/dto/response/UserPresignedUrlCreateResponse.java @@ -0,0 +1,12 @@ +package com.listywave.image.application.dto.response; + +public record UserPresignedUrlCreateResponse( + Long userId, + String profilePresignedUrl, + String backgroundPresignedUrl +) { + + public static UserPresignedUrlCreateResponse of(Long userId, String profilePresignedUrl, String backgroundPresignedUrl) { + return new UserPresignedUrlCreateResponse(userId, profilePresignedUrl, backgroundPresignedUrl); + } +} diff --git a/src/main/java/com/listywave/image/application/dto/response/UserPresignedUrlResponse.java b/src/main/java/com/listywave/image/application/dto/response/UserPresignedUrlResponse.java deleted file mode 100644 index 14ef954c..00000000 --- a/src/main/java/com/listywave/image/application/dto/response/UserPresignedUrlResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.listywave.image.application.dto.response; - -import lombok.Builder; - -@Builder -public record UserPresignedUrlResponse(Long ownerId, String profilePresignedUrl, String backgroundPresignedUrl) { - - public static UserPresignedUrlResponse of(Long ownerId, String profilePresignedUrl, String backgroundPresignedUrl) { - return UserPresignedUrlResponse.builder() - .ownerId(ownerId) - .profilePresignedUrl(profilePresignedUrl) - .backgroundPresignedUrl(backgroundPresignedUrl) - .build(); - } -} diff --git a/src/main/java/com/listywave/image/application/service/ImageService.java b/src/main/java/com/listywave/image/application/service/ImageService.java index a9a74440..c667367b 100644 --- a/src/main/java/com/listywave/image/application/service/ImageService.java +++ b/src/main/java/com/listywave/image/application/service/ImageService.java @@ -1,32 +1,42 @@ package com.listywave.image.application.service; +import static com.amazonaws.HttpMethod.PUT; +import static com.amazonaws.services.s3.Headers.S3_CANNED_ACL; +import static com.amazonaws.services.s3.model.CannedAccessControlList.PublicRead; +import static com.listywave.common.exception.ErrorCode.RESOURCE_NOT_FOUND; import static com.listywave.common.exception.ErrorCode.S3_DELETE_OBJECTS_EXCEPTION; import static com.listywave.image.application.domain.ImageType.LISTS_ITEM; +import static com.listywave.image.application.domain.ImageType.NOTICE; +import static com.listywave.image.application.domain.ImageType.USER_BACKGROUND; +import static com.listywave.image.application.domain.ImageType.USER_PROFILE; import static java.util.Locale.ENGLISH; import com.amazonaws.AmazonServiceException; -import com.amazonaws.HttpMethod; import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.Headers; -import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.DeleteObjectRequest; import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; import com.amazonaws.services.s3.model.ListObjectsV2Result; import com.amazonaws.services.s3.model.S3ObjectSummary; import com.listywave.common.exception.CustomException; -import com.listywave.common.exception.ErrorCode; import com.listywave.image.application.domain.ImageFileExtension; import com.listywave.image.application.domain.ImageType; -import com.listywave.image.application.dto.ExtensionRanks; -import com.listywave.image.application.dto.response.ItemPresignedUrlResponse; -import com.listywave.image.application.dto.response.UserPresignedUrlResponse; +import com.listywave.image.application.dto.response.ListItemPresignedUrlResponse; +import com.listywave.image.application.dto.response.UserPresignedUrlCreateResponse; +import com.listywave.image.presentation.dto.request.ListImagesCreateRequest.ExtensionRanks; import com.listywave.list.application.domain.item.Item; import com.listywave.list.application.domain.item.ItemImageUrl; import com.listywave.list.application.domain.list.ListEntity; import com.listywave.list.repository.ItemRepository; import com.listywave.list.repository.list.ListRepository; +import com.listywave.notice.application.domain.Notice; +import com.listywave.notice.application.domain.NoticeContent; +import com.listywave.notice.application.dto.NoticeImagePresignedUrlCreateResponse; +import com.listywave.notice.application.dto.OrderAndExtensionDto; +import com.listywave.notice.repository.NoticeContentRepository; +import com.listywave.notice.repository.NoticeRepository; import com.listywave.user.application.domain.User; import com.listywave.user.repository.user.UserRepository; +import java.net.URL; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -54,118 +64,123 @@ public class ImageService { private final ItemRepository itemRepository; private final ListRepository listRepository; private final UserRepository userRepository; + private final NoticeRepository noticeRepository; + private final NoticeContentRepository noticeContentRepository; - public List createListsPresignedUrl(Long loginUserId, Long listId, List extensionRanks) { - User user = userRepository.getById(loginUserId); - - ListEntity list = findListById(listId); - validateListUserMismatch(list, user); + public List createPresignedUrlOfItem(Long userId, Long listId, List extensionRanks) { + User user = userRepository.getById(userId); + ListEntity list = listRepository.getById(listId); + list.validateOwner(user); return extensionRanks.stream() - .map((extensionRank) -> { - String imageKey = generatedUUID(); - GeneratePresignedUrlRequest generatePresignedUrlRequest = - getGeneratePresignedUrl(LISTS_ITEM, listId, imageKey, extensionRank.extension()); - updateItemImageKey(listId, extensionRank, imageKey); - - return ItemPresignedUrlResponse.of( - extensionRank.rank(), - amazonS3.generatePresignedUrl(generatePresignedUrlRequest).toString()); + .map(it -> { + Item item = itemRepository.findByListIdAndRanking(listId, it.rank()) + .orElseThrow(() -> new CustomException(RESOURCE_NOT_FOUND)); + String imageKey = UUID.randomUUID().toString(); + item.updateItemImageKey(imageKey); + + String fileName = createFileName(LISTS_ITEM, listId, imageKey, it.extension()); + GeneratePresignedUrlRequest request = createGeneratePreSignedUrlRequest(fileName); + + String presignedUrl = amazonS3.generatePresignedUrl(request).toString(); + return ListItemPresignedUrlResponse.from(it.rank(), presignedUrl); } - ) - .toList(); + ).toList(); } - public void uploadCompleteItemImages(Long loginUserId, Long listId, List extensionRanks) { - final User user = userRepository.getById(loginUserId); - - ListEntity list = findListById(listId); - validateListUserMismatch(list, user); + private String createFileName( + ImageType imageType, + Long resourceId, + String imageKey, + ImageFileExtension imageFileExtension + ) { + return getCurrentProfile() + + "/" + imageType.name().toLowerCase(ENGLISH) + + "/" + resourceId + + "/" + imageKey + + "." + imageFileExtension.name().toLowerCase(ENGLISH); + } - extensionRanks.forEach( - extensionRank -> { - Item item = findItem(listId, extensionRank.rank()); - String imageUrl = createReadImageUrl(LISTS_ITEM, listId, item.getImageKey(), extensionRank.extension()); - item.updateItemImageUrl(imageUrl); - } - ); + public String getCurrentProfile() { + return Arrays.stream(environment.getActiveProfiles()) + .filter(profile -> profile.equals("dev") || profile.equals("prod")) + .findFirst() + .orElse(LOCAL); } - public UserPresignedUrlResponse updateUserImagePresignedUrl( - ImageFileExtension profileExtension, - ImageFileExtension backgroundExtension, - Long loginUserId - ) { - User user = userRepository.getById(loginUserId); - - if (isExistProfileExtension(profileExtension, backgroundExtension)) { - deleteCustomUserImageFile(user.getProfileImageUrl()); - return getUserPresignedUrlResponse( - ImageType.USER_PROFILE, - null, - profileExtension, - backgroundExtension, - user, - false - ); - } + private GeneratePresignedUrlRequest createGeneratePreSignedUrlRequest(String fileName) { + var request = new GeneratePresignedUrlRequest(bucket, fileName, PUT) + .withExpiration(createPresignedUrlExpiration()); + request.addRequestParameter(S3_CANNED_ACL, PublicRead.toString()); - if (isExistBackgroundExtension(backgroundExtension, profileExtension)) { - deleteCustomUserImageFile(user.getBackgroundImageUrl()); - return getUserPresignedUrlResponse( - null, - ImageType.USER_BACKGROUND, - profileExtension, - backgroundExtension, - user, - false - ); - } + return request; + } + + private Date createPresignedUrlExpiration() { + Date expiration = new Date(); + var expTimeMillis = expiration.getTime(); + expTimeMillis += 1000 * 60 * 30; + expiration.setTime(expTimeMillis); + return expiration; + } - deleteCustomUserImageFile(user.getProfileImageUrl()); - deleteCustomUserImageFile(user.getBackgroundImageUrl()); + public void updateAllItemsImageUrl(Long userId, Long listId, List extensionRanks) { + User user = userRepository.getById(userId); + ListEntity list = listRepository.getById(listId); + list.validateOwner(user); - return getUserPresignedUrlResponse( - ImageType.USER_PROFILE, - ImageType.USER_BACKGROUND, - profileExtension, - backgroundExtension, - user, - true + extensionRanks.forEach(it -> { + Item item = itemRepository.findByListIdAndRanking(listId, it.rank()) + .orElseThrow(() -> new CustomException(RESOURCE_NOT_FOUND, "해당 아이템이 존재하지 않습니다.")); + String imageUrl = createReadImageUrl(LISTS_ITEM, listId, item.getImageKey(), it.extension()); + item.updateItemImageUrl(new ItemImageUrl(imageUrl)); + } ); } - public void uploadCompleteUserImages( + private String createReadImageUrl( + ImageType imageType, + Long resourceId, + String imageKey, + ImageFileExtension imageFileExtension + ) { + return IMAGE_DOMAIN_URL + + "/" + getCurrentProfile() + + "/" + imageType.name().toLowerCase(ENGLISH) + + "/" + resourceId + + "/" + imageKey + + "." + imageFileExtension.name().toLowerCase(ENGLISH); + } + + public UserPresignedUrlCreateResponse createPresignedUrlOfUserImage( ImageFileExtension profileExtension, ImageFileExtension backgroundExtension, - Long ownerId + Long userId ) { - User user = userRepository.getById(ownerId); + User user = userRepository.getById(userId); - String profileImageUrl = ""; - String backgroundImageUrl = ""; - boolean isBoth = true; + String profileImageKey = UUID.randomUUID().toString(); + String backgroundImageKey = UUID.randomUUID().toString(); - if (isExistProfileExtension(profileExtension, backgroundExtension)) { - profileImageUrl = createReadImageUrl(ImageType.USER_PROFILE, user.getId(), user.getProfileImageUrl(), profileExtension); - user.updateUserImageUrl(profileImageUrl, backgroundImageUrl); - isBoth = false; - } + String profilePresignedUrl = ""; + String backgroundPresignedUrl = ""; + + if (profileExtension != null) { + deleteUserImageFileIfCustomImage(user.getProfileImageUrl()); - if (isExistBackgroundExtension(backgroundExtension, profileExtension)) { - backgroundImageUrl = createReadImageUrl(ImageType.USER_BACKGROUND, user.getId(), user.getBackgroundImageUrl(), backgroundExtension); - user.updateUserImageUrl(profileImageUrl, backgroundImageUrl); - isBoth = false; + var presignedUrlRequest = createUserGeneratePresignedUrlRequest(USER_PROFILE, profileExtension, user, profileImageKey, ""); + profilePresignedUrl = amazonS3.generatePresignedUrl(presignedUrlRequest).toString(); } + if (backgroundExtension != null) { + deleteUserImageFileIfCustomImage(user.getBackgroundImageUrl()); - if (isBoth) { - profileImageUrl = createReadImageUrl(ImageType.USER_PROFILE, user.getId(), user.getProfileImageUrl(), profileExtension); - backgroundImageUrl = createReadImageUrl(ImageType.USER_BACKGROUND, user.getId(), user.getBackgroundImageUrl(), backgroundExtension); - user.updateUserImageUrl(profileImageUrl, backgroundImageUrl); + var presignedUrlRequest = createUserGeneratePresignedUrlRequest(USER_BACKGROUND, backgroundExtension, user, "", backgroundImageKey); + backgroundPresignedUrl = amazonS3.generatePresignedUrl(presignedUrlRequest).toString(); } + return UserPresignedUrlCreateResponse.of(userId, profilePresignedUrl, backgroundPresignedUrl); } - private void deleteCustomUserImageFile(String imageUrl) { + private void deleteUserImageFileIfCustomImage(String imageUrl) { if (isCustomUserImage(imageUrl)) { String fileFullPath = getFileFullName(imageUrl); deleteImageFile(fileFullPath); @@ -173,21 +188,14 @@ private void deleteCustomUserImageFile(String imageUrl) { } private boolean isCustomUserImage(String url) { - if (url.split("/").length >= 4) { - String type = url.split("/")[3]; + String[] split = url.split("/"); + if (split.length >= 4) { + String type = split[3]; return !type.equals("basic"); } return false; } - private void deleteImageFile(String fileFullPath) { - try { - amazonS3.deleteObject(bucket, fileFullPath); - } catch (AmazonServiceException e) { - throw new CustomException(ErrorCode.S3_DELETE_OBJECTS_EXCEPTION); - } - } - private String getFileFullName(String url) { String[] parts = url.split("/"); StringBuilder extracted = new StringBuilder(); @@ -200,63 +208,15 @@ private String getFileFullName(String url) { return extracted.toString(); } - private UserPresignedUrlResponse getUserPresignedUrlResponse( - ImageType profileImageType, - ImageType backgroundImageType, - ImageFileExtension profileExtension, - ImageFileExtension backgroundExtension, - User user, - Boolean isBoth - ) { - if (!isBoth && profileImageType != null) { - return generateUserPresignedUrlResponse(profileImageType, profileExtension, user, generatedUUID(), ""); - } - if (!isBoth && backgroundImageType != null) { - return generateUserPresignedUrlResponse(backgroundImageType, backgroundExtension, user, "", generatedUUID()); + private void deleteImageFile(String filePath) { + try { + amazonS3.deleteObject(bucket, filePath); + } catch (AmazonServiceException e) { + throw new CustomException(S3_DELETE_OBJECTS_EXCEPTION); } - return generateUserPresignedUrlResponseByBoth(profileImageType, backgroundImageType, profileExtension, backgroundExtension, user); - } - - private UserPresignedUrlResponse generateUserPresignedUrlResponseByBoth( - ImageType profileImageType, - ImageType backgroundImageType, - ImageFileExtension profileExtension, - ImageFileExtension backgroundExtension, - User user - ) { - String profileImageKey = generatedUUID(); - String backgroundImageKey = generatedUUID(); - - GeneratePresignedUrlRequest profileUrlRequest = - getGeneratePresignedUrl(profileImageType, user.getId(), profileImageKey, profileExtension); - GeneratePresignedUrlRequest backgroundUrlRequest = - getGeneratePresignedUrlRequest(backgroundImageType, backgroundExtension, user, profileImageKey, backgroundImageKey); - return UserPresignedUrlResponse.of( - user.getId(), - amazonS3.generatePresignedUrl(profileUrlRequest).toString(), - amazonS3.generatePresignedUrl(backgroundUrlRequest).toString() - ); - } - - private UserPresignedUrlResponse generateUserPresignedUrlResponse( - ImageType imageType, - ImageFileExtension extension, - User user, - String profileImageKey, - String backgroundImageKey - ) { - GeneratePresignedUrlRequest presignedUrlRequest = - getGeneratePresignedUrlRequest(imageType, extension, user, profileImageKey, backgroundImageKey); - return UserPresignedUrlResponse.of( - user.getId(), - imageType == ImageType.USER_PROFILE ? - amazonS3.generatePresignedUrl(presignedUrlRequest).toString() : "", - imageType == ImageType.USER_BACKGROUND ? - amazonS3.generatePresignedUrl(presignedUrlRequest).toString() : "" - ); } - private GeneratePresignedUrlRequest getGeneratePresignedUrlRequest( + private GeneratePresignedUrlRequest createUserGeneratePresignedUrlRequest( ImageType imageType, ImageFileExtension extension, User user, @@ -264,146 +224,36 @@ private GeneratePresignedUrlRequest getGeneratePresignedUrlRequest( String backgroundImageKey ) { String imageKey = ""; - if (imageType == ImageType.USER_PROFILE) { + if (imageType == USER_PROFILE) { imageKey = profileImageKey; } - if (imageType == ImageType.USER_BACKGROUND) { + if (imageType == USER_BACKGROUND) { imageKey = backgroundImageKey; } - GeneratePresignedUrlRequest generatePresignedUrlRequest = - getGeneratePresignedUrl(imageType, user.getId(), imageKey, extension); - updateUserImageKey(user, profileImageKey, backgroundImageKey); - return generatePresignedUrlRequest; - } + user.updateUserImageUrl(profileImageKey, backgroundImageKey); - private boolean isExistProfileExtension( - ImageFileExtension profileExtension, - ImageFileExtension backgroundExtension - ) { - return backgroundExtension == null && - profileExtension != null && - !profileExtension.getUploadExtension().isEmpty(); + String fileName = createFileName(imageType, user.getId(), imageKey, extension); + return createGeneratePreSignedUrlRequest(fileName); } - private boolean isExistBackgroundExtension( + public void updateUserImages( + ImageFileExtension profileExtension, ImageFileExtension backgroundExtension, - ImageFileExtension profileExtension - ) { - return profileExtension == null && - backgroundExtension != null && - !backgroundExtension.getUploadExtension().isEmpty(); - } - - private GeneratePresignedUrlRequest getGeneratePresignedUrl( - ImageType type, - Long targetId, - String imageKey, - ImageFileExtension extension - ) { - String fileName = createFileName(type, targetId, imageKey, extension); - return createGeneratePreSignedUrlRequest(bucket, fileName); - } - - - private String createFileName( - ImageType imageType, - Long targetId, - String imageKey, - ImageFileExtension imageFileExtension - ) { - return getCurrentProfile() - + "/" - + imageType.name().toLowerCase(ENGLISH) - + "/" - + targetId - + "/" - + imageKey - + "." - + imageFileExtension.getUploadExtension(); - } - - private String createReadImageUrl( - ImageType imageType, - Long targetId, - String imageKey, - ImageFileExtension imageFileExtension - ) { - return IMAGE_DOMAIN_URL - + "/" - + getCurrentProfile() - + "/" - + imageType.name().toLowerCase(ENGLISH) - + "/" - + targetId - + "/" - + imageKey - + "." - + imageFileExtension.getUploadExtension(); - } - - private void updateItemImageKey(Long listId, ExtensionRanks extensionRank, String imageKey) { - findItem(listId, extensionRank.rank()) - .updateItemImageKey(imageKey); - } - - private Item findItem(Long listId, int rank) { - return itemRepository - .findByListIdAndRanking(listId, rank) - .orElseThrow(() -> new CustomException(ErrorCode.RESOURCE_NOT_FOUND, "해당 아이템이 존재하지 않습니다.")); - } - - private void updateUserImageKey(User user, String profileImageKey, String backgroundImageKey) { - user.updateUserImageUrl(profileImageKey, backgroundImageKey); - } - - private GeneratePresignedUrlRequest createGeneratePreSignedUrlRequest( - String bucket, - String fileName + Long ownerId ) { - GeneratePresignedUrlRequest generatePresignedUrlRequest = - new GeneratePresignedUrlRequest(bucket, fileName) - .withMethod(HttpMethod.PUT) - .withExpiration(getPresignedUrlExpiration()); - - generatePresignedUrlRequest.addRequestParameter( - Headers.S3_CANNED_ACL, CannedAccessControlList.PublicRead.toString() - ); - return generatePresignedUrlRequest; - } - - private Date getPresignedUrlExpiration() { - Date expiration = new Date(); - var expTimeMillis = expiration.getTime(); - expTimeMillis += 1000 * 60 * 30; - expiration.setTime(expTimeMillis); - return expiration; - } + User user = userRepository.getById(ownerId); - private String generatedUUID() { - return UUID.randomUUID().toString(); - } + String profileImageUrl = ""; + String backgroundImageUrl = ""; - private void validateListUserMismatch(ListEntity list, User user) { - if (!list.getUser().getId().equals(user.getId())) { - throw new CustomException(ErrorCode.INVALID_ACCESS, "리스트를 생성한 유저와 로그인한 계정이 일치하지 않습니다."); + if (profileExtension != null) { + profileImageUrl = createReadImageUrl(USER_PROFILE, user.getId(), user.getProfileImageUrl(), profileExtension); + } + if (backgroundExtension != null) { + backgroundImageUrl = createReadImageUrl(USER_BACKGROUND, user.getId(), user.getBackgroundImageUrl(), backgroundExtension); } - } - - private ListEntity findListById(Long listId) { - return listRepository - .findById(listId) - .orElseThrow(() -> new CustomException(ErrorCode.RESOURCE_NOT_FOUND, "존재하지 않는 리스트입니다.")); - } - - public String getCurrentProfile() { - return Arrays.stream(environment.getActiveProfiles()) - .filter(this::isActivateDev) - .findFirst() - .orElse(LOCAL); - } - private boolean isActivateDev(String profile) { - return profile.equals(DEV); + user.updateUserImageUrl(profileImageUrl, backgroundImageUrl); } @Async @@ -435,4 +285,37 @@ public void deleteImageOfItem(Long listId, Long itemId, Long loginUserID) { String fileFullName = getFileFullName(itemImageUrl.getValue()); deleteImageFile(fileFullName); } + + public List createNoticeImagePresignedUrl( + Long noticeId, + List requests + ) { + Notice notice = noticeRepository.getById(noticeId); + + return requests.stream() + .map(it -> { + String imageKey = UUID.randomUUID().toString(); + NoticeContent noticeContent = noticeContentRepository.findByNoticeAndOrder(notice, it.order()) + .orElseThrow(); + + noticeContent.updateImageUrl(imageKey); + + String fileName = createFileName(NOTICE, noticeId, imageKey, it.extension()); + GeneratePresignedUrlRequest request = createGeneratePreSignedUrlRequest(fileName); + URL presignedUrl = amazonS3.generatePresignedUrl(request); + + return NoticeImagePresignedUrlCreateResponse.of(it.order(), presignedUrl.toString()); + }).toList(); + } + + public void updateNoticeContentImages(Long noticeId, List requests) { + Notice notice = noticeRepository.getById(noticeId); + requests.forEach(it -> { + NoticeContent noticeContent = noticeContentRepository.findByNoticeAndOrder(notice, it.order()) + .orElseThrow(); + + String imageUrl = createReadImageUrl(NOTICE, noticeId, noticeContent.getImageUrl(), it.extension()); + noticeContent.updateImageUrl(imageUrl); + }); + } } diff --git a/src/main/java/com/listywave/image/presentation/controller/ImageController.java b/src/main/java/com/listywave/image/presentation/controller/ImageController.java index 9f63a296..156e3f57 100644 --- a/src/main/java/com/listywave/image/presentation/controller/ImageController.java +++ b/src/main/java/com/listywave/image/presentation/controller/ImageController.java @@ -5,11 +5,13 @@ import com.listywave.image.application.domain.DefaultProfileImages; import com.listywave.image.application.dto.response.DefaultBackgroundImageUrlResponse; import com.listywave.image.application.dto.response.DefaultProfileImageUrlResponse; -import com.listywave.image.application.dto.response.ItemPresignedUrlResponse; -import com.listywave.image.application.dto.response.UserPresignedUrlResponse; +import com.listywave.image.application.dto.response.ListItemPresignedUrlResponse; +import com.listywave.image.application.dto.response.UserPresignedUrlCreateResponse; import com.listywave.image.application.service.ImageService; -import com.listywave.image.presentation.dto.request.ListsImagesCreateRequest; +import com.listywave.image.presentation.dto.request.ListImagesCreateRequest; import com.listywave.image.presentation.dto.request.UserImageUpdateRequest; +import com.listywave.notice.application.dto.NoticeImagePresignedUrlCreateResponse; +import com.listywave.notice.application.dto.OrderAndExtensionDto; import java.util.Arrays; import java.util.List; import lombok.RequiredArgsConstructor; @@ -28,50 +30,35 @@ public class ImageController { private final ImageService imageService; @PostMapping("/lists/upload-url") - ResponseEntity> listItemPresignedUrlCreate( - @RequestBody ListsImagesCreateRequest request, - @Auth Long loginUserId + ResponseEntity> createPresignedUrlOfItem( + @RequestBody ListImagesCreateRequest request, + @Auth Long userId ) { - List response = imageService.createListsPresignedUrl( - loginUserId, - request.listId(), - request.extensionRanks() - ); + var response = imageService.createPresignedUrlOfItem(userId, request.listId(), request.extensionRanks()); return ResponseEntity.ok().body(response); } @PostMapping("/lists/upload-complete") - ResponseEntity listItemImagesUpload( - @RequestBody ListsImagesCreateRequest request, - @Auth Long loginUserId - ) { - imageService.uploadCompleteItemImages(loginUserId, request.listId(), request.extensionRanks()); + ResponseEntity completeUploadItems(@RequestBody ListImagesCreateRequest request, @Auth Long userId) { + imageService.updateAllItemsImageUrl(userId, request.listId(), request.extensionRanks()); return ResponseEntity.noContent().build(); } @PostMapping("/users/upload-url") - ResponseEntity userImagePresignedUrlCreate( + ResponseEntity createPresignedUrlOfUserImage( @RequestBody UserImageUpdateRequest request, - @Auth Long loginUserId + @Auth Long userId ) { - UserPresignedUrlResponse userPresignedUrlResponse = imageService.updateUserImagePresignedUrl( - request.profileExtension(), - request.backgroundExtension(), - loginUserId - ); + var userPresignedUrlResponse = imageService.createPresignedUrlOfUserImage(request.profileExtension(), request.backgroundExtension(), userId); return ResponseEntity.ok(userPresignedUrlResponse); } @PostMapping("/users/upload-complete") - ResponseEntity userImageUpload( + ResponseEntity completeUploadUserImage( @RequestBody UserImageUpdateRequest request, - @Auth Long loginUserId + @Auth Long userId ) { - imageService.uploadCompleteUserImages( - request.profileExtension(), - request.backgroundExtension(), - loginUserId - ); + imageService.updateUserImages(request.profileExtension(), request.backgroundExtension(), userId); return ResponseEntity.noContent().build(); } @@ -101,4 +88,21 @@ ResponseEntity> getAllDefaultBackgroundI return ResponseEntity.ok().body(response); } + @PostMapping("/admin/notices/{noticeId}/presigned-url") + ResponseEntity> createPresignedUrlOfNoticeContent( + @PathVariable Long noticeId, + @RequestBody List request + ) { + var result = imageService.createNoticeImagePresignedUrl(noticeId, request); + return ResponseEntity.ok(result); + } + + @PostMapping("/admin/notices/{noticeId}/upload-complete") + ResponseEntity completeUploadNoticeImages( + @PathVariable Long noticeId, + @RequestBody List requests + ) { + imageService.updateNoticeContentImages(noticeId, requests); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/com/listywave/image/presentation/dto/request/ListImagesCreateRequest.java b/src/main/java/com/listywave/image/presentation/dto/request/ListImagesCreateRequest.java new file mode 100644 index 00000000..71deb68d --- /dev/null +++ b/src/main/java/com/listywave/image/presentation/dto/request/ListImagesCreateRequest.java @@ -0,0 +1,16 @@ +package com.listywave.image.presentation.dto.request; + +import com.listywave.image.application.domain.ImageFileExtension; +import java.util.List; + +public record ListImagesCreateRequest( + Long listId, + List extensionRanks +) { + + public record ExtensionRanks( + int rank, + ImageFileExtension extension + ) { + } +} diff --git a/src/main/java/com/listywave/image/presentation/dto/request/ListsImagesCreateRequest.java b/src/main/java/com/listywave/image/presentation/dto/request/ListsImagesCreateRequest.java deleted file mode 100644 index 8e70c235..00000000 --- a/src/main/java/com/listywave/image/presentation/dto/request/ListsImagesCreateRequest.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.listywave.image.presentation.dto.request; - -import com.listywave.image.application.dto.ExtensionRanks; -import java.util.List; - -public record ListsImagesCreateRequest( - Long listId, - List extensionRanks -) { -} diff --git a/src/main/java/com/listywave/list/application/domain/item/Item.java b/src/main/java/com/listywave/list/application/domain/item/Item.java index 35aa5402..e1e8dc71 100644 --- a/src/main/java/com/listywave/list/application/domain/item/Item.java +++ b/src/main/java/com/listywave/list/application/domain/item/Item.java @@ -54,8 +54,8 @@ public void updateItemImageKey(String imageKey) { this.imageKey = imageKey; } - public void updateItemImageUrl(String imageUrl) { - this.imageUrl = new ItemImageUrl(imageUrl); + public void updateItemImageUrl(ItemImageUrl imageUrl) { + this.imageUrl = imageUrl; } public void updateList(ListEntity list) { diff --git a/src/main/java/com/listywave/notice/application/domain/NoticeContent.java b/src/main/java/com/listywave/notice/application/domain/NoticeContent.java index 9c90a959..ac61a0d1 100644 --- a/src/main/java/com/listywave/notice/application/domain/NoticeContent.java +++ b/src/main/java/com/listywave/notice/application/domain/NoticeContent.java @@ -37,7 +37,7 @@ public class NoticeContent { @Column(nullable = true, length = 1000) private String description; - + @Column(nullable = true, length = 2048) private String imageUrl; @@ -58,4 +58,8 @@ public static NoticeContent create( ) { return new NoticeContent(null, notice, order, type, description, imageUrl, buttonName, buttonLink); } + + public void updateImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } } diff --git a/src/main/java/com/listywave/notice/application/dto/NoticeImagePresignedUrlCreateResponse.java b/src/main/java/com/listywave/notice/application/dto/NoticeImagePresignedUrlCreateResponse.java new file mode 100644 index 00000000..a116b998 --- /dev/null +++ b/src/main/java/com/listywave/notice/application/dto/NoticeImagePresignedUrlCreateResponse.java @@ -0,0 +1,11 @@ +package com.listywave.notice.application.dto; + +public record NoticeImagePresignedUrlCreateResponse( + int order, + String presignedUrl +) { + + public static NoticeImagePresignedUrlCreateResponse of(int order, String presignedUrl) { + return new NoticeImagePresignedUrlCreateResponse(order, presignedUrl); + } +} diff --git a/src/main/java/com/listywave/image/application/dto/ExtensionRanks.java b/src/main/java/com/listywave/notice/application/dto/OrderAndExtensionDto.java similarity index 52% rename from src/main/java/com/listywave/image/application/dto/ExtensionRanks.java rename to src/main/java/com/listywave/notice/application/dto/OrderAndExtensionDto.java index e96df4a9..2e9dacca 100644 --- a/src/main/java/com/listywave/image/application/dto/ExtensionRanks.java +++ b/src/main/java/com/listywave/notice/application/dto/OrderAndExtensionDto.java @@ -1,9 +1,9 @@ -package com.listywave.image.application.dto; +package com.listywave.notice.application.dto; import com.listywave.image.application.domain.ImageFileExtension; -public record ExtensionRanks( - int rank, +public record OrderAndExtensionDto( + int order, ImageFileExtension extension ) { } diff --git a/src/main/java/com/listywave/notice/repository/NoticeContentRepository.java b/src/main/java/com/listywave/notice/repository/NoticeContentRepository.java new file mode 100644 index 00000000..81741aed --- /dev/null +++ b/src/main/java/com/listywave/notice/repository/NoticeContentRepository.java @@ -0,0 +1,11 @@ +package com.listywave.notice.repository; + +import com.listywave.notice.application.domain.Notice; +import com.listywave.notice.application.domain.NoticeContent; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NoticeContentRepository extends JpaRepository { + + Optional findByNoticeAndOrder(Notice notice, int order); +} diff --git a/src/test/java/com/listywave/list/application/domain/item/ItemTest.java b/src/test/java/com/listywave/list/application/domain/item/ItemTest.java index eae486ae..0643855d 100644 --- a/src/test/java/com/listywave/list/application/domain/item/ItemTest.java +++ b/src/test/java/com/listywave/list/application/domain/item/ItemTest.java @@ -39,7 +39,7 @@ class ItemTest { void ItemImageUrl을_수정할_수_있다() { String newValue = "sfksadfhskfhjaf"; - item.updateItemImageUrl(newValue); + item.updateItemImageUrl(new ItemImageUrl(newValue)); assertThat(item.getImageUrl().getValue()).isEqualTo(newValue); } From 4592773a74b677251570d45f1d7684de0f83d851 Mon Sep 17 00:00:00 2001 From: kdkdhoho Date: Sun, 10 Nov 2024 20:04:15 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=20feat:=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=8B=9C=EC=97=90=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=9E=85=EB=A0=A5=EC=9D=84=20=EC=86=8C?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=EB=A1=9C=20=ED=95=B4=EB=8F=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9D=B4=20=EB=90=98=EB=8F=84=EB=A1=9D=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../list/application/domain/category/CategoryType.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/listywave/list/application/domain/category/CategoryType.java b/src/main/java/com/listywave/list/application/domain/category/CategoryType.java index 7cf7979a..7713a1c4 100644 --- a/src/main/java/com/listywave/list/application/domain/category/CategoryType.java +++ b/src/main/java/com/listywave/list/application/domain/category/CategoryType.java @@ -2,6 +2,7 @@ import static com.listywave.common.exception.ErrorCode.RESOURCE_NOT_FOUND; +import com.fasterxml.jackson.annotation.JsonCreator; import com.listywave.common.exception.CustomException; import java.util.Arrays; import lombok.AllArgsConstructor; @@ -36,6 +37,7 @@ public static CategoryType codeOf(String code) { .orElseThrow(() -> new CustomException(RESOURCE_NOT_FOUND, ERROR_MESSAGE + code)); } + @JsonCreator public static CategoryType nameOf(String name) { return Arrays.stream(CategoryType.values()) .filter(categoryType -> categoryType.name().equalsIgnoreCase(name))