## 주요 변경사항 ### 아키텍처 개선 - Clean Architecture 패턴 적용 (Domain, Data, Presentation 레이어 분리) - Use Case 패턴 도입으로 비즈니스 로직 캡슐화 - Repository 패턴으로 데이터 접근 추상화 - 의존성 주입 구조 개선 ### 상태 관리 최적화 - 모든 Controller에서 불필요한 상태 관리 로직 제거 - 페이지네이션 로직 통일 및 간소화 - 에러 처리 로직 개선 (에러 메시지 한글화) - 로딩 상태 관리 최적화 ### Mock 서비스 제거 - MockDataService 완전 제거 - 모든 화면을 실제 API 전용으로 전환 - 불필요한 Mock 관련 코드 정리 ### UI/UX 개선 - Overview 화면 대시보드 기능 강화 - 라이선스 만료 알림 위젯 추가 - 사이드바 네비게이션 개선 - 일관된 UI 컴포넌트 사용 ### 코드 품질 - 중복 코드 제거 및 함수 추출 - 파일별 책임 분리 명확화 - 테스트 코드 업데이트 ## 영향 범위 - 모든 화면의 Controller 리팩토링 - API 통신 레이어 구조 개선 - 에러 처리 및 로깅 시스템 개선 ## 향후 계획 - 단위 테스트 커버리지 확대 - 통합 테스트 시나리오 추가 - 성능 모니터링 도구 통합
307 lines
10 KiB
Dart
307 lines
10 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get_it/get_it.dart';
|
|
import 'package:superport/models/license_model.dart';
|
|
import 'package:superport/services/license_service.dart';
|
|
|
|
// 라이센스 폼의 상태 및 비즈니스 로직을 담당하는 컨트롤러
|
|
class LicenseFormController extends ChangeNotifier {
|
|
final LicenseService _licenseService = GetIt.instance<LicenseService>();
|
|
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
|
|
|
bool _isEditMode = false;
|
|
int? _licenseId;
|
|
License? _originalLicense;
|
|
bool _isLoading = false;
|
|
String? _error;
|
|
bool _isSaving = false;
|
|
|
|
// 폼 필드 값
|
|
String _name = '';
|
|
int _companyId = 1;
|
|
int _durationMonths = 12; // 기본값: 12개월
|
|
String _visitCycle = '미방문'; // 기본값: 미방문
|
|
|
|
// 추가 필드 컨트롤러
|
|
final TextEditingController productNameController = TextEditingController();
|
|
final TextEditingController licenseKeyController = TextEditingController();
|
|
final TextEditingController vendorController = TextEditingController();
|
|
final TextEditingController locationController = TextEditingController();
|
|
final TextEditingController assignedUserController = TextEditingController();
|
|
String status = '활성';
|
|
DateTime? purchaseDate;
|
|
DateTime? expiryDate;
|
|
|
|
// isEditMode setter
|
|
set isEditMode(bool value) {
|
|
_isEditMode = value;
|
|
notifyListeners();
|
|
}
|
|
|
|
// name setter
|
|
set name(String value) {
|
|
_name = value;
|
|
notifyListeners();
|
|
}
|
|
|
|
// durationMonths setter
|
|
set durationMonths(int value) {
|
|
_durationMonths = value;
|
|
notifyListeners();
|
|
}
|
|
|
|
// visitCycle setter
|
|
set visitCycle(String value) {
|
|
_visitCycle = value;
|
|
notifyListeners();
|
|
}
|
|
|
|
LicenseFormController({
|
|
int? licenseId,
|
|
bool isExtension = false,
|
|
}) {
|
|
if (licenseId != null && !isExtension) {
|
|
_licenseId = licenseId;
|
|
_isEditMode = true;
|
|
// loadLicense()는 별도로 호출됨
|
|
} else if (licenseId != null && isExtension) {
|
|
_licenseId = licenseId;
|
|
_isEditMode = false; // 연장 모드는 새로운 라이선스 생성
|
|
}
|
|
}
|
|
|
|
// Getters
|
|
bool get isEditMode => _isEditMode;
|
|
int? get licenseId => _licenseId;
|
|
License? get originalLicense => _originalLicense;
|
|
bool get isLoading => _isLoading;
|
|
String? get error => _error;
|
|
bool get isSaving => _isSaving;
|
|
String get name => _name;
|
|
int get companyId => _companyId;
|
|
int get durationMonths => _durationMonths;
|
|
String get visitCycle => _visitCycle;
|
|
|
|
// Setters
|
|
void setName(String value) {
|
|
_name = value;
|
|
notifyListeners();
|
|
}
|
|
|
|
void setCompanyId(int value) {
|
|
_companyId = value;
|
|
notifyListeners();
|
|
}
|
|
|
|
void setDurationMonths(int value) {
|
|
_durationMonths = value;
|
|
notifyListeners();
|
|
}
|
|
|
|
void setVisitCycle(String value) {
|
|
_visitCycle = value;
|
|
notifyListeners();
|
|
}
|
|
|
|
// 라이센스 정보 로드 (수정 모드)
|
|
Future<void> loadLicense() async {
|
|
if (_licenseId == null) return;
|
|
|
|
debugPrint('📝 loadLicense 시작 - ID: $_licenseId');
|
|
|
|
_isLoading = true;
|
|
_error = null;
|
|
notifyListeners();
|
|
|
|
try {
|
|
debugPrint('📝 API에서 라이센스 로드 중...');
|
|
_originalLicense = await _licenseService.getLicenseById(_licenseId!);
|
|
|
|
debugPrint('📝 로드된 라이센스: $_originalLicense');
|
|
|
|
if (_originalLicense != null) {
|
|
// 폼 필드에 데이터 설정
|
|
productNameController.text = _originalLicense!.productName ?? '';
|
|
licenseKeyController.text = _originalLicense!.licenseKey;
|
|
vendorController.text = _originalLicense!.vendor ?? '';
|
|
locationController.text = _originalLicense!.companyName ?? '';
|
|
assignedUserController.text = _originalLicense!.assignedUserName ?? '';
|
|
|
|
debugPrint('📝 폼 필드 설정 완료:');
|
|
debugPrint(' - 제품명: ${productNameController.text}');
|
|
debugPrint(' - 라이선스 키: ${licenseKeyController.text}');
|
|
debugPrint(' - 벤더: ${vendorController.text}');
|
|
debugPrint(' - 현위치: ${locationController.text}');
|
|
debugPrint(' - 할당 사용자: ${assignedUserController.text}');
|
|
status = _originalLicense!.isActive ? '활성' : '비활성';
|
|
purchaseDate = _originalLicense!.purchaseDate;
|
|
expiryDate = _originalLicense!.expiryDate;
|
|
|
|
_name = _originalLicense!.productName ?? '';
|
|
_companyId = _originalLicense!.companyId ?? 1;
|
|
|
|
// remark에서 방문주기 정보 추출 (있는 경우)
|
|
if (_originalLicense!.remark != null && _originalLicense!.remark!.contains('방문주기:')) {
|
|
final remarkParts = _originalLicense!.remark!.split('방문주기:');
|
|
if (remarkParts.length > 1) {
|
|
_visitCycle = remarkParts[1].trim();
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
_error = e.toString();
|
|
debugPrint('❌ 라이센스 로드 실패: $e');
|
|
} finally {
|
|
_isLoading = false;
|
|
notifyListeners();
|
|
debugPrint('📝 loadLicense 완료 - isLoading: false');
|
|
}
|
|
}
|
|
|
|
// 라이센스 정보 로드 (연장 모드 - 기존 데이터로 새 라이선스 생성)
|
|
Future<void> loadLicenseForExtension() async {
|
|
if (_licenseId == null) return;
|
|
|
|
debugPrint('📝 loadLicenseForExtension 시작 - ID: $_licenseId');
|
|
|
|
_isLoading = true;
|
|
_error = null;
|
|
notifyListeners();
|
|
|
|
try {
|
|
debugPrint('📝 API에서 라이센스 로드 중 (연장용)...');
|
|
final sourceLicense = await _licenseService.getLicenseById(_licenseId!);
|
|
|
|
debugPrint('📝 로드된 소스 라이센스: $sourceLicense');
|
|
|
|
if (sourceLicense != null) {
|
|
// 연장용으로 기존 데이터 복사 (ID는 null로 새 라이선스 생성)
|
|
productNameController.text = sourceLicense.productName ?? '';
|
|
licenseKeyController.text = '${sourceLicense.licenseKey}-EXT-${DateTime.now().millisecondsSinceEpoch}';
|
|
vendorController.text = sourceLicense.vendor ?? '';
|
|
locationController.text = sourceLicense.companyName ?? '';
|
|
assignedUserController.text = sourceLicense.assignedUserName ?? '';
|
|
|
|
debugPrint('📝 연장용 폼 필드 설정 완료:');
|
|
debugPrint(' - 제품명: ${productNameController.text}');
|
|
debugPrint(' - 라이선스 키: ${licenseKeyController.text}');
|
|
debugPrint(' - 벤더: ${vendorController.text}');
|
|
debugPrint(' - 현위치: ${locationController.text}');
|
|
debugPrint(' - 할당 사용자: ${assignedUserController.text}');
|
|
status = '활성'; // 연장은 항상 활성으로 시작
|
|
purchaseDate = DateTime.now(); // 구매일은 오늘
|
|
// 만료일은 기존 만료일에서 연장 (기본 12개월)
|
|
expiryDate = sourceLicense.expiryDate?.add(Duration(days: _durationMonths * 30))
|
|
?? DateTime.now().add(Duration(days: _durationMonths * 30));
|
|
|
|
_name = sourceLicense.productName ?? '';
|
|
_companyId = sourceLicense.companyId ?? 1;
|
|
|
|
// remark에서 방문주기 정보 추출 (있는 경우)
|
|
if (sourceLicense.remark != null && sourceLicense.remark!.contains('방문주기:')) {
|
|
final remarkParts = sourceLicense.remark!.split('방문주기:');
|
|
if (remarkParts.length > 1) {
|
|
_visitCycle = remarkParts[1].trim();
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
_error = e.toString();
|
|
debugPrint('❌ 라이센스 연장 로드 실패: $e');
|
|
} finally {
|
|
_isLoading = false;
|
|
notifyListeners();
|
|
debugPrint('📝 loadLicenseForExtension 완료 - isLoading: false');
|
|
}
|
|
}
|
|
|
|
// 라이센스 저장
|
|
Future<bool> saveLicense() async {
|
|
if (formKey.currentState?.validate() != true) return false;
|
|
|
|
formKey.currentState?.save();
|
|
|
|
_isSaving = true;
|
|
_error = null;
|
|
notifyListeners();
|
|
|
|
try {
|
|
final license = License(
|
|
id: _isEditMode ? _licenseId : null,
|
|
licenseKey: licenseKeyController.text.isNotEmpty
|
|
? licenseKeyController.text
|
|
: 'LIC-${DateTime.now().millisecondsSinceEpoch}',
|
|
productName: productNameController.text,
|
|
vendor: vendorController.text,
|
|
companyName: locationController.text,
|
|
assignedUserName: assignedUserController.text.isNotEmpty
|
|
? assignedUserController.text
|
|
: null,
|
|
companyId: _companyId,
|
|
isActive: status == '활성',
|
|
purchaseDate: purchaseDate ?? DateTime.now(),
|
|
expiryDate: expiryDate ?? DateTime.now().add(Duration(days: _durationMonths * 30)),
|
|
remark: '${_durationMonths}개월,${_visitCycle},방문',
|
|
);
|
|
|
|
if (_isEditMode) {
|
|
await _licenseService.updateLicense(license);
|
|
} else {
|
|
await _licenseService.createLicense(license);
|
|
}
|
|
|
|
return true;
|
|
} catch (e) {
|
|
_error = e.toString();
|
|
notifyListeners();
|
|
return false;
|
|
} finally {
|
|
_isSaving = false;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
// 폼 초기화
|
|
void resetForm() {
|
|
_name = '';
|
|
_companyId = 1;
|
|
_durationMonths = 12;
|
|
_visitCycle = '미방문';
|
|
_error = null;
|
|
formKey.currentState?.reset();
|
|
notifyListeners();
|
|
}
|
|
|
|
// 유효성 검사
|
|
String? validateName(String? value) {
|
|
if (value == null || value.isEmpty) {
|
|
return '라이선스명을 입력해주세요';
|
|
}
|
|
if (value.length < 2) {
|
|
return '라이선스명은 2자 이상이어야 합니다';
|
|
}
|
|
return null;
|
|
}
|
|
|
|
String? validateDuration(String? value) {
|
|
if (value == null || value.isEmpty) {
|
|
return '계약 기간을 입력해주세요';
|
|
}
|
|
final duration = int.tryParse(value);
|
|
if (duration == null || duration < 1) {
|
|
return '유효한 계약 기간을 입력해주세요';
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
// TextEditingController들 정리
|
|
productNameController.dispose();
|
|
licenseKeyController.dispose();
|
|
vendorController.dispose();
|
|
locationController.dispose();
|
|
assignedUserController.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|