From b381b66f912e33150c44b46b9369eb4e404a3893 Mon Sep 17 00:00:00 2001 From: frombunny Date: Thu, 14 Aug 2025 14:25:52 +0900 Subject: [PATCH 1/8] =?UTF-8?q?:sparkles:=20feat:=20Order=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/order/entity/Order.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/com/example/Centralthon/domain/order/entity/Order.java diff --git a/src/main/java/com/example/Centralthon/domain/order/entity/Order.java b/src/main/java/com/example/Centralthon/domain/order/entity/Order.java new file mode 100644 index 0000000..77d702d --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/entity/Order.java @@ -0,0 +1,29 @@ +package com.example.Centralthon.domain.order.entity; + +import com.example.Centralthon.domain.menu.entity.Menu; +import com.example.Centralthon.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Table(name = "order") +public class Order extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="order_id") + private Long id; + + @Column(nullable = false) + private String pickUpCode; + + @Column(nullable = false) + private int price; + + @Column(nullable = false) + private Boolean isPickedUp; +} From d6647834cc7f0c44a5ed113bd135dc7968d50694 Mon Sep 17 00:00:00 2001 From: frombunny Date: Thu, 14 Aug 2025 15:11:46 +0900 Subject: [PATCH 2/8] =?UTF-8?q?:sparkles:=20feat:=20OrderItem=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/order/entity/OrderItem.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/com/example/Centralthon/domain/order/entity/OrderItem.java diff --git a/src/main/java/com/example/Centralthon/domain/order/entity/OrderItem.java b/src/main/java/com/example/Centralthon/domain/order/entity/OrderItem.java new file mode 100644 index 0000000..4719783 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/entity/OrderItem.java @@ -0,0 +1,32 @@ +package com.example.Centralthon.domain.order.entity; + +import com.example.Centralthon.domain.menu.entity.Menu; +import com.example.Centralthon.domain.store.entity.Store; +import com.example.Centralthon.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Table(name = "order_items") +public class OrderItem extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="order_items_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "order_id", nullable = false) + private Order order; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "menu_id", nullable = false) + private Menu menu; + + @Column(nullable = false) + private int quantity; +} From 90e2b61d8e1dd71755c13250246fbd4e67956689 Mon Sep 17 00:00:00 2001 From: frombunny Date: Thu, 14 Aug 2025 15:21:19 +0900 Subject: [PATCH 3/8] =?UTF-8?q?:recycle:=20refactor:=20Order=EC=99=80=20Or?= =?UTF-8?q?derItem=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/Centralthon/domain/order/entity/Order.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/Centralthon/domain/order/entity/Order.java b/src/main/java/com/example/Centralthon/domain/order/entity/Order.java index 77d702d..9d7954c 100644 --- a/src/main/java/com/example/Centralthon/domain/order/entity/Order.java +++ b/src/main/java/com/example/Centralthon/domain/order/entity/Order.java @@ -11,7 +11,7 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PROTECTED) @Entity -@Table(name = "order") +@Table(name = "orders") public class Order extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -25,5 +25,5 @@ public class Order extends BaseEntity { private int price; @Column(nullable = false) - private Boolean isPickedUp; + private boolean isPickedUp; } From 93de38aaf2d5f9344b7781831d381a1f4765c360 Mon Sep 17 00:00:00 2001 From: frombunny Date: Fri, 15 Aug 2025 01:24:20 +0900 Subject: [PATCH 4/8] =?UTF-8?q?:sparkles:=20feat:=20=ED=94=BD=EC=97=85=20?= =?UTF-8?q?=EC=98=88=EC=95=BD=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/order/entity/Order.java | 15 +++- .../domain/order/entity/OrderItem.java | 8 ++ .../order/repository/OrderItemRepository.java | 7 ++ .../order/repository/OrderRepository.java | 10 +++ .../domain/order/service/OrderService.java | 4 + .../order/service/OrderServiceImpl.java | 88 +++++++++++++++++++ .../domain/order/web/dto/OrderReq.java | 17 ++++ .../domain/order/web/dto/OrderRes.java | 11 +++ 8 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/example/Centralthon/domain/order/repository/OrderItemRepository.java create mode 100644 src/main/java/com/example/Centralthon/domain/order/repository/OrderRepository.java create mode 100644 src/main/java/com/example/Centralthon/domain/order/service/OrderService.java create mode 100644 src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java create mode 100644 src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java create mode 100644 src/main/java/com/example/Centralthon/domain/order/web/dto/OrderRes.java diff --git a/src/main/java/com/example/Centralthon/domain/order/entity/Order.java b/src/main/java/com/example/Centralthon/domain/order/entity/Order.java index 9d7954c..7b12690 100644 --- a/src/main/java/com/example/Centralthon/domain/order/entity/Order.java +++ b/src/main/java/com/example/Centralthon/domain/order/entity/Order.java @@ -1,6 +1,5 @@ package com.example.Centralthon.domain.order.entity; -import com.example.Centralthon.domain.menu.entity.Menu; import com.example.Centralthon.global.entity.BaseEntity; import jakarta.persistence.*; import lombok.*; @@ -18,12 +17,20 @@ public class Order extends BaseEntity { @Column(name="order_id") private Long id; - @Column(nullable = false) + @Column(name = "pick_up_code", nullable = false, unique = true, length = 6) private String pickUpCode; @Column(nullable = false) private int price; - @Column(nullable = false) - private boolean isPickedUp; + @Column(name = "is_picked_up", nullable = false) + private boolean pickedUp; + + public static Order toEntity(String pickUpCode, int price){ + return Order.builder() + .pickUpCode(pickUpCode) + .price(price) + .pickedUp(false) + .build(); + } } diff --git a/src/main/java/com/example/Centralthon/domain/order/entity/OrderItem.java b/src/main/java/com/example/Centralthon/domain/order/entity/OrderItem.java index 4719783..17eca78 100644 --- a/src/main/java/com/example/Centralthon/domain/order/entity/OrderItem.java +++ b/src/main/java/com/example/Centralthon/domain/order/entity/OrderItem.java @@ -29,4 +29,12 @@ public class OrderItem extends BaseEntity { @Column(nullable = false) private int quantity; + + public static OrderItem toEntity(Order order, Menu menu, int quantity) { + return OrderItem.builder() + .order(order) + .menu(menu) + .quantity(quantity) + .build(); + } } diff --git a/src/main/java/com/example/Centralthon/domain/order/repository/OrderItemRepository.java b/src/main/java/com/example/Centralthon/domain/order/repository/OrderItemRepository.java new file mode 100644 index 0000000..1a553a9 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/repository/OrderItemRepository.java @@ -0,0 +1,7 @@ +package com.example.Centralthon.domain.order.repository; + +import com.example.Centralthon.domain.order.entity.OrderItem; +import org.springframework.data.repository.CrudRepository; + +public interface OrderItemRepository extends CrudRepository { +} diff --git a/src/main/java/com/example/Centralthon/domain/order/repository/OrderRepository.java b/src/main/java/com/example/Centralthon/domain/order/repository/OrderRepository.java new file mode 100644 index 0000000..f3d3bde --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/repository/OrderRepository.java @@ -0,0 +1,10 @@ +package com.example.Centralthon.domain.order.repository; + +import com.example.Centralthon.domain.order.entity.Order; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface OrderRepository extends CrudRepository { + Optional findByPickUpCode(String pickUpCode); +} diff --git a/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java b/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java new file mode 100644 index 0000000..8760188 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java @@ -0,0 +1,4 @@ +package com.example.Centralthon.domain.order.service; + +public interface OrderService { +} diff --git a/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java b/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java new file mode 100644 index 0000000..cdabe10 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java @@ -0,0 +1,88 @@ +package com.example.Centralthon.domain.order.service; + +import com.example.Centralthon.domain.menu.entity.Menu; +import com.example.Centralthon.domain.menu.exception.MenuNotFoundException; +import com.example.Centralthon.domain.menu.repository.MenuRepository; +import com.example.Centralthon.domain.order.entity.Order; +import com.example.Centralthon.domain.order.entity.OrderItem; +import com.example.Centralthon.domain.order.repository.OrderItemRepository; +import com.example.Centralthon.domain.order.repository.OrderRepository; +import com.example.Centralthon.domain.order.web.dto.OrderReq; +import com.example.Centralthon.domain.order.web.dto.OrderRes; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +@Service +@RequiredArgsConstructor +public class OrderServiceImpl implements OrderService { + private final MenuRepository menuRepository; + private final OrderRepository orderRepository; + private final OrderItemRepository orderItemRepository; + + @Transactional + public OrderRes orderMenus(List orderReqList) { + Map orderList = new HashMap<>(); + + for (OrderReq orderReq : orderReqList) { + orderList.merge(orderReq.getMenuId(), orderReq.getCount(), Integer::sum); + } + + // 메뉴 id가 없을 경우 -> MenuNotFoundException + List menuList = menuRepository.findAllById(orderList.keySet()); + if(menuList.size() != orderList.keySet().size()) { + throw new MenuNotFoundException(); + } + + // 최종 결제 금액 계산 + int totalPrice = 0; + for (Menu menu : menuList) { + // 오버플로우 방지를 위한 addExact, multiplyExact 사용 + totalPrice = Math.addExact(totalPrice, Math.multiplyExact(menu.getSalePrice(), orderList.get(menu.getId()))); + } + + // 픽업 코드 생성 + String code = generatePickUpCode(); + + Order order = Order.toEntity(code, totalPrice); + orderRepository.save(order); + + List orderItemList = new ArrayList<>(); + for(Menu menu : menuList) { + OrderItem orderItem = OrderItem.toEntity(order, menu, orderList.get(menu.getId())); + orderItemList.add(orderItem); + } + orderItemRepository.saveAll(orderItemList); + + return OrderRes.from(order); + } + + /** + * 픽업 코드(영문 두자리 + 숫자 네자리) 생성 메서드 + */ + private String generatePickUpCode() { + ThreadLocalRandom random = ThreadLocalRandom.current(); + char[] codeArray = new char[6]; + + String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + String number = "0123456789"; + + for (int i = 0; i < 2; i++) { + char alpha = alphabet.charAt(random.nextInt(alphabet.length())); + codeArray[i] = alpha; + } + + for (int i = 0; i < 4; i++) { + char num = number.charAt(random.nextInt(number.length())); + codeArray[i + 2] = num; + } + + return new String(codeArray); + } +} diff --git a/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java b/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java new file mode 100644 index 0000000..8febea2 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java @@ -0,0 +1,17 @@ +package com.example.Centralthon.domain.order.web.dto; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class OrderReq { + @NotNull(message = "메뉴 기본 키는 필수 값입니다.") + private Long menuId; + + @NotNull(message = "수량은 필수 값입니다.") + @Min(value = 1, message = "수량은 1 이상이어야 합니다.") + private Integer count; +} diff --git a/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderRes.java b/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderRes.java new file mode 100644 index 0000000..0c351a2 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderRes.java @@ -0,0 +1,11 @@ +package com.example.Centralthon.domain.order.web.dto; + +import com.example.Centralthon.domain.order.entity.Order; + +public record OrderRes( + String code +) { + public static OrderRes from(Order order) { + return new OrderRes(order.getPickUpCode()); + } +} From 722f904f08524407dbc4a9fde7b32febac285126 Mon Sep 17 00:00:00 2001 From: frombunny Date: Fri, 15 Aug 2025 02:32:26 +0900 Subject: [PATCH 5/8] =?UTF-8?q?:sparkles:=20feat:=20=ED=94=BD=EC=97=85=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EA=B0=80=20=EC=A4=91=EB=B3=B5=EB=90=A0=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=EC=9D=98=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/CodeNotCreatedException.java | 9 +++++ .../order/exception/OrderErrorCode.java | 15 +++++++ .../order/service/OrderServiceImpl.java | 40 ++++++++++++++++--- 3 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/example/Centralthon/domain/order/exception/CodeNotCreatedException.java create mode 100644 src/main/java/com/example/Centralthon/domain/order/exception/OrderErrorCode.java diff --git a/src/main/java/com/example/Centralthon/domain/order/exception/CodeNotCreatedException.java b/src/main/java/com/example/Centralthon/domain/order/exception/CodeNotCreatedException.java new file mode 100644 index 0000000..3d83bec --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/exception/CodeNotCreatedException.java @@ -0,0 +1,9 @@ +package com.example.Centralthon.domain.order.exception; + +import com.example.Centralthon.global.exception.BaseException; + +public class CodeNotCreatedException extends BaseException { + public CodeNotCreatedException() { + super(OrderErrorCode.CODE_NOT_CREATED); + } +} diff --git a/src/main/java/com/example/Centralthon/domain/order/exception/OrderErrorCode.java b/src/main/java/com/example/Centralthon/domain/order/exception/OrderErrorCode.java new file mode 100644 index 0000000..bf14a67 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/exception/OrderErrorCode.java @@ -0,0 +1,15 @@ +package com.example.Centralthon.domain.order.exception; + +import com.example.Centralthon.global.response.code.BaseResponseCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum OrderErrorCode implements BaseResponseCode { + CODE_NOT_CREATED("ORDER_500_1",500,"서버에서 유일한 픽업 코드를 생성하지 못했습니다."); + + private final String code; + private final int httpStatus; + private final String message; +} diff --git a/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java b/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java index cdabe10..90e330d 100644 --- a/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java +++ b/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java @@ -5,12 +5,17 @@ import com.example.Centralthon.domain.menu.repository.MenuRepository; import com.example.Centralthon.domain.order.entity.Order; import com.example.Centralthon.domain.order.entity.OrderItem; +import com.example.Centralthon.domain.order.exception.CodeNotCreatedException; import com.example.Centralthon.domain.order.repository.OrderItemRepository; import com.example.Centralthon.domain.order.repository.OrderRepository; import com.example.Centralthon.domain.order.web.dto.OrderReq; import com.example.Centralthon.domain.order.web.dto.OrderRes; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; @@ -22,6 +27,9 @@ @Service @RequiredArgsConstructor public class OrderServiceImpl implements OrderService { + @Lazy @Autowired + private OrderServiceImpl selfProxy; + private final MenuRepository menuRepository; private final OrderRepository orderRepository; private final OrderItemRepository orderItemRepository; @@ -47,11 +55,7 @@ public OrderRes orderMenus(List orderReqList) { totalPrice = Math.addExact(totalPrice, Math.multiplyExact(menu.getSalePrice(), orderList.get(menu.getId()))); } - // 픽업 코드 생성 - String code = generatePickUpCode(); - - Order order = Order.toEntity(code, totalPrice); - orderRepository.save(order); + Order order = createOrderWithUniqueCode(totalPrice); List orderItemList = new ArrayList<>(); for(Menu menu : menuList) { @@ -63,13 +67,37 @@ public OrderRes orderMenus(List orderReqList) { return OrderRes.from(order); } + private Order createOrderWithUniqueCode(int totalPrice) { + for(int i = 0; i < 10; i++) { + String code = generatePickUpCode(); + try{ + // 프록시를 적용해주지 않으면, this.saveOrder가 실행되어 트랙잭션이 실행되지 않음 + return selfProxy.saveOrder(code, totalPrice); + } catch (DataIntegrityViolationException e) { + // 다음 실행 진행 + } + } + throw new CodeNotCreatedException(); + } + + /** + * 같은 트랙잭션에서 실패하면 rollback-only 상태로 인해 정상 커밋이 불가능하므로, + * 새 트랜잭션을 열어 새로운 픽업 코드를 저장하도록 시도 + * -> 새 트랜잭션을 열기 위해 @Transactional(propagation = Propagation.REQUIRES_NEW) 사용 + * -> @Transactional(propagation = Propagation.REQUIRES_NEW)는 기존 트랜잭션을 중지시키고, 새 트랜잭션 시작 + */ + @Transactional(propagation = Propagation.REQUIRES_NEW) + public Order saveOrder(String code, int totalPrice){ + Order order = Order.toEntity(code, totalPrice); + return orderRepository.save(order); + } + /** * 픽업 코드(영문 두자리 + 숫자 네자리) 생성 메서드 */ private String generatePickUpCode() { ThreadLocalRandom random = ThreadLocalRandom.current(); char[] codeArray = new char[6]; - String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; String number = "0123456789"; From 2a18b2f36181d51e8eca2e5a0957d35b072aef7b Mon Sep 17 00:00:00 2001 From: frombunny Date: Fri, 15 Aug 2025 02:52:57 +0900 Subject: [PATCH 6/8] =?UTF-8?q?:sparkles:=20feat:=20Order=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/order/service/OrderService.java | 6 ++++ .../order/service/OrderServiceImpl.java | 1 + .../order/web/controller/OrderController.java | 28 +++++++++++++++++++ src/main/resources/application.properties | 2 +- 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java diff --git a/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java b/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java index 8760188..d52a0d4 100644 --- a/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java +++ b/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java @@ -1,4 +1,10 @@ package com.example.Centralthon.domain.order.service; +import com.example.Centralthon.domain.order.web.dto.OrderReq; +import com.example.Centralthon.domain.order.web.dto.OrderRes; + +import java.util.List; + public interface OrderService { + OrderRes orderMenus(List orderReqList); } diff --git a/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java b/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java index 90e330d..723c866 100644 --- a/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java +++ b/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java @@ -34,6 +34,7 @@ public class OrderServiceImpl implements OrderService { private final OrderRepository orderRepository; private final OrderItemRepository orderItemRepository; + @Override @Transactional public OrderRes orderMenus(List orderReqList) { Map orderList = new HashMap<>(); diff --git a/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java b/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java new file mode 100644 index 0000000..0cbfd0f --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java @@ -0,0 +1,28 @@ +package com.example.Centralthon.domain.order.web.controller; + +import com.example.Centralthon.domain.order.service.OrderService; +import com.example.Centralthon.domain.order.web.dto.OrderReq; +import com.example.Centralthon.domain.order.web.dto.OrderRes; +import com.example.Centralthon.global.response.SuccessResponse; +import com.example.Centralthon.global.response.code.SuccessResponseCode; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/orders") +public class OrderController { + private final OrderService orderService; + + @PostMapping + public ResponseEntity> createOrder(@RequestBody @Valid @NotEmpty List<@Valid OrderReq> orderList) { + OrderRes orderRes = orderService.orderMenus(orderList); + return ResponseEntity.status(HttpStatus.CREATED).body(SuccessResponse.of(orderRes, SuccessResponseCode.SUCCESS_CREATED)); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3289bd9..64ff75b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -9,7 +9,7 @@ spring.datasource.username=${DATABASE_USERNAME} spring.datasource.password=${DATABASE_PASSWORD} # JPA -spring.jpa.hibernate.ddl-auto=update +spring.jpa.hibernate.ddl-auto=create spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.use_sql_comments=true \ No newline at end of file From fea13e458f049a49a989a90f3fffafbee1356d00 Mon Sep 17 00:00:00 2001 From: frombunny Date: Fri, 15 Aug 2025 03:25:50 +0900 Subject: [PATCH 7/8] =?UTF-8?q?:recycle:=20refactor:=20Validation=20?= =?UTF-8?q?=EC=9C=84=EB=B0=98=20=EC=8B=9C,=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EC=9D=BC=EA=B4=80=EC=84=B1=EC=9D=84=20=EC=9C=84=ED=95=9C=20DTO?= =?UTF-8?q?=20=EA=B5=AC=EC=A1=B0=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/order/service/OrderService.java | 2 +- .../domain/order/service/OrderServiceImpl.java | 7 ++++--- .../order/web/controller/OrderController.java | 6 ++---- .../domain/order/web/dto/OrderItemListReq.java | 17 +++++++++++++++++ .../domain/order/web/dto/OrderReq.java | 12 ++++-------- src/main/resources/application.properties | 2 +- 6 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 src/main/java/com/example/Centralthon/domain/order/web/dto/OrderItemListReq.java diff --git a/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java b/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java index d52a0d4..fb43f7e 100644 --- a/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java +++ b/src/main/java/com/example/Centralthon/domain/order/service/OrderService.java @@ -6,5 +6,5 @@ import java.util.List; public interface OrderService { - OrderRes orderMenus(List orderReqList); + OrderRes orderMenus(OrderReq orderReq); } diff --git a/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java b/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java index 723c866..decaa3c 100644 --- a/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java +++ b/src/main/java/com/example/Centralthon/domain/order/service/OrderServiceImpl.java @@ -8,6 +8,7 @@ import com.example.Centralthon.domain.order.exception.CodeNotCreatedException; import com.example.Centralthon.domain.order.repository.OrderItemRepository; import com.example.Centralthon.domain.order.repository.OrderRepository; +import com.example.Centralthon.domain.order.web.dto.OrderItemListReq; import com.example.Centralthon.domain.order.web.dto.OrderReq; import com.example.Centralthon.domain.order.web.dto.OrderRes; import lombok.RequiredArgsConstructor; @@ -36,11 +37,11 @@ public class OrderServiceImpl implements OrderService { @Override @Transactional - public OrderRes orderMenus(List orderReqList) { + public OrderRes orderMenus(OrderReq orderReq) { Map orderList = new HashMap<>(); - for (OrderReq orderReq : orderReqList) { - orderList.merge(orderReq.getMenuId(), orderReq.getCount(), Integer::sum); + for (OrderItemListReq orderItemListReq : orderReq.getItems()) { + orderList.merge(orderItemListReq.getMenuId(), orderItemListReq.getCount(), Integer::sum); } // 메뉴 id가 없을 경우 -> MenuNotFoundException diff --git a/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java b/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java index 0cbfd0f..6e31431 100644 --- a/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java +++ b/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java @@ -12,8 +12,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.List; - @RestController @RequiredArgsConstructor @RequestMapping("/api/orders") @@ -21,8 +19,8 @@ public class OrderController { private final OrderService orderService; @PostMapping - public ResponseEntity> createOrder(@RequestBody @Valid @NotEmpty List<@Valid OrderReq> orderList) { - OrderRes orderRes = orderService.orderMenus(orderList); + public ResponseEntity> createOrder(@RequestBody @Valid OrderReq orderReq) { + OrderRes orderRes = orderService.orderMenus(orderReq); return ResponseEntity.status(HttpStatus.CREATED).body(SuccessResponse.of(orderRes, SuccessResponseCode.SUCCESS_CREATED)); } } diff --git a/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderItemListReq.java b/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderItemListReq.java new file mode 100644 index 0000000..97cec39 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderItemListReq.java @@ -0,0 +1,17 @@ +package com.example.Centralthon.domain.order.web.dto; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class OrderItemListReq { + @NotNull(message = "메뉴 기본 키는 필수 값입니다.") + private Long menuId; + + @NotNull(message = "수량은 필수 값입니다.") + @Min(value = 1, message = "수량은 1 이상이어야 합니다.") + private Integer count; +} diff --git a/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java b/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java index 8febea2..11fd986 100644 --- a/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java +++ b/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java @@ -1,17 +1,13 @@ package com.example.Centralthon.domain.order.web.dto; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotNull; +import jakarta.validation.Valid; import lombok.Getter; import lombok.Setter; +import java.util.List; + @Getter @Setter public class OrderReq { - @NotNull(message = "메뉴 기본 키는 필수 값입니다.") - private Long menuId; - - @NotNull(message = "수량은 필수 값입니다.") - @Min(value = 1, message = "수량은 1 이상이어야 합니다.") - private Integer count; + List<@Valid OrderItemListReq> items; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 64ff75b..3289bd9 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -9,7 +9,7 @@ spring.datasource.username=${DATABASE_USERNAME} spring.datasource.password=${DATABASE_PASSWORD} # JPA -spring.jpa.hibernate.ddl-auto=create +spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.use_sql_comments=true \ No newline at end of file From 6d2fffacf0784d2a932f1adc0589467acf195095 Mon Sep 17 00:00:00 2001 From: frombunny Date: Fri, 15 Aug 2025 03:31:14 +0900 Subject: [PATCH 8/8] =?UTF-8?q?:recycle:=20chore:=20=EC=A3=BC=EB=AC=B8=20?= =?UTF-8?q?=ED=95=AD=EB=AA=A9=20=EB=B9=88=20=EB=B0=B0=EC=97=B4=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/order/web/controller/OrderController.java | 1 - .../com/example/Centralthon/domain/order/web/dto/OrderReq.java | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java b/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java index 6e31431..da1dcfa 100644 --- a/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java +++ b/src/main/java/com/example/Centralthon/domain/order/web/controller/OrderController.java @@ -6,7 +6,6 @@ import com.example.Centralthon.global.response.SuccessResponse; import com.example.Centralthon.global.response.code.SuccessResponseCode; import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java b/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java index 11fd986..e36408a 100644 --- a/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java +++ b/src/main/java/com/example/Centralthon/domain/order/web/dto/OrderReq.java @@ -1,6 +1,7 @@ package com.example.Centralthon.domain.order.web.dto; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; import lombok.Getter; import lombok.Setter; @@ -9,5 +10,6 @@ @Getter @Setter public class OrderReq { + @NotEmpty(message = "주문 항목은 빈 배열일 수 없습니다.") List<@Valid OrderItemListReq> items; }