backup: 사용하지 않는 파일 삭제 전 복구 지점
- 전체 371개 파일 중 82개 미사용 파일 식별 - Phase 1: 33개 파일 삭제 예정 (100% 안전) - Phase 2: 30개 파일 삭제 검토 예정 - Phase 3: 19개 파일 수동 검토 예정 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ abstract class AdministratorRemoteDataSource {
|
||||
/// 관리자 목록 조회 (페이지네이션 지원)
|
||||
Future<AdministratorListResponse> getAdministrators({
|
||||
int page = 1,
|
||||
int pageSize = 20,
|
||||
int pageSize = 10,
|
||||
String? search,
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ class AdministratorRemoteDataSourceImpl implements AdministratorRemoteDataSource
|
||||
@override
|
||||
Future<AdministratorListResponse> getAdministrators({
|
||||
int page = 1,
|
||||
int pageSize = 20,
|
||||
int pageSize = 10,
|
||||
String? search,
|
||||
}) async {
|
||||
try {
|
||||
|
||||
@@ -9,12 +9,17 @@ import 'package:superport/data/models/auth/login_response.dart';
|
||||
import 'package:superport/data/models/auth/logout_request.dart';
|
||||
import 'package:superport/data/models/auth/refresh_token_request.dart';
|
||||
import 'package:superport/data/models/auth/token_response.dart';
|
||||
import 'package:superport/data/models/auth/auth_user.dart';
|
||||
import 'package:superport/data/models/auth/change_password_request.dart';
|
||||
import 'package:superport/data/models/auth/message_response.dart';
|
||||
import 'package:superport/core/utils/debug_logger.dart';
|
||||
|
||||
abstract class AuthRemoteDataSource {
|
||||
Future<Either<Failure, LoginResponse>> login(LoginRequest request);
|
||||
Future<Either<Failure, void>> logout(LogoutRequest request);
|
||||
Future<Either<Failure, TokenResponse>> refreshToken(RefreshTokenRequest request);
|
||||
Future<Either<Failure, AuthUser>> getCurrentAdmin();
|
||||
Future<Either<Failure, MessageResponse>> changePassword(ChangePasswordRequest request);
|
||||
}
|
||||
|
||||
@LazySingleton(as: AuthRemoteDataSource)
|
||||
@@ -242,4 +247,161 @@ class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
|
||||
return Left(ServerFailure(message: '토큰 갱신 중 오류가 발생했습니다.'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, AuthUser>> getCurrentAdmin() async {
|
||||
try {
|
||||
DebugLogger.logApiRequest(url: '/auth/me', method: 'GET');
|
||||
|
||||
final response = await _apiClient.get('/auth/me');
|
||||
|
||||
DebugLogger.logApiResponse(
|
||||
url: '/auth/me',
|
||||
statusCode: response.statusCode,
|
||||
data: response.data,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
final responseData = response.data as Map<String, dynamic>;
|
||||
|
||||
// 응답 데이터 구조 검증
|
||||
DebugLogger.validateResponseStructure(
|
||||
responseData,
|
||||
['id', 'name', 'email'],
|
||||
responseName: 'CurrentAdmin',
|
||||
);
|
||||
|
||||
final authUser = DebugLogger.parseJsonWithLogging(
|
||||
responseData,
|
||||
AuthUser.fromJson,
|
||||
objectName: 'AuthUser',
|
||||
);
|
||||
|
||||
if (authUser != null) {
|
||||
DebugLogger.log(
|
||||
'getCurrentAdmin 성공',
|
||||
tag: 'API_SUCCESS',
|
||||
data: {
|
||||
'url': '/auth/me',
|
||||
'adminId': authUser.id,
|
||||
'adminEmail': authUser.email,
|
||||
'adminName': authUser.name,
|
||||
},
|
||||
);
|
||||
return Right(authUser);
|
||||
} else {
|
||||
return Left(ServerFailure(message: 'AuthUser 파싱 실패'));
|
||||
}
|
||||
} else {
|
||||
return Left(ServerFailure(
|
||||
message: response.statusMessage ?? '관리자 정보 조회 실패',
|
||||
));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
DebugLogger.logError(
|
||||
'getCurrentAdmin DioException',
|
||||
error: e,
|
||||
additionalData: {
|
||||
'type': e.type.toString(),
|
||||
'statusCode': e.response?.statusCode,
|
||||
},
|
||||
);
|
||||
|
||||
if (e.response?.statusCode == 401) {
|
||||
return Left(AuthenticationFailure(
|
||||
message: '인증이 필요합니다. 다시 로그인해주세요.',
|
||||
));
|
||||
}
|
||||
|
||||
return Left(ServerFailure(message: '관리자 정보 조회 중 오류가 발생했습니다.'));
|
||||
} catch (e, stackTrace) {
|
||||
DebugLogger.logError(
|
||||
'getCurrentAdmin 예상치 못한 예외',
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
return Left(ServerFailure(message: '관리자 정보 조회 중 오류가 발생했습니다.'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, MessageResponse>> changePassword(ChangePasswordRequest request) async {
|
||||
try {
|
||||
DebugLogger.logApiRequest(url: '/auth/change-password', method: 'POST');
|
||||
|
||||
final response = await _apiClient.post(
|
||||
'/auth/change-password',
|
||||
data: request.toJson(),
|
||||
);
|
||||
|
||||
DebugLogger.logApiResponse(
|
||||
url: '/auth/change-password',
|
||||
statusCode: response.statusCode,
|
||||
data: response.data,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
final responseData = response.data as Map<String, dynamic>;
|
||||
|
||||
// 응답 데이터 구조 검증
|
||||
DebugLogger.validateResponseStructure(
|
||||
responseData,
|
||||
['message'],
|
||||
responseName: 'ChangePasswordResponse',
|
||||
);
|
||||
|
||||
final messageResponse = DebugLogger.parseJsonWithLogging(
|
||||
responseData,
|
||||
MessageResponse.fromJson,
|
||||
objectName: 'MessageResponse',
|
||||
);
|
||||
|
||||
if (messageResponse != null) {
|
||||
DebugLogger.log(
|
||||
'changePassword 성공',
|
||||
tag: 'API_SUCCESS',
|
||||
data: {
|
||||
'url': '/auth/change-password',
|
||||
'message': messageResponse.message,
|
||||
},
|
||||
);
|
||||
return Right(messageResponse);
|
||||
} else {
|
||||
return Left(ServerFailure(message: 'MessageResponse 파싱 실패'));
|
||||
}
|
||||
} else {
|
||||
return Left(ServerFailure(
|
||||
message: response.statusMessage ?? '비밀번호 변경 실패',
|
||||
));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
DebugLogger.logError(
|
||||
'changePassword DioException',
|
||||
error: e,
|
||||
additionalData: {
|
||||
'type': e.type.toString(),
|
||||
'statusCode': e.response?.statusCode,
|
||||
},
|
||||
);
|
||||
|
||||
if (e.response?.statusCode == 401) {
|
||||
return Left(AuthenticationFailure(
|
||||
message: '기존 비밀번호가 올바르지 않습니다.',
|
||||
));
|
||||
} else if (e.response?.statusCode == 400) {
|
||||
return Left(ValidationFailure(
|
||||
message: '비밀번호 형식이 올바르지 않습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
return Left(ServerFailure(message: '비밀번호 변경 중 오류가 발생했습니다.'));
|
||||
} catch (e, stackTrace) {
|
||||
DebugLogger.logError(
|
||||
'changePassword 예상치 못한 예외',
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
return Left(ServerFailure(message: '비밀번호 변경 중 오류가 발생했습니다.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,8 @@ abstract class CompanyRemoteDataSource {
|
||||
|
||||
Future<void> deleteCompany(int id);
|
||||
|
||||
Future<CompanyDto> restoreCompany(int id);
|
||||
|
||||
Future<List<CompanyNameDto>> getCompanyNames();
|
||||
|
||||
Future<List<CompanyWithChildren>> getCompaniesWithBranches();
|
||||
@@ -136,19 +138,9 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
debugPrint('[CompanyRemoteDataSource] Response data: ${response.data}');
|
||||
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
// API 응답 구조 확인
|
||||
final responseData = response.data;
|
||||
if (responseData != null && responseData['success'] == true && responseData['data'] != null) {
|
||||
// 직접 파싱
|
||||
return CompanyDto.fromJson(responseData['data'] as Map<String, dynamic>);
|
||||
} else {
|
||||
// ApiResponse 형식으로 파싱 시도
|
||||
final apiResponse = ApiResponse<CompanyDto>.fromJson(
|
||||
response.data,
|
||||
(json) => CompanyDto.fromJson(json as Map<String, dynamic>),
|
||||
);
|
||||
return apiResponse.data!;
|
||||
}
|
||||
// 백엔드가 직접 CompanyDto 형식으로 응답하므로 바로 파싱
|
||||
debugPrint('[CompanyRemoteDataSource] Parsing response as CompanyDto');
|
||||
return CompanyDto.fromJson(response.data as Map<String, dynamic>);
|
||||
} else {
|
||||
throw ApiException(
|
||||
message: 'Failed to create company - Status: ${response.statusCode}',
|
||||
@@ -226,6 +218,22 @@ class CompanyRemoteDataSourceImpl implements CompanyRemoteDataSource {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CompanyDto> restoreCompany(int id) async {
|
||||
try {
|
||||
final response = await _apiClient.dio.put(
|
||||
'${ApiEndpoints.companies}/$id/restore',
|
||||
);
|
||||
|
||||
return CompanyDto.fromJson(response.data['data']);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Failed to restore company',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<CompanyNameDto>> getCompanyNames() async {
|
||||
try {
|
||||
|
||||
@@ -10,6 +10,7 @@ abstract class EquipmentRemoteDataSource {
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
String? search,
|
||||
int? companyId,
|
||||
});
|
||||
|
||||
Future<EquipmentDto> createEquipment(EquipmentRequestDto request);
|
||||
@@ -19,22 +20,56 @@ abstract class EquipmentRemoteDataSource {
|
||||
Future<EquipmentDto> updateEquipment(int id, EquipmentUpdateRequestDto request);
|
||||
|
||||
Future<void> deleteEquipment(int id);
|
||||
|
||||
Future<EquipmentDto> restoreEquipment(int id);
|
||||
|
||||
Future<EquipmentDto> getEquipmentBySerial(String serial);
|
||||
|
||||
Future<EquipmentDto> getEquipmentByBarcode(String barcode);
|
||||
|
||||
Future<List<EquipmentDto>> getEquipmentsByCompany(int companyId);
|
||||
}
|
||||
|
||||
class EquipmentRemoteDataSourceImpl implements EquipmentRemoteDataSource {
|
||||
final ApiClient _apiClient = GetIt.instance<ApiClient>();
|
||||
|
||||
/// DioException을 ServerException으로 변환하는 헬퍼 메서드
|
||||
Never _handleDioException(DioException e) {
|
||||
String errorMessage = 'Network error occurred';
|
||||
|
||||
if (e.response?.data != null) {
|
||||
// 응답 데이터가 Map인 경우 (JSON 에러)
|
||||
if (e.response!.data is Map<String, dynamic>) {
|
||||
final errorData = e.response!.data as Map<String, dynamic>;
|
||||
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<EquipmentListResponse> getEquipments({
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
String? search,
|
||||
int? companyId,
|
||||
}) async {
|
||||
try {
|
||||
final queryParams = <String, dynamic>{
|
||||
'page': page,
|
||||
'page_size': perPage,
|
||||
if (search != null && search.isNotEmpty) 'search': search,
|
||||
if (companyId != null) 'company_id': companyId,
|
||||
};
|
||||
|
||||
final response = await _apiClient.get(
|
||||
@@ -48,10 +83,7 @@ class EquipmentRemoteDataSourceImpl implements EquipmentRemoteDataSource {
|
||||
final Map<String, dynamic> responseData = response.data;
|
||||
return EquipmentListResponse.fromJson(responseData);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Network error occurred',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
_handleDioException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +106,7 @@ class EquipmentRemoteDataSourceImpl implements EquipmentRemoteDataSource {
|
||||
return EquipmentDto.fromJson(responseData);
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Network error occurred',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
_handleDioException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,10 +126,7 @@ class EquipmentRemoteDataSourceImpl implements EquipmentRemoteDataSource {
|
||||
return EquipmentDto.fromJson(responseData);
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Network error occurred',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
_handleDioException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,10 +159,7 @@ class EquipmentRemoteDataSourceImpl implements EquipmentRemoteDataSource {
|
||||
return EquipmentDto.fromJson(responseData);
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Network error occurred',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
_handleDioException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,9 +167,74 @@ class EquipmentRemoteDataSourceImpl implements EquipmentRemoteDataSource {
|
||||
Future<void> deleteEquipment(int id) async {
|
||||
try {
|
||||
await _apiClient.delete('${ApiEndpoints.equipment}/$id');
|
||||
} on DioException catch (e) {
|
||||
_handleDioException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<EquipmentDto> 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'] ?? 'Network error occurred',
|
||||
message: e.response?.data['message'] ?? 'Failed to restore equipment',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<EquipmentDto> 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<EquipmentDto> 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<List<EquipmentDto>> 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<dynamic> 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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,15 @@ class ResponseInterceptor extends Interceptor {
|
||||
return false; // 페이지네이션 응답은 변형 안함
|
||||
}
|
||||
|
||||
// Hierarchy API 응답은 변형하지 않음 (data 배열 + meta 객체)
|
||||
if (data.containsKey('data') && data.containsKey('meta')) {
|
||||
debugPrint('[ResponseInterceptor] Hierarchy 체크 - data 타입: ${data['data'].runtimeType}, meta 타입: ${data['meta'].runtimeType}');
|
||||
if (data['data'] is List && data['meta'] is Map) {
|
||||
debugPrint('[ResponseInterceptor] Hierarchy 응답 감지 - 변형 안함');
|
||||
return false; // Hierarchy 응답은 변형 안함
|
||||
}
|
||||
}
|
||||
|
||||
// 엔티티 단일 응답 패턴 (vendor, model, equipment 등)
|
||||
// id, name이 있으면서 registered_at 또는 created_at이 있으면 엔티티 응답으로 간주
|
||||
if (data.containsKey('id') &&
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
import 'package:superport/data/datasources/remote/api_client.dart';
|
||||
@@ -20,28 +21,94 @@ class LookupRemoteDataSourceImpl implements LookupRemoteDataSource {
|
||||
@override
|
||||
Future<Either<Failure, LookupData>> getAllLookups() async {
|
||||
try {
|
||||
final response = await _apiClient.get(ApiEndpoints.lookups);
|
||||
debugPrint('📞 === LOOKUP 개별 API 요청 시작 ===');
|
||||
|
||||
if (response.data != null && response.data is Map<String, dynamic>) {
|
||||
// 정상 응답 처리
|
||||
if (response.data['success'] == true && response.data['data'] != null) {
|
||||
final lookupData = LookupData.fromJson(response.data['data']);
|
||||
return Right(lookupData);
|
||||
} else {
|
||||
final errorMessage = response.data['error']?['message'] ?? '응답 데이터가 올바르지 않습니다';
|
||||
return Left(ServerFailure(message: errorMessage));
|
||||
// 개별 API들을 병렬로 호출
|
||||
final List<Future> futures = [
|
||||
_apiClient.get(ApiEndpoints.lookupsVendors),
|
||||
_apiClient.get(ApiEndpoints.lookupsCompanies),
|
||||
_apiClient.get(ApiEndpoints.lookupsWarehouses),
|
||||
];
|
||||
|
||||
final responses = await Future.wait(futures, eagerError: false);
|
||||
|
||||
debugPrint('📊 === LOOKUP 개별 API 응답 ===');
|
||||
|
||||
// Vendors 데이터 처리
|
||||
List<LookupItem> manufacturers = [];
|
||||
try {
|
||||
final vendorsResponse = responses[0] as Response;
|
||||
if (vendorsResponse.data is List) {
|
||||
manufacturers = (vendorsResponse.data as List)
|
||||
.map((v) => LookupItem(
|
||||
id: v['id'] as int?,
|
||||
name: v['name'] as String
|
||||
))
|
||||
.toList();
|
||||
debugPrint('✅ 제조사 데이터: ${manufacturers.length}개');
|
||||
}
|
||||
} else {
|
||||
// 404 오류나 비정상 응답의 경우 빈 데이터 반환
|
||||
return Right(LookupData.empty());
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ 제조사 데이터 처리 실패: $e');
|
||||
}
|
||||
|
||||
// Companies 데이터 처리
|
||||
List<LookupItem> companies = [];
|
||||
try {
|
||||
final companiesResponse = responses[1] as Response;
|
||||
if (companiesResponse.data is List) {
|
||||
companies = (companiesResponse.data as List)
|
||||
.map((c) => LookupItem(
|
||||
id: c['id'] as int?,
|
||||
name: c['name'] as String
|
||||
))
|
||||
.toList();
|
||||
debugPrint('✅ 회사 데이터: ${companies.length}개');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ 회사 데이터 처리 실패: $e');
|
||||
}
|
||||
|
||||
// Warehouses 데이터 처리
|
||||
List<LookupItem> warehouses = [];
|
||||
try {
|
||||
final warehousesResponse = responses[2] as Response;
|
||||
if (warehousesResponse.data is List) {
|
||||
warehouses = (warehousesResponse.data as List)
|
||||
.map((w) => LookupItem(
|
||||
id: w['id'] as int?,
|
||||
name: w['name'] as String
|
||||
))
|
||||
.toList();
|
||||
debugPrint('✅ 창고 데이터: ${warehouses.length}개');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ 창고 데이터 처리 실패: $e');
|
||||
}
|
||||
|
||||
// 통합된 LookupData 생성
|
||||
final lookupData = LookupData(
|
||||
manufacturers: manufacturers,
|
||||
companies: companies,
|
||||
warehouses: warehouses,
|
||||
equipmentNames: [], // 현재 백엔드에서 제공하지 않음
|
||||
equipmentCategories: [], // 현재 백엔드에서 제공하지 않음
|
||||
equipmentStatuses: [], // 현재 백엔드에서 제공하지 않음
|
||||
);
|
||||
|
||||
debugPrint('📊 통합 Lookup 데이터 생성 완료');
|
||||
return Right(lookupData);
|
||||
|
||||
} on DioException catch (e) {
|
||||
// 404 오류는 빈 데이터로 처리 (백엔드에 lookups API가 없음)
|
||||
if (e.response?.statusCode == 404) {
|
||||
return Right(LookupData.empty());
|
||||
}
|
||||
return Left(_handleDioError(e));
|
||||
debugPrint('❌ === LOOKUP API DIO 에러 ===');
|
||||
debugPrint('❌ 에러 타입: ${e.type}');
|
||||
debugPrint('❌ 상태 코드: ${e.response?.statusCode}');
|
||||
debugPrint('❌ 에러 메시지: ${e.message}');
|
||||
|
||||
// 개별 API 오류의 경우 빈 데이터로 fallback
|
||||
return Right(LookupData.empty());
|
||||
} catch (e) {
|
||||
debugPrint('❌ === LOOKUP API 일반 에러 ===');
|
||||
debugPrint('❌ 에러: $e');
|
||||
return Left(ServerFailure(message: '조회 데이터를 가져오는 중 오류가 발생했습니다: $e'));
|
||||
}
|
||||
}
|
||||
@@ -55,9 +122,16 @@ class LookupRemoteDataSourceImpl implements LookupRemoteDataSource {
|
||||
);
|
||||
|
||||
if (response.data != null && response.data['success'] == true && response.data['data'] != null) {
|
||||
// 타입별 조회도 전체 LookupData 형식으로 반환
|
||||
final lookupData = LookupData.fromJson(response.data['data']);
|
||||
return Right(lookupData);
|
||||
// 타입별 조회도 전체 LookupData 형식으로 반환 - IdentityMap 타입 오류 수정
|
||||
try {
|
||||
final lookupData = LookupData.fromJson(response.data['data'] as Map<String, dynamic>);
|
||||
return Right(lookupData);
|
||||
} catch (jsonError) {
|
||||
debugPrint('❌ LookupData JSON 파싱 오류: $jsonError');
|
||||
debugPrint('❌ 응답 데이터: ${response.data['data']}');
|
||||
// JSON 파싱 실패 시 빈 데이터 반환
|
||||
return Right(LookupData.empty());
|
||||
}
|
||||
} else {
|
||||
final errorMessage = response.data?['error']?['message'] ?? '응답 데이터가 올바르지 않습니다';
|
||||
return Left(ServerFailure(message: errorMessage));
|
||||
@@ -65,6 +139,7 @@ class LookupRemoteDataSourceImpl implements LookupRemoteDataSource {
|
||||
} on DioException catch (e) {
|
||||
return Left(_handleDioError(e));
|
||||
} catch (e) {
|
||||
debugPrint('❌ getLookupsByType 일반 오류: $e');
|
||||
return Left(ServerFailure(message: '타입별 조회 데이터를 가져오는 중 오류가 발생했습니다: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
190
lib/data/datasources/remote/maintenance_remote_datasource.dart
Normal file
190
lib/data/datasources/remote/maintenance_remote_datasource.dart
Normal file
@@ -0,0 +1,190 @@
|
||||
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/maintenance_dto.dart';
|
||||
|
||||
abstract class MaintenanceRemoteDataSource {
|
||||
Future<MaintenanceListResponse> getMaintenances({
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
int? equipmentId,
|
||||
String? maintenanceType,
|
||||
bool? isExpired,
|
||||
int? expiringDays,
|
||||
bool includeDeleted = false,
|
||||
});
|
||||
|
||||
Future<MaintenanceDto> createMaintenance(MaintenanceRequestDto request);
|
||||
|
||||
Future<MaintenanceDto> getMaintenanceDetail(int id);
|
||||
|
||||
Future<MaintenanceDto> updateMaintenance(int id, MaintenanceUpdateRequestDto request);
|
||||
|
||||
Future<void> deleteMaintenance(int id);
|
||||
|
||||
// 만료 예정 유지보수 조회 (백엔드 전용 API)
|
||||
Future<List<MaintenanceDto>> getExpiringMaintenances({int days = 30});
|
||||
}
|
||||
|
||||
class MaintenanceRemoteDataSourceImpl implements MaintenanceRemoteDataSource {
|
||||
final ApiClient _apiClient = GetIt.instance<ApiClient>();
|
||||
|
||||
@override
|
||||
Future<MaintenanceListResponse> getMaintenances({
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
int? equipmentId,
|
||||
String? maintenanceType,
|
||||
bool? isExpired,
|
||||
int? expiringDays,
|
||||
bool includeDeleted = false,
|
||||
}) async {
|
||||
try {
|
||||
final queryParams = <String, dynamic>{
|
||||
'page': page,
|
||||
'per_page': perPage,
|
||||
if (equipmentId != null) 'equipment_id': equipmentId,
|
||||
if (maintenanceType != null) 'maintenance_type': maintenanceType,
|
||||
if (isExpired != null) 'is_expired': isExpired,
|
||||
if (expiringDays != null) 'expiring_days': expiringDays,
|
||||
'include_deleted': includeDeleted,
|
||||
};
|
||||
|
||||
final response = await _apiClient.get(
|
||||
ApiEndpoints.maintenances,
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
|
||||
print('[Maintenance API] Response: ${response.data}');
|
||||
|
||||
// 백엔드 응답은 직접 data 배열과 페이지네이션 정보 반환
|
||||
final Map<String, dynamic> responseData = response.data;
|
||||
return MaintenanceListResponse.fromJson(responseData);
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Network error occurred',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MaintenanceDto> createMaintenance(MaintenanceRequestDto request) async {
|
||||
try {
|
||||
final response = await _apiClient.post(
|
||||
ApiEndpoints.maintenances,
|
||||
data: request.toJson(),
|
||||
);
|
||||
|
||||
print('[Maintenance API] Create Response: ${response.data}');
|
||||
|
||||
// API 응답이 {success: true, data: {...}} 형태인 경우 처리
|
||||
final responseData = response.data;
|
||||
if (responseData is Map && responseData.containsKey('data')) {
|
||||
return MaintenanceDto.fromJson(responseData['data']);
|
||||
} else {
|
||||
// 직접 데이터인 경우
|
||||
return MaintenanceDto.fromJson(responseData);
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Network error occurred',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MaintenanceDto> getMaintenanceDetail(int id) async {
|
||||
try {
|
||||
final response = await _apiClient.get('${ApiEndpoints.maintenances}/$id');
|
||||
|
||||
print('[Maintenance API] Detail Response: ${response.data}');
|
||||
|
||||
// API 응답이 {success: true, data: {...}} 형태인 경우 처리
|
||||
final responseData = response.data;
|
||||
if (responseData is Map && responseData.containsKey('data')) {
|
||||
return MaintenanceDto.fromJson(responseData['data']);
|
||||
} else {
|
||||
// 직접 데이터인 경우
|
||||
return MaintenanceDto.fromJson(responseData);
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Network error occurred',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MaintenanceDto> updateMaintenance(int id, MaintenanceUpdateRequestDto request) async {
|
||||
try {
|
||||
// 디버그: 전송할 JSON 데이터 로깅
|
||||
final jsonData = request.toJson();
|
||||
|
||||
// null 필드 제거 (백엔드가 null을 처리하지 못하는 경우 대비)
|
||||
final cleanedData = Map<String, dynamic>.from(jsonData)
|
||||
..removeWhere((key, value) => value == null);
|
||||
|
||||
print('[Maintenance API] Update Request JSON: $cleanedData');
|
||||
print('[Maintenance API] JSON keys: ${cleanedData.keys.toList()}');
|
||||
|
||||
final response = await _apiClient.put(
|
||||
'${ApiEndpoints.maintenances}/$id',
|
||||
data: cleanedData,
|
||||
);
|
||||
|
||||
print('[Maintenance API] Update Response: ${response.data}');
|
||||
|
||||
// API 응답이 {success: true, data: {...}} 형태인 경우 처리
|
||||
final responseData = response.data;
|
||||
if (responseData is Map && responseData.containsKey('data')) {
|
||||
return MaintenanceDto.fromJson(responseData['data']);
|
||||
} else {
|
||||
// 직접 데이터인 경우
|
||||
return MaintenanceDto.fromJson(responseData);
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Network error occurred',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteMaintenance(int id) async {
|
||||
try {
|
||||
await _apiClient.delete('${ApiEndpoints.maintenances}/$id');
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Network error occurred',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MaintenanceDto>> getExpiringMaintenances({int days = 30}) async {
|
||||
try {
|
||||
final response = await _apiClient.get(
|
||||
'${ApiEndpoints.maintenances}/expiring',
|
||||
queryParameters: {'days': days},
|
||||
);
|
||||
|
||||
print('[Maintenance API] Expiring Response: ${response.data}');
|
||||
|
||||
// 백엔드는 직접 배열을 반환
|
||||
final List<dynamic> responseData = response.data;
|
||||
return responseData.map((json) => MaintenanceDto.fromJson(json)).toList();
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(
|
||||
message: e.response?.data['message'] ?? 'Network error occurred',
|
||||
statusCode: e.response?.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
180
lib/data/datasources/remote/model_remote_datasource.dart
Normal file
180
lib/data/datasources/remote/model_remote_datasource.dart
Normal file
@@ -0,0 +1,180 @@
|
||||
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/model/model_dto.dart';
|
||||
|
||||
abstract class ModelRemoteDataSource {
|
||||
Future<ModelListDto> getModels({
|
||||
int page = 1,
|
||||
int perPage = 10,
|
||||
String? search,
|
||||
int? vendorId,
|
||||
bool? includeDeleted,
|
||||
});
|
||||
|
||||
Future<ModelDto> createModel(CreateModelRequest request);
|
||||
|
||||
Future<ModelDto> getModelDetail(int id);
|
||||
|
||||
Future<ModelDto> updateModel(int id, UpdateModelRequest request);
|
||||
|
||||
Future<void> deleteModel(int id);
|
||||
|
||||
Future<ModelDto> restoreModel(int id);
|
||||
|
||||
Future<List<ModelDto>> getModelsByVendor(int vendorId);
|
||||
}
|
||||
|
||||
class ModelRemoteDataSourceImpl implements ModelRemoteDataSource {
|
||||
final ApiClient _apiClient = GetIt.instance<ApiClient>();
|
||||
|
||||
@override
|
||||
Future<ModelListDto> getModels({
|
||||
int page = 1,
|
||||
int perPage = 10,
|
||||
String? search,
|
||||
int? vendorId,
|
||||
bool? includeDeleted,
|
||||
}) async {
|
||||
try {
|
||||
final queryParams = <String, dynamic>{
|
||||
'page': page,
|
||||
'per_page': perPage,
|
||||
};
|
||||
|
||||
if (search != null && search.isNotEmpty) {
|
||||
queryParams['search'] = search;
|
||||
}
|
||||
if (vendorId != null) {
|
||||
queryParams['vendor_id'] = vendorId;
|
||||
}
|
||||
if (includeDeleted != null) {
|
||||
queryParams['include_deleted'] = includeDeleted;
|
||||
}
|
||||
|
||||
final response = await _apiClient.dio.get(
|
||||
ApiEndpoints.models,
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return ModelListDto.fromJson(response.data);
|
||||
} else {
|
||||
throw ServerException(message: '모델 목록 조회 실패: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(message: '네트워크 오류: ${e.message}');
|
||||
} catch (e) {
|
||||
throw ServerException(message: '모델 목록 조회 실패: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ModelDto> getModelDetail(int id) async {
|
||||
try {
|
||||
final response = await _apiClient.dio.get('${ApiEndpoints.models}/$id');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return ModelDto.fromJson(response.data);
|
||||
} else {
|
||||
throw ServerException(message: '모델 상세 조회 실패: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(message: '네트워크 오류: ${e.message}');
|
||||
} catch (e) {
|
||||
throw ServerException(message: '모델 상세 조회 실패: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ModelDto> createModel(CreateModelRequest request) async {
|
||||
try {
|
||||
final response = await _apiClient.dio.post(
|
||||
ApiEndpoints.models,
|
||||
data: request.toJson(),
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
return ModelDto.fromJson(response.data);
|
||||
} else {
|
||||
throw ServerException(message: '모델 생성 실패: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(message: '네트워크 오류: ${e.message}');
|
||||
} catch (e) {
|
||||
throw ServerException(message: '모델 생성 실패: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ModelDto> updateModel(int id, UpdateModelRequest request) async {
|
||||
try {
|
||||
final response = await _apiClient.dio.put(
|
||||
'${ApiEndpoints.models}/$id',
|
||||
data: request.toJson(),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return ModelDto.fromJson(response.data);
|
||||
} else {
|
||||
throw ServerException(message: '모델 수정 실패: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(message: '네트워크 오류: ${e.message}');
|
||||
} catch (e) {
|
||||
throw ServerException(message: '모델 수정 실패: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteModel(int id) async {
|
||||
try {
|
||||
final response = await _apiClient.dio.delete('${ApiEndpoints.models}/$id');
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw ServerException(message: '모델 삭제 실패: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(message: '네트워크 오류: ${e.message}');
|
||||
} catch (e) {
|
||||
throw ServerException(message: '모델 삭제 실패: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ModelDto> restoreModel(int id) async {
|
||||
try {
|
||||
final response = await _apiClient.dio.put('${ApiEndpoints.models}/$id/restore');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return ModelDto.fromJson(response.data);
|
||||
} else {
|
||||
throw ServerException(message: '모델 복구 실패: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(message: '네트워크 오류: ${e.message}');
|
||||
} catch (e) {
|
||||
throw ServerException(message: '모델 복구 실패: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ModelDto>> getModelsByVendor(int vendorId) async {
|
||||
try {
|
||||
final response = await _apiClient.dio.get('${ApiEndpoints.modelsByVendor}/$vendorId');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = response.data;
|
||||
return data.map((json) => ModelDto.fromJson(json)).toList();
|
||||
} else {
|
||||
throw ServerException(message: '제조사별 모델 조회 실패: ${response.statusCode}');
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw ServerException(message: '네트워크 오류: ${e.message}');
|
||||
} catch (e) {
|
||||
throw ServerException(message: '제조사별 모델 조회 실패: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import 'package:superport/data/models/warehouse/warehouse_dto.dart';
|
||||
abstract class WarehouseRemoteDataSource {
|
||||
Future<WarehouseLocationListDto> getWarehouseLocations({
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
int perPage = 10,
|
||||
bool? isActive,
|
||||
String? search,
|
||||
bool includeInactive = false,
|
||||
@@ -20,7 +20,7 @@ abstract class WarehouseRemoteDataSource {
|
||||
Future<WarehouseEquipmentListDto> getWarehouseEquipment(
|
||||
int warehouseId, {
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
int perPage = 10,
|
||||
});
|
||||
Future<WarehouseCapacityInfo> getWarehouseCapacity(int id);
|
||||
Future<List<WarehouseDto>> getInUseWarehouseLocations();
|
||||
@@ -37,7 +37,7 @@ class WarehouseRemoteDataSourceImpl implements WarehouseRemoteDataSource {
|
||||
@override
|
||||
Future<WarehouseLocationListDto> getWarehouseLocations({
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
int perPage = 10,
|
||||
bool? isActive,
|
||||
String? search,
|
||||
bool includeInactive = false,
|
||||
@@ -65,7 +65,7 @@ class WarehouseRemoteDataSourceImpl implements WarehouseRemoteDataSource {
|
||||
'items': dataList,
|
||||
'total': response.data['total'] ?? 0,
|
||||
'page': response.data['page'] ?? 1,
|
||||
'per_page': response.data['page_size'] ?? 20,
|
||||
'per_page': response.data['page_size'] ?? 10,
|
||||
'total_pages': response.data['total_pages'] ?? 1,
|
||||
};
|
||||
|
||||
@@ -178,7 +178,7 @@ class WarehouseRemoteDataSourceImpl implements WarehouseRemoteDataSource {
|
||||
Future<WarehouseEquipmentListDto> getWarehouseEquipment(
|
||||
int warehouseId, {
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
int perPage = 10,
|
||||
}) async {
|
||||
try {
|
||||
final queryParams = <String, dynamic>{
|
||||
|
||||
Reference in New Issue
Block a user