import 'package:dio/dio.dart'; import 'package:injectable/injectable.dart'; import 'package:superport/core/errors/exceptions.dart'; import 'package:superport/data/datasources/remote/api_client.dart'; import 'package:superport/data/models/user/user_dto.dart'; /// 사용자 원격 데이터 소스 (서버 API v0.2.1 대응) /// 엔드포인트: /api/v1/users abstract class UserRemoteDataSource { /// 사용자 목록 조회 (페이지네이션 지원) Future getUsers({ int page = 1, int perPage = 20, bool? isActive, String? role, }); /// 단일 사용자 조회 Future getUser(int id); /// 사용자 생성 Future createUser(UserRequestDto request); /// 사용자 정보 수정 (비밀번호 포함) Future updateUser(int id, UserUpdateRequestDto request); /// 사용자 소프트 삭제 (is_active = false) Future deleteUser(int id); } @LazySingleton(as: UserRemoteDataSource) class UserRemoteDataSourceImpl implements UserRemoteDataSource { final ApiClient _apiClient; UserRemoteDataSourceImpl(this._apiClient); /// 사용자 목록 조회 (실제 백엔드 API 응답 대응) @override Future getUsers({ int page = 1, int perPage = 20, bool? isActive, String? role, }) async { try { final queryParams = { 'page': page, 'per_page': perPage, }; // UI 호환 파라미터 (백엔드에서 무시) if (isActive != null) { queryParams['is_active'] = isActive; } if (role != null) { queryParams['role'] = role; } final response = await _apiClient.get( '/users', queryParameters: queryParams, ); // 실제 백엔드 응답 구조: {data: [...], total: 100, page: 1, page_size: 20, total_pages: 5} if (response.data != null) { return UserListResponse.fromJson(Map.from(response.data)); } else { throw ApiException( message: '사용자 목록을 불러오는데 실패했습니다', statusCode: response.statusCode, ); } } on DioException catch (e) { throw ApiException( message: e.response?.data?['message'] ?? e.message ?? '사용자 목록을 불러오는데 실패했습니다', statusCode: e.response?.statusCode, ); } } /// 단일 사용자 조회 (GET /api/v1/users/{id}) @override Future getUser(int id) async { try { final response = await _apiClient.get('/users/$id'); if (response.data != null) { // 실제 백엔드 응답이 단일 객체인 경우 return UserDto.fromJson(Map.from(response.data)); } else { throw ApiException( message: '사용자 정보를 불러오는데 실패했습니다', statusCode: response.statusCode, ); } } on DioException catch (e) { throw ApiException( message: e.response?.data?['message'] ?? e.message ?? '사용자 정보를 불러오는데 실패했습니다', statusCode: e.response?.statusCode, ); } } /// 사용자 생성 (POST /api/v1/users) @override Future createUser(UserRequestDto request) async { try { final response = await _apiClient.post( '/users', data: request.toJson(), ); if (response.data != null) { return UserDto.fromJson(Map.from(response.data)); } else { throw ApiException( message: '사용자 생성에 실패했습니다', statusCode: response.statusCode, ); } } on DioException catch (e) { throw ApiException( message: e.response?.data?['message'] ?? e.message ?? '사용자 생성에 실패했습니다', statusCode: e.response?.statusCode, ); } } /// 사용자 수정 (PUT /api/v1/users/{id}) @override Future updateUser(int id, UserUpdateRequestDto request) async { try { // null이나 빈 값 필터링하여 실제로 변경된 필드만 전송 final requestData = request.toJson(); requestData.removeWhere((key, value) => value == null || (value is String && value.isEmpty)); final response = await _apiClient.put( '/users/$id', data: requestData, ); if (response.data != null) { return UserDto.fromJson(Map.from(response.data)); } else { throw ApiException( message: '사용자 정보 수정에 실패했습니다', statusCode: response.statusCode, ); } } on DioException catch (e) { throw ApiException( message: e.response?.data?['message'] ?? e.message ?? '사용자 정보 수정에 실패했습니다', statusCode: e.response?.statusCode, ); } } /// 사용자 소프트 삭제 (DELETE /api/v1/users/{id}) /// 서버에서 is_active = false로 설정 @override Future deleteUser(int id) async { try { final response = await _apiClient.delete('/users/$id'); // 소프트 딜리트는 응답 데이터가 있을 수 있음 if (response.statusCode != null && response.statusCode! >= 400) { throw ApiException( message: response.data?['message'] ?? '사용자 삭제에 실패했습니다', statusCode: response.statusCode, ); } } on DioException catch (e) { throw ApiException( message: e.response?.data?['message'] ?? e.message ?? '사용자 삭제에 실패했습니다', statusCode: e.response?.statusCode, ); } } }