diff --git a/src/main/java/team7/inplace/global/queryDsl/QueryDslConfig.java b/src/main/java/team7/inplace/global/queryDsl/QueryDslConfig.java index 72c12822..73fab4eb 100644 --- a/src/main/java/team7/inplace/global/queryDsl/QueryDslConfig.java +++ b/src/main/java/team7/inplace/global/queryDsl/QueryDslConfig.java @@ -1,16 +1,20 @@ package team7.inplace.global.queryDsl; +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class QueryDslConfig { -// @PersistenceContext -// private EntityManager entityManager; -// -// @Bean -// public JPAQueryFactory jpaQueryFactory() { -// return new JPAQueryFactory(entityManager); -// } + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(entityManager); + } } diff --git a/src/main/java/team7/inplace/place/application/PlaceService.java b/src/main/java/team7/inplace/place/application/PlaceService.java index 7ccb2888..e50513f6 100644 --- a/src/main/java/team7/inplace/place/application/PlaceService.java +++ b/src/main/java/team7/inplace/place/application/PlaceService.java @@ -85,7 +85,7 @@ private Page getPlacesByDistance( List categoryFilters, List influencerFilters ) { - return placeRepository.getPlacesByDistanceAndFilters( + return placeRepository.findPlacesByDistanceAndFilters( placesCoordinateCommand.topLeftLongitude(), placesCoordinateCommand.topLeftLatitude(), placesCoordinateCommand.bottomRightLongitude(), diff --git a/src/main/java/team7/inplace/place/application/dto/PlaceDetailInfo.java b/src/main/java/team7/inplace/place/application/dto/PlaceDetailInfo.java index 25044d11..5d255ad4 100644 --- a/src/main/java/team7/inplace/place/application/dto/PlaceDetailInfo.java +++ b/src/main/java/team7/inplace/place/application/dto/PlaceDetailInfo.java @@ -50,7 +50,7 @@ public record MenuInfos( public static MenuInfos of(List menus) { List menuList = menus.stream() - .map(menu -> new MenuInfo(Integer.parseInt(menu.getPrice()), menu.isRecommend(), + .map(menu -> new MenuInfo(menu.getPrice(), menu.isRecommend(), menu.getMenuName(), menu.getMenuImgUrl(), menu.getDescription())) .toList(); @@ -58,7 +58,7 @@ public static MenuInfos of(List menus) { } public record MenuInfo( - Integer price, + String price, Boolean recommend, String menuName, String menuImgUrl, diff --git a/src/main/java/team7/inplace/place/persistence/PlaceCustomRepository.java b/src/main/java/team7/inplace/place/persistence/PlaceCustomRepository.java index 0965b6e7..ad6a2bb6 100644 --- a/src/main/java/team7/inplace/place/persistence/PlaceCustomRepository.java +++ b/src/main/java/team7/inplace/place/persistence/PlaceCustomRepository.java @@ -1,21 +1,27 @@ package team7.inplace.place.persistence; +import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.query.Param; +import team7.inplace.place.domain.Place; + public interface PlaceCustomRepository { -// Page findPlacesByDistance( -// @Param("longitude") String longitude, -// @Param("latitude") String latitude, -// Pageable pageable); -// -// Page findPlacesByDistanceAndFilters( -// @Param("topLeftLongitude") String topLeftLongitude, -// @Param("topLeftLatitude") String topLeftLatitude, -// @Param("bottomRightLongitude") String bottomRightLongitude, -// @Param("bottomRightLatitude") String bottomRightLatitude, -// @Param("longitude") String longitude, -// @Param("latitude") String latitude, -// @Param("categories") List categories, -// @Param("influencers") List influencers, -// Pageable pageable); + Page findPlacesByDistance( + @Param("longitude") String longitude, + @Param("latitude") String latitude, + Pageable pageable); + + Page findPlacesByDistanceAndFilters( + @Param("topLeftLongitude") String topLeftLongitude, + @Param("topLeftLatitude") String topLeftLatitude, + @Param("bottomRightLongitude") String bottomRightLongitude, + @Param("bottomRightLatitude") String bottomRightLatitude, + @Param("longitude") String longitude, + @Param("latitude") String latitude, + @Param("categories") List categories, + @Param("influencers") List influencers, + Pageable pageable); } diff --git a/src/main/java/team7/inplace/place/persistence/PlaceCustomRepositoryImpl.java b/src/main/java/team7/inplace/place/persistence/PlaceCustomRepositoryImpl.java index fc19f624..04d960c6 100644 --- a/src/main/java/team7/inplace/place/persistence/PlaceCustomRepositoryImpl.java +++ b/src/main/java/team7/inplace/place/persistence/PlaceCustomRepositoryImpl.java @@ -1,94 +1,111 @@ package team7.inplace.place.persistence; -//@Repository -//@AllArgsConstructor +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.core.types.dsl.NumberTemplate; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.List; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; +import team7.inplace.influencer.domain.QInfluencer; +import team7.inplace.place.domain.Category; +import team7.inplace.place.domain.Place; +import team7.inplace.place.domain.QPlace; +import team7.inplace.video.domain.QVideo; + +@Repository +@AllArgsConstructor public class PlaceCustomRepositoryImpl implements PlaceCustomRepository { -// private final JPAQueryFactory jpaQueryFactory; -// -// @Override -// public Page findPlacesByDistance(String longitude, String latitude, Pageable pageable) { -// QPlace place = QPlace.place; -// -// NumberTemplate distanceExpression = Expressions.numberTemplate(Double.class, -// "6371 * acos(cos(radians({0})) * cos(radians({1})) * cos(radians({2}) - radians({3})) + sin(radians({0})) * sin(radians({1})))", -// Double.parseDouble(latitude), place.coordinate.latitude, place.coordinate.longitude, -// Double.parseDouble(longitude)); -// -// List places = jpaQueryFactory.selectFrom(place) -// .orderBy(distanceExpression.asc()) -// .offset(pageable.getOffset()) -// .limit(pageable.getPageSize()) -// .fetch(); -// -// return new PageImpl<>(places, pageable, places.size()); -// } -// -// @Override -// public Page findPlacesByDistanceAndFilters(String topLeftLongitude, -// String topLeftLatitude, String bottomRightLongitude, String bottomRightLatitude, -// String longitude, String latitude, List categories, List influencers, -// Pageable pageable) { -// -// QPlace place = QPlace.place; -// QVideo video = QVideo.video; -// QInfluencer influencer = QInfluencer.influencer; -// -// NumberTemplate distanceExpression = Expressions.numberTemplate(Double.class, -// "6371 * acos(cos(radians({0})) * cos(radians({1})) * cos(radians({2}) - radians({3})) + sin(radians({0})) * sin(radians({1})))", -// Double.parseDouble(latitude), place.coordinate.latitude, place.coordinate.longitude, -// Double.parseDouble(longitude)); -// -// List places = jpaQueryFactory.selectFrom(place) -// .leftJoin(video).on(video.place.eq(place)) -// .leftJoin(place).on(influencer.id.eq(video.influencer.id)) -// .where( -// withinBoundary( -// place, -// Double.parseDouble(topLeftLongitude), -// Double.parseDouble(topLeftLatitude), -// Double.parseDouble(bottomRightLongitude), -// Double.parseDouble(bottomRightLatitude) -// ), -// placeCategoryIn(categories), -// placeInfluencerIn(influencers) -// ).orderBy(distanceExpression.asc()) -// .offset(pageable.getOffset()) -// .limit(pageable.getPageSize()) -// .fetch(); -// -// return new PageImpl<>(places, pageable, places.size()); -// } -// -// private BooleanExpression withinBoundary(QPlace place, double topLeftLongitude, -// double topLeftLatitude, -// double bottomRightLongitude, double bottomRightLatitude) { -// NumberTemplate longitude = Expressions.numberTemplate(Double.class, -// "CAST({0} AS DOUBLE)", place.coordinate.longitude); -// NumberTemplate latitude = Expressions.numberTemplate(Double.class, -// "CAST({0} AS DOUBLE)", place.coordinate.latitude); -// -// return longitude.between(topLeftLongitude, bottomRightLongitude) -// .and(latitude.between(bottomRightLatitude, topLeftLatitude)); -// } -// -// private BooleanExpression placeCategoryIn(List categories) { -// if (categories == null) { -// return null; -// } -// -// List enumCategories = categories.stream() -// .map(Category::valueOf) -// .collect(Collectors.toList()); -// -// return QPlace.place.category.in(enumCategories); -// } -// -// private BooleanExpression placeInfluencerIn(List influencers) { -// if (influencers == null) { -// return null; -// } -// -// return QInfluencer.influencer.name.in(influencers); -// } + private final JPAQueryFactory jpaQueryFactory; + + @Override + public Page findPlacesByDistance(String longitude, String latitude, Pageable pageable) { + QPlace place = QPlace.place; + + NumberTemplate distanceExpression = Expressions.numberTemplate(Double.class, + "6371 * acos(cos(radians({0})) * cos(radians(CAST({1} AS DOUBLE))) * cos(radians(CAST({2} AS DOUBLE)) - radians({3})) + sin(radians({0})) * sin(radians(CAST({1} AS DOUBLE))))", + Double.parseDouble(latitude), place.coordinate.latitude, place.coordinate.longitude, + Double.parseDouble(longitude)); + + List places = jpaQueryFactory.selectFrom(place) + .orderBy(distanceExpression.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + return new PageImpl<>(places, pageable, places.size()); + } + + @Override + public Page findPlacesByDistanceAndFilters(String topLeftLongitude, + String topLeftLatitude, String bottomRightLongitude, String bottomRightLatitude, + String longitude, String latitude, List categories, List influencers, + Pageable pageable) { + + QPlace place = QPlace.place; + QVideo video = QVideo.video; + QInfluencer influencer = QInfluencer.influencer; + + NumberTemplate distanceExpression = Expressions.numberTemplate(Double.class, + "6371 * acos(cos(radians({0})) * cos(radians(CAST({1} AS DOUBLE))) * cos(radians(CAST({2} AS DOUBLE)) - radians({3})) + sin(radians({0})) * sin(radians(CAST({1} AS DOUBLE))))", + Double.parseDouble(latitude), place.coordinate.latitude, place.coordinate.longitude, + Double.parseDouble(longitude)); + + List places = jpaQueryFactory.selectFrom(place) + .leftJoin(video).on(video.place.eq(place)) + .leftJoin(influencer).on(video.influencer.eq(influencer)) + .where( + withinBoundary( + place, + Double.parseDouble(topLeftLongitude), + Double.parseDouble(topLeftLatitude), + Double.parseDouble(bottomRightLongitude), + Double.parseDouble(bottomRightLatitude) + ), + placeCategoryIn(categories), + placeInfluencerIn(influencers) + ).orderBy(distanceExpression.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + return new PageImpl<>(places, pageable, places.size()); + } + + private BooleanExpression withinBoundary(QPlace place, double topLeftLongitude, + double topLeftLatitude, + double bottomRightLongitude, double bottomRightLatitude) { + NumberTemplate longitude = Expressions.numberTemplate(Double.class, + "CAST({0} AS DOUBLE)", place.coordinate.longitude); + NumberTemplate latitude = Expressions.numberTemplate(Double.class, + "CAST({0} AS DOUBLE)", place.coordinate.latitude); + + return longitude.between(topLeftLongitude, bottomRightLongitude) + .and(latitude.between(bottomRightLatitude, topLeftLatitude)); + } + + private BooleanExpression placeCategoryIn(List categories) { + if (categories == null) { + return null; + } + + List enumCategories = categories.stream() + .map(Category::of) // Category.of() 메서드로 직접 매핑 + .collect(Collectors.toList()); + + return QPlace.place.category.in(enumCategories); + } + + private BooleanExpression placeInfluencerIn(List influencers) { + if (influencers == null) { + return null; + } + + return QInfluencer.influencer.name.in(influencers); + } } diff --git a/src/main/java/team7/inplace/place/persistence/PlaceRepository.java b/src/main/java/team7/inplace/place/persistence/PlaceRepository.java index ae377f2c..18957b08 100644 --- a/src/main/java/team7/inplace/place/persistence/PlaceRepository.java +++ b/src/main/java/team7/inplace/place/persistence/PlaceRepository.java @@ -1,68 +1,10 @@ package team7.inplace.place.persistence; -import java.util.List; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; 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 team7.inplace.place.domain.Place; @Repository -public interface PlaceRepository extends JpaRepository { +public interface PlaceRepository extends JpaRepository, PlaceCustomRepository { - //거리 계산 - @Query(value = "SELECT *, " + - "(6371 * acos(cos(radians(CAST(:latitude AS DECIMAL(10, 6)))) " + - "* cos(radians(CAST(latitude AS DECIMAL(10, 6)))) " + - "* cos(radians(CAST(longitude AS DECIMAL(10, 6))) - radians(CAST(:longitude AS DECIMAL(10, 6)))) " - + - "+ sin(radians(CAST(:latitude AS DECIMAL(10, 6)))) " + - "* sin(radians(CAST(latitude AS DECIMAL(10, 6)))))) AS distance " + - "FROM places " + - "ORDER BY distance", - countQuery = "SELECT count(*) FROM places", // 총 개수 쿼리 - nativeQuery = true) - Page getPlacesByDistance( - @Param("longitude") String longitude, - @Param("latitude") String latitude, - Pageable pageable); - - @Query(value = "SELECT p.*, " + - "(6371 * acos(cos(radians(CAST(:latitude AS DECIMAL(10, 6)))) " + - "* cos(radians(CAST(p.latitude AS DECIMAL(10, 6)))) " + - "* cos(radians(CAST(p.longitude AS DECIMAL(10, 6))) - radians(CAST(:longitude AS DECIMAL(10, 6)))) " - + - "+ sin(radians(CAST(:latitude AS DECIMAL(10, 6)))) " + - "* sin(radians(CAST(p.latitude AS DECIMAL(10, 6)))))) AS distance " + - "FROM places p " + - "LEFT JOIN video v ON p.id = v.place_id " + - "LEFT JOIN influencer i ON v.influencer_id = i.id " + - "WHERE (:categories IS NULL OR p.category IN (:categories)) " + - "AND (:influencers IS NULL OR i.name IN (:influencers)) " + - "AND (CAST(p.longitude AS DECIMAL(10, 6)) BETWEEN CAST(:topLeftLongitude AS DECIMAL(10, 6)) AND CAST(:bottomRightLongitude AS DECIMAL(10, 6))) " - + - "AND (CAST(p.latitude AS DECIMAL(10, 6)) BETWEEN CAST(:bottomRightLatitude AS DECIMAL(10, 6)) AND CAST(:topLeftLatitude AS DECIMAL(10, 6)))" - + - "ORDER BY distance", - countQuery = "SELECT count(*) FROM places p " + - "LEFT JOIN video v ON p.id = v.place_id " + - "LEFT JOIN influencer i ON v.influencer_id = i.id " + - "WHERE (:categories IS NULL OR p.category IN (:categories)) " + - "AND (:influencers IS NULL OR i.name IN (:influencers)) " + - "AND (CAST(p.longitude AS DECIMAL(10, 6)) BETWEEN CAST(:topLeftLongitude AS DECIMAL(10, 6)) AND CAST(:bottomRightLongitude AS DECIMAL(10, 6))) " - + - "AND (CAST(p.latitude AS DECIMAL(10, 6)) BETWEEN CAST(:bottomRightLatitude AS DECIMAL(10, 6)) AND CAST(:topLeftLatitude AS DECIMAL(10, 6)))", - nativeQuery = true) - Page getPlacesByDistanceAndFilters( - @Param("topLeftLongitude") String topLeftLongitude, - @Param("topLeftLatitude") String topLeftLatitude, - @Param("bottomRightLongitude") String bottomRightLongitude, - @Param("bottomRightLatitude") String bottomRightLatitude, - @Param("longitude") String longitude, - @Param("latitude") String latitude, - @Param("categories") List categories, - @Param("influencers") List influencers, - Pageable pageable); } diff --git a/src/main/java/team7/inplace/video/application/VideoService.java b/src/main/java/team7/inplace/video/application/VideoService.java index 868d7864..8e7f8d9b 100644 --- a/src/main/java/team7/inplace/video/application/VideoService.java +++ b/src/main/java/team7/inplace/video/application/VideoService.java @@ -23,32 +23,34 @@ @Service @RequiredArgsConstructor public class VideoService { + private final VideoRepository videoRepository; private final PlaceRepository placeRepository; private final InfluencerRepository influencerRepository; - public Page getVideosBySurround(VideoSearchParams videoSearchParams, Pageable pageable) { + public Page getVideosBySurround(VideoSearchParams videoSearchParams, + Pageable pageable) { // Place 엔티티 조회 - Page places = placeRepository.getPlacesByDistanceAndFilters( - videoSearchParams.topLeftLongitude(), - videoSearchParams.topLeftLatitude(), - videoSearchParams.bottomRightLongitude(), - videoSearchParams.bottomRightLatitude(), - videoSearchParams.longitude(), - videoSearchParams.latitude(), - new ArrayList<>(), - new ArrayList<>(), - pageable + Page places = placeRepository.findPlacesByDistanceAndFilters( + videoSearchParams.topLeftLongitude(), + videoSearchParams.topLeftLatitude(), + videoSearchParams.bottomRightLongitude(), + videoSearchParams.bottomRightLatitude(), + videoSearchParams.longitude(), + videoSearchParams.latitude(), + new ArrayList<>(), + new ArrayList<>(), + pageable ); // 조회된 엔티티가 비어있는지 아닌지 확인 - if(places.isEmpty()){ + if (places.isEmpty()) { throw InplaceException.of(PlaceErrorCode.NOT_FOUND); } // 장소를 기준으로 비디오 엔티티 조회 ( 장소 별로 가장 최근 비디오 하나 씩 ) List