diff --git a/src/main/generated/org/store/clothstar/order/dto/reponse/QOrderResponse.java b/src/main/generated/org/store/clothstar/order/dto/reponse/QOrderResponse.java new file mode 100644 index 00000000..a41b13b0 --- /dev/null +++ b/src/main/generated/org/store/clothstar/order/dto/reponse/QOrderResponse.java @@ -0,0 +1,21 @@ +package org.store.clothstar.order.dto.reponse; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.ConstructorExpression; +import javax.annotation.processing.Generated; + +/** + * org.store.clothstar.order.dto.reponse.QOrderResponse is a Querydsl Projection type for OrderResponse + */ +@Generated("com.querydsl.codegen.DefaultProjectionSerializer") +public class QOrderResponse extends ConstructorExpression { + + private static final long serialVersionUID = -1258133913L; + + public QOrderResponse(com.querydsl.core.types.Expression orderEntity, com.querydsl.core.types.Expression orderDetailEntity, com.querydsl.core.types.Expression memberEntity, com.querydsl.core.types.Expression addressEntity, com.querydsl.core.types.Expression productLineEntity) { + super(OrderResponse.class, new Class[]{org.store.clothstar.order.entity.OrderEntity.class, org.store.clothstar.orderDetail.entity.OrderDetailEntity.class, org.store.clothstar.member.entity.MemberEntity.class, org.store.clothstar.member.entity.AddressEntity.class, org.store.clothstar.productLine.entity.ProductLineEntity.class}, orderEntity, orderDetailEntity, memberEntity, addressEntity, productLineEntity); + } + +} + diff --git a/src/main/generated/org/store/clothstar/orderDetail/dto/QOrderDetailDTO.java b/src/main/generated/org/store/clothstar/orderDetail/dto/QOrderDetailDTO.java new file mode 100644 index 00000000..bc407ada --- /dev/null +++ b/src/main/generated/org/store/clothstar/orderDetail/dto/QOrderDetailDTO.java @@ -0,0 +1,21 @@ +package org.store.clothstar.orderDetail.dto; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.ConstructorExpression; +import javax.annotation.processing.Generated; + +/** + * org.store.clothstar.orderDetail.dto.QOrderDetailDTO is a Querydsl Projection type for OrderDetailDTO + */ +@Generated("com.querydsl.codegen.DefaultProjectionSerializer") +public class QOrderDetailDTO extends ConstructorExpression { + + private static final long serialVersionUID = -767891785L; + + public QOrderDetailDTO(com.querydsl.core.types.Expression orderDetailEntity) { + super(OrderDetailDTO.class, new Class[]{org.store.clothstar.orderDetail.entity.OrderDetailEntity.class}, orderDetailEntity); + } + +} + diff --git a/src/main/java/org/store/clothstar/common/config/SecurityConfiguration.java b/src/main/java/org/store/clothstar/common/config/SecurityConfiguration.java index d1b1b236..f841407f 100644 --- a/src/main/java/org/store/clothstar/common/config/SecurityConfiguration.java +++ b/src/main/java/org/store/clothstar/common/config/SecurityConfiguration.java @@ -56,7 +56,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { , "/v1/members/login", "/signup", "/v1/members/email/**", "/v1/access", "/v1/categories/**", "/v1/products/**", "/v1/productLines/**", "/v2/productLines/**", "/v1/orderdetails", "/v1/orders", "membersPagingOffset", "membersPagingSlice", - "/v1/seller/orders/**", "/v1/seller/orders", "/v1/orders/**", + "/v1/orderdetails", "/v1/orders", "/v2/orders", "/v3/orders", "/v1/orders/list", + "/v1/orders/list","/ordersPagingOffset","/ordersPagingSlice","/v2/orders/list", + "/v1/seller/orders/**", "/v1/seller/orders", "/v1/orders/**", "/v1/orderdetails/**", "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**" ).permitAll() .requestMatchers(HttpMethod.POST, "/v1/members").permitAll() diff --git a/src/main/java/org/store/clothstar/order/controller/OrderController.java b/src/main/java/org/store/clothstar/order/controller/OrderController.java index 6fe7b1f7..46aeda3d 100644 --- a/src/main/java/org/store/clothstar/order/controller/OrderController.java +++ b/src/main/java/org/store/clothstar/order/controller/OrderController.java @@ -3,20 +3,20 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.web.PageableDefault; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.store.clothstar.common.dto.MessageDTO; +import org.store.clothstar.common.dto.SaveResponseDTO; import org.store.clothstar.order.dto.reponse.OrderResponse; import org.store.clothstar.order.dto.request.OrderRequestWrapper; import org.store.clothstar.order.service.OrderApplicationService; import org.store.clothstar.order.service.OrderService; -import org.store.clothstar.order.utils.URIBuilder; - -import java.net.URI; - - @Tag(name = "Order", description = "주문(Order) 정보 관리에 대한 API 입니다.") @RestController @@ -29,25 +29,48 @@ public class OrderController { @Operation(summary = "단일 주문 조회", description = "단일 주문의 정보를 조회한다.") @GetMapping("/{orderId}") - public ResponseEntity getOrder(@Validated @PathVariable Long orderId) { + public ResponseEntity getOrder(@PathVariable Long orderId) { OrderResponse orderResponse = orderService.getOrder(orderId); return ResponseEntity.ok(orderResponse); } + @Operation(summary = "전체 주문 조회 offset 페이징", description = "전체 주문 리스트를 offset 페이징 형식으로 가져온다.") + @GetMapping("/offset") + public ResponseEntity> getAllOrderOffsetPaging( + @PageableDefault(size = 15) Pageable pageable) { + Page orderPages = orderService.getAllOrderOffsetPaging(pageable); + return ResponseEntity.ok(orderPages); + } + + @Operation(summary = "전체 주문 조회 slice 페이징", description = "전체 주문 리스트를 slice 페이징 형식으로 가져온다.") + @GetMapping("/slice") + public ResponseEntity> getAllOrderSlicePaging( + @PageableDefault(size = 15) Pageable pageable) { + Slice orderPages = orderService.getAllOrderSlicePaging(pageable); + return ResponseEntity.ok(orderPages); + } + @Operation(summary = "주문 생성", description = "단일 주문을 생성한다.") @PostMapping - public ResponseEntity saveOrder(@RequestBody @Validated OrderRequestWrapper orderRequestWrapper) { + public ResponseEntity saveOrder(@RequestBody @Validated OrderRequestWrapper orderRequestWrapper) { Long orderId = orderApplicationService.saveOrderWithTransaction(orderRequestWrapper); - URI location = URIBuilder.buildURI(orderId); - return ResponseEntity.created(location).build(); + return ResponseEntity.ok(new SaveResponseDTO( + orderId, HttpStatus.OK.value(), "주문이 정상적으로 생성되었습니다.")); } @Operation(summary = "구매 확정", description = "구매자가 구매 확정 시, 주문상태가 '구매확정'으로 변경된다.") - @PatchMapping("/{orderId}") + @PatchMapping("{orderId}") public ResponseEntity deliveredToConfirmOrder(@PathVariable Long orderId) { orderService.deliveredToConfirmOrder(orderId); return ResponseEntity.ok(new MessageDTO(HttpStatus.OK.value(), "주문이 정상적으로 구매 확정 되었습니다.")); } + + @Operation(summary = "주문 삭제", description = "주문 삭제시간을 현재시간으로 업데이트 한다.") + @DeleteMapping("{orderId}") + public ResponseEntity deleteOrder(@PathVariable Long orderId) { + orderService.updateDeleteAt(orderId); + return ResponseEntity.ok(new MessageDTO(HttpStatus.OK.value(), "주문이 정상적으로 삭제되었습니다.")); + } } diff --git a/src/main/java/org/store/clothstar/order/controller/OrderViewController.java b/src/main/java/org/store/clothstar/order/controller/OrderViewController.java new file mode 100644 index 00000000..7e18047c --- /dev/null +++ b/src/main/java/org/store/clothstar/order/controller/OrderViewController.java @@ -0,0 +1,17 @@ +package org.store.clothstar.order.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class OrderViewController { + @GetMapping("/ordersPagingOffset") + public String ordersPagingOffset() { + return "orderOffsetList"; + } + + @GetMapping("/ordersPagingSlice") + public String ordersPagingSlice() { + return "orderSliceList"; + } +} diff --git a/src/main/java/org/store/clothstar/order/dto/reponse/AddressDTO.java b/src/main/java/org/store/clothstar/order/dto/reponse/AddressDTO.java new file mode 100644 index 00000000..e7aa741b --- /dev/null +++ b/src/main/java/org/store/clothstar/order/dto/reponse/AddressDTO.java @@ -0,0 +1,29 @@ +package org.store.clothstar.order.dto.reponse; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "배송지 정보") +public class AddressDTO { + @Schema(description = "수령인 이름", example = "수빈") + private String receiverName; + + @Schema(description = "기본 주소", example = "서울시 강남구") + private String addressBasic; + + @Schema(description = "상세 주소", example = "123-456") + private String addressDetail; + + @Schema(description = "전화번호", example = "010-1234-5678") + private String telNo; + + @Schema(description = "배송 요청 사항", example = "문 앞에 놓아주세요.") + private String deliveryRequest; +} diff --git a/src/main/java/org/store/clothstar/order/dto/reponse/OrderResponse.java b/src/main/java/org/store/clothstar/order/dto/reponse/OrderResponse.java index b7304229..1109ad15 100644 --- a/src/main/java/org/store/clothstar/order/dto/reponse/OrderResponse.java +++ b/src/main/java/org/store/clothstar/order/dto/reponse/OrderResponse.java @@ -38,12 +38,8 @@ public class OrderResponse { @Schema(description = "주문 상태", example = "WAITING") private Status status; - //address - private String receiverName; - private String addressBasic; - private String addressDetail; - private String telNo; - private String deliveryRequest; + @Schema(description = "주소 정보") + private AddressDTO address; @Schema(description = "결제 수단", example = "CARD") private PaymentMethod paymentMethod; @@ -60,15 +56,6 @@ public class OrderResponse { @Builder.Default private List orderDetailList = new ArrayList<>(); - public OrderResponse(OrderEntity orderEntity, OrderDetailEntity orderDetailEntity, MemberEntity memberEntity, AddressEntity addressEntity, - ProductLineEntity productLineEntity, List orderDetailList) { - this(orderEntity, - orderDetailEntity, - memberEntity, addressEntity, productLineEntity); - this.orderDetailList = orderDetailList != null ? orderDetailList : new ArrayList<>(); - } - - @QueryProjection public OrderResponse(OrderEntity orderEntity, OrderDetailEntity orderDetailEntity, @@ -82,15 +69,17 @@ public OrderResponse(OrderEntity orderEntity, this.totalProductsPrice = orderEntity.getTotalProductsPrice(); this.paymentMethod = orderEntity.getPaymentMethod(); this.totalPaymentPrice = orderEntity.getTotalPaymentPrice(); - this.receiverName = addressEntity.getReceiverName(); - this.addressBasic = addressEntity.getAddressBasic(); - this.addressDetail = addressEntity.getAddressDetail(); - this.telNo = addressEntity.getTelNo(); - this.deliveryRequest = addressEntity.getDeliveryRequest(); + this.address = AddressDTO.builder() + .receiverName(addressEntity.getReceiverName()) + .addressBasic(addressEntity.getAddressBasic()) + .addressDetail(addressEntity.getAddressDetail()) + .telNo(addressEntity.getTelNo()) + .deliveryRequest(addressEntity.getDeliveryRequest()) + .build(); this.orderDetailList = new ArrayList<>(); } - public static OrderResponse fromOrderEntity(OrderEntity orderEntity) { + public static OrderResponse from(OrderEntity orderEntity) { return OrderResponse.builder() .orderId(orderEntity.getOrderId()) .ordererName(orderEntity.getMember().getName()) @@ -100,6 +89,13 @@ public static OrderResponse fromOrderEntity(OrderEntity orderEntity) { .totalProductsPrice(orderEntity.getTotalProductsPrice()) .paymentMethod(orderEntity.getPaymentMethod()) .totalPaymentPrice(orderEntity.getTotalPaymentPrice()) + .address(AddressDTO.builder() + .receiverName(orderEntity.getAddress().getReceiverName()) + .addressBasic(orderEntity.getAddress().getAddressBasic()) + .addressDetail(orderEntity.getAddress().getAddressDetail()) + .telNo(orderEntity.getAddress().getTelNo()) + .deliveryRequest(orderEntity.getAddress().getDeliveryRequest()) + .build()) .build(); } diff --git a/src/main/java/org/store/clothstar/order/dto/request/CreateOrderRequest.java b/src/main/java/org/store/clothstar/order/dto/request/CreateOrderRequest.java index f067393e..6b068849 100644 --- a/src/main/java/org/store/clothstar/order/dto/request/CreateOrderRequest.java +++ b/src/main/java/org/store/clothstar/order/dto/request/CreateOrderRequest.java @@ -13,8 +13,6 @@ import org.store.clothstar.order.type.Status; import org.store.clothstar.order.utils.GenerateOrderId; -import java.time.LocalDateTime; - @Getter @Builder @NoArgsConstructor @@ -40,7 +38,6 @@ public OrderEntity toOrderEntity(MemberEntity memberEntity, AddressEntity addres .orderId(GenerateOrderId.generateOrderId()) .member(memberEntity) .address(addressEntity) - .createdAt(LocalDateTime.now()) .status(Status.WAITING) .totalShippingPrice(3000) .totalProductsPrice(0) diff --git a/src/main/java/org/store/clothstar/order/entity/OrderEntity.java b/src/main/java/org/store/clothstar/order/entity/OrderEntity.java index dd0799e9..d06acb1e 100644 --- a/src/main/java/org/store/clothstar/order/entity/OrderEntity.java +++ b/src/main/java/org/store/clothstar/order/entity/OrderEntity.java @@ -5,6 +5,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.store.clothstar.common.entity.BaseEntity; import org.store.clothstar.member.entity.AddressEntity; import org.store.clothstar.member.entity.MemberEntity; import org.store.clothstar.order.type.PaymentMethod; @@ -19,17 +20,13 @@ @Getter @Builder @Entity(name = "orders") -public class OrderEntity { +public class OrderEntity extends BaseEntity { @Id private Long orderId; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true) private List orderDetails; - - @Temporal(TemporalType.TIMESTAMP) - @Column(name = "created_at") - private LocalDateTime createdAt; - + @Enumerated(EnumType.STRING) private Status status; @@ -53,21 +50,13 @@ public class OrderEntity { @OneToOne @JoinColumn(name = "address_id") private AddressEntity address; -// -// @ManyToOne -// private OrderDetailEntity orderDetail; - public void setTotalProductsPrice(int totalProductsPrice) { + public void updatePrices(int totalProductsPrice, int totalPaymentPrice) { this.totalProductsPrice = totalProductsPrice; - } - - public void setTotalPaymentPrice(int totalPaymentPrice) { this.totalPaymentPrice = totalPaymentPrice; } - public void updatePrices(int totalProductsPrice, int totalPaymentPrice) { - this.totalProductsPrice = totalProductsPrice; - this.totalPaymentPrice = totalPaymentPrice; + public void updateDeletedAt() { + this.deletedAt = LocalDateTime.now(); } - } diff --git a/src/main/java/org/store/clothstar/order/repository/order/JpaOrderRepository.java b/src/main/java/org/store/clothstar/order/repository/order/JpaOrderRepository.java index 7edc26d8..bb318c32 100644 --- a/src/main/java/org/store/clothstar/order/repository/order/JpaOrderRepository.java +++ b/src/main/java/org/store/clothstar/order/repository/order/JpaOrderRepository.java @@ -1,5 +1,8 @@ package org.store.clothstar.order.repository.order; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/org/store/clothstar/order/repository/order/OrderRepository.java b/src/main/java/org/store/clothstar/order/repository/order/OrderRepository.java index ca187a8b..22a6d082 100644 --- a/src/main/java/org/store/clothstar/order/repository/order/OrderRepository.java +++ b/src/main/java/org/store/clothstar/order/repository/order/OrderRepository.java @@ -1,8 +1,12 @@ package org.store.clothstar.order.repository.order; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.store.clothstar.order.dto.reponse.OrderResponse; import org.store.clothstar.order.entity.OrderEntity; +import java.util.List; import java.util.Optional; public interface OrderRepository { @@ -11,8 +15,11 @@ public interface OrderRepository { OrderResponse findOrderWithDetails(Long orderId); + Page findAllOffsetPaging(Pageable pageable); + + Slice findAllSlicePaging(Pageable pageable); + OrderEntity save(OrderEntity orderEntity); void deliveredToConfirmOrder(Long orderId); - } diff --git a/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderEntityRepositoryCustom.java b/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderEntityRepositoryCustom.java index 802b0665..286d9663 100644 --- a/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderEntityRepositoryCustom.java +++ b/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderEntityRepositoryCustom.java @@ -1,12 +1,20 @@ package org.store.clothstar.order.repository.orderSeller; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.store.clothstar.order.dto.reponse.OrderResponse; -import org.store.clothstar.order.entity.OrderEntity; import java.util.List; public interface OrderEntityRepositoryCustom { + //Order 관련 메서드 OrderResponse findOrderWithDetails(Long orderId); + Page findAllOffsetPaging(Pageable pageable); + + Slice findAllSlicePaging(Pageable pageable); + + //OrderSeller 관련 메서드 List findWaitingOrders(); } diff --git a/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderEntityRepositoryCustomImpl.java b/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderEntityRepositoryCustomImpl.java index 0312eb97..d6790d25 100644 --- a/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderEntityRepositoryCustomImpl.java +++ b/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderEntityRepositoryCustomImpl.java @@ -1,7 +1,9 @@ package org.store.clothstar.order.repository.orderSeller; +import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.*; import org.springframework.stereotype.Repository; import org.store.clothstar.member.entity.QAddressEntity; import org.store.clothstar.member.entity.QMemberEntity; @@ -16,6 +18,8 @@ import org.store.clothstar.productLine.entity.QProductLineEntity; import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; @Repository @RequiredArgsConstructor @@ -30,12 +34,103 @@ public class OrderEntityRepositoryCustomImpl implements OrderEntityRepositoryCus QProductEntity qProductEntity = QProductEntity.productEntity; QProductLineEntity qProductLineEntity = QProductLineEntity.productLineEntity; + @Override + public Page findAllOffsetPaging(Pageable pageable){ + JPAQuery query = jpaQueryFactory + .select(new QOrderResponse( + qOrderEntity, + qOrderDetailEntity, + qMemberEntity, + qAddressEntity, + qProductLineEntity)) + .from(qOrderEntity) + .innerJoin(qOrderEntity.member, qMemberEntity) + .innerJoin(qOrderEntity.address, qAddressEntity) + .innerJoin(qOrderEntity.orderDetails, qOrderDetailEntity) + .innerJoin(qOrderDetailEntity.product, qProductEntity) + .innerJoin(qProductEntity.productLine, qProductLineEntity) + .where(qOrderEntity.status.eq(Status.WAITING)) + .groupBy(qOrderEntity.orderId); + + // 페이징 적용 + long total = query.fetchCount(); // 전체 레코드 수 + List results = query + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + // 추가 데이터 처리 + if (results != null) { + results.forEach(result -> { + List orderDetailList = jpaQueryFactory + .select(new QOrderDetailDTO( + qOrderDetailEntity + )) + .from(qOrderDetailEntity) + .where(qOrderDetailEntity.order.orderId.eq(result.getOrderId())) + .fetch(); + + result.setterOrderDetailList(orderDetailList); // setter 메서드 이름 수정 + }); + } + + // Page 객체로 변환하여 반환 + return new PageImpl<>(results, pageable, total); + } + + + @Override + public Slice findAllSlicePaging(Pageable pageable) { + JPAQuery query = jpaQueryFactory + .select(new QOrderResponse( + qOrderEntity, + qOrderDetailEntity, + qMemberEntity, + qAddressEntity, + qProductLineEntity)) + .from(qOrderEntity) + .innerJoin(qOrderEntity.member, qMemberEntity) + .innerJoin(qOrderEntity.address, qAddressEntity) + .innerJoin(qOrderEntity.orderDetails, qOrderDetailEntity) + .innerJoin(qOrderDetailEntity.product, qProductEntity) + .innerJoin(qProductEntity.productLine, qProductLineEntity) + .where(qOrderEntity.status.eq(Status.WAITING)) + .groupBy(qOrderEntity.orderId); + + // 페이징 적용 + List results = query + .offset(pageable.getOffset()) + .limit(pageable.getPageSize() + 1) // 페이지 크기보다 1개 더 가져옴 + .fetch(); + + // 추가 데이터 처리 + if (results != null && !results.isEmpty()) { + results.forEach(result -> { + List orderDetailList = jpaQueryFactory + .select(new QOrderDetailDTO( + qOrderDetailEntity + )) + .from(qOrderDetailEntity) + .where(qOrderDetailEntity.order.orderId.eq(result.getOrderId())) + .fetch(); + + result.setterOrderDetailList(orderDetailList); // setter 메서드 이름 수정 + }); + } + + boolean hasNext = false; + if (results.size() > pageable.getPageSize()) { + results.remove(results.size() - 1); // 마지막 요소 제거 + hasNext = true; + } + + return new SliceImpl<>(results, pageable, hasNext); + } + + + @Override public List findWaitingOrders() { -// return jpaQueryFactory.select(qOrderEntity) -// .from(qOrderEntity) -// .where(qOrderEntity.status.eq(Status.WAITING)) -// .fetch(); List results = jpaQueryFactory .select(new QOrderResponse( qOrderEntity, @@ -60,7 +155,8 @@ public List findWaitingOrders() { qOrderDetailEntity )) .from(qOrderDetailEntity) - .where(qOrderDetailEntity.order.orderId.eq(result.getOrderId())) + .where(qOrderDetailEntity.order.orderId.eq(result.getOrderId()) + .and(qOrderDetailEntity.deletedAt.isNull())) .fetch(); result.setterOrderDetailList(orderDetailList); @@ -95,7 +191,8 @@ public OrderResponse findOrderWithDetails(Long orderId){ qOrderDetailEntity )) .from(qOrderDetailEntity) - .where(qOrderDetailEntity.order.orderId.eq(orderId)) + .where(qOrderDetailEntity.order.orderId.eq(orderId) + .and(qOrderDetailEntity.deletedAt.isNull())) .fetch(); result.setterOrderDetailList(orderDetailList); diff --git a/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderSellerRepository.java b/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderSellerRepository.java index ec464df0..7a4ad82f 100644 --- a/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderSellerRepository.java +++ b/src/main/java/org/store/clothstar/order/repository/orderSeller/OrderSellerRepository.java @@ -1,7 +1,6 @@ package org.store.clothstar.order.repository.orderSeller; import org.store.clothstar.order.dto.reponse.OrderResponse; -import org.store.clothstar.order.entity.OrderEntity; import java.util.List; diff --git a/src/main/java/org/store/clothstar/order/service/OrderSellerService.java b/src/main/java/org/store/clothstar/order/service/OrderSellerService.java index e314ad38..4dd16939 100644 --- a/src/main/java/org/store/clothstar/order/service/OrderSellerService.java +++ b/src/main/java/org/store/clothstar/order/service/OrderSellerService.java @@ -7,7 +7,6 @@ import org.springframework.web.server.ResponseStatusException; import org.store.clothstar.common.dto.MessageDTO; import org.store.clothstar.order.dto.reponse.OrderResponse; -import org.store.clothstar.order.entity.OrderEntity; import org.store.clothstar.order.repository.order.OrderRepository; import org.store.clothstar.order.repository.orderSeller.OrderSellerRepository; import org.store.clothstar.order.type.Status; diff --git a/src/main/java/org/store/clothstar/order/service/OrderService.java b/src/main/java/org/store/clothstar/order/service/OrderService.java index 0f39d5b4..f0749402 100644 --- a/src/main/java/org/store/clothstar/order/service/OrderService.java +++ b/src/main/java/org/store/clothstar/order/service/OrderService.java @@ -2,6 +2,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -15,8 +18,12 @@ import org.store.clothstar.order.entity.OrderEntity; import org.store.clothstar.order.repository.order.OrderRepository; import org.store.clothstar.order.type.Status; +import org.store.clothstar.orderDetail.entity.OrderDetailEntity; +import org.store.clothstar.orderDetail.repository.OrderDetailRepository; import org.store.clothstar.orderDetail.service.OrderDetailService; +import java.util.List; + @Slf4j @Service public class OrderService { @@ -24,24 +31,36 @@ public class OrderService { private final OrderRepository orderRepository; private final MemberRepository memberRepository; private final AddressRepository addressRepository; + private final OrderDetailRepository orderDetailRepository; + private final OrderDetailService orderDetailService; public OrderService( @Qualifier("jpaOrderRepository") OrderRepository orderRepository ,@Qualifier("memberJpaRepository") MemberRepository memberRepository ,@Qualifier("addressJpaRepository") AddressRepository addressRepository - , OrderDetailService orderDetailService - ) { + ,OrderDetailService orderDetailService + ,OrderDetailRepository orderDetailRepository +) { this.orderRepository = orderRepository; this.memberRepository = memberRepository; this.addressRepository = addressRepository; + this.orderDetailRepository = orderDetailRepository; + this.orderDetailService = orderDetailService; } @Transactional(readOnly = true) public OrderResponse getOrder(Long orderId) { - return orderRepository.findOrderWithDetails(orderId); } + public Page getAllOrderOffsetPaging(Pageable pageable) { + return orderRepository.findAllOffsetPaging(pageable); + } + + public Slice getAllOrderSlicePaging(Pageable pageable) { + return orderRepository.findAllSlicePaging(pageable); + } + @Transactional public Long saveOrder(CreateOrderRequest createOrderRequest) { @@ -69,4 +88,19 @@ public void deliveredToConfirmOrder(Long orderId) { orderRepository.deliveredToConfirmOrder(orderId); } -} + + @Transactional + public void updateDeleteAt(Long orderId) { + OrderEntity orderEntity = orderRepository.findById(orderId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "주문 번호를 찾을 수 없습니다.")); + + if(orderEntity.getDeletedAt() != null){ + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "이미 삭제된 주문입니다."); + } + + List orderDetailList = orderDetailRepository.findOrderDetailListByOrderId(orderId); + orderDetailList.forEach(OrderDetailEntity::updateDeletedAt); + + orderEntity.updateDeletedAt(); + } +} \ No newline at end of file diff --git a/src/main/java/org/store/clothstar/orderDetail/controller/OrderDetailController.java b/src/main/java/org/store/clothstar/orderDetail/controller/OrderDetailController.java index ed250551..68de9814 100644 --- a/src/main/java/org/store/clothstar/orderDetail/controller/OrderDetailController.java +++ b/src/main/java/org/store/clothstar/orderDetail/controller/OrderDetailController.java @@ -3,18 +3,15 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; -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 org.store.clothstar.order.utils.URIBuilder; +import org.springframework.web.bind.annotation.*; +import org.store.clothstar.common.dto.MessageDTO; +import org.store.clothstar.common.dto.SaveResponseDTO; import org.store.clothstar.orderDetail.dto.request.AddOrderDetailRequest; import org.store.clothstar.orderDetail.service.OrderDetailService; -import java.net.URI; - @Tag(name = "OrderDetail", description = "주문 내 개별 상품에 대한 옵션, 수량 등을 나타내는, 주문상세(OrderDetail) 정보 관리에 대한 API 입니다.") @RestController @RequiredArgsConstructor @@ -25,9 +22,16 @@ public class OrderDetailController { @Operation(summary = "주문상세 추가 저장", description = "개별 상품에 대한 주문상세(상품명, 가격, 개수...)를 특정 주문에 추가 저장한다.") @PostMapping - public ResponseEntity addOrderDetail(@RequestBody @Validated AddOrderDetailRequest addOrderDetailRequest) { + public ResponseEntity addOrderDetail(@RequestBody @Validated AddOrderDetailRequest addOrderDetailRequest) { Long orderDetailId = orderdetailService.addOrderDetail(addOrderDetailRequest); - URI location = URIBuilder.buildURI(orderDetailId); - return ResponseEntity.created(location).build(); + return ResponseEntity.ok(new SaveResponseDTO( + orderDetailId, HttpStatus.OK.value(), "주문상세가 정상적으로 생성되었습니다.")); + } + + @Operation(summary = "주문상세 삭제", description = "주문상세 삭제시간을 현재시간으로 업데이트 한다.") + @DeleteMapping("{orderDetailId}") + public ResponseEntity deleteOrderDetail(@PathVariable Long orderDetailId) { + orderdetailService.updateDeleteAt(orderDetailId); + return ResponseEntity.ok(new MessageDTO(HttpStatus.OK.value(), "주문상세가 정상적으로 삭제되었습니다.")); } } diff --git a/src/main/java/org/store/clothstar/orderDetail/entity/OrderDetailEntity.java b/src/main/java/org/store/clothstar/orderDetail/entity/OrderDetailEntity.java index 155e620d..11162552 100644 --- a/src/main/java/org/store/clothstar/orderDetail/entity/OrderDetailEntity.java +++ b/src/main/java/org/store/clothstar/orderDetail/entity/OrderDetailEntity.java @@ -5,16 +5,19 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.store.clothstar.common.entity.BaseEntity; import org.store.clothstar.order.entity.OrderEntity; import org.store.clothstar.product.entity.ProductEntity; import org.store.clothstar.productLine.entity.ProductLineEntity; +import java.time.LocalDateTime; + @Getter @AllArgsConstructor @NoArgsConstructor @Builder @Entity(name = "order_detail") -public class OrderDetailEntity { +public class OrderDetailEntity extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long orderDetailId; @@ -38,8 +41,8 @@ public class OrderDetailEntity { @ManyToOne @JoinColumn(name = "product_id") private ProductEntity product; -// -// public updateOrderDetails(){ -// -// } + + public void updateDeletedAt() { + this.deletedAt = LocalDateTime.now(); + } } \ No newline at end of file diff --git a/src/main/java/org/store/clothstar/orderDetail/repository/OrderDetailRepository.java b/src/main/java/org/store/clothstar/orderDetail/repository/OrderDetailRepository.java index 35dd642e..3ff066b2 100644 --- a/src/main/java/org/store/clothstar/orderDetail/repository/OrderDetailRepository.java +++ b/src/main/java/org/store/clothstar/orderDetail/repository/OrderDetailRepository.java @@ -3,8 +3,10 @@ import org.store.clothstar.orderDetail.entity.OrderDetailEntity; import java.util.List; +import java.util.Optional; public interface OrderDetailRepository { + Optional findById(Long orderDetailId); OrderDetailEntity save(OrderDetailEntity orderdetailEntity); diff --git a/src/main/java/org/store/clothstar/orderDetail/service/OrderDetailService.java b/src/main/java/org/store/clothstar/orderDetail/service/OrderDetailService.java index ab6b6990..50bb4cf3 100644 --- a/src/main/java/org/store/clothstar/orderDetail/service/OrderDetailService.java +++ b/src/main/java/org/store/clothstar/orderDetail/service/OrderDetailService.java @@ -8,6 +8,7 @@ import org.springframework.web.server.ResponseStatusException; import org.store.clothstar.order.entity.OrderEntity; import org.store.clothstar.order.repository.order.OrderRepository; +import org.store.clothstar.order.type.Status; import org.store.clothstar.orderDetail.dto.request.AddOrderDetailRequest; import org.store.clothstar.orderDetail.dto.request.CreateOrderDetailRequest; import org.store.clothstar.orderDetail.entity.OrderDetailEntity; @@ -42,7 +43,6 @@ public OrderDetailService( this.productLineJPARepository = productLineJPARepository; } - // 주문 생성시 같이 호출되는 주문 상세 생성 메서드 - 하나의 트랜잭션으로 묶임 @Transactional public void saveOrderDetailWithOrder(CreateOrderDetailRequest createOrderDetailRequest, long orderId) { @@ -70,7 +70,6 @@ public void saveOrderDetailWithOrder(CreateOrderDetailRequest createOrderDetailR orderEntity.getTotalProductsPrice() + orderEntity.getTotalShippingPrice() + orderDetailEntity.getOneKindTotalPrice(); orderEntity.updatePrices(newTotalProductsPrice, newTotalPaymentPrice); -// orderDetailRepository.updateOrderPrices(orderEntity); // 주문 수량만큼 상품 재고 차감 updateProductStock(productEntity,orderDetailEntity.getQuantity()); @@ -93,6 +92,10 @@ public Long addOrderDetail(AddOrderDetailRequest addOrderDetailRequest) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "주문 개수가 재고보다 더 많습니다."); } + if(!orderEntity.getStatus().equals(Status.WAITING)){ + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "주문이 이미 처리된 상태에서는 추가 주문이 불가능합니다."); + } + OrderDetailEntity orderDetailEntity = addOrderDetailRequest.toOrderDetailEntity(orderEntity, productLineEntity, productEntity); orderDetailRepository.save(orderDetailEntity); @@ -108,7 +111,20 @@ public Long addOrderDetail(AddOrderDetailRequest addOrderDetailRequest) { } @Transactional - void updateProductStock(ProductEntity productEntity, int quantity) { + public void updateDeleteAt(Long orderDetailId) { + OrderDetailEntity orderDetailEntity = orderDetailRepository.findById(orderDetailId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "주문상세 번호를 찾을 수 없습니다.")); + + if(orderDetailEntity.getDeletedAt() != null){ + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "이미 삭제된 주문입니다."); + } + + restoreStockByOrderDetail(orderDetailId); + orderDetailEntity.updateDeletedAt(); + } + + @Transactional + public void updateProductStock(ProductEntity productEntity, int quantity) { long updatedStock = productEntity.getStock() - quantity; productEntity.updateStock(updatedStock); } @@ -116,6 +132,13 @@ void updateProductStock(ProductEntity productEntity, int quantity) { @Transactional public void restoreStockByOrder(Long orderId) { List orderDetailList = orderDetailRepository.findOrderDetailListByOrderId(orderId); - productService.restoreProductStock(orderDetailList); + productService.restoreProductStockByOrder(orderDetailList); + } + + @Transactional + public void restoreStockByOrderDetail(Long orderDetailId) { + OrderDetailEntity orderDetailEntity = orderDetailRepository.findById(orderDetailId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "주문상세 번호를 찾을 수 없습니다.")); + productService.restoreProductStockByOrderDetail(orderDetailEntity); } } diff --git a/src/main/java/org/store/clothstar/product/repository/ProductRepositoryImpl.java b/src/main/java/org/store/clothstar/product/repository/ProductRepositoryImpl.java new file mode 100644 index 00000000..4562125f --- /dev/null +++ b/src/main/java/org/store/clothstar/product/repository/ProductRepositoryImpl.java @@ -0,0 +1,35 @@ +package org.store.clothstar.product.repository; + +import org.springframework.stereotype.Repository; +import org.store.clothstar.product.domain.Product; + +import java.util.List; +import java.util.Optional; +@Repository +public class ProductRepositoryImpl implements ProductRepository +{ + @Override + public List selectAllProductByProductLineId(Long productId) { + return List.of(); + } + + @Override + public Optional selectByProductId(Long productId) { + return Optional.empty(); + } + + @Override + public int save(Product product) { + return 0; + } + + @Override + public int updateProduct(Product product) { + return 0; + } + + @Override + public int deleteProduct(Long productId) { + return 0; + } +} diff --git a/src/main/java/org/store/clothstar/product/service/ProductService.java b/src/main/java/org/store/clothstar/product/service/ProductService.java index fb5c8deb..8b441421 100644 --- a/src/main/java/org/store/clothstar/product/service/ProductService.java +++ b/src/main/java/org/store/clothstar/product/service/ProductService.java @@ -13,6 +13,7 @@ import org.store.clothstar.product.dto.response.ProductResponse; import org.store.clothstar.product.entity.ProductEntity; import org.store.clothstar.product.repository.ProductJPARepository; +import org.store.clothstar.product.repository.ProductRepository; import org.store.clothstar.productLine.entity.ProductLineEntity; import org.store.clothstar.productLine.repository.ProductLineJPARepository; @@ -75,14 +76,17 @@ public void deleteProduct(Long productId) { } @Transactional - public void restoreProductStock( - List orderDetailList - ) { - ProductEntity productEntity; - for (OrderDetailEntity orderDetailEntity : orderDetailList) { - productEntity = productJPARepository.findById(orderDetailEntity.getProduct().getProductId()) + public void restoreProductStockByOrder(List orderDetailList) { + orderDetailList.forEach(orderDetailEntity -> { + ProductEntity productEntity = productRepository.findById(orderDetailEntity.getProduct().getProductId()) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "상품 정보를 찾을 수 없습니다.")); productEntity.restoreStock(orderDetailEntity.getQuantity()); - } + }); + } + + public void restoreProductStockByOrderDetail(OrderDetailEntity orderDetailEntity) { + ProductEntity productEntity = productRepository.findById(orderDetailEntity.getProduct().getProductId()) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "상품 정보를 찾을 수 없습니다.")); + productEntity.restoreStock(orderDetailEntity.getQuantity()); } } diff --git a/src/main/resources/sql/order_detail.sql b/src/main/resources/sql/order_detail.sql index 415e113d..802bbaf4 100644 --- a/src/main/resources/sql/order_detail.sql +++ b/src/main/resources/sql/order_detail.sql @@ -9,14 +9,16 @@ CREATE TABLE `order_detail` `quantity` int NOT NULL, `fixed_price` int NOT NULL, `onekind_total_price` int NOT NULL, - `name` VARCHAR(255) NOT NULL, - `stock` VARCHAR(255) NOT NULL, - `option_name` VARCHAR(255) NOT NULL, - `brand_name` VARCHAR(255) NOT NULL, + `created_at` timestamp NOT NULL, + `updated_at` timestamp NULL, + `deleted_at` timestamp NULL, PRIMARY KEY (`order_detail_id`) ); +ALTER TABLE order_detail MODIFY COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP; + + select * from order_detail; diff --git a/src/main/resources/sql/orders.sql b/src/main/resources/sql/orders.sql index 9c43e9bd..28ef2be3 100644 --- a/src/main/resources/sql/orders.sql +++ b/src/main/resources/sql/orders.sql @@ -5,16 +5,19 @@ CREATE TABLE orders `order_id` bigint NOT NULL, `member_id` bigint NOT NULL, `address_id` bigint NOT NULL, - `created_at` timestamp NOT NULL, `status` varchar(255) NOT NULL, `total_shipping_price` int NOT NULL, `total_products_price` int NOT NULL, `payment_method` varchar(255) NOT NULL, `total_payment_price` int NOT NULL, + `created_at` timestamp NOT NULL, + `updated_at` timestamp NULL, + `deleted_at` timestamp NULL, PRIMARY KEY (`order_id`) ); + ALTER TABLE orders DROP PRIMARY KEY; @@ -30,6 +33,9 @@ where oe1_0.order_id = 202406297932955; select oe1_0.order_id,oe1_0.address_id,oe1_0.created_at,oe1_0.member_id,oe1_0.payment_method,oe1_0.status,oe1_0.total_payment_price,oe1_0.total_products_price,oe1_0.total_shipping_price,od1_0.order_detail_id,od1_0.brand_name,od1_0.fixed_price,od1_0.name,od1_0.onekind_total_price,od1_0.option_name,od1_0.product_id,od1_0.product_line_id,od1_0.quantity,od1_0.stock,m1_0.member_id,m1_0.created_at,m1_0.deleted_at,m1_0.email,m1_0.grade,m1_0.name,m1_0.password,m1_0.point,m1_0.role,m1_0.tel_no,m1_0.total_payment_price,m1_0.updated_at,a1_0.address_id,a1_0.address_basic,a1_0.address_detail,a1_0.default_address,a1_0.delivery_request,a1_0.member_id,a1_0.receiver_name,a1_0.tel_no,a1_0.zip_no,pl1_0.product_line_id,pl1_0.category_id,pl1_0.content,pl1_0.created_at,pl1_0.deleted_at,pl1_0.modified_at,pl1_0.name,pl1_0.price,pl1_0.sale_count,pl1_0.member_id,pl1_0.status,pl1_0.total_stock from orders oe1_0 join member m1_0 on m1_0.member_id=oe1_0.member_id join address a1_0 on a1_0.address_id=oe1_0.address_id join order_detail od1_0 on oe1_0.order_id=od1_0.order_id join product p1_0 on p1_0.product_id=od1_0.product_id join product_line pl1_0 on pl1_0.product_line_id=p1_0.product_line_id where oe1_0.order_id=202406297932955 group by oe1_0.order_id; +select ae1_0.address_id,ae1_0.address_basic,ae1_0.address_detail,ae1_0.default_address,ae1_0.delivery_request,m1_0.member_id,m1_0.created_at,m1_0.deleted_at,m1_0.email,m1_0.grade,m1_0.name,m1_0.password,m1_0.point,m1_0.role,m1_0.tel_no,m1_0.total_payment_price,m1_0.updated_at,ae1_0.receiver_name,ae1_0.tel_no,ae1_0.zip_no from address ae1_0 left join member m1_0 on m1_0.member_id=ae1_0.member_id where ae1_0.address_id=8; + +select ode1_0.order_detail_id,ode1_0.brand_name,ode1_0.fixed_price,ode1_0.name,ode1_0.onekind_total_price,ode1_0.option_name,ode1_0.order_id,ode1_0.product_id,ode1_0.product_line_id,ode1_0.quantity,ode1_0.stock from order_detail ode1_0 where ode1_0.order_id=202407037765571; select * @@ -78,7 +84,7 @@ select distinct oe1_0.order_id,oe1_0.address_id,oe1_0.created_at,oe1_0.member_id drop index FK_member_TO_orders_1 on orders; DELETE -FROM orders where total_products_price=10000; +FROM orders where member_id=1; INSERT INTO orders (order_id, member_id, address_id, created_at, status, total_shipping_price, total_products_price, payment_method, total_payment_price) diff --git a/src/main/resources/templates/orderOffsetList.html b/src/main/resources/templates/orderOffsetList.html new file mode 100644 index 00000000..ce644fcb --- /dev/null +++ b/src/main/resources/templates/orderOffsetList.html @@ -0,0 +1,131 @@ + + + + Orders List + + + + +
+

Orders List

+ + + + + + + + + + + + + +
IDStatusCreated AtBrand NamesOption Names
+ +
+ + + + diff --git a/src/main/resources/templates/orderSliceList.html b/src/main/resources/templates/orderSliceList.html new file mode 100644 index 00000000..71e1b832 --- /dev/null +++ b/src/main/resources/templates/orderSliceList.html @@ -0,0 +1,86 @@ + + + + Orders List + + + + +
+

Orders List

+ + + + + + + + + + + + + +
IDStatusCreated AtBrand NamesOption Names
+
+ + + + + diff --git a/src/test/java/org/store/clothstar/order/service/OrderServiceTest.java b/src/test/java/org/store/clothstar/order/service/OrderServiceTest.java index 51a95979..af9bf271 100644 --- a/src/test/java/org/store/clothstar/order/service/OrderServiceTest.java +++ b/src/test/java/org/store/clothstar/order/service/OrderServiceTest.java @@ -6,6 +6,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Pageable; import org.springframework.web.server.ResponseStatusException; import org.store.clothstar.member.domain.Address; import org.store.clothstar.member.domain.Member; @@ -19,7 +20,11 @@ import org.store.clothstar.order.entity.OrderEntity; import org.store.clothstar.order.repository.order.OrderRepository; import org.store.clothstar.order.type.Status; +import org.store.clothstar.orderDetail.entity.OrderDetailEntity; +import org.store.clothstar.orderDetail.repository.OrderDetailRepository; +import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -36,12 +41,18 @@ class OrderServiceTest { @Mock private OrderRepository orderRepository; + @Mock + private OrderDetailRepository orderDetailRepository; + @Mock private MemberRepository memberRepository; @Mock private AddressRepository addressRepository; + @Mock + private OrderDetailEntity orderDetailEntity; + @Test @DisplayName("getOrder: 주문 조회 - 메서드 호출 & 반환값 테스트") void getOrder_test() { @@ -59,6 +70,33 @@ void getOrder_test() { assertThat(orderResponse.getOrderId()).isEqualTo(orderId); } + @Test + @DisplayName("getAllOrderOffsetPaging: Offset 페이징 - 메서드 호출 테스트") + void getAllOrderOffsetPaging_verify_test() { + //given + Pageable pageable = mock(Pageable.class); + + //when + orderService.getAllOrderOffsetPaging(pageable); + + //then + then(orderRepository).should(times(1)).findAllOffsetPaging(pageable); + } + + @Test + @DisplayName("getAllOrderSlicePaging: Slice 페이징 - 메서드 호출 테스트") + void getAllOrderSlicePaging_verify_test() { + //given + Pageable pageable = mock(Pageable.class); + + //when + orderService.getAllOrderSlicePaging(pageable); + + //then + then(orderRepository).should(times(1)).findAllSlicePaging(pageable); + } + + @Test @DisplayName("saveOrder: 주문 생성 - 메서드 호출 테스트") void saveOrder_verify_test() { @@ -129,9 +167,8 @@ void saveOrder_member_exception_test() { given(memberRepository.findById(1L)).willReturn(Optional.empty()); //when - ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> { - orderService.saveOrder(orderRequestWrapper.getCreateOrderRequest()); - }); + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderService.saveOrder(orderRequestWrapper.getCreateOrderRequest())); //then assertEquals("400 BAD_REQUEST \"회원 정보를 찾을 수 없습니다.\"", thrown.getMessage()); @@ -154,9 +191,8 @@ void saveOrder_address_exception_test() { given(addressRepository.findById(2L)).willReturn(Optional.empty()); //when - ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> { - orderService.saveOrder(orderRequestWrapper.getCreateOrderRequest()); - }); + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderService.saveOrder(orderRequestWrapper.getCreateOrderRequest())); //then assertEquals("400 BAD_REQUEST \"배송지 정보를 찾을 수 없습니다.\"", thrown.getMessage()); @@ -192,11 +228,67 @@ void deliveredToConfirmOrder_fail_exception_test() { given(orderRepository.findById(orderId)).willReturn(Optional.of(mockOrder)); //when - ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> { - orderService.deliveredToConfirmOrder(orderId); - }); + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderService.deliveredToConfirmOrder(orderId)); //then assertEquals("400 BAD_REQUEST \"주문 상태가 '배송완료'가 아니기 때문에 주문확정이 불가능합니다.\"", thrown.getMessage()); } + + @Test + @DisplayName("updateDeleteAt: 주문 삭제 - 메서드 호출 테스트") + void updateDeleteAt_verify_test() { + //given + Long orderId = 1L; + OrderEntity orderEntity = mock(OrderEntity.class); + OrderDetailEntity mockOrderDetail1 = mock(OrderDetailEntity.class); + OrderDetailEntity mockOrderDetail2 = mock(OrderDetailEntity.class); + OrderDetailEntity mockOrderDetail3 = mock(OrderDetailEntity.class); + List orderDetailList = List.of(mockOrderDetail1, mockOrderDetail2, mockOrderDetail3); + given(orderRepository.findById(1L)).willReturn(Optional.of(orderEntity)); + given(orderDetailRepository.findOrderDetailListByOrderId(orderId)).willReturn(orderDetailList); + + //when + orderService.updateDeleteAt(orderId); + + //then + verify(mockOrderDetail1, times(1)).updateDeletedAt(); + verify(mockOrderDetail2, times(1)).updateDeletedAt(); + verify(mockOrderDetail3, times(1)).updateDeletedAt(); + then(orderRepository).should(times(1)).findById(orderId); + then(orderDetailRepository).should().findOrderDetailListByOrderId(orderId); + then(orderEntity).should(times(1)).updateDeletedAt(); + } + + @Test + @DisplayName("updateDeleteAt: 주문 삭제 - orderEntity null 예외처리 테스트") + void updateDeleteAt_orderEntityNull_exception_test() { + //given + Long orderId = 1L; + given(orderRepository.findById(1L)).willReturn(Optional.empty()); + + //when + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderService.updateDeleteAt(orderId)); + + //then + assertEquals("404 NOT_FOUND \"주문 번호를 찾을 수 없습니다.\"", thrown.getMessage()); + } + + @Test + @DisplayName("updateDeleteAt: 주문 삭제 - 이미 삭제된 경우 예외처리 테스트") + void updateDeleteAt_alreadyDelete_exception_test() { + //given + Long orderId = 1L; + OrderEntity orderEntity = mock(OrderEntity.class); + given(orderRepository.findById(1L)).willReturn(Optional.of(orderEntity)); + given(orderEntity.getDeletedAt()).willReturn(LocalDateTime.now()); + + //when + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderService.updateDeleteAt(orderId)); + + //then + assertEquals("400 BAD_REQUEST \"이미 삭제된 주문입니다.\"", thrown.getMessage()); + } } \ No newline at end of file diff --git a/src/test/java/org/store/clothstar/orderDetail/service/OrderDetailServiceTest.java b/src/test/java/org/store/clothstar/orderDetail/service/OrderDetailServiceTest.java index 67cb0b73..cb156af1 100644 --- a/src/test/java/org/store/clothstar/orderDetail/service/OrderDetailServiceTest.java +++ b/src/test/java/org/store/clothstar/orderDetail/service/OrderDetailServiceTest.java @@ -9,6 +9,7 @@ import org.springframework.web.server.ResponseStatusException; import org.store.clothstar.order.entity.OrderEntity; import org.store.clothstar.order.repository.order.OrderRepository; +import org.store.clothstar.order.type.Status; import org.store.clothstar.orderDetail.dto.request.AddOrderDetailRequest; import org.store.clothstar.orderDetail.dto.request.CreateOrderDetailRequest; import org.store.clothstar.orderDetail.entity.OrderDetailEntity; @@ -19,6 +20,7 @@ import org.store.clothstar.productLine.entity.ProductLineEntity; import org.store.clothstar.productLine.repository.ProductLineJPARepository; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -47,6 +49,9 @@ class OrderDetailServiceTest { @Mock private ProductJPARepository productJPARepository; + @Mock + private OrderDetailEntity orderDetailEntity; + @DisplayName("saveOrderDetailWithOrder: 주문상세 생성 - 메서드 호출 테스트") @Test void saveOrderDetailWithOrder_verify_test() { @@ -91,9 +96,8 @@ void saveOrderDetailWithOrder_exception_test() { given(mockProduct.getStock()).willReturn(1L); //when - ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> { - orderDetailService.saveOrderDetailWithOrder(mockRequest,orderId); - }); + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderDetailService.saveOrderDetailWithOrder(mockRequest,orderId)); //then assertEquals("400 BAD_REQUEST \"주문 개수가 재고보다 더 많습니다.\"", thrown.getMessage()); @@ -115,9 +119,8 @@ void getOrderDetail_quantityZero_exception_test() { given(mockProduct.getStock()).willReturn(1L); //when - ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> { - orderDetailService.addOrderDetail(mockRequest); - }); + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderDetailService.addOrderDetail(mockRequest)); //then assertEquals("400 BAD_REQUEST \"주문 개수가 재고보다 더 많습니다.\"", thrown.getMessage()); @@ -135,6 +138,7 @@ void addOrderDetail_test() { OrderEntity mockOrder = mock(OrderEntity.class); given(mockOrderDetail.getOrderDetailId()).willReturn(1L); + given(mockOrder.getStatus()).willReturn(Status.WAITING); given(orderRepository.findById(mockRequest.getOrderId())).willReturn(Optional.of(mockOrder)); given(productLineJPARepository.findById(mockRequest.getProductLineId())).willReturn(Optional.of(mockProductLine)); given(productJPARepository.findById(mockRequest.getProductId())).willReturn(Optional.of(mockProduct)); @@ -157,6 +161,7 @@ void addOrderDetail_verify_test() { ProductEntity mockProduct = mock(ProductEntity.class); OrderEntity mockOrder = mock(OrderEntity.class); + given(mockOrder.getStatus()).willReturn(Status.WAITING); given(orderRepository.findById(mockRequest.getOrderId())).willReturn(Optional.of(mockOrder)); given(productLineJPARepository.findById(mockRequest.getProductLineId())).willReturn(Optional.of(mockProductLine)); given(productJPARepository.findById(mockRequest.getProductId())).willReturn(Optional.of(mockProduct)); @@ -170,8 +175,6 @@ void addOrderDetail_verify_test() { then(productLineJPARepository).should(times(1)).findById(mockRequest.getProductLineId()); then(productJPARepository).should(times(1)).findById(mockRequest.getProductId()); then(orderDetailRepository).should(times(1)).save(mockOrderDetail); -// then(orderRepository).should(times(1)).updateOrderPrices(mockOrder); -// then(productJPARepository).should(times(1)).updateProduct(mockProduct); } @DisplayName("addOrderDetail: 주문상세 추가 - 주문 유효성 검사 예외처리 테스트") @@ -190,14 +193,82 @@ void addOrderDetail_quantityZero_exception_test() { given(mockProduct.getStock()).willReturn(1L); //when - ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> { - orderDetailService.addOrderDetail(mockRequest); - }); + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderDetailService.addOrderDetail(mockRequest)); //then assertEquals("400 BAD_REQUEST \"주문 개수가 재고보다 더 많습니다.\"", thrown.getMessage()); } + @DisplayName("addOrderDetail: 주문상세 추가 - 주문 상태 검사 예외처리 테스트") + @Test + void addOrderDetail_noWAITING_exception_test() { + //given + AddOrderDetailRequest mockRequest = mock(AddOrderDetailRequest.class); + ProductLineEntity mockProductLine = mock(ProductLineEntity.class); + ProductEntity mockProduct = mock(ProductEntity.class); + OrderEntity mockOrder = mock(OrderEntity.class); + + given(mockOrder.getStatus()).willReturn(Status.CANCEL); + given(orderRepository.findById(mockRequest.getOrderId())).willReturn(Optional.of(mockOrder)); + given(productLineJPARepository.findById(mockRequest.getProductLineId())).willReturn(Optional.of(mockProductLine)); + given(productJPARepository.findById(mockRequest.getProductId())).willReturn(Optional.of(mockProduct)); + + //when + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderDetailService.addOrderDetail(mockRequest)); + + //then + assertEquals("400 BAD_REQUEST \"주문이 이미 처리된 상태에서는 추가 주문이 불가능합니다.\"", thrown.getMessage()); + } + + @Test + @DisplayName("updateDeleteAt: 주문 상세 삭제 - 메서드 호출 테스트") + void updateDeleteAt_verify_test() { + //given + long orderDetailId = 1L; + given(orderDetailRepository.findById(orderDetailId)).willReturn(Optional.of(orderDetailEntity)); + + //when + orderDetailService.updateDeleteAt(orderDetailId); + + //then + then(orderDetailRepository).should(times(2)).findById(orderDetailId); + then(productService).should(times(1)).restoreProductStockByOrderDetail(orderDetailEntity); + then(orderDetailEntity).should(times(1)).updateDeletedAt(); + } + + @Test + @DisplayName("updateDeleteAt: 주문 상세 삭제 - OrderDetail null 예외처리 테스트") + void updateDeleteAt_null_exception_test() { + //given + long orderDetailId = 1L; + given(orderDetailRepository.findById(orderDetailId)).willReturn(Optional.empty()); + + //when + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderDetailService.updateDeleteAt(orderDetailId)); + + //then + assertEquals("404 NOT_FOUND \"주문상세 번호를 찾을 수 없습니다.\"", thrown.getMessage()); + } + + @Test + @DisplayName("updateDeleteAt: 주문 상세 삭제 - 이미 삭제된 경우 예외처리 테스트") + void updateDeleteAt_alreadyDelete_exception_test() { + //given + long orderDetailId = 1L; + given(orderDetailRepository.findById(orderDetailId)).willReturn(Optional.of(orderDetailEntity)); + given(orderDetailEntity.getDeletedAt()).willReturn(LocalDateTime.now()); + + //when + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderDetailService.updateDeleteAt(orderDetailId)); + + //then + assertEquals("404 NOT_FOUND \"이미 삭제된 주문입니다.\"", thrown.getMessage()); + } + @Test @DisplayName("restoreStockByOrder: 주문 취소시, 상품 재고 반환 - 메서드 호출 테스트") void restoreStockByOrder_verify_test() { @@ -213,6 +284,36 @@ void restoreStockByOrder_verify_test() { orderDetailService.restoreStockByOrder(orderId); //then - then(productService).should(times(1)).restoreProductStock(orderDetailList); + then(productService).should(times(1)).restoreProductStockByOrder(orderDetailList); + } + + @Test + @DisplayName("restoreStockByOrderDetail: 주문 상세 삭제시, 상품 재고 반환 - 메서드 호출 테스트") + void restoreStockByOrderDetail_verify_test() { + //given + long orderDetailId = 1L; + OrderDetailEntity mockOrderDetail = mock(OrderDetailEntity.class); + given(orderDetailRepository.findById(orderDetailId)).willReturn(Optional.of(mockOrderDetail)); + + //when + orderDetailService.restoreStockByOrderDetail(orderDetailId); + + //then + then(productService).should(times(1)).restoreProductStockByOrderDetail(mockOrderDetail); + } + + @Test + @DisplayName("restoreStockByOrderDetail: 주문 상세 삭제시, 상품 재고 반환 - orderDetail null 예외처리 테스트") + void restoreStockByOrderDetail_null_exception_test() { + //given + long orderDetailId = 1L; + given(orderDetailRepository.findById(orderDetailId)).willReturn(Optional.empty()); + + //when + ResponseStatusException thrown = assertThrows(ResponseStatusException.class, () -> + orderDetailService.restoreStockByOrderDetail(orderDetailId)); + + //then + assertEquals("404 NOT_FOUND \"주문상세 번호를 찾을 수 없습니다.\"", thrown.getMessage()); } } \ No newline at end of file