회원목록

This commit is contained in:
2025-08-01 16:45:26 +09:00
parent a996def67a
commit ed8c7eacba
191 changed files with 17491 additions and 0 deletions
+265
View File
@@ -0,0 +1,265 @@
/// 사용자 역할 정의
enum UserRole {
member, // 일반 회원
manager, // 매니저 (일부 관리 권한)
admin, // 관리자 (대부분의 관리 권한)
owner // 소유자 (모든 권한)
}
/// 관리 권한 정의
class AdminPermission {
final bool canManageMembers; // 회원 관리 권한
final bool canManageEvents; // 이벤트 관리 권한
final bool canManageScores; // 점수 관리 권한
final bool canManageSettings; // 설정 관리 권한
final bool canManageSubscription; // 구독 관리 권한
final bool canManageRoles; // 역할 관리 권한
final bool canViewFinancials; // 재정 정보 조회 권한
final bool canExportData; // 데이터 내보내기 권한
const AdminPermission({
this.canManageMembers = false,
this.canManageEvents = false,
this.canManageScores = false,
this.canManageSettings = false,
this.canManageSubscription = false,
this.canManageRoles = false,
this.canViewFinancials = false,
this.canExportData = false,
});
/// 역할에 따른 기본 권한 생성
factory AdminPermission.fromRole(UserRole role) {
switch (role) {
case UserRole.member:
return const AdminPermission();
case UserRole.manager:
return const AdminPermission(
canManageMembers: true,
canManageEvents: true,
canManageScores: true,
canExportData: true,
);
case UserRole.admin:
return const AdminPermission(
canManageMembers: true,
canManageEvents: true,
canManageScores: true,
canManageSettings: true,
canViewFinancials: true,
canExportData: true,
);
case UserRole.owner:
return const AdminPermission(
canManageMembers: true,
canManageEvents: true,
canManageScores: true,
canManageSettings: true,
canManageSubscription: true,
canManageRoles: true,
canViewFinancials: true,
canExportData: true,
);
}
}
/// JSON에서 권한 객체 생성
factory AdminPermission.fromJson(Map<String, dynamic> json) {
return AdminPermission(
canManageMembers: json['canManageMembers'] ?? false,
canManageEvents: json['canManageEvents'] ?? false,
canManageScores: json['canManageScores'] ?? false,
canManageSettings: json['canManageSettings'] ?? false,
canManageSubscription: json['canManageSubscription'] ?? false,
canManageRoles: json['canManageRoles'] ?? false,
canViewFinancials: json['canViewFinancials'] ?? false,
canExportData: json['canExportData'] ?? false,
);
}
/// 권한 객체를 JSON으로 변환
Map<String, dynamic> toJson() {
return {
'canManageMembers': canManageMembers,
'canManageEvents': canManageEvents,
'canManageScores': canManageScores,
'canManageSettings': canManageSettings,
'canManageSubscription': canManageSubscription,
'canManageRoles': canManageRoles,
'canViewFinancials': canViewFinancials,
'canExportData': canExportData,
};
}
}
/// 클럽 회원 역할 정보
class MemberRole {
final String userId;
final String memberId;
final String clubId;
final UserRole role;
final AdminPermission permissions;
final DateTime assignedAt;
final String? assignedBy;
MemberRole({
required this.userId,
required this.memberId,
required this.clubId,
required this.role,
required this.permissions,
required this.assignedAt,
this.assignedBy,
});
/// JSON에서 회원 역할 객체 생성
factory MemberRole.fromJson(Map<String, dynamic> json) {
return MemberRole(
userId: json['userId'],
memberId: json['memberId'],
clubId: json['clubId'],
role: UserRole.values.firstWhere(
(e) => e.toString().split('.').last == json['role'],
orElse: () => UserRole.member,
),
permissions: AdminPermission.fromJson(json['permissions'] ?? {}),
assignedAt: DateTime.parse(json['assignedAt']),
assignedBy: json['assignedBy'],
);
}
/// 회원 역할 객체를 JSON으로 변환
Map<String, dynamic> toJson() {
return {
'userId': userId,
'memberId': memberId,
'clubId': clubId,
'role': role.toString().split('.').last,
'permissions': permissions.toJson(),
'assignedAt': assignedAt.toIso8601String(),
'assignedBy': assignedBy,
};
}
/// 역할 이름 (한글)
String get roleName {
switch (role) {
case UserRole.member:
return '일반 회원';
case UserRole.manager:
return '매니저';
case UserRole.admin:
return '관리자';
case UserRole.owner:
return '소유자';
}
}
}
/// 클럽 설정 모델
class ClubSettings {
final String clubId;
final String clubName;
final String? logoUrl;
final String? bannerUrl;
final String? description;
final String? contactEmail;
final String? contactPhone;
final String? address;
final String? website;
final Map<String, dynamic>? customFields;
final Map<String, dynamic>? notificationSettings;
final Map<String, dynamic>? privacySettings;
final DateTime createdAt;
final DateTime updatedAt;
ClubSettings({
required this.clubId,
required this.clubName,
this.logoUrl,
this.bannerUrl,
this.description,
this.contactEmail,
this.contactPhone,
this.address,
this.website,
this.customFields,
this.notificationSettings,
this.privacySettings,
required this.createdAt,
required this.updatedAt,
});
/// JSON에서 클럽 설정 객체 생성
factory ClubSettings.fromJson(Map<String, dynamic> json) {
return ClubSettings(
clubId: json['clubId'],
clubName: json['clubName'],
logoUrl: json['logoUrl'],
bannerUrl: json['bannerUrl'],
description: json['description'],
contactEmail: json['contactEmail'],
contactPhone: json['contactPhone'],
address: json['address'],
website: json['website'],
customFields: json['customFields'],
notificationSettings: json['notificationSettings'],
privacySettings: json['privacySettings'],
createdAt: DateTime.parse(json['createdAt']),
updatedAt: DateTime.parse(json['updatedAt']),
);
}
/// 클럽 설정 객체를 JSON으로 변환
Map<String, dynamic> toJson() {
return {
'clubId': clubId,
'clubName': clubName,
'logoUrl': logoUrl,
'bannerUrl': bannerUrl,
'description': description,
'contactEmail': contactEmail,
'contactPhone': contactPhone,
'address': address,
'website': website,
'customFields': customFields,
'notificationSettings': notificationSettings,
'privacySettings': privacySettings,
'createdAt': createdAt.toIso8601String(),
'updatedAt': updatedAt.toIso8601String(),
};
}
/// 복사본 생성 (일부 필드 업데이트)
ClubSettings copyWith({
String? clubName,
String? logoUrl,
String? bannerUrl,
String? description,
String? contactEmail,
String? contactPhone,
String? address,
String? website,
Map<String, dynamic>? customFields,
Map<String, dynamic>? notificationSettings,
Map<String, dynamic>? privacySettings,
}) {
return ClubSettings(
clubId: clubId,
clubName: clubName ?? this.clubName,
logoUrl: logoUrl ?? this.logoUrl,
bannerUrl: bannerUrl ?? this.bannerUrl,
description: description ?? this.description,
contactEmail: contactEmail ?? this.contactEmail,
contactPhone: contactPhone ?? this.contactPhone,
address: address ?? this.address,
website: website ?? this.website,
customFields: customFields ?? this.customFields,
notificationSettings: notificationSettings ?? this.notificationSettings,
privacySettings: privacySettings ?? this.privacySettings,
createdAt: createdAt,
updatedAt: DateTime.now(),
);
}
}
+77
View File
@@ -0,0 +1,77 @@
class Club {
final String id;
final String name;
final String? description;
final String? logo;
final String? address;
final String? location;
final String? phone;
final String? email;
final String? website;
final int? memberCount;
final String? ownerId;
final int? femaleHandicap;
final String? averageCalculationPeriod;
final DateTime? createdAt;
final DateTime? updatedAt;
Club({
required this.id,
required this.name,
this.description,
this.logo,
this.address,
this.location,
this.phone,
this.email,
this.website,
this.memberCount,
this.ownerId,
this.femaleHandicap,
this.averageCalculationPeriod,
this.createdAt,
this.updatedAt,
});
// JSON 데이터로부터 Club 객체 생성
factory Club.fromJson(Map<String, dynamic> json) {
return Club(
id: json['id'] != null ? json['id'].toString() : '',
name: json['name'] ?? '',
description: json['description'],
logo: json['logo'],
address: json['address'],
location: json['location'],
phone: json['phone'] ?? json['contact'], // contact 필드도 확인
email: json['email'],
website: json['website'],
memberCount: json['memberCount'],
ownerId: json['ownerId']?.toString(),
femaleHandicap: json['femaleHandicap'] != null ? int.tryParse(json['femaleHandicap'].toString()) : null,
averageCalculationPeriod: json['averageCalculationPeriod'],
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt']) : null,
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt']) : null,
);
}
// Club 객체를 JSON으로 변환
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'description': description,
'logo': logo,
'address': address,
'location': location,
'phone': phone,
'email': email,
'website': website,
'memberCount': memberCount,
'ownerId': ownerId,
'femaleHandicap': femaleHandicap,
'averageCalculationPeriod': averageCalculationPeriod,
'createdAt': createdAt?.toIso8601String(),
'updatedAt': updatedAt?.toIso8601String(),
};
}
}
+73
View File
@@ -0,0 +1,73 @@
class Event {
final String id;
final String clubId;
final String title;
final String? description;
final DateTime startDate;
final DateTime? endDate;
final String? location;
final String? type;
final int? maxParticipants;
final int? currentParticipants;
final bool isActive;
final String? createdBy;
final DateTime? createdAt;
final DateTime? updatedAt;
Event({
required this.id,
required this.clubId,
required this.title,
this.description,
required this.startDate,
this.endDate,
this.location,
this.type,
this.maxParticipants,
this.currentParticipants,
required this.isActive,
this.createdBy,
this.createdAt,
this.updatedAt,
});
// JSON 데이터로부터 Event 객체 생성
factory Event.fromJson(Map<String, dynamic> json) {
return Event(
id: json['id']?.toString() ?? '',
clubId: json['clubId']?.toString() ?? '',
title: json['title'] ?? '',
description: json['description']?.toString(),
startDate: json['startDate'] != null ? DateTime.parse(json['startDate'].toString()) : DateTime.now(),
endDate: json['endDate'] != null ? DateTime.parse(json['endDate'].toString()) : null,
location: json['location']?.toString(),
type: json['type']?.toString(),
maxParticipants: json['maxParticipants'],
currentParticipants: json['currentParticipants'],
isActive: json['isActive'] ?? true,
createdBy: json['createdBy']?.toString(),
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt'].toString()) : null,
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt'].toString()) : null,
);
}
// Event 객체를 JSON으로 변환
Map<String, dynamic> toJson() {
return {
'id': id,
'clubId': clubId,
'title': title,
'description': description,
'startDate': startDate.toIso8601String(),
'endDate': endDate?.toIso8601String(),
'location': location,
'type': type,
'maxParticipants': maxParticipants,
'currentParticipants': currentParticipants,
'isActive': isActive,
'createdBy': createdBy,
'createdAt': createdAt?.toIso8601String(),
'updatedAt': updatedAt?.toIso8601String(),
};
}
}
+126
View File
@@ -0,0 +1,126 @@
class Member {
final String id;
final String userId;
final String clubId;
final String name;
final String email;
final String? role;
final String? profileImage;
final String? phone;
final String? address;
final DateTime? joinDate;
final bool isActive;
final String? gender;
final String? memberType;
final int? handicap;
final String? status;
final DateTime? createdAt;
final DateTime? updatedAt;
Member({
required this.id,
required this.userId,
required this.clubId,
required this.name,
required this.email,
this.role,
this.profileImage,
this.phone,
this.address,
this.joinDate,
required this.isActive,
this.gender,
this.memberType,
this.handicap,
this.status,
this.createdAt,
this.updatedAt,
});
// JSON 데이터로부터 Member 객체 생성
factory Member.fromJson(Map<String, dynamic> json) {
return Member(
id: json['id']?.toString() ?? '',
userId: json['userId']?.toString() ?? '',
clubId: json['clubId']?.toString() ?? '',
name: json['name'] ?? '',
email: json['email'] ?? '',
role: json['role']?.toString(),
profileImage: json['profileImage']?.toString(),
phone: json['phone']?.toString(),
address: json['address']?.toString(),
joinDate: json['joinDate'] != null ? DateTime.parse(json['joinDate'].toString()) : null,
isActive: json['isActive'] ?? true,
gender: json['gender']?.toString(),
memberType: json['memberType']?.toString(),
handicap: json['handicap'] != null ? int.tryParse(json['handicap'].toString()) : null,
status: json['status']?.toString(),
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt'].toString()) : null,
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt'].toString()) : null,
);
}
// Member 객체를 JSON으로 변환
Map<String, dynamic> toJson() {
return {
'id': id,
'userId': userId,
'clubId': clubId,
'name': name,
'email': email,
'role': role,
'profileImage': profileImage,
'phone': phone,
'address': address,
'joinDate': joinDate?.toIso8601String(),
'isActive': isActive,
'gender': gender,
'memberType': memberType,
'handicap': handicap,
'status': status,
'createdAt': createdAt?.toIso8601String(),
'updatedAt': updatedAt?.toIso8601String(),
};
}
// 객체 복사 및 특정 필드 업데이트를 위한 copyWith 메서드
Member copyWith({
String? id,
String? userId,
String? clubId,
String? name,
String? email,
String? role,
String? profileImage,
String? phone,
String? address,
DateTime? joinDate,
bool? isActive,
String? gender,
String? memberType,
int? handicap,
String? status,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return Member(
id: id ?? this.id,
userId: userId ?? this.userId,
clubId: clubId ?? this.clubId,
name: name ?? this.name,
email: email ?? this.email,
role: role ?? this.role,
profileImage: profileImage ?? this.profileImage,
phone: phone ?? this.phone,
address: address ?? this.address,
joinDate: joinDate ?? this.joinDate,
isActive: isActive ?? this.isActive,
gender: gender ?? this.gender,
memberType: memberType ?? this.memberType,
handicap: handicap ?? this.handicap,
status: status ?? this.status,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}
+83
View File
@@ -0,0 +1,83 @@
class Score {
final String id;
final String memberId;
final String eventId;
final String clubId;
final List<int> frames;
final int totalScore;
final DateTime date;
final String? notes;
Score({
required this.id,
required this.memberId,
required this.eventId,
required this.clubId,
required this.frames,
required this.totalScore,
required this.date,
this.notes,
});
factory Score.fromJson(Map<String, dynamic> json) {
return Score(
id: json['id']?.toString() ?? '',
memberId: json['memberId']?.toString() ?? '',
eventId: json['eventId']?.toString() ?? '',
clubId: json['clubId']?.toString() ?? '',
frames: json['frames'] != null ? List<int>.from(json['frames']) : [],
totalScore: json['totalScore'] ?? 0,
date: json['date'] != null ? DateTime.parse(json['date'].toString()) : DateTime.now(),
notes: json['notes']?.toString(),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'memberId': memberId,
'eventId': eventId,
'clubId': clubId,
'frames': frames,
'totalScore': totalScore,
'date': date.toIso8601String(),
'notes': notes,
};
}
}
class ScoreStatistics {
final double average;
final int highScore;
final int lowScore;
final int gamesPlayed;
final Map<String, dynamic>? additionalStats;
ScoreStatistics({
required this.average,
required this.highScore,
required this.lowScore,
required this.gamesPlayed,
this.additionalStats,
});
factory ScoreStatistics.fromJson(Map<String, dynamic> json) {
return ScoreStatistics(
average: json['average'] != null ? (json['average'] is int ? (json['average'] as int).toDouble() : json['average'].toDouble()) : 0.0,
highScore: json['highScore'] ?? 0,
lowScore: json['lowScore'] ?? 0,
gamesPlayed: json['gamesPlayed'] ?? 0,
additionalStats: json['additionalStats'],
);
}
Map<String, dynamic> toJson() {
return {
'average': average,
'highScore': highScore,
'lowScore': lowScore,
'gamesPlayed': gamesPlayed,
'additionalStats': additionalStats,
};
}
}
+243
View File
@@ -0,0 +1,243 @@
/// 구독 플랜 타입
enum SubscriptionPlanType {
free, // 무료 플랜
basic, // 기본 유료 플랜
premium, // 프리미엄 플랜
enterprise // 기업용 플랜
}
/// 구독 상태
enum SubscriptionStatus {
active, // 활성화 상태
canceled, // 취소됨 (아직 만료 안됨)
expired, // 만료됨
pending, // 결제 대기 중
failed // 결제 실패
}
/// 구독 모델
class Subscription {
final String id;
final String userId;
final String? clubId;
final SubscriptionPlanType planType;
final SubscriptionStatus status;
final DateTime startDate;
final DateTime endDate;
final double price;
final String currency;
final bool autoRenew;
final Map<String, dynamic>? features;
final String? paymentMethod;
final String? transactionId;
final DateTime createdAt;
final DateTime updatedAt;
Subscription({
required this.id,
required this.userId,
this.clubId,
required this.planType,
required this.status,
required this.startDate,
required this.endDate,
required this.price,
required this.currency,
required this.autoRenew,
this.features,
this.paymentMethod,
this.transactionId,
required this.createdAt,
required this.updatedAt,
});
/// JSON에서 구독 객체 생성
factory Subscription.fromJson(Map<String, dynamic> json) {
return Subscription(
id: json['id'],
userId: json['userId'],
clubId: json['clubId'],
planType: SubscriptionPlanType.values.firstWhere(
(e) => e.toString().split('.').last == json['planType'],
orElse: () => SubscriptionPlanType.free,
),
status: SubscriptionStatus.values.firstWhere(
(e) => e.toString().split('.').last == json['status'],
orElse: () => SubscriptionStatus.expired,
),
startDate: DateTime.parse(json['startDate']),
endDate: DateTime.parse(json['endDate']),
price: json['price'].toDouble(),
currency: json['currency'],
autoRenew: json['autoRenew'] ?? false,
features: json['features'],
paymentMethod: json['paymentMethod'],
transactionId: json['transactionId'],
createdAt: DateTime.parse(json['createdAt']),
updatedAt: DateTime.parse(json['updatedAt']),
);
}
/// 구독 객체를 JSON으로 변환
Map<String, dynamic> toJson() {
return {
'id': id,
'userId': userId,
'clubId': clubId,
'planType': planType.toString().split('.').last,
'status': status.toString().split('.').last,
'startDate': startDate.toIso8601String(),
'endDate': endDate.toIso8601String(),
'price': price,
'currency': currency,
'autoRenew': autoRenew,
'features': features,
'paymentMethod': paymentMethod,
'transactionId': transactionId,
'createdAt': createdAt.toIso8601String(),
'updatedAt': updatedAt.toIso8601String(),
};
}
/// 구독이 활성 상태인지 확인
bool get isActive => status == SubscriptionStatus.active;
/// 구독이 만료되었는지 확인
bool get isExpired => status == SubscriptionStatus.expired ||
DateTime.now().isAfter(endDate);
/// 구독 남은 일수 계산
int get daysLeft {
if (isExpired) return 0;
return endDate.difference(DateTime.now()).inDays;
}
/// 구독 플랜 이름 (한글)
String get planName {
switch (planType) {
case SubscriptionPlanType.free:
return '무료';
case SubscriptionPlanType.basic:
return '기본';
case SubscriptionPlanType.premium:
return '프리미엄';
case SubscriptionPlanType.enterprise:
return '기업용';
}
}
}
/// 구독 플랜 정보 모델
class SubscriptionPlan {
final SubscriptionPlanType type;
final String name;
final String description;
final double monthlyPrice;
final double yearlyPrice;
final String currency;
final List<String> features;
final int maxMembers;
final int maxEvents;
final bool hasAdvancedStats;
final bool hasCustomization;
final bool hasPriority;
SubscriptionPlan({
required this.type,
required this.name,
required this.description,
required this.monthlyPrice,
required this.yearlyPrice,
required this.currency,
required this.features,
required this.maxMembers,
required this.maxEvents,
required this.hasAdvancedStats,
required this.hasCustomization,
required this.hasPriority,
});
/// 기본 구독 플랜 목록 반환
static List<SubscriptionPlan> getDefaultPlans() {
return [
SubscriptionPlan(
type: SubscriptionPlanType.free,
name: '무료',
description: '소규모 클럽을 위한 기본 기능',
monthlyPrice: 0,
yearlyPrice: 0,
currency: 'KRW',
features: [
'최대 10명 회원 관리',
'기본 점수 기록',
'간단한 통계',
],
maxMembers: 10,
maxEvents: 5,
hasAdvancedStats: false,
hasCustomization: false,
hasPriority: false,
),
SubscriptionPlan(
type: SubscriptionPlanType.basic,
name: '기본',
description: '중소규모 클럽을 위한 확장 기능',
monthlyPrice: 9900,
yearlyPrice: 99000,
currency: 'KRW',
features: [
'최대 30명 회원 관리',
'상세 점수 분석',
'이벤트 관리',
'회원 통계',
],
maxMembers: 30,
maxEvents: 20,
hasAdvancedStats: true,
hasCustomization: false,
hasPriority: false,
),
SubscriptionPlan(
type: SubscriptionPlanType.premium,
name: '프리미엄',
description: '대규모 클럽을 위한 고급 기능',
monthlyPrice: 19900,
yearlyPrice: 199000,
currency: 'KRW',
features: [
'무제한 회원 관리',
'고급 통계 및 분석',
'무제한 이벤트',
'맞춤형 보고서',
'우선 지원',
],
maxMembers: 100,
maxEvents: 100,
hasAdvancedStats: true,
hasCustomization: true,
hasPriority: true,
),
SubscriptionPlan(
type: SubscriptionPlanType.enterprise,
name: '기업용',
description: '프로 클럽 및 단체를 위한 맞춤형 솔루션',
monthlyPrice: 49900,
yearlyPrice: 499000,
currency: 'KRW',
features: [
'무제한 회원 및 이벤트',
'전문가 수준 분석',
'맞춤형 기능',
'전담 지원',
'API 액세스',
],
maxMembers: 1000,
maxEvents: 1000,
hasAdvancedStats: true,
hasCustomization: true,
hasPriority: true,
),
];
}
}
+45
View File
@@ -0,0 +1,45 @@
class User {
final String id;
final String email;
final String name;
final String role;
final String? profileImage;
final String? clubId;
final String? clubRole;
User({
required this.id,
required this.email,
required this.name,
required this.role,
this.profileImage,
this.clubId,
this.clubRole,
});
// JSON 데이터로부터 User 객체 생성
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] != null ? json['id'].toString() : '',
email: json['email'] ?? '',
name: json['name'] ?? '',
role: json['role'] ?? 'user',
profileImage: json['profileImage'],
clubId: json['clubId']?.toString(),
clubRole: json['clubRole'],
);
}
// User 객체를 JSON으로 변환
Map<String, dynamic> toJson() {
return {
'id': id,
'email': email,
'name': name,
'role': role,
'profileImage': profileImage,
'clubId': clubId,
'clubRole': clubRole,
};
}
}