From f05f1a157042cae3ceeda0042e437db12c14c4dc Mon Sep 17 00:00:00 2001 From: bum12ark Date: Mon, 7 Feb 2022 20:09:20 +0900 Subject: [PATCH] =?UTF-8?q?test(user):=20=EC=A0=90=EC=A3=BC=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20-=20=EC=A3=BC=EB=AC=B8=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Todo: JWT 구현 시 storeId 및 userId 가져오는 부분 기능 리팩터링 필요 - Spring Rest Docs 를 통한 테스트 추가 --- .../src}/docs/asciidoc/api-docs.adoc | 13 +- .../order/dto/OrderSearchCondition.java | 28 +++ .../domain/order/entity/Order.java | 7 +- .../repository/OrderRepositoryCustom.java | 30 ++- .../domain/order/service/OrderService.java | 4 +- .../order/service/OrderServiceImpl.java | 23 +-- .../domain/order/web/OrderController.java | 24 +-- .../orderservice/config/TestConfig.java | 18 ++ .../domain/order/web/OrderControllerTest.java | 172 ++++++++++++++++++ 9 files changed, 274 insertions(+), 45 deletions(-) rename {store-service/src/main => order-service/src}/docs/asciidoc/api-docs.adoc (77%) create mode 100644 order-service/src/main/java/com/justpickup/orderservice/domain/order/dto/OrderSearchCondition.java create mode 100644 order-service/src/test/java/com/justpickup/orderservice/config/TestConfig.java create mode 100644 order-service/src/test/java/com/justpickup/orderservice/domain/order/web/OrderControllerTest.java diff --git a/store-service/src/main/docs/asciidoc/api-docs.adoc b/order-service/src/docs/asciidoc/api-docs.adoc similarity index 77% rename from store-service/src/main/docs/asciidoc/api-docs.adoc rename to order-service/src/docs/asciidoc/api-docs.adoc index 15ce2f7..f38345e 100644 --- a/store-service/src/main/docs/asciidoc/api-docs.adoc +++ b/order-service/src/docs/asciidoc/api-docs.adoc @@ -64,8 +64,11 @@ == snippets 작성 컨벤션 domain-httpRequestCode-etc -== 상품 -=== 상품 조회 -operation::item-get[snippets='curl-request,http-request,http-response,path-parameters,response-fields'] -=== 상품 조회 (존재하지 않는 상품) -operation::item-get-notExistItemException[snippets='curl-request,http-request,http-response,path-parameters,response-fields'] \ No newline at end of file +== 주문 +=== 점주 서비스 - 주문 페이지 +- 페이지 offset : 6 + +operation::orderMain-get[snippets='curl-request,http-request,http-response,request-parameters,response-fields'] +=== 점주 서비스 - 주문 페이지 (잘못된 파라미터 형식) + +operation::orderMain-get-badParameterException[snippets='curl-request,http-request,http-response,request-parameters,response-fields'] diff --git a/order-service/src/main/java/com/justpickup/orderservice/domain/order/dto/OrderSearchCondition.java b/order-service/src/main/java/com/justpickup/orderservice/domain/order/dto/OrderSearchCondition.java new file mode 100644 index 0000000..c664d3c --- /dev/null +++ b/order-service/src/main/java/com/justpickup/orderservice/domain/order/dto/OrderSearchCondition.java @@ -0,0 +1,28 @@ +package com.justpickup.orderservice.domain.order.dto; + +import lombok.*; + +import javax.validation.constraints.Pattern; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +@Data @NoArgsConstructor @AllArgsConstructor +public class OrderSearchCondition { + + @Pattern(regexp = "^(19|20)\\d{2}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[0-1])$", + message = "YYYY-MM-DD 형식에 맞게 작성되지 않았습니다.") + private String orderDate; + private Long lastOrderId; + + public LocalDateTime getOrderStartTime() { + LocalDate orderTime = LocalDate.parse(orderDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + return orderTime.atStartOfDay(); + } + + public LocalDateTime getOrderEndTime() { + LocalDate orderTime = LocalDate.parse(orderDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + return LocalDateTime.of(orderTime, LocalTime.of(23, 59, 59)); + } +} \ No newline at end of file diff --git a/order-service/src/main/java/com/justpickup/orderservice/domain/order/entity/Order.java b/order-service/src/main/java/com/justpickup/orderservice/domain/order/entity/Order.java index 4d9547b..7b2554e 100644 --- a/order-service/src/main/java/com/justpickup/orderservice/domain/order/entity/Order.java +++ b/order-service/src/main/java/com/justpickup/orderservice/domain/order/entity/Order.java @@ -1,18 +1,16 @@ package com.justpickup.orderservice.domain.order.entity; -import com.justpickup.orderservice.domain.order.dto.OrderDto; -import com.justpickup.orderservice.domain.orderItem.dto.OrderItemDto; import com.justpickup.orderservice.domain.orderItem.entity.OrderItem; import com.justpickup.orderservice.domain.transaction.entity.Transaction; import com.justpickup.orderservice.global.entity.BaseEntity; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.BatchSize; import javax.persistence.*; import java.time.LocalDateTime; import java.util.List; -import java.util.stream.Collectors; @Entity @Table(name = "orders") @@ -28,6 +26,8 @@ public class Order extends BaseEntity { private Long userCouponId; + private Long storeId; + private Long orderPrice; private LocalDateTime orderTime; @@ -40,6 +40,7 @@ public class Order extends BaseEntity { @OneToOne(mappedBy = "order", fetch = FetchType.LAZY) private Transaction transaction; + @BatchSize(size = 100) @OneToMany(mappedBy = "order") private List orderItems; diff --git a/order-service/src/main/java/com/justpickup/orderservice/domain/order/repository/OrderRepositoryCustom.java b/order-service/src/main/java/com/justpickup/orderservice/domain/order/repository/OrderRepositoryCustom.java index de4b027..7b178e7 100644 --- a/order-service/src/main/java/com/justpickup/orderservice/domain/order/repository/OrderRepositoryCustom.java +++ b/order-service/src/main/java/com/justpickup/orderservice/domain/order/repository/OrderRepositoryCustom.java @@ -1,13 +1,13 @@ package com.justpickup.orderservice.domain.order.repository; +import com.justpickup.orderservice.domain.order.dto.OrderSearchCondition; import com.justpickup.orderservice.domain.order.entity.Order; +import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; -import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.LocalTime; import java.util.List; import static com.justpickup.orderservice.domain.order.entity.QOrder.order; @@ -18,18 +18,34 @@ public class OrderRepositoryCustom { private final JPAQueryFactory queryFactory; - public List findOrderMainBetweenOrderDate(LocalDate orderDate) { - LocalDateTime start = orderDate.atStartOfDay(); - LocalDateTime end = LocalDateTime.of(orderDate, LocalTime.of(23, 59, 59)); + /* + SELECT * + FROM order + WHERE 조건문 + AND id < 마지막 조회 ID + ORDER BY id desc + LIMIT 페이지 사이즈 + */ + public List findOrderMain(OrderSearchCondition condition, Long storeId) { + LocalDateTime start = condition.getOrderStartTime(); + LocalDateTime end = condition.getOrderEndTime(); return queryFactory .selectFrom(order) - .join(order.orderItems).fetchJoin() .join(order.transaction).fetchJoin() .where( - order.orderTime.between(start, end) + order.orderTime.between(start, end), + order.storeId.eq(storeId), + orderIdLt(condition.getLastOrderId()) ) + .orderBy(order.orderTime.desc()) + .limit(6) .distinct() .fetch(); } + + private BooleanExpression orderIdLt(Long lastOrderId) { + return lastOrderId != null ? order.id.lt(lastOrderId) : null; + } + } diff --git a/order-service/src/main/java/com/justpickup/orderservice/domain/order/service/OrderService.java b/order-service/src/main/java/com/justpickup/orderservice/domain/order/service/OrderService.java index 1ef99e9..6c9e6b8 100644 --- a/order-service/src/main/java/com/justpickup/orderservice/domain/order/service/OrderService.java +++ b/order-service/src/main/java/com/justpickup/orderservice/domain/order/service/OrderService.java @@ -1,10 +1,10 @@ package com.justpickup.orderservice.domain.order.service; import com.justpickup.orderservice.domain.order.dto.OrderDto; +import com.justpickup.orderservice.domain.order.dto.OrderSearchCondition; -import java.time.LocalDate; import java.util.List; public interface OrderService { - List findOrderMain(LocalDate localDate); + List findOrderMain(OrderSearchCondition condition, Long storeId); } diff --git a/order-service/src/main/java/com/justpickup/orderservice/domain/order/service/OrderServiceImpl.java b/order-service/src/main/java/com/justpickup/orderservice/domain/order/service/OrderServiceImpl.java index 2626f8f..87c0db6 100644 --- a/order-service/src/main/java/com/justpickup/orderservice/domain/order/service/OrderServiceImpl.java +++ b/order-service/src/main/java/com/justpickup/orderservice/domain/order/service/OrderServiceImpl.java @@ -1,18 +1,18 @@ package com.justpickup.orderservice.domain.order.service; import com.justpickup.orderservice.domain.order.dto.OrderDto; +import com.justpickup.orderservice.domain.order.dto.OrderSearchCondition; import com.justpickup.orderservice.domain.order.repository.OrderRepository; import com.justpickup.orderservice.domain.order.repository.OrderRepositoryCustom; import com.justpickup.orderservice.global.client.store.GetItemResponse; import com.justpickup.orderservice.global.client.store.StoreClient; -import com.justpickup.orderservice.global.client.user.UserClient; import com.justpickup.orderservice.global.client.user.GetCustomerResponse; +import com.justpickup.orderservice.global.client.user.UserClient; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDate; import java.util.List; import java.util.stream.Collectors; @@ -28,23 +28,24 @@ public class OrderServiceImpl implements OrderService { private final UserClient userClient; @Override - public List findOrderMain(LocalDate orderDate) { + public List findOrderMain(OrderSearchCondition condition, Long storeId) { // 주문 가져오기 - List orderDtoList = orderRepositoryCustom.findOrderMainBetweenOrderDate(orderDate) - .stream() - .map(OrderDto::createFullField) - .collect(Collectors.toList()); + List orderDtoList = + orderRepositoryCustom.findOrderMain(condition, storeId) + .stream() + .map(OrderDto::createFullField) + .collect(Collectors.toList()); // 사용자명 및 아이템 이름 가져오기 orderDtoList.forEach(orderDto -> { - GetCustomerResponse getCustomerResponse = userClient.getUser(orderDto.getUserId()) - .getData(); + GetCustomerResponse getCustomerResponse = + userClient.getUser(orderDto.getUserId()).getData(); orderDto.setUserName(getCustomerResponse.getUserName()); orderDto.getOrderItemDtoList() .forEach(orderItemDto -> { - GetItemResponse getItemResponse = storeClient.getItem(orderItemDto.getItemId()) - .getData(); + GetItemResponse getItemResponse = + storeClient.getItem(orderItemDto.getItemId()).getData(); orderItemDto.setItemName(getItemResponse.getName()); }); }); diff --git a/order-service/src/main/java/com/justpickup/orderservice/domain/order/web/OrderController.java b/order-service/src/main/java/com/justpickup/orderservice/domain/order/web/OrderController.java index 91ca880..c55c363 100644 --- a/order-service/src/main/java/com/justpickup/orderservice/domain/order/web/OrderController.java +++ b/order-service/src/main/java/com/justpickup/orderservice/domain/order/web/OrderController.java @@ -1,6 +1,7 @@ package com.justpickup.orderservice.domain.order.web; import com.justpickup.orderservice.domain.order.dto.OrderDto; +import com.justpickup.orderservice.domain.order.dto.OrderSearchCondition; import com.justpickup.orderservice.domain.order.entity.OrderStatus; import com.justpickup.orderservice.domain.order.service.OrderService; import com.justpickup.orderservice.domain.orderItem.dto.OrderItemDto; @@ -16,8 +17,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; -import javax.validation.constraints.Pattern; -import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.stream.Collectors; @@ -30,30 +29,21 @@ public class OrderController { private final OrderService orderService; @GetMapping("/orderMain") - public ResponseEntity orderMain(@Valid OrderMainRequest orderMainRequest) { + public ResponseEntity orderMain(@Valid OrderSearchCondition condition) { + // TODO: 2022/02/04 JWT 구현 시 변경 요망 + Long userId = 1L; + Long storeId = 1L; - List orderDto = orderService.findOrderMain(orderMainRequest.convertOrderTimeToLocalDate()); + List orderDto = orderService.findOrderMain(condition, storeId); List orderMainResponses = orderDto.stream() .map(OrderMainResponse::new) .collect(Collectors.toList()); - + return ResponseEntity.status(HttpStatus.OK) .body(Result.createSuccessResult(orderMainResponses)); } - @Data @NoArgsConstructor @AllArgsConstructor - static class OrderMainRequest { - // yyyy-mm-dd 형태를 가지는 패턴 조사 - @Pattern(regexp = "^(19|20)\\d{2}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[0-1])$", - message = "YYYY-MM-DD 형식에 맞게 작성되지 않았습니다.") - private String orderTime; - - public LocalDate convertOrderTimeToLocalDate() { - return LocalDate.parse(orderTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")); - } - } - @Data @NoArgsConstructor @AllArgsConstructor static class OrderMainResponse { private Long orderId; diff --git a/order-service/src/test/java/com/justpickup/orderservice/config/TestConfig.java b/order-service/src/test/java/com/justpickup/orderservice/config/TestConfig.java new file mode 100644 index 0000000..9b17478 --- /dev/null +++ b/order-service/src/test/java/com/justpickup/orderservice/config/TestConfig.java @@ -0,0 +1,18 @@ +package com.justpickup.orderservice.config; + +import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; + +@TestConfiguration +public class TestConfig { + + @Bean + public RestDocsMockMvcConfigurationCustomizer restDocsMockMvcConfigurationCustomizer() { + return configurer -> configurer.operationPreprocessors() + .withRequestDefaults(prettyPrint()) + .withResponseDefaults(prettyPrint()); + } +} diff --git a/order-service/src/test/java/com/justpickup/orderservice/domain/order/web/OrderControllerTest.java b/order-service/src/test/java/com/justpickup/orderservice/domain/order/web/OrderControllerTest.java new file mode 100644 index 0000000..345817f --- /dev/null +++ b/order-service/src/test/java/com/justpickup/orderservice/domain/order/web/OrderControllerTest.java @@ -0,0 +1,172 @@ +package com.justpickup.orderservice.domain.order.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.justpickup.orderservice.config.TestConfig; +import com.justpickup.orderservice.domain.order.dto.OrderDto; +import com.justpickup.orderservice.domain.order.dto.OrderSearchCondition; +import com.justpickup.orderservice.domain.order.entity.OrderStatus; +import com.justpickup.orderservice.domain.order.service.OrderService; +import com.justpickup.orderservice.domain.orderItem.dto.OrderItemDto; +import com.justpickup.orderservice.global.dto.Code; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.restdocs.request.RequestDocumentation.*; + +@WebMvcTest(OrderController.class) +@Import(TestConfig.class) +@AutoConfigureRestDocs(uriHost = "127.0.0.1", uriPort = 8001) +class OrderControllerTest { + + @Autowired + ObjectMapper objectMapper; + + @Autowired + MockMvc mockMvc; + + @MockBean + OrderService orderService; + + @Test + @DisplayName("점주 서비스 - 주문 페이지") + void getOrderMain() throws Exception { + // GIVEN + String orderDate = "2022-02-03"; + Long lastOrderId = 7L; + OrderSearchCondition condition = new OrderSearchCondition(orderDate, lastOrderId); + // TODO: 2022/02/07 jwt 구현 시 변경 요망 + Long storeId = 1L; + + given(orderService.findOrderMain(condition, storeId)) + .willReturn(getOrderMainDtoList()); + + // WHEN + ResultActions actions = mockMvc.perform(get("/orderMain") + .param("orderDate", orderDate) + .param("lastOrderId", String.valueOf(lastOrderId)) + ); + + // THEN + actions.andExpect(status().isOk()) + .andExpect(jsonPath("code").value(Code.SUCCESS.name())) + .andExpect(jsonPath("message").isEmpty()) + .andExpect(jsonPath("data").exists()) + .andExpect(jsonPath("data[*].orderItemResponses").exists()) + .andExpect(jsonPath("data[*].orderStatus").exists()) + .andExpect(jsonPath("data[*].orderTime").exists()) + .andDo(print()) + .andDo(document("orderMain-get", + requestParameters( + parameterWithName("orderDate").description("주문 날짜 YYYY-MM-DD"), + parameterWithName("lastOrderId").optional().description("페이지의 마지막 주문 고유 번호") + ), + responseFields( + fieldWithPath("code").description("결과 코드 SUCCESS/ERROR"), + fieldWithPath("message").description("메시지"), + fieldWithPath("data[*].orderId").description("주문 고유 번호"), + fieldWithPath("data[*].userId").description("고객 고유 번호"), + fieldWithPath("data[*].userName").description("고객 이름"), + fieldWithPath("data[*].orderItemResponses[*].orderItemId").description("장바구니 고유번호"), + fieldWithPath("data[*].orderItemResponses[*].itemId").description("상품 고유번호"), + fieldWithPath("data[*].orderItemResponses[*].itemName").description("상품 이름"), + fieldWithPath("data[*].orderStatus").description("주문 상태"), + fieldWithPath("data[*].orderTime").description("주문 시간") + ) + )) + ; + } + + @Test + @DisplayName("점주 서비스 - 주문 페이지 (잘못된 파라미터 형식)") + void getOrderMainBadRequestException() throws Exception { + // GIVEN + String orderDate = "20220203"; + Long lastOrderId = 7L; + + // WHEN + ResultActions actions = mockMvc.perform(get("/orderMain") + .param("orderDate", orderDate) + .param("lastOrderId", String.valueOf(lastOrderId)) + ); + + // THEN + actions.andExpect(status().isBadRequest()) + .andExpect(jsonPath("code").value(Code.ERROR.name())) + .andExpect(jsonPath("message").isNotEmpty()) + .andExpect(jsonPath("data").isEmpty()) + .andDo(print()) + .andDo(document("orderMain-get-badParameterException", + requestParameters( + parameterWithName("orderDate").description("주문 날짜 YYYY-MM-DD"), + parameterWithName("lastOrderId").optional().description("페이지의 마지막 주문 고유 번호") + ), + responseFields( + fieldWithPath("code").description("결과 코드 SUCCESS/ERROR"), + fieldWithPath("message").description("메시지"), + fieldWithPath("data").description("데이터") + ) + ) + ) + ; + } + + private List getOrderMainDtoList() { + OrderItemDto orderItemDto_100 = OrderItemDto.builder() + .id(100L) + .itemId(100L) + .build(); + orderItemDto_100.setItemName("아이템1"); + OrderItemDto orderItemDto_101 = OrderItemDto.builder() + .id(101L) + .itemId(101L) + .build(); + orderItemDto_101.setItemName("아이템2"); + OrderItemDto orderItemDto_102 = OrderItemDto.builder() + .id(102L) + .itemId(102L) + .build(); + orderItemDto_102.setItemName("아이템3"); + OrderItemDto orderItemDto_103 = OrderItemDto.builder() + .id(103L) + .itemId(103L) + .build(); + orderItemDto_103.setItemName("아이템2"); + + OrderDto orderDto_1 = OrderDto.builder() + .id(1L) + .userId(1L) + .orderItemDtoList(List.of(orderItemDto_100, orderItemDto_101)) + .orderStatus(OrderStatus.PLACED) + .orderTime(LocalDateTime.of(2022, 2, 3, 14, 0, 0)) + .build(); + orderDto_1.setUserName("닉네임"); + OrderDto orderDto_2 = OrderDto.builder() + .id(2L) + .userId(1L) + .orderItemDtoList(List.of(orderItemDto_102, orderItemDto_103)) + .orderStatus(OrderStatus.CANCELED) + .orderTime(LocalDateTime.of(2022, 2, 3, 15, 0, 0)) + .build(); + orderDto_2.setUserName("닉네임"); + + return List.of(orderDto_1, orderDto_2); + } +} \ No newline at end of file