diff --git a/src/main/java/com/example/Centralthon/domain/store/repository/StoreRepository.java b/src/main/java/com/example/Centralthon/domain/store/repository/StoreRepository.java index d7ea91b..2b52cba 100644 --- a/src/main/java/com/example/Centralthon/domain/store/repository/StoreRepository.java +++ b/src/main/java/com/example/Centralthon/domain/store/repository/StoreRepository.java @@ -1,9 +1,31 @@ package com.example.Centralthon.domain.store.repository; +import com.example.Centralthon.domain.menu.entity.Menu; import com.example.Centralthon.domain.store.entity.Store; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; + @Repository public interface StoreRepository extends JpaRepository { + @Query(value = """ + SELECT DISTINCT s.* FROM stores s + JOIN menus m ON m.store_id = s.store_id + WHERE s.latitude BETWEEN :minLat AND :maxLat AND s.longitude BETWEEN :minLng AND :maxLng + AND ST_Distance_Sphere(POINT(s.longitude, s.latitude), POINT(:lng, :lat)) <= 2000 + AND m.quantity > 0 AND m.deadline > :now + """, nativeQuery = true) + List findNearbyStores( + @Param("lat") double lat, + @Param("lng") double lng, + @Param("now") LocalDateTime now, + @Param("minLat") double minLat, + @Param("maxLat") double maxLat, + @Param("minLng") double minLng, + @Param("maxLng") double maxLng + ); } 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 new file mode 100644 index 0000000..b8f59cb --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/store/service/StoreService.java @@ -0,0 +1,10 @@ +package com.example.Centralthon.domain.store.service; + +import com.example.Centralthon.domain.store.web.dto.NearbyStoresRes; + +import java.util.List; + +public interface StoreService { + List nearbyStores(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 new file mode 100644 index 0000000..7ffd7ad --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/store/service/StoreServiceImpl.java @@ -0,0 +1,36 @@ +package com.example.Centralthon.domain.store.service; + +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.global.util.geo.BoundingBox; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +import static com.example.Centralthon.global.util.geo.GeoUtils.calculateBoundingBox; + +@Service +@RequiredArgsConstructor +public class StoreServiceImpl implements StoreService { + private final StoreRepository storeRepository; + + @Override + @Transactional(readOnly = true) + public List nearbyStores(double lat, double lng) { + LocalDateTime now = LocalDateTime.now(); + + // 사용자 위치를 기반으로 2km 반경의 Bounding Box 계산 + BoundingBox bbox = calculateBoundingBox(lat, lng, 2.0); + + List stores = storeRepository.findNearbyStores(lat, lng, now, + bbox.minLat(), bbox.maxLat(), bbox.minLng(), bbox.maxLng()); + + return stores.stream() + .map(store -> NearbyStoresRes.from(store)) + .toList(); + } +} 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 new file mode 100644 index 0000000..e0031d7 --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/store/web/controller/StoreController.java @@ -0,0 +1,31 @@ +package com.example.Centralthon.domain.store.web.controller; + +import com.example.Centralthon.domain.store.service.StoreService; +import com.example.Centralthon.domain.store.web.dto.NearbyStoresRes; +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 java.util.List; + +@RestController +@RequestMapping("/api/stores") +@RequiredArgsConstructor +public class StoreController { + private final StoreService storeService; + + // 근처 가게 위치 목록 조회 + @GetMapping("") + public ResponseEntity>> nearbyStores( + @RequestParam("lat") Double lat, + @RequestParam("lng") Double lng){ + + List stores = storeService.nearbyStores(lat, lng); + + return ResponseEntity.status(HttpStatus.OK).body(SuccessResponse.from(stores)); + } +} diff --git a/src/main/java/com/example/Centralthon/domain/store/web/dto/NearbyStoresRes.java b/src/main/java/com/example/Centralthon/domain/store/web/dto/NearbyStoresRes.java new file mode 100644 index 0000000..7d2124d --- /dev/null +++ b/src/main/java/com/example/Centralthon/domain/store/web/dto/NearbyStoresRes.java @@ -0,0 +1,17 @@ +package com.example.Centralthon.domain.store.web.dto; + +import com.example.Centralthon.domain.store.entity.Store; + +public record NearbyStoresRes( + Long storeId, + Double lat, + Double lng +) { + public static NearbyStoresRes from(Store store){ + return new NearbyStoresRes( + store.getId(), + store.getLatitude(), + store.getLongitude() + ); + } +}