diff --git a/src/main/java/com/prgrms/nabmart/domain/delivery/controller/FindDeliveryDetailResponse.java b/src/main/java/com/prgrms/nabmart/domain/delivery/controller/FindDeliveryDetailResponse.java index 77fcec698..5644c1740 100644 --- a/src/main/java/com/prgrms/nabmart/domain/delivery/controller/FindDeliveryDetailResponse.java +++ b/src/main/java/com/prgrms/nabmart/domain/delivery/controller/FindDeliveryDetailResponse.java @@ -38,9 +38,9 @@ public record OrderItemResponse(String name, int quantity, int price) { public static OrderItemResponse from(final OrderItem orderItem) { return new OrderItemResponse( - orderItem.getItem().getName(), + orderItem.getItemName(), orderItem.getQuantity(), - orderItem.getItem().getPrice()); + orderItem.getItemPrice()); } } } diff --git a/src/main/java/com/prgrms/nabmart/domain/delivery/repository/DeliveryRepository.java b/src/main/java/com/prgrms/nabmart/domain/delivery/repository/DeliveryRepository.java index 27112e0f2..d24980c90 100644 --- a/src/main/java/com/prgrms/nabmart/domain/delivery/repository/DeliveryRepository.java +++ b/src/main/java/com/prgrms/nabmart/domain/delivery/repository/DeliveryRepository.java @@ -45,7 +45,6 @@ Page findRiderDeliveries( @Query("select d from Delivery d" + " join fetch d.order o" + " join fetch o.orderItems oi" - + " join fetch oi.item i" + " where d.deliveryId = :deliveryId") Optional findByIdWithOrderAndItems(@Param("deliveryId") Long deliveryId); } diff --git a/src/main/java/com/prgrms/nabmart/domain/event/service/EventService.java b/src/main/java/com/prgrms/nabmart/domain/event/service/EventService.java index 42d1ec710..f97ff0e51 100644 --- a/src/main/java/com/prgrms/nabmart/domain/event/service/EventService.java +++ b/src/main/java/com/prgrms/nabmart/domain/event/service/EventService.java @@ -54,8 +54,8 @@ public FindEventDetailResponse findEventDetail(FindEventDetailCommand findEventD eventItem.getItem().getName(), eventItem.getItem().getPrice(), eventItem.getItem().getDiscount(), - eventItem.getItem().getReviews().size(), - eventItem.getItem().getLikeItems().size(), + eventItem.getItem().getStatistics().getReviews(), + eventItem.getItem().getStatistics().getLikes(), eventItem.getItem().getRate() ) ).collect(Collectors.toList()); diff --git a/src/main/java/com/prgrms/nabmart/domain/event/service/response/FindEventDetailResponse.java b/src/main/java/com/prgrms/nabmart/domain/event/service/response/FindEventDetailResponse.java index 7ef3113bf..4ff0910ad 100644 --- a/src/main/java/com/prgrms/nabmart/domain/event/service/response/FindEventDetailResponse.java +++ b/src/main/java/com/prgrms/nabmart/domain/event/service/response/FindEventDetailResponse.java @@ -19,7 +19,7 @@ public record EventDetailResponse(Long eventId, String eventTitle, String eventD } public record EventItemResponse(Long itemId, String name, int price, int discount, - int reviewCount, int like, double rate) { + long reviewCount, long like, double rate) { } } diff --git a/src/main/java/com/prgrms/nabmart/domain/item/Item.java b/src/main/java/com/prgrms/nabmart/domain/item/Item.java index e6cf0a100..e25b39cff 100644 --- a/src/main/java/com/prgrms/nabmart/domain/item/Item.java +++ b/src/main/java/com/prgrms/nabmart/domain/item/Item.java @@ -5,7 +5,9 @@ import com.prgrms.nabmart.domain.item.exception.InvalidItemException; import com.prgrms.nabmart.domain.order.OrderItem; import com.prgrms.nabmart.domain.review.Review; +import com.prgrms.nabmart.domain.statistics.Statistics; import com.prgrms.nabmart.global.BaseTimeEntity; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -15,6 +17,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; import java.util.ArrayList; import java.util.List; import lombok.AccessLevel; @@ -70,14 +73,8 @@ public class Item extends BaseTimeEntity { @Column(nullable = false) private boolean isDeleted = Boolean.FALSE; - @OneToMany(mappedBy = "item") - private List reviews = new ArrayList<>(); - - @OneToMany(mappedBy = "item") - private List likeItems = new ArrayList<>(); - - @OneToMany(mappedBy = "item") - private List orderItems = new ArrayList<>(); + @OneToOne(mappedBy = "item", cascade = CascadeType.ALL) + private Statistics statistics; @Builder public Item(String name, int price, String description, int quantity, int discount, @@ -95,16 +92,13 @@ public Item(String name, int price, String description, int quantity, int discou this.maxBuyQuantity = maxBuyQuantity; this.mainCategory = mainCategory; this.subCategory = subCategory; + this.statistics = new Statistics(this); } public void decreaseQuantity(final int quantity) { this.quantity -= quantity; } - public void increaseQuantity(final int quantity) { - this.quantity += quantity; - } - public void updateItem( final String name, final int price, diff --git a/src/main/java/com/prgrms/nabmart/domain/item/repository/ItemRepositoryImpl.java b/src/main/java/com/prgrms/nabmart/domain/item/repository/ItemRepositoryImpl.java index 2913b90e3..6c6608521 100644 --- a/src/main/java/com/prgrms/nabmart/domain/item/repository/ItemRepositoryImpl.java +++ b/src/main/java/com/prgrms/nabmart/domain/item/repository/ItemRepositoryImpl.java @@ -1,9 +1,8 @@ package com.prgrms.nabmart.domain.item.repository; import static com.prgrms.nabmart.domain.item.QItem.item; -import static com.prgrms.nabmart.domain.item.QLikeItem.likeItem; import static com.prgrms.nabmart.domain.order.QOrderItem.orderItem; -import static com.prgrms.nabmart.domain.review.QReview.review; +import static com.prgrms.nabmart.domain.statistics.QStatistics.statistics; import com.prgrms.nabmart.domain.category.MainCategory; import com.prgrms.nabmart.domain.category.SubCategory; @@ -40,13 +39,11 @@ public List findNewItemsOrderBy(Long lastIdx, Long lastItemId, ItemSortTyp return queryFactory .selectFrom(item) - .leftJoin(item.reviews, review) - .leftJoin(item.likeItems, likeItem) - .leftJoin(item.orderItems, orderItem) + .join(item.statistics, statistics) .where(predicate) .groupBy(item) .having( - getHavingCondition(lastIdx, lastItemId, sortType) + getCondition(lastIdx, lastItemId, sortType) ) .orderBy(orderSpecifier, item.itemId.asc()) .offset(pageable.getOffset()) @@ -61,18 +58,16 @@ public List findHotItemsOrderBy(Long lastIdx, Long lastItemId, ItemSortTyp Predicate predicate = item.rate.gt(HOT_PRODUCT_REFERENCE_RATE); Predicate orderCondition = JPAExpressions.select(orderItem.quantity.sum().coalesce(0)) .from(orderItem) - .where(orderItem.item.eq(item)) + .where(orderItem.itemId.eq(item.itemId)) .gt(HOT_PRODUCT_REFERENCE_ORDERS); return queryFactory .selectFrom(item) - .leftJoin(item.reviews, review) - .leftJoin(item.likeItems, likeItem) - .leftJoin(item.orderItems, orderItem) - .where(predicate) + .join(item.statistics, statistics) + .where(predicate, getCondition(lastIdx, lastItemId, sortType)) .groupBy(item) .having( - getHavingCondition(lastIdx, lastItemId, sortType), + getCondition(lastIdx, lastItemId, sortType), orderCondition ) .orderBy(orderSpecifier, item.itemId.asc()) @@ -88,9 +83,8 @@ public List findByMainCategoryOrderBy(MainCategory mainCategory, Long last Predicate mainCategoryCondition = item.mainCategory.eq(mainCategory); OrderSpecifier orderSpecifier = createOrderSpecifier(sortType); - return buildDataSetJPAQuery(sortType) + return queryFactory.selectFrom(item) .where(mainCategoryCondition, getCondition(lastIdx, lastItemId, sortType)) - .groupBy(item.itemId) .orderBy(orderSpecifier, item.itemId.desc()) .limit(pageable.getPageSize()) .fetch(); @@ -104,41 +98,14 @@ public List findBySubCategoryOrderBy(MainCategory mainCategory, SubCategor Predicate subCategoryCondition = item.subCategory.eq(subCategory); OrderSpecifier orderSpecifier = createOrderSpecifier(sortType); - return buildDataSetJPAQuery(sortType) + return queryFactory.selectFrom(item) .where(mainCategoryCondition, subCategoryCondition, getCondition(lastIdx, lastItemId, sortType)) - .groupBy(item.itemId) .orderBy(orderSpecifier, item.itemId.desc()) .limit(pageable.getPageSize()) .fetch(); } - private JPAQuery buildDataSetJPAQuery(ItemSortType sortType) { - JPAQuery itemJPAQuery = queryFactory.selectFrom(item); - if (sortType.isPopular()) { - return itemJPAQuery - .leftJoin(item.orderItems, orderItem); - } - return itemJPAQuery; - } - - - private Predicate getHavingCondition(Long lastIdx, Long lastItemId, ItemSortType sortType) { - return switch (sortType) { - case NEW -> item.itemId.lt(lastIdx); - case HIGHEST_AMOUNT -> item.price.lt(lastIdx) - .or(item.price.eq(lastIdx.intValue()).and(item.itemId.gt(lastItemId))); - case LOWEST_AMOUNT -> item.price.gt(lastIdx) - .or(item.price.eq(lastIdx.intValue()).and(item.itemId.gt(lastItemId))); - case DISCOUNT -> item.discount.lt(lastIdx) - .or(item.discount.eq(lastIdx.intValue()).and(item.itemId.gt(lastItemId))); - default -> JPAExpressions.select(orderItem.quantity.longValue().sum().coalesce(0L)) - .from(orderItem) - .where(orderItem.item.eq(item)) - .lt(lastIdx); - }; - } - private Predicate getCondition(Long lastIdx, Long lastItemId, ItemSortType sortType) { return switch (sortType) { case NEW -> item.itemId.lt(lastIdx); @@ -148,10 +115,7 @@ private Predicate getCondition(Long lastIdx, Long lastItemId, ItemSortType sortT .or(item.price.eq(lastIdx.intValue()).and(item.itemId.lt(lastItemId))); case DISCOUNT -> item.discount.lt(lastIdx) .or(item.discount.eq(lastIdx.intValue()).and(item.itemId.lt(lastItemId))); - default -> JPAExpressions.select(orderItem.quantity.longValue().sum().coalesce(0L)) - .from(orderItem) - .where(orderItem.item.eq(item)) - .lt(lastIdx); + default -> item.statistics.orders.lt(lastIdx); }; } @@ -162,7 +126,7 @@ private OrderSpecifier createOrderSpecifier(ItemSortType sortType) { case HIGHEST_AMOUNT -> new OrderSpecifier<>(Order.DESC, item.price); case LOWEST_AMOUNT -> new OrderSpecifier<>(Order.ASC, item.price); case DISCOUNT -> new OrderSpecifier<>(Order.DESC, item.discount); - default -> new OrderSpecifier<>(Order.DESC, item.orderItems.any().quantity.sum()); + default -> new OrderSpecifier<>(Order.DESC, item.statistics.orders); }; } } diff --git a/src/main/java/com/prgrms/nabmart/domain/item/service/ItemService.java b/src/main/java/com/prgrms/nabmart/domain/item/service/ItemService.java index 742d290ba..a5225bf21 100644 --- a/src/main/java/com/prgrms/nabmart/domain/item/service/ItemService.java +++ b/src/main/java/com/prgrms/nabmart/domain/item/service/ItemService.java @@ -93,9 +93,9 @@ public FindItemDetailResponse findItemDetail(FindItemDetailCommand findItemDetai item.getDescription(), item.getQuantity(), item.getRate(), - item.getReviews().size(), + item.getStatistics().getReviews(), item.getDiscount(), - item.getLikeItems().size(), + item.getStatistics().getLikes(), item.getMaxBuyQuantity() ); } diff --git a/src/main/java/com/prgrms/nabmart/domain/item/service/LikeItemService.java b/src/main/java/com/prgrms/nabmart/domain/item/service/LikeItemService.java index 935d63ae2..611bc716d 100644 --- a/src/main/java/com/prgrms/nabmart/domain/item/service/LikeItemService.java +++ b/src/main/java/com/prgrms/nabmart/domain/item/service/LikeItemService.java @@ -12,6 +12,7 @@ import com.prgrms.nabmart.domain.item.service.request.FindLikeItemsCommand; import com.prgrms.nabmart.domain.item.service.request.RegisterLikeItemCommand; import com.prgrms.nabmart.domain.item.service.response.FindLikeItemsResponse; +import com.prgrms.nabmart.domain.statistics.StatisticsRepository; import com.prgrms.nabmart.domain.user.User; import com.prgrms.nabmart.domain.user.exception.NotFoundUserException; import com.prgrms.nabmart.domain.user.repository.UserRepository; @@ -27,6 +28,7 @@ public class LikeItemService { private final UserRepository userRepository; private final ItemRepository itemRepository; private final LikeItemRepository likeItemRepository; + private final StatisticsRepository statisticsRepository; @Transactional public Long registerLikeItem(RegisterLikeItemCommand registerLikeItemCommand) { @@ -35,6 +37,7 @@ public Long registerLikeItem(RegisterLikeItemCommand registerLikeItemCommand) { checkDuplicateLikedItem(user, item); LikeItem likeItem = new LikeItem(user, item); likeItemRepository.save(likeItem); + statisticsRepository.increaseLikes(item.getItemId()); return likeItem.getLikeItemId(); } @@ -45,6 +48,7 @@ public void deleteLikeItem(DeleteLikeItemCommand deleteLikeItemCommand) { throw new UnauthorizedLikeItemException("권한이 없습니다."); } likeItemRepository.delete(likeItem); + statisticsRepository.decreaseLikes(likeItem.getItem().getItemId()); } private void checkDuplicateLikedItem(final User user, final Item item) { diff --git a/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindItemDetailResponse.java b/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindItemDetailResponse.java index d65cbae7e..7f1781506 100644 --- a/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindItemDetailResponse.java +++ b/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindItemDetailResponse.java @@ -1,13 +1,13 @@ package com.prgrms.nabmart.domain.item.service.response; public record FindItemDetailResponse(Long itemId, String name, int price, String description, - int quantity, double rate, int reviewCount, int discount, - int like, int maxBuyQuantity) { + int quantity, double rate, long reviewCount, int discount, + long like, int maxBuyQuantity) { public static FindItemDetailResponse of(final Long itemId, final String name, final int price, final String description, final int quantity, - final double rate, final int reviewCount, final int discount, final int like, + final double rate, final long reviewCount, final int discount, final long like, final int maxBuyQuantity) { return new FindItemDetailResponse(itemId, name, price, description, quantity, rate, reviewCount, discount, like, maxBuyQuantity); diff --git a/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindItemsResponse.java b/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindItemsResponse.java index 2db7c506a..905be569b 100644 --- a/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindItemsResponse.java +++ b/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindItemsResponse.java @@ -14,12 +14,12 @@ public static FindItemsResponse from(final List items) { } public record FindItemResponse(Long itemId, String name, int price, int discount, - int reviewCount, int like, double rate) { + long reviewCount, long like, double rate) { public static FindItemResponse from(final Item item) { return new FindItemResponse( item.getItemId(), item.getName(), item.getPrice(), item.getDiscount(), - item.getReviews().size(), item.getLikeItems().size(), item.getRate() + item.getStatistics().getReviews(), item.getStatistics().getLikes(), item.getRate() ); } } diff --git a/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindLikeItemsResponse.java b/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindLikeItemsResponse.java index 8ee0e2643..cfc1fac05 100644 --- a/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindLikeItemsResponse.java +++ b/src/main/java/com/prgrms/nabmart/domain/item/service/response/FindLikeItemsResponse.java @@ -25,8 +25,8 @@ public record FindLikeItemResponse( String name, int price, int discount, - int reviewCount, - int like, + Long reviewCount, + Long like, double rate) { public static FindLikeItemResponse from(final LikeItem likeItem) { @@ -37,8 +37,8 @@ public static FindLikeItemResponse from(final LikeItem likeItem) { item.getName(), item.getPrice(), item.getDiscount(), - item.getReviews().size(), - item.getLikeItems().size(), + item.getStatistics().getReviews(), + item.getStatistics().getLikes(), item.getRate()); } } diff --git a/src/main/java/com/prgrms/nabmart/domain/order/Order.java b/src/main/java/com/prgrms/nabmart/domain/order/Order.java index 0a46b8e18..dced03891 100644 --- a/src/main/java/com/prgrms/nabmart/domain/order/Order.java +++ b/src/main/java/com/prgrms/nabmart/domain/order/Order.java @@ -89,8 +89,8 @@ public void updateOrderStatus(OrderStatus orderStatus) { private void createOrderName(final List orderItems) { this.name = (orderItems.size() == 1) ? - orderItems.get(0).getItem().getName() : - orderItems.get(0).getItem().getName() + "외 " + (orderItems.size() - 1) + "개"; + orderItems.get(0).getItemName() : + orderItems.get(0).getItemName() + "외 " + (orderItems.size() - 1) + "개"; } private void setOrderItems(final List orderItems) { diff --git a/src/main/java/com/prgrms/nabmart/domain/order/OrderItem.java b/src/main/java/com/prgrms/nabmart/domain/order/OrderItem.java index 395308c9d..78a13e834 100644 --- a/src/main/java/com/prgrms/nabmart/domain/order/OrderItem.java +++ b/src/main/java/com/prgrms/nabmart/domain/order/OrderItem.java @@ -32,17 +32,24 @@ public class OrderItem extends BaseTimeEntity { @JoinColumn(name = "order_id") private Order order; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "item_id") - private Item item; + @Column(nullable = false) + private Long itemId; + + @Column(nullable = false) + private String itemName; + + @Column(nullable = false) + private int itemPrice; public OrderItem(Item item, int quantity) { - this.item = item; + this.itemId = item.getItemId(); + this.itemPrice = item.getPrice(); + this.itemName = item.getName(); this.quantity = quantity; } public int calculateSubtotal() { - return item.getPrice() * quantity; + return itemPrice * quantity; } public void setOrder(Order order) { diff --git a/src/main/java/com/prgrms/nabmart/domain/order/repository/OrderItemRepository.java b/src/main/java/com/prgrms/nabmart/domain/order/repository/OrderItemRepository.java index f8e4bfd7e..0a0f6e979 100644 --- a/src/main/java/com/prgrms/nabmart/domain/order/repository/OrderItemRepository.java +++ b/src/main/java/com/prgrms/nabmart/domain/order/repository/OrderItemRepository.java @@ -1,18 +1,8 @@ package com.prgrms.nabmart.domain.order.repository; -import com.prgrms.nabmart.domain.item.Item; import com.prgrms.nabmart.domain.order.OrderItem; -import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; public interface OrderItemRepository extends JpaRepository { - Optional findByItem(Item item); - - @Query("SELECT SUM(oi.quantity) " - + "FROM OrderItem oi " - + "WHERE oi.item.id = :itemId") - Long countByOrderItemId(@Param("itemId") Long itemId); } diff --git a/src/main/java/com/prgrms/nabmart/domain/order/service/OrderCancelService.java b/src/main/java/com/prgrms/nabmart/domain/order/service/OrderCancelService.java index 9c27f879e..7ae3437a5 100644 --- a/src/main/java/com/prgrms/nabmart/domain/order/service/OrderCancelService.java +++ b/src/main/java/com/prgrms/nabmart/domain/order/service/OrderCancelService.java @@ -2,9 +2,12 @@ import com.prgrms.nabmart.domain.item.repository.ItemRepository; import com.prgrms.nabmart.domain.order.Order; +import com.prgrms.nabmart.domain.order.OrderItem; import com.prgrms.nabmart.domain.order.OrderStatus; +import com.prgrms.nabmart.domain.statistics.StatisticsRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service @@ -12,15 +15,16 @@ public class OrderCancelService { private final ItemRepository itemRepository; + private final StatisticsRepository statisticsRepository; - @Transactional + @Transactional(propagation = Propagation.REQUIRES_NEW) public void cancelOrder(Order order) { order.updateOrderStatus(OrderStatus.CANCELED); order.unUseCoupon(); - order.getOrderItems().forEach( - orderItem -> itemRepository.increaseQuantity(orderItem.getItem().getItemId(), - orderItem.getQuantity()) - ); + for (OrderItem orderItem : order.getOrderItems()) { + itemRepository.increaseQuantity(orderItem.getItemId(), orderItem.getQuantity()); + statisticsRepository.decreaseOrders(orderItem.getItemId(), orderItem.getQuantity()); + } } } diff --git a/src/main/java/com/prgrms/nabmart/domain/order/service/OrderService.java b/src/main/java/com/prgrms/nabmart/domain/order/service/OrderService.java index 5a4a1ab5f..b09cf07c9 100644 --- a/src/main/java/com/prgrms/nabmart/domain/order/service/OrderService.java +++ b/src/main/java/com/prgrms/nabmart/domain/order/service/OrderService.java @@ -24,6 +24,7 @@ import com.prgrms.nabmart.domain.order.service.response.FindPayedOrdersResponse; import com.prgrms.nabmart.domain.order.service.response.UpdateOrderByCouponResponse; import com.prgrms.nabmart.domain.payment.service.request.FindPayedOrdersCommand; +import com.prgrms.nabmart.domain.statistics.StatisticsRepository; import com.prgrms.nabmart.domain.user.User; import com.prgrms.nabmart.domain.user.exception.NotFoundUserException; import com.prgrms.nabmart.domain.user.repository.UserRepository; @@ -49,6 +50,7 @@ public class OrderService { private final UserRepository userRepository; private final ItemRepository itemRepository; private final UserCouponRepository userCouponRepository; + private final StatisticsRepository statisticsRepository; private final OrderCancelService orderCancelService; @Transactional @@ -57,8 +59,7 @@ public CreateOrderResponse createOrder(final CreateOrdersCommand createOrdersCom List orderItem = createOrderItem(createOrdersCommand.createOrderRequest() .createOrderItemRequests()); Order order = new Order(findUser, orderItem); - orderRepository.save(order).getOrderId(); - + orderRepository.save(order); return CreateOrderResponse.from(order); } @@ -87,16 +88,6 @@ public void updateOrderStatus() { expiredOrders.forEach(orderCancelService::cancelOrder); } - @Transactional - public void cancelOrder(final Order order) { - order.updateOrderStatus(OrderStatus.CANCELED); - order.unUseCoupon(); - order.getOrderItems().forEach( - orderItem -> itemRepository.increaseQuantity(orderItem.getItem().getItemId(), - orderItem.getQuantity()) - ); - } - @Transactional public void deleteOrder(final Long orderId, final Long userId) { Order order = getOrderByOrderIdAndUserId(orderId, userId); @@ -125,6 +116,7 @@ private List createOrderItem(final List order Integer quantity = createOrderRequest.quantity(); validateItemQuantity(findItem, quantity); findItem.decreaseQuantity(quantity); + statisticsRepository.increaseOrders(findItem.getItemId(), quantity); // OrderItem 생성 및 초기화 OrderItem orderItem = new OrderItem(findItem, quantity); orderItems.add(orderItem); diff --git a/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrderDetailItemResponse.java b/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrderDetailItemResponse.java index 1f8bba82f..92951abdd 100644 --- a/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrderDetailItemResponse.java +++ b/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrderDetailItemResponse.java @@ -11,10 +11,10 @@ public record FindOrderDetailItemResponse( public static FindOrderDetailItemResponse from(final OrderItem orderItem) { return new FindOrderDetailItemResponse( - orderItem.getItem().getItemId(), - orderItem.getItem().getName(), + orderItem.getItemId(), + orderItem.getItemName(), orderItem.getQuantity(), - orderItem.getItem().getPrice() + orderItem.getItemPrice() ); } } diff --git a/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrderResponse.java b/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrderResponse.java index fa7ea5362..e81280859 100644 --- a/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrderResponse.java +++ b/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrderResponse.java @@ -17,7 +17,7 @@ public record FindOrderResponse( public static FindOrderResponse from(Order order) { List items = order.getOrderItems().stream() .map(FindOrdersItemResponse::from) - .collect(Collectors.toList()); + .toList(); return new FindOrderResponse( order.getOrderId(), diff --git a/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrdersItemResponse.java b/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrdersItemResponse.java index 7ca56db3c..4544d8e8e 100644 --- a/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrdersItemResponse.java +++ b/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrdersItemResponse.java @@ -11,10 +11,10 @@ public record FindOrdersItemResponse( public static FindOrdersItemResponse from(OrderItem orderItem) { return new FindOrdersItemResponse( - orderItem.getItem().getItemId(), - orderItem.getItem().getName(), + orderItem.getItemId(), + orderItem.getItemName(), orderItem.getQuantity(), - orderItem.getItem().getPrice() + orderItem.getItemPrice() ); } } diff --git a/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrdersResponse.java b/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrdersResponse.java index 399cf217f..dfc2ea0e6 100644 --- a/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrdersResponse.java +++ b/src/main/java/com/prgrms/nabmart/domain/order/service/response/FindOrdersResponse.java @@ -13,7 +13,7 @@ public static FindOrdersResponse of(List orders, Integer totalPages) { return new FindOrdersResponse( orders.stream() .map(FindOrderResponse::from) - .collect(Collectors.toList()), + .toList(), totalPages ); } diff --git a/src/main/java/com/prgrms/nabmart/domain/review/service/ReviewService.java b/src/main/java/com/prgrms/nabmart/domain/review/service/ReviewService.java index 710a0ecc9..730e2685a 100644 --- a/src/main/java/com/prgrms/nabmart/domain/review/service/ReviewService.java +++ b/src/main/java/com/prgrms/nabmart/domain/review/service/ReviewService.java @@ -11,6 +11,8 @@ import com.prgrms.nabmart.domain.review.service.request.UpdateReviewCommand; import com.prgrms.nabmart.domain.review.service.response.FindReviewsByItemResponse; import com.prgrms.nabmart.domain.review.service.response.FindReviewsByUserResponse; +import com.prgrms.nabmart.domain.statistics.Statistics; +import com.prgrms.nabmart.domain.statistics.StatisticsRepository; import com.prgrms.nabmart.domain.user.User; import com.prgrms.nabmart.domain.user.exception.NotFoundUserException; import com.prgrms.nabmart.domain.user.repository.UserRepository; @@ -26,6 +28,7 @@ public class ReviewService { private final ReviewRepository reviewRepository; private final ItemRepository itemRepository; private final UserRepository userRepository; + private final StatisticsRepository statisticsRepository; private final RedisCacheService redisCacheService; private static final String REVIEW_COUNT_CACHE_KEY = "reviewCount:Item:"; @@ -48,6 +51,7 @@ public Long registerReview( .build(); Review savedReview = reviewRepository.save(review); + statisticsRepository.increaseReviews(foundItem.getItemId()); String cacheKey = REVIEW_COUNT_CACHE_KEY + foundItem.getItemId(); @@ -66,6 +70,7 @@ public void deleteReview( .orElseThrow(() -> new NotFoundReviewException("해당 리뷰를 찾을 수 없습니다.")); reviewRepository.delete(foundReview); + statisticsRepository.decreaseReviews(foundReview.getItem().getItemId()); String cacheKey = REVIEW_COUNT_CACHE_KEY + foundReview.getItem().getItemId(); redisCacheService.minusOneToTotalNumberOfReviewsByItemId(foundReview.getItem().getItemId(), diff --git a/src/main/java/com/prgrms/nabmart/domain/statistics/Statistics.java b/src/main/java/com/prgrms/nabmart/domain/statistics/Statistics.java new file mode 100644 index 000000000..35c0d987e --- /dev/null +++ b/src/main/java/com/prgrms/nabmart/domain/statistics/Statistics.java @@ -0,0 +1,41 @@ +package com.prgrms.nabmart.domain.statistics; + +import com.prgrms.nabmart.domain.item.Item; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Statistics { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long statisticsId; + + @Column(nullable = false) + private Long likes = 0L; + + @Column(nullable = false) + private Long reviews = 0L; + + @Column(nullable = false) + private Long orders = 0L; + + @OneToOne + @JoinColumn(name = "item_id") + private Item item; + + public Statistics(Item item) { + this.item = item; + } +} diff --git a/src/main/java/com/prgrms/nabmart/domain/statistics/StatisticsRepository.java b/src/main/java/com/prgrms/nabmart/domain/statistics/StatisticsRepository.java new file mode 100644 index 000000000..e02010f82 --- /dev/null +++ b/src/main/java/com/prgrms/nabmart/domain/statistics/StatisticsRepository.java @@ -0,0 +1,34 @@ +package com.prgrms.nabmart.domain.statistics; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface StatisticsRepository extends JpaRepository { + + @Modifying(flushAutomatically = true, clearAutomatically = true) + @Query("update Statistics st set st.likes = st.likes + 1 where st.item.itemId = :itemId") + void increaseLikes(@Param("itemId") Long itemId); + + @Modifying(flushAutomatically = true, clearAutomatically = true) + @Query("update Statistics st set st.likes = st.likes - 1 where st.item.itemId = :itemId") + void decreaseLikes(@Param("itemId") Long itemId); + + @Modifying(flushAutomatically = true, clearAutomatically = true) + @Query("update Statistics st set st.orders = st.orders + :quantity where st.item.itemId = :itemId") + void increaseOrders(@Param("itemId") Long itemId, @Param("quantity") int quantity); + + @Modifying(flushAutomatically = true, clearAutomatically = true) + @Query("update Statistics st set st.orders = st.orders - :quantity where st.item.itemId = :itemId") + void decreaseOrders(@Param("itemId") Long itemId, @Param("quantity") int quantity); + + @Modifying(flushAutomatically = true, clearAutomatically = true) + @Query("update Statistics st set st.reviews = st.reviews + 1 where st.item.itemId = :itemId") + void increaseReviews(@Param("itemId") Long itemId); + + @Modifying(flushAutomatically = true, clearAutomatically = true) + @Query("update Statistics st set st.reviews = st.reviews - 1 where st.item.itemId = :itemId") + void decreaseReviews(@Param("itemId") Long itemId); + +} diff --git a/src/test/java/com/prgrms/nabmart/domain/delivery/DeliveryIntegrationTest.java b/src/test/java/com/prgrms/nabmart/domain/delivery/DeliveryIntegrationTest.java index e1ea9ec01..ee097fb6f 100644 --- a/src/test/java/com/prgrms/nabmart/domain/delivery/DeliveryIntegrationTest.java +++ b/src/test/java/com/prgrms/nabmart/domain/delivery/DeliveryIntegrationTest.java @@ -20,6 +20,7 @@ import com.prgrms.nabmart.domain.order.OrderStatus; import com.prgrms.nabmart.domain.order.repository.OrderItemRepository; import com.prgrms.nabmart.domain.order.repository.OrderRepository; +import com.prgrms.nabmart.domain.statistics.StatisticsRepository; import com.prgrms.nabmart.domain.user.User; import com.prgrms.nabmart.domain.user.repository.UserRepository; import com.prgrms.nabmart.domain.user.support.UserFixture; @@ -67,6 +68,9 @@ public class DeliveryIntegrationTest { @Autowired DeliveryRepository deliveryRepository; + @Autowired + StatisticsRepository statisticsRepository; + @Autowired DeliveryService deliveryService; @@ -101,8 +105,8 @@ static void beforeAll() { MainCategory mainCategory = CategoryFixture.mainCategory(); SubCategory subCategory = CategoryFixture.subCategory(mainCategory); Item item = ItemFixture.item(mainCategory, subCategory); - OrderItem orderItem = new OrderItem(item, 5); - Order order = new Order(user, List.of(orderItem)); + OrderItem orderItem; + Order order; @BeforeEach void setUpData() { @@ -110,6 +114,9 @@ void setUpData() { mainCategoryRepository.save(mainCategory); subCategoryRepository.save(subCategory); itemRepository.save(item); + + orderItem = new OrderItem(item, 5); + order = new Order(user, List.of(orderItem)); ReflectionTestUtils.setField(order, "status", OrderStatus.PAYED); orderRepository.save(order); } @@ -122,6 +129,7 @@ void tearDown() { EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); + em.createQuery("delete from Statistics").executeUpdate(); em.createQuery("delete from Item").executeUpdate(); // 소프트 딜리트 아이템 강제 삭제 tx.commit(); subCategoryRepository.deleteAll(); diff --git a/src/test/java/com/prgrms/nabmart/domain/item/repository/ItemRepositoryTest.java b/src/test/java/com/prgrms/nabmart/domain/item/repository/ItemRepositoryTest.java index 4dad208f2..8ef0ba033 100644 --- a/src/test/java/com/prgrms/nabmart/domain/item/repository/ItemRepositoryTest.java +++ b/src/test/java/com/prgrms/nabmart/domain/item/repository/ItemRepositoryTest.java @@ -14,6 +14,7 @@ import com.prgrms.nabmart.domain.item.support.ItemFixture; import com.prgrms.nabmart.domain.order.OrderItem; import com.prgrms.nabmart.domain.order.repository.OrderItemRepository; +import com.prgrms.nabmart.domain.statistics.StatisticsRepository; import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Collections; @@ -41,6 +42,8 @@ class ItemRepositoryTest { @Autowired OrderItemRepository orderItemRepository; @Autowired + StatisticsRepository statisticsRepository; + @Autowired EntityManager entityManager; @AfterEach @@ -165,6 +168,7 @@ public void findByOrderCount() { mainCategory, subCategory); OrderItem orderItem = new OrderItem(item, (50 - i)); orderItemRepository.save(orderItem); + statisticsRepository.increaseOrders(item.getItemId(), 50 - i); } List expectedItemIds = List.of(1L, 2L, 3L, 4L, 5L); @@ -297,6 +301,7 @@ public void findByOrderCount() { subCategory); OrderItem orderItem = new OrderItem(item, (50 - i)); orderItemRepository.save(orderItem); + statisticsRepository.increaseOrders(item.getItemId(), 50 - i); } List expected = List.of(1L, 2L, 3L, 4L, 5L); diff --git a/src/test/java/com/prgrms/nabmart/domain/item/service/LikeItemServiceTest.java b/src/test/java/com/prgrms/nabmart/domain/item/service/LikeItemServiceTest.java index d50a5c54b..cbee55d83 100644 --- a/src/test/java/com/prgrms/nabmart/domain/item/service/LikeItemServiceTest.java +++ b/src/test/java/com/prgrms/nabmart/domain/item/service/LikeItemServiceTest.java @@ -5,6 +5,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import com.prgrms.nabmart.domain.category.MainCategory; import com.prgrms.nabmart.domain.category.SubCategory; @@ -23,6 +25,7 @@ import com.prgrms.nabmart.domain.item.service.response.FindLikeItemsResponse; import com.prgrms.nabmart.domain.item.service.response.FindLikeItemsResponse.FindLikeItemResponse; import com.prgrms.nabmart.domain.item.support.ItemFixture; +import com.prgrms.nabmart.domain.statistics.StatisticsRepository; import com.prgrms.nabmart.domain.user.User; import com.prgrms.nabmart.domain.user.exception.NotFoundUserException; import com.prgrms.nabmart.domain.user.repository.UserRepository; @@ -49,6 +52,9 @@ class LikeItemServiceTest { @Mock UserRepository userRepository; + @Mock + StatisticsRepository statisticsRepository; + @Mock ItemRepository itemRepository; @@ -82,6 +88,7 @@ void success() { //then then(likeItemRepository).should().save(any()); + verify(statisticsRepository, times(1)).increaseLikes(likeItem.getItem().getItemId()); } @Test @@ -144,6 +151,7 @@ void success() { //then then(likeItemRepository).should().delete(any()); + verify(statisticsRepository, times(1)).decreaseLikes(likeItem.getItem().getItemId()); } @Test diff --git a/src/test/java/com/prgrms/nabmart/domain/item/support/ItemFixture.java b/src/test/java/com/prgrms/nabmart/domain/item/support/ItemFixture.java index 79bfff67d..0a12a1753 100644 --- a/src/test/java/com/prgrms/nabmart/domain/item/support/ItemFixture.java +++ b/src/test/java/com/prgrms/nabmart/domain/item/support/ItemFixture.java @@ -37,8 +37,8 @@ public final class ItemFixture { private static final String DESCRIPTION = "아이템설명"; private static final int QUANTITY = 10; private static final int DISCOUNT = 0; - private static final int REVIEW_COUNT = 0; - private static final int LIKE_COUNT = 0; + private static final long REVIEW_COUNT = 0; + private static final long LIKE_COUNT = 0; private static final int RATE = 0; private static final int MAX_QUANTITY = 10; private static final Long USER_ID = 1L; diff --git a/src/test/java/com/prgrms/nabmart/domain/order/service/OrderCancelServiceTest.java b/src/test/java/com/prgrms/nabmart/domain/order/service/OrderCancelServiceTest.java index 3a40bc361..2dd271773 100644 --- a/src/test/java/com/prgrms/nabmart/domain/order/service/OrderCancelServiceTest.java +++ b/src/test/java/com/prgrms/nabmart/domain/order/service/OrderCancelServiceTest.java @@ -10,6 +10,7 @@ import com.prgrms.nabmart.domain.order.Order; import com.prgrms.nabmart.domain.order.OrderItem; import com.prgrms.nabmart.domain.order.OrderStatus; +import com.prgrms.nabmart.domain.statistics.StatisticsRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -27,6 +28,9 @@ class OrderCancelServiceTest { @Mock ItemRepository itemRepository; + @Mock + StatisticsRepository statisticsRepository; + @Nested @DisplayName("cancelOrder 메서드 실행 시") class CancelOrderTest { @@ -43,8 +47,9 @@ void success() { // then assertThat(order.getStatus()).isEqualTo(OrderStatus.CANCELED); - verify(itemRepository, times(1)).increaseQuantity(orderItem.getItem().getItemId(), + verify(itemRepository, times(1)).increaseQuantity(orderItem.getItemId(), orderItem.getQuantity()); + verify(statisticsRepository, times(1)).decreaseOrders(orderItem.getItemId(), orderItem.getQuantity()); } } diff --git a/src/test/java/com/prgrms/nabmart/domain/order/service/OrderServiceTest.java b/src/test/java/com/prgrms/nabmart/domain/order/service/OrderServiceTest.java index ca5c915cc..952c6bb28 100644 --- a/src/test/java/com/prgrms/nabmart/domain/order/service/OrderServiceTest.java +++ b/src/test/java/com/prgrms/nabmart/domain/order/service/OrderServiceTest.java @@ -45,6 +45,7 @@ import com.prgrms.nabmart.domain.order.service.response.FindOrdersResponse; import com.prgrms.nabmart.domain.order.service.response.UpdateOrderByCouponResponse; import com.prgrms.nabmart.domain.payment.service.request.FindPayedOrdersCommand; +import com.prgrms.nabmart.domain.statistics.StatisticsRepository; import com.prgrms.nabmart.domain.user.User; import com.prgrms.nabmart.domain.user.repository.UserRepository; import com.prgrms.nabmart.domain.user.support.UserFixture; @@ -83,6 +84,9 @@ public class OrderServiceTest { @Mock UserCouponRepository userCouponRepository; + @Mock + StatisticsRepository statisticsRepository; + @Nested @DisplayName("findOrderByIdAndUserId 메서드 실행 시") class FindOrderByIdAndUserId { @@ -309,27 +313,6 @@ void throwExceptionWhenInvalidCoupon() { assertThat(exception).isInstanceOf(InvalidCouponException.class); } - @Nested - @DisplayName("cancelOrder 메서드 실행 시") - class CancelOrderTest { - - @Test - @DisplayName("성공") - void success() { - // given - Order order = pendingOrder(1L, user()); - OrderItem orderItem = order.getOrderItems().get(0); - - // when - orderService.cancelOrder(order); - - // then - assertThat(order.getStatus()).isEqualTo(OrderStatus.CANCELED); - verify(itemRepository, times(1)).increaseQuantity(orderItem.getItem().getItemId(), - orderItem.getQuantity()); - } - } - @Nested @DisplayName("deleteOrder 메서드 실행 시") class DeleteOrder { diff --git a/src/test/java/com/prgrms/nabmart/domain/review/service/ReviewServiceTest.java b/src/test/java/com/prgrms/nabmart/domain/review/service/ReviewServiceTest.java index 3c1a151ae..e58de2862 100644 --- a/src/test/java/com/prgrms/nabmart/domain/review/service/ReviewServiceTest.java +++ b/src/test/java/com/prgrms/nabmart/domain/review/service/ReviewServiceTest.java @@ -2,8 +2,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import com.prgrms.nabmart.domain.category.MainCategory; import com.prgrms.nabmart.domain.category.SubCategory; @@ -20,6 +23,7 @@ import com.prgrms.nabmart.domain.review.support.RegisterReviewCommandFixture; import com.prgrms.nabmart.domain.review.support.ReviewFixture; import com.prgrms.nabmart.domain.review.support.UpdateReviewCommandFixture; +import com.prgrms.nabmart.domain.statistics.StatisticsRepository; import com.prgrms.nabmart.domain.user.User; import com.prgrms.nabmart.domain.user.UserGrade; import com.prgrms.nabmart.domain.user.UserRole; @@ -47,6 +51,9 @@ class ReviewServiceTest { @Mock private ReviewRepository reviewRepository; + @Mock + private StatisticsRepository statisticsRepository; + @Mock private UserRepository userRepository; @@ -90,6 +97,7 @@ void registerReview() { // then then(reviewRepository).should().save(any()); + verify(statisticsRepository, times(1)).increaseReviews(any()); } } @@ -110,6 +118,7 @@ void deleteReview() { // then then(reviewRepository).should().delete(any()); + verify(statisticsRepository, times(1)).decreaseReviews(any()); } } diff --git a/src/test/java/com/prgrms/nabmart/domain/user/UserIntegrationTest.java b/src/test/java/com/prgrms/nabmart/domain/user/UserIntegrationTest.java index 8592d7384..7791669c5 100644 --- a/src/test/java/com/prgrms/nabmart/domain/user/UserIntegrationTest.java +++ b/src/test/java/com/prgrms/nabmart/domain/user/UserIntegrationTest.java @@ -80,11 +80,11 @@ static void beforeAll() { protected LikeItem likeItem = ItemFixture.likeItem(user, item); protected Coupon coupon = CouponFixture.coupon(); protected UserCoupon userCoupon = new UserCoupon(user, coupon); - protected OrderItem orderItem = new OrderItem(item, 5); - protected Order order = new Order(user, List.of(orderItem)); - protected Payment payment = PaymentFixture.pendingPayment(user, order); + protected OrderItem orderItem; + protected Order order; + protected Payment payment; protected Rider rider = DeliveryFixture.rider(); - protected Delivery delivery = DeliveryFixture.completedDelivery(order, rider); + protected Delivery delivery; @BeforeEach void setUp() { @@ -98,9 +98,15 @@ void setUp() { likeItemRepository.save(likeItem); couponRepository.save(coupon); userCouponRepository.save(userCoupon); + riderRepository.save(rider); + + orderItem = new OrderItem(item, 5); + order = new Order(user, List.of(orderItem)); orderRepository.save(order); + + payment = PaymentFixture.pendingPayment(user, order); paymentRepository.save(payment); - riderRepository.save(rider); + delivery = DeliveryFixture.completedDelivery(order, rider); deliveryRepository.save(delivery); }