import 'package:dartz/dartz.dart'; import 'package:injectable/injectable.dart'; import 'package:superport/core/errors/failures.dart'; import 'package:superport/data/models/administrator_dto.dart'; import 'package:superport/domain/repositories/administrator_repository.dart'; import 'package:superport/utils/constants.dart'; /// 관리자 UseCase 인터페이스 (비즈니스 로직) abstract class AdministratorUseCase { Future> getAdministrators({ int page = 1, int pageSize = PaginationConstants.defaultPageSize, String? search, }); Future> getAdministratorById(int id); Future> createAdministrator( AdministratorRequestDto administrator, ); Future> updateAdministrator( int id, AdministratorUpdateRequestDto administrator, ); Future> deleteAdministrator(int id); Future> checkEmailDuplicate(String email, {int? excludeId}); Future> authenticateAdministrator( String email, String password, ); Future> validateAdministratorData( String name, String email, String phone, String mobile, ); } /// 관리자 UseCase 구현체 @Injectable(as: AdministratorUseCase) class AdministratorUseCaseImpl implements AdministratorUseCase { final AdministratorRepository _repository; AdministratorUseCaseImpl(this._repository); @override Future> getAdministrators({ int page = 1, int pageSize = PaginationConstants.defaultPageSize, String? search, }) async { // 비즈니스 로직: 페이지네이션 유효성 검사 if (page < 1) page = 1; if (pageSize < 1 || pageSize > 100) pageSize = 20; return await _repository.getAdministrators( page: page, pageSize: pageSize, search: search, ); } @override Future> getAdministratorById(int id) async { // 비즈니스 규칙: ID 유효성 검사 if (id <= 0) { return Left(ValidationFailure( message: '유효하지 않은 관리자 ID입니다.', )); } return await _repository.getAdministratorById(id); } @override Future> createAdministrator( AdministratorRequestDto administrator, ) async { // 비즈니스 규칙 검증 final validationResult = await validateAdministratorData( administrator.name, administrator.email, administrator.phone, administrator.mobile, ); return validationResult.fold( (failure) => Left(failure), (isValid) async { if (!isValid) { return Left(ValidationFailure( message: '관리자 정보가 유효하지 않습니다.', )); } // 이메일 중복 검사 final duplicateResult = await checkEmailDuplicate(administrator.email); return duplicateResult.fold( (failure) => Left(failure), (isDuplicate) async { if (isDuplicate) { return Left(DuplicateFailure( message: '이미 사용 중인 이메일 주소입니다.', )); } return await _repository.createAdministrator(administrator); }, ); }, ); } @override Future> updateAdministrator( int id, AdministratorUpdateRequestDto administrator, ) async { // 비즈니스 규칙: ID 유효성 검사 if (id <= 0) { return Left(ValidationFailure( message: '유효하지 않은 관리자 ID입니다.', )); } // 수정할 데이터가 있는 경우에만 검증 if (administrator.name != null || administrator.email != null || administrator.phone != null || administrator.mobile != null) { // 현재 데이터 조회 (검증용) final currentResult = await _repository.getAdministratorById(id); return currentResult.fold( (failure) => Left(failure), (current) async { // 새로운 값들로 검증 final newName = administrator.name ?? current.name; final newEmail = administrator.email ?? current.email; final newPhone = administrator.phone ?? current.phone; final newMobile = administrator.mobile ?? current.mobile; final validationResult = await validateAdministratorData( newName, newEmail, newPhone, newMobile, ); return validationResult.fold( (failure) => Left(failure), (isValid) async { if (!isValid) { return Left(ValidationFailure( message: '관리자 정보가 유효하지 않습니다.', )); } // 이메일이 변경된 경우 중복 검사 if (administrator.email != null && administrator.email != current.email) { final duplicateResult = await checkEmailDuplicate( administrator.email!, excludeId: id, ); return duplicateResult.fold( (failure) => Left(failure), (isDuplicate) async { if (isDuplicate) { return Left(DuplicateFailure( message: '이미 사용 중인 이메일 주소입니다.', )); } return await _repository.updateAdministrator(id, administrator); }, ); } return await _repository.updateAdministrator(id, administrator); }, ); }, ); } return await _repository.updateAdministrator(id, administrator); } @override Future> deleteAdministrator(int id) async { // 비즈니스 규칙: ID 유효성 검사 if (id <= 0) { return Left(ValidationFailure( message: '유효하지 않은 관리자 ID입니다.', )); } // 비즈니스 로직: 관리자가 최소 1명은 남아있어야 함 final administratorsResult = await _repository.getAdministrators( page: 1, pageSize: 5, ); return administratorsResult.fold( (failure) => Left(failure), (administrators) async { if (administrators.totalCount <= 1) { return Left(ValidationFailure( message: '최소 1명의 관리자가 존재해야 합니다.', )); } return await _repository.deleteAdministrator(id); }, ); } @override Future> checkEmailDuplicate(String email, {int? excludeId}) async { // 이메일 형식 검증 if (!_isValidEmail(email)) { return Left(ValidationFailure( message: '유효하지 않은 이메일 형식입니다.', )); } return await _repository.isDuplicateEmail(email, excludeId: excludeId); } @override Future> authenticateAdministrator( String email, String password, ) async { // 입력값 검증 if (email.trim().isEmpty || password.isEmpty) { return Left(ValidationFailure( message: '이메일과 비밀번호를 입력해주세요.', )); } if (!_isValidEmail(email)) { return Left(ValidationFailure( message: '유효하지 않은 이메일 형식입니다.', )); } return await _repository.authenticateAdministrator(email, password); } @override Future> validateAdministratorData( String name, String email, String phone, String mobile, ) async { // 필수 필드 검증 if (name.trim().isEmpty) { return Left(ValidationFailure( message: '관리자 이름을 입력해주세요.', )); } if (email.trim().isEmpty) { return Left(ValidationFailure( message: '이메일 주소를 입력해주세요.', )); } if (phone.trim().isEmpty) { return Left(ValidationFailure( message: '전화번호를 입력해주세요.', )); } if (mobile.trim().isEmpty) { return Left(ValidationFailure( message: '휴대폰 번호를 입력해주세요.', )); } // 이메일 형식 검증 if (!_isValidEmail(email)) { return Left(ValidationFailure( message: '유효하지 않은 이메일 형식입니다.', )); } // 이름 길이 검증 if (name.length > 100) { return Left(ValidationFailure( message: '관리자 이름은 100자 이내로 입력해주세요.', )); } // 전화번호 형식 검증 (숫자, 하이픈, 공백만 허용) if (!_isValidPhoneNumber(phone)) { return Left(ValidationFailure( message: '유효하지 않은 전화번호 형식입니다.', )); } if (!_isValidPhoneNumber(mobile)) { return Left(ValidationFailure( message: '유효하지 않은 휴대폰 번호 형식입니다.', )); } return const Right(true); } /// 이메일 형식 검증 (간단한 정규식) bool _isValidEmail(String email) { return RegExp(r'^[^\s@]+@[^\s@]+\.[^\s@]+$').hasMatch(email.trim()); } /// 전화번호 형식 검증 (한국 형식 기준) bool _isValidPhoneNumber(String phone) { final cleaned = phone.replaceAll(RegExp(r'[\s\-\(\)]'), ''); return RegExp(r'^\d{8,11}$').hasMatch(cleaned); } }