고객사 목록 쿼리스트링 연동 및 공통 JSON 파서 도입

This commit is contained in:
JiWoong Sul
2025-09-25 20:13:46 +09:00
parent 8a6ad1e81b
commit 900990c46b
27 changed files with 1458 additions and 176 deletions

View File

@@ -0,0 +1,68 @@
import 'package:superport_v2/core/common/utils/json_utils.dart';
import '../../domain/entities/postal_code.dart';
/// 우편번호 검색 API 응답을 표현하는 DTO.
class PostalCodeDto {
PostalCodeDto({
required this.zipcode,
this.sido,
this.sigungu,
this.roadName,
this.buildingMainNo,
this.buildingSubNo,
});
final String zipcode;
final String? sido;
final String? sigungu;
final String? roadName;
final int? buildingMainNo;
final int? buildingSubNo;
factory PostalCodeDto.fromJson(Map<String, dynamic> json) {
return PostalCodeDto(
zipcode: json['zipcode'] as String,
sido: json['sido'] as String?,
sigungu: json['sigungu'] as String?,
roadName: json['road_name'] as String?,
buildingMainNo: _parseInt(json['building_main_no']),
buildingSubNo: _parseInt(json['building_sub_no']),
);
}
PostalCode toEntity() {
return PostalCode(
zipcode: zipcode,
sido: sido,
sigungu: sigungu,
roadName: roadName,
buildingMainNo: buildingMainNo,
buildingSubNo: buildingSubNo,
);
}
static List<PostalCode> fromResponse(dynamic data) {
final items = JsonUtils.extractList(data, keys: const ['items', 'data']);
if (items.isEmpty) {
return const [];
}
return items
.map(PostalCodeDto.fromJson)
.map((dto) => dto.toEntity())
.toList(growable: false);
}
}
int? _parseInt(Object? value) {
if (value == null) {
return null;
}
if (value is int) {
return value;
}
if (value is String) {
return int.tryParse(value);
}
return null;
}

View File

@@ -0,0 +1,41 @@
import 'package:dio/dio.dart';
import 'package:superport_v2/core/network/api_client.dart';
import '../../domain/entities/postal_code.dart';
import '../../domain/repositories/postal_search_repository.dart';
import '../dtos/postal_code_dto.dart';
/// 우편번호 검색 API를 호출하는 원격 저장소 구현체.
class PostalSearchRepositoryRemote implements PostalSearchRepository {
PostalSearchRepositoryRemote({required ApiClient apiClient})
: _api = apiClient;
final ApiClient _api;
static const _path = '/zipcodes';
@override
Future<List<PostalCode>> search({
required String keyword,
int limit = 20,
}) async {
final trimmed = keyword.trim();
if (trimmed.isEmpty) {
return const [];
}
final response = await _api.get<dynamic>(
_path,
query: {
'zipcode': trimmed,
'road_name': trimmed,
'q': trimmed,
'page_size': limit,
},
options: Options(responseType: ResponseType.json),
);
return PostalCodeDto.fromResponse(response.data);
}
}