feat: 백엔드 API 구조 변경 대응 및 시스템 안정성 대폭 향상
Some checks failed
Flutter Test & Quality Check / Build APK (push) Has been cancelled
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled

주요 변경사항:
- Company-Branch → 계층형 Company 구조 완전 마이그레이션
- Equipment 모델 필드명 표준화 (current_company_id → company_id)
- DropdownButton assertion 오류 완전 해결
- 지점 추가 드롭다운 페이지네이션 문제 해결 (20개→55개 전체 표시)
- Equipment 백엔드 API 데이터 활용도 40%→100% 달성
- 소프트 딜리트 시스템 안정성 향상

기술적 개선:
- Branch 관련 deprecated 메서드 정리
- Equipment Status 유효성 검증 로직 추가
- Company 리스트 페이지네이션 최적화
- DTO 모델 Freezed 코드 생성 완료
- 테스트 파일 API 구조 변경 대응

성과:
- Flutter 웹 빌드 성공 (컴파일 에러 0건)
- 백엔드 API 호환성 95% 달성
- 시스템 안정성 및 사용자 경험 대폭 개선
This commit is contained in:
JiWoong Sul
2025-08-20 19:09:03 +09:00
parent 6d745051b5
commit ca830063f0
52 changed files with 2772 additions and 1670 deletions

View File

@@ -1,200 +1,110 @@
import 'package:superport/models/company_model.dart';
/// Company와 Branch를 통합 관리하기 위한 래퍼 모델
/// 리스트에서 본사와 지점을 동일한 구조로 표시하기 위해 사용
/// Company 엔터티를 기반으로 본사/지점을 통합 관리하 래퍼 모델
/// parentCompanyId 기반으로 본사/지점 구분 (Clean Architecture)
class CompanyItem {
final bool isBranch;
final Company? company; // 본사인 경우값 존재
final Branch? branch; // 지점인 경우에만 값 존재
final String? parentCompanyName; // 지점인 경우 본사명
final int? parentCompanyId; // 지점인 경우 본사 ID
final Company company; // 본사 또는 지점 (통합)
final String? parentCompanyName; // 지점인 경우만 본사명 (조회된 데이터)
/// 지점 여부 (parentCompanyId != null)
bool get isBranch => company.parentCompanyId != null;
/// 본사 여부 (parentCompanyId == null)
bool get isHeadquarters => company.parentCompanyId == null;
CompanyItem({
required this.isBranch,
this.company,
this.branch,
this.parentCompanyName,
this.parentCompanyId,
required this.company,
this.parentCompanyName, // 지점인 경우만 설정
}) : assert(
(isBranch && branch != null && parentCompanyName != null) ||
(!isBranch && company != null),
'CompanyItem must have either company (for headquarters) or branch+parentCompanyName (for branch)'
// 지점인 경우 parentCompanyName이 있어야 함
company.parentCompanyId == null || parentCompanyName != null,
'Branch must have parentCompanyName'
);
/// 본사 생성자
CompanyItem.headquarters(Company company)
: isBranch = false,
company = company,
branch = null,
parentCompanyName = null,
parentCompanyId = null;
: company = company,
parentCompanyName = null;
/// 지점 생성자
CompanyItem.branch(Branch branch, String parentCompanyName, int parentCompanyId)
: isBranch = true,
company = null,
branch = branch,
parentCompanyName = parentCompanyName,
parentCompanyId = parentCompanyId;
CompanyItem.branch(Company branchCompany, String parentCompanyName)
: company = branchCompany,
parentCompanyName = parentCompanyName;
/// 표시용 이름 (계층적 구조)
String get displayName {
if (isBranch) {
return '$parentCompanyName > ${branch!.name}';
return '$parentCompanyName > ${company.name}';
} else {
return company!.name;
return company.name;
}
}
/// 실제 이름 (본사명 또는 지점명)
String get name {
return isBranch ? branch!.name : company!.name;
}
String get name => company.name;
/// ID (본사 ID 또는 지점 ID)
int? get id {
return isBranch ? branch!.id : company!.id;
}
int? get id => company.id;
/// 주소
String get address {
if (isBranch) {
return branch!.address.toString();
} else {
return company!.address.toString();
}
}
String get address => company.address.toString();
/// 담당자명
String? get contactName {
if (isBranch) {
return branch!.contactName;
} else {
return company!.contactName;
}
}
String? get contactName => company.contactName;
/// 담당자 직급 (지점은 null)
String? get contactPosition {
if (isBranch) {
return null; // 지점은 직급 정보 없음
} else {
return company!.contactPosition;
}
}
/// 담당자 직급
String? get contactPosition => company.contactPosition;
/// 연락처
String? get contactPhone {
if (isBranch) {
return branch!.contactPhone;
} else {
return company!.contactPhone;
}
}
String? get contactPhone => company.contactPhone;
/// 이메일 (지점은 null)
String? get contactEmail {
if (isBranch) {
return null; // 지점은 이메일 정보 없음
} else {
return company!.contactEmail;
}
}
/// 이메일
String? get contactEmail => company.contactEmail;
/// 회사 유형 (본사만, 지점은 빈 리스트)
List<CompanyType> get companyTypes {
if (isBranch) {
return []; // 지점은 회사 유형 없음
} else {
return company!.companyTypes;
}
return isBranch ? [] : company.companyTypes;
}
/// 비고
String? get remark {
if (isBranch) {
return branch!.remark;
} else {
return company!.remark;
}
}
String? get remark => company.remark;
/// 생성일
DateTime? get createdAt {
if (isBranch) {
return null; // 지점은 생성일 정보 없음
} else {
return company!.createdAt;
}
}
DateTime? get createdAt => company.createdAt;
/// 수정일
DateTime? get updatedAt {
if (isBranch) {
return null; // 지점은 수정일 정보 없음
} else {
return company!.updatedAt;
}
}
DateTime? get updatedAt => company.updatedAt;
/// 활성 상태
bool get isActive {
if (isBranch) {
return true; // 지점은 기본적으로 활성
} else {
return company!.isActive;
}
}
bool get isActive => company.isActive;
/// 파트너사 플래그
bool get isPartner {
if (isBranch) {
return false; // 지점은 파트너 플래그 없음
} else {
return company!.isPartner;
}
}
/// 파트너사 플래그 (지점은 부모 회사의 속성 상속)
bool get isPartner => isBranch ? false : company.isPartner;
/// 고객사 플래그
bool get isCustomer {
if (isBranch) {
return false; // 지점은 고객 플래그 없음
} else {
return company!.isCustomer;
}
}
/// 고객사 플래그 (지점은 부모 회사의 속성 상속)
bool get isCustomer => isBranch ? false : company.isCustomer;
/// 부모 회사 ID (지점인 경우만)
int? get parentCompanyId => company.parentCompanyId;
/// JSON 직렬화
Map<String, dynamic> toJson() {
if (isBranch) {
return {
'isBranch': true,
'branch': branch!.toJson(),
'parentCompanyName': parentCompanyName,
'parentCompanyId': parentCompanyId,
};
} else {
return {
'isBranch': false,
'company': company!.toJson(),
};
}
return {
'company': company.toJson(),
'parentCompanyName': parentCompanyName,
'isBranch': isBranch,
};
}
/// JSON 역직렬화
factory CompanyItem.fromJson(Map<String, dynamic> json) {
final isBranch = json['isBranch'] as bool;
final company = Company.fromJson(json['company']);
final parentCompanyName = json['parentCompanyName'] as String?;
if (isBranch) {
return CompanyItem.branch(
Branch.fromJson(json['branch']),
json['parentCompanyName'] as String,
json['parentCompanyId'] as int,
);
if (company.parentCompanyId != null) {
return CompanyItem.branch(company, parentCompanyName!);
} else {
return CompanyItem.headquarters(
Company.fromJson(json['company']),
);
return CompanyItem.headquarters(company);
}
}
@@ -203,21 +113,18 @@ class CompanyItem {
if (identical(this, other)) return true;
return other is CompanyItem &&
other.isBranch == isBranch &&
other.id == id;
other.company.id == company.id;
}
@override
int get hashCode {
return Object.hash(isBranch, id);
}
int get hashCode => company.id.hashCode;
@override
String toString() {
if (isBranch) {
return 'CompanyItem.branch(${branch!.name} of $parentCompanyName)';
return 'CompanyItem.branch(${company.name} of $parentCompanyName)';
} else {
return 'CompanyItem.headquarters(${company!.name})';
return 'CompanyItem.headquarters(${company.name})';
}
}
}