diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApi.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApi.java index de4e73f3..acb25d49 100644 --- a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApi.java +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApi.java @@ -5,7 +5,7 @@ import org.sopt.makers.operation.dto.BaseResponse; -import org.sopt.makers.operation.web.banner.dto.request.*; +import org.sopt.makers.operation.web.banner.dto.request.BannerRequest; import org.springframework.http.ResponseEntity; public interface BannerApi { @@ -129,6 +129,24 @@ public interface BannerApi { ) } ) - ResponseEntity> createBanner(BannerRequest.BannerCreate request); + ResponseEntity> createBanner(BannerRequest.BannerCreateOrModify request); + @Operation( + summary = "배너 수정 API", + responses = { + @ApiResponse( + responseCode = "200", + description = "배너 수정 성공" + ), + @ApiResponse( + responseCode = "400", + description = "잘못된 요청" + ), + @ApiResponse( + responseCode = "500", + description = "서버 내부 오류" + ) + } + ) + ResponseEntity> updateBanner(Long bannerId, BannerRequest.BannerCreateOrModify request); } diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java index d18d9d23..27810d62 100644 --- a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java @@ -21,6 +21,11 @@ import org.springframework.web.bind.annotation.*; +import static org.sopt.makers.operation.code.success.web.BannerSuccessCode.SUCCESS_CREATE_BANNER; +import static org.sopt.makers.operation.code.success.web.BannerSuccessCode.SUCCESS_GET_BANNER_DETAIL; +import static org.sopt.makers.operation.code.success.web.BannerSuccessCode.SUCCESS_GET_BANNER_IMAGE_PRE_SIGNED_URL; +import static org.sopt.makers.operation.code.success.web.BannerSuccessCode.SUCCESS_UPDATE_BANNER; + @RestController @RequestMapping("/api/v1/banners") @RequiredArgsConstructor @@ -37,6 +42,20 @@ public ResponseEntity> getBannerDetail( return ApiResponseUtil.success(SUCCESS_GET_BANNER_DETAIL, response); } + @PostMapping + @Override + public ResponseEntity> createBanner(@RequestBody BannerRequest.BannerCreateOrModify request) { + val response = bannerService.createBanner(request); + return ApiResponseUtil.success(SUCCESS_CREATE_BANNER, response); + } + + @PutMapping("/{bannerId}") + @Override + public ResponseEntity> updateBanner(@PathVariable("bannerId") Long bannerId, BannerRequest.BannerCreateOrModify request) { + val response = bannerService.updateBanner(bannerId, request); + return ApiResponseUtil.success(SUCCESS_UPDATE_BANNER, response); + } + @Override @GetMapping public ResponseEntity> getBanners( @@ -81,13 +100,4 @@ public ResponseEntity> getIssuedPreSignedUrlForPutImage( imageExtension, contentType); return ApiResponseUtil.success(SUCCESS_GET_BANNER_IMAGE_PRE_SIGNED_URL, response); } - - @PostMapping - @Override - public ResponseEntity> createBanner( - @RequestBody BannerRequest.BannerCreate request - ) { - val response = bannerService.createBanner(request); - return ApiResponseUtil.success(SUCCESS_CREATE_BANNER, response); - } } diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/request/BannerRequest.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/request/BannerRequest.java index 2721dfab..009a05a8 100644 --- a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/request/BannerRequest.java +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/dto/request/BannerRequest.java @@ -9,7 +9,7 @@ @RequiredArgsConstructor(access = PRIVATE) public class BannerRequest { - public record BannerCreate( + public record BannerCreateOrModify( @JsonProperty("location") String bannerLocation, @JsonProperty("content_type") String bannerType, @JsonProperty("publisher") String publisher, diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerService.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerService.java index 006d1403..5707d685 100644 --- a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerService.java +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerService.java @@ -1,11 +1,12 @@ package org.sopt.makers.operation.web.banner.service; +import org.sopt.makers.operation.web.banner.dto.request.BannerRequest; + import java.util.Arrays; import java.util.List; import org.sopt.makers.operation.code.failure.BannerFailureCode; import org.sopt.makers.operation.exception.BannerException; -import org.sopt.makers.operation.web.banner.dto.request.*; import org.sopt.makers.operation.web.banner.dto.response.BannerResponse; import org.sopt.makers.operation.web.banner.dto.response.BannerResponse.BannerImageUrl; @@ -19,7 +20,9 @@ public interface BannerService { BannerResponse.ImagePreSignedUrl getIssuedPreSignedUrlForPutImage(String contentName, String imageType, String imageExtension, String contentType); - BannerResponse.BannerDetail createBanner(BannerRequest.BannerCreate request); + BannerResponse.BannerDetail createBanner(BannerRequest.BannerCreateOrModify request); + + BannerResponse.BannerDetail updateBanner(Long bannerId, BannerRequest.BannerCreateOrModify request); List getBanners(final FilterCriteria status, final SortCriteria sort); @@ -62,5 +65,4 @@ public static SortCriteria fromParameter(String parameter) { .findAny().orElseThrow(() -> new BannerException(BannerFailureCode.INVALID_BANNER_SORT_CRITERIA_PARAMETER)); } } - } diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerServiceImpl.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerServiceImpl.java index d0ade422..fa908106 100644 --- a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerServiceImpl.java +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/service/BannerServiceImpl.java @@ -17,7 +17,7 @@ import org.sopt.makers.operation.code.failure.BannerFailureCode; import org.sopt.makers.operation.config.ValueConfig; import org.sopt.makers.operation.exception.BannerException; -import org.sopt.makers.operation.web.banner.dto.request.BannerRequest.*; +import org.sopt.makers.operation.web.banner.dto.request.*; import org.sopt.makers.operation.web.banner.dto.response.BannerResponse; import org.sopt.makers.operation.web.banner.dto.response.BannerResponse.*; import org.springframework.stereotype.Service; @@ -27,7 +27,9 @@ @RequiredArgsConstructor @Transactional(readOnly = true) public class BannerServiceImpl implements BannerService { - + private static final String SLASH = "/"; + private static final String PROTOCOL_SEPARATOR = "//"; + private static final int PROTOCOL_END_OFFSET = 2; private final BannerRepository bannerRepository; private final S3Service s3Service; private final ValueConfig valueConfig; @@ -84,7 +86,7 @@ private String getBannerImageName(String location, String contentName, String im @Transactional @Override - public BannerDetail createBanner(BannerCreate request) { + public BannerDetail createBanner(BannerRequest.BannerCreateOrModify request) { val period = getPublishPeriod(request.startDate(), request.endDate()); val image = getBannerImage(request.pcImage(), request.mobileImage()); val newBanner = Banner.builder() @@ -100,6 +102,31 @@ public BannerDetail createBanner(BannerCreate request) { return BannerResponse.BannerDetail.fromEntity(banner); } + @Transactional + @Override + public BannerDetail updateBanner(Long bannerId, BannerRequest.BannerCreateOrModify request) { + var banner = getBannerById(bannerId); + val period = getPublishPeriod(request.startDate(), request.endDate()); + val image = getBannerImage(request.pcImage(), request.mobileImage()); + + deleteExistImage(banner.getImage().getPcImageUrl()); + deleteExistImage(banner.getImage().getMobileImageUrl()); + banner.updatePublisher(request.publisher()); + banner.updateLink(request.link()); + banner.updateContentType(ContentType.getByValue(request.bannerType())); + banner.updateLocation(PublishLocation.getByValue(request.bannerLocation())); + banner.updatePeriod(period); + banner.updateImage(image); + return BannerResponse.BannerDetail.fromEntity(banner); + } + + private void deleteExistImage(String url) { + val protocolEndIndex = url.indexOf(PROTOCOL_SEPARATOR) + PROTOCOL_END_OFFSET; + val firstSlashIndex = url.indexOf(SLASH, protocolEndIndex); + val extractedPath = url.substring(firstSlashIndex); + s3Service.deleteFile(valueConfig.getBannerBucket(), extractedPath); + } + @Override public List getBanners(FilterCriteria filter, SortCriteria sort) { val allBanners = bannerRepository.findAll(); diff --git a/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java b/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java index 1790714f..895e094e 100644 --- a/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java +++ b/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java @@ -131,13 +131,13 @@ class CreateBannerTests { @DisplayName("(POST) New Banner") void createNewBanner() throws Exception { // given - BannerCreate bannerCreate = new BannerCreate("pg_community", "product", "publisher", + BannerCreateOrModify bannerCreate = new BannerCreateOrModify("pg_community", "product", "publisher", LocalDate.of(2024, 1, 1), LocalDate.of(2024, 12, 31), "link", "image-url-pc", "image-url-mobile"); BannerResponse.BannerDetail mockBannerDetail = new BannerResponse.BannerDetail( MOCK_BANNER_ID, "in_progress", "pg_community", "product", "publisher", "link", LocalDate.of(2024, 1, 1), LocalDate.of(2024, 12, 31), "image-url-pc", "image-url-mobile"); String request = objectMapper.writeValueAsString(bannerCreate); - when(bannerService.createBanner(any(BannerCreate.class))).thenReturn(mockBannerDetail); + when(bannerService.createBanner(any(BannerCreateOrModify.class))).thenReturn(mockBannerDetail); // when mockMvc.perform(post("/api/v1/banners") diff --git a/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java b/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java index 793b6884..fa11eb65 100644 --- a/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java +++ b/operation-common/src/main/java/org/sopt/makers/operation/code/success/web/BannerSuccessCode.java @@ -15,6 +15,7 @@ public enum BannerSuccessCode implements SuccessCode { SUCCESS_DELETE_BANNER(HttpStatus. NO_CONTENT, "배너 삭제 성공"), SUCCESS_GET_EXTERNAL_BANNERS(HttpStatus.OK, "외부 배너 조회 성공"), SUCCESS_GET_BANNER_IMAGE_PRE_SIGNED_URL(HttpStatus.OK, "이미지 업로드 pre signed url 조회에 성공했습니다"), + SUCCESS_UPDATE_BANNER(HttpStatus.OK, "배너 수정에 성공했습니다"), SUCCESS_CREATE_BANNER(HttpStatus.CREATED, "배너 생성에 성공했습니다") ; diff --git a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java index 0db9ef46..c87dabab 100644 --- a/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java +++ b/operation-domain/src/main/java/org/sopt/makers/operation/banner/domain/Banner.java @@ -81,4 +81,10 @@ public void updateContentType(ContentType contentType) { public void updatePublisher(String publisher) { this.publisher = publisher; } + + public void updateLink(String link) { this.link = link;} + + public void updatePeriod(PublishPeriod period) { this.period = period; } + + public void updateImage(BannerImage image) { this.image = image; } } diff --git a/operation-external/src/main/java/org/sopt/makers/operation/client/s3/S3Service.java b/operation-external/src/main/java/org/sopt/makers/operation/client/s3/S3Service.java index 6b5e1b60..cb5f6a3a 100644 --- a/operation-external/src/main/java/org/sopt/makers/operation/client/s3/S3Service.java +++ b/operation-external/src/main/java/org/sopt/makers/operation/client/s3/S3Service.java @@ -4,4 +4,6 @@ public interface S3Service { String createPreSignedUrlForPutObject(String bucketName, String fileName); String getUrl(String bucketName, String fileName); + + void deleteFile(String bucketName, String fileName); } diff --git a/operation-external/src/main/java/org/sopt/makers/operation/client/s3/S3ServiceImpl.java b/operation-external/src/main/java/org/sopt/makers/operation/client/s3/S3ServiceImpl.java index f14aa89c..a1ffd6b1 100644 --- a/operation-external/src/main/java/org/sopt/makers/operation/client/s3/S3ServiceImpl.java +++ b/operation-external/src/main/java/org/sopt/makers/operation/client/s3/S3ServiceImpl.java @@ -46,7 +46,8 @@ public String getUrl(String bucketName, String fileName) { } } - public void deleteFile(String bucketName, String fileName){ + @Override + public void deleteFile(String bucketName, String fileName) { s3Client.deleteObject(b -> b.bucket(bucketName).key(fileName)); } } \ No newline at end of file