Files
app/lib/data/models/user_setting_model.dart
2026-05-15 09:03:09 +08:00

347 lines
9.7 KiB
Dart

/// 用户设置模型 - 对应 GET /user/setting 响应
class UserSettingModel {
final DateTime? groupExpires;
final List<OpenIdProvider> openId;
final bool versionRetentionEnabled;
final List<String>? versionRetentionExt;
final int versionRetentionMax;
final bool passwordless;
final bool twoFaEnabled;
final List<PasskeyModel> passkeys;
final List<LoginActivity> loginActivity;
final List<StoragePack> storagePacks;
final int credit;
final bool disableViewSync;
final String shareLinksInProfile;
final List<OAuthGrant> oauthGrants;
UserSettingModel({
this.groupExpires,
this.openId = const [],
this.versionRetentionEnabled = false,
this.versionRetentionExt,
this.versionRetentionMax = 0,
this.passwordless = false,
this.twoFaEnabled = false,
this.passkeys = const [],
this.loginActivity = const [],
this.storagePacks = const [],
this.credit = 0,
this.disableViewSync = false,
this.shareLinksInProfile = '',
this.oauthGrants = const [],
});
factory UserSettingModel.fromJson(Map<String, dynamic> json) {
return UserSettingModel(
groupExpires: json['group_expires'] != null
? DateTime.parse(json['group_expires'] as String)
: null,
openId: (json['open_id'] as List?)
?.map((e) => OpenIdProvider.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
versionRetentionEnabled: json['version_retention_enabled'] as bool? ?? false,
versionRetentionExt: (json['version_retention_ext'] as List?)
?.map((e) => e as String)
.toList(),
versionRetentionMax: json['version_retention_max'] as int? ?? 0,
passwordless: json['passwordless'] as bool? ?? false,
twoFaEnabled: json['two_fa_enabled'] as bool? ?? false,
passkeys: (json['passkeys'] as List?)
?.map((e) => PasskeyModel.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
loginActivity: (json['login_activity'] as List?)
?.map((e) => LoginActivity.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
storagePacks: (json['storage_packs'] as List?)
?.map((e) => StoragePack.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
credit: json['credit'] as int? ?? 0,
disableViewSync: json['disable_view_sync'] as bool? ?? false,
shareLinksInProfile: json['share_links_in_profile'] as String? ?? '',
oauthGrants: (json['oauth_grants'] as List?)
?.map((e) => OAuthGrant.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
);
}
UserSettingModel copyWith({
DateTime? groupExpires,
List<OpenIdProvider>? openId,
bool? versionRetentionEnabled,
List<String>? versionRetentionExt,
int? versionRetentionMax,
bool? passwordless,
bool? twoFaEnabled,
List<PasskeyModel>? passkeys,
List<LoginActivity>? loginActivity,
List<StoragePack>? storagePacks,
int? credit,
bool? disableViewSync,
String? shareLinksInProfile,
List<OAuthGrant>? oauthGrants,
}) {
return UserSettingModel(
groupExpires: groupExpires ?? this.groupExpires,
openId: openId ?? this.openId,
versionRetentionEnabled: versionRetentionEnabled ?? this.versionRetentionEnabled,
versionRetentionExt: versionRetentionExt ?? this.versionRetentionExt,
versionRetentionMax: versionRetentionMax ?? this.versionRetentionMax,
passwordless: passwordless ?? this.passwordless,
twoFaEnabled: twoFaEnabled ?? this.twoFaEnabled,
passkeys: passkeys ?? this.passkeys,
loginActivity: loginActivity ?? this.loginActivity,
storagePacks: storagePacks ?? this.storagePacks,
credit: credit ?? this.credit,
disableViewSync: disableViewSync ?? this.disableViewSync,
shareLinksInProfile: shareLinksInProfile ?? this.shareLinksInProfile,
oauthGrants: oauthGrants ?? this.oauthGrants,
);
}
}
/// 已关联的外部身份提供商
class OpenIdProvider {
final int provider;
final DateTime linkedAt;
OpenIdProvider({required this.provider, required this.linkedAt});
factory OpenIdProvider.fromJson(Map<String, dynamic> json) {
return OpenIdProvider(
provider: json['provider'] as int,
linkedAt: DateTime.parse(json['linked_at'] as String),
);
}
String get providerName {
switch (provider) {
case 0:
return 'Logto';
case 1:
return 'QQ';
case 2:
return 'OIDC';
default:
return 'Unknown';
}
}
}
/// Passkey 模型
class PasskeyModel {
final String id;
final String name;
final DateTime? usedAt;
final DateTime createdAt;
PasskeyModel({
required this.id,
required this.name,
this.usedAt,
required this.createdAt,
});
factory PasskeyModel.fromJson(Map<String, dynamic> json) {
return PasskeyModel(
id: json['id'] as String,
name: json['name'] as String,
usedAt: json['used_at'] != null
? DateTime.parse(json['used_at'] as String)
: null,
createdAt: DateTime.parse(json['created_at'] as String),
);
}
}
/// 登录活动记录
class LoginActivity {
final DateTime createdAt;
final String ip;
final String browser;
final String device;
final String os;
final String loginWith;
final int? openIdProvider;
final bool success;
final bool webdav;
LoginActivity({
required this.createdAt,
required this.ip,
required this.browser,
required this.device,
required this.os,
required this.loginWith,
this.openIdProvider,
required this.success,
required this.webdav,
});
factory LoginActivity.fromJson(Map<String, dynamic> json) {
return LoginActivity(
createdAt: DateTime.parse(json['created_at'] as String),
ip: json['ip'] as String,
browser: json['browser'] as String,
device: json['device'] as String,
os: json['os'] as String,
loginWith: json['login_with'] as String? ?? '',
openIdProvider: json['open_id_provider'] as int?,
success: json['success'] as bool? ?? false,
webdav: json['webdav'] as bool? ?? false,
);
}
String get loginMethodName {
if (webdav) return 'WebDAV';
switch (loginWith) {
case 'passkey':
return 'Passkey';
case 'openid':
return '第三方登录';
default:
return '密码';
}
}
}
/// 存储包
class StoragePack {
final String name;
final DateTime activeSince;
final DateTime? expireAt;
final int size;
StoragePack({
required this.name,
required this.activeSince,
this.expireAt,
required this.size,
});
factory StoragePack.fromJson(Map<String, dynamic> json) {
return StoragePack(
name: json['name'] as String,
activeSince: DateTime.parse(json['active_since'] as String),
expireAt: json['expire_at'] != null
? DateTime.parse(json['expire_at'] as String)
: null,
size: json['size'] as int,
);
}
bool get isExpired => expireAt != null && DateTime.now().isAfter(expireAt!);
}
/// OAuth 授权应用
class OAuthGrant {
final String clientId;
final String clientName;
final String? clientLogo;
final List<String> scopes;
final DateTime? lastUsedAt;
OAuthGrant({
required this.clientId,
required this.clientName,
this.clientLogo,
this.scopes = const [],
this.lastUsedAt,
});
factory OAuthGrant.fromJson(Map<String, dynamic> json) {
return OAuthGrant(
clientId: json['client_id'] as String,
clientName: json['client_name'] as String,
clientLogo: json['client_logo'] as String?,
scopes: (json['scopes'] as List?)?.map((e) => e as String).toList() ?? [],
lastUsedAt: json['last_used_at'] != null
? DateTime.parse(json['last_used_at'] as String)
: null,
);
}
}
/// 用户容量模型(扩展版,含存储包)
class UserCapacityModel {
final int total;
final int used;
final int storagePackTotal;
UserCapacityModel({
required this.total,
required this.used,
this.storagePackTotal = 0,
});
factory UserCapacityModel.fromJson(Map<String, dynamic> json) {
return UserCapacityModel(
total: json['total'] as int,
used: json['used'] as int,
storagePackTotal: json['storage_pack_total'] as int? ?? 0,
);
}
double get usagePercentage => total > 0 ? (used / total) * 100 : 0;
int get remaining => total - used;
}
/// 积分变动记录
class CreditChange {
final DateTime changedAt;
final int diff;
final String reason;
CreditChange({
required this.changedAt,
required this.diff,
required this.reason,
});
factory CreditChange.fromJson(Map<String, dynamic> json) {
return CreditChange(
changedAt: DateTime.parse(json['changed_at'] as String),
diff: json['diff'] as int,
reason: json['reason'] as String,
);
}
String get reasonLabel {
switch (reason) {
case 'share_purchased':
return '分享被购买';
case 'pay':
return '支付';
case 'adjust':
return '管理员调整';
default:
return reason;
}
}
}
/// 积分变动列表响应
class CreditChangeList {
final List<CreditChange> changes;
final String? nextToken;
CreditChangeList({required this.changes, this.nextToken});
factory CreditChangeList.fromJson(Map<String, dynamic> json) {
return CreditChangeList(
changes: (json['changes'] as List?)
?.map((e) => CreditChange.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
nextToken: (json['pagination'] as Map<String, dynamic>?)?['next_token'] as String?,
);
}
bool get hasMore => nextToken != null && nextToken!.isNotEmpty;
}