import 'package:injectable/injectable.dart'; import 'package:superport/data/models/zipcode_dto.dart'; import 'package:superport/data/repositories/zipcode_repository.dart'; abstract class ZipcodeUseCase { /// 우편번호 검색 (페이지네이션 지원) Future searchZipcodes({ int page = 1, int limit = 20, String? search, String? sido, String? gu, }); /// 우편번호로 정확한 주소 조회 Future getZipcodeByNumber(int zipcode); /// 시도별 구 목록 조회 Future> getGuListBySido(String sido); /// 전체 시도 목록 조회 Future> getAllSidoList(); /// 주소 문자열로 우편번호 검색 (최적화된 검색) Future> searchByAddress(String address); /// 우편번호 유효성 검사 bool validateZipcode(int zipcode); /// 검색어 유효성 검사 및 정규화 String normalizeSearchQuery(String query); } @Injectable(as: ZipcodeUseCase) class ZipcodeUseCaseImpl implements ZipcodeUseCase { final ZipcodeRepository _repository; ZipcodeUseCaseImpl(this._repository); @override Future searchZipcodes({ int page = 1, int limit = 20, String? search, String? sido, String? gu, }) async { // 비즈니스 로직: 페이지네이션 유효성 검사 if (page < 1) page = 1; if (limit < 1 || limit > 100) limit = 20; // 검색어 정규화 final normalizedSearch = search != null && search.isNotEmpty ? normalizeSearchQuery(search) : null; return await _repository.search( page: page, limit: limit, search: normalizedSearch, sido: sido?.trim(), gu: gu?.trim(), ); } @override Future getZipcodeByNumber(int zipcode) async { // 우편번호 유효성 검사 if (!validateZipcode(zipcode)) { throw ArgumentError('유효하지 않은 우편번호입니다. (5자리 숫자)'); } return await _repository.getByZipcode(zipcode); } @override Future> getGuListBySido(String sido) async { if (sido.trim().isEmpty) { throw ArgumentError('시도명을 입력해주세요.'); } final normalizedSido = sido.trim(); return await _repository.getGuBySido(normalizedSido); } @override Future> getAllSidoList() async { return await _repository.getAllSido(); } @override Future> searchByAddress(String address) async { if (address.trim().isEmpty) { return []; } final normalizedAddress = normalizeSearchQuery(address); try { // 먼저 전체 검색으로 시도 final response = await _repository.search( search: normalizedAddress, limit: 10, // 상위 10개만 가져오기 ); return response.items; } catch (e) { // 검색 실패 시 빈 목록 반환 return []; } } @override bool validateZipcode(int zipcode) { // 한국 우편번호는 5자리 숫자 (00000 ~ 99999) return zipcode >= 0 && zipcode <= 99999; } @override String normalizeSearchQuery(String query) { if (query.trim().isEmpty) return ''; String normalized = query.trim(); // 공백 정규화 (여러 공백을 하나로) normalized = normalized.replaceAll(RegExp(r'\s+'), ' '); // 특수문자 제거 (단, 한글, 영문, 숫자, 공백, 하이픈만 유지) normalized = normalized.replaceAll(RegExp(r'[^\w\s가-힣ㄱ-ㅎㅏ-ㅣ-]'), ''); return normalized; } }