diff --git a/src/main/java/com/almang/inventory/global/api/SuccessMessage.java b/src/main/java/com/almang/inventory/global/api/SuccessMessage.java index f6c46064..8d274692 100644 --- a/src/main/java/com/almang/inventory/global/api/SuccessMessage.java +++ b/src/main/java/com/almang/inventory/global/api/SuccessMessage.java @@ -30,6 +30,7 @@ public enum SuccessMessage { // VENDOR CREATE_VENDOR_SUCCESS("발주처 등록 성공"), UPDATE_VENDOR_SUCCESS("발주처 수정 성공"), + GET_VENDOR_DETAIL_SUCCESS("발주처 상세 조회 성공"), ; private final String message; diff --git a/src/main/java/com/almang/inventory/vendor/controller/VendorController.java b/src/main/java/com/almang/inventory/vendor/controller/VendorController.java index 8c178398..7c3dcf9f 100644 --- a/src/main/java/com/almang/inventory/vendor/controller/VendorController.java +++ b/src/main/java/com/almang/inventory/vendor/controller/VendorController.java @@ -14,6 +14,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -60,4 +61,19 @@ public ResponseEntity> updateVendor( ApiResponse.success(SuccessMessage.UPDATE_VENDOR_SUCCESS.getMessage(), response) ); } + + @GetMapping("/{vendorId}") + @Operation(summary = "발주처 상세 조회", description = "발주처 정보를 반환합니다.") + public ResponseEntity> getVendorDetail( + @PathVariable Long vendorId, + @AuthenticationPrincipal CustomUserPrincipal userPrincipal + ) { + Long userId = userPrincipal.getId(); + log.info("[VendorController] 발주처 상세 조회 요청 - vendorId: {}, userId: {}", vendorId, userId); + VendorResponse response = vendorService.getVendorDetail(vendorId, userId); + + return ResponseEntity.ok( + ApiResponse.success(SuccessMessage.GET_VENDOR_DETAIL_SUCCESS.getMessage(), response) + ); + } } diff --git a/src/main/java/com/almang/inventory/vendor/service/VendorService.java b/src/main/java/com/almang/inventory/vendor/service/VendorService.java index 88cba89a..147bd58e 100644 --- a/src/main/java/com/almang/inventory/vendor/service/VendorService.java +++ b/src/main/java/com/almang/inventory/vendor/service/VendorService.java @@ -48,6 +48,15 @@ public VendorResponse updateVendor(Long vendorId, UpdateVendorRequest request, L return VendorResponse.from(vendor); } + @Transactional(readOnly = true) + public VendorResponse getVendorDetail(Long vendorId, Long userId) { + User user = findUserById(userId); + Vendor vendor = findVendorByIdAndValidateAccess(vendorId, user); + + log.info("[VendorService] 발주처 상세 조회 성공 - vendorId: {}", vendor.getId()); + return VendorResponse.from(vendor); + } + private Vendor toEntity(CreateVendorRequest request, User user) { return Vendor.builder() .store(user.getStore()) diff --git a/src/test/java/com/almang/inventory/vendor/controller/VendorControllerTest.java b/src/test/java/com/almang/inventory/vendor/controller/VendorControllerTest.java index edc1c7f5..50fe8fd0 100644 --- a/src/test/java/com/almang/inventory/vendor/controller/VendorControllerTest.java +++ b/src/test/java/com/almang/inventory/vendor/controller/VendorControllerTest.java @@ -6,6 +6,7 @@ import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import com.almang.inventory.global.api.SuccessMessage; @@ -234,4 +235,58 @@ private UsernamePasswordAuthenticationToken auth() { .value(ErrorCode.INVALID_INPUT_VALUE.getMessage())) .andExpect(jsonPath("$.data").doesNotExist()); } + + @Test + void 발주처_상세_조회에_성공한다() throws Exception { + // given + Long vendorId = 1L; + + VendorResponse response = new VendorResponse( + vendorId, + "테스트 발주처", + VendorChannel.KAKAO, + "010-1111-2222", + "메모", + true, + 1L + ); + + when(vendorService.getVendorDetail(anyLong(), anyLong())) + .thenReturn(response); + + // when & then + mockMvc.perform(get("/api/v1/vendor/{vendorId}", vendorId) + .with(authentication(auth())) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value(200)) + .andExpect(jsonPath("$.message") + .value(SuccessMessage.GET_VENDOR_DETAIL_SUCCESS.getMessage())) + .andExpect(jsonPath("$.data.vendorId").value(vendorId)) + .andExpect(jsonPath("$.data.name").value("테스트 발주처")) + .andExpect(jsonPath("$.data.channel").value("KAKAO")) + .andExpect(jsonPath("$.data.contactPoint").value("010-1111-2222")) + .andExpect(jsonPath("$.data.note").value("메모")) + .andExpect(jsonPath("$.data.storeId").value(1L)) + .andExpect(jsonPath("$.data.activated").value(true)); + } + + @Test + void 발주처_상세_조회_시_존재하지_않는_발주처면_예외가_발생한다() throws Exception { + // given + Long vendorId = 9999L; + + when(vendorService.getVendorDetail(anyLong(), anyLong())) + .thenThrow(new BaseException(ErrorCode.VENDOR_NOT_FOUND)); + + // when & then + mockMvc.perform(get("/api/v1/vendor/{vendorId}", vendorId) + .with(authentication(auth())) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.status") + .value(ErrorCode.VENDOR_NOT_FOUND.getHttpStatus().value())) + .andExpect(jsonPath("$.message").value(ErrorCode.VENDOR_NOT_FOUND.getMessage())) + .andExpect(jsonPath("$.data").doesNotExist()); + } } \ No newline at end of file diff --git a/src/test/java/com/almang/inventory/vendor/service/VendorServiceTest.java b/src/test/java/com/almang/inventory/vendor/service/VendorServiceTest.java index bdbb0f82..a1cf4049 100644 --- a/src/test/java/com/almang/inventory/vendor/service/VendorServiceTest.java +++ b/src/test/java/com/almang/inventory/vendor/service/VendorServiceTest.java @@ -238,4 +238,73 @@ private Vendor newVendor(Store store) { .isInstanceOf(BaseException.class) .hasMessageContaining(ErrorCode.VENDOR_ACCESS_DENIED.getMessage()); } + + @Test + void 발주처_상세_조회에_성공한다() { + // given + Store store = newStore(); + User user = newUser(store); + Vendor vendor = newVendor(store); + + // when + VendorResponse response = vendorService.getVendorDetail(vendor.getId(), user.getId()); + + // then + assertThat(response.vendorId()).isEqualTo(vendor.getId()); + assertThat(response.name()).isEqualTo("기존 발주처"); + assertThat(response.channel()).isEqualTo(VendorChannel.KAKAO); + assertThat(response.contactPoint()).isEqualTo("010-1111-1111"); + assertThat(response.note()).isEqualTo("원본 메모"); + assertThat(response.storeId()).isEqualTo(store.getId()); + assertThat(response.activated()).isTrue(); + } + + @Test + void 존재하지_않는_발주처_상세_조회시_예외가_발생한다() { + // given + Store store = newStore(); + User user = newUser(store); + Long notExistVendorId = 9999L; + + // when & then + assertThatThrownBy(() -> vendorService.getVendorDetail(notExistVendorId, user.getId())) + .isInstanceOf(BaseException.class) + .hasMessageContaining(ErrorCode.VENDOR_NOT_FOUND.getMessage()); + } + + @Test + void 다른_상점_발주처_상세_조회시_예외가_발생한다() { + // given + Store store1 = newStore(); + Store store2 = newStore(); + + User userOfStore1 = newUser(store1); + User userOfStore2 = userRepository.save( + User.builder() + .store(store2) + .username("detail_tester_store2") + .password("password") + .name("상점2 관리자") + .role(UserRole.ADMIN) + .build() + ); + + Vendor vendorOfStore2 = vendorRepository.save( + Vendor.builder() + .store(store2) + .name("상점2 발주처") + .channel(VendorChannel.KAKAO) + .contactPoint("010-2222-2222") + .note("상점2 메모") + .activated(true) + .build() + ); + + // when & then + assertThatThrownBy(() -> + vendorService.getVendorDetail(vendorOfStore2.getId(), userOfStore1.getId()) + ) + .isInstanceOf(BaseException.class) + .hasMessageContaining(ErrorCode.VENDOR_ACCESS_DENIED.getMessage()); + } }