From 33e1838ba779a6845fab134ed97c34f9751dc4ca Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 12 Sep 2025 16:43:13 +0900 Subject: [PATCH 01/27] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../applicationuser/order/service/OrderService.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/order/service/OrderService.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/order/service/OrderService.java index 5b317dc..df3626b 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/order/service/OrderService.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/order/service/OrderService.java @@ -17,16 +17,19 @@ import com.nowait.applicationuser.order.dto.OrderMenuDto; import com.nowait.applicationuser.order.dto.OrderResponseDto; import com.nowait.domaincorerdb.menu.entity.Menu; +import com.nowait.domaincorerdb.menu.exception.MenuNotFoundException; import com.nowait.domaincorerdb.menu.repository.MenuRepository; import com.nowait.domaincorerdb.order.entity.OrderItem; import com.nowait.domaincorerdb.order.entity.OrderStatus; import com.nowait.domaincorerdb.order.entity.UserOrder; +import com.nowait.domaincorerdb.order.exception.DepositorNameTooLongException; import com.nowait.domaincorerdb.order.exception.DuplicateOrderException; import com.nowait.domaincorerdb.order.exception.OrderItemsEmptyException; import com.nowait.domaincorerdb.order.exception.OrderParameterEmptyException; import com.nowait.domaincorerdb.order.repository.OrderItemRepository; import com.nowait.domaincorerdb.order.repository.OrderRepository; import com.nowait.domaincorerdb.store.entity.Store; +import com.nowait.domaincorerdb.store.exception.StoreNotFoundException; import com.nowait.domaincorerdb.store.repository.StoreRepository; import lombok.RequiredArgsConstructor; @@ -49,7 +52,7 @@ public OrderCreateResponseDto createOrder(String publicCode, Long tableId, // 1. Store 조회 Store store = storeRepository.findByPublicCodeAndDeletedFalse(publicCode) - .orElseThrow(() -> new IllegalArgumentException("store not found")); + .orElseThrow(StoreNotFoundException::new); // 2. UserOrder 생성 및 signature 저장 UserOrder order = UserOrder.builder() @@ -77,7 +80,7 @@ public OrderCreateResponseDto createOrder(String publicCode, Long tableId, List orderItems = orderCreateRequestDto.getItems().stream() .map(item -> { Menu menu = Optional.ofNullable(menuMap.get(item.getMenuId())) - .orElseThrow(() -> new IllegalArgumentException("menu not found: " + item.getMenuId())); + .orElseThrow(MenuNotFoundException::new); return OrderItem.builder() .userOrder(savedOrder) .menu(menu) @@ -127,7 +130,7 @@ private static void parameterValidation(String publicCode, Long tableId, OrderCr throw new OrderParameterEmptyException(); } if (orderCreateRequestDto.getDepositorName().length() > 20) { - throw new IllegalArgumentException("Depositor name is too long"); + throw new DepositorNameTooLongException(); } } private String generateOrderSignature(String storeId, Long tableId, List items) { From 2abd8d30a2bb6eaa234b22d26c715a5c143c5b52 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 12 Sep 2025 16:45:13 +0900 Subject: [PATCH 02/27] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/OrderService.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/order/service/OrderService.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/order/service/OrderService.java index df3626b..4f7f5b9 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/order/service/OrderService.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/order/service/OrderService.java @@ -41,6 +41,7 @@ public class OrderService { private final StoreRepository storeRepository; private final MenuRepository menuRepository; private final OrderItemRepository orderItemRepository; + @Transactional public OrderCreateResponseDto createOrder(String publicCode, Long tableId, OrderCreateRequestDto orderCreateRequestDto, String sessionId) { @@ -89,18 +90,18 @@ public OrderCreateResponseDto createOrder(String publicCode, Long tableId, }) .collect(Collectors.toList()); - orderItemRepository.saveAll(orderItems); // 5. 응답 반환 - return OrderCreateResponseDto.fromEntity(savedOrder,orderItems); + return OrderCreateResponseDto.fromEntity(savedOrder, orderItems); } @Transactional(readOnly = true) public List getOrderItemsGroupByOrderId( String publicCode, Long tableId, String sessionId) { - List userOrders = orderRepository.findByStore_PublicCodeAndTableIdAndSessionId(publicCode, tableId, sessionId); + List userOrders = orderRepository.findByStore_PublicCodeAndTableIdAndSessionId(publicCode, tableId, + sessionId); // orderId 기준으로 바로 변환 return userOrders.stream() @@ -118,21 +119,24 @@ public List getOrderItemsGroupByOrderId( .toList(); } - - private static void parameterValidation(String publicCode, Long tableId, OrderCreateRequestDto orderCreateRequestDto) { + private static void parameterValidation(String publicCode, Long tableId, + OrderCreateRequestDto orderCreateRequestDto) { if (publicCode == null || tableId == null || orderCreateRequestDto == null) { - throw new OrderParameterEmptyException(); + throw new OrderParameterEmptyException(); } if (orderCreateRequestDto.getItems() == null || orderCreateRequestDto.getItems().isEmpty()) { - throw new OrderItemsEmptyException(); + throw new OrderItemsEmptyException(); } - if (orderCreateRequestDto.getDepositorName() == null || orderCreateRequestDto.getDepositorName().trim().isEmpty()) { - throw new OrderParameterEmptyException(); + if (orderCreateRequestDto.getDepositorName() == null || orderCreateRequestDto.getDepositorName() + .trim() + .isEmpty()) { + throw new OrderParameterEmptyException(); } if (orderCreateRequestDto.getDepositorName().length() > 20) { - throw new DepositorNameTooLongException(); + throw new DepositorNameTooLongException(); } } + private String generateOrderSignature(String storeId, Long tableId, List items) { String cartString = items.stream() .sorted((a, b) -> a.getMenuId().compareTo(b.getMenuId())) // 메뉴 ID 기준 정렬 From 24ce6591ceccb5885f94c61d201b5354725b5ec3 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 14:01:51 +0900 Subject: [PATCH 03/27] =?UTF-8?q?chore:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=98=88=EC=99=B8=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/exception/StoreKeywordEmptyException.java | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/store/exception/StoreKeywordEmptyException.java diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/store/exception/StoreKeywordEmptyException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/store/exception/StoreKeywordEmptyException.java deleted file mode 100644 index e69ad30..0000000 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/store/exception/StoreKeywordEmptyException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.nowait.domaincorerdb.store.exception; - -import com.nowait.common.exception.ErrorMessage; - -public class StoreKeywordEmptyException extends RuntimeException { - public StoreKeywordEmptyException() { - super(ErrorMessage.STORE_PARAMETER_EMPTY.getMessage()); - } -} From 4642383cc816411b95f02bc49df198fab3cd863d Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 14:02:12 +0900 Subject: [PATCH 04/27] =?UTF-8?q?refactor:=20=EC=97=90=EB=9F=AC=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=9D=BC=EB=B6=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/nowait/common/exception/ErrorMessage.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java index be45f30..f1e69dc 100644 --- a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java +++ b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java @@ -38,7 +38,6 @@ public enum ErrorMessage { NOT_FOUND_BOOKMARK("북마크를 찾을 수 없습니다", "bookmark003"), ALREADY_DELETED_BOOKMARK("이미 삭제된 북마크입니다.", "bookmark004"), - // menu MENU_PARAMETER_EMPTY("메뉴 생성 시 파라미터 정보가 없습니다.", "menu001"), MENU_NOT_FOUND("해당 메뉴를 찾을 수 없습니다.", "menu002"), @@ -69,8 +68,8 @@ public enum ErrorMessage { MENU_COUNTER_UPDATE("메뉴 카운터 업데이트 실패", "statistics002"), // image - IMAGE_FILE_EMPTY("이미지 파일을 업로드 해주세요", "image001"), - IMAGE_FILE_NOT_FOUND("이미지 파일을 업로드 해주세요", "image001"), + IMAGE_FILE_EMPTY("업로드 된 이미지 파일이 없습니다.", "image001"), + IMAGE_FILE_NOT_FOUND("DB에 해당 이미지 메타데이터가 존재하지 않습니다.", "image001"), // search SEARCH_PARAMETER_EMPTY("검색어가 비어있습니다.", "search001"); From 0249ef210535db8c21368c7b574a36680975cc25 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 14:02:28 +0900 Subject: [PATCH 05/27] =?UTF-8?q?feat:=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 282 +++++++++++++++--- 1 file changed, 242 insertions(+), 40 deletions(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java index c87fe1b..2929ba8 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java @@ -1,9 +1,7 @@ package com.nowait.applicationadmin.exception; import static com.nowait.common.exception.ErrorMessage.*; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.FORBIDDEN; -import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.*; import static org.springframework.http.HttpStatus.UNAUTHORIZED; import java.util.Map; @@ -27,22 +25,42 @@ import com.nowait.discord.service.DiscordAlarmService; import com.nowait.domaincorerdb.menu.exception.MenuCreationUnauthorizedException; import com.nowait.domaincorerdb.menu.exception.MenuDeleteUnauthorizedException; +import com.nowait.domaincorerdb.menu.exception.MenuImageEmptyException; +import com.nowait.domaincorerdb.menu.exception.MenuNotFoundException; +import com.nowait.domaincorerdb.menu.exception.MenuParamEmptyException; import com.nowait.domaincorerdb.menu.exception.MenuUpdateUnauthorizedException; import com.nowait.domaincorerdb.menu.exception.MenuViewUnauthorizedException; +import com.nowait.domaincorerdb.order.exception.DepositorNameTooLongException; import com.nowait.domaincorerdb.order.exception.DuplicateOrderException; import com.nowait.domaincorerdb.order.exception.OrderItemsEmptyException; import com.nowait.domaincorerdb.order.exception.OrderNotFoundException; import com.nowait.domaincorerdb.order.exception.OrderParameterEmptyException; import com.nowait.domaincorerdb.order.exception.OrderUpdateUnauthorizedException; import com.nowait.domaincorerdb.order.exception.OrderViewUnauthorizedException; +import com.nowait.domaincorerdb.reservation.exception.DuplicateReservationException; +import com.nowait.domaincorerdb.reservation.exception.ReservationAddUnauthorizedException; import com.nowait.domaincorerdb.reservation.exception.ReservationNotFoundException; +import com.nowait.domaincorerdb.reservation.exception.ReservationNumberIssueFailException; import com.nowait.domaincorerdb.reservation.exception.ReservationUpdateUnauthorizedException; import com.nowait.domaincorerdb.reservation.exception.ReservationViewUnauthorizedException; +import com.nowait.domaincorerdb.reservation.exception.UserWaitingLimitExceededException; import com.nowait.domaincorerdb.store.exception.StoreDeleteUnauthorizedException; +import com.nowait.domaincorerdb.store.exception.StoreImageEmptyException; +import com.nowait.domaincorerdb.store.exception.StoreImageNotFoundException; +import com.nowait.domaincorerdb.store.exception.StoreNotFoundException; +import com.nowait.domaincorerdb.store.exception.StoreParamEmptyException; import com.nowait.domaincorerdb.store.exception.StoreUpdateUnauthorizedException; import com.nowait.domaincorerdb.store.exception.StoreViewUnauthorizedException; +import com.nowait.domaincorerdb.store.exception.StoreWaitingDisabledException; +import com.nowait.domaincorerdb.storepayment.exception.StorePaymentAlreadyExistsException; +import com.nowait.domaincorerdb.storepayment.exception.StorePaymentCreationUnauthorizedException; +import com.nowait.domaincorerdb.storepayment.exception.StorePaymentNotFoundException; +import com.nowait.domaincorerdb.storepayment.exception.StorePaymentParamEmptyException; +import com.nowait.domaincorerdb.storepayment.exception.StorePaymentUpdateUnauthorizedException; +import com.nowait.domaincorerdb.storepayment.exception.StorePaymentViewUnauthorizedException; import com.nowait.domaincorerdb.token.exception.BusinessException; import com.nowait.domaincorerdb.user.exception.UserNotFoundException; +import com.nowait.domaincoreredis.rank.exception.MenuCounterUpdateException; import io.swagger.v3.oas.annotations.Hidden; import lombok.RequiredArgsConstructor; @@ -134,20 +152,17 @@ public ErrorResponse userNotFoundException(UserNotFoundException e, WebRequest r return new ErrorResponse(e.getMessage(), NOTFOUND_USER.getCode()); } - @ResponseStatus(BAD_REQUEST) - @ExceptionHandler(OrderParameterEmptyException.class) - public ErrorResponse orderParameterEmptyException(OrderParameterEmptyException e, WebRequest request) { - alarm(e, request); - log.error("orderParameterEmptyException", e); - return new ErrorResponse(e.getMessage(), ORDER_PARAMETER_EMPTY.getCode()); - } + + /** + * 주문 관련 예외 처리 + */ @ResponseStatus(BAD_REQUEST) - @ExceptionHandler(OrderItemsEmptyException.class) - public ErrorResponse orderItemsEmptyException(OrderItemsEmptyException e, WebRequest request) { + @ExceptionHandler(DepositorNameTooLongException.class) + public ErrorResponse depositorNameTooLongException(DepositorNameTooLongException e, WebRequest request) { alarm(e, request); - log.error("orderItemsEmptyException", e); - return new ErrorResponse(e.getMessage(), ORDER_ITEMS_EMPTY.getCode()); + log.error("depositorNameTooLongException", e); + return new ErrorResponse(e.getMessage(), DEPOSITOR_NAME_TOO_LONG.getCode()); } @ResponseStatus(BAD_REQUEST) @@ -158,12 +173,12 @@ public ErrorResponse duplicateOrderException(DuplicateOrderException e, WebReque return new ErrorResponse(e.getMessage(), ErrorMessage.DUPLICATE_ORDER.getCode()); } - @ResponseStatus(FORBIDDEN) - @ExceptionHandler(OrderViewUnauthorizedException.class) - public ErrorResponse orderViewUnauthorizedException(OrderViewUnauthorizedException e, WebRequest request) { + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(OrderItemsEmptyException.class) + public ErrorResponse orderItemsEmptyException(OrderItemsEmptyException e, WebRequest request) { alarm(e, request); - log.error("orderViewUnauthorizedException", e); - return new ErrorResponse(e.getMessage(), ORDER_VIEW_UNAUTHORIZED.getCode()); + log.error("orderItemsEmptyException", e); + return new ErrorResponse(e.getMessage(), ORDER_ITEMS_EMPTY.getCode()); } @ResponseStatus(NOT_FOUND) @@ -174,6 +189,14 @@ public ErrorResponse orderNotFoundException(OrderNotFoundException e, WebRequest return new ErrorResponse(e.getMessage(), ORDER_NOT_FOUND.getCode()); } + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(OrderParameterEmptyException.class) + public ErrorResponse orderParameterEmptyException(OrderParameterEmptyException e, WebRequest request) { + alarm(e, request); + log.error("orderParameterEmptyException", e); + return new ErrorResponse(e.getMessage(), ORDER_PARAMETER_EMPTY.getCode()); + } + @ResponseStatus(FORBIDDEN) @ExceptionHandler(OrderUpdateUnauthorizedException.class) public ErrorResponse orderUpdateUnauthorizedException(OrderUpdateUnauthorizedException e, WebRequest request) { @@ -182,6 +205,34 @@ public ErrorResponse orderUpdateUnauthorizedException(OrderUpdateUnauthorizedExc return new ErrorResponse(e.getMessage(), ORDER_UPDATE_UNAUTHORIZED.getCode()); } + @ResponseStatus(FORBIDDEN) + @ExceptionHandler(OrderViewUnauthorizedException.class) + public ErrorResponse orderViewUnauthorizedException(OrderViewUnauthorizedException e, WebRequest request) { + alarm(e, request); + log.error("orderViewUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), ORDER_VIEW_UNAUTHORIZED.getCode()); + } + + /** + * 예약 관련 예외 처리 + */ + + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(DuplicateReservationException.class) + public ErrorResponse duplicateReservationException(DuplicateReservationException e, WebRequest request) { + alarm(e, request); + log.error("duplicateReservationException", e); + return new ErrorResponse(e.getMessage(), DUPLICATE_RESERVATION.getCode()); + } + + @ResponseStatus(FORBIDDEN) + @ExceptionHandler(ReservationAddUnauthorizedException.class) + public ErrorResponse reservationAddUnauthorizedException(ReservationAddUnauthorizedException e, WebRequest request) { + alarm(e, request); + log.error("reservationAddUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), RESERVATION_ADD_UNAUTHORIZED.getCode()); + } + @ResponseStatus(NOT_FOUND) @ExceptionHandler(ReservationNotFoundException.class) public ErrorResponse reservationNotFoundException(ReservationNotFoundException e, WebRequest request) { @@ -190,12 +241,12 @@ public ErrorResponse reservationNotFoundException(ReservationNotFoundException e return new ErrorResponse(e.getMessage(), NOTFOUND_RESERVATION.getCode()); } - @ResponseStatus(FORBIDDEN) - @ExceptionHandler(ReservationViewUnauthorizedException.class) - public ErrorResponse reservationViewUnauthorizedException(ReservationViewUnauthorizedException e, WebRequest request) { + @ResponseStatus(INTERNAL_SERVER_ERROR) + @ExceptionHandler(ReservationNumberIssueFailException.class) + public ErrorResponse reservationNumberIssueFailException(ReservationNumberIssueFailException e, WebRequest request) { alarm(e, request); - log.error("reservation_viewUnauthorizedException", e); - return new ErrorResponse(e.getMessage(), RESERVATION_VIEW_UNAUTHORIZED.getCode()); + log.error("reservationNumberIssueFailException", e); + return new ErrorResponse(e.getMessage(), RESERVATION_NUMBER_ISSUE_FAIL.getCode()); } @ResponseStatus(FORBIDDEN) @@ -206,6 +257,26 @@ public ErrorResponse reservationUpdateUnauthorizedException(ReservationUpdateUna return new ErrorResponse(e.getMessage(), RESERVATION_UPDATE_UNAUTHORIZED.getCode()); } + @ResponseStatus(FORBIDDEN) + @ExceptionHandler(ReservationViewUnauthorizedException.class) + public ErrorResponse reservationViewUnauthorizedException(ReservationViewUnauthorizedException e, WebRequest request) { + alarm(e, request); + log.error("reservation_viewUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), RESERVATION_VIEW_UNAUTHORIZED.getCode()); + } + + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(UserWaitingLimitExceededException.class) + public ErrorResponse userWaitingLimitExceededException(UserWaitingLimitExceededException e, WebRequest request) { + alarm(e, request); + log.error("userWaitingLimitExceededException", e); + return new ErrorResponse(e.getMessage(), USER_WAITING_LIMIT_EXCEEDED.getCode()); + } + + + /** + * 메뉴 관련 예외 처리 + */ @ResponseStatus(FORBIDDEN) @ExceptionHandler(MenuCreationUnauthorizedException.class) public ErrorResponse menuCreationUnauthorizedException(MenuCreationUnauthorizedException e, WebRequest request) { @@ -215,11 +286,35 @@ public ErrorResponse menuCreationUnauthorizedException(MenuCreationUnauthorizedE } @ResponseStatus(FORBIDDEN) - @ExceptionHandler(MenuViewUnauthorizedException.class) - public ErrorResponse menuViewUnauthorizedException(MenuViewUnauthorizedException e, WebRequest request) { + @ExceptionHandler(MenuDeleteUnauthorizedException.class) + public ErrorResponse menuDeleteUnauthorizedException(MenuDeleteUnauthorizedException e, WebRequest request) { alarm(e, request); - log.error("menuViewUnauthorizedException", e); - return new ErrorResponse(e.getMessage(), MENU_VIEW_UNAUTHORIZED.getCode()); + log.error("menuDeleteUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), MENU_DELETE_UNAUTHORIZED.getCode()); + } + + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(MenuImageEmptyException.class) + public ErrorResponse menuImageEmptyException(MenuImageEmptyException e, WebRequest request) { + alarm(e, request); + log.error("menuImageEmptyException", e); + return new ErrorResponse(e.getMessage(), IMAGE_FILE_EMPTY.getCode()); + } + + @ResponseStatus(NOT_FOUND) + @ExceptionHandler(MenuNotFoundException.class) + public ErrorResponse menuNotFoundException(MenuNotFoundException e, WebRequest request) { + alarm(e, request); + log.error("menuNotFoundException", e); + return new ErrorResponse(e.getMessage(), MENU_NOT_FOUND.getCode()); + } + + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(MenuParamEmptyException.class) + public ErrorResponse menuParamEmptyException(MenuParamEmptyException e, WebRequest request) { + alarm(e, request); + log.error("menuParamEmptyException", e); + return new ErrorResponse(e.getMessage(), MENU_PARAMETER_EMPTY.getCode()); } @ResponseStatus(FORBIDDEN) @@ -231,19 +326,55 @@ public ErrorResponse menuUpdateUnauthorizedException(MenuUpdateUnauthorizedExcep } @ResponseStatus(FORBIDDEN) - @ExceptionHandler(MenuDeleteUnauthorizedException.class) - public ErrorResponse menuDeleteUnauthorizedException(MenuDeleteUnauthorizedException e, WebRequest request) { + @ExceptionHandler(MenuViewUnauthorizedException.class) + public ErrorResponse menuViewUnauthorizedException(MenuViewUnauthorizedException e, WebRequest request) { alarm(e, request); - log.error("menuDeleteUnauthorizedException", e); - return new ErrorResponse(e.getMessage(), MENU_DELETE_UNAUTHORIZED.getCode()); + log.error("menuViewUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), MENU_VIEW_UNAUTHORIZED.getCode()); } + + /** + * 주점 관련 예외 처리 + */ @ResponseStatus(FORBIDDEN) - @ExceptionHandler(StoreViewUnauthorizedException.class) - public ErrorResponse storeViewUnauthorizedException(StoreViewUnauthorizedException e, WebRequest request) { + @ExceptionHandler(StoreDeleteUnauthorizedException.class) + public ErrorResponse storeDeleteUnauthorizedException(StoreDeleteUnauthorizedException e, WebRequest request) { alarm(e, request); - log.error("storeViewUnauthorizedException", e); - return new ErrorResponse(e.getMessage(), STORE_VIEW_UNAUTHORIZED.getCode()); + log.error("storeDeleteUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), STORE_DELETE_UNAUTHORIZED.getCode()); + } + + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(StoreImageEmptyException.class) + public ErrorResponse storeImageEmptyException(StoreImageEmptyException e, WebRequest request) { + alarm(e, request); + log.error("storeImageEmptyException", e); + return new ErrorResponse(e.getMessage(), IMAGE_FILE_EMPTY.getCode()); + } + + @ResponseStatus(NOT_FOUND) + @ExceptionHandler(StoreImageNotFoundException.class) + public ErrorResponse storeImageNotFoundException(StoreImageNotFoundException e, WebRequest request) { + alarm(e, request); + log.error("storeImageNotFoundException", e); + return new ErrorResponse(e.getMessage(), IMAGE_FILE_NOT_FOUND.getCode()); + } + + @ResponseStatus(NOT_FOUND) + @ExceptionHandler(StoreNotFoundException.class) + public ErrorResponse storeNotFoundException(StoreNotFoundException e, WebRequest request) { + alarm(e, request); + log.error("storeNotFoundException", e); + return new ErrorResponse(e.getMessage(), STORE_NOT_FOUND.getCode()); + } + + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(StoreParamEmptyException.class) + public ErrorResponse storeParamEmptyException(StoreParamEmptyException e, WebRequest request) { + alarm(e, request); + log.error("storeParamEmptyException", e); + return new ErrorResponse(e.getMessage(), STORE_PARAMETER_EMPTY.getCode()); } @ResponseStatus(FORBIDDEN) @@ -255,13 +386,84 @@ public ErrorResponse storeUpdateUnauthorizedException(StoreUpdateUnauthorizedExc } @ResponseStatus(FORBIDDEN) - @ExceptionHandler(StoreDeleteUnauthorizedException.class) - public ErrorResponse storeDeleteUnauthorizedException(StoreDeleteUnauthorizedException e, WebRequest request) { + @ExceptionHandler(StoreViewUnauthorizedException.class) + public ErrorResponse storeViewUnauthorizedException(StoreViewUnauthorizedException e, WebRequest request) { alarm(e, request); - log.error("storeDeleteUnauthorizedException", e); - return new ErrorResponse(e.getMessage(), STORE_DELETE_UNAUTHORIZED.getCode()); + log.error("storeViewUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), STORE_VIEW_UNAUTHORIZED.getCode()); + } + + @ResponseStatus(FORBIDDEN) + @ExceptionHandler(StoreWaitingDisabledException.class) + public ErrorResponse storeWaitingDisabledException(StoreWaitingDisabledException e, WebRequest request) { + alarm(e, request); + log.error("storeWaitingDisabledException", e); + return new ErrorResponse(e.getMessage(), STORE_WAITING_DISABLED.getCode()); + } + + /** + * 결제 수단 관련 예외처리 + */ + @ResponseStatus(CONFLICT) + @ExceptionHandler(StorePaymentAlreadyExistsException.class) + public ErrorResponse storePaymentAlreadyExistsException(StorePaymentAlreadyExistsException e, WebRequest request) { + alarm(e, request); + log.error("storePaymentAlreadyExistsException", e); + return new ErrorResponse(e.getMessage(), STORE_PAYMENT_ALREADY_EXISTS.getCode()); } + @ResponseStatus(FORBIDDEN) + @ExceptionHandler(StorePaymentCreationUnauthorizedException.class) + public ErrorResponse storePaymentCreationUnauthorizedException(StorePaymentCreationUnauthorizedException e, WebRequest request) { + alarm(e, request); + log.error("storePaymentCreationUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), STORE_PAYMENT_CREATION_UNAUTHORIZED.getCode()); + } + + @ResponseStatus(NOT_FOUND) + @ExceptionHandler(StorePaymentNotFoundException.class) + public ErrorResponse storePaymentNotFoundException(StorePaymentNotFoundException e, WebRequest request) { + alarm(e, request); + log.error("storePaymentNotFoundException", e); + return new ErrorResponse(e.getMessage(), STORE_PAYMENT_NOT_FOUND.getCode()); + } + + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(StorePaymentParamEmptyException.class) + public ErrorResponse storePaymentParamEmptyException(StorePaymentParamEmptyException e, WebRequest request) { + alarm(e, request); + log.error("storePaymentParamEmptyException", e); + return new ErrorResponse(e.getMessage(), STORE_PAYMENT_PARAMETER_EMPTY.getCode()); + } + + @ResponseStatus(FORBIDDEN) + @ExceptionHandler(StorePaymentUpdateUnauthorizedException.class) + public ErrorResponse storePaymentUpdateUnauthorizedException(StorePaymentUpdateUnauthorizedException e, WebRequest request) { + alarm(e, request); + log.error("storePaymentUpdateUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), STORE_PAYMENT_UPDATE_UNAUTHORIZED.getCode()); + } + + @ResponseStatus(FORBIDDEN) + @ExceptionHandler(StorePaymentViewUnauthorizedException.class) + public ErrorResponse storePaymentViewUnauthorizedException(StorePaymentViewUnauthorizedException e, WebRequest request) { + alarm(e, request); + log.error("storePaymentViewUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), STORE_PAYMENT_VIEW_UNAUTHORIZED.getCode()); + } + + /** + * redis 관련 예외처리 + */ + @ResponseStatus(INTERNAL_SERVER_ERROR) + @ExceptionHandler(MenuCounterUpdateException.class) + public ErrorResponse menuCounterUpdateException(MenuCounterUpdateException e, WebRequest request) { + alarm(e, request); + log.error("menuCounterUpdateException", e); + return new ErrorResponse(e.getMessage(), MENU_COUNTER_UPDATE.getCode()); + } + + // 공통 에러 Map 생성 private static Map getErrors(MethodArgumentNotValidException e) { return e.getBindingResult() From 6c078a35e655002b0f408ec384fc0210365a3a12 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 14:52:23 +0900 Subject: [PATCH 06/27] =?UTF-8?q?feat:=20=EC=A3=BC=EB=AC=B8=20=EC=B7=A8?= =?UTF-8?q?=EC=86=8C=20=EB=B0=8F=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EC=97=90=EB=9F=AC=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nowait/common/exception/ErrorMessage.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java index f1e69dc..4db017d 100644 --- a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java +++ b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java @@ -12,7 +12,7 @@ public enum ErrorMessage { DOES_NOT_MATCH_REFRESH_TOKEN("기존 리프레시 토큰이 일치하지 않습니다.", "token002"), // user - NOTFOUND_USER("저장된 사용자 정보가 없습니다.", "user001"), + NOT_FOUND_USER("저장된 사용자 정보가 없습니다.", "user001"), //order ORDER_PARAMETER_EMPTY("주문 생성 시 파라미터 정보가 없습니다.", "order001"), @@ -22,6 +22,8 @@ public enum ErrorMessage { ORDER_VIEW_UNAUTHORIZED("주문 보기 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "order005"), ORDER_NOT_FOUND("해당 주문을 찾을 수 없습니다.", "order006"), ORDER_UPDATE_UNAUTHORIZED("주문 수정 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "order007"), + ORDER_ALREADY_CANCELLED("이미 취소된 주문입니다.", "order008"), + INVALID_ORDER_STATUS_TRANSITION("유효하지 않은 주문 상태 변경입니다. (현재: %s, 요청: %s)", "order009"), //reservation NOTFOUND_RESERVATION("저장된 예약 정보가 없습니다.", "reservation001"), @@ -59,9 +61,9 @@ public enum ErrorMessage { STORE_PAYMENT_NOT_FOUND("해당 주점 결제 정보를 찾을 수 없습니다.", "storePayment002"), STORE_PAYMENT_VIEW_UNAUTHORIZED("주점 결제 정보 보기 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "storePayment003"), STORE_PAYMENT_CREATION_UNAUTHORIZED("주점 결제 정보 생성 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "storePayment004"), - STORE_PAYMENT_UPDATE_UNAUTHORIZED("주점 결제 정보 수정 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "storePayment004"), - STORE_PAYMENT_DELETE_UNAUTHORIZED("주점 결제 정보 삭제 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "storePayment005"), - STORE_PAYMENT_ALREADY_EXISTS("이미 존재하는 주점 결제 정보입니다.", "storePayment006"), + STORE_PAYMENT_UPDATE_UNAUTHORIZED("주점 결제 정보 수정 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "storePayment005"), + STORE_PAYMENT_DELETE_UNAUTHORIZED("주점 결제 정보 삭제 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "storePayment006"), + STORE_PAYMENT_ALREADY_EXISTS("이미 존재하는 주점 결제 정보입니다.", "storePayment007"), // Statistics STATISTIC_VIEW_UNAUTHORIZED("통계 보기 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "statistics001"), @@ -69,7 +71,7 @@ public enum ErrorMessage { // image IMAGE_FILE_EMPTY("업로드 된 이미지 파일이 없습니다.", "image001"), - IMAGE_FILE_NOT_FOUND("DB에 해당 이미지 메타데이터가 존재하지 않습니다.", "image001"), + IMAGE_FILE_NOT_FOUND("DB에 해당 이미지 메타데이터가 존재하지 않습니다.", "image002"), // search SEARCH_PARAMETER_EMPTY("검색어가 비어있습니다.", "search001"); @@ -89,4 +91,8 @@ public String getMessage() { public String getCode() { return code; } + + public String format(Object... args) { + return String.format(message, args); + } } From ccbf25862070130ec5bab823b20a0305f03facd9 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 14:52:55 +0900 Subject: [PATCH 07/27] =?UTF-8?q?refactor:=20=EC=97=90=EB=9F=AC=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EB=AA=85=20=EC=9D=BC=EA=B4=80=EC=84=B1=20=EC=9E=88?= =?UTF-8?q?=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 11 ++++++++++- .../exception/GlobalExceptionHandler.java | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java index 2929ba8..79f3ae0 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java @@ -32,6 +32,7 @@ import com.nowait.domaincorerdb.menu.exception.MenuViewUnauthorizedException; import com.nowait.domaincorerdb.order.exception.DepositorNameTooLongException; import com.nowait.domaincorerdb.order.exception.DuplicateOrderException; +import com.nowait.domaincorerdb.order.exception.OrderAlreadyCancelledException; import com.nowait.domaincorerdb.order.exception.OrderItemsEmptyException; import com.nowait.domaincorerdb.order.exception.OrderNotFoundException; import com.nowait.domaincorerdb.order.exception.OrderParameterEmptyException; @@ -149,7 +150,7 @@ public ErrorResponse handleMultipartException(MultipartException e, WebRequest r public ErrorResponse userNotFoundException(UserNotFoundException e, WebRequest request) { alarm(e, request); log.error("userNotFoundException", e); - return new ErrorResponse(e.getMessage(), NOTFOUND_USER.getCode()); + return new ErrorResponse(e.getMessage(), NOT_FOUND_USER.getCode()); } @@ -173,6 +174,14 @@ public ErrorResponse duplicateOrderException(DuplicateOrderException e, WebReque return new ErrorResponse(e.getMessage(), ErrorMessage.DUPLICATE_ORDER.getCode()); } + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(OrderAlreadyCancelledException.class) + public ErrorResponse orderAlreadyCancelledException(OrderAlreadyCancelledException e, WebRequest request) { + alarm(e, request); + log.error("orderAlreadyCancelledException", e); + return new ErrorResponse(e.getMessage(), ORDER_ALREADY_CANCELLED.getCode()); + } + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(OrderItemsEmptyException.class) public ErrorResponse orderItemsEmptyException(OrderItemsEmptyException e, WebRequest request) { diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/exception/GlobalExceptionHandler.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/exception/GlobalExceptionHandler.java index 9b4c5f4..cf09ca4 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/exception/GlobalExceptionHandler.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/exception/GlobalExceptionHandler.java @@ -172,7 +172,7 @@ public ErrorResponse handleAlreadyDeleteBookmark( public ErrorResponse userNotFoundException(UserNotFoundException e, WebRequest request) { alarm(e, request); log.error("userNotFoundException", e); - return new ErrorResponse(e.getMessage(), NOTFOUND_USER.getCode()); + return new ErrorResponse(e.getMessage(), NOT_FOUND_USER.getCode()); } @ResponseStatus(BAD_REQUEST) From 86f66eb5da34622ea3116314ab1977bd30d61d14 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 14:53:38 +0900 Subject: [PATCH 08/27] =?UTF-8?q?feat:=20=EC=A3=BC=EB=AC=B8=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EB=B3=80=EA=B2=BD=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=20=EC=98=88=EC=99=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InvalidOrderStatusTransitionException.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/exception/InvalidOrderStatusTransitionException.java diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/exception/InvalidOrderStatusTransitionException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/exception/InvalidOrderStatusTransitionException.java new file mode 100644 index 0000000..571c9b2 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/exception/InvalidOrderStatusTransitionException.java @@ -0,0 +1,10 @@ +package com.nowait.domaincorerdb.order.exception; + +import com.nowait.common.exception.ErrorMessage; +import com.nowait.domaincorerdb.order.entity.OrderStatus; + +public class InvalidOrderStatusTransitionException extends RuntimeException { + public InvalidOrderStatusTransitionException(OrderStatus current, OrderStatus target) { + super(ErrorMessage.INVALID_ORDER_STATUS_TRANSITION.format(current, target)); + } +} From 09f161f3b58e6ab18d87528af17d84fe08bd6b57 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 14:53:51 +0900 Subject: [PATCH 09/27] =?UTF-8?q?feat:=20=EC=A3=BC=EB=AC=B8=20=EC=B7=A8?= =?UTF-8?q?=EC=86=8C=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=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 --- .../order/exception/OrderAlreadyCancelledException.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/exception/OrderAlreadyCancelledException.java diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/exception/OrderAlreadyCancelledException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/exception/OrderAlreadyCancelledException.java new file mode 100644 index 0000000..2d65067 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/exception/OrderAlreadyCancelledException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincorerdb.order.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class OrderAlreadyCancelledException extends RuntimeException { + public OrderAlreadyCancelledException() { + super(ErrorMessage.ORDER_ALREADY_CANCELLED.getMessage()); + } +} From f82e07c6e3be47376d2ec2b0e91815ee5f6aa32e Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 14:54:42 +0900 Subject: [PATCH 10/27] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EA=B0=80=EB=8F=85=EC=84=B1=20=EA=B0=9C=EC=84=A0=20-=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=EA=B6=8C=ED=95=9C=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC=20?= =?UTF-8?q?-=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/OrderService.java | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java index cfd2622..aaa3436 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java @@ -46,11 +46,10 @@ public class OrderService { @Transactional(readOnly = true) public List findAllOrders(Long storeId, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + User user = getUser(memberDetails); storeRepository.findByStoreIdAndDeletedFalse(storeId).orElseThrow(StoreNotFoundException::new); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { - throw new OrderViewUnauthorizedException(); - } + + validateViewAuthorization(user, storeId); LocalDate today = LocalDate.now(ZoneId.of("Asia/Seoul")); LocalDateTime startDateTime = today.atStartOfDay(); @@ -64,11 +63,12 @@ public List findAllOrders(Long storeId, MemberDetails memberDe @Transactional public OrderStatusUpdateResponseDto updateOrderStatus(Long orderId, OrderStatus newStatus, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + User user = getUser(memberDetails); UserOrder userOrder = orderRepository.findById(orderId).orElseThrow(OrderNotFoundException::new); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(userOrder.getStore().getStoreId())) { - throw new OrderUpdateUnauthorizedException(); - } + Long storeId = userOrder.getStore().getStoreId(); + + validateUpdateAuthorization(user, storeId); + userOrder.updateStatus(newStatus); if (OrderStatus.COOKED.equals(newStatus)) { @@ -92,12 +92,10 @@ public OrderStatusUpdateResponseDto updateOrderStatus(Long orderId, OrderStatus @Transactional public OrderStatusUpdateResponseDto cancelOrder(Long orderId, CancelOrderRequest cancelOrderRequest, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + User user = getUser(memberDetails); UserOrder userOrder = orderRepository.findById(orderId).orElseThrow(OrderNotFoundException::new); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(userOrder.getStore().getStoreId())) { - throw new OrderUpdateUnauthorizedException(); - } + validateUpdateAuthorization(user, userOrder.getStore().getStoreId()); userOrder.cancelOrder(); @@ -115,25 +113,40 @@ public OrderStatusUpdateResponseDto cancelOrder(Long orderId, CancelOrderRequest @Transactional(readOnly = true) public OrderSalesSumDetail getSaleSumByStoreId(MemberDetails memberDetails, LocalDate date) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + User user = getUser(memberDetails); Long storeId = user.getStoreId(); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { - throw new OrderViewUnauthorizedException(); - } + validateViewAuthorization(user, storeId); return statisticCustomRepository.findSalesSumByStoreId(storeId, date); } + // 현재는 사용하지 않음. 향후 관리자 통계 페이지 확장 시 활용 가능 @Transactional(readOnly = true) public List getTop5StoresBySalesToday(MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + User user = getUser(memberDetails); Long storeId = user.getStoreId(); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { + validateUpdateAuthorization(user, storeId); + + return statisticCustomRepository.getTop4PlusMine(storeId); + } + + private void validateViewAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) + || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { throw new OrderViewUnauthorizedException(); } + } + + private void validateUpdateAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) + || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { + throw new OrderUpdateUnauthorizedException(); + } + } - return statisticCustomRepository.getTop4PlusMine(storeId); + private User getUser(MemberDetails memberDetails) { + return userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); } } From 8610b76e2ae5e49aea146a8ce87489995bcd0d33 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 14:54:55 +0900 Subject: [PATCH 11/27] =?UTF-8?q?chore:=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EC=9D=BC=EA=B4=80=EC=84=B1=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domaincorerdb/user/exception/UserNotFoundException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/exception/UserNotFoundException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/exception/UserNotFoundException.java index aef89ab..ec74236 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/exception/UserNotFoundException.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/user/exception/UserNotFoundException.java @@ -4,6 +4,6 @@ public class UserNotFoundException extends RuntimeException { public UserNotFoundException() { - super(ErrorMessage.NOTFOUND_USER.getMessage()); + super(ErrorMessage.NOT_FOUND_USER.getMessage()); } } From 964c7cd63a3025df968c31a9b450ccacd2c4b194 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 14:55:10 +0900 Subject: [PATCH 12/27] =?UTF-8?q?refactor:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domaincorerdb/order/entity/UserOrder.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/entity/UserOrder.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/entity/UserOrder.java index 54fd36a..c26ac87 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/entity/UserOrder.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/order/entity/UserOrder.java @@ -4,6 +4,8 @@ import java.util.List; import com.nowait.domaincorerdb.base.entity.BaseTimeEntity; +import com.nowait.domaincorerdb.order.exception.InvalidOrderStatusTransitionException; +import com.nowait.domaincorerdb.order.exception.OrderAlreadyCancelledException; import com.nowait.domaincorerdb.store.entity.Store; import jakarta.persistence.CascadeType; @@ -62,14 +64,25 @@ public class UserOrder extends BaseTimeEntity { private Integer totalPrice; public void updateStatus(OrderStatus newStatus) { + if (!isValidTransition(this.status, newStatus)) { + throw new InvalidOrderStatusTransitionException(this.status, newStatus); + } this.status = newStatus; } public void cancelOrder() { if (this.status == OrderStatus.CANCELLED) { - return; + throw new OrderAlreadyCancelledException(); } this.status = OrderStatus.CANCELLED; } + private boolean isValidTransition(OrderStatus current, OrderStatus target) { + return switch (current) { + case WAITING_FOR_PAYMENT, COOKED -> target == OrderStatus.COOKING || target == OrderStatus.CANCELLED; + case COOKING -> target == OrderStatus.COOKED || target == OrderStatus.CANCELLED; + case CANCELLED -> false; + }; + } + } From f11504eab6d2e046798b06077e6f66cbf7df3e93 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 15:14:05 +0900 Subject: [PATCH 13/27] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=EA=B6=8C=ED=95=9C=20=EA=B2=80=EC=A6=9D=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ReservationService.java | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java index fb46126..45e8149 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java @@ -26,6 +26,8 @@ import com.nowait.applicationadmin.reservation.dto.WaitingUserResponse; import com.nowait.common.enums.ReservationStatus; import com.nowait.common.enums.Role; +import com.nowait.domaincorerdb.order.exception.OrderUpdateUnauthorizedException; +import com.nowait.domaincorerdb.order.exception.OrderViewUnauthorizedException; import com.nowait.domaincorerdb.reservation.entity.Reservation; import com.nowait.domaincorerdb.reservation.exception.ReservationNotFoundException; import com.nowait.domaincorerdb.reservation.exception.ReservationUpdateUnauthorizedException; @@ -56,10 +58,8 @@ public class ReservationService { //TODO 성능 비교를 위해 남겨둔 로직 @Transactional(readOnly = true) public ReservationStatusSummaryDto getReservationListByStoreId(Long storeId, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { - throw new ReservationViewUnauthorizedException(); - } + User user = getUser(memberDetails); + validateViewAuthorization(user, storeId); List reservations = reservationRepository.findAllByStore_StoreIdOrderByRequestedAtAsc(storeId); // 상태별 카운트 집계 @@ -93,13 +93,11 @@ public ReservationStatusSummaryDto getReservationListByStoreId(Long storeId, Mem @Transactional public CallGetResponseDto updateReservationStatus(Long reservationId, ReservationStatusUpdateRequestDto requestDto, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + User user = getUser(memberDetails); Reservation reservation = reservationRepository.findById(reservationId) .orElseThrow(ReservationNotFoundException::new); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId() - .equals(reservation.getStore().getStoreId())) { - throw new ReservationUpdateUnauthorizedException(); - } + validateUpdateAuthorization(user, reservation.getStore().getStoreId()); + reservation.markUpdated(LocalDateTime.now(), requestDto.getStatus()); return CallGetResponseDto.fromEntity(reservation); } @@ -194,7 +192,8 @@ public List getAllWaitingUserDetails(Long storeId) { // 완료 or 취소 처리된 대기 리스트 조회 @Transactional(readOnly = true) public List getCompletedWaitingUserDetails(Long storeId, MemberDetails memberDetails) { - authorize(storeId, memberDetails); + User user = getUser(memberDetails); + validateViewAuthorization(user, storeId); List reservations = findTodayWaiting(storeId); return reservations.stream() @@ -212,7 +211,8 @@ public List getCompletedWaitingUserDetails(Long storeId, Me public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, MemberDetails member, ReservationStatus newStatus) { - authorize(storeId, member); + User user = getUser(member); + validateUpdateAuthorization(user, storeId); String queueKey = RedisKeyUtils.buildWaitingKeyPrefix() + storeId; @@ -350,13 +350,22 @@ private List findTodayWaiting(Long storeId) { } // 사용자 인증 - private User authorize(Long storeId, MemberDetails member) { - User u = userRepository.findById(member.getId()) - .orElseThrow(UserNotFoundException::new); - if (!Role.SUPER_ADMIN.equals(u.getRole()) && !storeId.equals(u.getStoreId())) { + private void validateViewAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) + || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { throw new ReservationViewUnauthorizedException(); } - return u; + } + + private void validateUpdateAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) + || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { + throw new ReservationUpdateUnauthorizedException(); + } + } + + private User getUser(MemberDetails memberDetails) { + return userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); } } From 537422f7762d4345872ffbbc151f48a0f0e3f73a Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 15:50:28 +0900 Subject: [PATCH 14/27] =?UTF-8?q?refactor:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 45 +++++++++++++++++++ .../nowait/common/exception/ErrorMessage.java | 7 +++ .../InvalidReservationParameterException.java | 9 ++++ ...dReservationStatusTransitionException.java | 10 +++++ .../ReservationAlreadyCancelledException.java | 9 ++++ .../ReservationAlreadyConfirmedException.java | 9 ++++ ...ReservationDataInconsistencyException.java | 10 +++++ ...UnsupportedReservationStatusException.java | 10 +++++ 8 files changed, 109 insertions(+) create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/InvalidReservationParameterException.java create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/InvalidReservationStatusTransitionException.java create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationAlreadyCancelledException.java create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationAlreadyConfirmedException.java create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationDataInconsistencyException.java create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/UnsupportedReservationStatusException.java diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java index 79f3ae0..5c8de09 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java @@ -39,11 +39,16 @@ import com.nowait.domaincorerdb.order.exception.OrderUpdateUnauthorizedException; import com.nowait.domaincorerdb.order.exception.OrderViewUnauthorizedException; import com.nowait.domaincorerdb.reservation.exception.DuplicateReservationException; +import com.nowait.domaincorerdb.reservation.exception.InvalidReservationParameterException; +import com.nowait.domaincorerdb.reservation.exception.InvalidReservationStatusTransitionException; import com.nowait.domaincorerdb.reservation.exception.ReservationAddUnauthorizedException; +import com.nowait.domaincorerdb.reservation.exception.ReservationAlreadyCancelledException; +import com.nowait.domaincorerdb.reservation.exception.ReservationAlreadyConfirmedException; import com.nowait.domaincorerdb.reservation.exception.ReservationNotFoundException; import com.nowait.domaincorerdb.reservation.exception.ReservationNumberIssueFailException; import com.nowait.domaincorerdb.reservation.exception.ReservationUpdateUnauthorizedException; import com.nowait.domaincorerdb.reservation.exception.ReservationViewUnauthorizedException; +import com.nowait.domaincorerdb.reservation.exception.UnsupportedReservationStatusException; import com.nowait.domaincorerdb.reservation.exception.UserWaitingLimitExceededException; import com.nowait.domaincorerdb.store.exception.StoreDeleteUnauthorizedException; import com.nowait.domaincorerdb.store.exception.StoreImageEmptyException; @@ -234,6 +239,22 @@ public ErrorResponse duplicateReservationException(DuplicateReservationException return new ErrorResponse(e.getMessage(), DUPLICATE_RESERVATION.getCode()); } + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(InvalidReservationParameterException.class) + public ErrorResponse invalidReservationParameterException(InvalidReservationParameterException e, WebRequest request) { + alarm(e, request); + log.error("invalidReservationParameterException", e); + return new ErrorResponse(e.getMessage(), INVALID_RESERVATION_PARAMETER.getCode()); + } + + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(InvalidReservationStatusTransitionException.class) + public ErrorResponse invalidReservationStatusTransitionException(InvalidReservationStatusTransitionException e, WebRequest request) { + alarm(e, request); + log.error("invalidReservationStatusTransitionException", e); + return new ErrorResponse(e.getMessage(), INVALID_RESERVATION_STATUS_TRANSITION.getCode()); + } + @ResponseStatus(FORBIDDEN) @ExceptionHandler(ReservationAddUnauthorizedException.class) public ErrorResponse reservationAddUnauthorizedException(ReservationAddUnauthorizedException e, WebRequest request) { @@ -242,6 +263,22 @@ public ErrorResponse reservationAddUnauthorizedException(ReservationAddUnauthori return new ErrorResponse(e.getMessage(), RESERVATION_ADD_UNAUTHORIZED.getCode()); } + @ResponseStatus(CONFLICT) + @ExceptionHandler(ReservationAlreadyCancelledException.class) + public ErrorResponse reservationAlreadyCancelledException(ReservationAlreadyCancelledException e, WebRequest request) { + alarm(e, request); + log.error("reservationAlreadyCancelledException", e); + return new ErrorResponse(e.getMessage(), RESERVATION_ALREADY_CANCELLED.getCode()); + } + + @ResponseStatus(CONFLICT) + @ExceptionHandler(ReservationAlreadyConfirmedException.class) + public ErrorResponse reservationAlreadyConfirmedException(ReservationAlreadyConfirmedException e, WebRequest request) { + alarm(e, request); + log.error("reservationAlreadyConfirmedException", e); + return new ErrorResponse(e.getMessage(), RESERVATION_ALREADY_CONFIRMED.getCode()); + } + @ResponseStatus(NOT_FOUND) @ExceptionHandler(ReservationNotFoundException.class) public ErrorResponse reservationNotFoundException(ReservationNotFoundException e, WebRequest request) { @@ -274,6 +311,14 @@ public ErrorResponse reservationViewUnauthorizedException(ReservationViewUnautho return new ErrorResponse(e.getMessage(), RESERVATION_VIEW_UNAUTHORIZED.getCode()); } + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(UnsupportedReservationStatusException.class) + public ErrorResponse unsupportedReservationStatusException(UnsupportedReservationStatusException e, WebRequest request) { + alarm(e, request); + log.error("unsupportedReservationStatusException", e); + return new ErrorResponse(e.getMessage(), UNSUPPORTED_RESERVATION_STATUS.getCode()); + } + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(UserWaitingLimitExceededException.class) public ErrorResponse userWaitingLimitExceededException(UserWaitingLimitExceededException e, WebRequest request) { diff --git a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java index 4db017d..9edb952 100644 --- a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java +++ b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java @@ -33,6 +33,13 @@ public enum ErrorMessage { USER_WAITING_LIMIT_EXCEEDED("유저당 웨이팅 가능 개수(3개)를 초과했습니다.", "reservation005"), RESERVATION_NUMBER_ISSUE_FAIL("예약 번호 발급에 실패했습니다.", "reservation006"), RESERVATION_ADD_UNAUTHORIZED("MANAGER는 예약 대기를 할 수 없습니다.", "reservation007"), + INVALID_RESERVATION_STATUS_TRANSITION("유효하지 않은 예약 상태 변경입니다. (현재: %s, 요청: %s)", "reservation008"), + RESERVATION_ALREADY_CONFIRMED("이미 확정된 예약입니다.", "reservation009"), + RESERVATION_ALREADY_CANCELLED("이미 취소된 예약입니다.", "reservation010"), + RESERVATION_DATA_INCONSISTENCY("예약 데이터가 Redis와 DB 간 일치하지 않습니다. (%s)", "reservation011"), + UNSUPPORTED_RESERVATION_STATUS("지원하지 않는 예약 상태입니다: %s", "reservation012"), + INVALID_RESERVATION_PARAMETER("잘못된 예약 요청 파라미터입니다. (%s)", "reservation013"), + // bookmark DUPLICATE_BOOKMARK("이미 북마크한 주점입니다.", "bookmark001"), diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/InvalidReservationParameterException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/InvalidReservationParameterException.java new file mode 100644 index 0000000..2db8dd4 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/InvalidReservationParameterException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincorerdb.reservation.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class InvalidReservationParameterException extends RuntimeException { + public InvalidReservationParameterException(String details) { + super(ErrorMessage.INVALID_RESERVATION_PARAMETER.format(details)); + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/InvalidReservationStatusTransitionException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/InvalidReservationStatusTransitionException.java new file mode 100644 index 0000000..cc6fe02 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/InvalidReservationStatusTransitionException.java @@ -0,0 +1,10 @@ +package com.nowait.domaincorerdb.reservation.exception; + +import com.nowait.common.enums.ReservationStatus; +import com.nowait.common.exception.ErrorMessage; + +public class InvalidReservationStatusTransitionException extends RuntimeException { + public InvalidReservationStatusTransitionException(ReservationStatus current, ReservationStatus target) { + super(ErrorMessage.INVALID_RESERVATION_STATUS_TRANSITION.format(current, target)); + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationAlreadyCancelledException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationAlreadyCancelledException.java new file mode 100644 index 0000000..1d2a766 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationAlreadyCancelledException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincorerdb.reservation.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class ReservationAlreadyCancelledException extends RuntimeException { + public ReservationAlreadyCancelledException() { + super(ErrorMessage.RESERVATION_ALREADY_CANCELLED.getMessage()); + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationAlreadyConfirmedException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationAlreadyConfirmedException.java new file mode 100644 index 0000000..724b167 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationAlreadyConfirmedException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincorerdb.reservation.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class ReservationAlreadyConfirmedException extends RuntimeException { + public ReservationAlreadyConfirmedException() { + super(ErrorMessage.RESERVATION_ALREADY_CONFIRMED.getMessage()); + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationDataInconsistencyException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationDataInconsistencyException.java new file mode 100644 index 0000000..37cd13d --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationDataInconsistencyException.java @@ -0,0 +1,10 @@ +package com.nowait.domaincorerdb.reservation.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class ReservationDataInconsistencyException extends RuntimeException { + public ReservationDataInconsistencyException(String details) { + super(ErrorMessage.RESERVATION_DATA_INCONSISTENCY.format(details)); + } +} + diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/UnsupportedReservationStatusException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/UnsupportedReservationStatusException.java new file mode 100644 index 0000000..9829575 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/UnsupportedReservationStatusException.java @@ -0,0 +1,10 @@ +package com.nowait.domaincorerdb.reservation.exception; + +import com.nowait.common.enums.ReservationStatus; +import com.nowait.common.exception.ErrorMessage; + +public class UnsupportedReservationStatusException extends RuntimeException { + public UnsupportedReservationStatusException(ReservationStatus status) { + super(ErrorMessage.UNSUPPORTED_RESERVATION_STATUS.format(status)); + } +} From 09ee01d57511bc5d23c41cae17ae0238ef21017b Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 15:50:36 +0900 Subject: [PATCH 15/27] =?UTF-8?q?refactor:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ReservationService.java | 35 +++++++++++++++---- .../reservation/entity/Reservation.java | 32 +++++++++++++++-- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java index 45e8149..eae6fdf 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java @@ -26,12 +26,14 @@ import com.nowait.applicationadmin.reservation.dto.WaitingUserResponse; import com.nowait.common.enums.ReservationStatus; import com.nowait.common.enums.Role; -import com.nowait.domaincorerdb.order.exception.OrderUpdateUnauthorizedException; -import com.nowait.domaincorerdb.order.exception.OrderViewUnauthorizedException; import com.nowait.domaincorerdb.reservation.entity.Reservation; +import com.nowait.domaincorerdb.reservation.exception.InvalidReservationParameterException; +import com.nowait.domaincorerdb.reservation.exception.InvalidReservationStatusTransitionException; +import com.nowait.domaincorerdb.reservation.exception.ReservationDataInconsistencyException; import com.nowait.domaincorerdb.reservation.exception.ReservationNotFoundException; import com.nowait.domaincorerdb.reservation.exception.ReservationUpdateUnauthorizedException; import com.nowait.domaincorerdb.reservation.exception.ReservationViewUnauthorizedException; +import com.nowait.domaincorerdb.reservation.exception.UnsupportedReservationStatusException; import com.nowait.domaincorerdb.reservation.repository.ReservationRepository; import com.nowait.domaincorerdb.store.repository.StoreRepository; import com.nowait.domaincorerdb.user.entity.MemberDetails; @@ -214,6 +216,10 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me User user = getUser(member); validateUpdateAuthorization(user, storeId); + if (userId == null || userId.isBlank()) { + throw new InvalidReservationParameterException("userId 값이 비어있습니다."); + } + String queueKey = RedisKeyUtils.buildWaitingKeyPrefix() + storeId; // Redis에서 상태·score·partySize·calledAt 조회 @@ -222,6 +228,17 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me Double score = redisTemplate.opsForZSet().score(queueKey, userId); Integer partySize = waitingRedisRepository.getWaitingPartySize(storeId, userId); + if (partySize == null || partySize <= 0) { + throw new InvalidReservationParameterException("partySize가 유효하지 않습니다. (storeId=" + storeId + ", userId=" + userId + ")"); + } + + if (reservationNumber == null || currStatus == null) { + throw new ReservationDataInconsistencyException( + String.format("storeId=%d, userId=%s, reservationNumber=%s, status=%s, partySize=%s", + storeId, userId, reservationNumber, currStatus, partySize) + ); + } + LocalDateTime requestedAt = score != null ? Instant.ofEpochMilli(score.longValue()).atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime() : LocalDateTime.now(); @@ -230,7 +247,9 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me switch (newStatus) { case CALLING: if (!ReservationStatus.WAITING.name().equals(currStatus)) { - throw new IllegalStateException("WAITING 상태에서만 CALLING 가능합니다."); + throw new InvalidReservationStatusTransitionException( + ReservationStatus.valueOf(currStatus), ReservationStatus.CALLING + ); } waitingRedisRepository.setWaitingStatus(storeId, userId, ReservationStatus.CALLING.name()); waitingRedisRepository.setWaitingCalledAt(storeId, userId, @@ -294,7 +313,9 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me List.of(ReservationStatus.CANCELLED), start, end - ).orElseThrow(() -> new IllegalStateException("취소된 예약이 없습니다.")); + ).orElseThrow(() -> new ReservationDataInconsistencyException( + String.format("취소된 예약이 DB에 존재하지 않습니다. (storeId=%d, userId=%s)", storeId, userId) + )); existing.markUpdated(LocalDateTime.now(ZoneId.of("Asia/Seoul")), ReservationStatus.CONFIRMED); Reservation saved = reservationRepository.save(existing); @@ -304,7 +325,9 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me case CANCELLED: if (!(ReservationStatus.WAITING.name().equals(currStatus) || ReservationStatus.CALLING.name().equals(currStatus))) { - throw new IllegalStateException("WAITING/CALLING 상태에서만 취소 가능합니다."); + throw new InvalidReservationStatusTransitionException( + ReservationStatus.valueOf(currStatus), ReservationStatus.CANCELLED + ); } if (reservationNumber != null) { @@ -329,7 +352,7 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me return EntryStatusResponseDto.fromEntity(saved); default: - throw new IllegalArgumentException("지원하지 않는 상태: " + newStatus); + throw new UnsupportedReservationStatusException(newStatus); } } diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/entity/Reservation.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/entity/Reservation.java index da5ec2b..4874444 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/entity/Reservation.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/entity/Reservation.java @@ -3,6 +3,9 @@ import java.time.LocalDateTime; import com.nowait.common.enums.ReservationStatus; +import com.nowait.domaincorerdb.reservation.exception.InvalidReservationStatusTransitionException; +import com.nowait.domaincorerdb.reservation.exception.ReservationAlreadyCancelledException; +import com.nowait.domaincorerdb.reservation.exception.ReservationAlreadyConfirmedException; import com.nowait.domaincorerdb.store.entity.Store; import com.nowait.domaincorerdb.user.entity.User; @@ -59,8 +62,31 @@ public class Reservation { @Column(name = "party_size", nullable = false) private Integer partySize; - public void markUpdated(LocalDateTime ts, ReservationStatus status) { - this.status = status; - this.updatedAt = ts; + public void markUpdated(LocalDateTime updatedAt, ReservationStatus newStatus) { + if (this.status == newStatus) { + switch (newStatus) { + case CONFIRMED -> throw new ReservationAlreadyConfirmedException(); + case CANCELLED -> throw new ReservationAlreadyCancelledException(); + default -> {} + } + } + + if (!isValidTransition(this.status, newStatus)) { + throw new InvalidReservationStatusTransitionException(this.status, newStatus); + } + this.status = newStatus; + this.updatedAt = updatedAt; + } + + private boolean isValidTransition(ReservationStatus current, ReservationStatus target) { + return switch (current) { + case WAITING -> target == ReservationStatus.CALLING + || target == ReservationStatus.CONFIRMED + || target == ReservationStatus.CANCELLED; + case CALLING -> target == ReservationStatus.CONFIRMED + || target == ReservationStatus.CANCELLED; + case CONFIRMED -> target == ReservationStatus.CANCELLED; + case CANCELLED -> false; + }; } } From 0569690fb50a5bb1afc833e436f9d4b0f5ed56d9 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 16:46:55 +0900 Subject: [PATCH 16/27] =?UTF-8?q?refactor:=20RESERVATION=5FDATA=5FINCONSIS?= =?UTF-8?q?TENCY=20redis=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/nowait/common/exception/ErrorMessage.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java index 9edb952..1f61d39 100644 --- a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java +++ b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java @@ -36,10 +36,11 @@ public enum ErrorMessage { INVALID_RESERVATION_STATUS_TRANSITION("유효하지 않은 예약 상태 변경입니다. (현재: %s, 요청: %s)", "reservation008"), RESERVATION_ALREADY_CONFIRMED("이미 확정된 예약입니다.", "reservation009"), RESERVATION_ALREADY_CANCELLED("이미 취소된 예약입니다.", "reservation010"), - RESERVATION_DATA_INCONSISTENCY("예약 데이터가 Redis와 DB 간 일치하지 않습니다. (%s)", "reservation011"), - UNSUPPORTED_RESERVATION_STATUS("지원하지 않는 예약 상태입니다: %s", "reservation012"), - INVALID_RESERVATION_PARAMETER("잘못된 예약 요청 파라미터입니다. (%s)", "reservation013"), + UNSUPPORTED_RESERVATION_STATUS("지원하지 않는 예약 상태입니다: %s", "reservation011"), + INVALID_RESERVATION_PARAMETER("잘못된 예약 요청 파라미터입니다. (%s)", "reservation012"), + // redis + RESERVATION_DATA_INCONSISTENCY("예약 데이터가 Redis와 DB 간 일치하지 않습니다. (%s)", "redis001"), // bookmark DUPLICATE_BOOKMARK("이미 북마크한 주점입니다.", "bookmark001"), From 78afb19705e300c039a9b89041cd7510211bcca2 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 16:47:12 +0900 Subject: [PATCH 17/27] =?UTF-8?q?feat:=20buildReservationUserKey=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 --- .../com/nowait/domaincoreredis/common/util/RedisKeyUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java index f40d569..1f81d2f 100644 --- a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java @@ -76,6 +76,10 @@ public static String buildReservationNumberKey(Long storeId) { return String.format("reservation:number:%d", storeId); } + public static String buildReservationUserKey(Long storeId) { + return String.format("reservation:user:%d", storeId); + } + /** * 대기 호출 시각(hash)에 사용할 키 접두사 */ From 342c8ccdf5248949b3f1a73f68a4030a44fa63dc Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 16:47:40 +0900 Subject: [PATCH 18/27] =?UTF-8?q?feat:=20=EC=98=88=EC=95=BD=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EA=B8=B0=EB=B0=98=20=EC=83=81=ED=83=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=A1=9C=EC=A7=81=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReservationController.java | 18 ++ .../service/ReservationService.java | 156 +++++++++++++++++- 2 files changed, 172 insertions(+), 2 deletions(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/controller/ReservationController.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/controller/ReservationController.java index d962de4..3d5eaf7 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/controller/ReservationController.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/controller/ReservationController.java @@ -96,4 +96,22 @@ public ResponseEntity updateEntry( )); } + @PatchMapping("/admin/{storeId}/{reservationNumber}") + @Operation(summary = "예약팀 상태 업데이트 처리", description = "특정 예약에 대한 입장 완료 처리") + @ApiResponse(responseCode = "200", description = "예약팀 상태 변경 : CALLING -> CONFIRMED") + public ResponseEntity updateEntryWithReservationNumber( + @PathVariable Long storeId, + @PathVariable String reservationNumber, + @RequestBody ReservationStatusRequest request, + @AuthenticationPrincipal MemberDetails memberDetails + ) { + EntryStatusResponseDto response = reservationService.processEntryStatusByReservationNumber(storeId, reservationNumber, memberDetails, request.getStatus()); + return ResponseEntity + .status(HttpStatus.OK) + .body( + ApiUtils.success( + response + )); + } + } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java index eae6fdf..4d728c1 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java @@ -29,7 +29,7 @@ import com.nowait.domaincorerdb.reservation.entity.Reservation; import com.nowait.domaincorerdb.reservation.exception.InvalidReservationParameterException; import com.nowait.domaincorerdb.reservation.exception.InvalidReservationStatusTransitionException; -import com.nowait.domaincorerdb.reservation.exception.ReservationDataInconsistencyException; +import com.nowait.domaincoreredis.reservation.exception.ReservationDataInconsistencyException; import com.nowait.domaincorerdb.reservation.exception.ReservationNotFoundException; import com.nowait.domaincorerdb.reservation.exception.ReservationUpdateUnauthorizedException; import com.nowait.domaincorerdb.reservation.exception.ReservationViewUnauthorizedException; @@ -233,7 +233,7 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me } if (reservationNumber == null || currStatus == null) { - throw new ReservationDataInconsistencyException( + new ReservationDataInconsistencyException( String.format("storeId=%d, userId=%s, reservationNumber=%s, status=%s, partySize=%s", storeId, userId, reservationNumber, currStatus, partySize) ); @@ -356,6 +356,158 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me } } + + @Transactional + public EntryStatusResponseDto processEntryStatusByReservationNumber(Long storeId, String reservationNumber, + MemberDetails member, ReservationStatus newStatus) { + + User user = getUser(member); + validateUpdateAuthorization(user, storeId); + + if (reservationNumber == null || reservationNumber.isBlank()) { + throw new InvalidReservationParameterException("reservationNumber 값이 비어있습니다."); + } + + // Redis에서 userId 역추적 + String userId = waitingRedisRepository.getUserIdByReservationNumber(storeId, reservationNumber); + if (userId == null) { + throw new ReservationDataInconsistencyException( + String.format("storeId=%d, reservationNumber=%s 에 해당하는 userId를 찾을 수 없습니다.", storeId, reservationNumber) + ); + } + + String queueKey = RedisKeyUtils.buildWaitingKeyPrefix() + storeId; + + String currStatus = waitingRedisRepository.getWaitingStatus(storeId, userId); + Double score = redisTemplate.opsForZSet().score(queueKey, userId); + Integer partySize = waitingRedisRepository.getWaitingPartySize(storeId, userId); + + + if (partySize == null || partySize <= 0) { + throw new InvalidReservationParameterException("partySize가 유효하지 않습니다. (storeId=" + storeId + ", reservationNumber=" + reservationNumber + ")"); + } + + if (currStatus == null) { + throw new ReservationDataInconsistencyException( + String.format("storeId=%d, reservationNumber=%s 의 상태값을 찾을 수 없습니다.", storeId, reservationNumber) + ); + } + + LocalDateTime requestedAt = score != null + ? Instant.ofEpochMilli(score.longValue()).atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime() + : LocalDateTime.now(); + LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Seoul")); + + switch (newStatus) { + case CALLING: + if (!ReservationStatus.WAITING.name().equals(currStatus)) { + throw new InvalidReservationStatusTransitionException( + ReservationStatus.valueOf(currStatus), ReservationStatus.CALLING + ); + } + waitingRedisRepository.setWaitingStatus(storeId, userId, ReservationStatus.CALLING.name()); + waitingRedisRepository.setWaitingCalledAt(storeId, userId, + now.atZone(ZoneId.of("Asia/Seoul")).toInstant().toEpochMilli()); + + return EntryStatusResponseDto.builder() + .reservationNumber(reservationNumber) + .userId(userId) + .partySize(partySize) + .userName(userRepository.getReferenceById(Long.valueOf(userId)).getNickname()) + .createdAt(requestedAt) + .status("CALLING") + .updatedAt(now) + .message("호출되었습니다.") + .build(); + + case CONFIRMED: + // 1) 기존 대기 중이거나 호출 중일 때: Redis → DB 최초 저장 + if (ReservationStatus.WAITING.name().equals(currStatus) || ReservationStatus.CALLING.name() + .equals(currStatus)) { + + if (reservationNumber != null) { + waitingPermitLuaRepository.removeActiveMember( + userId, String.valueOf(storeId), reservationNumber + ); + } + + // 새 Reservation 생성 & 저장 + Reservation r = Reservation.builder() + .reservationNumber(reservationNumber) + .store(storeRepository.getReferenceById(storeId)) + .user(userRepository.getReferenceById(Long.valueOf(userId))) + .partySize(partySize) + .requestedAt(requestedAt) + .updatedAt(LocalDateTime.now(ZoneId.of("Asia/Seoul"))) + .status(ReservationStatus.valueOf(currStatus)) + .build(); + + // 호출 시각 반영 + r.markUpdated(LocalDateTime.now(), ReservationStatus.CONFIRMED); + Reservation saved = reservationRepository.save(r); + // Redis 전부 삭제 + waitingRedisRepository.deleteWaiting(storeId, userId); + return EntryStatusResponseDto.fromEntity(saved); + } else { + if (reservationNumber != null) { + try { + waitingPermitLuaRepository.removeActiveMember(userId, String.valueOf(storeId), + reservationNumber); + } catch (Exception ignore) { + } + } + + // 2) 이미 취소(CANCELLED)된 경우: DB 레코드 찾아 바로 CONFIRMED 로 전환 + LocalDateTime start = LocalDate.now().atStartOfDay(); + LocalDateTime end = LocalDate.now().atTime(LocalTime.MAX); + Reservation existing = reservationRepository + .findFirstByStore_StoreIdAndUserIdAndStatusInAndRequestedAtBetweenOrderByRequestedAtDesc( + storeId, + Long.valueOf(userId), + List.of(ReservationStatus.CANCELLED), + start, + end + ).orElseThrow(() -> new ReservationDataInconsistencyException( + String.format("취소된 예약이 DB에 존재하지 않습니다. (storeId=%d, userId=%s)", storeId, userId) + )); + + existing.markUpdated(LocalDateTime.now(ZoneId.of("Asia/Seoul")), ReservationStatus.CONFIRMED); + Reservation saved = reservationRepository.save(existing); + return EntryStatusResponseDto.fromEntity(saved); + } + + case CANCELLED: + if (!(ReservationStatus.WAITING.name().equals(currStatus) + || ReservationStatus.CALLING.name().equals(currStatus))) { + throw new InvalidReservationStatusTransitionException( + ReservationStatus.valueOf(currStatus), ReservationStatus.CANCELLED + ); + } + + waitingPermitLuaRepository.removeActiveMember(userId, String.valueOf(storeId), reservationNumber); + + Reservation r = Reservation.builder() + .reservationNumber(reservationNumber) + .store(storeRepository.getReferenceById(storeId)) + .user(userRepository.getReferenceById(Long.valueOf(userId))) + .partySize(partySize) + .requestedAt(requestedAt) + .updatedAt(now) + .status(ReservationStatus.valueOf(currStatus)) + .build(); + + r.markUpdated(now, ReservationStatus.CANCELLED); + Reservation saved = reservationRepository.save(r); + waitingRedisRepository.deleteWaiting(storeId, userId); + + return EntryStatusResponseDto.fromEntity(saved); + + default: + throw new UnsupportedReservationStatusException(newStatus); + } + } + + /** * 공통 메서드 */ From 352b561d8a0109d89876f20707a4aa022553af59 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 16:47:50 +0900 Subject: [PATCH 19/27] =?UTF-8?q?feat:=20=EC=98=88=EC=95=BD=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EA=B8=B0=EB=B0=98=20=EC=83=81=ED=83=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=A1=9C=EC=A7=81=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/WaitingRedisRepository.java | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/repository/WaitingRedisRepository.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/repository/WaitingRedisRepository.java index 0799786..f9eb921 100644 --- a/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/repository/WaitingRedisRepository.java +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/repository/WaitingRedisRepository.java @@ -25,7 +25,6 @@ public List> getAllWaitingWithScore(Long store return waitingSet == null ? List.of() : new ArrayList<>(waitingSet); } - public List getAllWaitingUserIds(Long storeId) { String queueKey = RedisKeyUtils.buildWaitingKeyPrefix() + storeId; // 0부터 -1까지: 전체 범위 @@ -33,7 +32,6 @@ public List getAllWaitingUserIds(Long storeId) { return userIds == null ? List.of() : new ArrayList<>(userIds); } - // 상태값 저장 및 변경 public void setWaitingStatus(Long storeId, String userId, String status) { String statusKey = RedisKeyUtils.buildWaitingStatusKeyPrefix() + storeId; @@ -54,7 +52,7 @@ public Integer getWaitingPartySize(Long storeId, String userId) { return value == null ? null : Integer.valueOf(value.toString()); } - // ReservationNumber 조회 + // userId → reservationNumber 조회 public String getReservationId(Long storeId, String userId) { String status = getWaitingStatus(storeId, userId); if (!"WAITING".equals(status) && !"CALLING".equals(status)) { @@ -67,7 +65,27 @@ public String getReservationId(Long storeId, String userId) { return val != null ? val.toString() : null; } + // reservationNumber → userId 조회 + public String getUserIdByReservationNumber(Long storeId, String reservationNumber) { + String userMapKey = RedisKeyUtils.buildReservationUserKey(storeId); + Object val = redisTemplate.opsForHash().get(userMapKey, reservationNumber); + return val == null ? null : val.toString(); + } + public void deleteWaiting(Long storeId, String userId) { + String numberMapKey = RedisKeyUtils.buildReservationNumberKey(storeId); + String userMapKey = RedisKeyUtils.buildReservationUserKey(storeId); + + Object reservationNumber = redisTemplate.opsForHash().get(numberMapKey, userId); + + // userId → reservationNumber 삭제 + redisTemplate.opsForHash().delete(numberMapKey, userId); + + // reservationNumber → userId 삭제 + if (reservationNumber != null) { + redisTemplate.opsForHash().delete(userMapKey, reservationNumber); + } + String statusKey = RedisKeyUtils.buildWaitingStatusKeyPrefix() + storeId; redisTemplate.opsForHash().delete(statusKey, userId); @@ -77,11 +95,8 @@ public void deleteWaiting(Long storeId, String userId) { String partyKey = RedisKeyUtils.buildWaitingPartySizeKeyPrefix() + storeId; redisTemplate.opsForHash().delete(partyKey, userId); - String numberMapKey = RedisKeyUtils.buildReservationNumberKey(storeId); - redisTemplate.opsForHash().delete(numberMapKey, userId); - - String key = RedisKeyUtils.buildWaitingCalledAtKeyPrefix() + storeId; - redisTemplate.opsForHash().delete(key, userId); + String calledAtKey = RedisKeyUtils.buildWaitingCalledAtKeyPrefix() + storeId; + redisTemplate.opsForHash().delete(calledAtKey, userId); } // 호출 시각 기록 From d51e03a25612cfd8f352238a89a0263463fe7ef1 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 16:47:55 +0900 Subject: [PATCH 20/27] =?UTF-8?q?feat:=20=EC=98=88=EC=95=BD=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EA=B8=B0=EB=B0=98=20=EC=83=81=ED=83=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=A1=9C=EC=A7=81=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reservation/repository/WaitingUserRedisRepository.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java index 50c84d8..d11e5a3 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java @@ -37,6 +37,7 @@ public String addToWaitingQueue(Long storeId, String userId, Integer partySize, String statusKey = RedisKeyUtils.buildWaitingStatusKeyPrefix() + storeId; String seqKey = RedisKeyUtils.buildReservationSeqKey(storeId); String numberMapKey = RedisKeyUtils.buildReservationNumberKey(storeId); + String userMapKey = RedisKeyUtils.buildReservationUserKey(storeId); Boolean added = redisTemplate.opsForZSet().addIfAbsent(queueKey, userId, timestamp); String reservationId; @@ -47,7 +48,7 @@ public String addToWaitingQueue(Long storeId, String userId, Integer partySize, // 5) Hash에 저장 redisTemplate.opsForHash().put(numberMapKey, userId, reservationId); - + redisTemplate.opsForHash().put(userMapKey, reservationId, userId); // 6) 기존 partySize, status, TTL 설정 redisTemplate.opsForHash().put(partyKey, userId, partySize.toString()); redisTemplate.opsForHash().put(statusKey, userId, "WAITING"); From 230d2e5a596e2178058f234ea0e6607c9401f833 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 16:48:08 +0900 Subject: [PATCH 21/27] =?UTF-8?q?refactor:=20redis=20=EC=95=84=EB=9E=98?= =?UTF-8?q?=EB=A1=9C=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ReservationDataInconsistencyException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename nowait-domain/{domain-core-rdb/src/main/java/com/nowait/domaincorerdb => domain-redis/src/main/java/com/nowait/domaincoreredis}/reservation/exception/ReservationDataInconsistencyException.java (82%) diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationDataInconsistencyException.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/exception/ReservationDataInconsistencyException.java similarity index 82% rename from nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationDataInconsistencyException.java rename to nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/exception/ReservationDataInconsistencyException.java index 37cd13d..22cc515 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/exception/ReservationDataInconsistencyException.java +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/exception/ReservationDataInconsistencyException.java @@ -1,4 +1,4 @@ -package com.nowait.domaincorerdb.reservation.exception; +package com.nowait.domaincoreredis.reservation.exception; import com.nowait.common.exception.ErrorMessage; From cc3f2f66e117976d56dcb68b8e81c91578a3f139 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 18:48:00 +0900 Subject: [PATCH 22/27] =?UTF-8?q?refactor:=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/service/StoreServiceImpl.java | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java index abdc583..1f3d470 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java @@ -15,6 +15,8 @@ import com.nowait.common.enums.Role; import com.nowait.domaincorerdb.department.entity.Department; import com.nowait.domaincorerdb.department.repository.DepartmentRepository; +import com.nowait.domaincorerdb.order.exception.OrderUpdateUnauthorizedException; +import com.nowait.domaincorerdb.order.exception.OrderViewUnauthorizedException; import com.nowait.domaincorerdb.reservation.exception.ReservationUpdateUnauthorizedException; import com.nowait.domaincorerdb.store.entity.Store; import com.nowait.domaincorerdb.store.entity.StoreImage; @@ -57,10 +59,9 @@ public StoreCreateResponse createStore(StoreCreateRequest request) { @Transactional(readOnly = true) public StoreDetailReadResponse getStoreByStoreId(Long storeId, MemberDetails memberDetails) { if (storeId == null) throw new StoreParamEmptyException(); - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { - throw new StoreViewUnauthorizedException(); - } + User user = getUser(memberDetails); + validateViewAuthorization(user, storeId); + Store store = storeRepository.findByStoreIdAndDeletedFalse(storeId) .orElseThrow(StoreNotFoundException::new); @@ -79,11 +80,11 @@ public StoreDetailReadResponse getStoreByStoreId(Long storeId, MemberDetails mem @Override @Transactional public StoreReadDto updateStore(Long storeId, StoreUpdateRequest request, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); if (storeId == null || request == null) throw new StoreParamEmptyException(); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { - throw new StoreUpdateUnauthorizedException(); - } + + User user = getUser(memberDetails); + validateUpdateAuthorization(user, storeId); + Store store = storeRepository.findByStoreIdAndDeletedFalse(storeId) .orElseThrow(StoreNotFoundException::new); @@ -109,13 +110,13 @@ public StoreReadDto updateStore(Long storeId, StoreUpdateRequest request, Member @Override @Transactional public String deleteStore(Long storeId, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + if (storeId == null) { throw new StoreParamEmptyException(); } - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { - throw new StoreDeleteUnauthorizedException(); - } + + User user = getUser(memberDetails); + validateUpdateAuthorization(user, storeId); Store store = storeRepository.findByStoreIdAndDeletedFalse(storeId) .orElseThrow(StoreNotFoundException::new); @@ -134,4 +135,22 @@ public Boolean toggleActive(Long storeId) { store.toggleActive(); return store.getIsActive(); } + + private void validateViewAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) + || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { + throw new OrderViewUnauthorizedException(); + } + } + + private void validateUpdateAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) + || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { + throw new OrderUpdateUnauthorizedException(); + } + } + + private User getUser(MemberDetails memberDetails) { + return userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + } } From c40461d8d9453993b6ae01f8049f5b37843a8a0e Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 18:50:02 +0900 Subject: [PATCH 23/27] =?UTF-8?q?refactor:=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nowait/applicationadmin/store/service/StoreServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java index 1f3d470..0892bd0 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java @@ -130,7 +130,7 @@ public String deleteStore(Long storeId, MemberDetails memberDetails) { @Transactional public Boolean toggleActive(Long storeId) { Store store = storeRepository.findById(storeId) - .orElseThrow(() -> new IllegalArgumentException("해당 스토어가 존재하지 않습니다.")); + .orElseThrow(StoreNotFoundException::new); store.toggleActive(); return store.getIsActive(); From 41f64d91f9c12fe93ced05dcc8a67021914f5ff7 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 19:13:12 +0900 Subject: [PATCH 24/27] =?UTF-8?q?feat:=20=EB=A9=94=EB=89=B4=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EB=B3=B4?= =?UTF-8?q?=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 45 +++++++++++ .../menu/service/MenuService.java | 77 ++++++++++++------- .../store/service/StoreServiceImpl.java | 4 +- .../nowait/common/exception/ErrorMessage.java | 5 ++ .../MenuAlreadyDeletedException.java | 9 +++ .../MenuCrossStoreConflictException.java | 9 +++ .../exception/MenuDuplicateIdException.java | 9 +++ .../MenuInvalidSortOrderException.java | 9 +++ .../MenuToggleUnauthorizedException.java | 9 +++ 9 files changed, 146 insertions(+), 30 deletions(-) create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuAlreadyDeletedException.java create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuCrossStoreConflictException.java create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuDuplicateIdException.java create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuInvalidSortOrderException.java create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuToggleUnauthorizedException.java diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java index 5c8de09..1c08cc7 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java @@ -23,11 +23,16 @@ import com.nowait.common.exception.ErrorMessage; import com.nowait.common.exception.ErrorResponse; import com.nowait.discord.service.DiscordAlarmService; +import com.nowait.domaincorerdb.menu.exception.MenuAlreadyDeletedException; import com.nowait.domaincorerdb.menu.exception.MenuCreationUnauthorizedException; +import com.nowait.domaincorerdb.menu.exception.MenuCrossStoreConflictException; import com.nowait.domaincorerdb.menu.exception.MenuDeleteUnauthorizedException; +import com.nowait.domaincorerdb.menu.exception.MenuDuplicateIdException; import com.nowait.domaincorerdb.menu.exception.MenuImageEmptyException; +import com.nowait.domaincorerdb.menu.exception.MenuInvalidSortOrderException; import com.nowait.domaincorerdb.menu.exception.MenuNotFoundException; import com.nowait.domaincorerdb.menu.exception.MenuParamEmptyException; +import com.nowait.domaincorerdb.menu.exception.MenuToggleUnauthorizedException; import com.nowait.domaincorerdb.menu.exception.MenuUpdateUnauthorizedException; import com.nowait.domaincorerdb.menu.exception.MenuViewUnauthorizedException; import com.nowait.domaincorerdb.order.exception.DepositorNameTooLongException; @@ -331,6 +336,14 @@ public ErrorResponse userWaitingLimitExceededException(UserWaitingLimitExceededE /** * 메뉴 관련 예외 처리 */ + @ResponseStatus(CONFLICT) + @ExceptionHandler(MenuAlreadyDeletedException.class) + public ErrorResponse menuAlreadyDeletedException(MenuAlreadyDeletedException e, WebRequest request) { + alarm(e, request); + log.error("menuAlreadyDeletedException", e); + return new ErrorResponse(e.getMessage(), MENU_ALREADY_DELETED.getCode()); + } + @ResponseStatus(FORBIDDEN) @ExceptionHandler(MenuCreationUnauthorizedException.class) public ErrorResponse menuCreationUnauthorizedException(MenuCreationUnauthorizedException e, WebRequest request) { @@ -339,6 +352,14 @@ public ErrorResponse menuCreationUnauthorizedException(MenuCreationUnauthorizedE return new ErrorResponse(e.getMessage(), MENU_CREATION_UNAUTHORIZED.getCode()); } + @ResponseStatus(CONFLICT) + @ExceptionHandler(MenuCrossStoreConflictException.class) + public ErrorResponse menuCrossStoreConflictException(MenuCrossStoreConflictException e, WebRequest request) { + alarm(e, request); + log.error("menuCrossStoreConflictException", e); + return new ErrorResponse(e.getMessage(), MENU_CROSS_STORE_CONFLICT.getCode()); + } + @ResponseStatus(FORBIDDEN) @ExceptionHandler(MenuDeleteUnauthorizedException.class) public ErrorResponse menuDeleteUnauthorizedException(MenuDeleteUnauthorizedException e, WebRequest request) { @@ -347,6 +368,14 @@ public ErrorResponse menuDeleteUnauthorizedException(MenuDeleteUnauthorizedExcep return new ErrorResponse(e.getMessage(), MENU_DELETE_UNAUTHORIZED.getCode()); } + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(MenuDuplicateIdException.class) + public ErrorResponse menuDuplicateIdException(MenuDuplicateIdException e, WebRequest request) { + alarm(e, request); + log.error("menuDuplicateIdException", e); + return new ErrorResponse(e.getMessage(), MENU_DUPLICATE_ID.getCode()); + } + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(MenuImageEmptyException.class) public ErrorResponse menuImageEmptyException(MenuImageEmptyException e, WebRequest request) { @@ -363,6 +392,14 @@ public ErrorResponse menuNotFoundException(MenuNotFoundException e, WebRequest r return new ErrorResponse(e.getMessage(), MENU_NOT_FOUND.getCode()); } + @ResponseStatus(BAD_REQUEST) + @ExceptionHandler(MenuInvalidSortOrderException.class) + public ErrorResponse menuInvalidSortOrderException(MenuInvalidSortOrderException e, WebRequest request) { + alarm(e, request); + log.error("menuInvalidSortOrderException", e); + return new ErrorResponse(e.getMessage(), MENU_INVALID_SORT_ORDER.getCode()); + } + @ResponseStatus(BAD_REQUEST) @ExceptionHandler(MenuParamEmptyException.class) public ErrorResponse menuParamEmptyException(MenuParamEmptyException e, WebRequest request) { @@ -379,6 +416,14 @@ public ErrorResponse menuUpdateUnauthorizedException(MenuUpdateUnauthorizedExcep return new ErrorResponse(e.getMessage(), MENU_UPDATE_UNAUTHORIZED.getCode()); } + @ResponseStatus(FORBIDDEN) + @ExceptionHandler(MenuToggleUnauthorizedException.class) + public ErrorResponse menuToggleUnauthorizedException(MenuToggleUnauthorizedException e, WebRequest request) { + alarm(e, request); + log.error("menuToggleUnauthorizedException", e); + return new ErrorResponse(e.getMessage(), MENU_TOGGLE_UNAUTHORIZED.getCode()); + } + @ResponseStatus(FORBIDDEN) @ExceptionHandler(MenuViewUnauthorizedException.class) public ErrorResponse menuViewUnauthorizedException(MenuViewUnauthorizedException e, WebRequest request) { diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/service/MenuService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/service/MenuService.java index 2bdd558..50805e1 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/service/MenuService.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/service/MenuService.java @@ -17,13 +17,18 @@ import com.nowait.domaincorerdb.menu.entity.Menu; import com.nowait.domaincorerdb.menu.entity.MenuImage; import com.nowait.domaincorerdb.menu.exception.MenuCreationUnauthorizedException; +import com.nowait.domaincorerdb.menu.exception.MenuCrossStoreConflictException; import com.nowait.domaincorerdb.menu.exception.MenuDeleteUnauthorizedException; +import com.nowait.domaincorerdb.menu.exception.MenuDuplicateIdException; +import com.nowait.domaincorerdb.menu.exception.MenuInvalidSortOrderException; import com.nowait.domaincorerdb.menu.exception.MenuNotFoundException; import com.nowait.domaincorerdb.menu.exception.MenuParamEmptyException; import com.nowait.domaincorerdb.menu.exception.MenuUpdateUnauthorizedException; import com.nowait.domaincorerdb.menu.exception.MenuViewUnauthorizedException; import com.nowait.domaincorerdb.menu.repository.MenuImageRepository; import com.nowait.domaincorerdb.menu.repository.MenuRepository; +import com.nowait.domaincorerdb.order.exception.OrderUpdateUnauthorizedException; +import com.nowait.domaincorerdb.order.exception.OrderViewUnauthorizedException; import com.nowait.domaincorerdb.user.entity.MemberDetails; import com.nowait.domaincorerdb.user.entity.User; import com.nowait.domaincorerdb.user.exception.UserNotFoundException; @@ -42,12 +47,10 @@ public class MenuService { @Transactional public MenuCreateResponse createMenu(MenuCreateRequest request, MemberDetails memberDetails) { // 사용자 정보 가져오기 - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); - + User user = getUser(memberDetails); // 사용자 역할이 SUPER_ADMIN이거나, storeId가 일치하는지 확인 - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(request.getStoreId())) { - throw new MenuCreationUnauthorizedException(); - } + validateViewAuthorization(user, request.getStoreId()); + // 메뉴 생성 로직 Menu toSave = request.toEntity(); Menu saved = menuRepository.save(toSave); @@ -58,10 +61,10 @@ public MenuCreateResponse createMenu(MenuCreateRequest request, MemberDetails me @Transactional(readOnly = true) public MenuReadResponse getAllMenusByStoreId(Long storeId, MemberDetails memberDetails) { // 사용자 정보 가져오기 - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); - + User user = getUser(memberDetails); // 사용자 역할이 SUPER_ADMIN이거나, storeId가 일치하는지 확인 - validateMenuViewAuthorization(user, storeId); + validateViewAuthorization(user, storeId); + List menus = menuRepository.findAllByStoreIdAndDeletedFalseOrderBySortOrder(storeId); List menuReadResponse = menus.stream() @@ -83,11 +86,10 @@ public MenuReadDto getMenuById(Long storeId, Long menuId, MemberDetails memberDe throw new MenuParamEmptyException(); } // 사용자 정보 가져오기 - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); - Menu menu = menuRepository.findByStoreIdAndIdAndDeletedFalse(storeId, menuId) - .orElseThrow(MenuNotFoundException::new); + User user = getUser(memberDetails); + Menu menu = getMenu(menuId); // 사용자 역할이 SUPER_ADMIN이거나, storeId가 일치하는지 확인 - validateMenuViewAuthorization(user, menu.getStoreId()); + validateViewAuthorization(user, menu.getStoreId()); List images = menuImageRepository.findByMenu(menu); List imageDto = images.stream() @@ -100,13 +102,10 @@ public MenuReadDto getMenuById(Long storeId, Long menuId, MemberDetails memberDe @Transactional public MenuReadDto updateMenu(Long menuId, MenuUpdateRequest request, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); - Menu menu = menuRepository.findByIdAndDeletedFalse(menuId) - .orElseThrow(MenuNotFoundException::new); + User user = getUser(memberDetails); + Menu menu = getMenu(menuId); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(menu.getStoreId())) { - throw new MenuUpdateUnauthorizedException(); - } + validateUpdateAuthorization(user, menu.getStoreId()); menu.updateInfo( request.getAdminDisplayName(), @@ -138,11 +137,11 @@ public String updateMenuSortOrder(List requests, MemberDe } if (requests.stream().map(MenuSortUpdateRequest::getMenuId).distinct().count() != requests.size()) { - throw new IllegalArgumentException("중복된 메뉴 ID가 포함되어 있습니다."); + throw new MenuDuplicateIdException(); } if (requests.stream().anyMatch(r -> r.getSortOrder() == null || r.getSortOrder() < 0)) { - throw new IllegalArgumentException("잘못된 정렬 순서가 포함되어 있습니다. 정렬 순서는 0 이상의 정수여야 합니다."); + throw new MenuInvalidSortOrderException(); } List ids = requests.stream().map(MenuSortUpdateRequest::getMenuId).toList(); @@ -153,7 +152,7 @@ public String updateMenuSortOrder(List requests, MemberDe Long storeId = menus.get(0).getStoreId(); if (!menus.stream().allMatch(m -> storeId.equals(m.getStoreId()))) { - throw new IllegalArgumentException("다른 매장의 메뉴가 있습니다."); + throw new MenuCrossStoreConflictException(); } Map idToSort = requests.stream() @@ -169,12 +168,10 @@ public String updateMenuSortOrder(List requests, MemberDe @Transactional public String deleteMenu(Long menuId, MemberDetails memberDetails) { - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); - Menu menu = menuRepository.findById(menuId).orElseThrow(MenuNotFoundException::new); + User user = getUser(memberDetails); + Menu menu = getMenu(menuId); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(menu.getStoreId())) { - throw new MenuDeleteUnauthorizedException(); - } + validateDeleteAuthorization(user, menu.getStoreId()); menu.markAsDeleted(); menuRepository.save(menu); @@ -191,10 +188,34 @@ public Boolean toggleSoldOut(Long menuId) { return menu.getIsSoldOut(); } - private static void validateMenuViewAuthorization(User user, Long storeId) { - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { + + private void validateViewAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) + || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { throw new MenuViewUnauthorizedException(); } } + private void validateUpdateAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) + || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { + throw new MenuUpdateUnauthorizedException(); + } + } + + private void validateDeleteAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) + || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { + throw new MenuDeleteUnauthorizedException(); + } + } + + private User getUser(MemberDetails memberDetails) { + return userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + } + + private Menu getMenu(Long menuId) { + return menuRepository.findByIdAndDeletedFalse(menuId) + .orElseThrow(MenuNotFoundException::new); + } } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java index 0892bd0..ea6b2da 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java @@ -139,14 +139,14 @@ public Boolean toggleActive(Long storeId) { private void validateViewAuthorization(User user, Long storeId) { if (!(Role.SUPER_ADMIN.equals(user.getRole()) || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { - throw new OrderViewUnauthorizedException(); + throw new StoreViewUnauthorizedException(); } } private void validateUpdateAuthorization(User user, Long storeId) { if (!(Role.SUPER_ADMIN.equals(user.getRole()) || (Role.MANAGER.equals(user.getRole()) && storeId.equals(user.getStoreId())))) { - throw new OrderUpdateUnauthorizedException(); + throw new StoreUpdateUnauthorizedException(); } } diff --git a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java index 1f61d39..494df3d 100644 --- a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java +++ b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java @@ -55,6 +55,11 @@ public enum ErrorMessage { MENU_VIEW_UNAUTHORIZED("메뉴 보기 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "menu004"), MENU_UPDATE_UNAUTHORIZED("메뉴 수정 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "menu005"), MENU_DELETE_UNAUTHORIZED("메뉴 삭제 권한이 없습니다.(슈퍼계정 or 주점 관리자만 가능)", "menu006"), + MENU_INVALID_SORT_ORDER("잘못된 정렬 순서가 포함되어 있습니다. 정렬 순서는 0 이상의 정수여야 합니다.", "menu007"), + MENU_DUPLICATE_ID("중복된 메뉴 ID가 포함되어 있습니다.", "menu008"), + MENU_CROSS_STORE_CONFLICT("서로 다른 매장의 메뉴가 포함되어 있습니다.", "menu009"), + MENU_ALREADY_DELETED("이미 삭제된 메뉴입니다.", "menu010"), + MENU_TOGGLE_UNAUTHORIZED("메뉴 품절 상태 변경 권한이 없습니다.", "menu011"), // store STORE_PARAMETER_EMPTY("주점 생성 시 파라미터 정보가 없습니다.", "store001"), diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuAlreadyDeletedException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuAlreadyDeletedException.java new file mode 100644 index 0000000..0a2202a --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuAlreadyDeletedException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincorerdb.menu.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class MenuAlreadyDeletedException extends RuntimeException { + public MenuAlreadyDeletedException() { + super(ErrorMessage.MENU_ALREADY_DELETED.getMessage()); + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuCrossStoreConflictException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuCrossStoreConflictException.java new file mode 100644 index 0000000..70caeae --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuCrossStoreConflictException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincorerdb.menu.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class MenuCrossStoreConflictException extends RuntimeException { + public MenuCrossStoreConflictException() { + super(ErrorMessage.MENU_CROSS_STORE_CONFLICT.getMessage()); + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuDuplicateIdException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuDuplicateIdException.java new file mode 100644 index 0000000..761595a --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuDuplicateIdException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincorerdb.menu.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class MenuDuplicateIdException extends RuntimeException { + public MenuDuplicateIdException() { + super(ErrorMessage.MENU_DUPLICATE_ID.getMessage()); + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuInvalidSortOrderException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuInvalidSortOrderException.java new file mode 100644 index 0000000..55570c3 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuInvalidSortOrderException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincorerdb.menu.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class MenuInvalidSortOrderException extends RuntimeException { + public MenuInvalidSortOrderException() { + super(ErrorMessage.MENU_INVALID_SORT_ORDER.getMessage()); + } +} diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuToggleUnauthorizedException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuToggleUnauthorizedException.java new file mode 100644 index 0000000..9dea000 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/exception/MenuToggleUnauthorizedException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincorerdb.menu.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class MenuToggleUnauthorizedException extends RuntimeException { + public MenuToggleUnauthorizedException() { + super(ErrorMessage.MENU_TOGGLE_UNAUTHORIZED.getMessage()); + } +} From 814cbce0039bfca4e9deecb3a310ff6d376f017a Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 19:28:38 +0900 Subject: [PATCH 25/27] =?UTF-8?q?feat:=20=EA=B2=B0=EC=A0=9C=EC=88=98?= =?UTF-8?q?=EB=8B=A8=20=EA=B4=80=EB=A0=A8=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/StorePaymentServiceImpl.java | 48 ++++++++++++++----- ...orePaymentDeleteUnauthorizedException.java | 9 ++++ 2 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/storePayment/exception/StorePaymentDeleteUnauthorizedException.java diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentServiceImpl.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentServiceImpl.java index a785282..95aab0e 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentServiceImpl.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/service/StorePaymentServiceImpl.java @@ -13,6 +13,7 @@ import com.nowait.domaincorerdb.storepayment.entity.StorePayment; import com.nowait.domaincorerdb.storepayment.exception.StorePaymentAlreadyExistsException; import com.nowait.domaincorerdb.storepayment.exception.StorePaymentCreationUnauthorizedException; +import com.nowait.domaincorerdb.storepayment.exception.StorePaymentDeleteUnauthorizedException; import com.nowait.domaincorerdb.storepayment.exception.StorePaymentNotFoundException; import com.nowait.domaincorerdb.storepayment.exception.StorePaymentParamEmptyException; import com.nowait.domaincorerdb.storepayment.exception.StorePaymentUpdateUnauthorizedException; @@ -37,14 +38,13 @@ public class StorePaymentServiceImpl implements StorePaymentService { public StorePaymentCreateResponse createStorePayment(StorePaymentCreateRequest request, MemberDetails memberDetails) { if (request == null) throw new StorePaymentParamEmptyException(); - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + User user = getUser(memberDetails); Long storeId = user.getStoreId(); if (storePaymentRepository.findByStoreId(storeId).isPresent()) { throw new StorePaymentAlreadyExistsException(); } - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { - throw new StorePaymentCreationUnauthorizedException(); - } + validateCreationAuthorization(user, storeId); + StorePayment toSave = request.toEntity(storeId); StorePayment saved = storePaymentRepository.save(toSave); @@ -56,11 +56,9 @@ public StorePaymentCreateResponse createStorePayment(StorePaymentCreateRequest r public Optional getStorePaymentByStoreId(MemberDetails memberDetails) { if (memberDetails == null) throw new StorePaymentParamEmptyException(); - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + User user = getUser(memberDetails); Long storeId = user.getStoreId(); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { - throw new StorePaymentViewUnauthorizedException(); - } + validateViewAuthorization(user, storeId); return storePaymentRepository.findByStoreId(storeId) .map(StorePaymentReadDto::fromEntity); @@ -71,11 +69,9 @@ public Optional getStorePaymentByStoreId(MemberDetails memb public StorePaymentReadDto updateStorePayment(StorePaymentUpdateRequest request, MemberDetails memberDetails) { if (request == null) throw new StorePaymentParamEmptyException(); - User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + User user = getUser(memberDetails); Long storeId = user.getStoreId(); - if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { - throw new StorePaymentUpdateUnauthorizedException(); - } + validateUpdateAuthorization(user, storeId); StorePayment storePayment = storePaymentRepository.findByStoreId(storeId) .orElseThrow(StorePaymentNotFoundException::new); @@ -89,4 +85,32 @@ public StorePaymentReadDto updateStorePayment(StorePaymentUpdateRequest request, return StorePaymentReadDto.fromEntity(storePayment); } + + private User getUser(MemberDetails memberDetails) { + return userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + } + + private void validateViewAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) || user.getStoreId().equals(storeId))) { + throw new StorePaymentViewUnauthorizedException(); + } + } + + private void validateCreationAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) || user.getStoreId().equals(storeId))) { + throw new StorePaymentCreationUnauthorizedException(); + } + } + + private void validateUpdateAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) || user.getStoreId().equals(storeId))) { + throw new StorePaymentUpdateUnauthorizedException(); + } + } + + private void validateDeleteAuthorization(User user, Long storeId) { + if (!(Role.SUPER_ADMIN.equals(user.getRole()) || user.getStoreId().equals(storeId))) { + throw new StorePaymentDeleteUnauthorizedException(); + } + } } diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/storePayment/exception/StorePaymentDeleteUnauthorizedException.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/storePayment/exception/StorePaymentDeleteUnauthorizedException.java new file mode 100644 index 0000000..c65f5e5 --- /dev/null +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/storePayment/exception/StorePaymentDeleteUnauthorizedException.java @@ -0,0 +1,9 @@ +package com.nowait.domaincorerdb.storepayment.exception; + +import com.nowait.common.exception.ErrorMessage; + +public class StorePaymentDeleteUnauthorizedException extends RuntimeException{ + public StorePaymentDeleteUnauthorizedException() { + super(ErrorMessage.STORE_PAYMENT_DELETE_UNAUTHORIZED.getMessage()); + } +} From 4378a71dd352a3ce5f51ffc23f5873052ea1425a Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 19:33:25 +0900 Subject: [PATCH 26/27] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 14 ++++++++++++++ .../controller/StorePaymentController.java | 1 + .../com/nowait/common/exception/ErrorMessage.java | 5 ++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java index 1c08cc7..730d9ab 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/exception/GlobalExceptionHandler.java @@ -562,6 +562,20 @@ public ErrorResponse menuCounterUpdateException(MenuCounterUpdateException e, We return new ErrorResponse(e.getMessage(), MENU_COUNTER_UPDATE.getCode()); } + /** + * * 그 외 예외처리 + */ + @ResponseStatus(INTERNAL_SERVER_ERROR) + @ExceptionHandler(Exception.class) + public ErrorResponse handleUnexpectedException(Exception e, WebRequest request) { + alarm(e, request); + log.error("handleUnexpectedException", e); + return new ErrorResponse( + ErrorMessage.UNEXPECTED_ERROR.getMessage(), + ErrorMessage.UNEXPECTED_ERROR.getCode() + ); + } + // 공통 에러 Map 생성 private static Map getErrors(MethodArgumentNotValidException e) { diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/controller/StorePaymentController.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/controller/StorePaymentController.java index 5bcf314..203aea7 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/controller/StorePaymentController.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/storePayment/controller/StorePaymentController.java @@ -5,6 +5,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PostMapping; diff --git a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java index 494df3d..754b4ba 100644 --- a/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java +++ b/nowait-common/src/main/java/com/nowait/common/exception/ErrorMessage.java @@ -87,7 +87,10 @@ public enum ErrorMessage { IMAGE_FILE_NOT_FOUND("DB에 해당 이미지 메타데이터가 존재하지 않습니다.", "image002"), // search - SEARCH_PARAMETER_EMPTY("검색어가 비어있습니다.", "search001"); + SEARCH_PARAMETER_EMPTY("검색어가 비어있습니다.", "search001"), + + // common + UNEXPECTED_ERROR("예상하지 못한 오류가 발생했습니다.", "common999"); private final String message; private final String code; From b8c90a9f43a28e8143e2813c83e6af88962a52bd Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Fri, 19 Sep 2025 19:57:38 +0900 Subject: [PATCH 27/27] =?UTF-8?q?feat:=20NPE=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=B3=B4=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nowait/applicationadmin/order/service/OrderService.java | 3 +++ .../reservation/service/ReservationService.java | 6 +++++- .../applicationadmin/store/service/StoreServiceImpl.java | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java index aaa3436..0ef84e7 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/service/OrderService.java @@ -147,6 +147,9 @@ private void validateUpdateAuthorization(User user, Long storeId) { } private User getUser(MemberDetails memberDetails) { + if (memberDetails == null) { + throw new OrderViewUnauthorizedException(); + } return userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); } } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java index 4d728c1..6860d13 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java @@ -29,6 +29,7 @@ import com.nowait.domaincorerdb.reservation.entity.Reservation; import com.nowait.domaincorerdb.reservation.exception.InvalidReservationParameterException; import com.nowait.domaincorerdb.reservation.exception.InvalidReservationStatusTransitionException; +import com.nowait.domaincorerdb.store.exception.StoreViewUnauthorizedException; import com.nowait.domaincoreredis.reservation.exception.ReservationDataInconsistencyException; import com.nowait.domaincorerdb.reservation.exception.ReservationNotFoundException; import com.nowait.domaincorerdb.reservation.exception.ReservationUpdateUnauthorizedException; @@ -233,7 +234,7 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me } if (reservationNumber == null || currStatus == null) { - new ReservationDataInconsistencyException( + throw new ReservationDataInconsistencyException( String.format("storeId=%d, userId=%s, reservationNumber=%s, status=%s, partySize=%s", storeId, userId, reservationNumber, currStatus, partySize) ); @@ -540,6 +541,9 @@ private void validateUpdateAuthorization(User user, Long storeId) { } private User getUser(MemberDetails memberDetails) { + if (memberDetails == null) { + throw new ReservationViewUnauthorizedException(); + } return userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); } } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java index ea6b2da..da356da 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/store/service/StoreServiceImpl.java @@ -151,6 +151,9 @@ private void validateUpdateAuthorization(User user, Long storeId) { } private User getUser(MemberDetails memberDetails) { + if (memberDetails == null) { + throw new StoreViewUnauthorizedException(); + } return userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); } }