diff --git a/backend/src/main/java/com/handong/rebon/member/domain/Member.java b/backend/src/main/java/com/handong/rebon/member/domain/Member.java index 949e7df2..b0eef625 100644 --- a/backend/src/main/java/com/handong/rebon/member/domain/Member.java +++ b/backend/src/main/java/com/handong/rebon/member/domain/Member.java @@ -2,10 +2,14 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import javax.persistence.*; +import com.handong.rebon.category.domain.Category; import com.handong.rebon.common.BaseEntity; import com.handong.rebon.review.domain.empathy.Empathy; +import com.handong.rebon.shop.domain.Shop; import com.handong.rebon.shop.domain.like.Likes; import lombok.*; @@ -82,7 +86,13 @@ public String getNickName() { return profile.getNickname(); } - public String getEmail() { return profile.getEmail(); } + public String getEmail() {return profile.getEmail();} - public String getImage() { return profile.getImage(); } + public String getImage() {return profile.getImage();} + + public List filterByCategory(Category category) { + return likes.stream() + .filter(like -> like.isSameCategory(category)) + .collect(Collectors.toList()); + } } diff --git a/backend/src/main/java/com/handong/rebon/member/domain/repository/MemberRepository.java b/backend/src/main/java/com/handong/rebon/member/domain/repository/MemberRepository.java index cd1de9e1..b5eae208 100644 --- a/backend/src/main/java/com/handong/rebon/member/domain/repository/MemberRepository.java +++ b/backend/src/main/java/com/handong/rebon/member/domain/repository/MemberRepository.java @@ -7,7 +7,9 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface MemberRepository extends JpaRepository { + Optional findByProfileEmailAndOauthProvider(String email, String oauthProvider); boolean existsByProfileNickname(String nickname); + } diff --git a/backend/src/main/java/com/handong/rebon/review/domain/repository/ReviewRepository.java b/backend/src/main/java/com/handong/rebon/review/domain/repository/ReviewRepository.java index 60ecd593..04b243dd 100644 --- a/backend/src/main/java/com/handong/rebon/review/domain/repository/ReviewRepository.java +++ b/backend/src/main/java/com/handong/rebon/review/domain/repository/ReviewRepository.java @@ -12,5 +12,4 @@ public interface ReviewRepository extends JpaRepository, ReviewRep Page findAllByMember(Member member, Pageable pageable); Page findAllByShop(Shop shop, Pageable pageable); - } diff --git a/backend/src/main/java/com/handong/rebon/shop/application/ShopService.java b/backend/src/main/java/com/handong/rebon/shop/application/ShopService.java index aa90ac1a..e2826a70 100644 --- a/backend/src/main/java/com/handong/rebon/shop/application/ShopService.java +++ b/backend/src/main/java/com/handong/rebon/shop/application/ShopService.java @@ -32,6 +32,7 @@ import com.handong.rebon.tag.domain.Tag; import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -186,6 +187,21 @@ private ShopImages changeImages(Shop shop, ShopRequestDto shopRequestDto) { return saveImages(shopRequestDto.getImages()); } + @Transactional(readOnly = true) + public List findLikeShops(Long memberId, Long categoryId, Pageable pageable) { + + Category category = categoryService.findById(categoryId); + Member member = memberService.findById(memberId); + + + Page shops = shopRepository.findByCategoryAndLikesMember(category, member, pageable); + return shops.get() + .map(ShopSimpleResponseDto::from) + .collect(Collectors.toList()); + + } + + @Transactional(readOnly = true) public boolean isLike(Long id, LoginMember loginMember) { Shop shop = findById(id); diff --git a/backend/src/main/java/com/handong/rebon/shop/application/dto/response/ShopSimpleResponseDto.java b/backend/src/main/java/com/handong/rebon/shop/application/dto/response/ShopSimpleResponseDto.java index 1aa9bfde..ec5f3ad1 100644 --- a/backend/src/main/java/com/handong/rebon/shop/application/dto/response/ShopSimpleResponseDto.java +++ b/backend/src/main/java/com/handong/rebon/shop/application/dto/response/ShopSimpleResponseDto.java @@ -1,11 +1,11 @@ package com.handong.rebon.shop.application.dto.response; import java.util.List; -import java.util.stream.Collectors; import com.handong.rebon.auth.domain.LoginMember; import com.handong.rebon.shop.application.dto.response.tag.ShopTagResponseDto; import com.handong.rebon.shop.domain.Shop; +import com.handong.rebon.shop.domain.like.Likes; import lombok.AccessLevel; import lombok.Builder; @@ -35,9 +35,6 @@ public ShopSimpleResponseDto(Long id, String name, double star, int reviewCount, } public static ShopSimpleResponseDto of(Shop shop, LoginMember loginMember) { - List tags = shop.getShopTags().stream() - .map(shopTag -> ShopTagResponseDto.from(shopTag.getTag())) - .collect(Collectors.toList()); return ShopSimpleResponseDto.builder() .id(shop.getId()) @@ -45,7 +42,19 @@ public static ShopSimpleResponseDto of(Shop shop, LoginMember loginMember) { .star(shop.getStar()) .reviewCount(shop.getReviewCount()) .like(shop.containLike(loginMember)) - .tags(tags) + .tags(ShopTagResponseDto.toDtos(shop)) + .image(shop.getMainImage()) + .build(); + } + + public static ShopSimpleResponseDto from(Shop shop) { + + return ShopSimpleResponseDto.builder() + .id(shop.getId()) + .name(shop.getName()) + .star(shop.getStar()) + .like(true) + .tags(ShopTagResponseDto.toDtos(shop)) .image(shop.getMainImage()) .build(); } diff --git a/backend/src/main/java/com/handong/rebon/shop/application/dto/response/tag/ShopTagResponseDto.java b/backend/src/main/java/com/handong/rebon/shop/application/dto/response/tag/ShopTagResponseDto.java index 85326b3d..1632d269 100644 --- a/backend/src/main/java/com/handong/rebon/shop/application/dto/response/tag/ShopTagResponseDto.java +++ b/backend/src/main/java/com/handong/rebon/shop/application/dto/response/tag/ShopTagResponseDto.java @@ -1,5 +1,9 @@ package com.handong.rebon.shop.application.dto.response.tag; +import java.util.List; +import java.util.stream.Collectors; + +import com.handong.rebon.shop.domain.Shop; import com.handong.rebon.tag.domain.Tag; import lombok.AccessLevel; @@ -20,4 +24,10 @@ public ShopTagResponseDto(Long id, String name) { public static ShopTagResponseDto from(Tag tag) { return new ShopTagResponseDto(tag.getId(), tag.getName()); } + + public static List toDtos(Shop shop) { + return shop.getShopTags().stream() + .map(shopTag -> ShopTagResponseDto.from(shopTag.getTag())) + .collect(Collectors.toList()); + } } diff --git a/backend/src/main/java/com/handong/rebon/shop/domain/like/Likes.java b/backend/src/main/java/com/handong/rebon/shop/domain/like/Likes.java index 321feb86..5be1234a 100644 --- a/backend/src/main/java/com/handong/rebon/shop/domain/like/Likes.java +++ b/backend/src/main/java/com/handong/rebon/shop/domain/like/Likes.java @@ -3,12 +3,15 @@ import java.util.Objects; import javax.persistence.*; +import com.handong.rebon.category.domain.Category; import com.handong.rebon.member.domain.Member; import com.handong.rebon.shop.domain.Shop; +import lombok.Getter; import lombok.NoArgsConstructor; @Entity +@Getter @NoArgsConstructor public class Likes { @@ -45,4 +48,8 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(member, shop); } + + public boolean isSameCategory(Category category) { + return shop.sameCategory(category); + } } diff --git a/backend/src/main/java/com/handong/rebon/shop/domain/repository/ShopRepository.java b/backend/src/main/java/com/handong/rebon/shop/domain/repository/ShopRepository.java index cbb9d300..4ecc414d 100644 --- a/backend/src/main/java/com/handong/rebon/shop/domain/repository/ShopRepository.java +++ b/backend/src/main/java/com/handong/rebon/shop/domain/repository/ShopRepository.java @@ -4,12 +4,18 @@ import java.util.Optional; import com.handong.rebon.category.domain.Category; +import com.handong.rebon.member.domain.Member; import com.handong.rebon.shop.domain.Shop; +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; public interface ShopRepository extends JpaRepository, ShopRepositoryCustom { List findByCategory(Category category); + + Page findByCategoryAndLikesMember(Category category, Member member, Pageable pageable); Optional findByNaverId(Long naverId); } diff --git a/backend/src/main/java/com/handong/rebon/shop/presentation/ApiShopController.java b/backend/src/main/java/com/handong/rebon/shop/presentation/ApiShopController.java index a1ac0098..d11b59ad 100644 --- a/backend/src/main/java/com/handong/rebon/shop/presentation/ApiShopController.java +++ b/backend/src/main/java/com/handong/rebon/shop/presentation/ApiShopController.java @@ -76,9 +76,21 @@ public ResponseEntity unlike( return ResponseEntity.ok(ShopLikeResponse.from(shopLikeResponseDto)); } + + @RequiredLogin + @GetMapping("/shops/likes") + public ResponseEntity> getLikeShops( + @Login LoginMember loginMember, + @RequestParam Long categoryId, + @PageableDefault Pageable pageable + ) { + List responses = shopService.findLikeShops(loginMember.getId(), categoryId, pageable); + return ResponseEntity.ok(ShopSimpleResponse.convert(responses)); + @PostMapping("/shops/naver") public ResponseEntity naverShops(@RequestBody NaverShopRequest shopRequest) { shopService.createNaverShops(shopRequest.getKeyword(), shopRequest.getDisplayCount(), shopRequest.getPage()); return ResponseEntity.ok().build(); + } } diff --git a/backend/src/test/java/com/handong/rebon/acceptance/shop/LikeShopReadAcceptanceTest.java b/backend/src/test/java/com/handong/rebon/acceptance/shop/LikeShopReadAcceptanceTest.java new file mode 100644 index 00000000..d2085e3b --- /dev/null +++ b/backend/src/test/java/com/handong/rebon/acceptance/shop/LikeShopReadAcceptanceTest.java @@ -0,0 +1,142 @@ +package com.handong.rebon.acceptance.shop; + +import java.time.LocalTime; +import java.util.*; + +import com.handong.rebon.acceptance.AcceptanceTest; +import com.handong.rebon.category.domain.Category; +import com.handong.rebon.common.admin.AdminCategoryRegister; +import com.handong.rebon.common.admin.AdminShopRegister; +import com.handong.rebon.common.admin.AdminTagRegister; +import com.handong.rebon.shop.domain.Shop; +import com.handong.rebon.shop.presentation.dto.response.ShopSimpleResponse; +import com.handong.rebon.tag.domain.Tag; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import io.restassured.RestAssured; +import io.restassured.common.mapper.TypeRef; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; + +import static com.handong.rebon.acceptance.AcceptanceUtils.getRequestSpecification; +import static com.handong.rebon.acceptance.shop.ShopLikeAcceptanceTest.가게_좋아요; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + + +public class LikeShopReadAcceptanceTest extends AcceptanceTest { + @Autowired + private AdminTagRegister adminTagRegister; + + @Autowired + private AdminCategoryRegister adminCategoryRegister; + + @Autowired + private AdminShopRegister adminShopRegister; + + private Map tags = new HashMap<>(); + private Map categories = new HashMap<>(); + private Map shops = new HashMap<>(); + + @BeforeEach + void setUp() { + tags = adminTagRegister.register("포항", "영일대", "한동대", "양덕"); + categories = adminCategoryRegister.registerMain("식당", "숙소", "카페"); + categories.putAll(adminCategoryRegister.registerSubs(categories.get("식당"), "한식", "분식", "일식", "양식")); + categories.putAll(adminCategoryRegister.registerSubs(categories.get("카페"), "프랜차이즈", "개인카페")); + shops.clear(); + shops.put("티타", adminShopRegister.CafeRegisterWithMenu( + "티타", + categories.get("카페"), + Collections.singletonList(categories.get("개인카페")), + Arrays.asList(tags.get("포항"), tags.get("양덕")), + LocalTime.MIN, + LocalTime.MAX + )); + shops.put("설빙", adminShopRegister.CafeRegisterWithMenu( + "설빙", + categories.get("카페"), + Collections.singletonList(categories.get("프랜차이즈")), + Arrays.asList(tags.get("포항"), tags.get("양덕")), + LocalTime.MIN, + LocalTime.MAX + )); + shops.put("인브리즈", adminShopRegister.CafeRegisterWithMenu( + "인브리즈", + categories.get("카페"), + Collections.singletonList(categories.get("개인카페")), + Arrays.asList(tags.get("포항"), tags.get("한동대")), + LocalTime.MIN, + LocalTime.MAX + )); + shops.put("예소드", adminShopRegister.CafeRegisterWithMenu( + "예소드", + categories.get("카페"), + Collections.singletonList(categories.get("개인카페")), + Arrays.asList(tags.get("포항"), tags.get("한동대")), + LocalTime.MIN, + LocalTime.MAX + )); + } + + @Test + @DisplayName("내가 찜한 가게 리스트 조회") + public void getLikeShops() { + //given + ExtractableResponse registerResponse = 회원가입("test@gmail.com", "test"); + String token = extractedToken(registerResponse); + 가게_좋아요(token, shops.get("티타")); + 가게_좋아요(token, shops.get("설빙")); + Long categoryId = categories.get("카페").getId(); + //when + ExtractableResponse response = 찜한_가게_조회(token, categoryId, 0,2); + List result = response.as(new TypeRef<>() {}); + //then + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(result.size()).isEqualTo(2); + assertThat(result).extracting("name") + .containsOnly("티타", "설빙"); + + } + + @Test + @DisplayName("내가 찜한 가게 리스트 다음페이지 조회") + public void getLikeShopsWithPaging() { + //given + ExtractableResponse registerResponse = 회원가입("test@gmail.com", "test"); + String token = extractedToken(registerResponse); + 가게_좋아요(token, shops.get("티타")); + 가게_좋아요(token, shops.get("설빙")); + 가게_좋아요(token, shops.get("인브리즈")); + 가게_좋아요(token, shops.get("예소드")); + Long categoryId = categories.get("카페").getId(); + //when + ExtractableResponse response = 찜한_가게_조회(token, categoryId, 1,2); + List result = response.as(new TypeRef<>() {}); + //then + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(result.size()).isEqualTo(2); + assertThat(result).extracting("name") + .containsOnly("인브리즈", "예소드"); + + } + + private ExtractableResponse 찜한_가게_조회(String token, Long categoryId, int pageNum, int pageSize) { + return RestAssured.given(getRequestSpecification()) + .log().all() + .header("Authorization", "Bearer " + token) + .contentType(APPLICATION_JSON_VALUE) + .when() + .get("/api/shops/likes?categoryId=" + categoryId + + "&page=" + pageNum + "&size=" + pageSize) + .then() + .log().all() + .extract(); + } +}