diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/controller/MenuController.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/controller/MenuController.java index e4ee6369..3b28ee64 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/controller/MenuController.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/controller/MenuController.java @@ -1,5 +1,7 @@ package com.nowait.applicationadmin.menu.controller; +import java.util.List; + import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -14,6 +16,7 @@ import com.nowait.applicationadmin.menu.dto.MenuCreateRequest; import com.nowait.applicationadmin.menu.dto.MenuCreateResponse; +import com.nowait.applicationadmin.menu.dto.MenuSortUpdateRequest; import com.nowait.applicationadmin.menu.dto.MenuUpdateRequest; import com.nowait.applicationadmin.menu.service.MenuService; import com.nowait.common.api.ApiUtils; @@ -125,4 +128,20 @@ public ResponseEntity toggleSoldOut(@PathVariable Long menuId) { ) ); } + + @PatchMapping("/update-sort") + @Operation(summary = "메뉴 순서 수정", description = "메뉴의 순서를 수정합니다.") + @ApiResponse(responseCode = "200", description = "메뉴 순서 수정") + public ResponseEntity updateMenuSortOrder( + @Valid @RequestBody List request, + @AuthenticationPrincipal MemberDetails memberDetails + ) { + return ResponseEntity + .status(HttpStatus.OK) + .body( + ApiUtils.success( + menuService.updateMenuSortOrder(request, memberDetails) + ) + ); + } } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuCreateRequest.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuCreateRequest.java index 4b5fc37c..89813c71 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuCreateRequest.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuCreateRequest.java @@ -31,6 +31,7 @@ public Menu toEntity() { .description(description) .price(price) .isSoldOut(false) + .sortOrder(0L) .deleted(false) .build(); } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuCreateResponse.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuCreateResponse.java index 466f2d50..41d2affe 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuCreateResponse.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuCreateResponse.java @@ -4,7 +4,6 @@ import com.nowait.domaincorerdb.menu.entity.Menu; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -20,6 +19,7 @@ public class MenuCreateResponse { private String description; private Integer price; private Boolean isSoldOut; + private Long sortOrder; private Boolean deleted; private LocalDateTime createdAt; @@ -34,6 +34,7 @@ public static MenuCreateResponse fromEntity(Menu menu) { .description(menu.getDescription()) .price(menu.getPrice()) .isSoldOut(menu.getIsSoldOut()) + .sortOrder(menu.getSortOrder()) .deleted(menu.getDeleted()) .build(); } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuReadDto.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuReadDto.java index a5e25126..d4d3dc36 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuReadDto.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuReadDto.java @@ -4,7 +4,6 @@ import com.nowait.domaincorerdb.menu.entity.Menu; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -19,6 +18,7 @@ public class MenuReadDto { private String name; private String description; private Integer price; + private Long sortOrder; private Boolean isSoldOut; private Boolean deleted; private List images; @@ -31,6 +31,7 @@ public static MenuReadDto fromEntity(Menu menu, List im .name(menu.getName()) .description(menu.getDescription()) .price(menu.getPrice()) + .sortOrder(menu.getSortOrder()) .isSoldOut(menu.getIsSoldOut()) .deleted(menu.getDeleted()) .images(images) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuSortUpdateRequest.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuSortUpdateRequest.java new file mode 100644 index 00000000..c9509e1f --- /dev/null +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/menu/dto/MenuSortUpdateRequest.java @@ -0,0 +1,20 @@ +package com.nowait.applicationadmin.menu.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class MenuSortUpdateRequest { + @NotNull + private Long menuId; + @NotNull + @PositiveOrZero + private Long sortOrder; +} 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 54972594..2bdd5589 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 @@ -1,6 +1,7 @@ package com.nowait.applicationadmin.menu.service; import java.util.List; +import java.util.Map; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -10,6 +11,7 @@ import com.nowait.applicationadmin.menu.dto.MenuImageUploadResponse; import com.nowait.applicationadmin.menu.dto.MenuReadDto; import com.nowait.applicationadmin.menu.dto.MenuReadResponse; +import com.nowait.applicationadmin.menu.dto.MenuSortUpdateRequest; import com.nowait.applicationadmin.menu.dto.MenuUpdateRequest; import com.nowait.common.enums.Role; import com.nowait.domaincorerdb.menu.entity.Menu; @@ -60,7 +62,7 @@ public MenuReadResponse getAllMenusByStoreId(Long storeId, MemberDetails memberD // 사용자 역할이 SUPER_ADMIN이거나, storeId가 일치하는지 확인 validateMenuViewAuthorization(user, storeId); - List menus = menuRepository.findAllByStoreIdAndDeletedFalse(storeId); + List menus = menuRepository.findAllByStoreIdAndDeletedFalseOrderBySortOrder(storeId); List menuReadResponse = menus.stream() .map(menu -> { @@ -123,6 +125,48 @@ public MenuReadDto updateMenu(Long menuId, MenuUpdateRequest request, MemberDeta return MenuReadDto.fromEntity(saved, imageDto); } + @Transactional + public String updateMenuSortOrder(List requests, MemberDetails memberDetails) { + User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); + + if (!Role.SUPER_ADMIN.equals(user.getRole())) { + throw new MenuUpdateUnauthorizedException(); + } + + if (requests == null || requests.isEmpty()) { + throw new MenuParamEmptyException(); + } + + if (requests.stream().map(MenuSortUpdateRequest::getMenuId).distinct().count() != requests.size()) { + throw new IllegalArgumentException("중복된 메뉴 ID가 포함되어 있습니다."); + } + + if (requests.stream().anyMatch(r -> r.getSortOrder() == null || r.getSortOrder() < 0)) { + throw new IllegalArgumentException("잘못된 정렬 순서가 포함되어 있습니다. 정렬 순서는 0 이상의 정수여야 합니다."); + } + + List ids = requests.stream().map(MenuSortUpdateRequest::getMenuId).toList(); + List menus = menuRepository.findAllById(ids); + if (menus.size() != ids.size()) { + throw new MenuNotFoundException(); + } + + Long storeId = menus.get(0).getStoreId(); + if (!menus.stream().allMatch(m -> storeId.equals(m.getStoreId()))) { + throw new IllegalArgumentException("다른 매장의 메뉴가 있습니다."); + } + + Map idToSort = requests.stream() + .collect(java.util.stream.Collectors.toMap(MenuSortUpdateRequest::getMenuId, + MenuSortUpdateRequest::getSortOrder)); + + menus.forEach(m -> m.updateSortOrder(idToSort.get(m.getId()))); + + menuRepository.saveAll(menus); + + return "메뉴 순서가 성공적으로 업데이트되었습니다."; + } + @Transactional public String deleteMenu(Long menuId, MemberDetails memberDetails) { User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/dto/OrderResponseDto.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/dto/OrderResponseDto.java index d8a3979a..3bd2e1b9 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/dto/OrderResponseDto.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/order/dto/OrderResponseDto.java @@ -1,6 +1,7 @@ package com.nowait.applicationadmin.order.dto; import java.time.LocalDateTime; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -19,14 +20,18 @@ public class OrderResponseDto { private String depositorName; private Integer totalPrice; private OrderStatus status; - private Map menuDetails; + private HashMap menuDetails; private LocalDateTime createdAt; public static OrderResponseDto fromEntity(UserOrder userOrder) { - Map menuDetails = new LinkedHashMap<>(); + HashMap menuDetails = new LinkedHashMap<>(); for (OrderItem item : userOrder.getOrderItems()) { - String displayName = item.getMenu().getAdminDisplayName(); + String adminDisplayName = item.getMenu().getAdminDisplayName(); + String displayName = (adminDisplayName == null || adminDisplayName.isBlank()) + ? item.getMenu().getName() // 관리자 표시명이 없거나 공백이면 일반 메뉴명 사용 + : adminDisplayName; + int quantity = item.getQuantity(); int price = item.getMenu().getPrice(); // 메뉴 단가 diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/menu/dto/MenuReadDto.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/menu/dto/MenuReadDto.java index cf013289..ce8d6325 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/menu/dto/MenuReadDto.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/menu/dto/MenuReadDto.java @@ -17,6 +17,7 @@ public class MenuReadDto { private String name; private String description; private Integer price; + private Long sortOrder; private Boolean isSoldOut; private Boolean deleted; private List images; @@ -28,6 +29,7 @@ public static MenuReadDto fromEntity(Menu menu, List im .name(menu.getName()) .description(menu.getDescription()) .price(menu.getPrice()) + .sortOrder(menu.getSortOrder()) .isSoldOut(menu.getIsSoldOut()) .deleted(menu.getDeleted()) .images(images) diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/menu/service/MenuService.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/menu/service/MenuService.java index 2fc43c90..2ab8cfb7 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/menu/service/MenuService.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/menu/service/MenuService.java @@ -30,7 +30,7 @@ public MenuReadResponse getAllMenusByStoreId(Long storeId) { if (storeId == null) { throw new MenuParamEmptyException(); } - List menus = menuRepository.findAllByStoreIdAndDeletedFalse(storeId); + List menus = menuRepository.findAllByStoreIdAndDeletedFalseOrderBySortOrder(storeId); List menuReadResponse = menus.stream() .map(menu -> { diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/entity/Menu.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/entity/Menu.java index fc1f35bc..c1297176 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/entity/Menu.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/entity/Menu.java @@ -46,6 +46,9 @@ public class Menu extends BaseTimeEntity { @Column(nullable = false) private Boolean isSoldOut; + @Column(nullable = false) + private Long sortOrder; + @Column(nullable = false) private Boolean deleted; @@ -72,4 +75,8 @@ public void updateInfo(String adminDisplayName, String name, String description, public void markAsDeleted() { this.deleted = true; } public void toggleSoldOut() { this.isSoldOut = !this.isSoldOut; } + + public void updateSortOrder(Long sortOrder) { + this.sortOrder = sortOrder; + } } diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/repository/MenuRepository.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/repository/MenuRepository.java index 80f07c95..b785836c 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/repository/MenuRepository.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/menu/repository/MenuRepository.java @@ -11,6 +11,7 @@ @Repository public interface MenuRepository extends JpaRepository { List findAllByStoreIdAndDeletedFalse(Long storeId); + List findAllByStoreIdAndDeletedFalseOrderBySortOrder(Long storeId); Optional findByStoreIdAndIdAndDeletedFalse(Long storeId, Long menuId); Optional findByIdAndDeletedFalse(Long menuId); }