feat: 라이선스 및 창고 관리 API 연동 구현
- 라이선스 관리 API 연동 완료 - LicenseRemoteDataSource, LicenseService 구현 - LicenseListController, LicenseFormController API 연동 - 페이지네이션, 검색, 필터링 기능 추가 - 라이선스 할당/해제 기능 구현 - 창고 관리 API 연동 완료 - WarehouseRemoteDataSource, WarehouseService 구현 - WarehouseLocationListController, WarehouseLocationFormController API 연동 - 창고별 장비 조회 및 용량 관리 기능 추가 - DI 컨테이너에 새로운 서비스 등록 - API 통합 문서 업데이트 (전체 진행률 100% 달성)
This commit is contained in:
@@ -1,57 +1,186 @@
|
||||
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';
|
||||
import 'package:superport/services/mock_data_service.dart';
|
||||
|
||||
// 라이센스 폼의 상태 및 비즈니스 로직을 담당하는 컨트롤러
|
||||
class LicenseFormController {
|
||||
final MockDataService dataService;
|
||||
class LicenseFormController extends ChangeNotifier {
|
||||
final bool useApi;
|
||||
final MockDataService? mockDataService;
|
||||
late final LicenseService _licenseService;
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
|
||||
bool isEditMode = false;
|
||||
int? licenseId;
|
||||
String name = '';
|
||||
int durationMonths = 12; // 기본값: 12개월
|
||||
String visitCycle = '미방문'; // 기본값: 미방문
|
||||
bool _isEditMode = false;
|
||||
int? _licenseId;
|
||||
License? _originalLicense;
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
bool _isSaving = false;
|
||||
|
||||
LicenseFormController({required this.dataService, this.licenseId});
|
||||
// 폼 필드 값
|
||||
String _name = '';
|
||||
int _companyId = 1;
|
||||
int _durationMonths = 12; // 기본값: 12개월
|
||||
String _visitCycle = '미방문'; // 기본값: 미방문
|
||||
|
||||
LicenseFormController({
|
||||
this.useApi = true,
|
||||
this.mockDataService,
|
||||
int? licenseId,
|
||||
}) {
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
_licenseService = GetIt.instance<LicenseService>();
|
||||
}
|
||||
|
||||
if (licenseId != null) {
|
||||
_licenseId = licenseId;
|
||||
_isEditMode = true;
|
||||
loadLicense();
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// 라이센스 정보 로드 (수정 모드)
|
||||
void loadLicense() {
|
||||
if (licenseId == null) return;
|
||||
final license = dataService.getLicenseById(licenseId!);
|
||||
if (license != null) {
|
||||
name = license.name;
|
||||
durationMonths = license.durationMonths;
|
||||
visitCycle = license.visitCycle;
|
||||
Future<void> loadLicense() async {
|
||||
if (_licenseId == null) return;
|
||||
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
_originalLicense = await _licenseService.getLicenseById(_licenseId!);
|
||||
} else {
|
||||
_originalLicense = mockDataService?.getLicenseById(_licenseId!);
|
||||
}
|
||||
|
||||
if (_originalLicense != null) {
|
||||
_name = _originalLicense!.name;
|
||||
_companyId = _originalLicense!.companyId;
|
||||
_durationMonths = _originalLicense!.durationMonths;
|
||||
_visitCycle = _originalLicense!.visitCycle;
|
||||
}
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// 라이센스 저장 (UI에서 호출)
|
||||
void saveLicense(Function() onSuccess) {
|
||||
if (formKey.currentState?.validate() != true) return;
|
||||
// 라이센스 저장
|
||||
Future<bool> saveLicense() async {
|
||||
if (formKey.currentState?.validate() != true) return false;
|
||||
|
||||
formKey.currentState?.save();
|
||||
if (isEditMode && licenseId != null) {
|
||||
final license = dataService.getLicenseById(licenseId!);
|
||||
if (license != null) {
|
||||
final updatedLicense = License(
|
||||
id: license.id,
|
||||
companyId: license.companyId,
|
||||
name: name,
|
||||
durationMonths: durationMonths,
|
||||
visitCycle: visitCycle,
|
||||
);
|
||||
dataService.updateLicense(updatedLicense);
|
||||
}
|
||||
} else {
|
||||
// 라이센스 추가 시 임시 회사 ID 사용 또는 나중에 설정하도록 변경
|
||||
final newLicense = License(
|
||||
companyId: 1, // 기본값 또는 필요에 따라 수정
|
||||
name: name,
|
||||
durationMonths: durationMonths,
|
||||
visitCycle: visitCycle,
|
||||
|
||||
_isSaving = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final license = License(
|
||||
id: _isEditMode ? _licenseId : null,
|
||||
companyId: _companyId,
|
||||
name: _name,
|
||||
durationMonths: _durationMonths,
|
||||
visitCycle: _visitCycle,
|
||||
);
|
||||
dataService.addLicense(newLicense);
|
||||
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
if (_isEditMode) {
|
||||
await _licenseService.updateLicense(license);
|
||||
} else {
|
||||
await _licenseService.createLicense(license);
|
||||
}
|
||||
} else {
|
||||
if (_isEditMode) {
|
||||
mockDataService?.updateLicense(license);
|
||||
} else {
|
||||
mockDataService?.addLicense(license);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
notifyListeners();
|
||||
return false;
|
||||
} finally {
|
||||
_isSaving = false;
|
||||
notifyListeners();
|
||||
}
|
||||
onSuccess();
|
||||
}
|
||||
|
||||
// 폼 초기화
|
||||
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() {
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,227 @@
|
||||
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';
|
||||
import 'package:superport/services/mock_data_service.dart';
|
||||
|
||||
// 라이센스 목록 화면의 상태 및 비즈니스 로직을 담당하는 컨트롤러
|
||||
class LicenseListController {
|
||||
final MockDataService dataService;
|
||||
List<License> licenses = [];
|
||||
class LicenseListController extends ChangeNotifier {
|
||||
final bool useApi;
|
||||
final MockDataService? mockDataService;
|
||||
late final LicenseService _licenseService;
|
||||
|
||||
List<License> _licenses = [];
|
||||
List<License> _filteredLicenses = [];
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
String _searchQuery = '';
|
||||
int _currentPage = 1;
|
||||
final int _pageSize = 20;
|
||||
bool _hasMore = true;
|
||||
int _total = 0;
|
||||
|
||||
LicenseListController({required this.dataService});
|
||||
// 필터 옵션
|
||||
int? _selectedCompanyId;
|
||||
bool? _isActive;
|
||||
String? _licenseType;
|
||||
|
||||
LicenseListController({this.useApi = true, this.mockDataService}) {
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
_licenseService = GetIt.instance<LicenseService>();
|
||||
}
|
||||
}
|
||||
|
||||
// Getters
|
||||
List<License> get licenses => _filteredLicenses;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
String get searchQuery => _searchQuery;
|
||||
int get currentPage => _currentPage;
|
||||
bool get hasMore => _hasMore;
|
||||
int get total => _total;
|
||||
int? get selectedCompanyId => _selectedCompanyId;
|
||||
bool? get isActive => _isActive;
|
||||
String? get licenseType => _licenseType;
|
||||
|
||||
// 데이터 로드
|
||||
void loadData() {
|
||||
licenses = dataService.getAllLicenses();
|
||||
Future<void> loadData({bool isInitialLoad = true}) async {
|
||||
if (_isLoading) return;
|
||||
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
|
||||
if (isInitialLoad) {
|
||||
_currentPage = 1;
|
||||
_licenses.clear();
|
||||
_hasMore = true;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
// API 사용
|
||||
final fetchedLicenses = await _licenseService.getLicenses(
|
||||
page: _currentPage,
|
||||
perPage: _pageSize,
|
||||
isActive: _isActive,
|
||||
companyId: _selectedCompanyId,
|
||||
licenseType: _licenseType,
|
||||
);
|
||||
|
||||
if (isInitialLoad) {
|
||||
_licenses = fetchedLicenses;
|
||||
} else {
|
||||
_licenses.addAll(fetchedLicenses);
|
||||
}
|
||||
|
||||
_hasMore = fetchedLicenses.length >= _pageSize;
|
||||
|
||||
// 전체 개수 조회
|
||||
_total = await _licenseService.getTotalLicenses(
|
||||
isActive: _isActive,
|
||||
companyId: _selectedCompanyId,
|
||||
licenseType: _licenseType,
|
||||
);
|
||||
} else {
|
||||
// Mock 데이터 사용
|
||||
final allLicenses = mockDataService?.getAllLicenses() ?? [];
|
||||
|
||||
// 필터링 적용
|
||||
var filtered = allLicenses;
|
||||
if (_selectedCompanyId != null) {
|
||||
filtered = filtered.where((l) => l.companyId == _selectedCompanyId).toList();
|
||||
}
|
||||
|
||||
// 페이지네이션 적용
|
||||
final startIndex = (_currentPage - 1) * _pageSize;
|
||||
final endIndex = startIndex + _pageSize;
|
||||
|
||||
if (startIndex < filtered.length) {
|
||||
final pageLicenses = filtered.sublist(
|
||||
startIndex,
|
||||
endIndex > filtered.length ? filtered.length : endIndex,
|
||||
);
|
||||
|
||||
if (isInitialLoad) {
|
||||
_licenses = pageLicenses;
|
||||
} else {
|
||||
_licenses.addAll(pageLicenses);
|
||||
}
|
||||
|
||||
_hasMore = endIndex < filtered.length;
|
||||
} else {
|
||||
_hasMore = false;
|
||||
}
|
||||
|
||||
_total = filtered.length;
|
||||
}
|
||||
|
||||
_applySearchFilter();
|
||||
|
||||
if (!isInitialLoad) {
|
||||
_currentPage++;
|
||||
}
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// 다음 페이지 로드
|
||||
Future<void> loadNextPage() async {
|
||||
if (!_hasMore || _isLoading) return;
|
||||
_currentPage++;
|
||||
await loadData(isInitialLoad: false);
|
||||
}
|
||||
|
||||
// 검색
|
||||
void search(String query) {
|
||||
_searchQuery = query;
|
||||
_applySearchFilter();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// 검색 필터 적용
|
||||
void _applySearchFilter() {
|
||||
if (_searchQuery.isEmpty) {
|
||||
_filteredLicenses = List.from(_licenses);
|
||||
} else {
|
||||
_filteredLicenses = _licenses.where((license) {
|
||||
return license.name.toLowerCase().contains(_searchQuery.toLowerCase());
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
// 필터 설정
|
||||
void setFilters({
|
||||
int? companyId,
|
||||
bool? isActive,
|
||||
String? licenseType,
|
||||
}) {
|
||||
_selectedCompanyId = companyId;
|
||||
_isActive = isActive;
|
||||
_licenseType = licenseType;
|
||||
loadData();
|
||||
}
|
||||
|
||||
// 필터 초기화
|
||||
void clearFilters() {
|
||||
_selectedCompanyId = null;
|
||||
_isActive = null;
|
||||
_licenseType = null;
|
||||
_searchQuery = '';
|
||||
loadData();
|
||||
}
|
||||
|
||||
// 라이센스 삭제
|
||||
void deleteLicense(int id) {
|
||||
dataService.deleteLicense(id);
|
||||
loadData();
|
||||
Future<void> deleteLicense(int id) async {
|
||||
try {
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
await _licenseService.deleteLicense(id);
|
||||
} else {
|
||||
mockDataService?.deleteLicense(id);
|
||||
}
|
||||
|
||||
// 목록에서 제거
|
||||
_licenses.removeWhere((l) => l.id == id);
|
||||
_applySearchFilter();
|
||||
_total--;
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// 새로고침
|
||||
Future<void> refresh() async {
|
||||
await loadData();
|
||||
}
|
||||
|
||||
// 만료 예정 라이선스 조회
|
||||
Future<List<License>> getExpiringLicenses({int days = 30}) async {
|
||||
try {
|
||||
if (useApi && GetIt.instance.isRegistered<LicenseService>()) {
|
||||
return await _licenseService.getExpiringLicenses(days: days);
|
||||
} else {
|
||||
// Mock 데이터에서 만료 예정 라이선스 필터링
|
||||
final now = DateTime.now();
|
||||
final allLicenses = mockDataService?.getAllLicenses() ?? [];
|
||||
|
||||
return allLicenses.where((license) {
|
||||
// Mock 데이터는 만료일이 없으므로 임의로 계산
|
||||
final expiryDate = now.add(Duration(days: license.durationMonths * 30));
|
||||
final daysUntilExpiry = expiryDate.difference(now).inDays;
|
||||
return daysUntilExpiry > 0 && daysUntilExpiry <= days;
|
||||
}).toList();
|
||||
}
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
notifyListeners();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user