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(), "마슬랜새혜택설명" )); }); }