Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature, test: 전체 조회 및 상세 조회시, 즐겨찾기 여부 추가 응답!! #124

Merged
merged 2 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public FavoritesResponseDto getFavorites(long memberId, Pageable pageable) {
Member member = memberService.getMemberById(memberId);
Page<Favorite> favorites = favoriteRepository.findAllByMemberId(member.getId(), pageable);
for (Favorite favorite : favorites) {
productResponses.add(ProductMapper.toProductResponse(favorite.getProduct()));
productResponses.add(ProductMapper.toProductResponse(favorite.getProduct(),true));
}
return FavoritesResponseDto.builder()
.pageCount(favorites.getTotalPages())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.fc.shimpyo_be.domain.product.entity;

import com.fc.shimpyo_be.domain.favorite.entity.Favorite;
import com.fc.shimpyo_be.domain.product.util.CategoryConverter;
import com.fc.shimpyo_be.domain.room.entity.Room;
import jakarta.persistence.CascadeType;
Expand Down Expand Up @@ -59,12 +60,14 @@ public class Product {
private List<ProductImage> photoUrls = new ArrayList<>();
@OneToMany(mappedBy = "product", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Room> rooms = new ArrayList<>();
@OneToMany(mappedBy = "product", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Favorite> favorites = new ArrayList<>();


@Builder
public Product(Long id, String name, Address address, Category category, String description,
float starAvg, String thumbnail, ProductOption productOption, Amenity amenity,
List<ProductImage> photoUrls, List<Room> rooms) {
List<ProductImage> photoUrls, List<Room> rooms, List<Favorite> favorites) {
this.id = id;
this.name = name;
this.address = address;
Expand All @@ -76,6 +79,7 @@ public Product(Long id, String name, Address address, Category category, String
this.amenity = amenity;
this.photoUrls = photoUrls;
this.rooms = rooms;
this.favorites = favorites;
}

public void updateStarAvg(float starAvg) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.fc.shimpyo_be.domain.product.service;

import com.fc.shimpyo_be.domain.favorite.entity.Favorite;
import com.fc.shimpyo_be.domain.product.dto.request.SearchKeywordRequest;
import com.fc.shimpyo_be.domain.product.dto.response.PaginatedProductResponse;
import com.fc.shimpyo_be.domain.product.dto.response.ProductDetailsResponse;
import com.fc.shimpyo_be.domain.product.dto.response.ProductResponse;
import com.fc.shimpyo_be.domain.product.entity.Product;
import com.fc.shimpyo_be.domain.product.exception.ProductNotFoundException;
import com.fc.shimpyo_be.domain.product.repository.ProductCustomRepositoryImpl;
Expand All @@ -11,7 +13,9 @@
import com.fc.shimpyo_be.domain.room.entity.Room;
import com.fc.shimpyo_be.domain.room.repository.RoomRepository;
import com.fc.shimpyo_be.global.util.DateTimeUtil;
import com.fc.shimpyo_be.global.util.SecurityUtil;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
Expand All @@ -34,6 +38,8 @@ public class ProductService {

private final RedisTemplate<String, Object> restTemplate;

private final SecurityUtil securityUtil;

public PaginatedProductResponse getProducts(final SearchKeywordRequest searchKeywordRequest,
final Pageable pageable) {

Expand All @@ -43,7 +49,7 @@ public PaginatedProductResponse getProducts(final SearchKeywordRequest searchKey

return PaginatedProductResponse.builder()
.productResponses(
products.getContent().stream().map(ProductMapper::toProductResponse).toList())
getProductResponseSettingFavorites(products.getContent()))
.pageCount(products.getTotalPages())
.build();
}
Expand All @@ -52,16 +58,19 @@ public ProductDetailsResponse getProductDetails(final Long productId, final Stri
final String endDate) {
Product product = productRepository.findById(productId)
.orElseThrow(ProductNotFoundException::new);
HashSet<Long> favoriteProductIds = getFavoriteProductIds(List.of(product));
ProductDetailsResponse productDetailsResponse = ProductMapper.toProductDetailsResponse(
product);
product, favoriteProductIds.contains(product.getId()));

productDetailsResponse.rooms().forEach(
roomResponse -> roomResponse.setRemaining(
countAvailableForReservationUsingRoomCode(roomResponse.getRoomCode(), startDate,
endDate)));
return productDetailsResponse;
}

public long countAvailableForReservationUsingRoomCode(final Long roomCode, final String startDate,
public long countAvailableForReservationUsingRoomCode(final Long roomCode,
final String startDate,
final String endDate) {
AtomicLong remaining = new AtomicLong();
List<Room> rooms = Optional.of(roomRepository.findByCode(roomCode)).orElseThrow();
Expand Down Expand Up @@ -93,5 +102,31 @@ public boolean isAvailableForReservation(final Long roomId, final String startDa
return true;
}

private List<ProductResponse> getProductResponseSettingFavorites(List<Product> products) {

HashSet<Long> favoriteProductIds = getFavoriteProductIds(products);

return products.stream().map(product -> ProductMapper.toProductResponse(product,
favoriteProductIds.contains(product.getId()))).toList();

}

private HashSet<Long> getFavoriteProductIds(List<Product> products) {
Long userId = securityUtil.getNullableCurrentMemberId();
HashSet<Long> favoriteProductId = new HashSet<>();
if (userId != null) {
for (Product product : products) {
for (Favorite favorite : product.getFavorites()) {
if (favorite.getMember().getId().equals(userId)) {
favoriteProductId.add(product.getId());
break;
}
}
}
}

return favoriteProductId;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,24 @@
public class ProductMapper {


public static ProductResponse toProductResponse(Product product) {

List<Room> rooms = product.getRooms();
long price = rooms.isEmpty() ? 0 : rooms.stream().map(PricePickerByDateUtil::getPrice)
.min((o1, o2) -> Math.toIntExact(
o1 - o2)).orElseThrow();
price = price == 0 ? 100000 : price;
public static ProductResponse toProductResponse(Product product, boolean isFavorite) {

return ProductResponse.builder().productId(product.getId()).productName(product.getName())
.address(
product.getAddress().getAddress() + " " + product.getAddress().getDetailAddress())
.category(product.getCategory().getName())
.image(product.getThumbnail())
.starAvg(product.getStarAvg())
.price(price)
.price(getPrice(product))
.capacity(product.getRooms().isEmpty()
? 0 : Long.valueOf(
product.getRooms().stream().map(Room::getCapacity).min((o1, o2) -> o2 - o1)
.orElseThrow()))
.favorites(false)
.favorites(isFavorite)
.build();
}

public static ProductDetailsResponse toProductDetailsResponse(Product product) {

List<String> images = new ArrayList<>();
images.add(product.getThumbnail());

if (product.getPhotoUrls() != null) {
images.addAll(product.getPhotoUrls().stream().map(ProductImage::getPhotoUrl).toList());
}
public static ProductDetailsResponse toProductDetailsResponse(Product product, boolean isFavorite) {

return ProductDetailsResponse.builder()
.productId(product.getId())
Expand All @@ -60,8 +47,8 @@ public static ProductDetailsResponse toProductDetailsResponse(Product product) {
.productAmenityResponse(toProductAmenityResponse(product.getAmenity()))
.starAvg(product.getStarAvg())
.productOptionResponse(toProductOptionResponse(product.getProductOption()))
.favorites(false)
.images(images)
.favorites(isFavorite)
.images(getImage(product))
.rooms(product.getRooms().stream().map(RoomMapper::toRoomResponse).distinct().toList())
.build();
}
Expand Down Expand Up @@ -102,4 +89,24 @@ private static ProductOptionResponse toProductOptionResponse(ProductOption produ
.build();
}

private static long getPrice(Product product) {
List<Room> rooms = product.getRooms();
long price = rooms.isEmpty() ? 0 : rooms.stream().map(PricePickerByDateUtil::getPrice)
.min((o1, o2) -> Math.toIntExact(
o1 - o2)).orElseThrow();

return price == 0 ? 100000 : price;
}

private static List<String> getImage(Product product) {
List<String> images = new ArrayList<>();
images.add(product.getThumbnail());

if (product.getPhotoUrls() != null) {
images.addAll(product.getPhotoUrls().stream().map(ProductImage::getPhotoUrl).toList());
}

return images;
}

}
9 changes: 9 additions & 0 deletions src/main/java/com/fc/shimpyo_be/global/util/SecurityUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,13 @@ public Long getCurrentMemberId() {
}
return Long.parseLong(authentication.getName());
}

public Long getNullableCurrentMemberId() {
final Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (authentication == null || authentication.getName() == null) {
return null;
}
return Long.parseLong(authentication.getName());
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
package com.fc.shimpyo_be.domain.product.docs;

import static org.junit.matchers.JUnitMatchers.everyItem;
import static org.mockito.BDDMockito.given;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.fc.shimpyo_be.config.RestDocsSupport;
import com.fc.shimpyo_be.domain.favorite.entity.Favorite;
import com.fc.shimpyo_be.domain.favorite.repository.FavoriteRepository;
import com.fc.shimpyo_be.domain.member.entity.Authority;
import com.fc.shimpyo_be.domain.member.entity.Member;
import com.fc.shimpyo_be.domain.member.repository.MemberRepository;
import com.fc.shimpyo_be.domain.product.entity.Product;
import com.fc.shimpyo_be.domain.product.entity.ProductImage;
import com.fc.shimpyo_be.domain.product.factory.ProductFactory;
import com.fc.shimpyo_be.domain.product.repository.ProductImageRepository;
import com.fc.shimpyo_be.domain.product.repository.ProductRepository;
import com.fc.shimpyo_be.domain.room.entity.Room;
import com.fc.shimpyo_be.domain.room.repository.RoomRepository;
import com.fc.shimpyo_be.global.util.SecurityUtil;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.restdocs.payload.JsonFieldType;
Expand All @@ -45,6 +55,15 @@ class ProductRestIntegrationDocsTest extends RestDocsSupport {
@Autowired
private RedisTemplate<String, Object> restTemplate;

@Autowired
private FavoriteRepository favoriteRepository;

@Autowired
private MemberRepository memberRepository;

@MockBean
private SecurityUtil securityUtil;

@DisplayName("숙소 저장 후, 검색 조회 및 페이징할 수 있다.")
@Test
void getProducts() throws Exception {
Expand Down Expand Up @@ -301,5 +320,34 @@ void isAvailableForReservation() throws Exception {
));
}

@Test
@DisplayName("전체 조회에서 즐겨찾기 여부를 볼 수 있다.")
void isAvailableGetFavoriteInGetProducts() throws Exception {
//given
given(securityUtil.getNullableCurrentMemberId()).willReturn(1L);
Product product = productRepository.save(ProductFactory.createTestProduct());
Room room = roomRepository.save(ProductFactory.createTestRoom(product, 0L));
Member member = Member.builder()
.email("[email protected]")
.name("test")
.password("$10$ygrAExVYmFTkZn2d0.Pk3Ot5CNZwIBjZH5f.WW0AnUq4w4PtBi9Nm")
.photoUrl(
"https://fastly.picsum.photos/id/866/200/300.jpg?hmac=rcadCENKh4rD6MAp6V_ma-AyWv641M4iiOpe1RyFHeI")
.authority(Authority.ROLE_USER)
.build();
memberRepository.save(member);
Favorite favorite = Favorite.builder().product(product).member(member).build();
favoriteRepository.save(favorite);

// when
ResultActions getProductAction = mockMvc.perform(
get("/api/products"));

//then
getProductAction
.andDo(MockMvcResultHandlers.print()).andExpect(status().isOk()).andExpect(jsonPath("$.data.productResponses[0].favorites").value(true));

}


}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class ProductRestControllerTest {
void getAllProducts() {
//given
List<ProductResponse> productResponses = new ArrayList<>();
productResponses.add(ProductMapper.toProductResponse(ProductFactory.createTestProduct()));
productResponses.add(ProductMapper.toProductResponse(ProductFactory.createTestProduct(),false));
PaginatedProductResponse paginatedProductResponse = PaginatedProductResponse.builder()
.productResponses(productResponses)
.pageCount(1)
Expand All @@ -67,7 +67,7 @@ void getAllProducts() {
void getProductDetails() {
//given
Product product = ProductFactory.createTestProduct();
ProductDetailsResponse expectedResult = ProductMapper.toProductDetailsResponse(product);
ProductDetailsResponse expectedResult = ProductMapper.toProductDetailsResponse(product,false);
doReturn(expectedResult).when(productService)
.getProductDetails(1L, "2024-12-27", "2024-12-28");
//when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import com.fc.shimpyo_be.domain.product.util.ProductMapper;
import com.fc.shimpyo_be.domain.room.dto.response.RoomResponse;
import com.fc.shimpyo_be.domain.room.entity.Room;
import com.fc.shimpyo_be.global.util.SecurityUtil;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
Expand All @@ -34,6 +36,9 @@
@ExtendWith(MockitoExtension.class)
class ProductServiceTest {

@Mock
private SecurityUtil securityUtil;

@Mock
private ProductRepository productRepository;

Expand All @@ -44,6 +49,10 @@ class ProductServiceTest {
@InjectMocks
private ProductService productService;

@BeforeEach
void init() {
given(securityUtil.getNullableCurrentMemberId()).willReturn(null);
}

@Test
void getProducts() {
Expand All @@ -64,14 +73,15 @@ void getProducts() {

//then
assertThat(result.productResponses()).usingRecursiveAssertion().isEqualTo(
productPage.getContent().stream().map(ProductMapper::toProductResponse).toList());
productPage.getContent().stream()
.map(product -> ProductMapper.toProductResponse(product, false)).toList());
}

@Test
void getProductDetails() {
//given
Product product = ProductFactory.createTestProduct();
Room room = ProductFactory.createTestRoom(product,0L);
Room room = ProductFactory.createTestRoom(product, 0L);
product.getRooms().add(room);
given(productRepository.findById(product.getId())).willReturn(Optional.ofNullable(product));
doReturn(1L).when(
Expand All @@ -83,7 +93,8 @@ void getProductDetails() {
"2023-11-27", "2023-11-28");
//then
for (int i = 0; i < result.rooms().size(); i++) {
RoomResponse roomResponse = ProductMapper.toProductDetailsResponse(product).rooms()
RoomResponse roomResponse = ProductMapper.toProductDetailsResponse(product, false)
.rooms()
.get(i);
roomResponse.setRemaining(1L);
assertThat(result.rooms().get(i)).usingRecursiveComparison()
Expand Down