Skip to content

Commit 2ea77b7

Browse files
authored
✨ feat : 가게 판매 메뉴 목록 조회 API 개발
✨ feat : 가게 판매 메뉴 목록 조회 API 개발
2 parents 930047a + a79114e commit 2ea77b7

File tree

9 files changed

+151
-25
lines changed

9 files changed

+151
-25
lines changed

src/main/java/com/example/Centralthon/domain/menu/repository/MenuRepository.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.time.LocalDateTime;
1212
@Repository
1313
public interface MenuRepository extends JpaRepository<Menu, Long> {
14+
// BoundingBox 안에 존재하는 모든 메뉴 반환
1415
@Query(value = """
1516
SELECT m.* FROM menus m
1617
JOIN stores s ON m.store_id = s.store_id
@@ -28,12 +29,20 @@ List<Menu> findNearbyMenus(
2829
@Param("maxLng") double maxLng
2930
);
3031

32+
// 해당 가게에서 판매 하는 메뉴 반환
33+
@Query("""
34+
SELECT m FROM Menu m
35+
JOIN FETCH m.store s
36+
WHERE s.id = :storeId AND m.quantity > 0 AND m.deadline > :now
37+
""")
38+
List<Menu> findByStoreId(
39+
@Param("storeId") Long storeId,
40+
@Param("now") LocalDateTime now);
41+
3142
/**
3243
* ids에 포함된 Menu와 연관된 Store를 한번에 가져옴
3344
*/
3445
@Query(value = "SELECT m from Menu m join fetch m.store where m.id in :ids")
3546
List<Menu> findAllByIdWithStore(@Param("ids") Collection<Long> ids);
3647

37-
38-
3948
}

src/main/java/com/example/Centralthon/domain/menu/web/dto/MenuDetailsRes.java

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,19 @@
22

33
import com.example.Centralthon.domain.menu.entity.Menu;
44
import com.example.Centralthon.domain.menu.entity.enums.MenuCategory;
5+
import com.fasterxml.jackson.annotation.JsonUnwrapped;
56

67
import java.math.BigDecimal;
78
import java.math.RoundingMode;
89

910
public record MenuDetailsRes(
10-
long menuId,
11-
String name,
12-
MenuCategory category,
13-
int costPrice,
14-
int salePrice,
15-
int salePercent,
11+
@JsonUnwrapped MenuItemRes item,
1612
String storeName
1713
) {
1814
public static MenuDetailsRes from(Menu menu) {
1915
return new MenuDetailsRes(
20-
menu.getId(),
21-
menu.getName(),
22-
menu.getCategory(),
23-
menu.getCostPrice(),
24-
menu.getSalePrice(),
25-
calculateDiscount(menu.getCostPrice(), menu.getSalePrice()),
16+
MenuItemRes.from(menu),
2617
menu.getStore().getName()
2718
);
2819
}
29-
30-
private static int calculateDiscount(int cost, int sale) {
31-
return BigDecimal.valueOf(cost - sale)
32-
.multiply(BigDecimal.valueOf(100))
33-
.divide(BigDecimal.valueOf(cost), 0, RoundingMode.HALF_UP)
34-
.intValue();
35-
}
3620
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.example.Centralthon.domain.menu.web.dto;
2+
3+
import com.example.Centralthon.domain.menu.entity.Menu;
4+
import com.example.Centralthon.domain.menu.entity.enums.MenuCategory;
5+
6+
import java.math.BigDecimal;
7+
import java.math.RoundingMode;
8+
9+
// 하위 공통 DTO
10+
public record MenuItemRes(
11+
Long menuId,
12+
String name,
13+
MenuCategory category, // 메뉴 카테고리 (예: 볶음)
14+
int costPrice,
15+
int salePrice,
16+
int salePercent, // (costPrice - salePrice) * 100 / costPrice
17+
int quantity) {
18+
19+
public static MenuItemRes from(Menu menu) {
20+
return new MenuItemRes(
21+
menu.getId(),
22+
menu.getName(),
23+
menu.getCategory(),
24+
menu.getCostPrice(),
25+
menu.getSalePrice(),
26+
calculateDiscount(menu.getCostPrice(), menu.getSalePrice()),
27+
menu.getQuantity()
28+
);
29+
}
30+
31+
private static int calculateDiscount(int cost, int sale) {
32+
return BigDecimal.valueOf(cost - sale)
33+
.multiply(BigDecimal.valueOf(100))
34+
.divide(BigDecimal.valueOf(cost), 0, RoundingMode.HALF_UP)
35+
.intValue();
36+
}
37+
}
38+
39+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.example.Centralthon.domain.store.exception;
2+
3+
import com.example.Centralthon.global.response.code.BaseResponseCode;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Getter;
6+
7+
@Getter
8+
@AllArgsConstructor
9+
public enum StoreErrorCode implements BaseResponseCode {
10+
STORE_NOT_FOUND("STORE_NOT_FOUND_404_1",404,"존재하지 않는 가게 입니다.");
11+
12+
private final String code;
13+
private final int httpStatus;
14+
private final String message;
15+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.example.Centralthon.domain.store.exception;
2+
3+
import com.example.Centralthon.global.exception.BaseException;
4+
5+
public class StoreNotFoundException extends BaseException {
6+
public StoreNotFoundException( ) {
7+
super(StoreErrorCode.STORE_NOT_FOUND);
8+
}
9+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.example.Centralthon.domain.store.service;
22

33
import com.example.Centralthon.domain.store.web.dto.NearbyStoresRes;
4+
import com.example.Centralthon.domain.store.web.dto.StoreMenusRes;
45

56
import java.util.List;
67

78
public interface StoreService {
89
List<NearbyStoresRes> nearbyStores(double lat, double lng);
910

11+
StoreMenusRes getStoreMenus(Long storeId, double lat, double lng);
1012
}

src/main/java/com/example/Centralthon/domain/store/service/StoreServiceImpl.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package com.example.Centralthon.domain.store.service;
22

3+
import com.example.Centralthon.domain.menu.entity.Menu;
4+
import com.example.Centralthon.domain.menu.repository.MenuRepository;
5+
import com.example.Centralthon.domain.menu.web.dto.MenuItemRes;
36
import com.example.Centralthon.domain.store.entity.Store;
47
import com.example.Centralthon.domain.store.repository.StoreRepository;
58
import com.example.Centralthon.domain.store.web.dto.NearbyStoresRes;
9+
import com.example.Centralthon.domain.store.web.dto.StoreMenusRes;
10+
import com.example.Centralthon.domain.store.exception.StoreNotFoundException;
611
import com.example.Centralthon.global.util.geo.BoundingBox;
12+
import com.example.Centralthon.global.util.geo.GeoUtils;
713
import lombok.RequiredArgsConstructor;
814
import org.springframework.stereotype.Service;
915
import org.springframework.transaction.annotation.Transactional;
@@ -17,6 +23,7 @@
1723
@RequiredArgsConstructor
1824
public class StoreServiceImpl implements StoreService {
1925
private final StoreRepository storeRepository;
26+
private final MenuRepository menuRepository;
2027

2128
@Override
2229
@Transactional(readOnly = true)
@@ -33,4 +40,29 @@ public List<NearbyStoresRes> nearbyStores(double lat, double lng) {
3340
.map(store -> NearbyStoresRes.from(store))
3441
.toList();
3542
}
43+
44+
@Override
45+
@Transactional(readOnly = true)
46+
public StoreMenusRes getStoreMenus(Long storeId, double lat, double lng) {
47+
LocalDateTime now = LocalDateTime.now();
48+
49+
// 가게 id가 없을 경우 -> StoreNotFoundException
50+
Store store = storeRepository.findById(storeId)
51+
.orElseThrow(StoreNotFoundException::new);
52+
53+
// 사용자와 가게 간의 거리 구하기
54+
double distance = GeoUtils.calculateDistance(lat, lng, store.getLatitude(), store.getLongitude());
55+
56+
List<Menu> menus = menuRepository.findByStoreId(storeId, now);
57+
58+
List<MenuItemRes> menuItems = menus.stream()
59+
.map(MenuItemRes::from)
60+
.toList();
61+
62+
return StoreMenusRes.from(
63+
store,
64+
distance,
65+
menuItems
66+
);
67+
}
3668
}

src/main/java/com/example/Centralthon/domain/store/web/controller/StoreController.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
import com.example.Centralthon.domain.store.service.StoreService;
44
import com.example.Centralthon.domain.store.web.dto.NearbyStoresRes;
5+
import com.example.Centralthon.domain.store.web.dto.StoreMenusRes;
56
import com.example.Centralthon.global.response.SuccessResponse;
67
import lombok.RequiredArgsConstructor;
78
import org.springframework.http.HttpStatus;
89
import org.springframework.http.ResponseEntity;
9-
import org.springframework.web.bind.annotation.GetMapping;
10-
import org.springframework.web.bind.annotation.RequestMapping;
11-
import org.springframework.web.bind.annotation.RequestParam;
12-
import org.springframework.web.bind.annotation.RestController;
10+
import org.springframework.web.bind.annotation.*;
11+
1312
import java.util.List;
1413

1514
@RestController
@@ -28,4 +27,17 @@ public ResponseEntity<SuccessResponse<List<NearbyStoresRes>>> nearbyStores(
2827

2928
return ResponseEntity.status(HttpStatus.OK).body(SuccessResponse.from(stores));
3029
}
30+
31+
// 가게 판매 메뉴 목록 조회
32+
@GetMapping("/{storeId}/menus")
33+
public ResponseEntity<SuccessResponse<StoreMenusRes>> getStoreMenus(
34+
@PathVariable Long storeId,
35+
@RequestParam("lat") Double lat,
36+
@RequestParam("lng") Double lng){
37+
38+
StoreMenusRes menus = storeService.getStoreMenus(storeId, lat, lng);
39+
40+
return ResponseEntity.status(HttpStatus.OK).body(SuccessResponse.from(menus));
41+
}
42+
3143
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.example.Centralthon.domain.store.web.dto;
2+
3+
import com.example.Centralthon.domain.menu.web.dto.MenuItemRes;
4+
import com.example.Centralthon.domain.menu.web.dto.MenuItemRes;
5+
import com.example.Centralthon.domain.store.entity.Store;
6+
import com.example.Centralthon.domain.store.entity.enums.StoreCategory;
7+
8+
import java.util.List;
9+
10+
public record StoreMenusRes(
11+
String name,
12+
StoreCategory category,
13+
double distance,
14+
List<MenuItemRes> menus
15+
) {
16+
public static StoreMenusRes from(Store store, double distance, List<MenuItemRes> menuItems) {
17+
return new StoreMenusRes(
18+
store.getName(),
19+
store.getCategory(),
20+
distance,
21+
menuItems
22+
);
23+
}
24+
}

0 commit comments

Comments
 (0)