/// 用户 Domain 实体 /// /// 全局共享实体,被 auth / chat / contact 等多个 Feature 共用。 /// 纯 Dart 类,零 Flutter / 零网络 / 零 DB 依赖。 /// /// ## 数据流 /// ``` /// 服务端 JSON /// → User.fromJson() ← 直接从网络创建 /// → ★ User ★ ← 你在这里 /// → userRepo.saveUser(user) ← 可选持久化 /// → ViewModel.state /// → View 渲染 /// ``` class User { final int uid; final String? uuid; final int? lastOnline; final String? profilePic; final String? profilePicGaussian; final String? nickname; final String? depositName; final int? hasSetDepositName; final String? contact; final String? countryCode; final String? username; final int? role; final int? relationship; final int? friendStatus; final String? bio; final String? userAlias; final int? requestAt; final int? deletedAt; final String? email; final String? recoveryEmail; final String? remark; final String? source; final int? addIndex; final int? incomingSoundId; final int? outgoingSoundId; final int? notificationSoundId; final int? sendMessageSoundId; final int? groupNotificationSoundId; final String? groupTags; final String? friendTags; final String? publicKey; final int? configBits; final String? hint; /// 标记此用户数据是否为部分数据,需要 upsert 而非全字段覆盖。 /// /// 由响应解析层设置,Repository 据此决定写入策略: /// - true → [UserRepository.upsertUser],仅更新非 null 字段,保留本地已有数据 /// - false → [UserRepository.insertOrReplaceUser],全字段覆盖(默认) /// /// 注意:此字段仅用于内存传递,不会被持久化到 DB。 final bool requireUpsert; /// TODO(contacts): 添加 localName / localPhoneNumbers,关联本地通讯录。 /// 这两个字段是设备侧数据,不应持久化到服务端 payload。 /// /// TODO(history): status 和 created_at 仅出现在历史用户记录中(如审计日志)。 /// 建议用独立的 UserHistory 实体承载,避免污染此 Domain 模型。 /// /// TODO(online): objectMgr.onlineMgr.updateOnlineTime() 原先在 fromJson() 内 /// 作为副作用调用。不要在此处复现——副作用应在 Repository 或 UseCase 层处理。 const User({ required this.uid, this.uuid, this.lastOnline, this.profilePic, this.profilePicGaussian, this.nickname, this.depositName, this.hasSetDepositName, this.contact, this.countryCode, this.username, this.role, this.relationship, this.friendStatus, this.bio, this.userAlias, this.requestAt, this.deletedAt, this.email, this.recoveryEmail, this.remark, this.source, this.addIndex, this.incomingSoundId, this.outgoingSoundId, this.notificationSoundId, this.sendMessageSoundId, this.groupNotificationSoundId, this.groupTags, this.friendTags, this.publicKey, this.configBits, this.hint, this.requireUpsert = false, }); /// 直接从网络 JSON 创建 Domain 实体。 /// /// [requireUpsert] 默认 false,如响应解析层判断为部分数据, /// 可在调用后通过 copyWith(requireUpsert: true) 标记。 factory User.fromJson(Map json) => User( uid: json['uid'] as int, uuid: json['uuid'], lastOnline: json['last_online'], profilePic: json['profile_pic'], profilePicGaussian: json['profile_pic_gaussian'], nickname: json['nickname'], depositName: json['deposit_name'], hasSetDepositName: json['has_set_deposit_name'], contact: json['contact'], countryCode: json['country_code'], username: json['username'], role: json['role'], relationship: json['relationship'], friendStatus: json['friend_status'], bio: json['bio'], userAlias: json['user_alias'], requestAt: json['request_at'], deletedAt: json['deleted_at'], email: json['email'], recoveryEmail: json['recovery_email'], remark: json['remark'], source: json['source'], addIndex: json['__add_index'], incomingSoundId: json['incoming_sound_id'], outgoingSoundId: json['outgoing_sound_id'], notificationSoundId: json['notification_sound_id'], sendMessageSoundId: json['send_message_sound_id'], groupNotificationSoundId: json['group_notification_sound_id'], groupTags: json['group_tags'], friendTags: json['friend_tags'], publicKey: json['public_key'], configBits: json['config_bits'], hint: json['hint'], ); /// 仅更新部分字段,其余保持不变。 /// /// 注意:[requireUpsert] 不会随其他字段自动继承, /// 需要显式传入以避免意外的写入策略变更。 User copyWith({ int? uid, String? uuid, int? lastOnline, String? profilePic, String? profilePicGaussian, String? nickname, String? depositName, int? hasSetDepositName, String? contact, String? countryCode, String? username, int? role, int? relationship, int? friendStatus, String? bio, String? userAlias, int? requestAt, int? deletedAt, String? email, String? recoveryEmail, String? remark, String? source, int? addIndex, int? incomingSoundId, int? outgoingSoundId, int? notificationSoundId, int? sendMessageSoundId, int? groupNotificationSoundId, String? groupTags, String? friendTags, String? publicKey, int? configBits, String? hint, bool? requireUpsert, }) { return User( uid: uid ?? this.uid, uuid: uuid ?? this.uuid, lastOnline: lastOnline ?? this.lastOnline, profilePic: profilePic ?? this.profilePic, profilePicGaussian: profilePicGaussian ?? this.profilePicGaussian, nickname: nickname ?? this.nickname, depositName: depositName ?? this.depositName, hasSetDepositName: hasSetDepositName ?? this.hasSetDepositName, contact: contact ?? this.contact, countryCode: countryCode ?? this.countryCode, username: username ?? this.username, role: role ?? this.role, relationship: relationship ?? this.relationship, friendStatus: friendStatus ?? this.friendStatus, bio: bio ?? this.bio, userAlias: userAlias ?? this.userAlias, requestAt: requestAt ?? this.requestAt, deletedAt: deletedAt ?? this.deletedAt, email: email ?? this.email, recoveryEmail: recoveryEmail ?? this.recoveryEmail, remark: remark ?? this.remark, source: source ?? this.source, addIndex: addIndex ?? this.addIndex, incomingSoundId: incomingSoundId ?? this.incomingSoundId, outgoingSoundId: outgoingSoundId ?? this.outgoingSoundId, notificationSoundId: notificationSoundId ?? this.notificationSoundId, sendMessageSoundId: sendMessageSoundId ?? this.sendMessageSoundId, groupNotificationSoundId: groupNotificationSoundId ?? this.groupNotificationSoundId, groupTags: groupTags ?? this.groupTags, friendTags: friendTags ?? this.friendTags, publicKey: publicKey ?? this.publicKey, configBits: configBits ?? this.configBits, hint: hint ?? this.hint, requireUpsert: requireUpsert ?? this.requireUpsert, ); } }