Skip to content

Commit

Permalink
[feat] #66 - 상점 상품 조회 api 구현 및 상품주인여부 반환 로직 추가 (#67)
Browse files Browse the repository at this point in the history
* [#66] feat(ProductWithFirstPhoto): vo에 storeId 추가

* [#66] feat: dto에 isOwnedByCurrentUser 추가

* [#66] feat: response에 isOwnedByCurrentUser 계산 및 dto에 삽입하도록 로직 수정

* [#66] feat: 상점의 상품 리스트 조회하는 repository 로직 findProductsByStoreIdAndSortOptionAndFilters 추가

* [#66] feat(ProductRetriever): 상점의 상품 리스트 조회용 retrieveStoreProducts 추가

* [#66] feat(ProductService): 상점의 상품 리스트 조회용 getStoreSellProducts, getStoreBuyProducts 추가

* [#66] feat(ProductController): 상점의 상품 리스트 조회용 getStoreSellProducts, getStoreBuyProducts 추가, isOwnedByCurrentUser 판단을 위한 파라미터 전달 추가

* [#66] fix(InterestRepository): 잘못된 쿼리문 수정
  • Loading branch information
hyerinhwang-sailin authored Jan 17, 2025
1 parent a9b91ba commit fec0b06
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ boolean existsByProductIdAndStoreId(@Param("productId") Long productId,

@Query("SELECT i.productId " +
"FROM InterestEntity i " +
"WHERE i.productId IN :productIds AND i.productId = :storeId")
"WHERE i.productId IN :productIds AND i.storeId = :storeId")
List<Long> findLikedProductIdsByStore(@Param("productIds") List<Long> productIds,
@Param("storeId") Long storeId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand Down Expand Up @@ -68,7 +69,7 @@ public ResponseEntity<SuccessResponse<ProductSellListResponse>> getSellProducts(

// 4. 응답 생성
ProductSellListResponse response = ProductSellListResponse.from(
parsedSortOption, pagination, interestMap, genreMap
parsedSortOption, pagination, interestMap, genreMap, storeId
);

// 5. 응답 반환
Expand Down Expand Up @@ -105,7 +106,7 @@ public ResponseEntity<SuccessResponse<ProductBuyListResponse>> getBuyProducts(
Map<Long, String> genreMap = fetchGenreMap(pagination);

ProductBuyListResponse response = ProductBuyListResponse.from(
parsedSortOption, pagination, interestMap, genreMap
parsedSortOption, pagination, interestMap, genreMap, storeId
);

return ResponseEntity.ok(
Expand Down Expand Up @@ -145,7 +146,7 @@ public ResponseEntity<SuccessResponse<ProductSellListResponse>> searchSellProduc
Map<Long, String> genreMap = fetchGenreMap(pagination);

ProductSellListResponse response = ProductSellListResponse.from(
parsedSortOption, pagination, interestMap, genreMap
parsedSortOption, pagination, interestMap, genreMap, storeId
);

return ResponseEntity.ok(
Expand Down Expand Up @@ -183,7 +184,85 @@ public ResponseEntity<SuccessResponse<ProductBuyListResponse>> searchBuyProducts
Map<Long, String> genreMap = fetchGenreMap(pagination);

ProductBuyListResponse response = ProductBuyListResponse.from(
parsedSortOption, pagination, interestMap, genreMap
parsedSortOption, pagination, interestMap, genreMap, storeId
);

return ResponseEntity.ok(
SuccessResponse.of(
ProductSuccessCode.PRODUCT_LIST_RETRIEVE_SUCCESS,
response
)
);
}

@GetMapping("sell/store/{storeOwnerId}")
public ResponseEntity<SuccessResponse<ProductSellListResponse>> getStoreSellProducts(
@RequestParam(defaultValue = "RECENT") String sortOption,
@RequestParam(defaultValue = "false") Boolean isOnSale,
@RequestParam(defaultValue = "false") Boolean isUnopened,
@RequestParam(required = false) List<Long> genreId,
@RequestParam(required = false) String cursor,
@RequestParam(defaultValue = "10") int size,
@PathVariable Long storeOwnerId,
@CurrentMember Long currentStoreId
) {
SortOption parsedSortOption = parseSortOption(sortOption);
CursorValues cursorValues = parseCursorValues(cursor, parsedSortOption);

ProductPagination pagination = productService.getStoreSellProducts(
storeOwnerId,
parsedSortOption,
cursorValues.getCursorProductId(),
cursorValues.getCursorOptionalValue(),
size,
isOnSale,
isUnopened,
genreId
);

Map<Long, Boolean> interestMap = fetchInterestMap(pagination, currentStoreId);
Map<Long, String> genreMap = fetchGenreMap(pagination);

ProductSellListResponse response = ProductSellListResponse.from(
parsedSortOption, pagination, interestMap, genreMap, currentStoreId
);

return ResponseEntity.ok(
SuccessResponse.of(
ProductSuccessCode.PRODUCT_LIST_RETRIEVE_SUCCESS,
response
)
);
}

@GetMapping("/buy/store/{storeOwnerId}")
public ResponseEntity<SuccessResponse<ProductBuyListResponse>> getStoreBuyProducts(
@RequestParam(defaultValue = "RECENT") String sortOption,
@RequestParam(defaultValue = "false") Boolean isOnSale,
@RequestParam(required = false) List<Long> genreId,
@RequestParam(required = false) String cursor,
@RequestParam(defaultValue = "10") int size,
@PathVariable Long storeOwnerId,
@CurrentMember Long currentStoreId
) {
SortOption parsedSortOption = parseSortOption(sortOption);
CursorValues cursorValues = parseCursorValues(cursor, parsedSortOption);

ProductPagination pagination = productService.getStoreBuyProducts(
storeOwnerId,
parsedSortOption,
cursorValues.getCursorProductId(),
cursorValues.getCursorOptionalValue(),
size,
isOnSale,
genreId
);

Map<Long, Boolean> interestMap = fetchInterestMap(pagination, currentStoreId);
Map<Long, String> genreMap = fetchGenreMap(pagination);

ProductBuyListResponse response = ProductBuyListResponse.from(
parsedSortOption, pagination, interestMap, genreMap, currentStoreId
);

return ResponseEntity.ok(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ public record ProductBuyDto(
boolean isInterested,
TradeType tradeType,
TradeStatus tradeStatus,
boolean isPriceNegotiable
boolean isPriceNegotiable,
boolean isOwnedByCurrentUser
) {
// ProductWithFirstPhoto에서 genreName을 외부에서 전달받는 메서드
public static ProductBuyDto from(
ProductWithFirstPhoto product,
String firstPhoto,
String uploadTime,
boolean isInterested,
String genreName
String genreName,
boolean isOwnedByCurrentUser
) {
return new ProductBuyDto(
product.getId(),
Expand All @@ -34,7 +36,8 @@ public static ProductBuyDto from(
isInterested,
product.getTradeType(),
product.getTradeStatus(),
product.isPriceNegotiable()
product.isPriceNegotiable(),
isOwnedByCurrentUser
);
}

Expand All @@ -49,11 +52,12 @@ public static ProductBuyDto from(
boolean isInterested,
TradeType tradeType,
TradeStatus tradeStatus,
boolean isPriceNegotiable
boolean isPriceNegotiable,
boolean isOwnedByCurrentUser
) {
return new ProductBuyDto(
productId, genreName, productName, photo, price, uploadTime, isInterested,
tradeType, tradeStatus, isPriceNegotiable
tradeType, tradeStatus, isPriceNegotiable, isOwnedByCurrentUser
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ public static ProductBuyListResponse from(
SortOption sortOption,
ProductPagination pagination,
Map<Long, Boolean> interestMap,
Map<Long, String> genreMap
Map<Long, String> genreMap,
Long currentStoreId
) {
// 1. DTO 생성
List<ProductBuyDto> productDtos = pagination.getProductList().stream()
.map(product -> {
String uploadTime = TimeUtils.calculateUploadTime(product.getCreatedAt());
boolean isInterested = interestMap.getOrDefault(product.getId(), false);
String genreName = genreMap.getOrDefault(product.getGenreId(), "기타"); // genreName 매핑
String genreName = genreMap.getOrDefault(product.getGenreId(), "기타");
boolean isOwnedByCurrentUser = currentStoreId.equals(product.getStoreId());

return ProductBuyDto.from(
product, product.getFirstPhoto(), uploadTime, isInterested, genreName
product, product.getFirstPhoto(), uploadTime, isInterested, genreName, isOwnedByCurrentUser
);
}).toList();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ public record ProductSellDto(
String uploadTime,
boolean isInterested,
TradeType tradeType,
TradeStatus tradeStatus
TradeStatus tradeStatus,
boolean isOwnedByCurrentUser
) {
// ProductWithFirstPhoto에서 genreName을 외부에서 전달받는 메서드
public static ProductSellDto from(
ProductWithFirstPhoto product,
String firstPhoto,
String uploadTime,
boolean isInterested,
String genreName
String genreName,
boolean isOwnedByCurrentUser
) {
return new ProductSellDto(
product.getId(),
Expand All @@ -32,7 +34,8 @@ public static ProductSellDto from(
uploadTime,
isInterested,
product.getTradeType(),
product.getTradeStatus()
product.getTradeStatus(),
isOwnedByCurrentUser
);
}

Expand All @@ -46,10 +49,11 @@ public static ProductSellDto from(
String uploadTime,
boolean isInterested,
TradeType tradeType,
TradeStatus tradeStatus
TradeStatus tradeStatus,
boolean isOwnedByCurrentUser
) {
return new ProductSellDto(
productId, genreName, productName, photo, price, uploadTime, isInterested, tradeType, tradeStatus
productId, genreName, productName, photo, price, uploadTime, isInterested, tradeType, tradeStatus, isOwnedByCurrentUser
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ public static ProductSellListResponse from(
SortOption sortOption,
ProductPagination pagination,
Map<Long, Boolean> interestMap,
Map<Long, String> genreMap // 추가
Map<Long, String> genreMap,
Long currentStoreId
) {
// 1. DTO 생성
List<ProductSellDto> productDtos = pagination.getProductList().stream()
.map(product -> {
String uploadTime = TimeUtils.calculateUploadTime(product.getCreatedAt());
boolean isInterested = interestMap.getOrDefault(product.getId(), false);
String genreName = genreMap.getOrDefault(product.getGenreId(), "기타"); // genreName 매핑
String genreName = genreMap.getOrDefault(product.getGenreId(), "기타");
boolean isOwnedByCurrentUser = currentStoreId.equals(product.getStoreId());

return ProductSellDto.from(
product, product.getFirstPhoto(), uploadTime, isInterested, genreName
product, product.getFirstPhoto(), uploadTime, isInterested, genreName, isOwnedByCurrentUser
);
}).toList();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,32 @@ public ProductPagination searchBuyProducts(
);
}

public ProductPagination getStoreSellProducts(
Long storeId, SortOption sortOption, Long cursorProductId, Integer cursorOptionalValue,
int size, Boolean isOnSale, Boolean isUnopened, List<Long> genreIds) {

return retrieveAndPreparePagination(
() -> productRetriever.retrieveStoreProducts(
storeId, sortOption, cursorProductId, cursorOptionalValue, size,
isOnSale, isUnopened, genreIds, TradeType.SELL
),
size
);
}

public ProductPagination getStoreBuyProducts(
Long storeId, SortOption sortOption, Long cursorProductId, Integer cursorOptionalValue,
int size, Boolean isOnSale, List<Long> genreIds) {

return retrieveAndPreparePagination(
() -> productRetriever.retrieveStoreProducts(
storeId, sortOption, cursorProductId, cursorOptionalValue, size,
isOnSale, null, genreIds, TradeType.BUY
),
size
);
}

private ProductPagination retrieveAndPreparePagination(
ProductRetrieval retrievalLogic,
int size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ List<ProductEntity> findProductsBySortOptionAndFilters(
List<ProductEntity> searchProductsBySearchWordAndSortOptionAndFilters(
String searchWord, OrderSpecifier<?> orderSpecifier, Long cursorProductId, Integer cursorOptionalValue, int size,
Boolean isOnSale, Boolean isUnopened, List<Long> genreIds, TradeType tradeType);

List<ProductEntity> findProductsByStoreIdAndSortOptionAndFilters(
Long storeId, OrderSpecifier<?> orderSpecifier, Long cursorProductId, Integer cursorOptionalValue,
int size, Boolean isOnSale, Boolean isUnopened, List<Long> genreIds, TradeType tradeType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,24 @@ public List<ProductEntity> searchProductsBySearchWordAndSortOptionAndFilters(
.fetch();
};

@Override
public List<ProductEntity> findProductsByStoreIdAndSortOptionAndFilters(
Long storeId, OrderSpecifier<?> orderSpecifier, Long cursorProductId, Integer cursorOptionalValue,
int size, Boolean isOnSale, Boolean isUnopened, List<Long> genreIds, TradeType tradeType) {
return queryFactory.selectFrom(productEntity)
.where(
productEntity.storeId.eq(storeId),
genreFilter(genreIds),
isUnopenedFilter(isUnopened),
tradeTypeFilter(tradeType),
isOnSaleFilter(isOnSale),
cursorFilter(cursorProductId, cursorOptionalValue, orderSpecifier)
)
.orderBy(orderSpecifier)
.limit(size + 1)
.fetch();
}

private BooleanExpression tradeTypeFilter(TradeType tradeType) {
if (tradeType != null) {
return productEntity.tradeType.eq(tradeType);
Expand Down Expand Up @@ -90,12 +108,13 @@ private BooleanExpression genreFilter(List<Long> genreIds) {
return null;
}

private BooleanExpression cursorFilter(Long cursorProductId, Integer cursorOptionalValue, OrderSpecifier<?> orderSpecifier) {
private BooleanExpression cursorFilter(Long cursorProductId, Integer cursorOptionalValue,
OrderSpecifier<?> orderSpecifier) {
if (cursorProductId == null) {
return null;
}

String fieldName = ((PathBuilder<?>) orderSpecifier.getTarget()).getMetadata().getName();
String fieldName = ((PathBuilder<?>)orderSpecifier.getTarget()).getMetadata().getName();
com.querydsl.core.types.Order direction = orderSpecifier.getOrder();

if (fieldName.equals("id")) {
Expand All @@ -104,7 +123,8 @@ private BooleanExpression cursorFilter(Long cursorProductId, Integer cursorOptio
: productEntity.id.loe(cursorProductId); // RECENT
} else if (fieldName.equals("interestCount")) {
return productEntity.interestCount.loe(cursorOptionalValue)
.or(productEntity.interestCount.eq(cursorOptionalValue).and(productEntity.id.loe(cursorProductId))); // POPULAR
.or(productEntity.interestCount.eq(cursorOptionalValue)
.and(productEntity.id.loe(cursorProductId))); // POPULAR
} else if (fieldName.equals("price")) {
return direction == com.querydsl.core.types.Order.ASC
? productEntity.price.goe(cursorOptionalValue) // LOW_PRICE
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/napzak/domain/product/core/ProductRetriever.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ public List<Product> retrieveProducts(
.toList();
}

@Transactional(readOnly = true)
public List<Product> retrieveStoreProducts(
Long storeId, SortOption sortOption, Long cursorProductId, Integer cursorOptionalValue,
int size, Boolean isOnSale, Boolean isUnopened, List<Long> genreIds, TradeType tradeType) {

return productRepository.findProductsByStoreIdAndSortOptionAndFilters(
storeId, sortOption.toOrderSpecifier(), cursorProductId, cursorOptionalValue, size,
isOnSale, isUnopened, genreIds, tradeType
).stream()
.map(Product::fromEntity)
.toList();
}

@Transactional(readOnly = true)
public List<Product> searchProducts(
String searchWord, SortOption sortOption, Long cursorProductId, Integer cursorOptionalValue, int size,
Expand Down
Loading

0 comments on commit fec0b06

Please sign in to comment.