diff --git a/src/main/java/com/example/Centralthon/domain/menu/repository/MenuRepository.java b/src/main/java/com/example/Centralthon/domain/menu/repository/MenuRepository.java index e75eb69..3c8b82e 100644 --- a/src/main/java/com/example/Centralthon/domain/menu/repository/MenuRepository.java +++ b/src/main/java/com/example/Centralthon/domain/menu/repository/MenuRepository.java @@ -11,6 +11,7 @@ import java.time.LocalDateTime; @Repository public interface MenuRepository extends JpaRepository { + // BoundingBox 안에 존재하는 모든 메뉴 반환 @Query(value = """ SELECT m.* FROM menus m JOIN stores s ON m.store_id = s.store_id @@ -28,12 +29,20 @@ List findNearbyMenus( @Param("maxLng") double maxLng ); + // 해당 가게에서 판매 하는 메뉴 반환 + @Query(""" + SELECT m FROM Menu m + JOIN FETCH m.store s + WHERE s.id = :storeId AND m.quantity > 0 AND m.deadline > :now + """) + List findByStoreId( + @Param("storeId") Long storeId, + @Param("now") LocalDateTime now); + /** * ids에 포함된 Menu와 연관된 Store를 한번에 가져옴 */ @Query(value = "SELECT m from Menu m join fetch m.store where m.id in :ids") List findAllByIdWithStore(@Param("ids") Collection ids); - - } diff --git a/src/main/java/com/example/Centralthon/domain/menu/web/dto/MenuDetailsRes.java b/src/main/java/com/example/Centralthon/domain/menu/web/dto/MenuDetailsRes.java index 636c51a..3b29aea 100644 --- a/src/main/java/com/example/Centralthon/domain/menu/web/dto/MenuDetailsRes.java +++ b/src/main/java/com/example/Centralthon/domain/menu/web/dto/MenuDetailsRes.java @@ -2,35 +2,19 @@ import com.example.Centralthon.domain.menu.entity.Menu; import com.example.Centralthon.domain.menu.entity.enums.MenuCategory; +import com.fasterxml.jackson.annotation.JsonUnwrapped; import java.math.BigDecimal; import java.math.RoundingMode; public record MenuDetailsRes( - long menuId, - String name, - MenuCategory category, - int costPrice, - int salePrice, - int salePercent, + @JsonUnwrapped MenuItemRes item, String storeName ) { public static MenuDetailsRes from(Menu menu) { return new MenuDetailsRes( - menu.getId(), - menu.getName(), - menu.getCategory(), - menu.getCostPrice(), - menu.getSalePrice(), - calculateDiscount(menu.getCostPrice(), menu.getSalePrice()), + MenuItemRes.from(menu), menu.getStore().getName() ); } - - private static int calculateDiscount(int cost, int sale) { - return BigDecimal.valueOf(cost - sale) - .multiply(BigDecimal.valueOf(100)) - .divide(BigDecimal.valueOf(cost), 0, RoundingMode.HALF_UP) - .intValue(); - } } diff --git a/src/main/java/com/example/Centralthon/domain/menu/web/dto/MenuItemRes.java b/src/main/java/com/example/Centralthon/domain/menu/web/dto/MenuItemRes.java new file mode 100644 index 0000000..bdb0974 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/menu/web/dto/MenuItemRes.java @@ -0,0 +1,39 @@ +package com.example.Centralthon.domain.menu.web.dto; + +import com.example.Centralthon.domain.menu.entity.Menu; +import com.example.Centralthon.domain.menu.entity.enums.MenuCategory; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +// 하위 공통 DTO +public record MenuItemRes( + Long menuId, + String name, + MenuCategory category, // 메뉴 카테고리 (예: 볶음) + int costPrice, + int salePrice, + int salePercent, // (costPrice - salePrice) * 100 / costPrice + int quantity) { + + public static MenuItemRes from(Menu menu) { + return new MenuItemRes( + menu.getId(), + menu.getName(), + menu.getCategory(), + menu.getCostPrice(), + menu.getSalePrice(), + calculateDiscount(menu.getCostPrice(), menu.getSalePrice()), + menu.getQuantity() + ); + } + + private static int calculateDiscount(int cost, int sale) { + return BigDecimal.valueOf(cost - sale) + .multiply(BigDecimal.valueOf(100)) + .divide(BigDecimal.valueOf(cost), 0, RoundingMode.HALF_UP) + .intValue(); + } +} + + diff --git a/src/main/java/com/example/Centralthon/domain/store/exception/StoreErrorCode.java b/src/main/java/com/example/Centralthon/domain/store/exception/StoreErrorCode.java new file mode 100644 index 0000000..aa0c96e --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/store/exception/StoreErrorCode.java @@ -0,0 +1,15 @@ +package com.example.Centralthon.domain.store.exception; + +import com.example.Centralthon.global.response.code.BaseResponseCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum StoreErrorCode implements BaseResponseCode { + STORE_NOT_FOUND("STORE_NOT_FOUND_404_1",404,"존재하지 않는 가게 입니다."); + + private final String code; + private final int httpStatus; + private final String message; +} diff --git a/src/main/java/com/example/Centralthon/domain/store/exception/StoreNotFoundException.java b/src/main/java/com/example/Centralthon/domain/store/exception/StoreNotFoundException.java new file mode 100644 index 0000000..fb85dbf --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/store/exception/StoreNotFoundException.java @@ -0,0 +1,9 @@ +package com.example.Centralthon.domain.store.exception; + +import com.example.Centralthon.global.exception.BaseException; + +public class StoreNotFoundException extends BaseException { + public StoreNotFoundException( ) { + super(StoreErrorCode.STORE_NOT_FOUND); + } +} diff --git a/src/main/java/com/example/Centralthon/domain/store/service/StoreService.java b/src/main/java/com/example/Centralthon/domain/store/service/StoreService.java index b8f59cb..695cf08 100644 --- a/src/main/java/com/example/Centralthon/domain/store/service/StoreService.java +++ b/src/main/java/com/example/Centralthon/domain/store/service/StoreService.java @@ -1,10 +1,12 @@ package com.example.Centralthon.domain.store.service; import com.example.Centralthon.domain.store.web.dto.NearbyStoresRes; +import com.example.Centralthon.domain.store.web.dto.StoreMenusRes; import java.util.List; public interface StoreService { List nearbyStores(double lat, double lng); + StoreMenusRes getStoreMenus(Long storeId, double lat, double lng); } diff --git a/src/main/java/com/example/Centralthon/domain/store/service/StoreServiceImpl.java b/src/main/java/com/example/Centralthon/domain/store/service/StoreServiceImpl.java index 7ffd7ad..4bda5b6 100644 --- a/src/main/java/com/example/Centralthon/domain/store/service/StoreServiceImpl.java +++ b/src/main/java/com/example/Centralthon/domain/store/service/StoreServiceImpl.java @@ -1,9 +1,15 @@ package com.example.Centralthon.domain.store.service; +import com.example.Centralthon.domain.menu.entity.Menu; +import com.example.Centralthon.domain.menu.repository.MenuRepository; +import com.example.Centralthon.domain.menu.web.dto.MenuItemRes; import com.example.Centralthon.domain.store.entity.Store; import com.example.Centralthon.domain.store.repository.StoreRepository; import com.example.Centralthon.domain.store.web.dto.NearbyStoresRes; +import com.example.Centralthon.domain.store.web.dto.StoreMenusRes; +import com.example.Centralthon.domain.store.exception.StoreNotFoundException; import com.example.Centralthon.global.util.geo.BoundingBox; +import com.example.Centralthon.global.util.geo.GeoUtils; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -17,6 +23,7 @@ @RequiredArgsConstructor public class StoreServiceImpl implements StoreService { private final StoreRepository storeRepository; + private final MenuRepository menuRepository; @Override @Transactional(readOnly = true) @@ -33,4 +40,29 @@ public List nearbyStores(double lat, double lng) { .map(store -> NearbyStoresRes.from(store)) .toList(); } + + @Override + @Transactional(readOnly = true) + public StoreMenusRes getStoreMenus(Long storeId, double lat, double lng) { + LocalDateTime now = LocalDateTime.now(); + + // 가게 id가 없을 경우 -> StoreNotFoundException + Store store = storeRepository.findById(storeId) + .orElseThrow(StoreNotFoundException::new); + + // 사용자와 가게 간의 거리 구하기 + double distance = GeoUtils.calculateDistance(lat, lng, store.getLatitude(), store.getLongitude()); + + List menus = menuRepository.findByStoreId(storeId, now); + + List menuItems = menus.stream() + .map(MenuItemRes::from) + .toList(); + + return StoreMenusRes.from( + store, + distance, + menuItems + ); + } } diff --git a/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreController.java b/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreController.java index e0031d7..c5bc02e 100644 --- a/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreController.java +++ b/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreController.java @@ -2,14 +2,13 @@ import com.example.Centralthon.domain.store.service.StoreService; import com.example.Centralthon.domain.store.web.dto.NearbyStoresRes; +import com.example.Centralthon.domain.store.web.dto.StoreMenusRes; import com.example.Centralthon.global.response.SuccessResponse; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + import java.util.List; @RestController @@ -28,4 +27,17 @@ public ResponseEntity>> nearbyStores( return ResponseEntity.status(HttpStatus.OK).body(SuccessResponse.from(stores)); } + + // 가게 판매 메뉴 목록 조회 + @GetMapping("/{storeId}/menus") + public ResponseEntity> getStoreMenus( + @PathVariable Long storeId, + @RequestParam("lat") Double lat, + @RequestParam("lng") Double lng){ + + StoreMenusRes menus = storeService.getStoreMenus(storeId, lat, lng); + + return ResponseEntity.status(HttpStatus.OK).body(SuccessResponse.from(menus)); + } + } diff --git a/src/main/java/com/example/Centralthon/domain/store/web/dto/StoreMenusRes.java b/src/main/java/com/example/Centralthon/domain/store/web/dto/StoreMenusRes.java new file mode 100644 index 0000000..a5c3df6 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/store/web/dto/StoreMenusRes.java @@ -0,0 +1,24 @@ +package com.example.Centralthon.domain.store.web.dto; + +import com.example.Centralthon.domain.menu.web.dto.MenuItemRes; +import com.example.Centralthon.domain.menu.web.dto.MenuItemRes; +import com.example.Centralthon.domain.store.entity.Store; +import com.example.Centralthon.domain.store.entity.enums.StoreCategory; + +import java.util.List; + +public record StoreMenusRes( + String name, + StoreCategory category, + double distance, + List menus +) { + public static StoreMenusRes from(Store store, double distance, List menuItems) { + return new StoreMenusRes( + store.getName(), + store.getCategory(), + distance, + menuItems + ); + } +}