135 lines
4.3 KiB
Dart
135 lines
4.3 KiB
Dart
import 'package:uuid/uuid.dart';
|
|
|
|
import '../../../domain/entities/restaurant.dart';
|
|
import '../naver/naver_local_search_api.dart';
|
|
import '../../../core/utils/category_mapper.dart';
|
|
|
|
/// 네이버 데이터 변환기
|
|
///
|
|
/// 네이버 API 응답을 도메인 엔티티로 변환합니다.
|
|
class NaverDataConverter {
|
|
static const _uuid = Uuid();
|
|
|
|
/// NaverLocalSearchResult를 Restaurant 엔티티로 변환
|
|
static Restaurant fromLocalSearchResult(
|
|
NaverLocalSearchResult result, {
|
|
String? id,
|
|
}) {
|
|
// 좌표 변환 (네이버 지도 좌표계 -> WGS84)
|
|
final convertedCoords = _convertNaverMapCoordinates(
|
|
result.mapx,
|
|
result.mapy,
|
|
);
|
|
|
|
// 카테고리 파싱 및 정규화
|
|
final categoryParts = result.category
|
|
.split('>')
|
|
.map((s) => s.trim())
|
|
.toList();
|
|
final mainCategory = categoryParts.isNotEmpty ? categoryParts.first : '음식점';
|
|
final subCategory = categoryParts.length > 1
|
|
? categoryParts.last
|
|
: mainCategory;
|
|
|
|
// CategoryMapper를 사용한 정규화
|
|
final normalizedCategory = CategoryMapper.normalizeNaverCategory(
|
|
mainCategory,
|
|
subCategory,
|
|
);
|
|
|
|
return Restaurant(
|
|
id: id ?? _uuid.v4(),
|
|
name: result.title,
|
|
category: normalizedCategory,
|
|
subCategory: subCategory,
|
|
description: result.description.isNotEmpty ? result.description : null,
|
|
phoneNumber: result.telephone.isNotEmpty ? result.telephone : null,
|
|
roadAddress: result.roadAddress.isNotEmpty
|
|
? result.roadAddress
|
|
: result.address,
|
|
jibunAddress: result.address,
|
|
latitude: convertedCoords['latitude'] ?? 37.5665,
|
|
longitude: convertedCoords['longitude'] ?? 126.9780,
|
|
naverUrl: result.link.isNotEmpty ? result.link : null,
|
|
createdAt: DateTime.now(),
|
|
updatedAt: DateTime.now(),
|
|
source: DataSource.NAVER,
|
|
);
|
|
}
|
|
|
|
/// GraphQL 응답을 Restaurant 엔티티로 변환
|
|
static Restaurant fromGraphQLResponse(
|
|
Map<String, dynamic> placeData, {
|
|
String? id,
|
|
String? naverUrl,
|
|
}) {
|
|
// 영업시간 파싱
|
|
String? businessHours;
|
|
if (placeData['businessHours'] != null) {
|
|
final hours = placeData['businessHours'] as List;
|
|
businessHours = hours
|
|
.where((h) => h['businessHours'] != null)
|
|
.map((h) => h['businessHours'])
|
|
.join('\n');
|
|
}
|
|
|
|
// 좌표 추출
|
|
double? latitude;
|
|
double? longitude;
|
|
if (placeData['location'] != null) {
|
|
latitude = placeData['location']['latitude']?.toDouble();
|
|
longitude = placeData['location']['longitude']?.toDouble();
|
|
}
|
|
|
|
// 카테고리 파싱 및 정규화
|
|
final rawCategory = placeData['category'] ?? '음식점';
|
|
final categoryParts = rawCategory.split('>').map((s) => s.trim()).toList();
|
|
final mainCategory = categoryParts.isNotEmpty ? categoryParts.first : '음식점';
|
|
final subCategory = categoryParts.length > 1
|
|
? categoryParts.last
|
|
: mainCategory;
|
|
|
|
// CategoryMapper를 사용한 정규화
|
|
final normalizedCategory = CategoryMapper.normalizeNaverCategory(
|
|
mainCategory,
|
|
subCategory,
|
|
);
|
|
|
|
return Restaurant(
|
|
id: id ?? _uuid.v4(),
|
|
name: placeData['name'] ?? '이름 없음',
|
|
category: normalizedCategory,
|
|
subCategory: subCategory,
|
|
description: placeData['description'],
|
|
phoneNumber: placeData['phone'],
|
|
roadAddress: placeData['address']?['roadAddress'] ?? '',
|
|
jibunAddress: placeData['address']?['jibunAddress'] ?? '',
|
|
latitude: latitude ?? 37.5665,
|
|
longitude: longitude ?? 126.9780,
|
|
businessHours: businessHours,
|
|
naverUrl: naverUrl,
|
|
createdAt: DateTime.now(),
|
|
updatedAt: DateTime.now(),
|
|
source: DataSource.NAVER,
|
|
);
|
|
}
|
|
|
|
/// 네이버 지도 좌표를 WGS84로 변환
|
|
static Map<String, double?> _convertNaverMapCoordinates(
|
|
double? mapx,
|
|
double? mapy,
|
|
) {
|
|
if (mapx == null || mapy == null) {
|
|
return {'latitude': null, 'longitude': null};
|
|
}
|
|
|
|
// 네이버 지도 좌표계는 KATEC을 사용
|
|
// 간단한 변환 공식 (정확도는 떨어지지만 실용적)
|
|
// 실제로는 더 정교한 변환이 필요할 수 있음
|
|
final longitude = mapx / 10000000.0;
|
|
final latitude = mapy / 10000000.0;
|
|
|
|
return {'latitude': latitude, 'longitude': longitude};
|
|
}
|
|
}
|