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..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 @@ -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,22 @@ 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 + ResponseEntity modifyBenefitShops( + @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..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 @@ -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,15 @@ public ResponseEntity createBenefitShops( return ResponseEntity.status(HttpStatus.CREATED).body(response); } + @PutMapping + public ResponseEntity modifyBenefitShops( + @RequestBody AdminModifyBenefitShopsRequest request, + @Auth(permit = {ADMIN}) Integer adminId + ) { + adminBenefitService.modifyBenefitShops(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/AdminBenefitShopsResponse.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java index 8ef41e87a..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 @@ -5,9 +5,11 @@ 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; +@JsonNaming(SnakeCaseStrategy.class) public record AdminBenefitShopsResponse( @Schema(example = "3", description = "상점 개수") Integer count, @@ -16,27 +18,36 @@ 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() ); } + @JsonNaming(SnakeCaseStrategy.class) private record InnerShopResponse( + @Schema(example = "1", description = "상점혜택 매핑id") + Integer shopBenefitMapId, + @Schema(example = "1", description = "고유 id") 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.getId(), + benefitCategoryMap.getShop().getId(), + benefitCategoryMap.getShop().getName(), + benefitCategoryMap.getDetail() ); } } 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..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 @@ -9,14 +9,27 @@ 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 + @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/dto/AdminModifyBenefitShopsRequest.java b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java new file mode 100644 index 000000000..c0c2614a6 --- /dev/null +++ b/src/main/java/in/koreatech/koin/admin/benefit/dto/AdminModifyBenefitShopsRequest.java @@ -0,0 +1,35 @@ +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( + @NotNull(message = "혜택문구 변경정보 리스트는 필수입니다.") + @NotBlankElement(message = "혜택문구 변경정보 리스트는 빈 요소가 존재할 수 없습니다.") + List modifyDetails +) { + + @JsonNaming(SnakeCaseStrategy.class) + public record InnerBenefitShopsRequest( + @Schema(description = "상점혜택 매핑id", example = "2", requiredMode = REQUIRED) + @NotNull(message = "상점혜택 매핑id는 필수입니다.") + Integer shopBenefitMapId, + + @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/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/repository/AdminBenefitCategoryMapRepository.java b/src/main/java/in/koreatech/koin/admin/benefit/repository/AdminBenefitCategoryMapRepository.java index 20f2ba22e..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 @@ -4,14 +4,14 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.Repository; +import org.springframework.data.repository.CrudRepository; import in.koreatech.koin.domain.benefit.model.BenefitCategoryMap; import org.springframework.data.repository.query.Param; -public interface AdminBenefitCategoryMapRepository extends Repository { +public interface AdminBenefitCategoryMapRepository extends CrudRepository { - void save(BenefitCategoryMap benefitCategoryMap); + List findAllByIdIn(List ids); @Query(""" SELECT bcm @@ -22,6 +22,16 @@ public interface AdminBenefitCategoryMapRepository extends Repository 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 7b349578e..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 @@ -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; @@ -16,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.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; @@ -90,10 +93,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 @@ -101,18 +101,53 @@ 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); + } + + @Transactional + public void modifyBenefitShops(AdminModifyBenefitShopsRequest request) { + Map shopBenefitIdToDetail = request.modifyDetails().stream() + .collect(Collectors.toMap( + AdminModifyBenefitShopsRequest.InnerBenefitShopsRequest::shopBenefitMapId, + AdminModifyBenefitShopsRequest.InnerBenefitShopsRequest::detail + )); + + 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 (!notFoundMapIds.isEmpty()) { + throw new BenefitMapNotFoundException("해당 혜택 카테고리에 존재하지 않는 상점이 포함되어 있습니다. shopBenefitMapId: " + notFoundMapIds); + } + } @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; + } } 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..f2b5b6c29 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 현수_사장님; @@ -73,6 +79,11 @@ public class AdminBenefitApiTest extends AcceptanceTest { BenefitCategory 서비스_증정; BenefitCategory 가게까지_픽업; + BenefitCategoryMap 김밥천국_혜택; + BenefitCategoryMap 마슬랜_혜택; + BenefitCategoryMap 티바_혜택; + BenefitCategoryMap 신전_혜택; + Shop 마슬랜; Shop 김밥천국; Shop 영업중인_티바; @@ -99,10 +110,10 @@ void setup() { 영업중인_티바 = shopFixture.영업중인_티바(현수_사장님); 영업중이_아닌_신전_떡볶이 = shopFixture.영업중이_아닌_신전_떡볶이(현수_사장님); - benefitCategoryMapFixture.혜택_추가(김밥천국, 배달비_무료); - benefitCategoryMapFixture.혜택_추가(마슬랜, 배달비_무료); - benefitCategoryMapFixture.혜택_추가(영업중인_티바, 배달비_무료); - benefitCategoryMapFixture.혜택_추가(영업중이_아닌_신전_떡볶이, 배달비_무료); + 김밥천국_혜택 = benefitCategoryMapFixture.설명이_포함된_혜택_추가(김밥천국, 배달비_무료, "설명1"); + 마슬랜_혜택 = benefitCategoryMapFixture.설명이_포함된_혜택_추가(마슬랜, 배달비_무료, "설명2"); + 티바_혜택 = benefitCategoryMapFixture.설명이_포함된_혜택_추가(영업중인_티바, 배달비_무료, "설명3"); + 신전_혜택 = benefitCategoryMapFixture.설명이_포함된_혜택_추가(영업중이_아닌_신전_떡볶이, 배달비_무료, "설명4"); notificationMessage_가게 = shopNotificationMessageFixture.알림메시지_가게(); shopParentCategory_가게 = shopParentCategoryFixture.상위_카테고리_가게(notificationMessage_가게); @@ -233,24 +244,36 @@ void setup() { "count": 4, "shops": [ { + "shop_benefit_map_id": %d, "id": %d, - "name": "김밥천국" + "name": "김밥천국", + "detail": "설명1" }, { + "shop_benefit_map_id": %d, "id": %d, - "name": "마슬랜 치킨" + "name": "마슬랜 치킨", + "detail": "설명2" }, { + "shop_benefit_map_id": %d, "id": %d, - "name": "티바" + "name": "티바", + "detail": "설명3" }, { + "shop_benefit_map_id": %d, "id": %d, - "name": "신전 떡볶이" + "name": "신전 떡볶이", + "detail": "설명4" } ] } - """, 김밥천국.getId(), 마슬랜.getId(), 영업중인_티바.getId(), 영업중이_아닌_신전_떡볶이.getId()))); + """, + 김밥천국_혜택.getId(), 김밥천국.getId(), + 마슬랜_혜택.getId(), 마슬랜.getId(), + 티바_혜택.getId(), 영업중인_티바.getId(), + 신전_혜택.getId(), 영업중이_아닌_신전_떡볶이.getId()))); } @Test @@ -261,7 +284,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())) ) @@ -271,17 +303,61 @@ void setup() { "shops": [ { "id": %d, - "name": "김밥천국" + "name": "김밥천국", + "detail": "김밥혜택설명" }, { "id": %d, - "name": "마슬랜 치킨" + "name": "마슬랜 치킨", + "detail": "마슬랜혜택설명" } ] } """, 김밥천국.getId(), 마슬랜.getId()))); } + @Test + void 특정_혜택을_제공하는_상점들을_수정한다() throws Exception { + mockMvc.perform( + put("/admin/benefit") + .header("Authorization", "Bearer " + token_admin) + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "modify_details": [ + { + "shop_benefit_map_id": %d, + "detail": "김밥새혜택설명" + }, + { + "shop_benefit_map_id": %d, + "detail": "마슬랜새혜택설명" + } + ] + } + """, 김밥천국_혜택.getId(), 마슬랜_혜택.getId())) + ) + .andExpect(status().isOk()); + + transactionTemplate.executeWithoutResult(status -> { + List updatedBenefit = + adminBenefitCategoryMapRepository.findAllByIdIn( + List.of(김밥천국_혜택.getId(), 마슬랜_혜택.getId()) + ); + + Map details = updatedBenefit.stream() + .collect(Collectors.toMap( + BenefitCategoryMap::getId, + BenefitCategoryMap::getDetail + )); + + assertThat(details).isEqualTo(Map.of( + 김밥천국_혜택.getId(), "김밥새혜택설명", + 마슬랜_혜택.getId(), "마슬랜새혜택설명" + )); + }); + } + @Test void 특정_혜택을_제공하는_상점들을_삭제한다() throws Exception { mockMvc.perform(