-
Notifications
You must be signed in to change notification settings - Fork 2
feat : 특정상점 전체메뉴조회 api작성 #208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| package in.koreatech.koin.domain.shop.dto; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; | ||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||
|
|
||
| import in.koreatech.koin.domain.shop.model.Menu; | ||
| import in.koreatech.koin.domain.shop.model.MenuCategory; | ||
| import in.koreatech.koin.domain.shop.model.MenuImage; | ||
| import in.koreatech.koin.domain.shop.model.MenuOption; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import lombok.extern.slf4j.Slf4j; | ||
|
|
||
| @Slf4j | ||
| @JsonNaming(value = SnakeCaseStrategy.class) | ||
| public record MenuDetailResponse( | ||
| @Schema(example = "1", description = "고유id") | ||
| Long id, | ||
|
|
||
| @Schema(example = "1", description = "메뉴가 소속된 상점의 고유 id ") | ||
| Long shopId, | ||
|
|
||
| @Schema(example = "탕수육", description = "이름") | ||
| String name, | ||
|
|
||
| @Schema(example = "false", description = "숨김 여부") | ||
| Boolean isHidden, | ||
|
|
||
| @Schema(example = "false", description = "단일 메뉴 여부") | ||
| Boolean isSingle, | ||
|
|
||
| @Schema(example = "7000", description = "단일 메뉴일때(is_single이 true일때)의 가격") | ||
| Integer singlePrice, | ||
|
|
||
| @Schema(description = "옵션이 있는 메뉴일때(is_single이 false일때)의 가격") | ||
| List<InnerOptionPriceResponse> optionPrices, | ||
|
|
||
| @Schema(example = "돼지고기 + 튀김", description = "구성 설명") | ||
| String description, | ||
|
|
||
| @Schema(description = "소속되어 있는 메뉴 카테고리 고유 id 리스트") | ||
| List<Long> categoryIds, | ||
|
|
||
| @Schema(description = "이미지 URL 리스트") | ||
| List<String> imageUrls | ||
| ) { | ||
|
|
||
| public static MenuDetailResponse createForSingleOption(Menu menu, List<MenuCategory> shopMenuCategories) { | ||
| if (menu.hasMultipleOption()) { | ||
| log.warn("{}는 옵션이 하나 이상인 메뉴입니다. createForMultipleOption 메서드를 이용해야 합니다.", menu); | ||
| throw new IllegalStateException("서버에 에러가 발생했습니다."); | ||
| } | ||
|
|
||
| return new MenuDetailResponse( | ||
| menu.getId(), | ||
| menu.getShopId(), | ||
| menu.getName(), | ||
| menu.getIsHidden(), | ||
| true, | ||
| menu.getMenuOptions().get(0).getPrice(), | ||
| null, | ||
| menu.getDescription(), | ||
| shopMenuCategories.stream().map(MenuCategory::getId).toList(), | ||
| menu.getMenuImages().stream().map(MenuImage::getImageUrl).toList() | ||
| ); | ||
| } | ||
|
|
||
| public static MenuDetailResponse createForMultipleOption(Menu menu, List<MenuCategory> shopMenuCategories) { | ||
| if (!menu.hasMultipleOption()) { | ||
| log.error("{}는 옵션이 하나인 메뉴입니다. createForSingleOption 메서드를 이용해야 합니다.", menu); | ||
| throw new IllegalStateException("서버에 에러가 발생했습니다."); | ||
| } | ||
|
|
||
| return new MenuDetailResponse( | ||
| menu.getId(), | ||
| menu.getShopId(), | ||
| menu.getName(), | ||
| menu.getIsHidden(), | ||
| false, | ||
| null, | ||
| menu.getMenuOptions().stream().map(InnerOptionPriceResponse::of).toList(), menu.getDescription(), | ||
| shopMenuCategories.stream().map(MenuCategory::getId).toList(), | ||
| menu.getMenuImages().stream().map(MenuImage::getImageUrl).toList() | ||
| ); | ||
| } | ||
|
|
||
| private record InnerOptionPriceResponse( | ||
| @Schema(example = "소", description = "옵션명") | ||
| String option, | ||
|
|
||
| @Schema(example = "10000", description = "옵션에 대한 가격") | ||
| Integer price | ||
| ) { | ||
| public static InnerOptionPriceResponse of(MenuOption menuOption) { | ||
| return new InnerOptionPriceResponse(menuOption.getOption(), menuOption.getPrice()); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,64 +1,125 @@ | ||
| package in.koreatech.koin.domain.shop.dto; | ||
|
|
||
| import java.time.LocalDateTime; | ||
| import java.util.List; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonFormat; | ||
| import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; | ||
| import com.fasterxml.jackson.databind.annotation.JsonNaming; | ||
|
|
||
| import in.koreatech.koin.domain.shop.model.Menu; | ||
| import in.koreatech.koin.domain.shop.model.MenuCategory; | ||
| import in.koreatech.koin.domain.shop.model.MenuCategoryMap; | ||
| import in.koreatech.koin.domain.shop.model.MenuImage; | ||
| import in.koreatech.koin.domain.shop.model.MenuOption; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
|
|
||
| @Slf4j | ||
| @JsonNaming(value = SnakeCaseStrategy.class) | ||
| public record ShopMenuResponse(Long id, Long shopId, String name, Boolean isHidden, Boolean isSingle, | ||
| Integer singlePrice, List<InnerOptionPriceResponse> optionPrices, String description, | ||
| List<Long> categoryIds, List<String> imageUrls) { | ||
|
|
||
| public static ShopMenuResponse createForSingleOption(Menu menu, List<MenuCategory> shopMenuCategories) { | ||
| if (menu.hasMultipleOption()) { | ||
| log.warn("{}는 옵션이 하나 이상인 메뉴입니다. createForMultipleOption 메서드를 이용해야 합니다.", menu); | ||
| throw new IllegalStateException("서버에 에러가 발생했습니다."); | ||
| } | ||
| public record ShopMenuResponse( | ||
| @Schema(example = "20", description = "개수") | ||
| Integer count, | ||
|
|
||
| @Schema(description = "카테고리 별로 분류된 소속 메뉴 리스트") | ||
| List<InnerMenuCategoriesResponse> menuCategories, | ||
|
|
||
| @Schema(example = "2024-03-16", description = "해당 상점 마지막 메뉴 업데이트 날짜") | ||
| @JsonFormat(pattern = "yyyy-MM-dd") | ||
| LocalDateTime updatedAt | ||
| ) { | ||
| public static ShopMenuResponse from(List<MenuCategory> menuCategories) { | ||
| LocalDateTime lastUpdatedDate = LocalDateTime.of(1900, 1, 1, 0, 0); | ||
| int count = 0; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. C메뉴 개수면 menuCategories.size()로 충분하지 않나요?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MenuCategory객체 내부에 메뉴가 여러개 있을 수 있는 구조라서 전부 순회했습니다ㅠ ex) |
||
| for (MenuCategory menuCategory : menuCategories) { | ||
| for (MenuCategoryMap menuCategoryMap : menuCategory.getMenuCategoryMaps()) { | ||
| LocalDateTime updatedAt = menuCategoryMap.getMenu().getUpdatedAt(); | ||
| if (updatedAt.isAfter(lastUpdatedDate)) { | ||
| lastUpdatedDate = updatedAt; | ||
kwoo28 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| ++count; | ||
| } | ||
| } | ||
| return new ShopMenuResponse( | ||
| menu.getId(), | ||
| menu.getShopId(), | ||
| menu.getName(), | ||
| menu.getIsHidden(), | ||
| true, | ||
| menu.getMenuOptions().get(0).getPrice(), | ||
| null, | ||
| menu.getDescription(), | ||
| shopMenuCategories.stream().map(MenuCategory::getId).toList(), | ||
| menu.getMenuImages().stream().map(MenuImage::getImageUrl).toList() | ||
| count, | ||
| menuCategories.stream().map(InnerMenuCategoriesResponse::from).toList(), | ||
| lastUpdatedDate | ||
| ); | ||
| } | ||
|
|
||
| public static ShopMenuResponse createForMultipleOption(Menu menu, List<MenuCategory> shopMenuCategories) { | ||
| if (!menu.hasMultipleOption()) { | ||
| log.error("{}는 옵션이 하나인 메뉴입니다. createForSingleOption 메서드를 이용해야 합니다.", menu); | ||
| throw new IllegalStateException("서버에 에러가 발생했습니다."); | ||
| @JsonNaming(value = SnakeCaseStrategy.class) | ||
| private record InnerMenuCategoriesResponse( | ||
| @Schema(example = "1", description = "카테고리 id") | ||
| Long id, | ||
|
|
||
| @Schema(example = "중식", description = "카테고리 이름") | ||
| String name, | ||
|
|
||
| @Schema(description = "해당 상점의 모든 메뉴 리스트") | ||
| List<InnerMenuResponse> menuResponses | ||
| ) { | ||
| public static InnerMenuCategoriesResponse from(MenuCategory menuCategory) { | ||
| return new InnerMenuCategoriesResponse( | ||
| menuCategory.getId(), | ||
| menuCategory.getName(), | ||
| menuCategory.getMenuCategoryMaps().stream().map(InnerMenuResponse::from).toList() | ||
| ); | ||
| } | ||
|
|
||
| return new ShopMenuResponse( | ||
| menu.getId(), | ||
| menu.getShopId(), | ||
| menu.getName(), | ||
| menu.getIsHidden(), | ||
| false, | ||
| null, | ||
| menu.getMenuOptions().stream().map(InnerOptionPriceResponse::of).toList(), menu.getDescription(), | ||
| shopMenuCategories.stream().map(MenuCategory::getId).toList(), | ||
| menu.getMenuImages().stream().map(MenuImage::getImageUrl).toList() | ||
| ); | ||
| } | ||
| @JsonNaming(value = SnakeCaseStrategy.class) | ||
| private record InnerMenuResponse( | ||
| @Schema(example = "저희 식당의 대표 메뉴 탕수육입니다.", description = "설명") | ||
| String description, | ||
|
|
||
| @Schema(example = "1", description = "고유 id") | ||
| Long id, | ||
|
|
||
| @Schema(description = "이미지 URL리스트") | ||
| List<String> imageUrls, | ||
|
|
||
| @Schema(example = "false", description = "숨김 여부") | ||
| Boolean isHidden, | ||
|
|
||
| @Schema(example = "false", description = "단일 메뉴 여부") | ||
| Boolean isSingle, | ||
|
|
||
| @Schema(example = "탕수육", description = "이름") | ||
| String name, | ||
|
|
||
| @Schema(description = "옵션이 있는 메뉴일때(is_single이 false일때)의 옵션에 따른 가격 리스트") | ||
| List<InnerOptionPrice> optionPrices, | ||
|
|
||
| @Schema(example = "10000", description = "단일 메뉴일때(is_single이 true일때)의 가격") | ||
| Integer singlePrice | ||
| ) { | ||
| public static InnerMenuResponse from(MenuCategoryMap menuCategoryMap) { | ||
| Menu menu = menuCategoryMap.getMenu(); | ||
| boolean isSingle = !menu.hasMultipleOption(); | ||
| return new InnerMenuResponse( | ||
| menu.getDescription(), | ||
| menu.getId(), | ||
| menu.getMenuImages().stream().map(MenuImage::getImageUrl).toList(), | ||
| menu.getIsHidden(), | ||
| isSingle, | ||
| menu.getName(), | ||
| isSingle ? null : menu.getMenuOptions().stream().map(InnerOptionPrice::from).toList(), | ||
| isSingle ? menu.getMenuOptions().get(0).getPrice() : null | ||
| ); | ||
| } | ||
|
|
||
| @JsonNaming(value = SnakeCaseStrategy.class) | ||
| private record InnerOptionPrice( | ||
| @Schema(example = "대", description = "옵션명") | ||
| String option, | ||
|
|
||
| private record InnerOptionPriceResponse(String option, Integer price) { | ||
| public static InnerOptionPriceResponse of(MenuOption menuOption) { | ||
| return new InnerOptionPriceResponse(menuOption.getOption(), menuOption.getPrice()); | ||
| @Schema(example = "26000", description = "가격") | ||
| Integer price | ||
| ) { | ||
| public static InnerOptionPrice from(MenuOption menuOption) { | ||
| return new InnerOptionPrice( | ||
| menuOption.getOption(), | ||
| menuOption.getPrice() | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| import in.koreatech.koin.domain.shop.dto.MenuCategoriesResponse; | ||
| import in.koreatech.koin.domain.shop.dto.MenuDetailResponse; | ||
| import in.koreatech.koin.domain.shop.dto.ShopMenuResponse; | ||
| import in.koreatech.koin.domain.shop.dto.ShopResponse; | ||
| import in.koreatech.koin.domain.shop.model.Menu; | ||
|
|
@@ -35,22 +36,22 @@ public class ShopService { | |
| private final ShopCategoryMapRepository shopCategoryMapRepository; | ||
| private final ShopImageRepository shopImageRepository; | ||
|
|
||
| public ShopMenuResponse findMenu(Long menuId) { | ||
| public MenuDetailResponse findMenu(Long menuId) { | ||
| Menu menu = menuRepository.getById(menuId); | ||
|
|
||
| List<MenuCategory> menuCategories = menu.getMenuCategoryMaps() | ||
| .stream() | ||
| .map(MenuCategoryMap::getMenuCategory) | ||
| .toList(); | ||
|
|
||
| return createShopMenuResponse(menu, menuCategories); | ||
| return createMenuDetailResponse(menu, menuCategories); | ||
| } | ||
|
|
||
| private ShopMenuResponse createShopMenuResponse(Menu menu, List<MenuCategory> menuCategories) { | ||
| private MenuDetailResponse createMenuDetailResponse(Menu menu, List<MenuCategory> menuCategories) { | ||
| if (menu.hasMultipleOption()) { | ||
| return ShopMenuResponse.createForMultipleOption(menu, menuCategories); | ||
| return MenuDetailResponse.createForMultipleOption(menu, menuCategories); | ||
| } | ||
| return ShopMenuResponse.createForSingleOption(menu, menuCategories); | ||
| return MenuDetailResponse.createForSingleOption(menu, menuCategories); | ||
| } | ||
|
|
||
| public MenuCategoriesResponse getMenuCategories(Long shopId) { | ||
|
|
@@ -67,4 +68,9 @@ public ShopResponse getShop(Long shopId) { | |
| List<MenuCategory> menuCategories = menuCategoryRepository.findAllByShopId(shopId); | ||
| return ShopResponse.of(shop, shopOpens, shopImages, shopCategoryMaps, menuCategories); | ||
| } | ||
|
|
||
| public ShopMenuResponse getShopMenu(Long shopId) { | ||
| List<MenuCategory> menuCategories = menuCategoryRepository.findAllByShopId(shopId); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CmenuCategoryRepository에서 findAllByShopId 메서드에 대해 조회된 데이터가 없을때에 대한 예외처리 부분이 안보이는것같은데 따로 예외처리 안해도 상관없는건가요??
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. findAll이라서 List형태로 반환할거라서 따로 예외처리는 안했어요~ |
||
| return ShopMenuResponse.from(menuCategories); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A
상세한 로깅 좋네요