diff --git a/cakey-api/src/main/java/com/cakey/cake/controller/CakeController.java b/cakey-api/src/main/java/com/cakey/cake/controller/CakeController.java index f17d2aa..0d9bc44 100644 --- a/cakey-api/src/main/java/com/cakey/cake/controller/CakeController.java +++ b/cakey-api/src/main/java/com/cakey/cake/controller/CakeController.java @@ -10,7 +10,6 @@ import com.cakey.store.domain.Station; import jakarta.validation.constraints.Min; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -57,9 +56,9 @@ public ResponseEntity> getRankCakesByStationStore( - //선택 디자인 조회 + //디자인 둘러보기 선택 디자인 조회 @GetMapping("/select/{cakeId}") - public ResponseEntity> getCakeSelect( + public ResponseEntity> getSearchDesignCakeSelect( @UserId final Long userId, @PathVariable(value = "cakeId") @Min(1) final long cakeId, @RequestParam(value = "dayCategory") final DayCategory dayCategory, @@ -129,4 +128,15 @@ public ResponseEntity> getPopularCakesByLikedStore( ); } + //지도뷰 선택 디자인 조회 + @GetMapping("/select/map/{cakeId}") + public ResponseEntity> getMapSelectCake( + @UserId final Long userId, + @PathVariable(value = "cakeId") final long cakeId + ) { + return ApiResponseUtil.success( + SuccessCode.OK, + cakeService.getMapSelectCake(userId, cakeId) + ); + } } diff --git a/cakey-api/src/main/java/com/cakey/cake/dto/CakeSelectedMapRes.java b/cakey-api/src/main/java/com/cakey/cake/dto/CakeSelectedMapRes.java new file mode 100644 index 0000000..c8b866d --- /dev/null +++ b/cakey-api/src/main/java/com/cakey/cake/dto/CakeSelectedMapRes.java @@ -0,0 +1,31 @@ +package com.cakey.cake.dto; + +import com.cakey.store.domain.Station; +import lombok.Builder; + +@Builder +public record CakeSelectedMapRes( + long storeId, + String storeName, + String address, + Station station, + boolean isLiked, + String imageUrl +) { + + public static CakeSelectedMapRes from(final long storeId, + final String storeName, + final String address, + final Station station, + final boolean isLiked, + final String imageUrl) { + return CakeSelectedMapRes.builder() + .storeId(storeId) + .storeName(storeName) + .address(address) + .station(station) + .isLiked(isLiked) + .imageUrl(imageUrl) + .build(); + } +} diff --git a/cakey-api/src/main/java/com/cakey/cake/service/CakeService.java b/cakey-api/src/main/java/com/cakey/cake/service/CakeService.java index 649dda8..e3e4fc2 100644 --- a/cakey-api/src/main/java/com/cakey/cake/service/CakeService.java +++ b/cakey-api/src/main/java/com/cakey/cake/service/CakeService.java @@ -13,6 +13,7 @@ import com.cakey.store.exception.StoreNotfoundException; import com.cakey.store.facade.StoreFacade; import lombok.RequiredArgsConstructor; +import org.springframework.boot.actuate.cache.CachesEndpoint; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,6 +25,7 @@ public class CakeService { private final CakeFacade cakeFacade; private final StoreFacade storeFacade; + private final CachesEndpoint cachesEndpoint; //해당역 스토어의 케이크들 조회(최신순) @Transactional(readOnly = true) @@ -284,7 +286,7 @@ public CakesPopularListRes getPopularCakeByStoreLiked(final long userId, ///페이지네이션 try { cakeInfoDtos = cakeFacade.findPopularCakesLikedByUser(userId, cakeIdCursor, cakeLikesCursor, size); - } catch (final NotFoundBaseException e) { + } catch (NotFoundBaseException e) { throw new CakeyNotFoundException(CakeErrorCode.CAKE_NOT_FOUND_ENTITY); } ///전체 케이크 개수 @@ -310,4 +312,22 @@ public CakesPopularListRes getPopularCakeByStoreLiked(final long userId, return CakesPopularListRes.from(nextLikesCursor, nextCakeIdCursor, totalCakeCount, isLastData, cakes); } + + //지도뷰 선택 디자인 조회 + public CakeSelectedMapRes getMapSelectCake(final Long userId, final long cakeId) { + final CakeSelectedMapDto cakeSelectedMapDto; + try { + cakeSelectedMapDto = cakeFacade.getCakeSelectedMap(userId, cakeId); + } catch (final NotFoundBaseException e) { + throw new CakeyNotFoundException(CakeErrorCode.CAKE_NOT_FOUND_ENTITY); + } + + return CakeSelectedMapRes.from( + cakeSelectedMapDto.getStoreId(), + cakeSelectedMapDto.getStoreName(), + cakeSelectedMapDto.getAddress(), + cakeSelectedMapDto.getStation(), + cakeSelectedMapDto.getIsLiked(), + cakeSelectedMapDto.getImageUrl()); + } } diff --git a/cakey-domain/src/main/java/com/cakey/cake/dto/CakeSelectedMapDto.java b/cakey-domain/src/main/java/com/cakey/cake/dto/CakeSelectedMapDto.java new file mode 100644 index 0000000..745016b --- /dev/null +++ b/cakey-domain/src/main/java/com/cakey/cake/dto/CakeSelectedMapDto.java @@ -0,0 +1,31 @@ +package com.cakey.cake.dto; + +import com.cakey.store.domain.Station; +import com.querydsl.core.annotations.QueryProjection; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CakeSelectedMapDto { + private final Long storeId; + private final String storeName; + private final String address; + private final Station station; + private final Boolean isLiked; + private final String imageUrl; + + @QueryProjection + public CakeSelectedMapDto(final Long storeId, + final String storeName, final String address, + final Station station, + final Boolean isLiked, + final String imageUrl) { + this.storeId = storeId; + this.storeName = storeName; + this.address = address; + this.station = station; + this.isLiked = isLiked; + this.imageUrl = imageUrl; + } +} diff --git a/cakey-domain/src/main/java/com/cakey/cake/facade/CakeFacade.java b/cakey-domain/src/main/java/com/cakey/cake/facade/CakeFacade.java index 99f8995..3f0b076 100644 --- a/cakey-domain/src/main/java/com/cakey/cake/facade/CakeFacade.java +++ b/cakey-domain/src/main/java/com/cakey/cake/facade/CakeFacade.java @@ -2,10 +2,7 @@ import com.cakey.cake.domain.Cake; import com.cakey.cake.domain.DayCategory; -import com.cakey.cake.dto.CakeByPopularityDto; -import com.cakey.cake.dto.CakeInfoDto; -import com.cakey.cake.dto.CakeMainImageDto; -import com.cakey.cake.dto.CakeSelectedInfoDto; +import com.cakey.cake.dto.*; import com.cakey.caketheme.domain.ThemeName; import com.cakey.store.domain.Station; import com.cakey.store.dto.StoreBySelectedCakeDto; @@ -74,9 +71,9 @@ public List findLatestLikedCakesByUser(final Long userId, //찜한 디자인(케이크) 조회(인기순) public List findPopularLikedCakesByUser(final long userId, - final Long cakeIdCursor, - final Integer cakeLikesCursor, - final int size) { + final Long cakeIdCursor, + final Integer cakeLikesCursor, + final int size) { return cakeRetriever.findPopularLikedCakesByUser(userId, cakeIdCursor, cakeLikesCursor, size); } @@ -139,5 +136,8 @@ public void isExistCake(final long cakeId) { cakeRetriever.isExistCake(cakeId); } - + //지도뷰 조회된 디자인 조회 + public CakeSelectedMapDto getCakeSelectedMap(final Long userId, final long cakeId) { + return cakeRetriever.getCakeSelectedMap(userId, cakeId); + } } diff --git a/cakey-domain/src/main/java/com/cakey/cake/facade/CakeRetriever.java b/cakey-domain/src/main/java/com/cakey/cake/facade/CakeRetriever.java index 76c0766..535c7f2 100644 --- a/cakey-domain/src/main/java/com/cakey/cake/facade/CakeRetriever.java +++ b/cakey-domain/src/main/java/com/cakey/cake/facade/CakeRetriever.java @@ -2,10 +2,7 @@ import com.cakey.cake.domain.Cake; import com.cakey.cake.domain.DayCategory; -import com.cakey.cake.dto.CakeByPopularityDto; -import com.cakey.cake.dto.CakeInfoDto; -import com.cakey.cake.dto.CakeMainImageDto; -import com.cakey.cake.dto.CakeSelectedInfoDto; +import com.cakey.cake.dto.*; import com.cakey.cake.repository.CakeRepository; import com.cakey.caketheme.domain.ThemeName; import com.cakey.common.exception.NotFoundBaseException; @@ -148,4 +145,10 @@ public void isExistCake(final long cakeId) { throw new NotFoundBaseException(); } } + + //지도뷰 선택 케이크 조회 + public CakeSelectedMapDto getCakeSelectedMap(final Long userId, final long cakeId) { + return cakeRepository.getCakeSelectedMap(userId, cakeId).orElseThrow(NotFoundBaseException::new); + } + } diff --git a/cakey-domain/src/main/java/com/cakey/cake/repository/CakeRepositoryCustom.java b/cakey-domain/src/main/java/com/cakey/cake/repository/CakeRepositoryCustom.java index 2c8cf0d..a620953 100644 --- a/cakey-domain/src/main/java/com/cakey/cake/repository/CakeRepositoryCustom.java +++ b/cakey-domain/src/main/java/com/cakey/cake/repository/CakeRepositoryCustom.java @@ -1,16 +1,14 @@ package com.cakey.cake.repository; import com.cakey.cake.domain.DayCategory; -import com.cakey.cake.dto.CakeInfoDto; -import com.cakey.cake.dto.CakeMainImageDto; -import com.cakey.cake.dto.CakeSelectedDto; -import com.cakey.cake.dto.CakeSelectedInfoDto; +import com.cakey.cake.dto.*; import com.cakey.caketheme.domain.ThemeName; import com.cakey.store.domain.Station; import com.cakey.store.dto.StoreBySelectedCakeDto; import com.cakey.store.dto.StoreInfoDto; import java.util.List; +import java.util.Optional; public interface CakeRepositoryCustom { List findMainImageByStoreIds(final List storeIds); @@ -78,5 +76,7 @@ List findPopularCakesLikedByUser(final long userId, int countCakesByCategoryAndTheme(final DayCategory dayCategory, final ThemeName theme); - + //지도뷰 조회된 디자인 조회 + Optional getCakeSelectedMap(final Long userId, + final long cakeId); } diff --git a/cakey-domain/src/main/java/com/cakey/cake/repository/CakeRepositoryCustomImpl.java b/cakey-domain/src/main/java/com/cakey/cake/repository/CakeRepositoryCustomImpl.java index 88b9a0f..9651bef 100644 --- a/cakey-domain/src/main/java/com/cakey/cake/repository/CakeRepositoryCustomImpl.java +++ b/cakey-domain/src/main/java/com/cakey/cake/repository/CakeRepositoryCustomImpl.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; @RequiredArgsConstructor public class CakeRepositoryCustomImpl implements CakeRepositoryCustom { @@ -37,6 +38,7 @@ public class CakeRepositoryCustomImpl implements CakeRepositoryCustom { QCake cake = QCake.cake; QStore store = QStore.store; QCakeLikes cakeLikes = QCakeLikes.cakeLikes; + QStoreLike storeLike = QStoreLike.storeLike; //가게 메인이미지 조회 @Override @@ -136,7 +138,7 @@ public List findPopularCakesByStation(final Long userId, .where(cakeLikes.cakeId.eq(cake.id)); /// 좋아요 여부 서브쿼리 - final BooleanExpression isLikedExpression = getIsLikedExpression(userId); + final BooleanExpression isLikedExpression = getCakeIsLikedExpression(userId); /// 역 조건 final BooleanExpression stationCondition = station != Station.ALL @@ -498,7 +500,7 @@ public List findCakesByCategoryAndTheme(final DayCategory dayCatego cake.storeId, store.name, store.station, - getIsLikedExpression(userId), // isLiked 조건 + getCakeIsLikedExpression(userId), // isLiked 조건 cake.imageUrl, likeCountSubQuery, /// 좋아요 개수 cake.id, /// 현재 케이크 ID를 커서로 반환 @@ -559,7 +561,7 @@ public List findPopularCakesByCategoryAndTheme(final DayCategory da store.id, store.name, store.station, - getIsLikedExpression(userId), // isLiked 조건 + getCakeIsLikedExpression(userId), // isLiked 조건 cake.imageUrl, cakeLikesOrderExpression, // 좋아요 개수 cake.id, @@ -672,7 +674,7 @@ public List findPopularCakesLikedByUser(final long userId, store.id, store.name, store.station, - getIsLikedExpression(userId), /// isLiked 조건 + getCakeIsLikedExpression(userId), /// isLiked 조건 cake.imageUrl, cakeLikesCountSubQuery, /// 좋아요 개수 cake.id, @@ -771,7 +773,7 @@ public List findCakesLikedByUser(final long userId, store.id, store.name, store.station, - getIsLikedExpression(userId), // isLiked 조건 + getCakeIsLikedExpression(userId), // isLiked 조건 cake.imageUrl, likeCountSubQuery, // 좋아요 개수 cake.id, // 현재 케이크 ID를 반환 @@ -836,6 +838,24 @@ public int countCakesByCategoryAndTheme(final DayCategory dayCategory, final The return count != null ? Math.toIntExact(count) : 0; } + //지도뷰 선택 디자인 조회 + @Override + public Optional getCakeSelectedMap(final Long userId, final long cakeId) { + return Optional.ofNullable(queryFactory + .select(new QCakeSelectedMapDto( + store.id, + store.name, + store.address, + store.station, + getStoreIsLikedExpression(userId), + cake.imageUrl + )) + .from(cake) + .join(store).on(cake.storeId.eq(store.id)) + .where(cake.id.eq(cakeId)) + .fetchOne()); + } + private BooleanExpression isLikedByUser(NumberExpression cakeId, Long userId) { QCakeLikes cakeLikes = QCakeLikes.cakeLikes; @@ -853,7 +873,7 @@ private BooleanExpression isLikedByUser(NumberExpression cakeId, Long user } // 유저의 케이크 좋아요 여부 서브쿼리 - private BooleanExpression getIsLikedExpression(final Long userId) { + private BooleanExpression getCakeIsLikedExpression(final Long userId) { if (userId != null) { return JPAExpressions.selectOne() .from(cakeLikes) @@ -874,4 +894,17 @@ private BooleanExpression isLikedExpression(final Long userId, final NumberPath< return Expressions.asBoolean(false); } } + + + //스토어 좋아요 여부 서브쿼리 + private BooleanExpression getStoreIsLikedExpression(final Long userId) { + if (userId != null) { + return JPAExpressions.selectOne() + .from(storeLike) + .where(storeLike.storeId.eq(store.id).and(storeLike.userId.eq(userId))) + .exists(); + } else { + return Expressions.asBoolean(false); + } + } }