import 'package:dartz/dartz.dart'; import 'package:injectable/injectable.dart'; import '../../core/errors/failures.dart'; import '../../domain/repositories/license_repository.dart'; import '../../models/license_model.dart'; import '../datasources/remote/license_remote_datasource.dart'; import '../models/common/paginated_response.dart'; import '../models/dashboard/license_expiry_summary.dart'; import '../models/license/license_dto.dart'; import '../models/license/license_request_dto.dart'; /// 라이선스 Repository 구현체 /// 라이선스 및 유지보수 계약 관리 작업을 처리하며 도메인 모델과 API DTO 간 변환을 담당 @Injectable(as: LicenseRepository) class LicenseRepositoryImpl implements LicenseRepository { final LicenseRemoteDataSource remoteDataSource; LicenseRepositoryImpl({required this.remoteDataSource}); @override Future>> getLicenses({ int? page, int? limit, String? search, int? companyId, String? equipmentType, String? expiryStatus, String? sortBy, String? sortOrder, }) async { try { final result = await remoteDataSource.getLicenses( page: page ?? 1, perPage: limit ?? 20, isActive: null, // expiryStatus에 따른 필터링 로직 필요 시 추가 companyId: companyId, assignedUserId: null, licenseType: equipmentType, ); // DTO를 도메인 모델로 변환 final licenses = result.items.map((dto) => _mapDtoToDomain(dto)).toList(); // 검색 필터링 (서버에서 지원하지 않는 경우 클라이언트 측에서 처리) if (search != null && search.isNotEmpty) { final filteredLicenses = licenses.where((license) { final searchLower = search.toLowerCase(); return (license.productName?.toLowerCase().contains(searchLower) ?? false) || (license.companyName?.toLowerCase().contains(searchLower) ?? false) || (license.vendor?.toLowerCase().contains(searchLower) ?? false); }).toList(); final paginatedResult = PaginatedResponse( items: filteredLicenses, page: result.page, size: 20, totalElements: filteredLicenses.length, totalPages: (filteredLicenses.length / 20).ceil(), first: result.page == 0, last: result.page >= (filteredLicenses.length / 20).ceil() - 1, ); return Right(paginatedResult); } final paginatedResult = PaginatedResponse( items: licenses, page: result.page, size: 20, totalElements: result.total, totalPages: (result.total / 20).ceil(), first: result.page == 0, last: result.page >= (result.total / 20).ceil() - 1, ); return Right(paginatedResult); } catch (e) { return Left(ServerFailure( message: '라이선스 목록 조회 중 오류가 발생했습니다: ${e.toString()}', )); } } @override Future> getLicenseById(int id) async { try { final result = await remoteDataSource.getLicenseById(id); final license = _mapDtoToDomain(result); return Right(license); } catch (e) { if (e.toString().contains('404')) { return Left(NotFoundFailure( message: '해당 라이선스를 찾을 수 없습니다.', resourceType: 'License', resourceId: id.toString(), )); } return Left(ServerFailure( message: '라이선스 상세 정보 조회 중 오류가 발생했습니다: ${e.toString()}', )); } } @override Future> createLicense(License license) async { try { final request = _mapDomainToCreateRequest(license); final result = await remoteDataSource.createLicense(request); final createdLicense = _mapDtoToDomain(result); return Right(createdLicense); } catch (e) { if (e.toString().contains('중복')) { return Left(DuplicateFailure( message: '이미 존재하는 라이선스입니다.', field: 'licenseKey', value: license.licenseKey, )); } if (e.toString().contains('유효성')) { return Left(ValidationFailure( message: '입력 데이터가 올바르지 않습니다.', )); } return Left(ServerFailure( message: '라이선스 생성 중 오류가 발생했습니다: ${e.toString()}', )); } } @override Future> updateLicense(int id, License license) async { try { final request = _mapDomainToUpdateRequest(license); final result = await remoteDataSource.updateLicense(id, request); final updatedLicense = _mapDtoToDomain(result); return Right(updatedLicense); } catch (e) { if (e.toString().contains('404')) { return Left(NotFoundFailure( message: '수정할 라이선스를 찾을 수 없습니다.', resourceType: 'License', resourceId: id.toString(), )); } if (e.toString().contains('중복')) { return Left(DuplicateFailure( message: '이미 존재하는 라이선스키입니다.', field: 'licenseKey', value: license.licenseKey, )); } return Left(ServerFailure( message: '라이선스 정보 수정 중 오류가 발생했습니다: ${e.toString()}', )); } } @override Future> deleteLicense(int id) async { try { await remoteDataSource.deleteLicense(id); return const Right(null); } catch (e) { if (e.toString().contains('404')) { return Left(NotFoundFailure( message: '삭제할 라이선스를 찾을 수 없습니다.', resourceType: 'License', resourceId: id.toString(), )); } if (e.toString().contains('참조')) { return Left(BusinessFailure( message: '해당 라이선스에 연결된 데이터가 있어 삭제할 수 없습니다.', )); } return Left(ServerFailure( message: '라이선스 삭제 중 오류가 발생했습니다: ${e.toString()}', )); } } @override Future>> getExpiringLicenses({int days = 30, int? companyId}) async { // TODO: API에서 만료 예정 라이선스 조회 기능이 구현되면 추가 return const Left(ServerFailure( message: '만료 예정 라이선스 조회 기능이 아직 구현되지 않았습니다.', )); } @override Future>> getExpiredLicenses({int? companyId}) async { // TODO: API에서 만료된 라이선스 조회 기능이 구현되면 추가 return const Left(ServerFailure( message: '만료된 라이선스 조회 기능이 아직 구현되지 않았습니다.', )); } @override Future> getLicenseExpirySummary() async { // TODO: API에서 라이선스 만료 요약 기능이 구현되면 추가 return const Left(ServerFailure( message: '라이선스 만료 요약 조회 기능이 아직 구현되지 않았습니다.', )); } @override Future> renewLicense(int id, DateTime newExpiryDate, {double? renewalCost, String? renewalNote}) async { // TODO: API에서 라이선스 갱신 기능이 구현되면 추가 return const Left(ServerFailure( message: '라이선스 갱신 기능이 아직 구현되지 않았습니다.', )); } @override Future>> getLicenseStatsByCompany(int companyId) async { // TODO: API에서 회사별 라이선스 통계 기능이 구현되면 추가 return const Left(ServerFailure( message: '회사별 라이선스 통계 기능이 아직 구현되지 않았습니다.', )); } @override Future>> getLicenseCountByType() async { // TODO: API에서 라이선스 유형별 통계 기능이 구현되면 추가 return const Left(ServerFailure( message: '라이선스 유형별 통계 기능이 아직 구현되지 않았습니다.', )); } @override Future> setExpiryNotification(int licenseId, {int notifyDays = 30}) async { // TODO: API에서 만료 알림 설정 기능이 구현되면 추가 return const Left(ServerFailure( message: '만료 알림 설정 기능이 아직 구현되지 않았습니다.', )); } @override Future>> searchLicenses(String query, {int? companyId, int? limit}) async { try { final result = await remoteDataSource.getLicenses( page: 1, perPage: limit ?? 10, companyId: companyId, ); // 클라이언트 측에서 검색 필터링 final searchLower = query.toLowerCase(); final filteredLicenses = result.items .where((dto) { final license = _mapDtoToDomain(dto); return (license.productName?.toLowerCase().contains(searchLower) ?? false) || (license.companyName?.toLowerCase().contains(searchLower) ?? false) || (license.vendor?.toLowerCase().contains(searchLower) ?? false); }) .map((dto) => _mapDtoToDomain(dto)) .toList(); return Right(filteredLicenses); } catch (e) { return Left(ServerFailure( message: '라이선스 검색 중 오류가 발생했습니다: ${e.toString()}', )); } } // Private 매퍼 메서드들 License _mapDtoToDomain(LicenseDto dto) { return License( id: dto.id, licenseKey: dto.licenseKey, productName: dto.productName, vendor: dto.vendor, licenseType: dto.licenseType, userCount: dto.userCount, purchaseDate: dto.purchaseDate, expiryDate: dto.expiryDate, purchasePrice: dto.purchasePrice, companyId: dto.companyId, branchId: dto.branchId, assignedUserId: dto.assignedUserId, remark: dto.remark, isActive: dto.isActive, createdAt: dto.createdAt, updatedAt: dto.updatedAt, companyName: dto.companyName, branchName: dto.branchName, assignedUserName: dto.assignedUserName, ); } CreateLicenseRequest _mapDomainToCreateRequest(License license) { return CreateLicenseRequest( licenseKey: license.licenseKey, productName: license.productName, vendor: license.vendor, licenseType: license.licenseType, userCount: license.userCount, purchaseDate: license.purchaseDate, expiryDate: license.expiryDate, purchasePrice: license.purchasePrice, companyId: license.companyId, branchId: license.branchId, remark: license.remark, ); } UpdateLicenseRequest _mapDomainToUpdateRequest(License license) { return UpdateLicenseRequest( licenseKey: license.licenseKey, productName: license.productName, vendor: license.vendor, licenseType: license.licenseType, userCount: license.userCount, purchaseDate: license.purchaseDate, expiryDate: license.expiryDate, purchasePrice: license.purchasePrice, remark: license.remark, isActive: license.isActive, ); } }