feat : 리뷰 조회 추가
This commit is contained in:
parent
2eefb269e6
commit
458f532b31
@ -17,6 +17,7 @@ public interface ExternalReviewPort {
|
|||||||
* 최근 리뷰 데이터 조회
|
* 최근 리뷰 데이터 조회
|
||||||
*/
|
*/
|
||||||
List<String> getRecentReviews(Long storeId, Integer days);
|
List<String> getRecentReviews(Long storeId, Integer days);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 리뷰 개수 조회
|
* 리뷰 개수 조회
|
||||||
|
|||||||
@ -96,7 +96,29 @@ public class ReviewInteractor implements CreateReviewUseCase, DeleteReviewUseCas
|
|||||||
.build())
|
.build())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ReviewListResponse> getStoreRecentReviews(Long storeId, Integer page, Integer size, Integer days) {
|
||||||
|
Pageable pageable = PageRequest.of(page != null ? page : 0, size == null || size == 0 ? 1000 : size);
|
||||||
|
|
||||||
|
LocalDateTime cutoffDate = LocalDateTime.now().minusDays(days);
|
||||||
|
Page<Review> reviews = reviewRepository.findRecentReviewsByStoreId(storeId, pageable, cutoffDate);
|
||||||
|
|
||||||
|
return reviews.stream()
|
||||||
|
.filter(review -> review.getStatus() == ReviewStatus.ACTIVE)
|
||||||
|
.map(review -> ReviewListResponse.builder()
|
||||||
|
.reviewId(review.getId())
|
||||||
|
.memberNickname(review.getMemberNickname())
|
||||||
|
.rating(review.getRating())
|
||||||
|
.content(review.getContent())
|
||||||
|
.imageUrls(review.getImageUrls())
|
||||||
|
.likeCount(review.getLikeCount())
|
||||||
|
.dislikeCount(review.getDislikeCount())
|
||||||
|
.createdAt(review.getCreatedAt())
|
||||||
|
.build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ReviewDetailResponse getReviewDetail(Long reviewId) {
|
public ReviewDetailResponse getReviewDetail(Long reviewId) {
|
||||||
|
|||||||
@ -15,6 +15,11 @@ public interface GetReviewUseCase {
|
|||||||
* 매장 리뷰 목록 조회
|
* 매장 리뷰 목록 조회
|
||||||
*/
|
*/
|
||||||
List<ReviewListResponse> getStoreReviews(Long storeId, Integer page, Integer size);
|
List<ReviewListResponse> getStoreReviews(Long storeId, Integer page, Integer size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 최근 리뷰 목록 조회
|
||||||
|
*/
|
||||||
|
List<ReviewListResponse> getStoreRecentReviews(Long storeId, Integer page, Integer size, Integer days);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 리뷰 상세 조회
|
* 리뷰 상세 조회
|
||||||
|
|||||||
@ -3,7 +3,9 @@ package com.ktds.hi.review.biz.usecase.out;
|
|||||||
import com.ktds.hi.review.biz.domain.Review;
|
import com.ktds.hi.review.biz.domain.Review;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@ -34,6 +36,11 @@ public interface ReviewRepository {
|
|||||||
*/
|
*/
|
||||||
Page<Review> findReviewsByStoreIdOrderByCreatedAtDesc(Long storeId, Pageable pageable);
|
Page<Review> findReviewsByStoreIdOrderByCreatedAtDesc(Long storeId, Pageable pageable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 ID와 일자 데이터로 최근 리뷰 목록 조회
|
||||||
|
*/
|
||||||
|
Page<Review> findRecentReviewsByStoreId(Long storeId, Pageable pageable, @Param("cutoffDate") LocalDateTime cutoffDate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 회원 ID로 리뷰 목록 조회
|
* 회원 ID로 리뷰 목록 조회
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -52,6 +52,24 @@ public class ReviewController {
|
|||||||
List<ReviewListResponse> reviews = getReviewUseCase.getStoreReviews(storeId, page, size);
|
List<ReviewListResponse> reviews = getReviewUseCase.getStoreReviews(storeId, page, size);
|
||||||
return ResponseEntity.ok(reviews);
|
return ResponseEntity.ok(reviews);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 최근 매장 리뷰 목록 조회 API
|
||||||
|
*/
|
||||||
|
@GetMapping("/stores/recent/{storeId}")
|
||||||
|
@Operation(summary = "매장 최근 리뷰 목록 조회", description = "특정 매장의 최근 리뷰 목록을 조회합니다.")
|
||||||
|
public ResponseEntity<List<ReviewListResponse>> getStoreRecentReviews(
|
||||||
|
@PathVariable Long storeId,
|
||||||
|
@RequestParam(defaultValue = "0") Integer page,
|
||||||
|
@RequestParam(defaultValue = "0") Integer size,
|
||||||
|
@RequestParam Integer days
|
||||||
|
) {
|
||||||
|
|
||||||
|
List<ReviewListResponse> reviews = getReviewUseCase.getStoreRecentReviews(storeId, page, size, days);
|
||||||
|
return ResponseEntity.ok(reviews);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 리뷰 상세 조회 API
|
* 리뷰 상세 조회 API
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import org.springframework.data.domain.Page;
|
|||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +48,17 @@ public class ReviewRepositoryAdapter implements ReviewRepository {
|
|||||||
pageable);
|
pageable);
|
||||||
return entities.map(this::toDomain);
|
return entities.map(this::toDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<Review> findRecentReviewsByStoreId(Long storeId, Pageable pageable,
|
||||||
|
LocalDateTime cutoffDate) {
|
||||||
|
|
||||||
|
Page<ReviewEntity> entities = reviewJpaRepository.findRecentReviewsByStoreId(storeId, ReviewStatus.ACTIVE,
|
||||||
|
cutoffDate,pageable);
|
||||||
|
return entities.map(this::toDomain);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<Review> findReviewsByMemberId(Long memberId, Pageable pageable) {
|
public Page<Review> findReviewsByMemberId(Long memberId, Pageable pageable) {
|
||||||
Page<ReviewEntity> entities = reviewJpaRepository.findByMemberIdAndStatus(memberId, ReviewStatus.ACTIVE, pageable);
|
Page<ReviewEntity> entities = reviewJpaRepository.findByMemberIdAndStatus(memberId, ReviewStatus.ACTIVE, pageable);
|
||||||
|
|||||||
@ -1,46 +1,58 @@
|
|||||||
package com.ktds.hi.review.infra.gateway.repository;
|
package com.ktds.hi.review.infra.gateway.repository;
|
||||||
|
|
||||||
import com.ktds.hi.review.biz.domain.ReviewStatus;
|
import com.ktds.hi.review.biz.domain.ReviewStatus;
|
||||||
import com.ktds.hi.review.infra.gateway.entity.ReviewEntity;
|
import com.ktds.hi.review.infra.gateway.entity.ReviewEntity;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Optional;
|
||||||
/**
|
|
||||||
* 리뷰 JPA 리포지토리 인터페이스
|
/**
|
||||||
* 리뷰 데이터의 CRUD 작업을 담당
|
* 리뷰 JPA 리포지토리 인터페이스
|
||||||
*/
|
* 리뷰 데이터의 CRUD 작업을 담당
|
||||||
@Repository
|
*/
|
||||||
public interface ReviewJpaRepository extends JpaRepository<ReviewEntity, Long> {
|
@Repository
|
||||||
|
public interface ReviewJpaRepository extends JpaRepository<ReviewEntity, Long> {
|
||||||
/**
|
|
||||||
* 매장 ID와 상태로 리뷰 목록 조회
|
/**
|
||||||
*/
|
* 매장 ID와 상태로 리뷰 목록 조회
|
||||||
Page<ReviewEntity> findByStoreIdAndStatus(Long storeId, ReviewStatus status, Pageable pageable);
|
*/
|
||||||
|
Page<ReviewEntity> findByStoreIdAndStatus(Long storeId, ReviewStatus status, Pageable pageable);
|
||||||
/**
|
|
||||||
* 회원 ID와 상태로 리뷰 목록 조회
|
/**
|
||||||
*/
|
* 회원 ID와 상태로 리뷰 목록 조회
|
||||||
Page<ReviewEntity> findByMemberIdAndStatus(Long memberId, ReviewStatus status, Pageable pageable);
|
*/
|
||||||
|
Page<ReviewEntity> findByMemberIdAndStatus(Long memberId, ReviewStatus status, Pageable pageable);
|
||||||
/**
|
|
||||||
* 리뷰 ID와 회원 ID로 리뷰 조회
|
@Query("SELECT r FROM ReviewEntity r WHERE r.storeId = :storeId " +
|
||||||
*/
|
"AND r.status = :status " +
|
||||||
Optional<ReviewEntity> findByIdAndMemberId(Long id, Long memberId);
|
"AND (CAST(:cutoffDate AS TIMESTAMP) IS NULL OR r.createdAt >= :cutoffDate) " +
|
||||||
|
"ORDER BY r.createdAt DESC")
|
||||||
|
Page<ReviewEntity> findRecentReviewsByStoreId(
|
||||||
/**
|
@Param("storeId") Long storeId,
|
||||||
* ✅ 수정: 매장ID + 내용으로 중복 리뷰 체크
|
@Param("status") ReviewStatus status,
|
||||||
*/
|
@Param("cutoffDate") LocalDateTime cutoffDate,
|
||||||
boolean existsByStoreIdAndContent(Long storeId, String content);
|
Pageable pageable
|
||||||
|
);
|
||||||
/**
|
|
||||||
* 대안: 외부 닉네임으로만 중복 체크 (더 간단한 방법)
|
/**
|
||||||
*/
|
* 리뷰 ID와 회원 ID로 리뷰 조회
|
||||||
// boolean existsByStoreIdAndExternalNickname(Long storeId, String externalNickname);
|
*/
|
||||||
}
|
Optional<ReviewEntity> findByIdAndMemberId(Long id, Long memberId);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ✅ 수정: 매장ID + 내용으로 중복 리뷰 체크
|
||||||
|
*/
|
||||||
|
boolean existsByStoreIdAndContent(Long storeId, String content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 대안: 외부 닉네임으로만 중복 체크 (더 간단한 방법)
|
||||||
|
*/
|
||||||
|
// boolean existsByStoreIdAndExternalNickname(Long storeId, String externalNickname);
|
||||||
|
}
|
||||||
|
|||||||
@ -5,4 +5,7 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
|
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
||||||
implementation 'com.azure:azure-messaging-eventhubs:5.15.0'
|
implementation 'com.azure:azure-messaging-eventhubs:5.15.0'
|
||||||
|
|
||||||
|
implementation 'com.azure:azure-storage-blob:12.22.1'
|
||||||
|
implementation 'com.azure:azure-storage-common:12.21.1'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.ktds.hi.store.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import com.azure.storage.blob.BlobServiceClient;
|
||||||
|
import com.azure.storage.blob.BlobServiceClientBuilder;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Azure Blob Storage 설정
|
||||||
|
*
|
||||||
|
* @author 하이오더 개발팀
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
// @Configuration
|
||||||
|
public class AzureStorageConfig {
|
||||||
|
|
||||||
|
@Value("${azure.storage.connection-string}")
|
||||||
|
private String connectionString;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public BlobServiceClient blobServiceClient() {
|
||||||
|
log.info("Azure Blob Storage 클라이언트 초기화");
|
||||||
|
return new BlobServiceClientBuilder()
|
||||||
|
.connectionString(connectionString)
|
||||||
|
.buildClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user