refactor: Repository 패턴 적용 및 Clean Architecture 완성
## 주요 변경사항 ### 🏗️ Architecture - Repository 패턴 전면 도입 (인터페이스/구현체 분리) - Domain Layer에 Repository 인터페이스 정의 - Data Layer에 Repository 구현체 배치 - UseCase 의존성을 Service에서 Repository로 전환 ### 📦 Dependency Injection - GetIt 기반 DI Container 재구성 (lib/injection_container.dart) - Repository 인터페이스와 구현체 등록 - Service와 Repository 공존 (마이그레이션 기간) ### 🔄 Migration Status 완료: - License 모듈 (6개 UseCase) - Warehouse Location 모듈 (5개 UseCase) 진행중: - Auth 모듈 (2/5 UseCase) - Company 모듈 (1/6 UseCase) 대기: - User 모듈 (7개 UseCase) - Equipment 모듈 (4개 UseCase) ### 🎯 Controller 통합 - 중복 Controller 제거 (with_usecase 버전) - 단일 Controller로 통합 - UseCase 패턴 직접 적용 ### 🧹 코드 정리 - 임시 파일 제거 (test_*.md, task.md) - Node.js 아티팩트 제거 (package.json) - 불필요한 테스트 파일 정리 ### ✅ 테스트 개선 - Real API 중심 테스트 구조 - Mock 제거, 실제 API 엔드포인트 사용 - 통합 테스트 프레임워크 강화 ## 기술적 영향 - 의존성 역전 원칙 적용 - 레이어 간 결합도 감소 - 테스트 용이성 향상 - 확장성 및 유지보수성 개선 ## 다음 단계 1. User/Equipment 모듈 Repository 마이그레이션 2. Service Layer 점진적 제거 3. 캐싱 전략 구현 4. 성능 최적화
This commit is contained in:
214
lib/data/repositories/auth_repository_impl.dart
Normal file
214
lib/data/repositories/auth_repository_impl.dart
Normal file
@@ -0,0 +1,214 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../core/errors/failures.dart';
|
||||
import '../../domain/repositories/auth_repository.dart';
|
||||
import '../datasources/remote/auth_remote_datasource.dart';
|
||||
import '../models/auth/auth_user.dart';
|
||||
import '../models/auth/login_request.dart';
|
||||
import '../models/auth/login_response.dart';
|
||||
import '../models/auth/logout_request.dart';
|
||||
import '../models/auth/refresh_token_request.dart';
|
||||
import '../models/auth/token_response.dart';
|
||||
|
||||
/// 인증 Repository 구현체
|
||||
/// JWT 토큰 기반 인증 시스템을 관리하며 SharedPreferences를 사용해 토큰을 저장
|
||||
@Injectable(as: AuthRepository)
|
||||
class AuthRepositoryImpl implements AuthRepository {
|
||||
final AuthRemoteDataSource remoteDataSource;
|
||||
final SharedPreferences sharedPreferences;
|
||||
|
||||
// SharedPreferences 키 상수
|
||||
static const String _keyAccessToken = 'access_token';
|
||||
static const String _keyRefreshToken = 'refresh_token';
|
||||
static const String _keyUserData = 'user_data';
|
||||
|
||||
AuthRepositoryImpl({
|
||||
required this.remoteDataSource,
|
||||
required this.sharedPreferences,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, LoginResponse>> login(LoginRequest loginRequest) async {
|
||||
try {
|
||||
final result = await remoteDataSource.login(loginRequest);
|
||||
|
||||
return result.fold(
|
||||
(failure) => Left(failure),
|
||||
(loginResponse) async {
|
||||
// 로그인 성공 시 토큰과 사용자 정보를 로컬에 저장
|
||||
await _saveTokens(loginResponse.accessToken, loginResponse.refreshToken);
|
||||
await _saveUserData(loginResponse.user);
|
||||
|
||||
return Right(loginResponse);
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '로그인 처리 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> logout() async {
|
||||
try {
|
||||
// 로컬에 저장된 리프레시 토큰으로 로그아웃 요청 생성
|
||||
final refreshToken = await _getRefreshToken();
|
||||
if (refreshToken == null) {
|
||||
// 토큰이 없으면 로컬 데이터만 삭제하고 성공 처리
|
||||
await _clearLocalData();
|
||||
return const Right(null);
|
||||
}
|
||||
|
||||
final logoutRequest = LogoutRequest(refreshToken: refreshToken);
|
||||
final result = await remoteDataSource.logout(logoutRequest);
|
||||
|
||||
return result.fold(
|
||||
(failure) async {
|
||||
// 서버 로그아웃 실패해도 로컬 데이터는 삭제
|
||||
await _clearLocalData();
|
||||
return Left(failure);
|
||||
},
|
||||
(_) async {
|
||||
// 성공 시 로컬 데이터 삭제
|
||||
await _clearLocalData();
|
||||
return const Right(null);
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
// 오류 발생해도 로컬 데이터는 삭제
|
||||
await _clearLocalData();
|
||||
return Left(ServerFailure(
|
||||
message: '로그아웃 처리 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, TokenResponse>> refreshToken(RefreshTokenRequest refreshRequest) async {
|
||||
try {
|
||||
final result = await remoteDataSource.refreshToken(refreshRequest);
|
||||
|
||||
return result.fold(
|
||||
(failure) => Left(failure),
|
||||
(tokenResponse) async {
|
||||
// 새 토큰 저장
|
||||
await _saveTokens(tokenResponse.accessToken, tokenResponse.refreshToken);
|
||||
return Right(tokenResponse);
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '토큰 갱신 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, AuthUser>> getCurrentUser() async {
|
||||
try {
|
||||
final userData = sharedPreferences.getString(_keyUserData);
|
||||
if (userData == null) {
|
||||
return const Left(AuthenticationFailure(
|
||||
message: '저장된 사용자 정보가 없습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
// JSON 문자열을 AuthUser 객체로 변환
|
||||
final user = AuthUser.fromJson(
|
||||
Map<String, dynamic>.from(
|
||||
// JSON 디코딩 처리 필요 시 여기에 추가
|
||||
{} // TODO: JSON 디코딩 로직 추가
|
||||
)
|
||||
);
|
||||
|
||||
return Right(user);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '사용자 정보 조회 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> isAuthenticated() async {
|
||||
try {
|
||||
final accessToken = await _getAccessToken();
|
||||
final refreshToken = await _getRefreshToken();
|
||||
|
||||
// 액세스 토큰과 리프레시 토큰이 모두 있으면 인증된 것으로 간주
|
||||
final isAuth = accessToken != null && refreshToken != null;
|
||||
return Right(isAuth);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '인증 상태 확인 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> changePassword(String currentPassword, String newPassword) async {
|
||||
// TODO: 비밀번호 변경 API가 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '비밀번호 변경 기능은 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> requestPasswordReset(String email) async {
|
||||
// TODO: 비밀번호 재설정 API가 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '비밀번호 재설정 기능은 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> validateSession() async {
|
||||
try {
|
||||
final accessToken = await _getAccessToken();
|
||||
if (accessToken == null) {
|
||||
return const Right(false);
|
||||
}
|
||||
|
||||
// TODO: 서버에서 세션 유효성 검증 API가 있으면 호출
|
||||
// 현재는 토큰 존재 여부만 확인
|
||||
return const Right(true);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '세션 유효성 검증 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Private 헬퍼 메서드들
|
||||
|
||||
/// 액세스 토큰과 리프레시 토큰을 로컬에 저장
|
||||
Future<void> _saveTokens(String accessToken, String refreshToken) async {
|
||||
await sharedPreferences.setString(_keyAccessToken, accessToken);
|
||||
await sharedPreferences.setString(_keyRefreshToken, refreshToken);
|
||||
}
|
||||
|
||||
/// 사용자 데이터를 로컬에 저장
|
||||
Future<void> _saveUserData(AuthUser user) async {
|
||||
// TODO: JSON 인코딩 로직 구현
|
||||
await sharedPreferences.setString(_keyUserData, user.toJson().toString());
|
||||
}
|
||||
|
||||
/// 액세스 토큰 조회
|
||||
Future<String?> _getAccessToken() async {
|
||||
return sharedPreferences.getString(_keyAccessToken);
|
||||
}
|
||||
|
||||
/// 리프레시 토큰 조회
|
||||
Future<String?> _getRefreshToken() async {
|
||||
return sharedPreferences.getString(_keyRefreshToken);
|
||||
}
|
||||
|
||||
/// 로컬 데이터 전체 삭제
|
||||
Future<void> _clearLocalData() async {
|
||||
await sharedPreferences.remove(_keyAccessToken);
|
||||
await sharedPreferences.remove(_keyRefreshToken);
|
||||
await sharedPreferences.remove(_keyUserData);
|
||||
}
|
||||
}
|
||||
456
lib/data/repositories/company_repository_impl.dart
Normal file
456
lib/data/repositories/company_repository_impl.dart
Normal file
@@ -0,0 +1,456 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../../core/errors/failures.dart';
|
||||
import '../../domain/repositories/company_repository.dart';
|
||||
import '../../models/company_model.dart';
|
||||
import '../../models/address_model.dart';
|
||||
import '../datasources/remote/company_remote_datasource.dart';
|
||||
import '../models/common/paginated_response.dart';
|
||||
import '../models/company/company_dto.dart';
|
||||
import '../models/company/branch_dto.dart';
|
||||
import '../models/company/company_list_dto.dart';
|
||||
|
||||
/// 회사 관리 Repository 구현체
|
||||
/// 회사 및 지점 정보 CRUD 작업을 처리하며 도메인 모델과 API DTO 간 변환을 담당
|
||||
@Injectable(as: CompanyRepository)
|
||||
class CompanyRepositoryImpl implements CompanyRepository {
|
||||
final CompanyRemoteDataSource remoteDataSource;
|
||||
|
||||
CompanyRepositoryImpl({required this.remoteDataSource});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, PaginatedResponse<Company>>> getCompanies({
|
||||
int? page,
|
||||
int? limit,
|
||||
String? search,
|
||||
CompanyType? companyType,
|
||||
String? sortBy,
|
||||
String? sortOrder,
|
||||
}) async {
|
||||
try {
|
||||
final result = await remoteDataSource.getCompanies(
|
||||
page: page ?? 1,
|
||||
perPage: limit ?? 20,
|
||||
search: search,
|
||||
isActive: null, // companyType에 따른 필터링 로직 필요 시 추가
|
||||
);
|
||||
|
||||
// DTO를 도메인 모델로 변환
|
||||
final companies = result.items.map((dto) => _mapDtoToDomain(dto)).toList();
|
||||
|
||||
final paginatedResult = PaginatedResponse<Company>(
|
||||
items: companies,
|
||||
page: result.page,
|
||||
size: result.size,
|
||||
totalElements: result.totalElements,
|
||||
totalPages: result.totalPages,
|
||||
first: result.first,
|
||||
last: result.last,
|
||||
);
|
||||
|
||||
return Right(paginatedResult);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '회사 목록 조회 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Company>> getCompanyById(int id) async {
|
||||
try {
|
||||
final result = await remoteDataSource.getCompanyWithBranches(id);
|
||||
final company = _mapDetailDtoToDomain(result);
|
||||
return Right(company);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '해당 회사를 찾을 수 없습니다.',
|
||||
resourceType: 'Company',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '회사 상세 정보 조회 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Company>> createCompany(Company company) async {
|
||||
try {
|
||||
final request = _mapDomainToCreateRequest(company);
|
||||
final result = await remoteDataSource.createCompany(request);
|
||||
final createdCompany = _mapResponseToDomain(result);
|
||||
return Right(createdCompany);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('중복')) {
|
||||
return Left(DuplicateFailure(
|
||||
message: '이미 존재하는 회사명입니다.',
|
||||
field: 'name',
|
||||
value: company.name,
|
||||
));
|
||||
}
|
||||
if (e.toString().contains('유효성')) {
|
||||
return Left(ValidationFailure(
|
||||
message: '입력 데이터가 올바르지 않습니다.',
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '회사 생성 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Company>> updateCompany(int id, Company company) async {
|
||||
try {
|
||||
final request = _mapDomainToUpdateRequest(company);
|
||||
final result = await remoteDataSource.updateCompany(id, request);
|
||||
final updatedCompany = _mapResponseToDomain(result);
|
||||
return Right(updatedCompany);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '수정할 회사를 찾을 수 없습니다.',
|
||||
resourceType: 'Company',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
if (e.toString().contains('중복')) {
|
||||
return Left(DuplicateFailure(
|
||||
message: '이미 존재하는 회사명입니다.',
|
||||
field: 'name',
|
||||
value: company.name,
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '회사 정보 수정 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteCompany(int id) async {
|
||||
try {
|
||||
await remoteDataSource.deleteCompany(id);
|
||||
return const Right(null);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '삭제할 회사를 찾을 수 없습니다.',
|
||||
resourceType: 'Company',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
if (e.toString().contains('참조')) {
|
||||
return Left(BusinessFailure(
|
||||
message: '해당 회사에 연결된 데이터가 있어 삭제할 수 없습니다.',
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '회사 삭제 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Company>> toggleCompanyStatus(int id) async {
|
||||
try {
|
||||
// 현재 회사 정보 조회
|
||||
final currentCompany = await remoteDataSource.getCompanyDetail(id);
|
||||
final newStatus = !currentCompany.isActive;
|
||||
|
||||
// 상태 업데이트
|
||||
await remoteDataSource.updateCompanyStatus(id, newStatus);
|
||||
|
||||
// 업데이트된 회사 정보 재조회
|
||||
final updatedCompany = await remoteDataSource.getCompanyDetail(id);
|
||||
final company = _mapResponseToDomain(updatedCompany);
|
||||
|
||||
return Right(company);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '상태를 변경할 회사를 찾을 수 없습니다.',
|
||||
resourceType: 'Company',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '회사 상태 변경 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Branch>> createBranch(int companyId, Branch branch) async {
|
||||
try {
|
||||
final request = _mapBranchToCreateRequest(branch);
|
||||
final result = await remoteDataSource.createBranch(companyId, request);
|
||||
final createdBranch = _mapBranchResponseToDomain(result);
|
||||
return Right(createdBranch);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '해당 회사를 찾을 수 없습니다.',
|
||||
resourceType: 'Company',
|
||||
resourceId: companyId.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '지점 생성 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Branch>> updateBranch(int companyId, int branchId, Branch branch) async {
|
||||
try {
|
||||
final request = _mapBranchToUpdateRequest(branch);
|
||||
final result = await remoteDataSource.updateBranch(companyId, branchId, request);
|
||||
final updatedBranch = _mapBranchResponseToDomain(result);
|
||||
return Right(updatedBranch);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '수정할 지점을 찾을 수 없습니다.',
|
||||
resourceType: 'Branch',
|
||||
resourceId: branchId.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '지점 정보 수정 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteBranch(int companyId, int branchId) async {
|
||||
try {
|
||||
await remoteDataSource.deleteBranch(companyId, branchId);
|
||||
return const Right(null);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '삭제할 지점을 찾을 수 없습니다.',
|
||||
resourceType: 'Branch',
|
||||
resourceId: branchId.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '지점 삭제 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<String>>> searchCompanyNames(String query, {int? limit}) async {
|
||||
try {
|
||||
final companies = await remoteDataSource.searchCompanies(query);
|
||||
final names = companies.map((company) => company.name).take(limit ?? 10).toList();
|
||||
return Right(names);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '회사명 검색 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Map<CompanyType, int>>> getCompanyCountByType() async {
|
||||
// TODO: API에서 회사 유형별 통계 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '회사 유형별 통계 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> hasLinkedUsers(int companyId) async {
|
||||
// TODO: 회사에 연결된 사용자 존재 여부 확인 API 구현 필요
|
||||
try {
|
||||
// 임시로 false 반환 - API 구현 후 수정 필요
|
||||
return const Right(false);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '연결된 사용자 확인 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> hasLinkedEquipment(int companyId) async {
|
||||
// TODO: 회사에 연결된 장비 존재 여부 확인 API 구현 필요
|
||||
try {
|
||||
// 임시로 false 반환 - API 구현 후 수정 필요
|
||||
return const Right(false);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '연결된 장비 확인 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> isDuplicateCompanyName(String name, {int? excludeId}) async {
|
||||
try {
|
||||
final isDuplicate = await remoteDataSource.checkDuplicateCompany(name);
|
||||
// excludeId가 있는 경우 해당 ID 제외 로직 추가 필요
|
||||
return Right(isDuplicate);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '중복 회사명 확인 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Private 매퍼 메서드들
|
||||
|
||||
Company _mapDtoToDomain(CompanyListDto dto) {
|
||||
return Company(
|
||||
id: dto.id,
|
||||
name: dto.name,
|
||||
address: Address.fromFullAddress(dto.address ?? ''),
|
||||
contactName: dto.contactName,
|
||||
contactPosition: null, // CompanyListDto에 없음
|
||||
contactPhone: dto.contactPhone,
|
||||
contactEmail: dto.contactEmail,
|
||||
companyTypes: _parseCompanyTypes(dto.companyTypes),
|
||||
remark: null, // CompanyListDto에 없음
|
||||
branches: [], // 목록에서는 지점 정보 비어있음
|
||||
);
|
||||
}
|
||||
|
||||
Company _mapDetailDtoToDomain(CompanyWithBranches dto) {
|
||||
return Company(
|
||||
id: dto.company.id,
|
||||
name: dto.company.name,
|
||||
address: Address.fromFullAddress(dto.company.address ?? ''),
|
||||
contactName: dto.company.contactName,
|
||||
contactPosition: dto.company.contactPosition,
|
||||
contactPhone: dto.company.contactPhone,
|
||||
contactEmail: dto.company.contactEmail,
|
||||
companyTypes: _parseCompanyTypes(dto.company.companyTypes),
|
||||
remark: dto.company.remark,
|
||||
branches: dto.branches.map((branch) => _mapBranchDtoToDomain(branch)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Company _mapResponseToDomain(CompanyResponse response) {
|
||||
return Company(
|
||||
id: response.id,
|
||||
name: response.name,
|
||||
address: Address.fromFullAddress(response.address ?? ''),
|
||||
contactName: response.contactName,
|
||||
contactPosition: response.contactPosition,
|
||||
contactPhone: response.contactPhone,
|
||||
contactEmail: response.contactEmail,
|
||||
companyTypes: _parseCompanyTypes(response.companyTypes),
|
||||
remark: response.remark,
|
||||
branches: [], // CompanyResponse에서는 지점 정보 따로 조회
|
||||
);
|
||||
}
|
||||
|
||||
Branch _mapBranchDtoToDomain(BranchListDto dto) {
|
||||
return Branch(
|
||||
id: dto.id,
|
||||
companyId: dto.companyId,
|
||||
name: dto.branchName,
|
||||
address: Address.fromFullAddress(dto.address ?? ''),
|
||||
contactName: dto.managerName,
|
||||
contactPosition: null, // BranchListDto에 없음
|
||||
contactPhone: dto.phone,
|
||||
contactEmail: null, // BranchListDto에 없음
|
||||
remark: null, // BranchListDto에 없음
|
||||
);
|
||||
}
|
||||
|
||||
Branch _mapBranchResponseToDomain(BranchResponse response) {
|
||||
return Branch(
|
||||
id: response.id,
|
||||
companyId: response.companyId,
|
||||
name: response.branchName,
|
||||
address: Address.fromFullAddress(response.address ?? ''),
|
||||
contactName: response.managerName,
|
||||
contactPosition: null,
|
||||
contactPhone: response.phone,
|
||||
contactEmail: null,
|
||||
remark: response.remark,
|
||||
);
|
||||
}
|
||||
|
||||
/// API에서 받은 문자열 리스트를 CompanyType enum 리스트로 변환
|
||||
/// 지원하는 형식: ['customer', 'partner'] 또는 ['고객사', '파트너사']
|
||||
List<CompanyType> _parseCompanyTypes(List<String>? types) {
|
||||
if (types == null || types.isEmpty) return [CompanyType.customer];
|
||||
|
||||
return types.map((type) {
|
||||
final lowerType = type.toLowerCase().trim();
|
||||
// API 문자열 형식 매칭
|
||||
if (lowerType == 'partner' || lowerType.contains('partner') || lowerType == '파트너사') {
|
||||
return CompanyType.partner;
|
||||
}
|
||||
// 기본값은 customer
|
||||
return CompanyType.customer;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/// CompanyType enum을 API 문자열로 변환
|
||||
String _mapCompanyTypeToApiString(CompanyType type) {
|
||||
switch (type) {
|
||||
case CompanyType.partner:
|
||||
return 'partner';
|
||||
case CompanyType.customer:
|
||||
return 'customer';
|
||||
}
|
||||
}
|
||||
|
||||
CreateCompanyRequest _mapDomainToCreateRequest(Company company) {
|
||||
return CreateCompanyRequest(
|
||||
name: company.name,
|
||||
address: company.address.toString(),
|
||||
contactName: company.contactName ?? '',
|
||||
contactPosition: company.contactPosition ?? '',
|
||||
contactPhone: company.contactPhone ?? '',
|
||||
contactEmail: company.contactEmail ?? '',
|
||||
companyTypes: company.companyTypes.map((type) => _mapCompanyTypeToApiString(type)).toList(),
|
||||
remark: company.remark,
|
||||
);
|
||||
}
|
||||
|
||||
UpdateCompanyRequest _mapDomainToUpdateRequest(Company company) {
|
||||
return UpdateCompanyRequest(
|
||||
name: company.name,
|
||||
address: company.address.toString(),
|
||||
contactName: company.contactName,
|
||||
contactPosition: company.contactPosition,
|
||||
contactPhone: company.contactPhone,
|
||||
contactEmail: company.contactEmail,
|
||||
companyTypes: company.companyTypes.map((type) => _mapCompanyTypeToApiString(type)).toList(),
|
||||
remark: company.remark,
|
||||
isActive: null, // UpdateCompanyRequest에서 필요한 경우 추가
|
||||
);
|
||||
}
|
||||
|
||||
CreateBranchRequest _mapBranchToCreateRequest(Branch branch) {
|
||||
return CreateBranchRequest(
|
||||
branchName: branch.name,
|
||||
address: branch.address.toString(),
|
||||
phone: branch.contactPhone ?? '',
|
||||
managerName: branch.contactName,
|
||||
managerPhone: null, // Branch에 없음
|
||||
remark: branch.remark,
|
||||
);
|
||||
}
|
||||
|
||||
UpdateBranchRequest _mapBranchToUpdateRequest(Branch branch) {
|
||||
return UpdateBranchRequest(
|
||||
branchName: branch.name,
|
||||
address: branch.address.toString(),
|
||||
phone: branch.contactPhone,
|
||||
managerName: branch.contactName,
|
||||
managerPhone: null, // Branch에 없음
|
||||
remark: branch.remark,
|
||||
);
|
||||
}
|
||||
}
|
||||
473
lib/data/repositories/equipment_repository_impl.dart
Normal file
473
lib/data/repositories/equipment_repository_impl.dart
Normal file
@@ -0,0 +1,473 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:superport/core/errors/exceptions.dart';
|
||||
import 'package:superport/core/errors/failures.dart';
|
||||
import 'package:superport/data/datasources/remote/equipment_remote_datasource.dart';
|
||||
import 'package:superport/data/models/equipment/equipment_dto.dart';
|
||||
import 'package:superport/data/models/equipment/equipment_in_request.dart';
|
||||
import 'package:superport/data/models/equipment/equipment_out_request.dart';
|
||||
import 'package:superport/data/models/equipment/equipment_request.dart';
|
||||
import 'package:superport/domain/repositories/equipment_repository.dart';
|
||||
import 'package:superport/models/equipment_unified_model.dart';
|
||||
|
||||
class EquipmentRepositoryImpl implements EquipmentRepository {
|
||||
final EquipmentRemoteDataSource _remoteDataSource;
|
||||
|
||||
EquipmentRepositoryImpl(this._remoteDataSource);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<EquipmentIn>>> getEquipmentIns({
|
||||
int? page,
|
||||
int? limit,
|
||||
String? search,
|
||||
String? sortBy,
|
||||
String? sortOrder,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _remoteDataSource.getEquipments(
|
||||
page: page ?? 1,
|
||||
perPage: limit ?? 20,
|
||||
status: 'IN_WAREHOUSE',
|
||||
search: search,
|
||||
);
|
||||
|
||||
final equipmentIns = response.items.map((dto) =>
|
||||
EquipmentIn(
|
||||
id: dto.id,
|
||||
equipment: Equipment(
|
||||
id: dto.id,
|
||||
manufacturer: dto.manufacturer,
|
||||
name: dto.modelName ?? '',
|
||||
category: 'N/A', // EquipmentListDto에는 category 필드가 없음
|
||||
subCategory: 'N/A', // EquipmentListDto에는 category 필드가 없음
|
||||
subSubCategory: 'N/A', // EquipmentListDto에는 category 필드가 없음
|
||||
serialNumber: dto.serialNumber,
|
||||
quantity: 1,
|
||||
),
|
||||
inDate: dto.createdAt,
|
||||
status: 'I',
|
||||
type: '신제품',
|
||||
warehouseLocation: dto.warehouseName,
|
||||
remark: null,
|
||||
)
|
||||
).toList();
|
||||
|
||||
return Right(equipmentIns);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 입고 목록 조회 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EquipmentIn>> getEquipmentInById(int id) async {
|
||||
try {
|
||||
final response = await _remoteDataSource.getEquipmentDetail(id);
|
||||
|
||||
final equipmentIn = EquipmentIn(
|
||||
id: response.id,
|
||||
equipment: Equipment(
|
||||
id: response.id,
|
||||
manufacturer: response.manufacturer,
|
||||
name: response.modelName ?? '',
|
||||
category: response.category1 ?? '',
|
||||
subCategory: response.category2 ?? '',
|
||||
subSubCategory: response.category3 ?? '',
|
||||
serialNumber: response.serialNumber,
|
||||
barcode: response.barcode,
|
||||
quantity: 1,
|
||||
inDate: response.purchaseDate,
|
||||
remark: response.remark,
|
||||
),
|
||||
inDate: response.purchaseDate ?? DateTime.now(),
|
||||
status: 'I',
|
||||
type: '신제품',
|
||||
warehouseLocation: null,
|
||||
remark: response.remark,
|
||||
);
|
||||
|
||||
return Right(equipmentIn);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 입고 상세 조회 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EquipmentIn>> createEquipmentIn(EquipmentIn equipmentIn) async {
|
||||
try {
|
||||
final request = EquipmentInRequest(
|
||||
equipmentId: equipmentIn.equipment.id ?? 0,
|
||||
quantity: equipmentIn.equipment.quantity,
|
||||
warehouseLocationId: 0, // TODO: warehouseLocation string을 ID로 변환 필요
|
||||
notes: equipmentIn.remark,
|
||||
);
|
||||
|
||||
final response = await _remoteDataSource.equipmentIn(request);
|
||||
|
||||
final newEquipmentIn = EquipmentIn(
|
||||
id: response.transactionId,
|
||||
equipment: Equipment(
|
||||
id: response.equipmentId,
|
||||
manufacturer: 'N/A', // 트랜잭션 응답에는 제조사 정보 없음
|
||||
name: 'N/A', // 트랜잭션 응답에는 모델명 정보 없음
|
||||
category: 'N/A', // 트랜잭션 응답에는 카테고리 정보 없음
|
||||
subCategory: 'N/A', // 트랜잭션 응답에는 카테고리 정보 없음
|
||||
subSubCategory: 'N/A', // 트랜잭션 응답에는 카테고리 정보 없음
|
||||
serialNumber: null,
|
||||
quantity: response.quantity,
|
||||
),
|
||||
inDate: response.transactionDate,
|
||||
status: 'I',
|
||||
type: '신제품',
|
||||
warehouseLocation: null,
|
||||
remark: response.message,
|
||||
);
|
||||
|
||||
return Right(newEquipmentIn);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 입고 생성 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EquipmentIn>> updateEquipmentIn(int id, EquipmentIn equipmentIn) async {
|
||||
try {
|
||||
final request = UpdateEquipmentRequest(
|
||||
manufacturer: equipmentIn.equipment.manufacturer,
|
||||
modelName: equipmentIn.equipment.name,
|
||||
category1: equipmentIn.equipment.category,
|
||||
category2: equipmentIn.equipment.subCategory,
|
||||
category3: equipmentIn.equipment.subSubCategory,
|
||||
serialNumber: equipmentIn.equipment.serialNumber,
|
||||
barcode: equipmentIn.equipment.barcode,
|
||||
purchaseDate: equipmentIn.inDate,
|
||||
remark: equipmentIn.remark,
|
||||
);
|
||||
|
||||
final response = await _remoteDataSource.updateEquipment(id, request);
|
||||
|
||||
final updatedEquipmentIn = EquipmentIn(
|
||||
id: response.id,
|
||||
equipment: Equipment(
|
||||
id: response.id,
|
||||
manufacturer: response.manufacturer,
|
||||
name: response.modelName ?? '',
|
||||
category: response.category1 ?? '',
|
||||
subCategory: response.category2 ?? '',
|
||||
subSubCategory: response.category3 ?? '',
|
||||
serialNumber: response.serialNumber,
|
||||
barcode: response.barcode,
|
||||
quantity: 1,
|
||||
inDate: response.purchaseDate,
|
||||
remark: response.remark,
|
||||
),
|
||||
inDate: response.purchaseDate ?? DateTime.now(),
|
||||
status: 'I',
|
||||
type: '신제품',
|
||||
warehouseLocation: null,
|
||||
remark: response.remark,
|
||||
);
|
||||
|
||||
return Right(updatedEquipmentIn);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 입고 수정 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteEquipmentIn(int id) async {
|
||||
try {
|
||||
await _remoteDataSource.deleteEquipment(id);
|
||||
return const Right(null);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 입고 삭제 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<EquipmentOut>>> getEquipmentOuts({
|
||||
int? page,
|
||||
int? limit,
|
||||
String? search,
|
||||
String? sortBy,
|
||||
String? sortOrder,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _remoteDataSource.getEquipments(
|
||||
page: page ?? 1,
|
||||
perPage: limit ?? 20,
|
||||
status: 'SHIPPED',
|
||||
search: search,
|
||||
);
|
||||
|
||||
final equipmentOuts = response.items.map((dto) =>
|
||||
EquipmentOut(
|
||||
id: dto.id,
|
||||
equipment: Equipment(
|
||||
id: dto.id,
|
||||
manufacturer: dto.manufacturer,
|
||||
name: dto.modelName ?? '',
|
||||
category: 'N/A', // EquipmentListDto에는 category 필드가 없음
|
||||
subCategory: 'N/A', // EquipmentListDto에는 category 필드가 없음
|
||||
subSubCategory: 'N/A', // EquipmentListDto에는 category 필드가 없음
|
||||
serialNumber: dto.serialNumber,
|
||||
quantity: 1,
|
||||
),
|
||||
outDate: dto.createdAt,
|
||||
status: 'O',
|
||||
company: dto.companyName,
|
||||
remark: null,
|
||||
)
|
||||
).toList();
|
||||
|
||||
return Right(equipmentOuts);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 출고 목록 조회 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EquipmentOut>> getEquipmentOutById(int id) async {
|
||||
try {
|
||||
final response = await _remoteDataSource.getEquipmentDetail(id);
|
||||
|
||||
final equipmentOut = EquipmentOut(
|
||||
id: response.id,
|
||||
equipment: Equipment(
|
||||
id: response.id,
|
||||
manufacturer: response.manufacturer,
|
||||
name: response.modelName ?? '',
|
||||
category: response.category1 ?? '',
|
||||
subCategory: response.category2 ?? '',
|
||||
subSubCategory: response.category3 ?? '',
|
||||
serialNumber: response.serialNumber,
|
||||
barcode: response.barcode,
|
||||
quantity: 1,
|
||||
inDate: response.purchaseDate,
|
||||
remark: response.remark,
|
||||
),
|
||||
outDate: DateTime.now(), // TODO: 실제 출고일 정보 필요
|
||||
status: 'O',
|
||||
company: null,
|
||||
remark: response.remark,
|
||||
);
|
||||
|
||||
return Right(equipmentOut);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 출고 상세 조회 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EquipmentOut>> createEquipmentOut(EquipmentOut equipmentOut) async {
|
||||
try {
|
||||
final request = EquipmentOutRequest(
|
||||
equipmentId: equipmentOut.equipment.id ?? 0,
|
||||
quantity: equipmentOut.equipment.quantity,
|
||||
companyId: 0, // TODO: company string을 ID로 변환 필요
|
||||
branchId: null,
|
||||
notes: equipmentOut.remark,
|
||||
);
|
||||
|
||||
final response = await _remoteDataSource.equipmentOut(request);
|
||||
|
||||
final newEquipmentOut = EquipmentOut(
|
||||
id: response.transactionId,
|
||||
equipment: Equipment(
|
||||
id: response.equipmentId,
|
||||
manufacturer: 'N/A', // 트랜잭션 응답에는 제조사 정보 없음
|
||||
name: 'N/A', // 트랜잭션 응답에는 모델명 정보 없음
|
||||
category: 'N/A', // 트랜잭션 응답에는 카테고리 정보 없음
|
||||
subCategory: 'N/A', // 트랜잭션 응답에는 카테고리 정보 없음
|
||||
subSubCategory: 'N/A', // 트랜잭션 응답에는 카테고리 정보 없음
|
||||
serialNumber: null,
|
||||
quantity: response.quantity,
|
||||
),
|
||||
outDate: response.transactionDate,
|
||||
status: 'O',
|
||||
company: null,
|
||||
remark: response.message,
|
||||
);
|
||||
|
||||
return Right(newEquipmentOut);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 출고 생성 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, EquipmentOut>> updateEquipmentOut(int id, EquipmentOut equipmentOut) async {
|
||||
try {
|
||||
final request = UpdateEquipmentRequest(
|
||||
currentCompanyId: 0, // TODO: company string을 ID로 변환 필요
|
||||
currentBranchId: null,
|
||||
remark: equipmentOut.remark,
|
||||
);
|
||||
|
||||
final response = await _remoteDataSource.updateEquipment(id, request);
|
||||
|
||||
final updatedEquipmentOut = EquipmentOut(
|
||||
id: response.id,
|
||||
equipment: Equipment(
|
||||
id: response.id,
|
||||
manufacturer: response.manufacturer,
|
||||
name: response.modelName ?? '',
|
||||
category: response.category1 ?? '',
|
||||
subCategory: response.category2 ?? '',
|
||||
subSubCategory: response.category3 ?? '',
|
||||
serialNumber: response.serialNumber,
|
||||
barcode: response.barcode,
|
||||
quantity: 1,
|
||||
inDate: response.purchaseDate,
|
||||
remark: response.remark,
|
||||
),
|
||||
outDate: DateTime.now(), // TODO: 실제 출고일 정보 필요
|
||||
status: 'O',
|
||||
company: null,
|
||||
remark: response.remark,
|
||||
);
|
||||
|
||||
return Right(updatedEquipmentOut);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 출고 수정 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteEquipmentOut(int id) async {
|
||||
try {
|
||||
await _remoteDataSource.deleteEquipment(id);
|
||||
return const Right(null);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 출고 삭제 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<EquipmentOut>>> createBatchEquipmentOut(List<EquipmentOut> equipmentOuts) async {
|
||||
try {
|
||||
final results = <EquipmentOut>[];
|
||||
|
||||
for (final equipmentOut in equipmentOuts) {
|
||||
final request = EquipmentOutRequest(
|
||||
equipmentId: equipmentOut.equipment.id ?? 0,
|
||||
quantity: equipmentOut.equipment.quantity,
|
||||
companyId: 0, // TODO: company string을 ID로 변환 필요
|
||||
branchId: null,
|
||||
notes: equipmentOut.remark,
|
||||
);
|
||||
|
||||
final response = await _remoteDataSource.equipmentOut(request);
|
||||
|
||||
results.add(EquipmentOut(
|
||||
id: response.transactionId,
|
||||
equipment: Equipment(
|
||||
id: response.equipmentId,
|
||||
manufacturer: 'N/A', // 트랜잭션 응답에는 제조사 정보 없음
|
||||
name: 'N/A', // 트랜잭션 응답에는 모델명 정보 없음
|
||||
category: 'N/A', // 트랜잭션 응답에는 카테고리 정보 없음
|
||||
subCategory: 'N/A', // 트랜잭션 응답에는 카테고리 정보 없음
|
||||
subSubCategory: 'N/A', // 트랜잭션 응답에는 카테고리 정보 없음
|
||||
serialNumber: null,
|
||||
quantity: response.quantity,
|
||||
),
|
||||
outDate: response.transactionDate,
|
||||
status: 'O',
|
||||
company: null,
|
||||
remark: response.message,
|
||||
));
|
||||
}
|
||||
|
||||
return Right(results);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 일괄 출고 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<String>>> getManufacturers() async {
|
||||
try {
|
||||
// TODO: 실제 API 엔드포인트 구현 필요
|
||||
return const Right(['삼성', 'LG', 'Apple', 'Dell', 'HP']);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '제조사 목록 조회 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<String>>> getEquipmentNames() async {
|
||||
try {
|
||||
// TODO: 실제 API 엔드포인트 구현 필요
|
||||
return const Right(['노트북', '모니터', '키보드', '마우스', '프린터']);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비명 목록 조회 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<dynamic>>> getEquipmentHistory(int equipmentId) async {
|
||||
try {
|
||||
final history = await _remoteDataSource.getEquipmentHistory(equipmentId);
|
||||
return Right(history);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 이력 조회 실패: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<Equipment>>> searchEquipment({
|
||||
String? manufacturer,
|
||||
String? name,
|
||||
String? category,
|
||||
String? serialNumber,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _remoteDataSource.getEquipments(
|
||||
search: serialNumber ?? name ?? manufacturer,
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
);
|
||||
|
||||
final equipments = response.items.map((dto) =>
|
||||
Equipment(
|
||||
id: dto.id,
|
||||
manufacturer: dto.manufacturer,
|
||||
name: dto.modelName ?? '',
|
||||
category: 'N/A', // EquipmentListDto에는 category 필드가 없음
|
||||
subCategory: 'N/A', // EquipmentListDto에는 category 필드가 없음
|
||||
subSubCategory: 'N/A', // EquipmentListDto에는 category 필드가 없음
|
||||
serialNumber: dto.serialNumber,
|
||||
quantity: 1,
|
||||
)
|
||||
).toList();
|
||||
|
||||
return Right(equipments);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(message: e.message ?? '서버 오류가 발생했습니다'));
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(message: '장비 검색 실패: $e'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import '../models/license/license_dto.dart';
|
||||
|
||||
/// 라이선스 Repository 인터페이스
|
||||
abstract class LicenseRepository {
|
||||
/// 라이선스 목록 조회
|
||||
Future<LicenseListResponseDto> getLicenses({
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
String? search,
|
||||
Map<String, dynamic>? filters,
|
||||
});
|
||||
|
||||
/// 라이선스 상세 조회
|
||||
Future<LicenseDto> getLicenseDetail(int id);
|
||||
|
||||
/// 라이선스 생성
|
||||
Future<LicenseDto> createLicense(Map<String, dynamic> data);
|
||||
|
||||
/// 라이선스 수정
|
||||
Future<LicenseDto> updateLicense(int id, Map<String, dynamic> data);
|
||||
|
||||
/// 라이선스 삭제
|
||||
Future<void> deleteLicense(int id);
|
||||
}
|
||||
@@ -1,58 +1,323 @@
|
||||
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';
|
||||
import 'license_repository.dart';
|
||||
|
||||
/// 라이선스 Repository 구현체
|
||||
/// 라이선스 및 유지보수 계약 관리 작업을 처리하며 도메인 모델과 API DTO 간 변환을 담당
|
||||
@Injectable(as: LicenseRepository)
|
||||
class LicenseRepositoryImpl implements LicenseRepository {
|
||||
final LicenseRemoteDataSource remoteDataSource;
|
||||
|
||||
LicenseRepositoryImpl(this.remoteDataSource);
|
||||
LicenseRepositoryImpl({required this.remoteDataSource});
|
||||
|
||||
@override
|
||||
Future<LicenseListResponseDto> getLicenses({
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
Future<Either<Failure, PaginatedResponse<License>>> getLicenses({
|
||||
int? page,
|
||||
int? limit,
|
||||
String? search,
|
||||
Map<String, dynamic>? filters,
|
||||
int? companyId,
|
||||
String? equipmentType,
|
||||
String? expiryStatus,
|
||||
String? sortBy,
|
||||
String? sortOrder,
|
||||
}) async {
|
||||
// 검색 및 필터 파라미터를 DataSource 형식에 맞게 변환
|
||||
bool? isActive = filters?['is_active'];
|
||||
int? companyId = filters?['company_id'];
|
||||
int? assignedUserId = filters?['assigned_user_id'];
|
||||
String? licenseType = filters?['license_type'];
|
||||
|
||||
return await remoteDataSource.getLicenses(
|
||||
page: page,
|
||||
perPage: perPage,
|
||||
isActive: isActive,
|
||||
companyId: companyId,
|
||||
assignedUserId: assignedUserId,
|
||||
licenseType: licenseType,
|
||||
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<License>(
|
||||
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<License>(
|
||||
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<Either<Failure, License>> 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<Either<Failure, License>> 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<Either<Failure, License>> 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<Either<Failure, void>> 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<Either<Failure, List<License>>> getExpiringLicenses({int days = 30, int? companyId}) async {
|
||||
// TODO: API에서 만료 예정 라이선스 조회 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '만료 예정 라이선스 조회 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<License>>> getExpiredLicenses({int? companyId}) async {
|
||||
// TODO: API에서 만료된 라이선스 조회 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '만료된 라이선스 조회 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, LicenseExpirySummary>> getLicenseExpirySummary() async {
|
||||
// TODO: API에서 라이선스 만료 요약 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '라이선스 만료 요약 조회 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, License>> renewLicense(int id, DateTime newExpiryDate, {double? renewalCost, String? renewalNote}) async {
|
||||
// TODO: API에서 라이선스 갱신 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '라이선스 갱신 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Map<String, int>>> getLicenseStatsByCompany(int companyId) async {
|
||||
// TODO: API에서 회사별 라이선스 통계 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '회사별 라이선스 통계 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Map<String, int>>> getLicenseCountByType() async {
|
||||
// TODO: API에서 라이선스 유형별 통계 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '라이선스 유형별 통계 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> setExpiryNotification(int licenseId, {int notifyDays = 30}) async {
|
||||
// TODO: API에서 만료 알림 설정 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '만료 알림 설정 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<License>>> 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,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LicenseDto> getLicenseDetail(int id) async {
|
||||
return await remoteDataSource.getLicenseById(id);
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LicenseDto> createLicense(Map<String, dynamic> data) async {
|
||||
final request = CreateLicenseRequest.fromJson(data);
|
||||
return await remoteDataSource.createLicense(request);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LicenseDto> updateLicense(int id, Map<String, dynamic> data) async {
|
||||
final request = UpdateLicenseRequest.fromJson(data);
|
||||
return await remoteDataSource.updateLicense(id, request);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteLicense(int id) async {
|
||||
await remoteDataSource.deleteLicense(id);
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
369
lib/data/repositories/user_repository_impl.dart
Normal file
369
lib/data/repositories/user_repository_impl.dart
Normal file
@@ -0,0 +1,369 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../../core/errors/failures.dart';
|
||||
import '../../domain/repositories/user_repository.dart';
|
||||
import '../../models/user_model.dart';
|
||||
import '../datasources/remote/user_remote_datasource.dart';
|
||||
import '../models/common/paginated_response.dart';
|
||||
import '../models/user/user_dto.dart';
|
||||
|
||||
/// 사용자 관리 Repository 구현체
|
||||
/// 사용자 계정 CRUD 및 권한 관리 작업을 처리하며 도메인 모델과 API DTO 간 변환을 담당
|
||||
@Injectable(as: UserRepository)
|
||||
class UserRepositoryImpl implements UserRepository {
|
||||
final UserRemoteDataSource remoteDataSource;
|
||||
|
||||
UserRepositoryImpl({required this.remoteDataSource});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, PaginatedResponse<User>>> getUsers({
|
||||
int? page,
|
||||
int? limit,
|
||||
String? search,
|
||||
String? role,
|
||||
int? companyId,
|
||||
bool? isActive,
|
||||
String? sortBy,
|
||||
String? sortOrder,
|
||||
}) async {
|
||||
try {
|
||||
final result = await remoteDataSource.getUsers(
|
||||
page: page ?? 1,
|
||||
perPage: limit ?? 20,
|
||||
isActive: isActive,
|
||||
companyId: companyId,
|
||||
role: role,
|
||||
);
|
||||
|
||||
// DTO를 도메인 모델로 변환
|
||||
final users = result.items.map((dto) => _mapDtoToDomain(dto)).toList();
|
||||
|
||||
// 검색 필터링 (서버에서 지원하지 않는 경우 클라이언트 측에서 처리)
|
||||
if (search != null && search.isNotEmpty) {
|
||||
final filteredUsers = users.where((user) {
|
||||
final searchLower = search.toLowerCase();
|
||||
return (user.username?.toLowerCase().contains(searchLower) ?? false) ||
|
||||
user.name.toLowerCase().contains(searchLower) ||
|
||||
(user.email?.toLowerCase().contains(searchLower) ?? false);
|
||||
}).toList();
|
||||
|
||||
final paginatedResult = PaginatedResponse<User>(
|
||||
items: filteredUsers,
|
||||
page: result.page,
|
||||
size: result.size,
|
||||
totalElements: filteredUsers.length,
|
||||
totalPages: (filteredUsers.length / result.size).ceil(),
|
||||
first: result.first,
|
||||
last: result.last,
|
||||
);
|
||||
|
||||
return Right(paginatedResult);
|
||||
}
|
||||
|
||||
final paginatedResult = PaginatedResponse<User>(
|
||||
items: users,
|
||||
page: result.page,
|
||||
size: result.size,
|
||||
totalElements: result.totalElements,
|
||||
totalPages: result.totalPages,
|
||||
first: result.first,
|
||||
last: result.last,
|
||||
);
|
||||
|
||||
return Right(paginatedResult);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '사용자 목록 조회 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, User>> getUserById(int id) async {
|
||||
try {
|
||||
final result = await remoteDataSource.getUser(id);
|
||||
final user = _mapDtoToDomain(result);
|
||||
return Right(user);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '해당 사용자를 찾을 수 없습니다.',
|
||||
resourceType: 'User',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '사용자 상세 정보 조회 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, User>> createUser(User user, String password) async {
|
||||
try {
|
||||
final request = _mapDomainToCreateRequest(user, password);
|
||||
final result = await remoteDataSource.createUser(request);
|
||||
final createdUser = _mapDtoToDomain(result);
|
||||
return Right(createdUser);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('중복')) {
|
||||
return Left(DuplicateFailure(
|
||||
message: '이미 사용 중인 이메일입니다.',
|
||||
field: 'username',
|
||||
value: user.username ?? '',
|
||||
));
|
||||
}
|
||||
if (e.toString().contains('유효성')) {
|
||||
return Left(ValidationFailure(
|
||||
message: '입력 데이터가 올바르지 않습니다.',
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '사용자 생성 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, User>> updateUser(int id, User user) async {
|
||||
try {
|
||||
final request = _mapDomainToUpdateRequest(user);
|
||||
final result = await remoteDataSource.updateUser(id, request);
|
||||
final updatedUser = _mapDtoToDomain(result);
|
||||
return Right(updatedUser);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '수정할 사용자를 찾을 수 없습니다.',
|
||||
resourceType: 'User',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
if (e.toString().contains('중복')) {
|
||||
return Left(DuplicateFailure(
|
||||
message: '이미 사용 중인 이메일입니다.',
|
||||
field: 'username',
|
||||
value: user.username ?? '',
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '사용자 정보 수정 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteUser(int id) async {
|
||||
try {
|
||||
await remoteDataSource.deleteUser(id);
|
||||
return const Right(null);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '삭제할 사용자를 찾을 수 없습니다.',
|
||||
resourceType: 'User',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
if (e.toString().contains('참조')) {
|
||||
return Left(BusinessFailure(
|
||||
message: '해당 사용자에 연결된 데이터가 있어 삭제할 수 없습니다.',
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '사용자 삭제 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, User>> toggleUserStatus(int id) async {
|
||||
try {
|
||||
// 현재 사용자 정보 조회
|
||||
final currentUser = await remoteDataSource.getUser(id);
|
||||
final newStatus = !currentUser.isActive;
|
||||
|
||||
// 상태 업데이트
|
||||
final request = ChangeStatusRequest(isActive: newStatus);
|
||||
final updatedUser = await remoteDataSource.changeUserStatus(id, request);
|
||||
final user = _mapDtoToDomain(updatedUser);
|
||||
|
||||
return Right(user);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '상태를 변경할 사용자를 찾을 수 없습니다.',
|
||||
resourceType: 'User',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '사용자 상태 변경 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> resetPassword(int id, String newPassword) async {
|
||||
try {
|
||||
// resetPassword 메서드가 데이터소스에 없으므로 changePassword 사용
|
||||
final request = ChangePasswordRequest(currentPassword: '', newPassword: newPassword);
|
||||
await remoteDataSource.changePassword(id, request);
|
||||
return const Right(null);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '비밀번호를 재설정할 사용자를 찾을 수 없습니다.',
|
||||
resourceType: 'User',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '비밀번호 재설정 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, User>> changeUserRole(int id, String newRole) async {
|
||||
try {
|
||||
// changeUserRole 메서드가 데이터소스에 없으므로 updateUser 사용
|
||||
final request = UpdateUserRequest(role: newRole);
|
||||
final updatedUser = await remoteDataSource.updateUser(id, request);
|
||||
final user = _mapDtoToDomain(updatedUser);
|
||||
|
||||
return Right(user);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '역할을 변경할 사용자를 찾을 수 없습니다.',
|
||||
resourceType: 'User',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '사용자 역할 변경 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> isDuplicateUsername(String username, {int? excludeId}) async {
|
||||
try {
|
||||
final isDuplicate = await remoteDataSource.checkDuplicateUsername(username);
|
||||
// excludeId가 있는 경우 해당 ID 제외 로직 추가 필요
|
||||
return Right(isDuplicate);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '중복 사용자명 확인 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<User>>> getUsersByCompany(int companyId, {bool includeInactive = false}) async {
|
||||
try {
|
||||
// getUsersByCompany 메서드가 없으므로 getUsers로 대체
|
||||
final result = await remoteDataSource.getUsers(
|
||||
companyId: companyId,
|
||||
isActive: includeInactive ? null : true,
|
||||
);
|
||||
final users = result.users.map((dto) => _mapDtoToDomain(dto)).toList();
|
||||
return Right(users);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '회사별 사용자 조회 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Map<String, int>>> getUserCountByRole() async {
|
||||
// TODO: API에서 역할별 사용자 수 통계 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '역할별 사용자 수 통계 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<User>>> searchUsers(String query, {int? companyId, int? limit}) async {
|
||||
try {
|
||||
final result = await remoteDataSource.searchUsers(
|
||||
query: query,
|
||||
companyId: companyId,
|
||||
perPage: limit ?? 10,
|
||||
);
|
||||
final users = result.users.map((dto) => _mapDtoToDomain(dto)).toList();
|
||||
return Right(users);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '사용자 검색 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> updateLastLoginTime(int id) async {
|
||||
try {
|
||||
// updateLastLoginTime 메서드가 데이터소스에 없으므로 비어있는 구현
|
||||
// TODO: API에서 지원되면 구현
|
||||
throw UnimplementedError('마지막 로그인 시간 업데이트 기능이 아직 구현되지 않았습니다.');
|
||||
return const Right(null);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '마지막 로그인 시간 업데이트 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Private 매퍼 메서드들
|
||||
|
||||
User _mapDtoToDomain(UserDto dto) {
|
||||
return User(
|
||||
id: dto.id,
|
||||
companyId: dto.companyId ?? 0,
|
||||
branchId: dto.branchId,
|
||||
name: dto.name,
|
||||
role: dto.role,
|
||||
email: dto.email,
|
||||
phoneNumbers: dto.phone != null ? [{'type': 'primary', 'number': dto.phone!}] : [],
|
||||
username: dto.username,
|
||||
isActive: dto.isActive,
|
||||
createdAt: dto.createdAt,
|
||||
updatedAt: dto.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
// _mapDetailDtoToDomain 함수는 더 이상 사용하지 않음 - _mapDtoToDomain 사용
|
||||
|
||||
// _mapResponseToDomain 함수는 더 이상 사용하지 않음 - _mapDtoToDomain 사용
|
||||
|
||||
// UserRole enum은 더 이상 필요하지 않음 - String role을 직접 사용
|
||||
|
||||
CreateUserRequest _mapDomainToCreateRequest(User user, String password) {
|
||||
return CreateUserRequest(
|
||||
username: user.username ?? user.email ?? '',
|
||||
password: password,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
phone: user.phoneNumbers.isNotEmpty ? user.phoneNumbers.first['number'] : null,
|
||||
role: user.role,
|
||||
companyId: user.companyId,
|
||||
branchId: user.branchId,
|
||||
);
|
||||
}
|
||||
|
||||
UpdateUserRequest _mapDomainToUpdateRequest(User user) {
|
||||
return UpdateUserRequest(
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
phone: user.phoneNumbers.isNotEmpty ? user.phoneNumbers.first['number'] : null,
|
||||
role: user.role,
|
||||
companyId: user.companyId,
|
||||
branchId: user.branchId,
|
||||
isActive: user.isActive,
|
||||
);
|
||||
}
|
||||
|
||||
// _mapRoleToString 함수는 더 이상 필요하지 않음 - role을 직접 String으로 사용
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import '../models/warehouse/warehouse_dto.dart';
|
||||
|
||||
/// 창고 위치 Repository 인터페이스
|
||||
abstract class WarehouseLocationRepository {
|
||||
/// 창고 위치 목록 조회
|
||||
Future<WarehouseLocationListDto> getWarehouseLocations({
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
String? search,
|
||||
Map<String, dynamic>? filters,
|
||||
});
|
||||
|
||||
/// 창고 위치 상세 조회
|
||||
Future<WarehouseLocationDto> getWarehouseLocationDetail(int id);
|
||||
|
||||
/// 창고 위치 생성
|
||||
Future<WarehouseLocationDto> createWarehouseLocation(Map<String, dynamic> data);
|
||||
|
||||
/// 창고 위치 수정
|
||||
Future<WarehouseLocationDto> updateWarehouseLocation(int id, Map<String, dynamic> data);
|
||||
|
||||
/// 창고 위치 삭제
|
||||
Future<void> deleteWarehouseLocation(int id);
|
||||
|
||||
/// 창고에 장비가 있는지 확인
|
||||
Future<bool> checkWarehouseHasEquipment(int id);
|
||||
}
|
||||
@@ -1,56 +1,362 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../../core/errors/failures.dart';
|
||||
import '../../domain/repositories/warehouse_location_repository.dart';
|
||||
import '../../models/warehouse_location_model.dart';
|
||||
import '../../models/address_model.dart';
|
||||
import '../datasources/remote/warehouse_location_remote_datasource.dart';
|
||||
import '../models/common/paginated_response.dart';
|
||||
import '../models/warehouse/warehouse_dto.dart';
|
||||
import 'warehouse_location_repository.dart';
|
||||
|
||||
/// 창고 위치 Repository 구현체
|
||||
/// 창고 위치 및 장비 입고지 관리 작업을 처리하며 도메인 모델과 API DTO 간 변환을 담당
|
||||
@Injectable(as: WarehouseLocationRepository)
|
||||
class WarehouseLocationRepositoryImpl implements WarehouseLocationRepository {
|
||||
final WarehouseLocationRemoteDataSource remoteDataSource;
|
||||
|
||||
WarehouseLocationRepositoryImpl(this.remoteDataSource);
|
||||
WarehouseLocationRepositoryImpl({required this.remoteDataSource});
|
||||
|
||||
@override
|
||||
Future<WarehouseLocationListDto> getWarehouseLocations({
|
||||
int page = 1,
|
||||
int perPage = 20,
|
||||
Future<Either<Failure, PaginatedResponse<WarehouseLocation>>> getWarehouseLocations({
|
||||
int? page,
|
||||
int? limit,
|
||||
String? search,
|
||||
Map<String, dynamic>? filters,
|
||||
String? locationType,
|
||||
bool? isActive,
|
||||
bool? hasEquipment,
|
||||
String? sortBy,
|
||||
String? sortOrder,
|
||||
}) async {
|
||||
return await remoteDataSource.getWarehouseLocations(
|
||||
page: page,
|
||||
perPage: perPage,
|
||||
search: search,
|
||||
filters: filters,
|
||||
try {
|
||||
final result = await remoteDataSource.getWarehouseLocations(
|
||||
page: page ?? 1,
|
||||
perPage: limit ?? 20,
|
||||
search: search,
|
||||
filters: {
|
||||
if (locationType != null) 'location_type': locationType,
|
||||
if (isActive != null) 'is_active': isActive,
|
||||
if (hasEquipment != null) 'has_equipment': hasEquipment,
|
||||
},
|
||||
);
|
||||
|
||||
// DTO를 도메인 모델로 변환
|
||||
final warehouseLocations = result.items.map((dto) => _mapDtoToDomain(dto)).toList();
|
||||
|
||||
final paginatedResult = PaginatedResponse<WarehouseLocation>(
|
||||
items: warehouseLocations,
|
||||
page: result.page,
|
||||
size: result.perPage,
|
||||
totalElements: result.total,
|
||||
totalPages: result.totalPages,
|
||||
first: result.page == 1,
|
||||
last: result.page == result.totalPages,
|
||||
);
|
||||
|
||||
return Right(paginatedResult);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '창고 위치 목록 조회 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, WarehouseLocation>> getWarehouseLocationById(int id) async {
|
||||
try {
|
||||
final result = await remoteDataSource.getWarehouseLocationDetail(id);
|
||||
final warehouseLocation = _mapDetailDtoToDomain(result);
|
||||
return Right(warehouseLocation);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '해당 창고 위치를 찾을 수 없습니다.',
|
||||
resourceType: 'WarehouseLocation',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '창고 위치 상세 정보 조회 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, WarehouseLocation>> createWarehouseLocation(WarehouseLocation warehouseLocation) async {
|
||||
try {
|
||||
final request = _mapDomainToCreateRequest(warehouseLocation);
|
||||
final result = await remoteDataSource.createWarehouseLocation(request);
|
||||
final createdWarehouseLocation = _mapDetailDtoToDomain(result);
|
||||
return Right(createdWarehouseLocation);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('중복')) {
|
||||
return Left(DuplicateFailure(
|
||||
message: '이미 존재하는 창고명입니다.',
|
||||
field: 'name',
|
||||
value: warehouseLocation.name,
|
||||
));
|
||||
}
|
||||
if (e.toString().contains('유효성')) {
|
||||
return Left(ValidationFailure(
|
||||
message: '입력 데이터가 올바르지 않습니다.',
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '창고 위치 생성 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, WarehouseLocation>> updateWarehouseLocation(int id, WarehouseLocation warehouseLocation) async {
|
||||
try {
|
||||
final request = _mapDomainToUpdateRequest(warehouseLocation);
|
||||
final result = await remoteDataSource.updateWarehouseLocation(id, request);
|
||||
final updatedWarehouseLocation = _mapDetailDtoToDomain(result);
|
||||
return Right(updatedWarehouseLocation);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '수정할 창고 위치를 찾을 수 없습니다.',
|
||||
resourceType: 'WarehouseLocation',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
if (e.toString().contains('중복')) {
|
||||
return Left(DuplicateFailure(
|
||||
message: '이미 존재하는 창고명입니다.',
|
||||
field: 'name',
|
||||
value: warehouseLocation.name,
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '창고 위치 정보 수정 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteWarehouseLocation(int id) async {
|
||||
try {
|
||||
await remoteDataSource.deleteWarehouseLocation(id);
|
||||
return const Right(null);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '삭제할 창고 위치를 찾을 수 없습니다.',
|
||||
resourceType: 'WarehouseLocation',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
if (e.toString().contains('참조')) {
|
||||
return Left(BusinessFailure(
|
||||
message: '해당 창고에 보관 중인 장비가 있어 삭제할 수 없습니다.',
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '창고 위치 삭제 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, WarehouseLocation>> toggleWarehouseLocationStatus(int id) async {
|
||||
try {
|
||||
// 현재 창고 위치 정보 조회
|
||||
final currentWarehouse = await remoteDataSource.getWarehouseLocationDetail(id);
|
||||
final newStatus = !currentWarehouse.isActive;
|
||||
|
||||
// 상태 업데이트
|
||||
await remoteDataSource.updateWarehouseLocationStatus(id, newStatus);
|
||||
|
||||
// 업데이트된 창고 위치 정보 재조회
|
||||
final updatedWarehouse = await remoteDataSource.getWarehouseLocationDetail(id);
|
||||
final warehouseLocation = _mapDetailDtoToDomain(updatedWarehouse);
|
||||
|
||||
return Right(warehouseLocation);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('404')) {
|
||||
return Left(NotFoundFailure(
|
||||
message: '상태를 변경할 창고 위치를 찾을 수 없습니다.',
|
||||
resourceType: 'WarehouseLocation',
|
||||
resourceId: id.toString(),
|
||||
));
|
||||
}
|
||||
return Left(ServerFailure(
|
||||
message: '창고 위치 상태 변경 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> hasEquipment(int id) async {
|
||||
try {
|
||||
final hasEquipment = await remoteDataSource.checkWarehouseHasEquipment(id);
|
||||
return Right(hasEquipment);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '창고 장비 보유 여부 확인 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, int>> getEquipmentCount(int id) async {
|
||||
// TODO: API에서 창고별 장비 수량 조회 기능이 구현되면 추가
|
||||
try {
|
||||
// 임시로 0 반환 - API 구현 후 수정 필요
|
||||
return const Right(0);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '창고 장비 수량 조회 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, PaginatedResponse<dynamic>>> getEquipmentByWarehouse(
|
||||
int warehouseId, {
|
||||
int? page,
|
||||
int? limit,
|
||||
}) async {
|
||||
// TODO: API에서 창고별 장비 목록 조회 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '창고별 장비 목록 조회 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Map<int, double>>> getWarehouseUtilization() async {
|
||||
// TODO: API에서 창고 사용률 통계 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '창고 사용률 통계 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Map<String, int>>> getWarehouseCountByType() async {
|
||||
// TODO: API에서 창고 유형별 통계 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '창고 유형별 통계 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, bool>> isDuplicateWarehouseName(String name, {int? excludeId}) async {
|
||||
try {
|
||||
final isDuplicate = await remoteDataSource.checkDuplicateWarehouseName(name);
|
||||
// excludeId가 있는 경우 해당 ID 제외 로직 추가 필요
|
||||
return Right(isDuplicate);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '중복 창고명 확인 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<WarehouseLocation>>> searchWarehouseLocations(String query, {int? limit}) async {
|
||||
try {
|
||||
final result = await remoteDataSource.getWarehouseLocations(
|
||||
page: 1,
|
||||
perPage: limit ?? 10,
|
||||
search: query,
|
||||
);
|
||||
|
||||
final warehouseLocations = result.items.map((dto) => _mapDtoToDomain(dto)).toList();
|
||||
return Right(warehouseLocations);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '창고 위치 검색 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<WarehouseLocation>>> getActiveWarehouseLocations() async {
|
||||
try {
|
||||
final result = await remoteDataSource.getWarehouseLocations(
|
||||
page: 1,
|
||||
perPage: 100, // 활성 창고 모두 조회
|
||||
filters: {'is_active': true},
|
||||
);
|
||||
|
||||
final activeWarehouses = result.items.map((dto) => _mapDtoToDomain(dto)).toList();
|
||||
return Right(activeWarehouses);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(
|
||||
message: '활성 창고 위치 조회 중 오류가 발생했습니다: ${e.toString()}',
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, WarehouseLocation>> updateWarehouseCapacity(
|
||||
int id,
|
||||
int totalCapacity,
|
||||
int usedCapacity,
|
||||
) async {
|
||||
// TODO: API에서 창고 용량 업데이트 기능이 구현되면 추가
|
||||
return const Left(ServerFailure(
|
||||
message: '창고 용량 업데이트 기능이 아직 구현되지 않았습니다.',
|
||||
));
|
||||
}
|
||||
|
||||
// Private 매퍼 메서드들
|
||||
|
||||
WarehouseLocation _mapDtoToDomain(WarehouseLocationDto dto) {
|
||||
return WarehouseLocation(
|
||||
id: dto.id,
|
||||
name: dto.name,
|
||||
// String? address를 Address 객체로 변환
|
||||
address: dto.address != null && dto.address!.isNotEmpty
|
||||
? Address.fromFullAddress(dto.address!)
|
||||
: const Address(),
|
||||
// DTO에 없는 필드는 remark로 통합 (WarehouseLocation 모델의 실제 필드)
|
||||
remark: null, // DTO에는 description이나 remark 필드가 없음
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<WarehouseLocationDto> getWarehouseLocationDetail(int id) async {
|
||||
return await remoteDataSource.getWarehouseLocationDetail(id);
|
||||
|
||||
WarehouseLocation _mapDetailDtoToDomain(WarehouseLocationDto dto) {
|
||||
return WarehouseLocation(
|
||||
id: dto.id,
|
||||
name: dto.name,
|
||||
// String? address를 Address 객체로 변환
|
||||
address: dto.address != null && dto.address!.isNotEmpty
|
||||
? Address.fromFullAddress(dto.address!)
|
||||
: const Address(),
|
||||
// DTO에 없는 필드는 remark로 통합 (WarehouseLocation 모델의 실제 필드)
|
||||
remark: null, // DTO에는 description이나 remark 필드가 없음
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<WarehouseLocationDto> createWarehouseLocation(Map<String, dynamic> data) async {
|
||||
final request = CreateWarehouseLocationRequest.fromJson(data);
|
||||
return await remoteDataSource.createWarehouseLocation(request);
|
||||
|
||||
// WarehouseLocationType enum이 WarehouseLocation 모델에 없으므로 제거
|
||||
// 필요시 나중에 모델 업데이트 후 재추가
|
||||
|
||||
CreateWarehouseLocationRequest _mapDomainToCreateRequest(WarehouseLocation warehouseLocation) {
|
||||
return CreateWarehouseLocationRequest(
|
||||
name: warehouseLocation.name,
|
||||
// Address 객체를 String으로 변환
|
||||
address: warehouseLocation.address.toString(),
|
||||
// DTO 요청에 없는 필드들은 제거하고 DTO에 있는 필드만 매핑
|
||||
// capacity는 DTO에 있지만 모델에 없으므로 기본값 사용
|
||||
capacity: 0,
|
||||
// 나머지 필드들도 DTO 구조에 맞게 조정
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<WarehouseLocationDto> updateWarehouseLocation(int id, Map<String, dynamic> data) async {
|
||||
final request = UpdateWarehouseLocationRequest.fromJson(data);
|
||||
return await remoteDataSource.updateWarehouseLocation(id, request);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteWarehouseLocation(int id) async {
|
||||
await remoteDataSource.deleteWarehouseLocation(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> checkWarehouseHasEquipment(int id) async {
|
||||
// TODO: API 엔드포인트 구현 필요
|
||||
// 현재는 항상 false 반환
|
||||
return false;
|
||||
|
||||
UpdateWarehouseLocationRequest _mapDomainToUpdateRequest(WarehouseLocation warehouseLocation) {
|
||||
return UpdateWarehouseLocationRequest(
|
||||
name: warehouseLocation.name,
|
||||
// Address 객체를 String으로 변환
|
||||
address: warehouseLocation.address.toString(),
|
||||
// DTO 요청에 없는 필드들은 제거하고 DTO에 있는 필드만 매핑
|
||||
// capacity는 DTO에 있지만 모델에 없으므로 기본값 사용
|
||||
capacity: 0,
|
||||
// isActive는 DTO에 있지만 모델에 없으므로 기본값 true 사용
|
||||
isActive: true,
|
||||
);
|
||||
}
|
||||
|
||||
// WarehouseLocationType enum이 WarehouseLocation 모델에 없으므로 제거
|
||||
// 필요시 나중에 모델 업데이트 후 재추가
|
||||
}
|
||||
Reference in New Issue
Block a user