결재 API 계약 보완 및 테스트 정리
This commit is contained in:
204
lib/features/dashboard/data/dtos/dashboard_summary_dto.dart
Normal file
204
lib/features/dashboard/data/dtos/dashboard_summary_dto.dart
Normal file
@@ -0,0 +1,204 @@
|
||||
import '../../../../core/common/utils/json_utils.dart';
|
||||
import '../../domain/entities/dashboard_kpi.dart';
|
||||
import '../../domain/entities/dashboard_pending_approval.dart';
|
||||
import '../../domain/entities/dashboard_summary.dart';
|
||||
import '../../domain/entities/dashboard_transaction_summary.dart';
|
||||
|
||||
/// 대시보드 요약 응답 DTO.
|
||||
class DashboardSummaryDto {
|
||||
const DashboardSummaryDto({
|
||||
this.generatedAt,
|
||||
this.kpis = const [],
|
||||
this.recentTransactions = const [],
|
||||
this.pendingApprovals = const [],
|
||||
});
|
||||
|
||||
final DateTime? generatedAt;
|
||||
final List<DashboardKpiDto> kpis;
|
||||
final List<DashboardTransactionDto> recentTransactions;
|
||||
final List<DashboardApprovalDto> pendingApprovals;
|
||||
|
||||
factory DashboardSummaryDto.fromJson(Map<String, dynamic> json) {
|
||||
final generatedAt = _parseDate(json['generated_at']);
|
||||
final kpiList = JsonUtils.extractList(json, keys: const ['kpis'])
|
||||
.map(DashboardKpiDto.fromJson)
|
||||
.toList(growable: false);
|
||||
final transactionList =
|
||||
JsonUtils.extractList(json, keys: const ['recent_transactions'])
|
||||
.map(DashboardTransactionDto.fromJson)
|
||||
.toList(growable: false);
|
||||
final approvalList =
|
||||
JsonUtils.extractList(json, keys: const ['pending_approvals'])
|
||||
.map(DashboardApprovalDto.fromJson)
|
||||
.toList(growable: false);
|
||||
|
||||
return DashboardSummaryDto(
|
||||
generatedAt: generatedAt,
|
||||
kpis: kpiList,
|
||||
recentTransactions: transactionList,
|
||||
pendingApprovals: approvalList,
|
||||
);
|
||||
}
|
||||
|
||||
DashboardSummary toEntity() {
|
||||
return DashboardSummary(
|
||||
generatedAt: generatedAt,
|
||||
kpis: kpis.map((dto) => dto.toEntity()).toList(growable: false),
|
||||
recentTransactions: recentTransactions
|
||||
.map((dto) => dto.toEntity())
|
||||
.toList(growable: false),
|
||||
pendingApprovals: pendingApprovals
|
||||
.map((dto) => dto.toEntity())
|
||||
.toList(growable: false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardKpiDto {
|
||||
const DashboardKpiDto({
|
||||
required this.key,
|
||||
required this.label,
|
||||
required this.value,
|
||||
this.trendLabel,
|
||||
this.delta,
|
||||
});
|
||||
|
||||
final String key;
|
||||
final String label;
|
||||
final num value;
|
||||
final String? trendLabel;
|
||||
final double? delta;
|
||||
|
||||
factory DashboardKpiDto.fromJson(Map<String, dynamic> json) {
|
||||
final key = _readString(json, 'key') ?? '';
|
||||
final label = _readString(json, 'label') ?? key;
|
||||
final value = _readNum(json, 'value');
|
||||
final trendLabel = _readString(json, 'trend_label');
|
||||
final delta = _readDouble(json, 'delta');
|
||||
return DashboardKpiDto(
|
||||
key: key,
|
||||
label: label,
|
||||
value: value ?? 0,
|
||||
trendLabel: trendLabel,
|
||||
delta: delta,
|
||||
);
|
||||
}
|
||||
|
||||
DashboardKpi toEntity() {
|
||||
return DashboardKpi(
|
||||
key: key,
|
||||
label: label,
|
||||
value: value,
|
||||
trendLabel: trendLabel,
|
||||
delta: delta,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardTransactionDto {
|
||||
const DashboardTransactionDto({
|
||||
required this.transactionNo,
|
||||
required this.transactionDate,
|
||||
required this.transactionType,
|
||||
required this.statusName,
|
||||
required this.createdBy,
|
||||
});
|
||||
|
||||
final String transactionNo;
|
||||
final String transactionDate;
|
||||
final String transactionType;
|
||||
final String statusName;
|
||||
final String createdBy;
|
||||
|
||||
factory DashboardTransactionDto.fromJson(Map<String, dynamic> json) {
|
||||
return DashboardTransactionDto(
|
||||
transactionNo: _readString(json, 'transaction_no') ?? '',
|
||||
transactionDate: _readString(json, 'transaction_date') ?? '',
|
||||
transactionType: _readString(json, 'transaction_type') ?? '',
|
||||
statusName: _readString(json, 'status_name') ?? '',
|
||||
createdBy: _readString(json, 'created_by') ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
DashboardTransactionSummary toEntity() {
|
||||
return DashboardTransactionSummary(
|
||||
transactionNo: transactionNo,
|
||||
transactionDate: transactionDate,
|
||||
transactionType: transactionType,
|
||||
statusName: statusName,
|
||||
createdBy: createdBy,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardApprovalDto {
|
||||
const DashboardApprovalDto({
|
||||
required this.approvalNo,
|
||||
required this.title,
|
||||
required this.stepSummary,
|
||||
this.requestedAt,
|
||||
});
|
||||
|
||||
final String approvalNo;
|
||||
final String title;
|
||||
final String stepSummary;
|
||||
final String? requestedAt;
|
||||
|
||||
factory DashboardApprovalDto.fromJson(Map<String, dynamic> json) {
|
||||
return DashboardApprovalDto(
|
||||
approvalNo: _readString(json, 'approval_no') ?? '',
|
||||
title: _readString(json, 'title') ?? '',
|
||||
stepSummary: _readString(json, 'step_summary') ?? '',
|
||||
requestedAt: _readString(json, 'requested_at'),
|
||||
);
|
||||
}
|
||||
|
||||
DashboardPendingApproval toEntity() {
|
||||
return DashboardPendingApproval(
|
||||
approvalNo: approvalNo,
|
||||
title: title,
|
||||
stepSummary: stepSummary,
|
||||
requestedAt: requestedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DateTime? _parseDate(Object? value) {
|
||||
if (value is DateTime) {
|
||||
return value;
|
||||
}
|
||||
if (value is String) {
|
||||
return DateTime.tryParse(value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? _readString(Map<String, dynamic>? source, String key) {
|
||||
if (source == null) return null;
|
||||
final value = source[key];
|
||||
if (value is String) {
|
||||
final trimmed = value.trim();
|
||||
return trimmed.isEmpty ? null : trimmed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
num? _readNum(Map<String, dynamic>? source, String key) {
|
||||
if (source == null) return null;
|
||||
final value = source[key];
|
||||
if (value is num) {
|
||||
return value;
|
||||
}
|
||||
if (value is String) {
|
||||
return num.tryParse(value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
double? _readDouble(Map<String, dynamic>? source, String key) {
|
||||
final value = _readNum(source, key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return value.toDouble();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../../../core/network/api_client.dart';
|
||||
import '../../../../core/network/api_routes.dart';
|
||||
import '../../domain/entities/dashboard_summary.dart';
|
||||
import '../../domain/repositories/dashboard_repository.dart';
|
||||
import '../dtos/dashboard_summary_dto.dart';
|
||||
|
||||
/// 대시보드 요약 데이터를 불러오는 원격 저장소.
|
||||
class DashboardRepositoryRemote implements DashboardRepository {
|
||||
DashboardRepositoryRemote({required ApiClient apiClient}) : _api = apiClient;
|
||||
|
||||
final ApiClient _api;
|
||||
|
||||
static const _summaryPath = '${ApiRoutes.apiV1}/dashboard/summary';
|
||||
|
||||
@override
|
||||
Future<DashboardSummary> fetchSummary() async {
|
||||
final response = await _api.get<Map<String, dynamic>>(
|
||||
_summaryPath,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
final json = (response.data?['data'] as Map<String, dynamic>?) ??
|
||||
response.data ??
|
||||
const <String, dynamic>{};
|
||||
return DashboardSummaryDto.fromJson(json).toEntity();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user