통계 및 부가 기능 | 인기 매장 추천 API 구현

Faithful Dev·2025년 3월 17일

매장 예약 서비스

목록 보기
15/15

구현한 기능

  • 매장 추천 API 개발 (평점, 예약 수, 리뷰 수 기준 정렬)
  • 사용자 선호도에 따른 정렬 기준 선택 기능 (sortBy 파라미터)
  • 결과 개수 제한 기능 (limit 파라미터)
  • 각 매장의 평균 평점, 리뷰 수, 예약 수 정보 제공

코드 스냅샷

@GetMapping
public ResponseEntity<ApiResponse<List<StoreRecommendationDto.Response>>> getRecommendedStores(
        @RequestParam(defaultValue = "rating") String sortBy,
        @RequestParam(defaultValue = "10") Integer limit
) {
    StoreRecommendationDto.RecommendationCriteria criteria = StoreRecommendationDto.RecommendationCriteria.builder()
            .sortBy(sortBy)
            .limit(limit)
            .build();

    List<StoreRecommendationDto.Response> recommendations = 
            storeRecommendationService.getRecommendedStores(criteria);

    return ResponseEntity.ok(ApiResponse.success("인기 매장 목록을 성공적으로 조회했습니다.", recommendations));
}
@Transactional(readOnly = true)
public List<StoreRecommendationDto.Response> getRecommendedStores(StoreRecommendationDto.RecommendationCriteria criteria) {
    String sortBy = criteria.getSortBy() != null ? criteria.getSortBy().toLowerCase() : "rating";
    int limit = criteria.getLimit() != null ? criteria.getLimit() : 10;

    List<Store> stores = storeRepository.findAllByActiveOrderByNameAsc(true);
    
    List<StoreRecommendationDto.Response> recommendationResponses = stores.stream()
            .map(store -> {
                Long storeId = store.getId();
                Double averageRating = reviewRepository.getAverageRatingByStoreId(storeId);
                Long reviewCount = reviewRepository.countByStoreIdAndActive(storeId, true);
                Long reservationCount = reservationRepository.countByStoreId(storeId);
                
                if (averageRating == null) {
                    averageRating = 0.0;
                }
                
                return StoreRecommendationDto.Response.builder()
                        .storeId(storeId)
                        .name(store.getName())
                        .address(store.getAddress())
                        .description(store.getDescription())
                        .phoneNumber(store.getPhoneNumber())
                        .businessHours(store.getBusinessHours())
                        .averageRating(averageRating)
                        .reviewCount(reviewCount)
                        .reservationCount(reservationCount)
                        .latitude(store.getLatitude())
                        .longitude(store.getLongitude())
                        .build();
            })
            .collect(Collectors.toList());

    // 정렬 기준 적용
    switch (sortBy) {
        case "reservation":
            recommendationResponses.sort((a, b) -> b.getReservationCount().compareTo(a.getReservationCount()));
            break;
        case "review":
            recommendationResponses.sort((a, b) -> b.getReviewCount().compareTo(a.getReviewCount()));
            break;
        case "rating":
        default:
            recommendationResponses.sort((a, b) -> b.getAverageRating().compareTo(a.getAverageRating()));
            break;
    }

    // 결과 개수 제한
    return recommendationResponses.stream()
            .limit(limit)
            .collect(Collectors.toList());
}
profile
Turning Vision into Reality.

0개의 댓글