import 'package:dio/dio.dart'; import 'package:get_it/get_it.dart'; import 'package:superport/core/constants/api_endpoints.dart'; import 'package:superport/core/errors/exceptions.dart'; import 'package:superport/data/datasources/remote/api_client.dart'; import 'package:superport/data/models/equipment/equipment_dto.dart'; abstract class EquipmentRemoteDataSource { Future getEquipments({ int page = 1, int perPage = 20, String? search, int? companyId, }); Future createEquipment(EquipmentRequestDto request); Future getEquipmentDetail(int id); Future updateEquipment(int id, EquipmentUpdateRequestDto request); Future deleteEquipment(int id); Future restoreEquipment(int id); Future getEquipmentBySerial(String serial); Future getEquipmentByBarcode(String barcode); Future> getEquipmentsByCompany(int companyId); } class EquipmentRemoteDataSourceImpl implements EquipmentRemoteDataSource { final ApiClient _apiClient = GetIt.instance(); /// DioException을 ServerException으로 변환하는 헬퍼 메서드 Never _handleDioException(DioException e) { String errorMessage = 'Network error occurred'; if (e.response?.data != null) { // 응답 데이터가 Map인 경우 (JSON 에러) if (e.response!.data is Map) { final errorData = e.response!.data as Map; errorMessage = errorData['error']?['message'] ?? errorData['message'] ?? 'Server error occurred'; } // 응답 데이터가 String인 경우 (Plain text 에러) else if (e.response!.data is String) { errorMessage = e.response!.data as String; } } throw ServerException( message: errorMessage, statusCode: e.response?.statusCode, ); } @override Future getEquipments({ int page = 1, int perPage = 20, String? search, int? companyId, }) async { try { final queryParams = { 'page': page, 'page_size': perPage, if (search != null && search.isNotEmpty) 'search': search, if (companyId != null) 'company_id': companyId, }; final response = await _apiClient.get( ApiEndpoints.equipment, queryParameters: queryParams, ); print('[Equipment API] Response: ${response.data}'); // 백엔드 응답은 직접 data 배열과 페이지네이션 정보 반환 final Map responseData = response.data; return EquipmentListResponse.fromJson(responseData); } on DioException catch (e) { _handleDioException(e); } } @override Future createEquipment(EquipmentRequestDto request) async { try { final response = await _apiClient.post( ApiEndpoints.equipment, data: request.toJson(), ); print('[Equipment API] Create Response: ${response.data}'); // API 응답이 {success: true, data: {...}} 형태인 경우 처리 final responseData = response.data; if (responseData is Map && responseData.containsKey('data')) { return EquipmentDto.fromJson(responseData['data']); } else { // 직접 데이터인 경우 return EquipmentDto.fromJson(responseData); } } on DioException catch (e) { _handleDioException(e); } } @override Future getEquipmentDetail(int id) async { try { final response = await _apiClient.get('${ApiEndpoints.equipment}/$id'); print('[Equipment API] Detail Response: ${response.data}'); // API 응답이 {success: true, data: {...}} 형태인 경우 처리 final responseData = response.data; if (responseData is Map && responseData.containsKey('data')) { return EquipmentDto.fromJson(responseData['data']); } else { // 직접 데이터인 경우 return EquipmentDto.fromJson(responseData); } } on DioException catch (e) { _handleDioException(e); } } @override Future updateEquipment(int id, EquipmentUpdateRequestDto request) async { try { // 디버그: 전송할 JSON 데이터 로깅 final jsonData = request.toJson(); // null 필드 제거 (백엔드가 null을 처리하지 못하는 경우 대비) final cleanedData = Map.from(jsonData) ..removeWhere((key, value) => value == null); print('[Equipment API] Update Request JSON: $cleanedData'); print('[Equipment API] JSON keys: ${cleanedData.keys.toList()}'); final response = await _apiClient.put( '${ApiEndpoints.equipment}/$id', data: cleanedData, ); print('[Equipment API] Update Response: ${response.data}'); // API 응답이 {success: true, data: {...}} 형태인 경우 처리 final responseData = response.data; if (responseData is Map && responseData.containsKey('data')) { return EquipmentDto.fromJson(responseData['data']); } else { // 직접 데이터인 경우 return EquipmentDto.fromJson(responseData); } } on DioException catch (e) { _handleDioException(e); } } @override Future deleteEquipment(int id) async { try { await _apiClient.delete('${ApiEndpoints.equipment}/$id'); } on DioException catch (e) { _handleDioException(e); } } @override Future restoreEquipment(int id) async { try { final response = await _apiClient.put('${ApiEndpoints.equipment}/$id/restore'); // 백엔드 응답 구조에 따라 data 필드에서 추출 final responseData = response.data; return EquipmentDto.fromJson(responseData['data'] ?? responseData); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to restore equipment', statusCode: e.response?.statusCode, ); } } @override Future getEquipmentBySerial(String serial) async { try { final response = await _apiClient.get('${ApiEndpoints.equipment}/serial/$serial'); // 백엔드 응답 구조에 따라 data 필드에서 추출 final responseData = response.data; return EquipmentDto.fromJson(responseData['data'] ?? responseData); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Equipment not found by serial', statusCode: e.response?.statusCode, ); } } @override Future getEquipmentByBarcode(String barcode) async { try { final response = await _apiClient.get('${ApiEndpoints.equipment}/barcode/$barcode'); // 백엔드 응답 구조에 따라 data 필드에서 추출 final responseData = response.data; return EquipmentDto.fromJson(responseData['data'] ?? responseData); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Equipment not found by barcode', statusCode: e.response?.statusCode, ); } } @override Future> getEquipmentsByCompany(int companyId) async { try { final response = await _apiClient.get('${ApiEndpoints.equipment}/by-company/$companyId'); print('[Equipment API] Company Filter Response: ${response.data}'); // 백엔드 응답 구조에 따라 data 필드에서 추출 final responseData = response.data; final List equipmentList = responseData['data'] ?? responseData; return equipmentList.map((json) => EquipmentDto.fromJson(json)).toList(); } on DioException catch (e) { throw ServerException( message: e.response?.data['message'] ?? 'Failed to get equipments by company', statusCode: e.response?.statusCode, ); } } }