diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/order/Order.java b/src/main/java/com/leungcheng/spring_simple_backend/domain/order/Order.java index b9daf39..675ca5f 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/order/Order.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/domain/order/Order.java @@ -1,5 +1,6 @@ package com.leungcheng.spring_simple_backend.domain.order; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; @@ -11,11 +12,15 @@ public class Order { private String buyerUserId; private PurchaseItems purchaseItems; + @Column(unique = true) + private String requestId; + private Order() {} - Order(String buyerUserId, PurchaseItems purchaseItems) { + Order(String buyerUserId, PurchaseItems purchaseItems, String requestId) { this.buyerUserId = buyerUserId; this.purchaseItems = purchaseItems; + this.requestId = requestId; } public String getId() { @@ -29,4 +34,8 @@ public String getBuyerUserId() { public PurchaseItems getPurchaseItems() { return purchaseItems; } + + public String getRequestId() { + return requestId; + } } diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/order/OrderRepository.java b/src/main/java/com/leungcheng/spring_simple_backend/domain/order/OrderRepository.java index 14765ce..6f620f7 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/order/OrderRepository.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/domain/order/OrderRepository.java @@ -2,4 +2,8 @@ import org.springframework.data.repository.CrudRepository; -public interface OrderRepository extends CrudRepository {} +import java.util.Optional; + +public interface OrderRepository extends CrudRepository { + Optional findByRequestId(String requestId); +} diff --git a/src/main/java/com/leungcheng/spring_simple_backend/domain/order/OrderService.java b/src/main/java/com/leungcheng/spring_simple_backend/domain/order/OrderService.java index 1b88e68..e768853 100644 --- a/src/main/java/com/leungcheng/spring_simple_backend/domain/order/OrderService.java +++ b/src/main/java/com/leungcheng/spring_simple_backend/domain/order/OrderService.java @@ -29,6 +29,11 @@ public CreateOrderException(String message) { @Retryable(noRetryFor = CreateOrderException.class) @Transactional(isolation = Isolation.SERIALIZABLE) public Order createOrder(String buyerUserId, PurchaseItems purchaseItems, String requestId) { + Optional order = orderRepository.findByRequestId(requestId); + if (order.isPresent()) { + return order.get(); + } + User buyer = getUser(buyerUserId).orElseThrow(() -> new CreateOrderException("Buyer does not exist")); @@ -39,7 +44,7 @@ public Order createOrder(String buyerUserId, PurchaseItems purchaseItems, String } saveNewBalance(buyer, buyer.getBalance().subtract(totalCost)); - return addNewOrder(buyerUserId, purchaseItems); + return addNewOrder(buyerUserId, purchaseItems, requestId); } private Optional getUser(String userId) { @@ -51,8 +56,8 @@ private void saveNewBalance(User buyer, BigDecimal newBalance) { userRepository.save(updatedBuyer); } - private Order addNewOrder(String buyerUserId, PurchaseItems purchaseItems) { - Order order = new Order(buyerUserId, purchaseItems); + private Order addNewOrder(String buyerUserId, PurchaseItems purchaseItems, String requestId) { + Order order = new Order(buyerUserId, purchaseItems, requestId); return orderRepository.save(order); } diff --git a/src/test/java/com/leungcheng/spring_simple_backend/domain/order/OrderServiceTest.java b/src/test/java/com/leungcheng/spring_simple_backend/domain/order/OrderServiceTest.java index dc1fca7..13ede40 100644 --- a/src/test/java/com/leungcheng/spring_simple_backend/domain/order/OrderServiceTest.java +++ b/src/test/java/com/leungcheng/spring_simple_backend/domain/order/OrderServiceTest.java @@ -225,12 +225,32 @@ void shouldEachCreatedOrderHasDifferentId() { PurchaseItems purchaseItems = new PurchaseItems(); purchaseItems.setPurchaseItem(product.getId(), 1); - Order order1 = createOrder(buyer.getId(), purchaseItems); - Order order2 = createOrder(buyer.getId(), purchaseItems); + Order order1 = createOrder(buyer.getId(), purchaseItems, "request-01"); + Order order2 = createOrder(buyer.getId(), purchaseItems, "request-02"); assertNotEquals(order1.getId(), order2.getId()); } + @Test + void shouldOneOrderBeingCreatedOnly_IfCreateOrderWithSameRequestIdTwice() { + User buyer = randomUsernameUserBuilder().balance(new BigDecimal(10)).build(); + userRepository.save(buyer); + + Product product = productBuilder().quantity(5).price(new BigDecimal(1)).build(); + productRepository.save(product); + + PurchaseItems purchaseItems = new PurchaseItems(); + purchaseItems.setPurchaseItem(product.getId(), 1); + + Order order1 = createOrder(buyer.getId(), purchaseItems, "request_id"); + Order order2 = createOrder(buyer.getId(), purchaseItems, "request_id"); + + assertOrderEquals(order2, order1); + assertEquals(4, productRepository.findById(product.getId()).orElseThrow().getQuantity()); + assertBigDecimalEquals( + new BigDecimal(9), userRepository.findById(buyer.getId()).orElseThrow().getBalance()); + } + @Test void shouldAutoRetry_WhenOneThreadMayFailJustDueToRacing() { User seller = randomUsernameUserBuilder().balance(new BigDecimal(999)).build(); @@ -245,8 +265,8 @@ void shouldAutoRetry_WhenOneThreadMayFailJustDueToRacing() { purchaseItems.setPurchaseItem(product.getId(), 1); // 2 threads try to buy the same product at the same time - Thread thread1 = new Thread(() -> createOrder(buyer.getId(), purchaseItems)); - Thread thread2 = new Thread(() -> createOrder(buyer.getId(), purchaseItems)); + Thread thread1 = new Thread(() -> createOrder(buyer.getId(), purchaseItems, "request-01")); + Thread thread2 = new Thread(() -> createOrder(buyer.getId(), purchaseItems, "request-02")); thread1.start(); thread2.start(); @@ -265,11 +285,16 @@ private Order createOrder(String buyerUserId, PurchaseItems purchaseItems) { return orderService.createOrder(buyerUserId, purchaseItems, "dummy_request_id"); } + private Order createOrder(String buyerUserId, PurchaseItems purchaseItems, String requestId) { + return orderService.createOrder(buyerUserId, purchaseItems, requestId); + } + private void assertOrderEquals(Order expected, Order actual) { assertEquals(expected.getId(), actual.getId()); assertEquals(expected.getBuyerUserId(), actual.getBuyerUserId()); assertEquals( expected.getPurchaseItems().getProductIdToQuantity(), actual.getPurchaseItems().getProductIdToQuantity()); + assertEquals(expected.getRequestId(), actual.getRequestId()); } }