diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/controller/StoreImageController.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/controller/StoreImageController.java index 61ed67f2..30e4202c 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/controller/StoreImageController.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/controller/StoreImageController.java @@ -29,7 +29,7 @@ public class StoreImageController { private final StoreImageService storeImageService; - @PostMapping("/store-images/{storeId}") + @PostMapping("/banner-images/{storeId}") @Operation( summary = "주점 이미지 업로드", description = "주점에 이미지를 업로드합니다. 최대 10개의 이미지 파일을 업로드할 수 있습니다." @@ -40,19 +40,8 @@ public ResponseEntity uploadStoreImage( @RequestParam("files") List files ) { // TODO 관련 정책 확정되면 메서드로 분리 예정 - // 파일 개수 제한 검증 - if (files.isEmpty() || files.size() > 10) { - throw new IllegalArgumentException("파일은 1개 이상 10개 이하로 업로드해 주세요."); - } // 파일 크기 검증 - for (MultipartFile file : files) { - if (file.isEmpty()) { - throw new IllegalArgumentException("빈 파일은 업로드할 수 없습니다."); - } - if (file.getSize() > 10 * 1024 * 1024) { // 10MB 제한 - throw new IllegalArgumentException("파일 크기는 10MB를 초과할 수 없습니다."); - } - } + validateFiles(files); List response = storeImageService.saveAll(storeId, files); return ResponseEntity @@ -64,6 +53,28 @@ public ResponseEntity uploadStoreImage( ); } + @PostMapping("/profile-images/{storeId}") + @Operation( + summary = "주점 프로필 이미지 업로드", + description = "주점의 프로필 이미지를 업로드합니다. 단일 이미지 파일을 업로드할 수 있습니다." + ) + @ApiResponse(responseCode = "201", description = "주점 프로필 이미지 업로드 성공") + public ResponseEntity uploadStoreProfileImage( + @PathVariable Long storeId, + @RequestParam("file") MultipartFile file + ) { + validateFileSize(file); + + StoreImageUploadResponse response = storeImageService.saveProfileImage(storeId, file); + return ResponseEntity + .status(HttpStatus.CREATED) + .body( + ApiUtils.success( + response + ) + ); + } + @DeleteMapping("/store-images/{storeImageId}") @Operation( summary = "주점 이미지 삭제", @@ -81,4 +92,22 @@ public ResponseEntity deleteStoreImage(@PathVariable Long imageId) { ) ); } + + private void validateFileSize(MultipartFile file) { + if (file == null || file.isEmpty()) { + throw new IllegalArgumentException("빈 파일은 업로드할 수 없습니다."); + } + if (file.getSize() > 10 * 1024 * 1024) { // 10MB 제한 + throw new IllegalArgumentException("파일 크기는 10MB를 초과할 수 없습니다."); + } + } + + private void validateFiles(List files) { + if (files.isEmpty() || files.size() > 10) { + throw new IllegalArgumentException("파일은 1개 이상 10개 이하로 업로드해 주세요."); + } + for (MultipartFile file : files) { + validateFileSize(file); + } + } } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/dto/StoreImageUploadResponse.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/dto/StoreImageUploadResponse.java index a34ca422..c5407e0f 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/dto/StoreImageUploadResponse.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/dto/StoreImageUploadResponse.java @@ -1,5 +1,6 @@ package com.nowait.applicationadmin.store.dto; +import com.nowait.domaincorerdb.store.entity.ImageType; import com.nowait.domaincorerdb.store.entity.StoreImage; import lombok.Builder; @@ -10,11 +11,13 @@ public class StoreImageUploadResponse { private final Long id; private final String imageUrl; + private final ImageType imageType; public static StoreImageUploadResponse fromEntity(StoreImage storeImage) { return StoreImageUploadResponse.builder() .id(storeImage.getId()) .imageUrl(storeImage.getImageUrl()) + .imageType(storeImage.getImageType()) .build(); } } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/dto/StoreReadDto.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/dto/StoreReadDto.java index b0497dd1..68dee6be 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/dto/StoreReadDto.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/dto/StoreReadDto.java @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import java.util.List; +import com.nowait.domaincorerdb.store.entity.ImageType; import com.nowait.domaincorerdb.store.entity.Store; import lombok.AllArgsConstructor; @@ -18,12 +19,23 @@ public class StoreReadDto { private String name; private String location; private String description; - private List images; + private StoreImageUploadResponse profileImage; + private List bannerImages; private Boolean isActive; private Boolean deleted; private LocalDateTime createdAt; - public static StoreReadDto fromEntity(Store store, List images) { + public static StoreReadDto fromEntity(Store store, List allImages) { + + StoreImageUploadResponse profile = allImages.stream() + .filter(image -> image.getImageType() == ImageType.PROFILE) + .findFirst() + .orElse(null); + + List banners = allImages.stream() + .filter(image -> image.getImageType() == ImageType.BANNER) + .toList(); + return StoreReadDto.builder() .createdAt(store.getCreatedAt()) .storeId(store.getStoreId()) @@ -33,7 +45,8 @@ public static StoreReadDto fromEntity(Store store, List saveAll(Long storeId, List files) { - if (files == null || files.isEmpty()) throw new StoreImageEmptyException(); + if (files == null || files.isEmpty()) + throw new StoreImageEmptyException(); String type = "store"; Store store = storeRepository.findById(storeId) @@ -60,6 +63,7 @@ public List saveAll(Long storeId, List .store(store) .imageUrl(uploadResult.url()) .fileKey(uploadResult.key()) + .imageType(ImageType.BANNER) .build(); storeImageRepository.save(storeImage); @@ -69,6 +73,36 @@ public List saveAll(Long storeId, List return imageUploadResponses; } + @Transactional + public StoreImageUploadResponse saveProfileImage(Long storeId, MultipartFile file) { + + String type = "store"; + Store store = storeRepository.findById(storeId) + .orElseThrow(StoreNotFoundException::new); + + Optional existingProfileImage = storeImageRepository.findByStoreStoreIdAndImageType(store.getStoreId(), + ImageType.PROFILE); + + existingProfileImage.ifPresent(profile -> { + s3Service.delete(profile.getFileKey()); + storeImageRepository.delete(profile); + }); + + S3Service.S3UploadResult uploadResult = s3Service.upload(type, storeId, file).join(); + + // StoreImage 엔티티 생성 및 저장 + StoreImage storeImage = StoreImage.builder() + .store(store) + .imageUrl(uploadResult.url()) + .fileKey(uploadResult.key()) + .imageType(ImageType.PROFILE) + .build(); + + storeImageRepository.save(storeImage); + + return StoreImageUploadResponse.fromEntity(storeImage); + } + @Transactional public void delete(Long storeImageId) { StoreImage storeImage = storeImageRepository.findById(storeImageId) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/controller/StoreController.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/controller/StoreController.java index 29b7287c..f1dbc8d2 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/controller/StoreController.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/controller/StoreController.java @@ -27,7 +27,6 @@ public class StoreController { private final StoreService storeService; - @GetMapping("/all-stores") @Operation(summary = "모든 주점 조회", description = "모든 주점을 조회합니다.") @ApiResponse(responseCode = "200", description = "모든 주점 조회 성공") @@ -50,7 +49,11 @@ public ResponseEntity getAllStores() { public ResponseEntity getAllStores(Pageable pageable) { return ResponseEntity .ok() - .body(ApiUtils.success(storeService.getAllStoresByPage(pageable))); + .body( + ApiUtils.success( + storeService.getAllStoresByPage(pageable) + ) + ); } @GetMapping("/{storeId}") diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StoreImageUploadResponse.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StoreImageUploadResponse.java index 5ff57643..62822b3d 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StoreImageUploadResponse.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StoreImageUploadResponse.java @@ -1,5 +1,6 @@ package com.nowait.applicationuser.store.dto; +import com.nowait.domaincorerdb.store.entity.ImageType; import com.nowait.domaincorerdb.store.entity.StoreImage; import lombok.Builder; @@ -10,11 +11,13 @@ public class StoreImageUploadResponse { private final Long id; private final String imageUrl; + private final ImageType imageType; public static StoreImageUploadResponse fromEntity(StoreImage storeImage) { return StoreImageUploadResponse.builder() .id(storeImage.getId()) .imageUrl(storeImage.getImageUrl()) + .imageType(storeImage.getImageType()) .build(); } } diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StoreReadDto.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StoreReadDto.java index f3316013..ddc78a70 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StoreReadDto.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/store/dto/StoreReadDto.java @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import java.util.List; +import com.nowait.domaincorerdb.store.entity.ImageType; import com.nowait.domaincorerdb.store.entity.Store; import lombok.AllArgsConstructor; @@ -18,12 +19,23 @@ public class StoreReadDto { private String name; private String location; private String description; - private List images; + private StoreImageUploadResponse profileImage; + private List bannerImages; private Boolean isActive; private Boolean deleted; private LocalDateTime createdAt; - public static StoreReadDto fromEntity(Store store, List images) { + public static StoreReadDto fromEntity(Store store, List allImages) { + + StoreImageUploadResponse profile = allImages.stream() + .filter(image -> image.getImageType() == ImageType.PROFILE) + .findFirst() + .orElse(null); + + List banners = allImages.stream() + .filter(image -> image.getImageType() == ImageType.BANNER) + .toList(); + return StoreReadDto.builder() .createdAt(store.getCreatedAt()) .storeId(store.getStoreId()) @@ -33,7 +45,8 @@ public static StoreReadDto fromEntity(Store store, List { List findByStore(Store store); + + Optional findByStoreStoreIdAndImageType(Long storeId, ImageType imageType); }