import 'package:dio/dio.dart'; import 'package:injectable/injectable.dart'; import 'package:superport/core/constants/api_endpoints.dart'; import 'package:superport/core/constants/app_constants.dart'; import 'package:superport/data/datasources/remote/api_client.dart'; import 'package:superport/data/models/zipcode_dto.dart'; abstract class ZipcodeRepository { /// 우편번호 검색 (페이지네이션 지원) Future search({ int page = 1, int limit = AppConstants.defaultPageSize, String? search, String? sido, String? gu, }); /// Hierarchy API - 시도 목록 조회 Future getHierarchySidos(); /// Hierarchy API - 특정 시도의 구/군 목록 조회 Future getHierarchyGusBySido(String sido); } @Injectable(as: ZipcodeRepository) class ZipcodeRepositoryImpl implements ZipcodeRepository { final ApiClient _apiClient; ZipcodeRepositoryImpl(this._apiClient); @override Future search({ int page = 1, int limit = AppConstants.defaultPageSize, String? search, String? sido, String? gu, }) async { try { final queryParams = { 'page': page, 'limit': limit, }; if (search != null && search.isNotEmpty) { queryParams['search'] = search; } if (sido != null && sido.isNotEmpty) { queryParams['sido'] = sido; } if (gu != null && gu.isNotEmpty) { queryParams['gu'] = gu; } final response = await _apiClient.dio.get( ApiEndpoints.zipcodes, queryParameters: queryParams, ); // API 응답 구조에 따라 파싱 if (response.data is Map) { // 페이지네이션 응답 형식 return ZipcodeListResponse.fromJson(response.data); } else if (response.data is List) { // 배열 직접 반환 형식 final zipcodes = (response.data as List) .map((json) => ZipcodeDto.fromJson(json)) .toList(); return ZipcodeListResponse( items: zipcodes, totalCount: zipcodes.length, currentPage: page, totalPages: 1, ); } else { throw Exception('예상치 못한 응답 형식입니다.'); } } on DioException catch (e) { throw _handleError(e); } } @override Future getHierarchySidos() async { try { final response = await _apiClient.dio.get( ApiEndpoints.zipcodeHierarchySidos, ); if (response.data is Map) { return HierarchyResponse.fromJson(response.data); } else { throw Exception('예상치 못한 응답 형식입니다.'); } } on DioException catch (e) { throw _handleError(e); } } @override Future getHierarchyGusBySido(String sido) async { try { final response = await _apiClient.dio.get( ApiEndpoints.zipcodeHierarchyGus, queryParameters: {'sido': sido}, ); if (response.data is Map) { return HierarchyResponse.fromJson(response.data); } else { throw Exception('예상치 못한 응답 형식입니다.'); } } on DioException catch (e) { throw _handleError(e); } } Exception _handleError(DioException e) { switch (e.type) { case DioExceptionType.connectionTimeout: case DioExceptionType.sendTimeout: case DioExceptionType.receiveTimeout: return Exception('연결 시간이 초과되었습니다.'); case DioExceptionType.badResponse: final statusCode = e.response?.statusCode; final message = e.response?.data?['message'] ?? '서버 오류가 발생했습니다.'; return Exception('[$statusCode] $message'); case DioExceptionType.connectionError: return Exception('네트워크 연결을 확인해주세요.'); default: return Exception('알 수 없는 오류가 발생했습니다.'); } } }