feat(app): finalize ad gated flows and weather
- add AppLogger and replace scattered print logging\n- implement ad-gated recommendation flow with reminder handling and calendar link\n- complete Bluetooth share pipeline with ad gate and merge\n- integrate KMA weather API with caching and dart-define decoding\n- add NaverUrlProcessor refactor and restore restaurant repository tests
This commit is contained in:
@@ -50,74 +50,109 @@ class RecommendationNotifier extends StateNotifier<AsyncValue<Restaurant?>> {
|
||||
: super(const AsyncValue.data(null));
|
||||
|
||||
/// 랜덤 추천 실행
|
||||
Future<void> getRandomRecommendation({
|
||||
Future<Restaurant?> getRandomRecommendation({
|
||||
required double maxDistance,
|
||||
required List<String> selectedCategories,
|
||||
List<String> excludedRestaurantIds = const [],
|
||||
bool shouldSaveRecord = true,
|
||||
}) async {
|
||||
state = const AsyncValue.loading();
|
||||
|
||||
try {
|
||||
// 현재 위치 가져오기
|
||||
final location = await _ref.read(currentLocationProvider.future);
|
||||
if (location == null) {
|
||||
throw Exception('위치 정보를 가져올 수 없습니다');
|
||||
}
|
||||
|
||||
// 날씨 정보 가져오기
|
||||
final weather = await _ref.read(weatherProvider.future);
|
||||
|
||||
// 사용자 설정 가져오기
|
||||
final userSettings = await _ref.read(userSettingsProvider.future);
|
||||
|
||||
// 모든 식당 가져오기
|
||||
final allRestaurants = await _ref.read(restaurantListProvider.future);
|
||||
|
||||
// 방문 기록 가져오기
|
||||
final allVisitRecords = await _ref.read(visitRecordsProvider.future);
|
||||
|
||||
// 추천 설정 구성
|
||||
final config = RecommendationConfig(
|
||||
userLatitude: location.latitude,
|
||||
userLongitude: location.longitude,
|
||||
final selectedRestaurant = await _generateCandidate(
|
||||
maxDistance: maxDistance,
|
||||
selectedCategories: selectedCategories,
|
||||
userSettings: userSettings,
|
||||
weather: weather,
|
||||
excludedRestaurantIds: excludedRestaurantIds,
|
||||
);
|
||||
|
||||
// 추천 엔진 사용
|
||||
final selectedRestaurant = await _recommendationEngine
|
||||
.generateRecommendation(
|
||||
allRestaurants: allRestaurants,
|
||||
recentVisits: allVisitRecords,
|
||||
config: config,
|
||||
);
|
||||
|
||||
if (selectedRestaurant == null) {
|
||||
state = const AsyncValue.data(null);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
// 추천 기록 저장
|
||||
await _saveRecommendationRecord(selectedRestaurant);
|
||||
if (shouldSaveRecord) {
|
||||
await saveRecommendationRecord(selectedRestaurant);
|
||||
}
|
||||
|
||||
state = AsyncValue.data(selectedRestaurant);
|
||||
return selectedRestaurant;
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Restaurant?> _generateCandidate({
|
||||
required double maxDistance,
|
||||
required List<String> selectedCategories,
|
||||
List<String> excludedRestaurantIds = const [],
|
||||
}) async {
|
||||
// 현재 위치 가져오기
|
||||
final location = await _ref.read(currentLocationProvider.future);
|
||||
if (location == null) {
|
||||
throw Exception('위치 정보를 가져올 수 없습니다');
|
||||
}
|
||||
|
||||
// 날씨 정보 가져오기
|
||||
final weather = await _ref.read(weatherProvider.future);
|
||||
|
||||
// 사용자 설정 가져오기
|
||||
final userSettings = await _ref.read(userSettingsProvider.future);
|
||||
|
||||
// 모든 식당 가져오기
|
||||
final allRestaurants = await _ref.read(restaurantListProvider.future);
|
||||
|
||||
// 방문 기록 가져오기
|
||||
final allVisitRecords = await _ref.read(visitRecordsProvider.future);
|
||||
|
||||
// 제외된 식당 제거
|
||||
final availableRestaurants = excludedRestaurantIds.isEmpty
|
||||
? allRestaurants
|
||||
: allRestaurants
|
||||
.where(
|
||||
(restaurant) => !excludedRestaurantIds.contains(restaurant.id),
|
||||
)
|
||||
.toList();
|
||||
|
||||
if (availableRestaurants.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 추천 설정 구성
|
||||
final config = RecommendationConfig(
|
||||
userLatitude: location.latitude,
|
||||
userLongitude: location.longitude,
|
||||
maxDistance: maxDistance,
|
||||
selectedCategories: selectedCategories,
|
||||
userSettings: userSettings,
|
||||
weather: weather,
|
||||
);
|
||||
|
||||
// 추천 엔진 사용
|
||||
return _recommendationEngine.generateRecommendation(
|
||||
allRestaurants: availableRestaurants,
|
||||
recentVisits: allVisitRecords,
|
||||
config: config,
|
||||
);
|
||||
}
|
||||
|
||||
/// 추천 기록 저장
|
||||
Future<void> _saveRecommendationRecord(Restaurant restaurant) async {
|
||||
Future<RecommendationRecord> saveRecommendationRecord(
|
||||
Restaurant restaurant, {
|
||||
DateTime? recommendationTime,
|
||||
}) async {
|
||||
final now = DateTime.now();
|
||||
|
||||
final record = RecommendationRecord(
|
||||
id: const Uuid().v4(),
|
||||
restaurantId: restaurant.id,
|
||||
recommendationDate: DateTime.now(),
|
||||
recommendationDate: recommendationTime ?? now,
|
||||
visited: false,
|
||||
createdAt: DateTime.now(),
|
||||
createdAt: now,
|
||||
);
|
||||
|
||||
await _repository.addRecommendationRecord(record);
|
||||
return record;
|
||||
}
|
||||
|
||||
/// 추천 후 방문 확인
|
||||
|
||||
Reference in New Issue
Block a user