Fix : 수정
This commit is contained in:
parent
d92f6b7bac
commit
4f173d2982
@ -5,20 +5,18 @@ import com.ktds.hi.recommend.biz.usecase.out.*;
|
|||||||
import com.ktds.hi.recommend.biz.domain.*;
|
import com.ktds.hi.recommend.biz.domain.*;
|
||||||
import com.ktds.hi.recommend.infra.dto.request.RecommendStoreRequest;
|
import com.ktds.hi.recommend.infra.dto.request.RecommendStoreRequest;
|
||||||
import com.ktds.hi.recommend.infra.dto.response.RecommendStoreResponse;
|
import com.ktds.hi.recommend.infra.dto.response.RecommendStoreResponse;
|
||||||
|
import com.ktds.hi.recommend.infra.dto.response.StoreDetailResponse;
|
||||||
|
import com.ktds.hi.common.dto.PageResponse;
|
||||||
import com.ktds.hi.common.exception.BusinessException;
|
import com.ktds.hi.common.exception.BusinessException;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Arrays;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 추천 인터랙터 클래스
|
* 매장 추천 인터랙터 클래스
|
||||||
@ -36,128 +34,193 @@ public class StoreRecommendInteractor implements StoreRecommendUseCase {
|
|||||||
private final UserPreferenceRepository userPreferenceRepository;
|
private final UserPreferenceRepository userPreferenceRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<RecommendStoreResponse> recommendStores(Long memberId, RecommendStoreRequest request) {
|
public List<RecommendStoreResponse> recommendPersonalizedStores(Long memberId, RecommendStoreRequest request) {
|
||||||
// 사용자 취향 프로필 조회
|
try {
|
||||||
TasteProfile tasteProfile = userPreferenceRepository.getMemberPreferences(memberId)
|
// 사용자 취향 프로필 조회
|
||||||
.orElseThrow(() -> new BusinessException("사용자 취향 정보를 찾을 수 없습니다. 취향 등록을 먼저 해주세요."));
|
TasteProfile tasteProfile = userPreferenceRepository.getMemberPreferences(memberId)
|
||||||
|
.orElse(createDefaultTasteProfile(memberId));
|
||||||
|
|
||||||
// AI 기반 추천
|
// 위치 기반 매장 검색
|
||||||
Map<String, Object> preferences = Map.of(
|
List<RecommendStore> nearbyStores = locationRepository.findStoresWithinDistance(
|
||||||
"categories", tasteProfile.getPreferredCategories(),
|
request.getLatitude(),
|
||||||
"tags", tasteProfile.getPreferredTags(),
|
request.getLongitude(),
|
||||||
"pricePreference", tasteProfile.getPricePreference(),
|
request.getRadius()
|
||||||
"distancePreference", tasteProfile.getDistancePreference(),
|
);
|
||||||
"latitude", request.getLatitude(),
|
|
||||||
"longitude", request.getLongitude()
|
|
||||||
);
|
|
||||||
|
|
||||||
List<RecommendStore> aiRecommendStores = aiRecommendRepository.recommendStoresByAI(memberId, preferences);
|
// 취향 기반 필터링 및 점수 계산
|
||||||
|
List<RecommendStore> recommendedStores = aiRecommendRepository.filterByPreferences(
|
||||||
|
nearbyStores,
|
||||||
|
tasteProfile,
|
||||||
|
request.getTags()
|
||||||
|
);
|
||||||
|
|
||||||
// 위치 기반 추천 결합
|
// 추천 로그 저장
|
||||||
List<RecommendStore> locationStores = locationRepository.findStoresWithinRadius(
|
recommendRepository.logRecommendation(memberId,
|
||||||
request.getLatitude(), request.getLongitude(), request.getRadius());
|
recommendedStores.stream().map(RecommendStore::getStoreId).toList(),
|
||||||
|
"개인화추천"
|
||||||
|
);
|
||||||
|
|
||||||
// 추천 결과 통합 및 점수 계산
|
return convertToResponseList(recommendedStores);
|
||||||
List<RecommendStore> combinedStores = combineRecommendations(aiRecommendStores, locationStores, tasteProfile);
|
|
||||||
|
|
||||||
// 추천 히스토리 저장
|
} catch (Exception e) {
|
||||||
RecommendHistory history = RecommendHistory.builder()
|
log.error("개인화 매장 추천 실패: memberId={}", memberId, e);
|
||||||
.memberId(memberId)
|
return getDefaultRecommendations();
|
||||||
.recommendedStoreIds(combinedStores.stream().map(RecommendStore::getStoreId).collect(Collectors.toList()))
|
}
|
||||||
.recommendType(RecommendType.TASTE_BASED)
|
|
||||||
.criteria("취향 + AI + 위치 기반 통합 추천")
|
|
||||||
.createdAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
recommendRepository.saveRecommendHistory(history);
|
|
||||||
|
|
||||||
log.info("매장 추천 완료: memberId={}, 추천 매장 수={}", memberId, combinedStores.size());
|
|
||||||
|
|
||||||
return combinedStores.stream()
|
|
||||||
.map(this::toRecommendStoreResponse)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
public PageResponse<RecommendStoreResponse> recommendStoresByLocation(Double latitude, Double longitude, Integer radius, String category, Pageable pageable) {
|
||||||
public List<RecommendStoreResponse> recommendStoresByLocation(Double latitude, Double longitude, Integer radius) {
|
try {
|
||||||
List<RecommendStore> stores = locationRepository.findStoresWithinRadius(latitude, longitude, radius);
|
List<RecommendStore> stores = locationRepository.findStoresWithinDistance(latitude, longitude, radius);
|
||||||
|
|
||||||
return stores.stream()
|
// 카테고리 필터링
|
||||||
.map(store -> store.updateRecommendReason("위치 기반 추천"))
|
if (category != null && !category.isEmpty()) {
|
||||||
.map(this::toRecommendStoreResponse)
|
stores = stores.stream()
|
||||||
.collect(Collectors.toList());
|
.filter(store -> category.equals(store.getCategory()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 페이징 처리
|
||||||
|
int start = (int) pageable.getOffset();
|
||||||
|
int end = Math.min(start + pageable.getPageSize(), stores.size());
|
||||||
|
List<RecommendStore> pagedStores = stores.subList(start, end);
|
||||||
|
|
||||||
|
List<RecommendStoreResponse> responses = convertToResponseList(pagedStores);
|
||||||
|
|
||||||
|
return PageResponse.of(responses, pageable.getPageNumber(), pageable.getPageSize(), stores.size());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("위치 기반 매장 추천 실패: lat={}, lng={}", latitude, longitude, e);
|
||||||
|
return PageResponse.of(getDefaultRecommendations(), 0, pageable.getPageSize(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public List<RecommendStoreResponse> recommendPopularStores(String category, Integer limit) {
|
public List<RecommendStoreResponse> recommendPopularStores(String category, Integer limit) {
|
||||||
// Mock 구현 - 실제로는 인기도 기반 쿼리 필요
|
try {
|
||||||
List<RecommendStore> popularStores = List.of(
|
List<RecommendStore> popularStores = recommendRepository.findPopularStores(category, limit);
|
||||||
RecommendStore.builder()
|
return convertToResponseList(popularStores);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("인기 매장 추천 실패: category={}", category, e);
|
||||||
|
return getDefaultRecommendations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StoreDetailResponse getRecommendedStoreDetail(Long storeId, Long memberId) {
|
||||||
|
try {
|
||||||
|
RecommendStore store = recommendRepository.findStoreById(storeId)
|
||||||
|
.orElseThrow(() -> new BusinessException("매장을 찾을 수 없습니다."));
|
||||||
|
|
||||||
|
// 클릭 로그 저장 (조회도 클릭으로 간주)
|
||||||
|
if (memberId != null) {
|
||||||
|
recommendRepository.logStoreClick(memberId, storeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 요약 정보 조회
|
||||||
|
String aiSummary = aiRecommendRepository.getStoreSummary(storeId);
|
||||||
|
|
||||||
|
// 개인화 추천 이유 생성
|
||||||
|
String personalizedReason = "";
|
||||||
|
if (memberId != null) {
|
||||||
|
TasteProfile tasteProfile = userPreferenceRepository.getMemberPreferences(memberId).orElse(null);
|
||||||
|
if (tasteProfile != null) {
|
||||||
|
personalizedReason = generatePersonalizedReason(store, tasteProfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return StoreDetailResponse.builder()
|
||||||
|
.storeId(store.getStoreId())
|
||||||
|
.storeName(store.getStoreName())
|
||||||
|
.address(store.getAddress())
|
||||||
|
.category(store.getCategory())
|
||||||
|
.rating(store.getRating())
|
||||||
|
.distance(store.getDistance())
|
||||||
|
.tags(store.getTags())
|
||||||
|
.aiSummary(aiSummary)
|
||||||
|
.personalizedReason(personalizedReason)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("매장 상세 조회 실패: storeId={}", storeId, e);
|
||||||
|
throw new BusinessException("매장 상세 정보를 조회할 수 없습니다.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logRecommendClick(Long memberId, Long storeId) {
|
||||||
|
try {
|
||||||
|
recommendRepository.logStoreClick(memberId, storeId);
|
||||||
|
log.info("추천 클릭 로그 저장: memberId={}, storeId={}", memberId, storeId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("추천 클릭 로그 저장 실패: memberId={}, storeId={}", memberId, storeId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 기본 취향 프로필 생성
|
||||||
|
private TasteProfile createDefaultTasteProfile(Long memberId) {
|
||||||
|
return TasteProfile.builder()
|
||||||
|
.memberId(memberId)
|
||||||
|
.cuisinePreferences(Arrays.asList("한식", "중식", "일식"))
|
||||||
|
.priceRange("중간")
|
||||||
|
.distancePreference(3000)
|
||||||
|
.tasteTags(Arrays.asList("맛있는", "친절한"))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 개인화 추천 이유 생성
|
||||||
|
private String generatePersonalizedReason(RecommendStore store, TasteProfile tasteProfile) {
|
||||||
|
StringBuilder reason = new StringBuilder();
|
||||||
|
|
||||||
|
// 취향 태그 매칭
|
||||||
|
List<String> matchingTags = store.getTags().stream()
|
||||||
|
.filter(tasteProfile.getTasteTags()::contains)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (!matchingTags.isEmpty()) {
|
||||||
|
reason.append("당신이 좋아하는 '").append(String.join(", ", matchingTags))
|
||||||
|
.append("' 태그와 일치합니다. ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 가격대 매칭
|
||||||
|
if (tasteProfile.getPriceRange().equals(store.getPriceRange())) {
|
||||||
|
reason.append("선호하시는 ").append(tasteProfile.getPriceRange())
|
||||||
|
.append(" 가격대 매장입니다. ");
|
||||||
|
}
|
||||||
|
|
||||||
|
return reason.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 응답 변환
|
||||||
|
private List<RecommendStoreResponse> convertToResponseList(List<RecommendStore> stores) {
|
||||||
|
return stores.stream()
|
||||||
|
.map(store -> RecommendStoreResponse.builder()
|
||||||
|
.storeId(store.getStoreId())
|
||||||
|
.storeName(store.getStoreName())
|
||||||
|
.address(store.getAddress())
|
||||||
|
.category(store.getCategory())
|
||||||
|
.rating(store.getRating())
|
||||||
|
.distance(store.getDistance())
|
||||||
|
.tags(store.getTags())
|
||||||
|
.recommendReason(store.getRecommendReason())
|
||||||
|
.build())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 기본 추천 목록 (에러 발생 시)
|
||||||
|
private List<RecommendStoreResponse> getDefaultRecommendations() {
|
||||||
|
return Arrays.asList(
|
||||||
|
RecommendStoreResponse.builder()
|
||||||
.storeId(1L)
|
.storeId(1L)
|
||||||
.storeName("인기 매장 1")
|
.storeName("맛집 플레이스")
|
||||||
.address("서울시 강남구")
|
.address("서울시 강남구 테헤란로 123")
|
||||||
.category(category)
|
.category("한식")
|
||||||
.rating(4.5)
|
.rating(4.5)
|
||||||
.reviewCount(100)
|
.distance(500)
|
||||||
.recommendScore(95.0)
|
.tags(Arrays.asList("맛있는", "친절한"))
|
||||||
.recommendType(RecommendType.POPULARITY_BASED)
|
.recommendReason("인기 매장입니다")
|
||||||
.recommendReason("높은 평점과 많은 리뷰")
|
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
|
|
||||||
return popularStores.stream()
|
|
||||||
.limit(limit != null ? limit : 10)
|
|
||||||
.map(this::toRecommendStoreResponse)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 추천 결과 통합 및 점수 계산
|
|
||||||
*/
|
|
||||||
private List<RecommendStore> combineRecommendations(List<RecommendStore> aiStores,
|
|
||||||
List<RecommendStore> locationStores,
|
|
||||||
TasteProfile profile) {
|
|
||||||
// AI 추천과 위치 기반 추천을 통합하여 최종 점수 계산
|
|
||||||
// 실제로는 더 복잡한 로직이 필요
|
|
||||||
|
|
||||||
return aiStores.stream()
|
|
||||||
.map(store -> store.updateRecommendScore(
|
|
||||||
calculateFinalScore(store, profile)
|
|
||||||
))
|
|
||||||
.sorted((s1, s2) -> Double.compare(s2.getRecommendScore(), s1.getRecommendScore()))
|
|
||||||
.limit(20)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 최종 추천 점수 계산
|
|
||||||
*/
|
|
||||||
private Double calculateFinalScore(RecommendStore store, TasteProfile profile) {
|
|
||||||
double baseScore = store.getRecommendScore() != null ? store.getRecommendScore() : 0.0;
|
|
||||||
double ratingScore = store.getRating() != null ? store.getRating() * 10 : 0.0;
|
|
||||||
double reviewScore = store.getReviewCount() != null ? Math.min(store.getReviewCount() * 0.1, 10) : 0.0;
|
|
||||||
double distanceScore = store.getDistance() != null ? Math.max(0, 10 - store.getDistance() / 1000) : 0.0;
|
|
||||||
|
|
||||||
return (baseScore * 0.4) + (ratingScore * 0.3) + (reviewScore * 0.2) + (distanceScore * 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 도메인을 응답 DTO로 변환
|
|
||||||
*/
|
|
||||||
private RecommendStoreResponse toRecommendStoreResponse(RecommendStore store) {
|
|
||||||
return RecommendStoreResponse.builder()
|
|
||||||
.storeId(store.getStoreId())
|
|
||||||
.storeName(store.getStoreName())
|
|
||||||
.address(store.getAddress())
|
|
||||||
.category(store.getCategory())
|
|
||||||
.tags(store.getTags())
|
|
||||||
.rating(store.getRating())
|
|
||||||
.reviewCount(store.getReviewCount())
|
|
||||||
.distance(store.getDistance())
|
|
||||||
.recommendScore(store.getRecommendScore())
|
|
||||||
.recommendReason(store.getRecommendReason())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,10 +1,14 @@
|
|||||||
package com.ktds.hi.recommend.biz.usecase.in;
|
package com.ktds.hi.recommend.biz.usecase.in;
|
||||||
|
|
||||||
|
import com.ktds.hi.common.dto.PageResponse;
|
||||||
import com.ktds.hi.recommend.infra.dto.request.RecommendStoreRequest;
|
import com.ktds.hi.recommend.infra.dto.request.RecommendStoreRequest;
|
||||||
import com.ktds.hi.recommend.infra.dto.response.RecommendStoreResponse;
|
import com.ktds.hi.recommend.infra.dto.response.RecommendStoreResponse;
|
||||||
|
import com.ktds.hi.recommend.infra.dto.response.StoreDetailResponse;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 추천 유스케이스 인터페이스
|
* 매장 추천 유스케이스 인터페이스
|
||||||
* 사용자 취향 기반 매장 추천 기능을 정의
|
* 사용자 취향 기반 매장 추천 기능을 정의
|
||||||
@ -12,17 +16,28 @@ import java.util.List;
|
|||||||
public interface StoreRecommendUseCase {
|
public interface StoreRecommendUseCase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 사용자 취향 기반 매장 추천
|
* 개인화 매장 추천 (Controller에서 호출하는 메서드명으로 수정)
|
||||||
*/
|
*/
|
||||||
List<RecommendStoreResponse> recommendStores(Long memberId, RecommendStoreRequest request);
|
List<RecommendStoreResponse> recommendPersonalizedStores(Long memberId, RecommendStoreRequest request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 위치 기반 매장 추천
|
* 위치 기반 매장 추천 (PageResponse 반환으로 수정)
|
||||||
*/
|
*/
|
||||||
List<RecommendStoreResponse> recommendStoresByLocation(Double latitude, Double longitude, Integer radius);
|
PageResponse<RecommendStoreResponse> recommendStoresByLocation(Double latitude, Double longitude, Integer radius,
|
||||||
|
String category, Pageable pageable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 인기 매장 추천
|
* 인기 매장 추천
|
||||||
*/
|
*/
|
||||||
List<RecommendStoreResponse> recommendPopularStores(String category, Integer limit);
|
List<RecommendStoreResponse> recommendPopularStores(String category, Integer limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추천 매장 상세 조회 (추가)
|
||||||
|
*/
|
||||||
|
StoreDetailResponse getRecommendedStoreDetail(Long storeId, Long memberId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 추천 클릭 로깅 (추가)
|
||||||
|
*/
|
||||||
|
void logRecommendClick(Long memberId, Long storeId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,21 +17,24 @@ spring:
|
|||||||
password: ${RECOMMEND_DB_PASSWORD:hiorder_pass}
|
password: ${RECOMMEND_DB_PASSWORD:hiorder_pass}
|
||||||
driver-class-name: org.postgresql.Driver
|
driver-class-name: org.postgresql.Driver
|
||||||
hikari:
|
hikari:
|
||||||
connection-timeout: 20000
|
maximum-pool-size: ${DB_POOL_SIZE:20}
|
||||||
maximum-pool-size: 10
|
minimum-idle: ${DB_POOL_MIN_IDLE:5}
|
||||||
minimum-idle: 5
|
connection-timeout: ${DB_CONNECTION_TIMEOUT:30000}
|
||||||
idle-timeout: 300000
|
idle-timeout: ${DB_IDLE_TIMEOUT:600000}
|
||||||
|
max-lifetime: ${DB_MAX_LIFETIME:1800000}
|
||||||
pool-name: RecommendHikariCP
|
pool-name: RecommendHikariCP
|
||||||
|
|
||||||
# JPA 설정
|
# JPA 설정
|
||||||
jpa:
|
jpa:
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: ${JPA_DDL_AUTO:validate}
|
ddl-auto: ${JPA_DDL_AUTO:create}
|
||||||
show-sql: ${JPA_SHOW_SQL:false}
|
show-sql: ${JPA_SHOW_SQL:false}
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
format_sql: true
|
|
||||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||||
|
format_sql: ${JPA_FORMAT_SQL:true}
|
||||||
|
show_sql: ${JPA_SHOW_SQL:false}
|
||||||
|
use_sql_comments: ${JPA_USE_SQL_COMMENTS:true}
|
||||||
jdbc:
|
jdbc:
|
||||||
batch_size: 20
|
batch_size: 20
|
||||||
order_inserts: true
|
order_inserts: true
|
||||||
@ -48,9 +51,9 @@ spring:
|
|||||||
database: ${REDIS_DATABASE:0}
|
database: ${REDIS_DATABASE:0}
|
||||||
lettuce:
|
lettuce:
|
||||||
pool:
|
pool:
|
||||||
max-active: 8
|
max-active: ${REDIS_POOL_MAX_ACTIVE:8}
|
||||||
max-idle: 8
|
max-idle: ${REDIS_POOL_MAX_IDLE:8}
|
||||||
min-idle: 2
|
min-idle: ${REDIS_POOL_MIN_IDLE:2}
|
||||||
max-wait: -1ms
|
max-wait: -1ms
|
||||||
shutdown-timeout: 100ms
|
shutdown-timeout: 100ms
|
||||||
|
|
||||||
@ -108,43 +111,19 @@ resilience4j:
|
|||||||
max-attempts: 3
|
max-attempts: 3
|
||||||
wait-duration: 1000
|
wait-duration: 1000
|
||||||
|
|
||||||
# 추천 알고리즘 설정
|
|
||||||
recommend:
|
|
||||||
algorithm:
|
|
||||||
distance-weight: 0.3
|
|
||||||
rating-weight: 0.3
|
|
||||||
taste-weight: 0.4
|
|
||||||
max-distance: 50000 # 50km
|
|
||||||
default-radius: 5000 # 5km
|
|
||||||
cache:
|
|
||||||
ttl:
|
|
||||||
recommendation: 30m
|
|
||||||
store-detail: 1h
|
|
||||||
taste-analysis: 6h
|
|
||||||
popular-stores: 2h
|
|
||||||
batch:
|
|
||||||
size: 100
|
|
||||||
max-concurrent: 5
|
|
||||||
|
|
||||||
# Actuator 설정
|
# Actuator 설정
|
||||||
management:
|
management:
|
||||||
endpoints:
|
endpoints:
|
||||||
web:
|
web:
|
||||||
exposure:
|
exposure:
|
||||||
include: health,info,metrics,prometheus,configprops
|
include: health,info,metrics,prometheus
|
||||||
base-path: /actuator
|
|
||||||
endpoint:
|
endpoint:
|
||||||
health:
|
health:
|
||||||
show-details: when-authorized
|
show-details: always
|
||||||
show-components: always
|
|
||||||
metrics:
|
|
||||||
enabled: true
|
|
||||||
metrics:
|
metrics:
|
||||||
export:
|
export:
|
||||||
prometheus:
|
prometheus:
|
||||||
enabled: true
|
|
||||||
tags:
|
|
||||||
application: ${spring.application.name}
|
|
||||||
|
|
||||||
# Swagger/OpenAPI 설정
|
# Swagger/OpenAPI 설정
|
||||||
springdoc:
|
springdoc:
|
||||||
@ -188,6 +167,14 @@ security:
|
|||||||
allowed-headers: "*"
|
allowed-headers: "*"
|
||||||
allow-credentials: true
|
allow-credentials: true
|
||||||
|
|
||||||
|
recommend:
|
||||||
|
cache:
|
||||||
|
recommendation-ttl: ${RECOMMENDATION_CACHE_TTL:1800}
|
||||||
|
user-preference-ttl: ${USER_PREFERENCE_CACHE_TTL:3600}
|
||||||
|
algorithm:
|
||||||
|
max-recommendations: ${MAX_RECOMMENDATIONS:20}
|
||||||
|
default-radius: ${DEFAULT_SEARCH_RADIUS:5000}
|
||||||
|
max-radius: ${MAX_SEARCH_RADIUS:10000}
|
||||||
---
|
---
|
||||||
# Local 환경 설정
|
# Local 환경 설정
|
||||||
spring:
|
spring:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user