diff --git a/src/main/java/com/somemore/center/controller/PreferItemCommandApiController.java b/src/main/java/com/somemore/center/controller/PreferItemCommandApiController.java new file mode 100644 index 000000000..cde5cfc10 --- /dev/null +++ b/src/main/java/com/somemore/center/controller/PreferItemCommandApiController.java @@ -0,0 +1,38 @@ +package com.somemore.center.controller; + +import com.somemore.center.dto.request.PreferItemCreateRequestDto; +import com.somemore.center.dto.response.PreferItemCreateResponseDto; +import com.somemore.center.usecase.command.CreatePreferItemUseCase; +import com.somemore.global.common.response.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.UUID; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/preferItem") +@Tag(name = "PreferItem Command API", description = "선호 물품 등록, 삭제 기능을 제공합니다") +public class PreferItemCommandApiController { + + private final CreatePreferItemUseCase createPreferItemUseCase; + + @Secured("ROLE_CENTER") + @Operation(summary = "기관 선호물품 등록 API") + @PostMapping() + public ApiResponse registerPreferItem(@Valid @RequestBody PreferItemCreateRequestDto requestDto, + @AuthenticationPrincipal String userId) { + + PreferItemCreateResponseDto responseDto = createPreferItemUseCase.createPreferItem(UUID.fromString(userId), requestDto); + + return ApiResponse.ok(200, responseDto, "관심 기관 등록 성공"); + } +} diff --git a/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java b/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java index bda87dae5..29a266231 100644 --- a/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java +++ b/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java @@ -1,18 +1,22 @@ package com.somemore.center.dto.request; - +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.somemore.center.domain.PreferItem; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import java.util.UUID; - +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public record PreferItemCreateRequestDto( - UUID centerId, + @Schema(description = "선호물품 이름", example = "어린이 도서") + @NotNull(message = "물품 이름은 필수값입니다.") String itemName ) { - public PreferItem createPreferItem() { + public PreferItem toEntity(UUID centerId) { return PreferItem.create(centerId, itemName); } diff --git a/src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java b/src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java new file mode 100644 index 000000000..faa0a8f20 --- /dev/null +++ b/src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java @@ -0,0 +1,29 @@ +package com.somemore.center.dto.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.somemore.center.domain.PreferItem; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; + +import java.util.UUID; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@Builder +public record PreferItemCreateResponseDto( + @Schema(description = "선호물품의 ID", example = "111") + Long id, + @Schema(description = "기관의 ID", example = "123e4567-e89b-12d3-a456-426614174000") + UUID centerId, + @Schema(description = "선호물품 이름", example = "어린이 도서") + String itemName +) { + public static PreferItemCreateResponseDto from(PreferItem preferItem) { + return PreferItemCreateResponseDto.builder() + .id(preferItem.getId()) + .centerId(preferItem.getCenterId()) + .itemName(preferItem.getItemName()) + .build(); + } +} + diff --git a/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java b/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java index 5a22f8679..d9565837c 100644 --- a/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java +++ b/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java @@ -1,6 +1,8 @@ package com.somemore.center.service.command; +import com.somemore.center.domain.PreferItem; import com.somemore.center.dto.request.PreferItemCreateRequestDto; +import com.somemore.center.dto.response.PreferItemCreateResponseDto; import com.somemore.center.repository.PreferItemRepository; import com.somemore.center.usecase.command.CreatePreferItemUseCase; import com.somemore.center.usecase.query.CenterQueryUseCase; @@ -8,6 +10,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.UUID; + @RequiredArgsConstructor @Transactional @Service @@ -17,11 +21,15 @@ public class CreatePreferItemService implements CreatePreferItemUseCase { private final PreferItemRepository preferItemRepository; @Override - public void createPreferItem(PreferItemCreateRequestDto requestDto) { + public PreferItemCreateResponseDto createPreferItem(UUID userId, PreferItemCreateRequestDto requestDto) { + + centerQueryUseCase.validateCenterExists(userId); + + PreferItem preferItem = requestDto.toEntity(userId); - centerQueryUseCase.validateCenterExists(requestDto.centerId()); + preferItemRepository.save(preferItem); - preferItemRepository.save(requestDto.createPreferItem()); + return PreferItemCreateResponseDto.from(preferItem); } } diff --git a/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java b/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java index 78c793edc..f500a6248 100644 --- a/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java +++ b/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java @@ -1,8 +1,11 @@ package com.somemore.center.usecase.command; import com.somemore.center.dto.request.PreferItemCreateRequestDto; +import com.somemore.center.dto.response.PreferItemCreateResponseDto; + +import java.util.UUID; public interface CreatePreferItemUseCase { - void createPreferItem(PreferItemCreateRequestDto requestDto); + PreferItemCreateResponseDto createPreferItem(UUID userId, PreferItemCreateRequestDto requestDto); } diff --git a/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java b/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java new file mode 100644 index 000000000..eac612b02 --- /dev/null +++ b/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java @@ -0,0 +1,83 @@ +package com.somemore.center.controller; + +import com.somemore.ControllerTestSupport; +import com.somemore.WithMockCustomUser; +import com.somemore.center.dto.request.PreferItemCreateRequestDto; +import com.somemore.center.dto.response.PreferItemCreateResponseDto; +import com.somemore.center.usecase.command.CreatePreferItemUseCase; +import com.somemore.global.exception.BadRequestException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; + +import java.util.UUID; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class PreferItemCommandApiControllerTest extends ControllerTestSupport { + + @MockBean + private CreatePreferItemUseCase createPreferItemUseCase; + + + @DisplayName("기관은 선호물품을 등록할 수 있다. (controller)") + @Test + @WithMockCustomUser(role = "CENTER") + void registerPreferItem() throws Exception { + // given + UUID userId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); // 고정된 UUID 사용 + PreferItemCreateRequestDto requestDto = new PreferItemCreateRequestDto("어린이 도서"); + PreferItemCreateResponseDto responseDto = new PreferItemCreateResponseDto( + 1L, + userId, + "어린이 도서" + ); + + given(createPreferItemUseCase.createPreferItem(userId, requestDto)) + .willReturn(responseDto); + + // when & then + mockMvc.perform(post("/api/preferItem") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + .principal(() -> userId.toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.message").value("관심 기관 등록 성공")) + .andExpect(jsonPath("$.data.id").value(responseDto.id())) + .andExpect(jsonPath("$.data.center_id").value(responseDto.centerId().toString())) + .andExpect(jsonPath("$.data.item_name").value(responseDto.itemName())); + + // verify + verify(createPreferItemUseCase).createPreferItem(userId, requestDto); + } + + @DisplayName("존재하지 않는 기관 ID로 선호물품을 등록할 수 없다. (controller)") + @Test + @WithMockCustomUser(role = "CENTER") + void registerPreferItem_Fail_WhenCenterNotExists() throws Exception { + // given + UUID userId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); + PreferItemCreateRequestDto requestDto = new PreferItemCreateRequestDto("어린이 도서"); + + given(createPreferItemUseCase.createPreferItem(userId, requestDto)) + .willThrow(new BadRequestException("존재하지 않는 기관입니다.")); + + // when & then + mockMvc.perform(post("/api/preferItem") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + .principal(() -> userId.toString())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.status").value(400)) + .andExpect(jsonPath("$.detail").value("존재하지 않는 기관입니다.")); + + // verify + verify(createPreferItemUseCase).createPreferItem(userId, requestDto); + } +} diff --git a/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java b/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java index a7db8c925..ca56042d5 100644 --- a/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java +++ b/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java @@ -6,12 +6,16 @@ import com.somemore.center.dto.request.PreferItemCreateRequestDto; import com.somemore.center.repository.CenterRepository; import com.somemore.center.repository.PreferItemRepository; +import com.somemore.global.exception.BadRequestException; import jakarta.transaction.Transactional; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.util.UUID; + import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; @Transactional class CreatePreferItemServiceTest extends IntegrationTestSupport { @@ -29,26 +33,16 @@ class CreatePreferItemServiceTest extends IntegrationTestSupport { @Test void createPreferItem() { //given - Center center = Center.create( - "기본 기관 이름", - "010-1234-5678", - "http://example.com/image.jpg", - "기관 소개 내용", - "http://example.com", - "account123", - "password123" - ); + Center center = createCenter(); centerRepository.save(center); String itemName = "어린이 도서"; - PreferItemCreateRequestDto requestDto = new PreferItemCreateRequestDto( - center.getId(), itemName ); //when - createPreferItemService.createPreferItem(requestDto); + createPreferItemService.createPreferItem(center.getId(), requestDto); //then PreferItem savedItem = preferItemRepository.findAll().stream() @@ -61,4 +55,37 @@ void createPreferItem() { assertThat(savedItem.getItemName()).isEqualTo(itemName); } + @DisplayName("존재하지 않는 기관 아이디로 선호물품을 등록하려고 하면 예외를 발생시킨다.") + @Test + void createPreferItemThrowsExceptionWhenCenterDoesNotExist() { + // given + UUID invalidCenterId = UUID.randomUUID(); + String itemName = "어린이 도서"; + + PreferItemCreateRequestDto requestDto = new PreferItemCreateRequestDto( + itemName + ); + + // when & then + assertThatThrownBy(() -> createPreferItemService.createPreferItem(invalidCenterId, requestDto)) + .isInstanceOf(BadRequestException.class) + .hasMessageContaining("존재하지 않는 기관입니다."); + } + + private Center createCenter() { + Center center = Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + + centerRepository.save(center); + + return center; + } + }