Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
433 changes: 433 additions & 0 deletions README.md

Large diffs are not rendered by default.

Binary file added image/img_25.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image/img_26.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image/img_27.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image/img_28.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image/img_29.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added image/img_30.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ public enum ErrorCode {
// 취소 권한 없음 (타 유저 주문)
INVALID_ORDER_OWNER(HttpStatus.FORBIDDEN, "ORD003", "해당 주문을 취소할 권한이 없습니다."),

// 주문 상태 불일치
INVALID_ORDER_STATUS(HttpStatus.BAD_REQUEST, "ORD004", "주문 상태가 올바르지 않습니다."),

// 요청 매점 불일치
INVALID_INVENTORY(HttpStatus.BAD_REQUEST, "INV001", "잘못된 매점 요청입니다."),

Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/ceos23/cgv_clone/store/entity/Order.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,32 @@ public static Order createPaid(User user, Store store, String paymentId, int tot
.build();
}

public static Order createPending(User user, Store store, String paymentId, int totalPrice) {
return Order.builder()
.paymentId(paymentId)
.orderStatus(OrderStatus.PENDING)
.totalPrice(totalPrice)
.user(user)
.store(store)
.build();
}

public void completePayment() {
if (orderStatus != OrderStatus.PENDING) {
throw new CustomException(ErrorCode.INVALID_ORDER_STATUS);
}

this.orderStatus = OrderStatus.PAID;
}

public void cancelPending() {
if (orderStatus != OrderStatus.PENDING) {
throw new CustomException(ErrorCode.INVALID_ORDER_STATUS);
}

this.orderStatus = OrderStatus.CANCELED;
}

public void cancel() {
if (orderStatus != OrderStatus.PAID) {
throw new CustomException(ErrorCode.ALREADY_CANCELED_ORDER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

import static java.time.LocalDate.*;
import static java.time.format.DateTimeFormatter.*;
Expand All @@ -34,10 +36,27 @@ public class OrderServicePessimistic implements OrderService {
private final InventoryRepository inventoryRepository;
private final OrderRepository orderRepository;
private final PaymentService paymentService;
private final TransactionTemplate transactionTemplate;

@Override
@Transactional
public OrderResponse createOrder(Long userId, Long storeId, OrderRequest request) {
PendingOrder pendingOrder = Objects.requireNonNull(transactionTemplate.execute(status ->
createPendingOrder(userId, storeId, request)
));

try {
paymentService.pay(pendingOrder.paymentId(), pendingOrder.orderName(), pendingOrder.totalPrice());
} catch (Exception e) {
transactionTemplate.executeWithoutResult(status -> cancelPendingOrder(pendingOrder.orderId()));
throw e;
}

return Objects.requireNonNull(transactionTemplate.execute(status ->
completePayment(pendingOrder.orderId())
));
}

private PendingOrder createPendingOrder(Long userId, Long storeId, OrderRequest request) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
Store store = storeRepository.findById(storeId)
Expand All @@ -51,16 +70,31 @@ public OrderResponse createOrder(Long userId, Long storeId, OrderRequest request
String paymentId = generatePaymentId();
String orderName = buildOrderName(inventories);

paymentService.pay(paymentId, orderName, totalPrice);

Order order = Order.createPaid(user, store, paymentId, totalPrice);
Order order = Order.createPending(user, store, paymentId, totalPrice);

addOrderItems(order, inventories, items);
orderRepository.save(order);

return new PendingOrder(order.getId(), paymentId, orderName, totalPrice);
}

private OrderResponse completePayment(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new CustomException(ErrorCode.ORDER_NOT_FOUND));

order.completePayment();

return OrderResponse.from(order);
}

private void cancelPendingOrder(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new CustomException(ErrorCode.ORDER_NOT_FOUND));

restoreInventories(order);
order.cancelPending();
}

@Override
@Transactional
public OrderResponse cancelOrder(Long userId, Long orderId) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cancelOrder 부분도 외부 API 호출(paymentService.cancel)이 @Transactional로 묶여 있어서 외부 API 호출 시간 동안 DB 커넥션을 잡고 있을 것 같아요.
이 부분도 createOrder처럼 추후에 트랜잭션을 분리해 주시면 좋을 것 같습니다!

Expand All @@ -71,17 +105,7 @@ public OrderResponse cancelOrder(Long userId, Long orderId) {

paymentService.cancel(order.getPaymentId());

List<OrderItem> items = order.getOrderItems().stream()
.sorted(Comparator.comparing(oi -> oi.getInventory().getId()))
.toList();

for (OrderItem item : items) {
Inventory inv = inventoryRepository.findByIdWithPessimisticLock(item.getInventory().getId())
.orElseThrow(() -> new CustomException(ErrorCode.ITEM_NOT_FOUND));

inv.increase(item.getQuantity());
}

restoreInventories(order);
order.cancel();

return OrderResponse.from(order);
Expand Down Expand Up @@ -112,6 +136,19 @@ private void verifyOrderOwner(Long userId, Order order) {
}
}

private void restoreInventories(Order order) {
List<OrderItem> items = order.getOrderItems().stream()
.sorted(Comparator.comparing(oi -> oi.getInventory().getId()))
.toList();

for (OrderItem item : items) {
Inventory inv = inventoryRepository.findByIdWithPessimisticLock(item.getInventory().getId())
.orElseThrow(() -> new CustomException(ErrorCode.ITEM_NOT_FOUND));

inv.increase(item.getQuantity());
}
}

private List<Inventory> validateAndDecreaseStock(Store store, List<OrderRequest.OrderItemRequest> items) {
List<Inventory> inventories = new ArrayList<>();

Expand Down Expand Up @@ -153,4 +190,7 @@ private String buildOrderName(List<Inventory> inventories) {
return inventories.size() == 1 ? first : first + " 외 " + (inventories.size() - 1) + "건";
}

private record PendingOrder(Long orderId, String paymentId, String orderName, int totalPrice) {
}

}
Loading