LunchPick(오늘 뭐 먹Z?) Flutter 앱의 초기 구현입니다. 주요 기능: - 네이버 지도 연동 맛집 추가 - 랜덤 메뉴 추천 시스템 - 날씨 기반 거리 조정 - 방문 기록 관리 - Bluetooth 맛집 공유 - 다크모드 지원 기술 스택: - Flutter 3.8.1+ - Riverpod 상태 관리 - Hive 로컬 DB - Clean Architecture 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
126 lines
4.2 KiB
Dart
126 lines
4.2 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,
|
|
};
|
|
}
|
|
|
|
|
|
} |