192 lines
5.6 KiB
Dart
192 lines
5.6 KiB
Dart
import 'package:hive_flutter/hive_flutter.dart';
|
|
import 'package:lunchpick/domain/entities/weather_info.dart';
|
|
import 'package:lunchpick/domain/repositories/weather_repository.dart';
|
|
|
|
class WeatherRepositoryImpl implements WeatherRepository {
|
|
static const String _boxName = 'weather_cache';
|
|
static const String _keyCachedWeather = 'cached_weather';
|
|
static const String _keyLastUpdateTime = 'last_update_time';
|
|
static const Duration _cacheValidDuration = Duration(hours: 1);
|
|
|
|
Future<Box> get _box async => await Hive.openBox(_boxName);
|
|
|
|
@override
|
|
Future<WeatherInfo> getCurrentWeather({
|
|
required double latitude,
|
|
required double longitude,
|
|
}) async {
|
|
// TODO: 실제 날씨 API 호출 구현
|
|
// 여기서는 임시로 더미 데이터 반환
|
|
|
|
final dummyWeather = WeatherInfo(
|
|
current: WeatherData(temperature: 20, isRainy: false, description: '맑음'),
|
|
nextHour: WeatherData(temperature: 22, isRainy: false, description: '맑음'),
|
|
);
|
|
|
|
// 캐시에 저장
|
|
await cacheWeatherInfo(dummyWeather);
|
|
|
|
return dummyWeather;
|
|
}
|
|
|
|
@override
|
|
Future<WeatherInfo?> getCachedWeather() async {
|
|
final box = await _box;
|
|
|
|
// 캐시가 유효한지 확인
|
|
final isValid = await _isCacheValid();
|
|
if (!isValid) {
|
|
return null;
|
|
}
|
|
|
|
// 캐시된 데이터 가져오기
|
|
final cachedData = box.get(_keyCachedWeather);
|
|
if (cachedData == null) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
// 안전한 타입 변환
|
|
if (cachedData is! Map) {
|
|
print(
|
|
'WeatherCache: Invalid data type - expected Map but got ${cachedData.runtimeType}',
|
|
);
|
|
await clearWeatherCache();
|
|
return null;
|
|
}
|
|
|
|
final Map<String, dynamic> weatherMap = Map<String, dynamic>.from(
|
|
cachedData,
|
|
);
|
|
|
|
// Map 구조 검증
|
|
if (!weatherMap.containsKey('current') ||
|
|
!weatherMap.containsKey('nextHour')) {
|
|
print('WeatherCache: Missing required fields in weather data');
|
|
await clearWeatherCache();
|
|
return null;
|
|
}
|
|
|
|
return _weatherInfoFromMap(weatherMap);
|
|
} catch (e) {
|
|
// 캐시 데이터가 손상된 경우
|
|
print('WeatherCache: Error parsing cached weather data: $e');
|
|
await clearWeatherCache();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> cacheWeatherInfo(WeatherInfo weatherInfo) async {
|
|
final box = await _box;
|
|
|
|
// WeatherInfo를 Map으로 변환하여 저장
|
|
final weatherMap = _weatherInfoToMap(weatherInfo);
|
|
await box.put(_keyCachedWeather, weatherMap);
|
|
await box.put(_keyLastUpdateTime, DateTime.now().toIso8601String());
|
|
}
|
|
|
|
@override
|
|
Future<void> clearWeatherCache() async {
|
|
final box = await _box;
|
|
await box.delete(_keyCachedWeather);
|
|
await box.delete(_keyLastUpdateTime);
|
|
}
|
|
|
|
@override
|
|
Future<bool> isWeatherUpdateNeeded() async {
|
|
final box = await _box;
|
|
|
|
// 캐시된 날씨 정보가 없으면 업데이트 필요
|
|
if (!box.containsKey(_keyCachedWeather)) {
|
|
return true;
|
|
}
|
|
|
|
// 캐시가 유효한지 확인
|
|
return !(await _isCacheValid());
|
|
}
|
|
|
|
Future<bool> _isCacheValid() async {
|
|
final box = await _box;
|
|
|
|
final lastUpdateTimeStr = box.get(_keyLastUpdateTime);
|
|
if (lastUpdateTimeStr == null) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// 날짜 파싱 시도
|
|
final lastUpdateTime = DateTime.tryParse(lastUpdateTimeStr);
|
|
if (lastUpdateTime == null) {
|
|
print('WeatherCache: Invalid date format in cache: $lastUpdateTimeStr');
|
|
return false;
|
|
}
|
|
|
|
final now = DateTime.now();
|
|
final difference = now.difference(lastUpdateTime);
|
|
|
|
return difference < _cacheValidDuration;
|
|
} catch (e) {
|
|
print('WeatherCache: Error checking cache validity: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Map<String, dynamic> _weatherInfoToMap(WeatherInfo weatherInfo) {
|
|
return {
|
|
'current': {
|
|
'temperature': weatherInfo.current.temperature,
|
|
'isRainy': weatherInfo.current.isRainy,
|
|
'description': weatherInfo.current.description,
|
|
},
|
|
'nextHour': {
|
|
'temperature': weatherInfo.nextHour.temperature,
|
|
'isRainy': weatherInfo.nextHour.isRainy,
|
|
'description': weatherInfo.nextHour.description,
|
|
},
|
|
};
|
|
}
|
|
|
|
WeatherInfo _weatherInfoFromMap(Map<String, dynamic> map) {
|
|
try {
|
|
// current 필드 검증
|
|
final currentMap = map['current'] as Map<String, dynamic>?;
|
|
if (currentMap == null) {
|
|
throw FormatException('Missing current weather data');
|
|
}
|
|
|
|
// nextHour 필드 검증
|
|
final nextHourMap = map['nextHour'] as Map<String, dynamic>?;
|
|
if (nextHourMap == null) {
|
|
throw FormatException('Missing nextHour weather data');
|
|
}
|
|
|
|
// 필수 필드 검증 및 기본값 제공
|
|
final currentTemp = currentMap['temperature'] as num? ?? 20;
|
|
final currentRainy = currentMap['isRainy'] as bool? ?? false;
|
|
final currentDesc = currentMap['description'] as String? ?? '알 수 없음';
|
|
|
|
final nextTemp = nextHourMap['temperature'] as num? ?? 20;
|
|
final nextRainy = nextHourMap['isRainy'] as bool? ?? false;
|
|
final nextDesc = nextHourMap['description'] as String? ?? '알 수 없음';
|
|
|
|
return WeatherInfo(
|
|
current: WeatherData(
|
|
temperature: currentTemp.round(),
|
|
isRainy: currentRainy,
|
|
description: currentDesc,
|
|
),
|
|
nextHour: WeatherData(
|
|
temperature: nextTemp.round(),
|
|
isRainy: nextRainy,
|
|
description: nextDesc,
|
|
),
|
|
);
|
|
} catch (e) {
|
|
print('WeatherCache: Error converting map to WeatherInfo: $e');
|
|
print('WeatherCache: Map data: $map');
|
|
rethrow;
|
|
}
|
|
}
|
|
}
|