From 3e652cb4bfe31242a55dc67bfeaff03116b926d7 Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Wed, 11 Dec 2024 16:54:46 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20=ED=8A=B9=EC=A0=95=20=ED=98=9C?= =?UTF-8?q?=ED=83=9D=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=83=81?= =?UTF-8?q?=EC=A0=90=20=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=EC=97=90=20?= =?UTF-8?q?=ED=98=9C=ED=83=9D=20=EB=AC=B8=EA=B5=AC=20=EB=AF=B8=EB=A6=AC?= =?UTF-8?q?=EB=B3=B4=EA=B8=B0=20=EC=B6=94=EA=B0=80=20=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/AdminBenefitShopsResponse.java | 19 ++++++++++++------- .../benefit/service/AdminBenefitService.java | 5 +---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java index 8ef41e87a..848f06c9b 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; +import in.koreatech.koin.domain.benefit.model.BenefitCategoryMap; import in.koreatech.koin.domain.shop.model.shop.Shop; import io.swagger.v3.oas.annotations.media.Schema; @@ -16,10 +17,10 @@ public record AdminBenefitShopsResponse( List shops ) { - public static AdminBenefitShopsResponse from(List shops) { + public static AdminBenefitShopsResponse from(List benefitCategoryMaps) { return new AdminBenefitShopsResponse( - shops.size(), - shops.stream() + benefitCategoryMaps.size(), + benefitCategoryMaps.stream() .map(InnerShopResponse::from) .toList() ); @@ -30,13 +31,17 @@ private record InnerShopResponse( Integer id, @Schema(example = "수신반점", description = "이름") - String name + String name, + + @Schema(example = "4인 이상 픽업서비스", description = "혜택 문구 미리보기") + String detail ) { - public static InnerShopResponse from(Shop shop) { + public static InnerShopResponse from(BenefitCategoryMap benefitCategoryMap) { return new InnerShopResponse( - shop.getId(), - shop.getName() + benefitCategoryMap.getShop().getId(), + benefitCategoryMap.getShop().getName(), + benefitCategoryMap.getDetail() ); } } diff --git a/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java b/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java index 7b349578e..429134f89 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java @@ -90,10 +90,7 @@ public void deleteBenefitCategory(Integer categoryId) { public AdminBenefitShopsResponse getBenefitShops(Integer benefitId) { List benefitCategoryMaps = adminBenefitCategoryMapRepository.findAllByBenefitCategoryIdOrderByShopName(benefitId); - List shops = benefitCategoryMaps.stream() - .map(BenefitCategoryMap::getShop) - .toList(); - return AdminBenefitShopsResponse.from(shops); + return AdminBenefitShopsResponse.from(benefitCategoryMaps); } @Transactional From 1641264a7fee750022a429acdb3d9ace3088940c Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Wed, 11 Dec 2024 17:39:16 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20=ED=8A=B9=EC=A0=95=20=ED=98=9C?= =?UTF-8?q?=ED=83=9D=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=83=81?= =?UTF-8?q?=EC=A0=90=20=EC=A0=84=EC=B2=B4=20=EC=B6=94=EA=B0=80=EC=97=90=20?= =?UTF-8?q?=ED=98=9C=ED=83=9D=20=EB=AC=B8=EA=B5=AC=20=EB=AF=B8=EB=A6=AC?= =?UTF-8?q?=EB=B3=B4=EA=B8=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/AdminBenefitShopsResponse.java | 2 +- .../dto/AdminCreateBenefitShopsRequest.java | 26 ++++++++++++++++--- .../dto/AdminCreateBenefitShopsResponse.java | 21 ++++++++++----- .../AdminBenefitCategoryMapRepository.java | 5 ++-- .../benefit/service/AdminBenefitService.java | 23 +++++++++++----- 5 files changed, 55 insertions(+), 22 deletions(-) diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java index 848f06c9b..44f043294 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java @@ -33,7 +33,7 @@ private record InnerShopResponse( @Schema(example = "수신반점", description = "이름") String name, - @Schema(example = "4인 이상 픽업서비스", description = "혜택 문구 미리보기") + @Schema(example = "4인 이상 픽업서비스", description = "혜택 미리보기 문구") String detail ) { diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java index 35b36ae40..3f10cd9ec 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java @@ -9,14 +9,32 @@ import in.koreatech.koin.global.validation.NotBlankElement; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; @JsonNaming(SnakeCaseStrategy.class) public record AdminCreateBenefitShopsRequest( - @Schema(description = "상점 ID 리스트", example = "[1, 2, 5]", requiredMode = REQUIRED) - @NotNull(message = "상점 ID 리스트는 필수입니다.") - @NotBlankElement(message = "상점 ID 리스트는 빈 요소가 존재할 수 없습니다.") - List shopIds + @Schema( + description = "상점정보 리스트", + example = "[{\"shop_id\": 1, \"detail\": \"배달비 무료\"}, {\"shop_id\": 2, \"detail\": \"최소주문금액 0원\"}]", + requiredMode = REQUIRED + ) + @NotNull(message = "상점정보 리스트는 필수입니다.") + @NotBlankElement(message = "상점정보 리스트는 빈 요소가 존재할 수 없습니다.") + List shopDetails ) { + @JsonNaming(SnakeCaseStrategy.class) + public record InnerBenefitShopsRequest( + @Schema(description = "상점 고유 id", example = "2", requiredMode = REQUIRED) + @NotNull(message = "상점은 필수입니다.") + Integer shopId, + + @Schema(description = "혜택 미리보기 문구", example = "4인 이상 픽업서비스", requiredMode = REQUIRED) + @NotBlank(message = "혜택 미리보기 문구는 필수입니다.") + @Size(min = 2, max = 15, message = "혜택 미리보기 문구는 최소 2자 최대 15자입니다.") + String detail + ) { + } } diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsResponse.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsResponse.java index 1f4adf7d9..246d431d1 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsResponse.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsResponse.java @@ -5,16 +5,19 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; +import in.koreatech.koin.domain.benefit.model.BenefitCategoryMap; import in.koreatech.koin.domain.shop.model.shop.Shop; import io.swagger.v3.oas.annotations.media.Schema; public record AdminCreateBenefitShopsResponse( - @Schema(description = "상점 리스트") + @Schema(description = "상점 정보") List shops ) { - public static AdminCreateBenefitShopsResponse from(List shops) { + public static AdminCreateBenefitShopsResponse from(List benefitCategoryMaps) { return new AdminCreateBenefitShopsResponse( - shops.stream().map(InnerShopResponse::from).toList() + benefitCategoryMaps.stream() + .map(InnerShopResponse::from) + .toList() ); } @@ -23,13 +26,17 @@ private record InnerShopResponse( Integer id, @Schema(description = "상점 이름", example = "수신반점") - String name + String name, + + @Schema(example = "4인 이상 픽업서비스", description = "혜택 미리보기 문구") + String detail ) { - public static InnerShopResponse from(Shop shop) { + public static InnerShopResponse from(BenefitCategoryMap benefitCategoryMap) { return new InnerShopResponse( - shop.getId(), - shop.getName() + benefitCategoryMap.getShop().getId(), + benefitCategoryMap.getShop().getName(), + benefitCategoryMap.getDetail() ); } } diff --git a/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java b/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java index 20f2ba22e..d6690436f 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java @@ -4,14 +4,13 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.Repository; import in.koreatech.koin.domain.benefit.model.BenefitCategoryMap; import org.springframework.data.repository.query.Param; -public interface AdminBenefitCategoryMapRepository extends Repository { - - void save(BenefitCategoryMap benefitCategoryMap); +public interface AdminBenefitCategoryMapRepository extends CrudRepository { @Query(""" SELECT bcm diff --git a/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java b/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java index 429134f89..fce80a54d 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java @@ -1,6 +1,7 @@ package in.koreatech.koin.admin.benefit.service; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -98,16 +99,24 @@ public AdminCreateBenefitShopsResponse createBenefitShops( Integer benefitId, AdminCreateBenefitShopsRequest request ) { - List shops = adminShopRepository.findAllByIdIn(request.shopIds()); BenefitCategory benefitCategory = adminBenefitCategoryRepository.getById(benefitId); - for (Shop shop : shops) { - BenefitCategoryMap benefitCategoryMap = BenefitCategoryMap.builder() + Map shopIdToDetail = request.shopDetails().stream() + .collect(Collectors.toMap( + AdminCreateBenefitShopsRequest.InnerBenefitShopsRequest::shopId, + AdminCreateBenefitShopsRequest.InnerBenefitShopsRequest::detail + )); + List shops = adminShopRepository.findAllByIdIn(shopIdToDetail.keySet().stream().toList()); + + List benefitCategoryMaps = shops.stream() + .map(shop -> BenefitCategoryMap.builder() .shop(shop) .benefitCategory(benefitCategory) - .build(); - adminBenefitCategoryMapRepository.save(benefitCategoryMap); - } - return AdminCreateBenefitShopsResponse.from(shops); + .detail(shopIdToDetail.get(shop.getId())) + .build() + ) + .toList(); + adminBenefitCategoryMapRepository.saveAll(benefitCategoryMaps); + return AdminCreateBenefitShopsResponse.from(benefitCategoryMaps); } From 11e63ec2f50dd814bfad8dafb7d222b9071ff549 Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Wed, 11 Dec 2024 19:32:11 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=ED=8A=B9=EC=A0=95=20=ED=98=9C?= =?UTF-8?q?=ED=83=9D=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=83=81?= =?UTF-8?q?=EC=A0=90=20=EC=88=98=EC=A0=95=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../benefit/controller/AdminBenefitApi.java | 18 +++++++++ .../controller/AdminBenefitController.java | 11 +++++ .../dto/AdminModifyBenefitShopsRequest.java | 40 +++++++++++++++++++ .../BenefitShopNotFoundException.java | 20 ++++++++++ .../AdminBenefitCategoryMapRepository.java | 11 ++++- .../benefit/service/AdminBenefitService.java | 24 +++++++++++ .../benefit/model/BenefitCategoryMap.java | 4 ++ 7 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java create mode 100644 src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitShopNotFoundException.java diff --git a/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitApi.java b/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitApi.java index 9b0ea821f..55eb27fc3 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitApi.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitApi.java @@ -21,6 +21,7 @@ import in.koreatech.koin.admin.benefit.dto.AdminDeleteShopsRequest; import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryRequest; import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryResponse; +import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitShopsRequest; import in.koreatech.koin.admin.benefit.dto.AdminSearchBenefitShopsResponse; import in.koreatech.koin.global.auth.Auth; import io.swagger.v3.oas.annotations.Operation; @@ -127,6 +128,23 @@ ResponseEntity createBenefitShops( @Auth(permit = {ADMIN}) Integer adminId ); + @ApiResponses( + value = { + @ApiResponse(responseCode = "201"), + @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), + } + ) + @Operation(summary = "특정 혜택을 제공하는 상점을 수정한다.") + @PutMapping("/{id}/shops") + ResponseEntity modifyBenefitShops( + @PathVariable("id") Integer benefitId, + @RequestBody AdminModifyBenefitShopsRequest request, + @Auth(permit = {ADMIN}) Integer adminId + ); + @ApiResponses( value = { @ApiResponse(responseCode = "204"), diff --git a/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java b/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java index 544180fbc..0ffe2229a 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java @@ -23,6 +23,7 @@ import in.koreatech.koin.admin.benefit.dto.AdminDeleteShopsRequest; import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryRequest; import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryResponse; +import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitShopsRequest; import in.koreatech.koin.admin.benefit.dto.AdminSearchBenefitShopsResponse; import in.koreatech.koin.admin.benefit.service.AdminBenefitService; import in.koreatech.koin.global.auth.Auth; @@ -90,6 +91,16 @@ public ResponseEntity createBenefitShops( return ResponseEntity.status(HttpStatus.CREATED).body(response); } + @PutMapping("{id}/shops") + public ResponseEntity modifyBenefitShops( + @PathVariable("id") Integer benefitId, + @RequestBody AdminModifyBenefitShopsRequest request, + @Auth(permit = {ADMIN}) Integer adminId + ) { + adminBenefitService.modifyBenefitShops(benefitId, request); + return ResponseEntity.ok().build(); + } + @DeleteMapping("/{id}/shops") public ResponseEntity deleteBenefitShops( @PathVariable("id") Integer benefitId, diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java new file mode 100644 index 000000000..1044d027d --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java @@ -0,0 +1,40 @@ +package in.koreatech.koin.admin.benefit.dto; + +import static com.fasterxml.jackson.databind.PropertyNamingStrategies.*; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +import java.util.List; + +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import in.koreatech.koin.global.validation.NotBlankElement; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +@JsonNaming(SnakeCaseStrategy.class) +public record AdminModifyBenefitShopsRequest( + @Schema( + description = "상점정보 리스트", + example = "[{\"shop_id\": 1, \"detail\": \"배달비 무료\"}, {\"shop_id\": 2, \"detail\": \"최소주문금액 0원\"}]", + requiredMode = REQUIRED + ) + @NotNull(message = "상점정보 리스트는 필수입니다.") + @NotBlankElement(message = "상점정보 리스트는 빈 요소가 존재할 수 없습니다.") + List shopDetails +) { + + @JsonNaming(SnakeCaseStrategy.class) + public record InnerBenefitShopsRequest( + @Schema(description = "상점 고유 id", example = "2", requiredMode = REQUIRED) + @NotNull(message = "상점은 필수입니다.") + Integer shopId, + + @Schema(description = "혜택 미리보기 문구", example = "4인 이상 픽업서비스", requiredMode = REQUIRED) + @NotBlank(message = "혜택 미리보기 문구는 필수입니다.") + @Size(min = 2, max = 15, message = "혜택 미리보기 문구는 최소 2자 최대 15자입니다.") + String detail + ) { + } +} \ No newline at end of file diff --git a/src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitShopNotFoundException.java b/src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitShopNotFoundException.java new file mode 100644 index 000000000..8035abfed --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitShopNotFoundException.java @@ -0,0 +1,20 @@ +package in.koreatech.koin.admin.benefit.exception; + +import in.koreatech.koin.global.exception.DataNotFoundException; + +public class BenefitShopNotFoundException extends DataNotFoundException { + + private static final String DEFAULT_MESSAGE = "해당 혜택 카테고리에 존재하지 않는 상점입니다."; + + public BenefitShopNotFoundException(String message) { + super(message); + } + + public BenefitShopNotFoundException(String message, String detail) { + super(message, detail); + } + + public static BenefitShopNotFoundException withDetail(String detail) { + return new BenefitShopNotFoundException(DEFAULT_MESSAGE, detail); + } +} diff --git a/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java b/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java index d6690436f..f7edd7069 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java @@ -5,7 +5,6 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.Repository; import in.koreatech.koin.domain.benefit.model.BenefitCategoryMap; import org.springframework.data.repository.query.Param; @@ -21,6 +20,16 @@ public interface AdminBenefitCategoryMapRepository extends CrudRepository findAllByBenefitCategoryIdOrderByShopName(@Param("benefitId") Integer benefitId); + @Query(""" + SELECT bcm + FROM BenefitCategoryMap bcm + WHERE bcm.benefitCategory.id = :benefitId AND bcm.shop.id IN :shopIds + """) + List findAllByBenefitCategoryIdAndShopIds( + @Param("benefitId") Integer benefitId, + @Param("shopIds") List shopIds + ); + @Modifying @Query(""" DELETE FROM BenefitCategoryMap bcm diff --git a/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java b/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java index fce80a54d..c297cd6bb 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java @@ -17,9 +17,11 @@ import in.koreatech.koin.admin.benefit.dto.AdminDeleteShopsRequest; import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryRequest; import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryResponse; +import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitShopsRequest; import in.koreatech.koin.admin.benefit.dto.AdminSearchBenefitShopsResponse; import in.koreatech.koin.admin.benefit.exception.BenefitDuplicationException; import in.koreatech.koin.admin.benefit.exception.BenefitLimitException; +import in.koreatech.koin.admin.benefit.exception.BenefitShopNotFoundException; import in.koreatech.koin.admin.benefit.repository.AdminBenefitCategoryMapRepository; import in.koreatech.koin.admin.benefit.repository.AdminBenefitCategoryRepository; import in.koreatech.koin.admin.shop.repository.shop.AdminShopRepository; @@ -119,6 +121,28 @@ public AdminCreateBenefitShopsResponse createBenefitShops( return AdminCreateBenefitShopsResponse.from(benefitCategoryMaps); } + @Transactional + public void modifyBenefitShops(Integer benefitId, AdminModifyBenefitShopsRequest request) { + Map shopIdToDetail = request.shopDetails().stream() + .collect(Collectors.toMap( + AdminModifyBenefitShopsRequest.InnerBenefitShopsRequest::shopId, + AdminModifyBenefitShopsRequest.InnerBenefitShopsRequest::detail + )); + + List benefitCategoryMaps = adminBenefitCategoryMapRepository.findAllByBenefitCategoryIdAndShopIds( + benefitId, + shopIdToDetail.keySet().stream().toList() + ); + List notFoundShopIds = shopIdToDetail.keySet().stream() + .filter(shopId -> benefitCategoryMaps.stream().noneMatch(map -> map.getShop().getId().equals(shopId))) + .toList(); + + if (!notFoundShopIds.isEmpty()) { + throw new BenefitShopNotFoundException("해당 혜택 카테고리에 존재하지 않는 상점이 포함되어 있습니다. shopId: " + notFoundShopIds); + } + + benefitCategoryMaps.forEach(map -> map.modifyDetail(shopIdToDetail.get(map.getShop().getId()))); + } @Transactional public void deleteBenefitShops(Integer benefitId, AdminDeleteShopsRequest request) { diff --git a/src/main/java/in/koreatech/koin/domain/benefit/model/BenefitCategoryMap.java b/src/main/java/in/koreatech/koin/domain/benefit/model/BenefitCategoryMap.java index f416584cd..d29a0d14c 100644 --- a/src/main/java/in/koreatech/koin/domain/benefit/model/BenefitCategoryMap.java +++ b/src/main/java/in/koreatech/koin/domain/benefit/model/BenefitCategoryMap.java @@ -46,4 +46,8 @@ public BenefitCategoryMap(Shop shop, BenefitCategory benefitCategory, String det this.benefitCategory = benefitCategory; this.detail = detail; } + + public void modifyDetail(String detail) { + this.detail = detail; + } } From b3d4b3df23fb64c9ca53322451fb29dba01847ff Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Wed, 11 Dec 2024 19:43:01 +0900 Subject: [PATCH 04/11] =?UTF-8?q?test:=20=ED=8A=B9=EC=A0=95=20=ED=98=9C?= =?UTF-8?q?=ED=83=9D=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=EC=9D=98=20?= =?UTF-8?q?=EC=83=81=EC=A0=90=20=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/acceptance/AdminBenefitApiTest.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java index 1dafe5c1e..9743d1622 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java @@ -99,10 +99,10 @@ void setup() { 영업중인_티바 = shopFixture.영업중인_티바(현수_사장님); 영업중이_아닌_신전_떡볶이 = shopFixture.영업중이_아닌_신전_떡볶이(현수_사장님); - benefitCategoryMapFixture.혜택_추가(김밥천국, 배달비_무료); - benefitCategoryMapFixture.혜택_추가(마슬랜, 배달비_무료); - benefitCategoryMapFixture.혜택_추가(영업중인_티바, 배달비_무료); - benefitCategoryMapFixture.혜택_추가(영업중이_아닌_신전_떡볶이, 배달비_무료); + benefitCategoryMapFixture.설명이_포함된_혜택_추가(김밥천국, 배달비_무료, "설명1"); + benefitCategoryMapFixture.설명이_포함된_혜택_추가(마슬랜, 배달비_무료, "설명2"); + benefitCategoryMapFixture.설명이_포함된_혜택_추가(영업중인_티바, 배달비_무료, "설명3"); + benefitCategoryMapFixture.설명이_포함된_혜택_추가(영업중이_아닌_신전_떡볶이, 배달비_무료, "설명4"); notificationMessage_가게 = shopNotificationMessageFixture.알림메시지_가게(); shopParentCategory_가게 = shopParentCategoryFixture.상위_카테고리_가게(notificationMessage_가게); @@ -234,19 +234,23 @@ void setup() { "shops": [ { "id": %d, - "name": "김밥천국" + "name": "김밥천국", + "detail": "설명1" }, { "id": %d, - "name": "마슬랜 치킨" + "name": "마슬랜 치킨", + "detail": "설명2" }, { "id": %d, - "name": "티바" + "name": "티바", + "detail": "설명3" }, { "id": %d, - "name": "신전 떡볶이" + "name": "신전 떡볶이", + "detail": "설명4" } ] } From ae9f25ff8abc41e0d951a1d6ef1d6340b7eea78d Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Wed, 11 Dec 2024 19:48:44 +0900 Subject: [PATCH 05/11] =?UTF-8?q?test:=20=ED=8A=B9=EC=A0=95=20=ED=98=9C?= =?UTF-8?q?=ED=83=9D=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=EC=9D=98=20?= =?UTF-8?q?=EC=83=81=EC=A0=90=20=EC=B6=94=EA=B0=80=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/acceptance/AdminBenefitApiTest.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java index 9743d1622..e9a026e63 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java @@ -265,7 +265,16 @@ void setup() { .contentType(MediaType.APPLICATION_JSON) .content(String.format(""" { - "shop_ids": [%d, %d] + "shop_details": [ + { + "shop_id": %d, + "detail": "김밥혜택설명" + }, + { + "shop_id": %d, + "detail": "마슬랜혜택설명" + } + ] } """, 김밥천국.getId(), 마슬랜.getId())) ) @@ -275,11 +284,13 @@ void setup() { "shops": [ { "id": %d, - "name": "김밥천국" + "name": "김밥천국", + "detail": "김밥혜택설명" }, { "id": %d, - "name": "마슬랜 치킨" + "name": "마슬랜 치킨", + "detail": "마슬랜혜택설명" } ] } From 97461c3d1756c7b07b5f43397b896f54fedec4e7 Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Wed, 11 Dec 2024 21:00:13 +0900 Subject: [PATCH 06/11] =?UTF-8?q?test:=20=ED=8A=B9=EC=A0=95=20=ED=98=9C?= =?UTF-8?q?=ED=83=9D=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=EC=9D=98=20?= =?UTF-8?q?=EC=83=81=EC=A0=90=EB=93=A4=20=EC=88=98=EC=A0=95=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AdminBenefitController.java | 2 +- .../admin/acceptance/AdminBenefitApiTest.java | 49 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java b/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java index 0ffe2229a..cf512cfe3 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java @@ -91,7 +91,7 @@ public ResponseEntity createBenefitShops( return ResponseEntity.status(HttpStatus.CREATED).body(response); } - @PutMapping("{id}/shops") + @PutMapping("/{id}/shops") public ResponseEntity modifyBenefitShops( @PathVariable("id") Integer benefitId, @RequestBody AdminModifyBenefitShopsRequest request, diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java index e9a026e63..cac4e5fb1 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java @@ -6,6 +6,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -13,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; import in.koreatech.koin.AcceptanceTest; import in.koreatech.koin.admin.benefit.repository.AdminBenefitCategoryMapRepository; @@ -64,6 +67,9 @@ public class AdminBenefitApiTest extends AcceptanceTest { @Autowired ShopNotificationMessageFixture shopNotificationMessageFixture; + @Autowired + private TransactionTemplate transactionTemplate; + Admin admin; String token_admin; Owner 현수_사장님; @@ -297,6 +303,49 @@ void setup() { """, 김밥천국.getId(), 마슬랜.getId()))); } + @Test + void 특정_혜택을_제공하는_상점들을_수정한다() throws Exception { + mockMvc.perform( + put("/admin/benefit/{id}/shops", 배달비_무료.getId()) + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "shop_details": [ + { + "shop_id": %d, + "detail": "김밥새혜택설명" + }, + { + "shop_id": %d, + "detail": "마슬랜새혜택설명" + } + ] + } + """, 김밥천국.getId(), 마슬랜.getId())) + ) + .andExpect(status().isOk()); + + transactionTemplate.executeWithoutResult(status -> { + List updatedBenefit = + adminBenefitCategoryMapRepository.findAllByBenefitCategoryIdAndShopIds( + 배달비_무료.getId(), + List.of(김밥천국.getId(), 마슬랜.getId()) + ); + + Map details = updatedBenefit.stream() + .collect(Collectors.toMap( + map -> map.getShop().getId(), + BenefitCategoryMap::getDetail + )); + + assertThat(details).isEqualTo(Map.of( + 김밥천국.getId(), "김밥새혜택설명", + 마슬랜.getId(), "마슬랜새혜택설명" + )); + }); + } + @Test void 특정_혜택을_제공하는_상점들을_삭제한다() throws Exception { mockMvc.perform( From 5fdae1f40862b4b3c81e472c13e1ac4e71552ebf Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Thu, 12 Dec 2024 15:52:09 +0900 Subject: [PATCH 07/11] =?UTF-8?q?chore:=20=EA=B0=9C=ED=96=89=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java index 1044d027d..44fe089eb 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java @@ -37,4 +37,4 @@ public record InnerBenefitShopsRequest( String detail ) { } -} \ No newline at end of file +} From d6c7f2a22bb58f82d7564f52b033122449c86d79 Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Thu, 12 Dec 2024 16:04:41 +0900 Subject: [PATCH 08/11] =?UTF-8?q?chore:=20=EB=A9=80=ED=8B=B0=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=20=EB=AC=B8=EC=9E=90=EC=97=B4=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/AdminCreateBenefitShopsRequest.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java index 3f10cd9ec..2fb42e2ac 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java @@ -16,8 +16,18 @@ @JsonNaming(SnakeCaseStrategy.class) public record AdminCreateBenefitShopsRequest( @Schema( - description = "상점정보 리스트", - example = "[{\"shop_id\": 1, \"detail\": \"배달비 무료\"}, {\"shop_id\": 2, \"detail\": \"최소주문금액 0원\"}]", + description = "상점정보 리스트", example = """ + [ + { + "shop_id": 1, + "detail": "배달비 무료" + }, + { + "shop_id": 2, + "detail": "최소주문금액 0원" + } + ] + """, requiredMode = REQUIRED ) @NotNull(message = "상점정보 리스트는 필수입니다.") From be254a3944d7d6684f79aa38ebb799e3c07bea66 Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Thu, 12 Dec 2024 16:19:15 +0900 Subject: [PATCH 09/11] =?UTF-8?q?chore:=20=EB=A9=80=ED=8B=B0=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=20=EB=AC=B8=EC=9E=90=EC=97=B4=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/AdminModifyBenefitShopsRequest.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java index 44fe089eb..f09629cc0 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java @@ -16,8 +16,18 @@ @JsonNaming(SnakeCaseStrategy.class) public record AdminModifyBenefitShopsRequest( @Schema( - description = "상점정보 리스트", - example = "[{\"shop_id\": 1, \"detail\": \"배달비 무료\"}, {\"shop_id\": 2, \"detail\": \"최소주문금액 0원\"}]", + description = "상점정보 리스트", example = """ + [ + { + "shop_id": 1, + "detail": "배달비 무료" + }, + { + "shop_id": 2, + "detail": "최소주문금액 0원" + } + ] + """, requiredMode = REQUIRED ) @NotNull(message = "상점정보 리스트는 필수입니다.") From 16ff6fe32e5dce367cfb80781712c67189798e10 Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Thu, 12 Dec 2024 19:25:47 +0900 Subject: [PATCH 10/11] =?UTF-8?q?feat:=20=EC=88=98=EC=A0=95=EC=97=90?= =?UTF-8?q?=EC=84=9C=20shopId=EA=B0=80=20=EC=95=84=EB=8B=8C=20mapId?= =?UTF-8?q?=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../benefit/controller/AdminBenefitApi.java | 3 +- .../controller/AdminBenefitController.java | 5 +-- .../dto/AdminBenefitShopsResponse.java | 6 +++ .../dto/AdminModifyBenefitShopsRequest.java | 18 ++++---- .../BenefitMapNotFoundException.java | 20 +++++++++ .../BenefitShopNotFoundException.java | 20 --------- .../AdminBenefitCategoryMapRepository.java | 2 + .../benefit/service/AdminBenefitService.java | 33 ++++++++------ .../admin/acceptance/AdminBenefitApiTest.java | 44 ++++++++++++------- 9 files changed, 87 insertions(+), 64 deletions(-) create mode 100644 src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitMapNotFoundException.java delete mode 100644 src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitShopNotFoundException.java diff --git a/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitApi.java b/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitApi.java index 55eb27fc3..094ec84ef 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitApi.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitApi.java @@ -138,9 +138,8 @@ ResponseEntity createBenefitShops( } ) @Operation(summary = "특정 혜택을 제공하는 상점을 수정한다.") - @PutMapping("/{id}/shops") + @PutMapping ResponseEntity modifyBenefitShops( - @PathVariable("id") Integer benefitId, @RequestBody AdminModifyBenefitShopsRequest request, @Auth(permit = {ADMIN}) Integer adminId ); diff --git a/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java b/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java index cf512cfe3..76577af7f 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java @@ -91,13 +91,12 @@ public ResponseEntity createBenefitShops( return ResponseEntity.status(HttpStatus.CREATED).body(response); } - @PutMapping("/{id}/shops") + @PutMapping public ResponseEntity modifyBenefitShops( - @PathVariable("id") Integer benefitId, @RequestBody AdminModifyBenefitShopsRequest request, @Auth(permit = {ADMIN}) Integer adminId ) { - adminBenefitService.modifyBenefitShops(benefitId, request); + adminBenefitService.modifyBenefitShops(request); return ResponseEntity.ok().build(); } diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java index 44f043294..67779287f 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java @@ -9,6 +9,7 @@ import in.koreatech.koin.domain.shop.model.shop.Shop; import io.swagger.v3.oas.annotations.media.Schema; +@JsonNaming(SnakeCaseStrategy.class) public record AdminBenefitShopsResponse( @Schema(example = "3", description = "상점 개수") Integer count, @@ -26,7 +27,11 @@ public static AdminBenefitShopsResponse from(List benefitCat ); } + @JsonNaming(SnakeCaseStrategy.class) private record InnerShopResponse( + @Schema(example = "1", description = "상점혜택 매핑id") + Integer shopBenefitMapId, + @Schema(example = "1", description = "고유 id") Integer id, @@ -39,6 +44,7 @@ private record InnerShopResponse( public static InnerShopResponse from(BenefitCategoryMap benefitCategoryMap) { return new InnerShopResponse( + benefitCategoryMap.getId(), benefitCategoryMap.getShop().getId(), benefitCategoryMap.getShop().getName(), benefitCategoryMap.getDetail() diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java index f09629cc0..2dc87e0f7 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java @@ -16,30 +16,30 @@ @JsonNaming(SnakeCaseStrategy.class) public record AdminModifyBenefitShopsRequest( @Schema( - description = "상점정보 리스트", example = """ + description = "혜택문구 변경정보 리스트", example = """ [ { - "shop_id": 1, + "shopBenefitMapId": 1, "detail": "배달비 무료" }, { - "shop_id": 2, + "shopBenefitMapId": 2, "detail": "최소주문금액 0원" } ] """, requiredMode = REQUIRED ) - @NotNull(message = "상점정보 리스트는 필수입니다.") - @NotBlankElement(message = "상점정보 리스트는 빈 요소가 존재할 수 없습니다.") - List shopDetails + @NotNull(message = "혜택문구 변경정보 리스트는 필수입니다.") + @NotBlankElement(message = "혜택문구 변경정보 리스트는 빈 요소가 존재할 수 없습니다.") + List modifyDetails ) { @JsonNaming(SnakeCaseStrategy.class) public record InnerBenefitShopsRequest( - @Schema(description = "상점 고유 id", example = "2", requiredMode = REQUIRED) - @NotNull(message = "상점은 필수입니다.") - Integer shopId, + @Schema(description = "상점혜택 매핑id", example = "2", requiredMode = REQUIRED) + @NotNull(message = "상점혜택 매핑id는 필수입니다.") + Integer shopBenefitMapId, @Schema(description = "혜택 미리보기 문구", example = "4인 이상 픽업서비스", requiredMode = REQUIRED) @NotBlank(message = "혜택 미리보기 문구는 필수입니다.") diff --git a/src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitMapNotFoundException.java b/src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitMapNotFoundException.java new file mode 100644 index 000000000..fa6faa242 --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitMapNotFoundException.java @@ -0,0 +1,20 @@ +package in.koreatech.koin.admin.benefit.exception; + +import in.koreatech.koin.global.exception.DataNotFoundException; + +public class BenefitMapNotFoundException extends DataNotFoundException { + + private static final String DEFAULT_MESSAGE = "해당 혜택 카테고리에 존재하지 않는 입니다."; + + public BenefitMapNotFoundException(String message) { + super(message); + } + + public BenefitMapNotFoundException(String message, String detail) { + super(message, detail); + } + + public static BenefitMapNotFoundException withDetail(String detail) { + return new BenefitMapNotFoundException(DEFAULT_MESSAGE, detail); + } +} diff --git a/src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitShopNotFoundException.java b/src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitShopNotFoundException.java deleted file mode 100644 index 8035abfed..000000000 --- a/src/main/java/in/koreatech/koin/admin/benefit/exception/BenefitShopNotFoundException.java +++ /dev/null @@ -1,20 +0,0 @@ -package in.koreatech.koin.admin.benefit.exception; - -import in.koreatech.koin.global.exception.DataNotFoundException; - -public class BenefitShopNotFoundException extends DataNotFoundException { - - private static final String DEFAULT_MESSAGE = "해당 혜택 카테고리에 존재하지 않는 상점입니다."; - - public BenefitShopNotFoundException(String message) { - super(message); - } - - public BenefitShopNotFoundException(String message, String detail) { - super(message, detail); - } - - public static BenefitShopNotFoundException withDetail(String detail) { - return new BenefitShopNotFoundException(DEFAULT_MESSAGE, detail); - } -} diff --git a/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java b/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java index f7edd7069..56125d12b 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java @@ -11,6 +11,8 @@ public interface AdminBenefitCategoryMapRepository extends CrudRepository { + List findAllByIdIn(List ids); + @Query(""" SELECT bcm FROM BenefitCategoryMap bcm diff --git a/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java b/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java index c297cd6bb..0a1c04c6c 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/service/AdminBenefitService.java @@ -21,7 +21,7 @@ import in.koreatech.koin.admin.benefit.dto.AdminSearchBenefitShopsResponse; import in.koreatech.koin.admin.benefit.exception.BenefitDuplicationException; import in.koreatech.koin.admin.benefit.exception.BenefitLimitException; -import in.koreatech.koin.admin.benefit.exception.BenefitShopNotFoundException; +import in.koreatech.koin.admin.benefit.exception.BenefitMapNotFoundException; import in.koreatech.koin.admin.benefit.repository.AdminBenefitCategoryMapRepository; import in.koreatech.koin.admin.benefit.repository.AdminBenefitCategoryRepository; import in.koreatech.koin.admin.shop.repository.shop.AdminShopRepository; @@ -122,26 +122,31 @@ public AdminCreateBenefitShopsResponse createBenefitShops( } @Transactional - public void modifyBenefitShops(Integer benefitId, AdminModifyBenefitShopsRequest request) { - Map shopIdToDetail = request.shopDetails().stream() + public void modifyBenefitShops(AdminModifyBenefitShopsRequest request) { + Map shopBenefitIdToDetail = request.modifyDetails().stream() .collect(Collectors.toMap( - AdminModifyBenefitShopsRequest.InnerBenefitShopsRequest::shopId, + AdminModifyBenefitShopsRequest.InnerBenefitShopsRequest::shopBenefitMapId, AdminModifyBenefitShopsRequest.InnerBenefitShopsRequest::detail )); - List benefitCategoryMaps = adminBenefitCategoryMapRepository.findAllByBenefitCategoryIdAndShopIds( - benefitId, - shopIdToDetail.keySet().stream().toList() - ); - List notFoundShopIds = shopIdToDetail.keySet().stream() - .filter(shopId -> benefitCategoryMaps.stream().noneMatch(map -> map.getShop().getId().equals(shopId))) + List benefitCategoryMaps = + adminBenefitCategoryMapRepository.findAllByIdIn(shopBenefitIdToDetail.keySet().stream().toList()); + + validateBenefitMapIds(shopBenefitIdToDetail, benefitCategoryMaps); + benefitCategoryMaps.forEach(map -> map.modifyDetail(shopBenefitIdToDetail.get(map.getId()))); + } + + private static void validateBenefitMapIds( + Map shopBenefitIdToDetail, + List benefitCategoryMaps + ) { + List notFoundMapIds = shopBenefitIdToDetail.keySet().stream() + .filter(mapId -> benefitCategoryMaps.stream().noneMatch(map -> map.getId().equals(mapId))) .toList(); - if (!notFoundShopIds.isEmpty()) { - throw new BenefitShopNotFoundException("해당 혜택 카테고리에 존재하지 않는 상점이 포함되어 있습니다. shopId: " + notFoundShopIds); + if (!notFoundMapIds.isEmpty()) { + throw new BenefitMapNotFoundException("해당 혜택 카테고리에 존재하지 않는 상점이 포함되어 있습니다. shopBenefitMapId: " + notFoundMapIds); } - - benefitCategoryMaps.forEach(map -> map.modifyDetail(shopIdToDetail.get(map.getShop().getId()))); } @Transactional diff --git a/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java b/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java index cac4e5fb1..f2b5b6c29 100644 --- a/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java +++ b/src/test/java/in/koreatech/koin/admin/acceptance/AdminBenefitApiTest.java @@ -79,6 +79,11 @@ public class AdminBenefitApiTest extends AcceptanceTest { BenefitCategory 서비스_증정; BenefitCategory 가게까지_픽업; + BenefitCategoryMap 김밥천국_혜택; + BenefitCategoryMap 마슬랜_혜택; + BenefitCategoryMap 티바_혜택; + BenefitCategoryMap 신전_혜택; + Shop 마슬랜; Shop 김밥천국; Shop 영업중인_티바; @@ -105,10 +110,10 @@ void setup() { 영업중인_티바 = shopFixture.영업중인_티바(현수_사장님); 영업중이_아닌_신전_떡볶이 = shopFixture.영업중이_아닌_신전_떡볶이(현수_사장님); - benefitCategoryMapFixture.설명이_포함된_혜택_추가(김밥천국, 배달비_무료, "설명1"); - benefitCategoryMapFixture.설명이_포함된_혜택_추가(마슬랜, 배달비_무료, "설명2"); - benefitCategoryMapFixture.설명이_포함된_혜택_추가(영업중인_티바, 배달비_무료, "설명3"); - benefitCategoryMapFixture.설명이_포함된_혜택_추가(영업중이_아닌_신전_떡볶이, 배달비_무료, "설명4"); + 김밥천국_혜택 = benefitCategoryMapFixture.설명이_포함된_혜택_추가(김밥천국, 배달비_무료, "설명1"); + 마슬랜_혜택 = benefitCategoryMapFixture.설명이_포함된_혜택_추가(마슬랜, 배달비_무료, "설명2"); + 티바_혜택 = benefitCategoryMapFixture.설명이_포함된_혜택_추가(영업중인_티바, 배달비_무료, "설명3"); + 신전_혜택 = benefitCategoryMapFixture.설명이_포함된_혜택_추가(영업중이_아닌_신전_떡볶이, 배달비_무료, "설명4"); notificationMessage_가게 = shopNotificationMessageFixture.알림메시지_가게(); shopParentCategory_가게 = shopParentCategoryFixture.상위_카테고리_가게(notificationMessage_가게); @@ -239,28 +244,36 @@ void setup() { "count": 4, "shops": [ { + "shop_benefit_map_id": %d, "id": %d, "name": "김밥천국", "detail": "설명1" }, { + "shop_benefit_map_id": %d, "id": %d, "name": "마슬랜 치킨", "detail": "설명2" }, { + "shop_benefit_map_id": %d, "id": %d, "name": "티바", "detail": "설명3" }, { + "shop_benefit_map_id": %d, "id": %d, "name": "신전 떡볶이", "detail": "설명4" } ] } - """, 김밥천국.getId(), 마슬랜.getId(), 영업중인_티바.getId(), 영업중이_아닌_신전_떡볶이.getId()))); + """, + 김밥천국_혜택.getId(), 김밥천국.getId(), + 마슬랜_혜택.getId(), 마슬랜.getId(), + 티바_혜택.getId(), 영업중인_티바.getId(), + 신전_혜택.getId(), 영업중이_아닌_신전_떡볶이.getId()))); } @Test @@ -306,42 +319,41 @@ void setup() { @Test void 특정_혜택을_제공하는_상점들을_수정한다() throws Exception { mockMvc.perform( - put("/admin/benefit/{id}/shops", 배달비_무료.getId()) + put("/admin/benefit") .header("Authorization", "Bearer " + token_admin) .contentType(MediaType.APPLICATION_JSON) .content(String.format(""" { - "shop_details": [ + "modify_details": [ { - "shop_id": %d, + "shop_benefit_map_id": %d, "detail": "김밥새혜택설명" }, { - "shop_id": %d, + "shop_benefit_map_id": %d, "detail": "마슬랜새혜택설명" } ] } - """, 김밥천국.getId(), 마슬랜.getId())) + """, 김밥천국_혜택.getId(), 마슬랜_혜택.getId())) ) .andExpect(status().isOk()); transactionTemplate.executeWithoutResult(status -> { List updatedBenefit = - adminBenefitCategoryMapRepository.findAllByBenefitCategoryIdAndShopIds( - 배달비_무료.getId(), - List.of(김밥천국.getId(), 마슬랜.getId()) + adminBenefitCategoryMapRepository.findAllByIdIn( + List.of(김밥천국_혜택.getId(), 마슬랜_혜택.getId()) ); Map details = updatedBenefit.stream() .collect(Collectors.toMap( - map -> map.getShop().getId(), + BenefitCategoryMap::getId, BenefitCategoryMap::getDetail )); assertThat(details).isEqualTo(Map.of( - 김밥천국.getId(), "김밥새혜택설명", - 마슬랜.getId(), "마슬랜새혜택설명" + 김밥천국_혜택.getId(), "김밥새혜택설명", + 마슬랜_혜택.getId(), "마슬랜새혜택설명" )); }); } From 5ca4290fe1cdb0e45ff57b2a9203e2e999dc0ada Mon Sep 17 00:00:00 2001 From: krSeonghyeon Date: Thu, 12 Dec 2024 19:49:32 +0900 Subject: [PATCH 11/11] =?UTF-8?q?feat:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20schema=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/AdminCreateBenefitShopsRequest.java | 15 --------------- .../dto/AdminModifyBenefitShopsRequest.java | 15 --------------- 2 files changed, 30 deletions(-) diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java index 2fb42e2ac..b2a11f935 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitShopsRequest.java @@ -15,21 +15,6 @@ @JsonNaming(SnakeCaseStrategy.class) public record AdminCreateBenefitShopsRequest( - @Schema( - description = "상점정보 리스트", example = """ - [ - { - "shop_id": 1, - "detail": "배달비 무료" - }, - { - "shop_id": 2, - "detail": "최소주문금액 0원" - } - ] - """, - requiredMode = REQUIRED - ) @NotNull(message = "상점정보 리스트는 필수입니다.") @NotBlankElement(message = "상점정보 리스트는 빈 요소가 존재할 수 없습니다.") List shopDetails diff --git a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java index 2dc87e0f7..c0c2614a6 100644 --- a/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java @@ -15,21 +15,6 @@ @JsonNaming(SnakeCaseStrategy.class) public record AdminModifyBenefitShopsRequest( - @Schema( - description = "혜택문구 변경정보 리스트", example = """ - [ - { - "shopBenefitMapId": 1, - "detail": "배달비 무료" - }, - { - "shopBenefitMapId": 2, - "detail": "최소주문금액 0원" - } - ] - """, - requiredMode = REQUIRED - ) @NotNull(message = "혜택문구 변경정보 리스트는 필수입니다.") @NotBlankElement(message = "혜택문구 변경정보 리스트는 빈 요소가 존재할 수 없습니다.") List modifyDetails