diff --git a/src/main/java/com/example/repick/domain/product/api/ProductController.java b/src/main/java/com/example/repick/domain/product/api/ProductController.java index e13c846d..15c27617 100644 --- a/src/main/java/com/example/repick/domain/product/api/ProductController.java +++ b/src/main/java/com/example/repick/domain/product/api/ProductController.java @@ -36,11 +36,6 @@ public SuccessResponse updateProduct(@PathVariable Long product return SuccessResponse.createSuccess(productService.updateProduct(productId, patchProduct)); } - @GetMapping("/like") - public SuccessResponse likeProduct(@RequestParam Long productId) { - return SuccessResponse.createSuccess(productService.likeProduct(productId)); - } - @GetMapping("/recommendation") public SuccessResponse> getMainPageRecommendation( @Parameter(description = "조회 의류 성별") @RequestParam(required = false) String gender, @@ -114,4 +109,23 @@ public SuccessResponse> getHighestDiscountProduct( return SuccessResponse.success(productService.getHighestDiscount(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize)); } + @GetMapping("/like") + public SuccessResponse toggleLike(@RequestParam Long productId) { + return SuccessResponse.createSuccess(productService.toggleLike(productId)); + } + + @GetMapping("/liked") + public SuccessResponse> getLikedProduct( + @Parameter(description = "카테고리") @RequestParam(required = false) String category, + @Parameter(description = "1번째 페이지 조회시 null, " + + "2번째 이상 페이지 조회시 직전 페이지의 마지막 episode id") @RequestParam(required = false) Long cursorId, + @Parameter(description = "한 페이지에 가져올 에피소드 개수, 기본값 4") @RequestParam(required = false) Integer pageSize) { + return SuccessResponse.success(productService.getLiked(category, cursorId, pageSize)); + } + + @GetMapping("/cart") + public SuccessResponse toggleCart(@RequestParam Long productId) { + return SuccessResponse.createSuccess(productService.toggleCart(productId)); + } + } diff --git a/src/main/java/com/example/repick/domain/product/entity/ProductCart.java b/src/main/java/com/example/repick/domain/product/entity/ProductCart.java new file mode 100644 index 00000000..d269e233 --- /dev/null +++ b/src/main/java/com/example/repick/domain/product/entity/ProductCart.java @@ -0,0 +1,35 @@ +package com.example.repick.domain.product.entity; + +import com.example.repick.global.entity.BaseEntity; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.NoArgsConstructor; + +@Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) +public class ProductCart extends BaseEntity { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Long userId; + private Long productId; + + @Builder + public ProductCart(Long userId, Long productId) { + this.userId = userId; + this.productId = productId; + } + + public static ProductCart of(Long userId, Long productId) { + return ProductCart.builder() + .userId(userId) + .productId(productId) + .build(); + } + + +} diff --git a/src/main/java/com/example/repick/domain/product/entity/ProductLike.java b/src/main/java/com/example/repick/domain/product/entity/ProductLike.java index 0786e201..33e3c101 100644 --- a/src/main/java/com/example/repick/domain/product/entity/ProductLike.java +++ b/src/main/java/com/example/repick/domain/product/entity/ProductLike.java @@ -1,5 +1,6 @@ package com.example.repick.domain.product.entity; +import com.example.repick.global.entity.BaseEntity; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -9,7 +10,7 @@ import lombok.NoArgsConstructor; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ProductLike { +public class ProductLike extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; diff --git a/src/main/java/com/example/repick/domain/product/repository/ProductCartRepository.java b/src/main/java/com/example/repick/domain/product/repository/ProductCartRepository.java new file mode 100644 index 00000000..d4be0c41 --- /dev/null +++ b/src/main/java/com/example/repick/domain/product/repository/ProductCartRepository.java @@ -0,0 +1,10 @@ +package com.example.repick.domain.product.repository; + +import com.example.repick.domain.product.entity.ProductCart; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface ProductCartRepository extends JpaRepository { + Optional findByUserIdAndProductId(Long userId, Long productId); +} diff --git a/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryCustom.java b/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryCustom.java index 33582b2e..67b36293 100644 --- a/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryCustom.java +++ b/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryCustom.java @@ -60,4 +60,6 @@ List findHighestDiscountProducts( Long userId); List findMainPageRecommendation(Long cursorId, Integer pageSize, Long userId, Gender gender); + + List findLikedProducts(String category, Long cursorId, Integer pageSize, Long userId); } diff --git a/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java b/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java index 9e269a0c..4060cfb7 100644 --- a/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java +++ b/src/main/java/com/example/repick/domain/product/repository/ProductRepositoryImpl.java @@ -223,6 +223,41 @@ public List findHighestDiscountProducts( .collect(Collectors.toList()); } + @Override + public List findLikedProducts(String category, Long cursorId, Integer pageSize, Long userId) { + return jpaQueryFactory + .select(Projections.constructor(GetProductThumbnail.class, + product.id, + product.thumbnailImageUrl, + product.productName, + product.price, + product.discountRate, + product.brandName, + product.qualityRate.stringValue(), + productLike.id.isNotNull())) + .from(product) + .leftJoin(productLike) + .on(product.id.eq(productLike.productId) + .and(productLike.userId.eq(userId))) + .leftJoin(productCategory) + .on(product.id.eq(productCategory.product.id)) + .where( + likeFilter(userId), + categoryFilter(category), + ltProductId(cursorId), + deletedFilter()) + .orderBy(product.discountRate.desc()) + .limit(pageSize) + .fetch() + .stream() + .distinct() + .collect(Collectors.toList()); + } + + private BooleanExpression likeFilter(Long userId) { + return productLike.userId.eq(userId); + } + private BooleanExpression genderFilter(String gender) { return gender != null ? product.gender.eq(Gender.fromValue(gender)) : null; } diff --git a/src/main/java/com/example/repick/domain/product/service/ProductService.java b/src/main/java/com/example/repick/domain/product/service/ProductService.java index 91d54aee..610171d4 100644 --- a/src/main/java/com/example/repick/domain/product/service/ProductService.java +++ b/src/main/java/com/example/repick/domain/product/service/ProductService.java @@ -30,6 +30,7 @@ public class ProductService { private final S3UploadService s3UploadService; private final UserRepository userRepository; private final ProductLikeRepository productLikeRepository; + private final ProductCartRepository productCartRepository; private String uploadImage(List images, Product product) { String thumbnailGeneratedUrl = null; @@ -148,16 +149,6 @@ public List getMainPageRecommendation(String gender, Long c return productRepository.findMainPageRecommendation(cursorId, pageSize, user.getId(), Gender.fromValue(gender)); } - public Boolean likeProduct(Long productId) { - User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) - .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); - - productLikeRepository.findByUserIdAndProductId(user.getId(), productId) - .ifPresentOrElse(productLikeRepository::delete, () -> productLikeRepository.save(ProductLike.of(user.getId(), productId))); - - return true; - } - public List getLatest(String gender, String category, List styles, Long minPrice, Long maxPrice, List brandNames, List qualityRates, List sizes, Long cursorId, Integer pageSize) { User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); @@ -193,4 +184,33 @@ public List getHighestDiscount(String gender, String catego return productRepository.findHighestDiscountProducts(gender, category, styles, minPrice, maxPrice, brandNames, qualityRates, sizes, cursorId, pageSize, user.getId()); } + + public Boolean toggleLike(Long productId) { + User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) + .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); + + productLikeRepository.findByUserIdAndProductId(user.getId(), productId) + .ifPresentOrElse(productLikeRepository::delete, () -> productLikeRepository.save(ProductLike.of(user.getId(), productId))); + + return true; + } + + public List getLiked(String category, Long cursorId, Integer pageSize) { + User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) + .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); + + if (pageSize == null) pageSize = 4; + + return productRepository.findLikedProducts(category, cursorId, pageSize, user.getId()); + } + + public Boolean toggleCart(Long productId) { + User user = userRepository.findByProviderId(SecurityContextHolder.getContext().getAuthentication().getName()) + .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다.")); + + productCartRepository.findByUserIdAndProductId(user.getId(), productId) + .ifPresentOrElse(productCartRepository::delete, () -> productCartRepository.save(ProductCart.of(user.getId(), productId))); + + return true; + } }