import 'package:freezed_annotation/freezed_annotation.dart'; part 'user_model.freezed.dart'; part 'user_model.g.dart'; /// 사용자 도메인 엔티티 (서버 API v0.2.1 스키마 대응) /// 권한: admin(관리자), manager(매니저), staff(직원) @freezed class User with _$User { const factory User({ /// 사용자 ID (자동 생성) int? id, /// 사용자명 (로그인용, 필수, 유니크, 3자 이상) required String username, /// 이메일 (필수, 유니크) required String email, /// 이름 (필수) required String name, /// 전화번호 (선택, "010-1234-5678" 형태) String? phone, /// 권한 (필수: admin, manager, staff) required UserRole role, /// 활성화 상태 (기본값: true) @Default(true) bool isActive, /// 생성일시 (자동 입력) DateTime? createdAt, /// 수정일시 (자동 갱신) DateTime? updatedAt, }) = _User; factory User.fromJson(Map json) => _$UserFromJson(json); } /// 사용자 권한 열거형 (서버 API 스키마 대응) @JsonEnum() enum UserRole { /// 관리자 - 전체 시스템 관리 권한 @JsonValue('admin') admin, /// 매니저 - 중간 관리 권한 @JsonValue('manager') manager, /// 직원 - 기본 사용 권한 @JsonValue('staff') staff; /// 권한 한글명 반환 String get displayName { switch (this) { case UserRole.admin: return '관리자'; case UserRole.manager: return '매니저'; case UserRole.staff: return '직원'; } } /// 권한 레벨 반환 (높을수록 상위 권한) int get level { switch (this) { case UserRole.admin: return 3; case UserRole.manager: return 2; case UserRole.staff: return 1; } } /// 문자열로부터 UserRole 생성 static UserRole fromString(String value) { switch (value.toLowerCase()) { case 'admin': return UserRole.admin; case 'manager': return UserRole.manager; case 'staff': return UserRole.staff; default: throw ArgumentError('Unknown user role: $value'); } } } /// 레거시 권한 시스템 호환성 유틸리티 /// 기존 S/M 코드와의 호환성을 위해 임시 유지 class LegacyUserRoles { static const String admin = 'S'; // 관리자 (삭제 예정) static const String member = 'M'; // 멤버 (삭제 예정) /// 레거시 권한을 새 권한으로 변환 static UserRole toLegacyRole(String legacyRole) { switch (legacyRole) { case 'S': return UserRole.admin; case 'M': return UserRole.staff; default: return UserRole.staff; } } /// 새 권한을 레거시 권한으로 변환 (임시) static String fromLegacyRole(UserRole role) { switch (role) { case UserRole.admin: return 'S'; case UserRole.manager: case UserRole.staff: return 'M'; } } } /// 전화번호 유틸리티 class PhoneNumberUtil { /// 전화번호 형식 검증 (010-1234-5678) static bool isValidFormat(String phone) { final regex = RegExp(r'^\d{3}-\d{3,4}-\d{4}$'); return regex.hasMatch(phone); } /// 전화번호 포맷팅 (01012345678 → 010-1234-5678) static String format(String phone) { final cleanPhone = phone.replaceAll(RegExp(r'[^\d]'), ''); if (cleanPhone.length == 10) { return '${cleanPhone.substring(0, 3)}-${cleanPhone.substring(3, 6)}-${cleanPhone.substring(6)}'; } else if (cleanPhone.length == 11) { return '${cleanPhone.substring(0, 3)}-${cleanPhone.substring(3, 7)}-${cleanPhone.substring(7)}'; } return phone; // 형식이 맞지 않으면 원본 반환 } /// UI용 전화번호 분리 (010-1234-5678 → {prefix: "010", number: "12345678"}) static Map splitForUI(String? phone) { if (phone == null || phone.isEmpty) { return {'prefix': '010', 'number': ''}; } final parts = phone.split('-'); if (parts.length >= 2) { return { 'prefix': parts[0], 'number': parts.sublist(1).join(''), }; } return {'prefix': '010', 'number': phone}; } /// UI에서 서버용 전화번호 조합 ({prefix: "010", number: "12345678"} → "010-1234-5678") static String combineFromUI(String prefix, String number) { if (number.isEmpty) return ''; final cleanNumber = number.replaceAll(RegExp(r'[^\d]'), ''); if (cleanNumber.length == 7) { return '$prefix-${cleanNumber.substring(0, 3)}-${cleanNumber.substring(3)}'; } else if (cleanNumber.length == 8) { return '$prefix-${cleanNumber.substring(0, 4)}-${cleanNumber.substring(4)}'; } return '$prefix-$cleanNumber'; } }