- V/R 시스템 완전 전환: WARRANTY/CONTRACT/INSPECTION → V(방문)/R(원격) - 유지보수 대시보드 카드 → StandardDataTable 테이블 형태 전환 - "조회중..." 문제 해결: 백엔드 직접 필드 사용 (equipment_model, company_name) - MaintenanceDto 신규 필드 추가: company_id, company_name, equipment_serial, equipment_model - preloadEquipmentData 비활성화로 불필요한 equipment-history API 호출 제거 - CO-STAR 프레임워크 적용 및 CLAUDE.md v3.0 업데이트 - Flutter Analyze ERROR: 0 유지, 100% shadcn_ui 컴플라이언스 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
125 lines
4.1 KiB
Dart
125 lines
4.1 KiB
Dart
import 'package:injectable/injectable.dart';
|
|
import 'package:superport/data/models/maintenance_stats_dto.dart';
|
|
import 'package:superport/data/repositories/maintenance_stats_repository.dart';
|
|
|
|
@lazySingleton
|
|
class GetMaintenanceStatsUseCase {
|
|
final MaintenanceStatsRepository _repository;
|
|
|
|
GetMaintenanceStatsUseCase({
|
|
required MaintenanceStatsRepository repository,
|
|
}) : _repository = repository;
|
|
|
|
/// 유지보수 대시보드 통계 조회
|
|
/// 60일내, 30일내, 7일내, 만료된 계약 통계를 포함한 종합 대시보드 데이터
|
|
Future<MaintenanceStatsDto> getMaintenanceStats() async {
|
|
try {
|
|
final stats = await _repository.getMaintenanceStats();
|
|
|
|
// 비즈니스 검증: 통계 데이터 무결성 확인
|
|
_validateStatsData(stats);
|
|
|
|
return stats;
|
|
} catch (e) {
|
|
// 통계 조회 실패시 기본값 반환 (UX 개선)
|
|
return const MaintenanceStatsDto(
|
|
updatedAt: null, // 실패 상태 표시를 위해 null
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 특정 기간 내 만료 예정 계약 수 조회
|
|
/// [days]: 조회할 기간 (일 단위)
|
|
/// 사용자 요구사항: 60일, 30일, 7일 통계 제공
|
|
Future<int> getExpiringContractsCount({required int days}) async {
|
|
// 입력값 검증
|
|
if (days <= 0) {
|
|
throw ArgumentError('조회 기간은 1일 이상이어야 합니다.');
|
|
}
|
|
|
|
if (days > 365) {
|
|
throw ArgumentError('조회 기간은 365일 이하여야 합니다.');
|
|
}
|
|
|
|
return await _repository.getExpiringContractsCount(days: days);
|
|
}
|
|
|
|
/// 계약 타입별 통계 조회
|
|
/// WARRANTY, CONTRACT, INSPECTION 별 계약 수 반환
|
|
Future<Map<String, int>> getContractsByType() async {
|
|
final contractsByType = await _repository.getContractsByType();
|
|
|
|
// 빈 결과 처리
|
|
if (contractsByType.isEmpty) {
|
|
return {
|
|
'WARRANTY': 0,
|
|
'CONTRACT': 0,
|
|
'INSPECTION': 0,
|
|
};
|
|
}
|
|
|
|
return contractsByType;
|
|
}
|
|
|
|
/// 만료된 계약 수 조회 (즉시 조치 필요)
|
|
Future<int> getExpiredContractsCount() async {
|
|
return await _repository.getExpiredContractsCount();
|
|
}
|
|
|
|
/// 활성 계약 수 조회
|
|
Future<int> getActiveContractsCount() async {
|
|
return await _repository.getActiveContractsCount();
|
|
}
|
|
|
|
/// 대시보드 카드 데이터 생성
|
|
/// UI에서 바로 사용할 수 있는 카드 형태 데이터 반환
|
|
Future<List<MaintenanceStatusCardData>> getDashboardCards() async {
|
|
final stats = await getMaintenanceStats();
|
|
return stats.dashboardCards;
|
|
}
|
|
|
|
/// 위험도 평가
|
|
/// 만료 임박 계약들의 위험 수준을 0.0~1.0으로 반환
|
|
Future<double> calculateRiskScore() async {
|
|
final stats = await getMaintenanceStats();
|
|
return stats.riskScore;
|
|
}
|
|
|
|
/// 통계 데이터 무결성 검증
|
|
void _validateStatsData(MaintenanceStatsDto stats) {
|
|
// 기본 유효성 검사
|
|
if (stats.totalContracts < 0) {
|
|
throw Exception('총 계약 수는 음수일 수 없습니다.');
|
|
}
|
|
|
|
if (stats.activeContracts < 0) {
|
|
throw Exception('활성 계약 수는 음수일 수 없습니다.');
|
|
}
|
|
|
|
if (stats.activeContracts > stats.totalContracts) {
|
|
throw Exception('활성 계약 수는 총 계약 수를 초과할 수 없습니다.');
|
|
}
|
|
|
|
// 만료 기간별 통계 검증
|
|
if (stats.expiring7Days < 0 ||
|
|
stats.expiring30Days < 0 ||
|
|
stats.expiring60Days < 0 ||
|
|
stats.expiredContracts < 0) {
|
|
throw Exception('만료 관련 통계는 음수일 수 없습니다.');
|
|
}
|
|
|
|
// 계약 타입별 통계 검증 (V/R 시스템)
|
|
final totalByType = stats.visitContracts +
|
|
stats.remoteContracts;
|
|
|
|
// 약간의 오차는 허용 (삭제된 계약 등으로 인한 불일치 가능)
|
|
if (totalByType > stats.totalContracts + 100) {
|
|
throw Exception('계약 타입별 합계가 총 계약 수를 크게 초과합니다.');
|
|
}
|
|
|
|
// 완료율 검증 (0.0 ~ 1.0 사이)
|
|
if (stats.completionRate < 0.0 || stats.completionRate > 1.0) {
|
|
throw Exception('완료율은 0~100% 사이여야 합니다.');
|
|
}
|
|
}
|
|
} |