From 0711109d31b798dc486f72efa65403d3492a5971 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 14:25:41 +0900 Subject: [PATCH 01/20] =?UTF-8?q?[REFACTOR]:=20url=20->=20key=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=AA=85=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20?= =?UTF-8?q?imageOrder=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/store/converter/StoreConverter.java | 5 ++--- .../com/eatsfine/eatsfine/domain/store/entity/Store.java | 2 +- .../domain/store/service/StoreCommandServiceImpl.java | 2 +- .../eatsfine/domain/tableimage/entity/TableImage.java | 5 ++++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java b/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java index 11f7ac4..6d70501 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java @@ -5,7 +5,6 @@ import com.eatsfine.eatsfine.domain.store.dto.StoreResDto; import com.eatsfine.eatsfine.domain.store.entity.Store; -import java.math.BigDecimal; import java.util.Collections; import java.util.List; @@ -26,7 +25,7 @@ public static StoreResDto.StoreSearchDto toSearchDto(Store store, Double distanc .rating(store.getRating()) .reviewCount(null) // 리뷰 도메인 구현 이후 추가 예정 .distance(distance) - .mainImageUrl(store.getMainImageUrl()) + .mainImageUrl(store.getMainImageKey()) .isOpenNow(isOpenNow) .build(); } @@ -46,7 +45,7 @@ public static StoreResDto.StoreDetailDto toDetailDto(Store store, boolean isOpen .category(store.getCategory()) .rating(store.getRating()) .reviewCount(null) // reviewCount는 추후 리뷰 로직 구현 시 추가 예정 - .mainImageUrl(store.getMainImageUrl()) + .mainImageUrl(store.getMainImageKey()) .tableImageUrls(Collections.emptyList()) // tableImages는 추후 사진 등록 API 구현 시 추가 예정 .depositAmount(store.calculateDepositAmount()) .businessHours( diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java b/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java index 9f8dc34..27bc572 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java @@ -66,7 +66,7 @@ public class Store extends BaseEntity { private String address; @Column(name = "main_image_url") - private String mainImageUrl; + private String mainImageKey; @Builder.Default @Column(name = "rating", precision = 2, scale = 1, nullable = false) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandServiceImpl.java index 982f0be..e6322a0 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandServiceImpl.java @@ -42,7 +42,7 @@ public StoreResDto.StoreCreateDto createStore(StoreReqDto.StoreCreateDto dto) { .businessNumber(dto.businessNumber()) .description(dto.description()) .address(dto.address()) - .mainImageUrl(null) // 별도 API로 구현 + .mainImageKey(null) // 별도 API로 구현 .region(region) .phoneNumber(dto.phoneNumber()) .category(dto.category()) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/entity/TableImage.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/entity/TableImage.java index 1bb000e..d13b237 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/entity/TableImage.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/entity/TableImage.java @@ -22,7 +22,10 @@ public class TableImage extends BaseEntity { private Store store; @Column(name = "table_image_url", nullable = false) - private String tableImageUrl; + private String tableImageKey; + + @Column(name = "image_order", nullable = false) + private int imageOrder; public void assignStore(Store store) { this.store = store; From 7244d0dbbc837ab44775072958fbb80524d20a20 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 14:57:13 +0900 Subject: [PATCH 02/20] =?UTF-8?q?[BUILD]:=20AWS=20S3=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index 87169b1..c9b5cfd 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,12 @@ dependencies { annotationProcessor "com.querydsl:querydsl-apt:5.1.0:jakarta" annotationProcessor "jakarta.persistence:jakarta.persistence-api" annotationProcessor "jakarta.annotation:jakarta.annotation-api" + + // amazon + implementation platform('software.amazon.awssdk:bom:2.25.17') + + implementation 'software.amazon.awssdk:s3' + implementation 'software.amazon.awssdk:auth' } // --- QueryDSL --- def generated = 'build/generated/sources/annotationProcessor/java/main' From fe7c096310fe4d2a75ee3810a05a8d20a81bd0a8 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 16:14:57 +0900 Subject: [PATCH 03/20] =?UTF-8?q?[FEAT]:=20S3Config,=20S3Service=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/global/config/S3Config.java | 17 +++++ .../eatsfine/global/s3/S3Service.java | 76 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/main/java/com/eatsfine/eatsfine/global/config/S3Config.java create mode 100644 src/main/java/com/eatsfine/eatsfine/global/s3/S3Service.java diff --git a/src/main/java/com/eatsfine/eatsfine/global/config/S3Config.java b/src/main/java/com/eatsfine/eatsfine/global/config/S3Config.java new file mode 100644 index 0000000..b8c5c14 --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/global/config/S3Config.java @@ -0,0 +1,17 @@ +package com.eatsfine.eatsfine.global.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +@Configuration +public class S3Config { + + @Bean + public S3Client s3Client() { + return S3Client.builder() + .region(Region.AP_NORTHEAST_2) + .build(); + } +} diff --git a/src/main/java/com/eatsfine/eatsfine/global/s3/S3Service.java b/src/main/java/com/eatsfine/eatsfine/global/s3/S3Service.java new file mode 100644 index 0000000..774fc36 --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/global/s3/S3Service.java @@ -0,0 +1,76 @@ +package com.eatsfine.eatsfine.global.s3; + +import com.eatsfine.eatsfine.domain.image.exception.ImageException; +import com.eatsfine.eatsfine.domain.image.status.ImageErrorStatus; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; + +import java.io.IOException; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +public class S3Service { + + private final S3Client s3Client; + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + @Value("${cloud.aws.s3.base-url}") + private String baseUrl; + + public String upload(MultipartFile file, String directory) { + String key = generateKey(file, directory); + + try { + PutObjectRequest request = PutObjectRequest.builder() + .bucket(bucket) + .key(key) + .contentType(file.getContentType()) + .build(); + + s3Client.putObject(request, + RequestBody.fromInputStream(file.getInputStream(), file.getSize())); + + return key; + } catch (IOException e) { + throw new ImageException(ImageErrorStatus.S3_UPLOAD_FAILED); + } + } + + public void deleteByKey(String key) { + if (key == null || key.isBlank()) return; + + s3Client.deleteObject(DeleteObjectRequest.builder() + .bucket(bucket) + .key(key) + .build()); + } + + public String toUrl(String key) { + if (key == null || key.isBlank()) return null; + return baseUrl + "/" + key; + } + + private String generateKey(MultipartFile file, String directory) { + if(directory == null || directory.isBlank()) { + throw new IllegalArgumentException("S3 디렉토리는 비어있을 수 없습니다."); + } + String extension = extractExtension(file.getOriginalFilename()); + return directory + "/" + UUID.randomUUID() + extension; + } + + private String extractExtension(String filename) { + if (filename == null || !filename.contains(".")) { + throw new ImageException(ImageErrorStatus.INVALID_FILE_TYPE); + } + return filename.substring(filename.lastIndexOf(".")); + } +} \ No newline at end of file From 8054000ecd6d1095f218dcf32350a276c554a9db Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 16:16:02 +0900 Subject: [PATCH 04/20] =?UTF-8?q?[FEAT]:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B4=80=EB=A0=A8=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EB=B0=8F=20=EC=97=90=EB=9F=AC=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../image/exception/ImageException.java | 11 ++++++ .../domain/image/status/ImageErrorStatus.java | 39 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/image/exception/ImageException.java create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/image/status/ImageErrorStatus.java diff --git a/src/main/java/com/eatsfine/eatsfine/domain/image/exception/ImageException.java b/src/main/java/com/eatsfine/eatsfine/domain/image/exception/ImageException.java new file mode 100644 index 0000000..3f70401 --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/image/exception/ImageException.java @@ -0,0 +1,11 @@ +package com.eatsfine.eatsfine.domain.image.exception; + + +import com.eatsfine.eatsfine.global.apiPayload.code.BaseErrorCode; +import com.eatsfine.eatsfine.global.apiPayload.exception.GeneralException; + +public class ImageException extends GeneralException { + public ImageException(BaseErrorCode code) { + super(code); + } +} diff --git a/src/main/java/com/eatsfine/eatsfine/domain/image/status/ImageErrorStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/image/status/ImageErrorStatus.java new file mode 100644 index 0000000..0a1a02b --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/image/status/ImageErrorStatus.java @@ -0,0 +1,39 @@ +package com.eatsfine.eatsfine.domain.image.status; + +import com.eatsfine.eatsfine.global.apiPayload.code.BaseErrorCode; +import com.eatsfine.eatsfine.global.apiPayload.code.ErrorReasonDto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ImageErrorStatus implements BaseErrorCode { + EMPTY_FILE(HttpStatus.BAD_REQUEST, "IMAGE4001", "업로드할 파일이 비어 있습니다."), + INVALID_FILE_TYPE(HttpStatus.BAD_REQUEST, "IMAGE4002", "지원하지 않는 파일 형식입니다."), + S3_UPLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "IMAGE5001", "이미지 업로드에 실패했습니다."); + + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReasonDto getReason() { + return ErrorReasonDto.builder() + .isSuccess(true) + .message(message) + .code(code) + .build(); + } + + @Override + public ErrorReasonDto getReasonHttpStatus() { + return ErrorReasonDto.builder() + .httpStatus(httpStatus) + .isSuccess(true) + .code(code) + .message(message) + .build(); + } +} From 30e5e29a5c741c84499d8e31bcbd233e18d98858 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 16:18:29 +0900 Subject: [PATCH 05/20] =?UTF-8?q?[FEAT]:=20=EA=B0=80=EA=B2=8C=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20DTO=20=EB=B0=8F=20=EC=84=B1=EA=B3=B5=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java | 5 +++-- .../eatsfine/domain/store/status/StoreSuccessStatus.java | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java b/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java index a674eaa..82c617f 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java @@ -70,8 +70,9 @@ public record StoreDetailDto( // 가게 대표 이미지 등록 응답 @Builder - public record uploadMainImageResDto( - String mainImageUrl + public record UploadMainImageDto( + Long storeId, + String mainImageKey ) {} // 식당 수정 응답 diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java index 088f093..59d4100 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java @@ -18,7 +18,9 @@ public enum StoreSuccessStatus implements BaseCode { _STORE_CREATED(HttpStatus.CREATED, "STORE201", "성공적으로 가게를 등록했습니다."), - _STORE_UPDATE_SUCCESS(HttpStatus.OK, "STORE2004", "성공적으로 가게 기본 정보를 수정했습니다.") + _STORE_UPDATE_SUCCESS(HttpStatus.OK, "STORE2004", "성공적으로 가게 기본 정보를 수정했습니다."), + + _STORE_MAIN_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "STORE2005", "성공적으로 가게 대표 이미지를 업로드했습니다.") ; From 5dd6d4c2952607c4dbde68a849219324d704d48a Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 17:05:29 +0900 Subject: [PATCH 06/20] =?UTF-8?q?[FEAT]:=20=EA=B0=80=EA=B2=8C=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/controller/StoreController.java | 17 ++++++++ .../store/converter/StoreConverter.java | 8 ++++ .../domain/store/dto/StoreResDto.java | 2 +- .../eatsfine/domain/store/entity/Store.java | 6 +++ .../store/service/StoreCommandService.java | 2 + .../service/StoreCommandServiceImpl.java | 42 +++++++++++++++---- 6 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/controller/StoreController.java b/src/main/java/com/eatsfine/eatsfine/domain/store/controller/StoreController.java index 6527f47..a49953f 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/controller/StoreController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/controller/StoreController.java @@ -12,7 +12,9 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; @Tag(name = "Store", description = "식당 조회 및 관리 API") @RestController @@ -71,4 +73,19 @@ public ApiResponse updateStoreBasicInfo( } + @Operation( + summary = "식당 대표 이미지 등록", + description = "식당의 대표 이미지를 등록합니다." + ) + @PostMapping( + value = "/stores/{storeId}/main-image", + consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ) + public ApiResponse uploadMainImage( + @RequestPart("mainImage")MultipartFile mainImage, + @PathVariable Long storeId + ){ + return ApiResponse.of(StoreSuccessStatus._STORE_MAIN_IMAGE_UPLOAD_SUCCESS, storeCommandService.uploadMainImage(storeId, mainImage)); + } + } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java b/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java index 6d70501..c2fd16b 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java @@ -4,6 +4,7 @@ import com.eatsfine.eatsfine.domain.businesshours.entity.BusinessHours; import com.eatsfine.eatsfine.domain.store.dto.StoreResDto; import com.eatsfine.eatsfine.domain.store.entity.Store; +import org.springframework.web.multipart.MultipartFile; import java.util.Collections; import java.util.List; @@ -64,5 +65,12 @@ public static StoreResDto.StoreUpdateDto toUpdateDto(Long storeId, List .updatedFields(updatedFields) .build(); } + + public static StoreResDto.UploadMainImageDto toUploadMainImageDto(Long storeId, String mainImageUrl) { + return StoreResDto.UploadMainImageDto.builder() + .storeId(storeId) + .mainImageUrl(mainImageUrl) + .build(); + } } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java b/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java index 82c617f..64b202c 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java @@ -72,7 +72,7 @@ public record StoreDetailDto( @Builder public record UploadMainImageDto( Long storeId, - String mainImageKey + String mainImageUrl ) {} // 식당 수정 응답 diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java b/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java index 27bc572..e64dc48 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java @@ -130,6 +130,11 @@ public void removeTableImage(TableImage tableImage) { tableImage.assignStore(null); } + // 가게 메인 이미지 등록 + public void updateMainImageKey(String mainImageKey) { + this.mainImageKey = mainImageKey; + } + // 특정 요일의 영업시간 조회 메서드 public BusinessHours getBusinessHoursByDay(DayOfWeek dayOfWeek) { return this.businessHours.stream() @@ -146,6 +151,7 @@ public Optional findBusinessHoursByDay(DayOfWeek dayOfWeek) { .findFirst(); } + // 예약금 계산 메서드 public BigDecimal calculateDepositAmount() { return BigDecimal.valueOf(minPrice) .multiply(BigDecimal.valueOf(depositRate.getPercent())) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandService.java b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandService.java index 65ab017..33d3918 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandService.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandService.java @@ -2,8 +2,10 @@ import com.eatsfine.eatsfine.domain.store.dto.StoreReqDto; import com.eatsfine.eatsfine.domain.store.dto.StoreResDto; +import org.springframework.web.multipart.MultipartFile; public interface StoreCommandService { StoreResDto.StoreCreateDto createStore(StoreReqDto.StoreCreateDto storeCreateDto); StoreResDto.StoreUpdateDto updateBasicInfo(Long storeId, StoreReqDto.StoreUpdateDto storeUpdateDto); + StoreResDto.UploadMainImageDto uploadMainImage(Long storeId, MultipartFile file); } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandServiceImpl.java index e6322a0..7ceae86 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreCommandServiceImpl.java @@ -3,6 +3,8 @@ import com.eatsfine.eatsfine.domain.businesshours.converter.BusinessHoursConverter; import com.eatsfine.eatsfine.domain.businesshours.entity.BusinessHours; import com.eatsfine.eatsfine.domain.businesshours.validator.BusinessHoursValidator; +import com.eatsfine.eatsfine.domain.image.exception.ImageException; +import com.eatsfine.eatsfine.domain.image.status.ImageErrorStatus; import com.eatsfine.eatsfine.domain.region.entity.Region; import com.eatsfine.eatsfine.domain.region.repository.RegionRepository; import com.eatsfine.eatsfine.domain.region.status.RegionErrorStatus; @@ -19,6 +21,8 @@ import java.util.ArrayList; import java.util.List; +import com.eatsfine.eatsfine.global.s3.S3Service; +import org.springframework.web.multipart.MultipartFile; @Service @Transactional @@ -27,7 +31,9 @@ public class StoreCommandServiceImpl implements StoreCommandService { private final StoreRepository storeRepository; private final RegionRepository regionRepository; + private final S3Service s3Service; + // 가게 등록 @Override public StoreResDto.StoreCreateDto createStore(StoreReqDto.StoreCreateDto dto) { Region region = regionRepository.findById(dto.regionId()) @@ -77,15 +83,37 @@ public StoreResDto.StoreUpdateDto updateBasicInfo(Long storeId, StoreReqDto.Stor public List extractUpdatedFields(StoreReqDto.StoreUpdateDto dto) { List updated = new ArrayList<>(); - if(dto.storeName() != null) updated.add("storeName"); - if(dto.description() != null) updated.add("description"); - if(dto.phoneNumber() != null) updated.add("phoneNumber"); - if(dto.category() != null) updated.add("category"); - if(dto.minPrice() != null) updated.add("minPrice"); - if(dto.depositRate() != null) updated.add("depositRate"); - if(dto.bookingIntervalMinutes() != null) updated.add("bookingIntervalMinutes"); + if (dto.storeName() != null) updated.add("storeName"); + if (dto.description() != null) updated.add("description"); + if (dto.phoneNumber() != null) updated.add("phoneNumber"); + if (dto.category() != null) updated.add("category"); + if (dto.minPrice() != null) updated.add("minPrice"); + if (dto.depositRate() != null) updated.add("depositRate"); + if (dto.bookingIntervalMinutes() != null) updated.add("bookingIntervalMinutes"); return updated; } + // 가게 메인 이미지 등록 + @Override + public StoreResDto.UploadMainImageDto uploadMainImage(Long storeId, MultipartFile file) { + Store store = storeRepository.findById(storeId).orElseThrow( + () -> new StoreException(StoreErrorStatus._STORE_NOT_FOUND) + ); + + if(file.isEmpty()) { + throw new ImageException(ImageErrorStatus.EMPTY_FILE); + } + + if(store.getMainImageKey() != null) { + s3Service.deleteByKey(store.getMainImageKey()); + } + + String key = s3Service.upload(file, "stores/" + storeId + "/main"); + store.updateMainImageKey(key); + + String mainImageUrl = s3Service.toUrl(store.getMainImageKey()); + + return StoreConverter.toUploadMainImageDto(store.getId(), mainImageUrl); + } } \ No newline at end of file From aa94926e2994c8e5524dabd09c5ec54445251e9b Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 17:39:06 +0900 Subject: [PATCH 07/20] =?UTF-8?q?[FEAT]:=20=EA=B0=80=EA=B2=8C=20=EB=A7=A4?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20DTO=20=EB=B0=8F=20=EC=84=B1=EA=B3=B5=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java | 6 ++++++ .../eatsfine/domain/store/status/StoreSuccessStatus.java | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java b/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java index 64b202c..c9ee44a 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/dto/StoreResDto.java @@ -81,5 +81,11 @@ public record StoreUpdateDto( Long storeId, List updatedFields ){}; + // 가게 대표 이미지 조회 응답 + @Builder + public record GetMainImageDto( + Long storeId, + String mainImageUrl + ) {} } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java index 59d4100..1e0474a 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java @@ -18,9 +18,15 @@ public enum StoreSuccessStatus implements BaseCode { _STORE_CREATED(HttpStatus.CREATED, "STORE201", "성공적으로 가게를 등록했습니다."), +<<<<<<< HEAD _STORE_UPDATE_SUCCESS(HttpStatus.OK, "STORE2004", "성공적으로 가게 기본 정보를 수정했습니다."), _STORE_MAIN_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "STORE2005", "성공적으로 가게 대표 이미지를 업로드했습니다.") +======= + _STORE_MAIN_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "STORE2004", "성공적으로 가게 대표 이미지를 업로드했습니다."), + + _STORE_MAIN_IMAGE_GET_SUCCESS(HttpStatus.OK, "STORE2004", "성공적으로 가게 대표 이미지를 조회했습니다.") +>>>>>>> 4502c2b ([FEAT]: 가게 매인 이미지 조회 응답 DTO 및 성공 상태코드 추가) ; From ce25f9d93e733afd285f1ff01c202c4d7a41de21 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 17:40:00 +0900 Subject: [PATCH 08/20] =?UTF-8?q?[FEAT]:=20=EA=B0=80=EA=B2=8C=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/store/controller/StoreController.java | 11 +++++++++++ .../domain/store/converter/StoreConverter.java | 7 +++++++ .../domain/store/service/StoreQueryService.java | 2 ++ .../domain/store/service/StoreQueryServiceImpl.java | 11 +++++++++++ 4 files changed, 31 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/controller/StoreController.java b/src/main/java/com/eatsfine/eatsfine/domain/store/controller/StoreController.java index a49953f..aab3686 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/controller/StoreController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/controller/StoreController.java @@ -88,4 +88,15 @@ public ApiResponse uploadMainImage( return ApiResponse.of(StoreSuccessStatus._STORE_MAIN_IMAGE_UPLOAD_SUCCESS, storeCommandService.uploadMainImage(storeId, mainImage)); } + @Operation( + summary = "식당 대표 이미지 조회", + description = "식당의 대표 이미지를 조회합니다." + ) + @GetMapping("/stores/{storeId}/main-image") + public ApiResponse getMainImage( + @PathVariable Long storeId + ) { + return ApiResponse.of(StoreSuccessStatus._STORE_MAIN_IMAGE_GET_SUCCESS, storeQueryService.getMainImage(storeId)); + } + } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java b/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java index c2fd16b..962c97a 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/converter/StoreConverter.java @@ -72,5 +72,12 @@ public static StoreResDto.UploadMainImageDto toUploadMainImageDto(Long storeId, .mainImageUrl(mainImageUrl) .build(); } + + public static StoreResDto.GetMainImageDto toGetMainImageDto(Long storeId, String key) { + return StoreResDto.GetMainImageDto.builder() + .storeId(storeId) + .mainImageUrl(key) + .build(); + } } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryService.java b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryService.java index 8171953..513f7cf 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryService.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryService.java @@ -17,6 +17,8 @@ StoreResDto.StoreSearchResDto search( StoreResDto.StoreDetailDto getStoreDetail(Long storeId); + StoreResDto.GetMainImageDto getMainImage(Long storeId); + boolean isOpenNow(Store store, LocalDateTime now); } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java index 2c93816..a726c21 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java @@ -10,6 +10,7 @@ import com.eatsfine.eatsfine.domain.store.exception.StoreException; import com.eatsfine.eatsfine.domain.store.repository.StoreRepository; import com.eatsfine.eatsfine.domain.store.status.StoreErrorStatus; +import com.eatsfine.eatsfine.global.s3.S3Service; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -26,6 +27,7 @@ public class StoreQueryServiceImpl implements StoreQueryService { private final StoreRepository storeRepository; + private final S3Service s3Service; // 식당 검색 @Override @@ -73,6 +75,15 @@ public StoreResDto.StoreDetailDto getStoreDetail(Long storeId) { return StoreConverter.toDetailDto(store, isOpenNow(store, LocalDateTime.now())); } + // 식당 대표 이미지 조회 + @Override + public StoreResDto.GetMainImageDto getMainImage(Long storeId) { + Store store = storeRepository.findById(storeId) + .orElseThrow(() -> new StoreException(StoreErrorStatus._STORE_NOT_FOUND)); + + return StoreConverter.toGetMainImageDto(storeId, s3Service.toUrl(store.getMainImageKey())); + } + // 현재 영업 여부 계산 (실시간 계산) @Override public boolean isOpenNow(Store store, LocalDateTime now) { From 7abb96565356a906e7e6b04ade07dec62c4e6e48 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 22:28:30 +0900 Subject: [PATCH 09/20] =?UTF-8?q?[FEAT]:=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=93=B1=EB=A1=9D=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20DTO=20=EB=B0=8F=20=EC=84=B1=EA=B3=B5=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tableimage/dto/TableImageResDto.java | 14 ++++++ .../status/TableImageSuccessStatus.java | 44 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java new file mode 100644 index 0000000..04f05e6 --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java @@ -0,0 +1,14 @@ +package com.eatsfine.eatsfine.domain.tableimage.dto; + +import lombok.Builder; + +import java.util.List; + +public class TableImageResDto { + + @Builder + public record UploadTableImageDto( + Long storeId, + List tableImages + ){} +} diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java new file mode 100644 index 0000000..a5191e8 --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java @@ -0,0 +1,44 @@ +package com.eatsfine.eatsfine.domain.tableimage.status; + +import com.eatsfine.eatsfine.global.apiPayload.code.BaseCode; +import com.eatsfine.eatsfine.global.apiPayload.code.ReasonDto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum TableImageSuccessStatus implements BaseCode { + + _STORE_TABLE_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "STORE200", "성공적으로 가게 테이블 이미지를 업로드했습니다."), + + _STORE_TABLE_IMAGE_GET_SUCCESS(HttpStatus.OK, "STORE2001", "성공적으로 가게 테이블 이미지를 조회했습니다.") + ; + + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ReasonDto getReason() { + return ReasonDto.builder() + .isSuccess(true) + .message(message) + .code(code) + .build(); + } + + @Override + public ReasonDto getReasonHttpStatus() { + return ReasonDto.builder() + .isSuccess(true) + .httpStatus(httpStatus) + .message(message) + .code(code) + .build(); + } + + } + + From 5320bdd58d280650749530d648cc3d8044642e5a Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 22:29:35 +0900 Subject: [PATCH 10/20] =?UTF-8?q?[FEAT]:=20=EA=B0=80=EA=B2=8C=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=9D=B4=EB=AF=B8=EC=A7=80(TableImage)=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/store/entity/Store.java | 1 - .../store/service/StoreQueryServiceImpl.java | 4 +- .../controller/TableImageController.java | 40 ++++++++++++++ .../converter/TableImageConverter.java | 16 ++++++ .../repository/TableImageRepository.java | 8 +++ .../service/TableImageCommandService.java | 11 ++++ .../service/TableImageCommandServiceImpl.java | 55 +++++++++++++++++++ 7 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandService.java create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java b/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java index e64dc48..fcd2bb2 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/entity/Store.java @@ -95,7 +95,6 @@ public class Store extends BaseEntity { @OneToMany(mappedBy = "store", cascade = CascadeType.ALL, orphanRemoval = true) private List tableImages = new ArrayList<>(); - // StoreTable이 아닌 TableLayout 엔티티 참조 @Builder.Default @OneToMany(mappedBy = "store") diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java index a726c21..fd06161 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/service/StoreQueryServiceImpl.java @@ -5,8 +5,6 @@ import com.eatsfine.eatsfine.domain.store.dto.StoreResDto; import com.eatsfine.eatsfine.domain.store.dto.projection.StoreSearchResult; import com.eatsfine.eatsfine.domain.store.entity.Store; -import com.eatsfine.eatsfine.domain.store.enums.Category; -import com.eatsfine.eatsfine.domain.store.enums.StoreSortType; import com.eatsfine.eatsfine.domain.store.exception.StoreException; import com.eatsfine.eatsfine.domain.store.repository.StoreRepository; import com.eatsfine.eatsfine.domain.store.status.StoreErrorStatus; @@ -16,6 +14,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.time.DayOfWeek; import java.time.LocalDateTime; @@ -24,6 +23,7 @@ @Service @RequiredArgsConstructor +@Transactional(readOnly = true) public class StoreQueryServiceImpl implements StoreQueryService { private final StoreRepository storeRepository; diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java new file mode 100644 index 0000000..b86a6d0 --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java @@ -0,0 +1,40 @@ +package com.eatsfine.eatsfine.domain.tableimage.controller; + +import com.eatsfine.eatsfine.domain.tableimage.dto.TableImageResDto; +import com.eatsfine.eatsfine.domain.tableimage.service.TableImageCommandService; +import com.eatsfine.eatsfine.domain.tableimage.status.TableImageSuccessStatus; +import com.eatsfine.eatsfine.global.apiPayload.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@RestController +@RequestMapping("/api/v1") +@RequiredArgsConstructor +public class TableImageController { + + private final TableImageCommandService tableImageCommandService; + + @Operation( + summary = "식당 테이블 이미지 등록", + description = "식당 테이블 이미지들을 등록합니다." + ) + @PostMapping( + value = "/stores/{storeId}/table-images", + consumes = MediaType.MULTIPART_FORM_DATA_VALUE + ) + ApiResponse uploadTableImage( + @RequestPart("file") List files, + @PathVariable Long storeId + ) { + return ApiResponse.of( + TableImageSuccessStatus._STORE_TABLE_IMAGE_UPLOAD_SUCCESS, + tableImageCommandService.uploadTableImage(storeId, files) + ); + } + +} diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java new file mode 100644 index 0000000..51518b2 --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java @@ -0,0 +1,16 @@ +package com.eatsfine.eatsfine.domain.tableimage.converter; + +import com.eatsfine.eatsfine.domain.tableimage.dto.TableImageResDto; +import com.eatsfine.eatsfine.domain.tableimage.entity.TableImage; + +import java.util.List; + +public class TableImageConverter { + + public static TableImageResDto.UploadTableImageDto toTableImageDto(Long storeId, List tableImages) { + return TableImageResDto.UploadTableImageDto.builder() + .storeId(storeId) + .tableImages(tableImages) + .build(); + } +} diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java index dda63a2..1cd6952 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java @@ -2,6 +2,14 @@ import com.eatsfine.eatsfine.domain.tableimage.entity.TableImage; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; public interface TableImageRepository extends JpaRepository { + + @Query(""" + select coalesce(max(ti.imageOrder), 0) + from TableImage ti + where ti.store.id = :storeId +""") + int findMaxOrderByStoreId(Long storeId); } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandService.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandService.java new file mode 100644 index 0000000..6cac13f --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandService.java @@ -0,0 +1,11 @@ +package com.eatsfine.eatsfine.domain.tableimage.service; + +import com.eatsfine.eatsfine.domain.tableimage.dto.TableImageResDto; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +public interface TableImageCommandService { + + TableImageResDto.UploadTableImageDto uploadTableImage(Long storeId, List files); +} diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java new file mode 100644 index 0000000..68128fd --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java @@ -0,0 +1,55 @@ +package com.eatsfine.eatsfine.domain.tableimage.service; + +import com.eatsfine.eatsfine.domain.image.exception.ImageException; +import com.eatsfine.eatsfine.domain.image.status.ImageErrorStatus; +import com.eatsfine.eatsfine.domain.store.entity.Store; +import com.eatsfine.eatsfine.domain.store.exception.StoreException; +import com.eatsfine.eatsfine.domain.store.repository.StoreRepository; +import com.eatsfine.eatsfine.domain.store.status.StoreErrorStatus; +import com.eatsfine.eatsfine.domain.tableimage.converter.TableImageConverter; +import com.eatsfine.eatsfine.domain.tableimage.dto.TableImageResDto; +import com.eatsfine.eatsfine.domain.tableimage.entity.TableImage; +import com.eatsfine.eatsfine.domain.tableimage.repository.TableImageRepository; +import com.eatsfine.eatsfine.global.s3.S3Service; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.List; + +@Service +@Transactional +@RequiredArgsConstructor +public class TableImageCommandServiceImpl implements TableImageCommandService { + + private final StoreRepository storeRepository; + private final TableImageRepository tableImageRepository; + private final S3Service s3Service; + + // 가게 테이블 이미지 등록 + public TableImageResDto.UploadTableImageDto uploadTableImage(Long storeId, List files) { + + Store store = storeRepository.findById(storeId) + .orElseThrow(() -> new StoreException(StoreErrorStatus._STORE_NOT_FOUND)); + + if(files == null || files.isEmpty() || files.stream().allMatch(MultipartFile::isEmpty)) { + throw new ImageException(ImageErrorStatus.EMPTY_FILE); + } + + int imageOrder = tableImageRepository.findMaxOrderByStoreId(storeId) + 1; + List tableImages = new ArrayList<>(); + + for (MultipartFile file : files) { + String key = s3Service.upload(file, "stores/" + storeId + "/tables"); + TableImage tableImage = TableImage.builder() + .tableImageKey(key) + .imageOrder(imageOrder++) + .build(); + store.addTableImage(tableImage); + tableImages.add(s3Service.toUrl(key)); + } + return TableImageConverter.toTableImageDto(storeId, tableImages); + } +} From 6c3779657fd1541939391a9952a1a7a4a07f3cfc Mon Sep 17 00:00:00 2001 From: twodo0 Date: Sun, 18 Jan 2026 23:13:41 +0900 Subject: [PATCH 11/20] =?UTF-8?q?[FEAT]:=20=EA=B0=80=EA=B2=8C=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=9D=B4=EB=AF=B8=EC=A7=80(TableImage)=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TableImageController.java | 13 ++++++ .../converter/TableImageConverter.java | 12 ++++-- .../tableimage/dto/TableImageResDto.java | 8 +++- .../repository/TableImageRepository.java | 5 +++ .../service/TableImageCommandServiceImpl.java | 2 +- .../service/TableImageQueryService.java | 7 ++++ .../service/TableImageQueryServiceImpl.java | 41 +++++++++++++++++++ 7 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageQueryService.java create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageQueryServiceImpl.java diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java index b86a6d0..1dcdaaa 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java @@ -2,6 +2,7 @@ import com.eatsfine.eatsfine.domain.tableimage.dto.TableImageResDto; import com.eatsfine.eatsfine.domain.tableimage.service.TableImageCommandService; +import com.eatsfine.eatsfine.domain.tableimage.service.TableImageQueryService; import com.eatsfine.eatsfine.domain.tableimage.status.TableImageSuccessStatus; import com.eatsfine.eatsfine.global.apiPayload.ApiResponse; import io.swagger.v3.oas.annotations.Operation; @@ -18,6 +19,7 @@ public class TableImageController { private final TableImageCommandService tableImageCommandService; + private final TableImageQueryService tableImageQueryService; @Operation( summary = "식당 테이블 이미지 등록", @@ -37,4 +39,15 @@ ApiResponse uploadTableImage( ); } + @Operation( + summary = "식당 테이블 이미지 조회", + description = "식당 테이블 이미지들을 조회합니다." + ) + @GetMapping("/stores/{storeId}/table-images") + ApiResponse getTableImage( + @PathVariable Long storeId + ) { + return ApiResponse.of(TableImageSuccessStatus._STORE_TABLE_IMAGE_GET_SUCCESS, tableImageQueryService.getTableImage(storeId)); + } + } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java index 51518b2..3cd5e53 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java @@ -1,16 +1,22 @@ package com.eatsfine.eatsfine.domain.tableimage.converter; import com.eatsfine.eatsfine.domain.tableimage.dto.TableImageResDto; -import com.eatsfine.eatsfine.domain.tableimage.entity.TableImage; import java.util.List; public class TableImageConverter { - public static TableImageResDto.UploadTableImageDto toTableImageDto(Long storeId, List tableImages) { + public static TableImageResDto.UploadTableImageDto toUploadTableImageDto(Long storeId, List tableImages) { return TableImageResDto.UploadTableImageDto.builder() .storeId(storeId) - .tableImages(tableImages) + .tableImageUrls(tableImages) + .build(); + } + + public static TableImageResDto.GetTableImageDto toGetTableImageDto(Long storeId, List tableImages) { + return TableImageResDto.GetTableImageDto.builder() + .storeId(storeId) + .tableImageUrls(tableImages) .build(); } } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java index 04f05e6..e05e0dd 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java @@ -9,6 +9,12 @@ public class TableImageResDto { @Builder public record UploadTableImageDto( Long storeId, - List tableImages + List tableImageUrls + ){} + + @Builder + public record GetTableImageDto( + Long storeId, + List tableImageUrls ){} } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java index 1cd6952..a9faaac 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java @@ -1,9 +1,12 @@ package com.eatsfine.eatsfine.domain.tableimage.repository; +import com.eatsfine.eatsfine.domain.store.entity.Store; import com.eatsfine.eatsfine.domain.tableimage.entity.TableImage; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import java.util.List; + public interface TableImageRepository extends JpaRepository { @Query(""" @@ -12,4 +15,6 @@ select coalesce(max(ti.imageOrder), 0) where ti.store.id = :storeId """) int findMaxOrderByStoreId(Long storeId); + + List findAllByStoreOrderByImageOrder(Store store); } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java index 68128fd..00a0708 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java @@ -50,6 +50,6 @@ public TableImageResDto.UploadTableImageDto uploadTableImage(Long storeId, List< store.addTableImage(tableImage); tableImages.add(s3Service.toUrl(key)); } - return TableImageConverter.toTableImageDto(storeId, tableImages); + return TableImageConverter.toUploadTableImageDto(storeId, tableImages); } } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageQueryService.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageQueryService.java new file mode 100644 index 0000000..52a0602 --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageQueryService.java @@ -0,0 +1,7 @@ +package com.eatsfine.eatsfine.domain.tableimage.service; + +import com.eatsfine.eatsfine.domain.tableimage.dto.TableImageResDto; + +public interface TableImageQueryService { + TableImageResDto.GetTableImageDto getTableImage(Long storeId); +} diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageQueryServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageQueryServiceImpl.java new file mode 100644 index 0000000..a2c19d9 --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageQueryServiceImpl.java @@ -0,0 +1,41 @@ +package com.eatsfine.eatsfine.domain.tableimage.service; + +import com.eatsfine.eatsfine.domain.store.entity.Store; +import com.eatsfine.eatsfine.domain.store.exception.StoreException; +import com.eatsfine.eatsfine.domain.store.repository.StoreRepository; +import com.eatsfine.eatsfine.domain.store.status.StoreErrorStatus; +import com.eatsfine.eatsfine.domain.tableimage.converter.TableImageConverter; +import com.eatsfine.eatsfine.domain.tableimage.dto.TableImageResDto; +import com.eatsfine.eatsfine.domain.tableimage.entity.TableImage; +import com.eatsfine.eatsfine.domain.tableimage.repository.TableImageRepository; +import com.eatsfine.eatsfine.global.s3.S3Service; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class TableImageQueryServiceImpl implements TableImageQueryService { + + private final StoreRepository storeRepository; + private final TableImageRepository tableImageRepository; + private final S3Service s3Service; + + @Override + public TableImageResDto.GetTableImageDto getTableImage(Long storeId) { + Store store = storeRepository.findById(storeId) + .orElseThrow(() -> new StoreException(StoreErrorStatus._STORE_NOT_FOUND)); + + List tableImages = tableImageRepository.findAllByStoreOrderByImageOrder(store); + + List tableImageUrls = tableImages.stream() + .map(ti-> s3Service.toUrl(ti.getTableImageKey())) + .toList(); + + return TableImageConverter.toGetTableImageDto(storeId, tableImageUrls); + } + +} From 91a30a910f322b6907f52d338c77db44b4731ce7 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Mon, 19 Jan 2026 01:11:52 +0900 Subject: [PATCH 12/20] =?UTF-8?q?[REFACTOR]:=20=ED=85=8C=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=93=B1=EB=A1=9D=20=EB=B0=8F?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EC=84=B1=EA=B3=B5=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/tableimage/controller/TableImageController.java | 2 ++ .../domain/tableimage/status/TableImageSuccessStatus.java | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java index 1dcdaaa..6a71104 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java @@ -6,6 +6,7 @@ import com.eatsfine.eatsfine.domain.tableimage.status.TableImageSuccessStatus; import com.eatsfine.eatsfine.global.apiPayload.ApiResponse; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; @@ -13,6 +14,7 @@ import java.util.List; +@Tag(name = "TableImage", description = "테이블 이미지 조회 및 관리 API") @RestController @RequestMapping("/api/v1") @RequiredArgsConstructor diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java index a5191e8..db64553 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java @@ -10,9 +10,9 @@ @AllArgsConstructor public enum TableImageSuccessStatus implements BaseCode { - _STORE_TABLE_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "STORE200", "성공적으로 가게 테이블 이미지를 업로드했습니다."), + _STORE_TABLE_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "TABLE_IMAGE200", "성공적으로 가게 테이블 이미지를 업로드했습니다."), - _STORE_TABLE_IMAGE_GET_SUCCESS(HttpStatus.OK, "STORE2001", "성공적으로 가게 테이블 이미지를 조회했습니다.") + _STORE_TABLE_IMAGE_GET_SUCCESS(HttpStatus.OK, "TABLE_IMAGE2001", "성공적으로 가게 테이블 이미지를 조회했습니다.") ; From ead2f174fd2e34d19c2beee18e4bbdec165e2f37 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Mon, 19 Jan 2026 12:37:40 +0900 Subject: [PATCH 13/20] =?UTF-8?q?[FEAT]:=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=82=AD=EC=A0=9C=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20DTO=20=EB=B0=8F=20=EC=84=B1=EA=B3=B5=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/image/status/ImageErrorStatus.java | 4 +++- .../eatsfine/domain/tableimage/dto/TableImageResDto.java | 6 ++++++ .../domain/tableimage/status/TableImageSuccessStatus.java | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/image/status/ImageErrorStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/image/status/ImageErrorStatus.java index 0a1a02b..f200815 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/image/status/ImageErrorStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/image/status/ImageErrorStatus.java @@ -11,7 +11,9 @@ public enum ImageErrorStatus implements BaseErrorCode { EMPTY_FILE(HttpStatus.BAD_REQUEST, "IMAGE4001", "업로드할 파일이 비어 있습니다."), INVALID_FILE_TYPE(HttpStatus.BAD_REQUEST, "IMAGE4002", "지원하지 않는 파일 형식입니다."), - S3_UPLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "IMAGE5001", "이미지 업로드에 실패했습니다."); + S3_UPLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "IMAGE5001", "이미지 업로드에 실패했습니다."), + _IMAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "IMAGE404", "해당하는 이미지가 존재하지 않습니다.") + ; private final HttpStatus httpStatus; diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java index e05e0dd..baea00d 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/dto/TableImageResDto.java @@ -17,4 +17,10 @@ public record GetTableImageDto( Long storeId, List tableImageUrls ){} + + @Builder + public record DeleteTableImageDto( + Long storeId, + List deletedTableImageIds + ){} } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java index db64553..058f30b 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/status/TableImageSuccessStatus.java @@ -12,7 +12,9 @@ public enum TableImageSuccessStatus implements BaseCode { _STORE_TABLE_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "TABLE_IMAGE200", "성공적으로 가게 테이블 이미지를 업로드했습니다."), - _STORE_TABLE_IMAGE_GET_SUCCESS(HttpStatus.OK, "TABLE_IMAGE2001", "성공적으로 가게 테이블 이미지를 조회했습니다.") + _STORE_TABLE_IMAGE_GET_SUCCESS(HttpStatus.OK, "TABLE_IMAGE2001", "성공적으로 가게 테이블 이미지를 조회했습니다."), + + _STORE_TABLE_IMAGE_DELETE_SUCCESS(HttpStatus.OK, "TABLE_IMAGE2002", "성공적으로 가게 테이블 이미지를 삭제했습니다.") ; From 1e729176e9c0973516f84f84b17d4f268762ad9d Mon Sep 17 00:00:00 2001 From: twodo0 Date: Mon, 19 Jan 2026 12:38:51 +0900 Subject: [PATCH 14/20] =?UTF-8?q?[FEAT]:=20=EA=B0=80=EA=B2=8C=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=9D=B4=EB=AF=B8=EC=A7=80(TableImage)=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TableImageController.java | 13 +++++++++++++ .../converter/TableImageConverter.java | 7 +++++++ .../repository/TableImageRepository.java | 3 +++ .../service/TableImageCommandService.java | 2 ++ .../service/TableImageCommandServiceImpl.java | 19 +++++++++++++++++++ 5 files changed, 44 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java index 6a71104..7ff69f6 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/controller/TableImageController.java @@ -52,4 +52,17 @@ ApiResponse getTableImage( return ApiResponse.of(TableImageSuccessStatus._STORE_TABLE_IMAGE_GET_SUCCESS, tableImageQueryService.getTableImage(storeId)); } + @Operation( + summary = "식당 테이블 이미지 삭제", + description = "식당 테이블 이미지를 삭제합니다." + ) + @DeleteMapping("/stores/{storeId}/table-images") + ApiResponse deleteTableImage( + @PathVariable Long storeId, + @RequestBody List tableImageIds + ) { + return ApiResponse.of(TableImageSuccessStatus._STORE_TABLE_IMAGE_DELETE_SUCCESS, tableImageCommandService.deleteTableImage(storeId, tableImageIds)); + } + + } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java index 3cd5e53..9daf0ae 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/converter/TableImageConverter.java @@ -19,4 +19,11 @@ public static TableImageResDto.GetTableImageDto toGetTableImageDto(Long storeId, .tableImageUrls(tableImages) .build(); } + + public static TableImageResDto.DeleteTableImageDto toDeleteTableImageDto(Long storeId, List removedTableImages) { + return TableImageResDto.DeleteTableImageDto.builder() + .storeId(storeId) + .deletedTableImageIds(removedTableImages) + .build(); + } } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java index a9faaac..414733b 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/repository/TableImageRepository.java @@ -6,6 +6,7 @@ import org.springframework.data.jpa.repository.Query; import java.util.List; +import java.util.Optional; public interface TableImageRepository extends JpaRepository { @@ -17,4 +18,6 @@ select coalesce(max(ti.imageOrder), 0) int findMaxOrderByStoreId(Long storeId); List findAllByStoreOrderByImageOrder(Store store); + + Optional findByIdAndStore(Long id, Store store); } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandService.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandService.java index 6cac13f..be27464 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandService.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandService.java @@ -8,4 +8,6 @@ public interface TableImageCommandService { TableImageResDto.UploadTableImageDto uploadTableImage(Long storeId, List files); + + TableImageResDto.DeleteTableImageDto deleteTableImage(Long storeId, List tableImageIds); } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java index 00a0708..bc72c8b 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/tableimage/service/TableImageCommandServiceImpl.java @@ -16,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.awt.*; import java.util.ArrayList; import java.util.List; @@ -52,4 +53,22 @@ public TableImageResDto.UploadTableImageDto uploadTableImage(Long storeId, List< } return TableImageConverter.toUploadTableImageDto(storeId, tableImages); } + + @Override + public TableImageResDto.DeleteTableImageDto deleteTableImage(Long storeId, List tableImageIds) { + Store store = storeRepository.findById(storeId) + .orElseThrow(() -> new StoreException(StoreErrorStatus._STORE_NOT_FOUND)); + + List tableImages = tableImageIds.stream() + .map(id -> tableImageRepository.findByIdAndStore(id, store) + .orElseThrow(() -> new ImageException(ImageErrorStatus._IMAGE_NOT_FOUND))) + .toList(); + + for (TableImage tableImage : tableImages) { + s3Service.deleteByKey(tableImage.getTableImageKey()); + store.removeTableImage(tableImage); + } + + return TableImageConverter.toDeleteTableImageDto(storeId, tableImageIds); + } } From 036a9fe22a0680e6300673a015cbc77ded3b24fe Mon Sep 17 00:00:00 2001 From: twodo0 Date: Tue, 20 Jan 2026 15:08:42 +0900 Subject: [PATCH 15/20] =?UTF-8?q?[CHORE]:=20application.yml=EC=97=90=20AWS?= =?UTF-8?q?=20S3=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b6cae83..98258c3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,3 +3,10 @@ spring: name: Eatsfine profiles: active: local + +cloud: + aws: + region: ap-northeast-2 + s3: + bucket: eatsfine-images + base-url: https://eatsfine-images.s3.ap-northeast-2.amazonaws.com \ No newline at end of file From d70f2a55e3762ac520c560da1f88451028002f4b Mon Sep 17 00:00:00 2001 From: twodo0 Date: Tue, 20 Jan 2026 15:53:29 +0900 Subject: [PATCH 16/20] =?UTF-8?q?[FIX]:=20StoreSuccessStatus=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/store/status/StoreSuccessStatus.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java index 1e0474a..3f55862 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java @@ -18,15 +18,11 @@ public enum StoreSuccessStatus implements BaseCode { _STORE_CREATED(HttpStatus.CREATED, "STORE201", "성공적으로 가게를 등록했습니다."), -<<<<<<< HEAD _STORE_UPDATE_SUCCESS(HttpStatus.OK, "STORE2004", "성공적으로 가게 기본 정보를 수정했습니다."), - _STORE_MAIN_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "STORE2005", "성공적으로 가게 대표 이미지를 업로드했습니다.") -======= - _STORE_MAIN_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "STORE2004", "성공적으로 가게 대표 이미지를 업로드했습니다."), + _STORE_MAIN_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "STORE2005", "성공적으로 가게 대표 이미지를 업로드했습니다."), _STORE_MAIN_IMAGE_GET_SUCCESS(HttpStatus.OK, "STORE2004", "성공적으로 가게 대표 이미지를 조회했습니다.") ->>>>>>> 4502c2b ([FEAT]: 가게 매인 이미지 조회 응답 DTO 및 성공 상태코드 추가) ; From 908ea076c86960a7325f0cc504c255e35e5459d2 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Tue, 20 Jan 2026 16:13:14 +0900 Subject: [PATCH 17/20] =?UTF-8?q?[FIX]:=20=EC=97=90=EB=9F=AC=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=BD=94=EB=93=9C=20=EC=A4=91=EB=B3=B5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/store/status/StoreSuccessStatus.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java index 3f55862..b581c92 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/store/status/StoreSuccessStatus.java @@ -22,7 +22,7 @@ public enum StoreSuccessStatus implements BaseCode { _STORE_MAIN_IMAGE_UPLOAD_SUCCESS(HttpStatus.OK, "STORE2005", "성공적으로 가게 대표 이미지를 업로드했습니다."), - _STORE_MAIN_IMAGE_GET_SUCCESS(HttpStatus.OK, "STORE2004", "성공적으로 가게 대표 이미지를 조회했습니다.") + _STORE_MAIN_IMAGE_GET_SUCCESS(HttpStatus.OK, "STORE2005", "성공적으로 가게 대표 이미지를 조회했습니다.") ; From 791e84ed58bbff4a6a75eeb453f85c91f0fa0375 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Tue, 20 Jan 2026 19:53:59 +0900 Subject: [PATCH 18/20] =?UTF-8?q?[CHORE]:=20application-test.yml=EC=97=90?= =?UTF-8?q?=20aws=20s3=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/application-test.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index 32c915a..76e7e81 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -6,3 +6,10 @@ spring: config: activate: on-profile: test + +cloud: + aws: + region: ap-northeast-2 + s3: + bucket: eatsfine-images + base-url: https://eatsfine-images.s3.ap-northeast-2.amazonaws.com \ No newline at end of file From 62079b2866db04f46cda39bb20884c7071ee84bc Mon Sep 17 00:00:00 2001 From: twodo0 Date: Tue, 20 Jan 2026 20:01:50 +0900 Subject: [PATCH 19/20] =?UTF-8?q?[FIX]:=20S3=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/application-test.yml | 9 +-------- src/test/resources/application.yml | 9 ++++++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index 76e7e81..addc334 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -5,11 +5,4 @@ server: spring: config: activate: - on-profile: test - -cloud: - aws: - region: ap-northeast-2 - s3: - bucket: eatsfine-images - base-url: https://eatsfine-images.s3.ap-northeast-2.amazonaws.com \ No newline at end of file + on-profile: test \ No newline at end of file diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index c005f2e..2cd499b 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -15,4 +15,11 @@ spring: payment: toss: - widget-secret-key: test_sk_sample_key_for_testing \ No newline at end of file + widget-secret-key: test_sk_sample_key_for_testing + +cloud: + aws: + region: ap-northeast-2 + s3: + bucket: eatsfine-images + base-url: https://eatsfine-images.s3.ap-northeast-2.amazonaws.com \ No newline at end of file From 12d1c43cec44f31045a2dd7e52eca4c88e244b65 Mon Sep 17 00:00:00 2001 From: twodo0 Date: Wed, 21 Jan 2026 01:54:16 +0900 Subject: [PATCH 20/20] =?UTF-8?q?[REFACTOR]:=20AWS=20S3=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EA=B0=92=20=ED=99=98=EA=B2=BD=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 6 +++--- src/test/resources/application.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 98258c3..2338f34 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -6,7 +6,7 @@ spring: cloud: aws: - region: ap-northeast-2 + region: ${AWS_REGION} s3: - bucket: eatsfine-images - base-url: https://eatsfine-images.s3.ap-northeast-2.amazonaws.com \ No newline at end of file + bucket: ${AWS_S3_BUCKET} + base-url: ${AWS_S3_BASE_URL} \ No newline at end of file diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 2cd499b..e65dfae 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -19,7 +19,7 @@ payment: cloud: aws: - region: ap-northeast-2 + region: test-region s3: - bucket: eatsfine-images - base-url: https://eatsfine-images.s3.ap-northeast-2.amazonaws.com \ No newline at end of file + bucket: test-bucket + base-url: https://test-bucket.s3.test-region.amazonaws.com \ No newline at end of file