Skip to content

Commit

Permalink
[feat] #49 - 탐색탭 api 구현 (#65)
Browse files Browse the repository at this point in the history
* [#49] refactor: docker-compose 수정

* [#49] feat(TimeUtils): 업로드 시간 string화

* [#49] feat: ProductErrorCode, ProductSuccessCode 추가

* [#49] refactor(ProductEntity): 연관관계 제거

* [#49] refactor(Product): 연관관계 제거

* [#49] feat: 상품 리스트 조회를 위한 vo ProductWithFirstPhoto와 list 추가

* [#49] feat(ProductTableConstants): COLUMN_INTEREST_COUNT 추가

* [#49] feat(InterestEntity): 연관관계 제거

* [#49] feat(Interest): 연관관계 제거

* [#49] feat(SortOption): 정렬기준 enum 추가

* [#49] feat: 정렬기준별 커서 추가

* [#49] feat: 팔아요/구해요 상품리스트 조회용 dto 추가

* [#49] feat: ProductEntity Repository 조회 로직 추가

* [#49] feat(ProductRetriever): ProductEntity를 조회 후 Product vo로 변환하는 ProductRetriever 추가

* [#49] feat(ProductPhotoRepository): ProductPhotoEntity를 조회하는 repository 로직 추가

* [#49] feat(ProductPhotoRetriever): ProductPhotoEntity를 조회 후 ProductPhoto vo로 변환하는 ProductPhotoRetriever 추가

* [#49] feat(ProductPagination): product 리스트 조회 페이지네이션 적용하는 ProductPagination 추가

* [#49] feat(ProductService): retriever 호출해 pagination 생성하는 로직들 포함한 ProductService 추가

* [#49] feat: interest 정보 가져오는 repository, retriever 추가

* [#49] feat: Genre 정보 가져오는 repository, retriever 추가

* [#49] feat(ProductGenreFacade): Product에 필요한 Genre 정보 가져오는 ProductGenreFacade 추가

* [#49] feat(ProductInterestFacade): Product에 필요한 Interest 정보 가져오는 ProductInterestFacade 추가

* [#49] feat(ProductController): ProductController 추가
  • Loading branch information
hyerinhwang-sailin authored Jan 17, 2025
1 parent 56fd25e commit 14fece4
Show file tree
Hide file tree
Showing 37 changed files with 1,775 additions and 270 deletions.
3 changes: 3 additions & 0 deletions docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ services:
retries: 3
networks:
- napzak-dev-net
deploy:
restart_policy:
condition: none

networks:
napzak-dev-net:
Expand Down
3 changes: 3 additions & 0 deletions docker-compose-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ services:
retries: 3
networks:
- napzak-prod-net
deploy:
restart_policy:
condition: none

networks:
napzak-prod-net:
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/napzak/domain/genre/core/GenreRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.napzak.domain.genre.core;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

import com.napzak.domain.genre.core.entity.GenreEntity;

@Repository
public interface GenreRepository extends JpaRepository<GenreEntity, Long> {
@Query("SELECT g.name FROM GenreEntity g WHERE g.id = :id")
String findNameById(@Param("id") Long id);

@Query("SELECT g.id, g.name FROM GenreEntity g WHERE g.id IN :ids")
List<Object[]> findNamesByIds(@Param("ids") List<Long> ids);
}
30 changes: 30 additions & 0 deletions src/main/java/com/napzak/domain/genre/core/GenreRetriever.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.napzak.domain.genre.core;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class GenreRetriever {
private final GenreRepository genreRepository;

@Transactional(readOnly = true)
public String getGenreNameById(Long genreId) {
return genreRepository.findNameById(genreId);
}

@Transactional(readOnly = true)
public Map<Long, String> getGenreNamesByIds(List<Long> genreIds) {
return genreRepository.findNamesByIds(genreIds).stream()
.collect(Collectors.toMap(
result -> (Long)result[0], // Key: Genre ID
result -> (String)result[1] // Value: Genre Name
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.napzak.domain.interest.core;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.napzak.domain.interest.core.entity.InterestEntity;

@Repository
public interface InterestRepository extends JpaRepository<InterestEntity, Long> {
@Query("SELECT COUNT(i) > 0 " +
"FROM InterestEntity i " +
"WHERE i.productId = :productId AND i.storeId = :storeId")
boolean existsByProductIdAndStoreId(@Param("productId") Long productId,
@Param("storeId") Long storeId);

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

@Modifying
@Query("""
DELETE FROM InterestEntity i
WHERE i.storeId = :storeId
AND i.productId = :productId
""")
void deleteByProductIdAndStoreId(
@Param("productId") Long productId,
@Param("storeId") Long storeId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.napzak.domain.interest.core;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class InterestRetriever {
private final InterestRepository interestRepository;

@Transactional(readOnly = true)
public boolean getIsInterested(Long productId, Long storeId) {
return interestRepository.existsByProductIdAndStoreId(productId, storeId);
}

@Transactional(readOnly = true)
public Map<Long, Boolean> areProductsInterested(List<Long> productIds, Long storeId) {
// Repository에서 좋아요된 상품 ID 목록 조회
List<Long> likedProductIds = interestRepository.findLikedProductIdsByStore(productIds, storeId);

// 상품 ID 리스트와 조회된 좋아요 ID를 매핑하여 Boolean 결과 생성
return productIds.stream()
.collect(Collectors.toMap(
productId -> productId, // 상품 ID
likedProductIds::contains // 좋아요 여부
));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.napzak.domain.interest.core.entity;

import com.napzak.domain.product.core.entity.ProductEntity;
import com.napzak.domain.store.core.entity.StoreEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
Expand All @@ -10,44 +8,40 @@

import static com.napzak.domain.interest.core.entity.InterestTableConstants.*;


@Table(name = TABLE_INTEREST)
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class InterestEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = COLUMN_ID)
private Long id;

@ManyToOne
@JoinColumn(name = COLUMN_STORE_ID, nullable = false)
private StoreEntity storeEntity;

@ManyToOne
@JoinColumn(name = COLUMN_PRODUCT_ID, nullable = false)
private ProductEntity productEntity;

@Builder
public InterestEntity(
Long id,
StoreEntity storeEntity,
ProductEntity productEntity) {
this.id = id;
this.storeEntity = storeEntity;
this.productEntity = productEntity;
}


public static InterestEntity create(
final StoreEntity storeEntity,
final ProductEntity productEntity
){
return InterestEntity.builder()
.storeEntity(storeEntity)
.productEntity(productEntity)
.build();
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = COLUMN_ID)
private Long id;

@Column(name = COLUMN_PRODUCT_ID, nullable = false)
private Long productId;

@Column(name = COLUMN_STORE_ID, nullable = false)
private Long storeId;

@Builder
public InterestEntity(
Long id,
Long productId,
Long storeId) {
this.id = id;
this.productId = productId;
this.storeId = storeId;
}

public static InterestEntity create(
final Long productId,
final Long storeId
) {
return InterestEntity.builder()
.productId(productId)
.storeId(storeId)
.build();
}
}
18 changes: 9 additions & 9 deletions src/main/java/com/napzak/domain/interest/core/vo/Interest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@
@Getter
public class Interest {
private final Long id;
private final StoreEntity storeEntity;
private final ProductEntity productEntity;
private final Long storeId;
private final Long productId;

public Interest(Long id, StoreEntity storeEntity, ProductEntity productEntity) {
public Interest(Long id, Long storeId, Long productId) {
this.id = id;
this.storeEntity = storeEntity;
this.productEntity = productEntity;
this.storeId = storeId;
this.productId = productId;
}

public static Interest fromEntity(InterestEntity interestEntity) {
return new Interest(
interestEntity.getId(),
interestEntity.getStoreEntity(),
interestEntity.getProductEntity()
interestEntity.getId(),
interestEntity.getStoreId(),
interestEntity.getProductId()
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.napzak.domain.product.api;

import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

import com.napzak.domain.genre.core.GenreRetriever;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class ProductGenreFacade {
private final GenreRetriever genreRetriever;

public String getGenreName(Long genreId) {
return genreRetriever.getGenreNameById(genreId);
}

public Map<Long, String> getGenreNames(List<Long> genreIds) {
return genreRetriever.getGenreNamesByIds(genreIds);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.napzak.domain.product.api;

import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Component;

import com.napzak.domain.interest.core.InterestRetriever;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class ProductInterestFacade {
private final InterestRetriever interestRetriever;

public boolean getIsInterested(Long productId, Long storeId) {
return interestRetriever.getIsInterested(productId, storeId);
}

public Map<Long, Boolean> getIsInterestedMap(List<Long> productIds, Long storeId) {
return interestRetriever.areProductsInterested(productIds, storeId);
}
}
Loading

0 comments on commit 14fece4

Please sign in to comment.