业务更新User所需(企业成员、聊天室群组成员)
This commit is contained in:
@@ -1,122 +0,0 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:im_app/data/local/drift/app_database.dart';
|
||||
import 'package:im_app/domain/entities/call_log.dart';
|
||||
|
||||
/// 通话记录 DTO(Data Transfer Object)
|
||||
///
|
||||
/// local / remote 共用的数据传输对象。
|
||||
/// 提供与 Domain Entity [CallLog] 之间的双向转换。
|
||||
class CallLogDto {
|
||||
final String id;
|
||||
final int? callerId;
|
||||
final int? receiverId;
|
||||
final int? chatId;
|
||||
final int? duration;
|
||||
final int? videoCall;
|
||||
final int? createdAt;
|
||||
final int? updatedAt;
|
||||
final int? endedAt;
|
||||
final int? status;
|
||||
final int? isDeleted;
|
||||
final int? deletedAt;
|
||||
final int? isRead;
|
||||
|
||||
const CallLogDto({
|
||||
required this.id,
|
||||
this.callerId,
|
||||
this.receiverId,
|
||||
this.chatId,
|
||||
this.duration,
|
||||
this.videoCall,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.endedAt,
|
||||
this.status,
|
||||
this.isDeleted,
|
||||
this.deletedAt,
|
||||
this.isRead,
|
||||
});
|
||||
|
||||
factory CallLogDto.fromJson(Map<String, dynamic> json) => CallLogDto(
|
||||
id: json['id'] as String,
|
||||
callerId: json['caller_id'],
|
||||
receiverId: json['receiver_id'],
|
||||
chatId: json['chat_id'],
|
||||
duration: json['duration'],
|
||||
videoCall: json['video_call'],
|
||||
createdAt: json['created_at'],
|
||||
updatedAt: json['updated_at'],
|
||||
endedAt: json['ended_at'],
|
||||
status: json['status'],
|
||||
isDeleted: json['is_deleted'],
|
||||
deletedAt: json['deleted_at'],
|
||||
isRead: json['is_read'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'caller_id': callerId,
|
||||
'receiver_id': receiverId,
|
||||
'chat_id': chatId,
|
||||
'duration': duration,
|
||||
'video_call': videoCall,
|
||||
'created_at': createdAt,
|
||||
'updated_at': updatedAt,
|
||||
'ended_at': endedAt,
|
||||
'status': status,
|
||||
'is_deleted': isDeleted,
|
||||
'deleted_at': deletedAt,
|
||||
'is_read': isRead,
|
||||
};
|
||||
|
||||
/// DTO → Domain Entity
|
||||
CallLog toEntity() => CallLog(
|
||||
id: id,
|
||||
callerId: callerId,
|
||||
receiverId: receiverId,
|
||||
chatId: chatId,
|
||||
duration: duration,
|
||||
videoCall: videoCall,
|
||||
createdAt: createdAt,
|
||||
updatedAt: updatedAt,
|
||||
endedAt: endedAt,
|
||||
status: status,
|
||||
isDeleted: isDeleted,
|
||||
deletedAt: deletedAt,
|
||||
isRead: isRead,
|
||||
);
|
||||
|
||||
/// Domain Entity → DTO
|
||||
factory CallLogDto.fromEntity(CallLog callLog) => CallLogDto(
|
||||
id: callLog.id,
|
||||
callerId: callLog.callerId,
|
||||
receiverId: callLog.receiverId,
|
||||
chatId: callLog.chatId,
|
||||
duration: callLog.duration,
|
||||
videoCall: callLog.videoCall,
|
||||
createdAt: callLog.createdAt,
|
||||
updatedAt: callLog.updatedAt,
|
||||
endedAt: callLog.endedAt,
|
||||
status: callLog.status,
|
||||
isDeleted: callLog.isDeleted,
|
||||
deletedAt: callLog.deletedAt,
|
||||
isRead: callLog.isRead,
|
||||
);
|
||||
|
||||
/// DTO → Drift Companion (for DB insert/update)
|
||||
CallLogsCompanion toCompanion() => CallLogsCompanion(
|
||||
id: Value(id),
|
||||
callerId: Value(callerId),
|
||||
receiverId: Value(receiverId),
|
||||
chatId: Value(chatId),
|
||||
duration: Value(duration),
|
||||
videoCall: Value(videoCall),
|
||||
createdAt: Value(createdAt),
|
||||
updatedAt: Value(updatedAt),
|
||||
endedAt: Value(endedAt),
|
||||
status: Value(status),
|
||||
isDeleted: Value(isDeleted),
|
||||
deletedAt: Value(deletedAt),
|
||||
isRead: Value(isRead),
|
||||
);
|
||||
}
|
||||
165
apps/im_app/lib/data/repositories/call_log_repository_impl.dart
Normal file
165
apps/im_app/lib/data/repositories/call_log_repository_impl.dart
Normal file
@@ -0,0 +1,165 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:im_app/data/local/drift/app_database.dart';
|
||||
import 'package:im_app/domain/entities/call_log.dart';
|
||||
import 'package:im_app/domain/repositories/call_log_repository.dart';
|
||||
import 'package:storage_sdk/storage_sdk.dart';
|
||||
|
||||
/// 通话记录仓储实现
|
||||
///
|
||||
/// ## 职责
|
||||
/// - 所有 DB 操作通过 [StorageSdkApi],不直接接触 AppDatabase
|
||||
/// - DriftCallLog ↔ Domain CallLog 映射
|
||||
/// - 所有公开接口只接受 Domain 实体,Companion 转换完全内聚在此类
|
||||
///
|
||||
/// ## 数据流
|
||||
/// ```
|
||||
/// 网络:CallLog.fromJson(json) → insertOrReplaceCallLog(callLog) → StorageSdkApi → DB
|
||||
/// 监听:StorageSdkApi.watchAll → DriftCallLog → _toEntity() → UI
|
||||
/// ```
|
||||
class CallLogRepositoryImpl implements CallLogRepository {
|
||||
final StorageSdkApi _storage;
|
||||
|
||||
CallLogRepositoryImpl(this._storage);
|
||||
|
||||
// ── DB row → Domain ──────────────────────────────────────────────────────
|
||||
|
||||
CallLog _toEntity(DriftCallLog row) => CallLog(
|
||||
id: row.id,
|
||||
callerId: row.callerId,
|
||||
receiverId: row.receiverId,
|
||||
chatId: row.chatId,
|
||||
duration: row.duration,
|
||||
videoCall: row.videoCall,
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
endedAt: row.endedAt,
|
||||
status: row.status,
|
||||
isDeleted: row.isDeleted,
|
||||
deletedAt: row.deletedAt,
|
||||
isRead: row.isRead,
|
||||
);
|
||||
|
||||
// ── Domain → DB companion (internal only) ────────────────────────────────
|
||||
|
||||
CallLogsCompanion _toCompanion(CallLog callLog) => CallLogsCompanion(
|
||||
id: Value(callLog.id),
|
||||
callerId: Value(callLog.callerId),
|
||||
receiverId: Value(callLog.receiverId),
|
||||
chatId: Value(callLog.chatId),
|
||||
duration: Value(callLog.duration),
|
||||
videoCall: Value(callLog.videoCall),
|
||||
createdAt: Value(callLog.createdAt),
|
||||
updatedAt: Value(callLog.updatedAt),
|
||||
endedAt: Value(callLog.endedAt),
|
||||
status: Value(callLog.status),
|
||||
isDeleted: Value(callLog.isDeleted),
|
||||
deletedAt: Value(callLog.deletedAt),
|
||||
isRead: Value(callLog.isRead),
|
||||
);
|
||||
|
||||
// ── 监听 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
Stream<List<CallLog>> watchAllCallLogs() {
|
||||
return _storage.watchAll<DriftCallLog>().map(
|
||||
(rows) => rows.map(_toEntity).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<CallLog?> watchCallLog(String id) {
|
||||
return _storage
|
||||
.watchFirst<DriftCallLog, $CallLogsTable>((t) => t.id.equals(id))
|
||||
.map((row) => row != null ? _toEntity(row) : null);
|
||||
}
|
||||
|
||||
// ── 读取 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
Future<List<CallLog>> getCallLogs() async {
|
||||
final rows = await _storage.rawQuery(
|
||||
'SELECT * FROM call_log ORDER BY updated_at DESC',
|
||||
[],
|
||||
);
|
||||
return rows
|
||||
.map(
|
||||
(row) => CallLog(
|
||||
id: row.read<String>('id'),
|
||||
callerId: row.readNullable<int>('caller_id'),
|
||||
receiverId: row.readNullable<int>('receiver_id'),
|
||||
chatId: row.readNullable<int>('chat_id'),
|
||||
duration: row.readNullable<int>('duration'),
|
||||
videoCall: row.readNullable<int>('video_call'),
|
||||
createdAt: row.readNullable<int>('created_at'),
|
||||
updatedAt: row.readNullable<int>('updated_at'),
|
||||
endedAt: row.readNullable<int>('ended_at'),
|
||||
status: row.readNullable<int>('status'),
|
||||
isDeleted: row.readNullable<int>('is_deleted'),
|
||||
deletedAt: row.readNullable<int>('deleted_at'),
|
||||
isRead: row.readNullable<int>('is_read'),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CallLog?> getCallLog(String id) async {
|
||||
final row = await _storage.selectFirst<DriftCallLog, $CallLogsTable>(
|
||||
(t) => t.id.equals(id),
|
||||
);
|
||||
return row != null ? _toEntity(row) : null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isExist(String id) async {
|
||||
final row = await _storage.selectFirst<DriftCallLog, $CallLogsTable>(
|
||||
(t) => t.id.equals(id),
|
||||
);
|
||||
return row != null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getUnreadCount(int currentUid) async {
|
||||
final result = await _storage.rawQuery(
|
||||
'''
|
||||
SELECT COUNT(*) as count FROM call_log
|
||||
WHERE is_read = 0
|
||||
AND caller_id != ?
|
||||
AND (status = 3 OR status = 4 OR status = 5 OR status = 6)
|
||||
''',
|
||||
[currentUid],
|
||||
);
|
||||
if (result.isNotEmpty) {
|
||||
return result.first.read<int>('count');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ── 写入 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
Future<void> insertOrReplaceCallLog(CallLog callLog) async {
|
||||
await _storage.insertOrReplace<DriftCallLog>(_toCompanion(callLog));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> insertOrReplaceCallLogs(List<CallLog> callLogs) async {
|
||||
await _storage.batchInsertOrReplace<DriftCallLog>(
|
||||
callLogs.map(_toCompanion).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> markAllAsRead() async {
|
||||
await _storage.rawQuery('UPDATE call_log SET is_read = 1', []);
|
||||
}
|
||||
|
||||
// ── 删除 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
Future<void> deleteCallLog(String id) async {
|
||||
await _storage.deleteWhere<DriftCallLog, $CallLogsTable>(
|
||||
(t) => t.id.equals(id),
|
||||
);
|
||||
}
|
||||
}
|
||||
179
apps/im_app/lib/data/repositories/chat_bot_repository_impl.dart
Normal file
179
apps/im_app/lib/data/repositories/chat_bot_repository_impl.dart
Normal file
@@ -0,0 +1,179 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:im_app/data/local/drift/app_database.dart';
|
||||
import 'package:im_app/domain/entities/chat_bot.dart';
|
||||
import 'package:im_app/domain/repositories/chat_bot_repository.dart';
|
||||
import 'package:storage_sdk/storage_sdk.dart';
|
||||
|
||||
/// 聊天机器人仓储实现
|
||||
///
|
||||
/// ## 职责
|
||||
/// - 所有 DB 操作通过 [StorageSdkApi],不直接接触 AppDatabase
|
||||
/// - DriftChatBot ↔ Domain ChatBot 映射
|
||||
/// - 所有公开接口只接受 Domain 实体,Companion 转换完全内聚在此类
|
||||
///
|
||||
/// ## 数据流
|
||||
/// ```
|
||||
/// 网络:ChatBot.fromJson(json) → insertOrReplaceChatBot(chatBot) → StorageSdkApi → DB
|
||||
/// 监听:StorageSdkApi.watchAll → DriftChatBot → _toEntity() → UI
|
||||
/// ```
|
||||
class ChatBotRepositoryImpl implements ChatBotRepository {
|
||||
final StorageSdkApi _storage;
|
||||
|
||||
ChatBotRepositoryImpl(this._storage);
|
||||
|
||||
// ── DB row → Domain ──────────────────────────────────────────────────────
|
||||
|
||||
ChatBot _toEntity(DriftChatBot row) => ChatBot(
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
username: row.username,
|
||||
botUserId: row.botUserId,
|
||||
icon: row.icon,
|
||||
iconGaussian: row.iconGaussian,
|
||||
description: row.description,
|
||||
token: row.token,
|
||||
flag: row.flag,
|
||||
status: row.status,
|
||||
webhook: row.webhook,
|
||||
commands: row.commands,
|
||||
banner: row.banner,
|
||||
channelId: row.channelId,
|
||||
channelGroupId: row.channelGroupId,
|
||||
deletedAt: row.deletedAt,
|
||||
internalWebhook: row.internalWebhook,
|
||||
mode: row.mode,
|
||||
redirectUrl: row.redirectUrl,
|
||||
isInvitable: row.isInvitable,
|
||||
isAllowForward: row.isAllowForward,
|
||||
tips: row.tips,
|
||||
);
|
||||
|
||||
// ── Domain → DB companion (internal only) ────────────────────────────────
|
||||
|
||||
ChatBotsCompanion _toCompanion(ChatBot chatBot) => ChatBotsCompanion(
|
||||
id: Value(chatBot.id),
|
||||
name: Value(chatBot.name),
|
||||
username: Value(chatBot.username),
|
||||
botUserId: Value(chatBot.botUserId),
|
||||
icon: Value(chatBot.icon),
|
||||
iconGaussian: Value(chatBot.iconGaussian),
|
||||
description: Value(chatBot.description),
|
||||
token: Value(chatBot.token),
|
||||
flag: Value(chatBot.flag),
|
||||
status: Value(chatBot.status),
|
||||
webhook: Value(chatBot.webhook ?? ''),
|
||||
commands: Value(chatBot.commands ?? '[]'),
|
||||
banner: Value(chatBot.banner),
|
||||
channelId: Value(chatBot.channelId),
|
||||
channelGroupId: Value(chatBot.channelGroupId),
|
||||
deletedAt: Value(chatBot.deletedAt),
|
||||
internalWebhook: Value(chatBot.internalWebhook),
|
||||
mode: Value(chatBot.mode),
|
||||
redirectUrl: Value(chatBot.redirectUrl),
|
||||
isInvitable: Value(chatBot.isInvitable),
|
||||
isAllowForward: Value(chatBot.isAllowForward),
|
||||
tips: Value(chatBot.tips),
|
||||
);
|
||||
|
||||
// ── 监听 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
Stream<List<ChatBot>> watchAllChatBots() {
|
||||
return _storage.watchAll<DriftChatBot>().map(
|
||||
(rows) => rows.map(_toEntity).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<ChatBot?> watchChatBot(int id) {
|
||||
return _storage
|
||||
.watchFirst<DriftChatBot, $ChatBotsTable>((t) => t.id.equals(id))
|
||||
.map((row) => row != null ? _toEntity(row) : null);
|
||||
}
|
||||
|
||||
// ── 读取 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
Future<List<ChatBot>> getChatBots({int? limit}) async {
|
||||
if (limit != null) {
|
||||
final rows = await _storage.rawQuery('SELECT * FROM chat_bot LIMIT ?', [
|
||||
limit,
|
||||
]);
|
||||
return rows
|
||||
.map(
|
||||
(row) => ChatBot(
|
||||
id: row.read<int>('id'),
|
||||
name: row.readNullable<String>('name'),
|
||||
username: row.readNullable<String>('username'),
|
||||
botUserId: row.readNullable<int>('bot_user_id'),
|
||||
icon: row.readNullable<String>('icon'),
|
||||
iconGaussian: row.readNullable<String>('icon_gaussian'),
|
||||
description: row.readNullable<String>('description'),
|
||||
token: row.readNullable<String>('token'),
|
||||
flag: row.readNullable<int>('flag'),
|
||||
status: row.readNullable<int>('status'),
|
||||
webhook: row.readNullable<String>('webhook'),
|
||||
commands: row.readNullable<String>('commands'),
|
||||
banner: row.readNullable<String>('banner'),
|
||||
channelId: row.readNullable<int>('channel_id'),
|
||||
channelGroupId: row.readNullable<int>('channel_group_id'),
|
||||
deletedAt: row.readNullable<int>('deleted_at'),
|
||||
internalWebhook: row.readNullable<String>('internal_webhook'),
|
||||
mode: row.readNullable<int>('mode'),
|
||||
redirectUrl: row.readNullable<String>('redirect_url'),
|
||||
isInvitable: row.readNullable<int>('is_invitable'),
|
||||
isAllowForward: row.readNullable<int>('is_allow_forward'),
|
||||
tips: row.readNullable<String>('tips'),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
final rows = await _storage.selectAll<DriftChatBot>();
|
||||
return rows.map(_toEntity).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ChatBot?> getChatBot(int id) async {
|
||||
final row = await _storage.selectFirst<DriftChatBot, $ChatBotsTable>(
|
||||
(t) => t.id.equals(id),
|
||||
);
|
||||
return row != null ? _toEntity(row) : null;
|
||||
}
|
||||
|
||||
// ── 写入 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
Future<void> insertOrReplaceChatBot(ChatBot chatBot) async {
|
||||
await _storage.insertOrReplace<DriftChatBot>(_toCompanion(chatBot));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> insertOrReplaceChatBots(List<ChatBot> chatBots) async {
|
||||
await _storage.batchInsertOrReplace<DriftChatBot>(
|
||||
chatBots.map(_toCompanion).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateChatBot(ChatBot chatBot) async {
|
||||
await _storage.updateWhere<DriftChatBot, $ChatBotsTable>(
|
||||
_toCompanion(chatBot),
|
||||
(t) => t.id.equals(chatBot.id),
|
||||
);
|
||||
}
|
||||
|
||||
// ── 删除 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
Future<void> deleteChatBot(int id) async {
|
||||
await _storage.deleteWhere<DriftChatBot, $ChatBotsTable>(
|
||||
(t) => t.id.equals(id),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clearChatBots() async {
|
||||
await _storage.deleteAll<DriftChatBot>();
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,13 @@ import 'package:storage_sdk/storage_sdk.dart';
|
||||
/// ## 职责
|
||||
/// - 所有 DB 操作通过 [StorageSdkApi],不直接接触 AppDatabase
|
||||
/// - DriftUser ↔ Domain User 映射
|
||||
/// - 持久化决策由调用方决定
|
||||
/// - 所有公开接口只接受 Domain 实体,Companion 转换完全内聚在此类
|
||||
///
|
||||
/// ## 数据流
|
||||
/// ```
|
||||
/// 网络:User.fromJson(json) → (可选) saveUser(user) → StorageSdkApi → DB
|
||||
/// 网络:User.fromJson(json) → (可选) insertOrReplaceUser(user) → StorageSdkApi → DB
|
||||
/// 监听:StorageSdkApi.watchWhere/watchAll → DriftUser → _toEntity() → UI
|
||||
/// 部分更新:updateFields(uid, UsersCompanion) → StorageSdkApi.updateWhere
|
||||
/// 更新:updateUser(user) / updateUsersBatch(users) → _toCompanion() → DB
|
||||
/// ```
|
||||
class UserRepositoryImpl implements UserRepository {
|
||||
final StorageSdkApi _storage;
|
||||
@@ -60,7 +60,7 @@ class UserRepositoryImpl implements UserRepository {
|
||||
hint: row.hint,
|
||||
);
|
||||
|
||||
// ── Domain → DB companion ────────────────────────────────────────────────
|
||||
// ── Domain → DB companion (internal only) ────────────────────────────────
|
||||
|
||||
UsersCompanion _toCompanion(User user) => UsersCompanion(
|
||||
uid: Value(user.uid),
|
||||
@@ -98,9 +98,92 @@ class UserRepositoryImpl implements UserRepository {
|
||||
hint: Value(user.hint),
|
||||
);
|
||||
|
||||
UsersCompanion _toPartialCompanion(User user) => UsersCompanion(
|
||||
uid: Value(user.uid),
|
||||
uuid: user.uuid != null ? Value(user.uuid) : const Value.absent(),
|
||||
lastOnline: user.lastOnline != null
|
||||
? Value(user.lastOnline)
|
||||
: const Value.absent(),
|
||||
profilePic: user.profilePic != null
|
||||
? Value(user.profilePic)
|
||||
: const Value.absent(),
|
||||
profilePicGaussian: user.profilePicGaussian != null
|
||||
? Value(user.profilePicGaussian!)
|
||||
: const Value.absent(),
|
||||
nickname: user.nickname != null
|
||||
? Value(user.nickname)
|
||||
: const Value.absent(),
|
||||
depositName: user.depositName != null
|
||||
? Value(user.depositName)
|
||||
: const Value.absent(),
|
||||
hasSetDepositName: user.hasSetDepositName != null
|
||||
? Value(user.hasSetDepositName!)
|
||||
: const Value.absent(),
|
||||
contact: user.contact != null ? Value(user.contact) : const Value.absent(),
|
||||
countryCode: user.countryCode != null
|
||||
? Value(user.countryCode)
|
||||
: const Value.absent(),
|
||||
username: user.username != null
|
||||
? Value(user.username)
|
||||
: const Value.absent(),
|
||||
role: user.role != null ? Value(user.role) : const Value.absent(),
|
||||
relationship: user.relationship != null
|
||||
? Value(user.relationship)
|
||||
: const Value.absent(),
|
||||
friendStatus: user.friendStatus != null
|
||||
? Value(user.friendStatus)
|
||||
: const Value.absent(),
|
||||
bio: user.bio != null ? Value(user.bio) : const Value.absent(),
|
||||
userAlias: user.userAlias != null
|
||||
? Value(user.userAlias)
|
||||
: const Value.absent(),
|
||||
requestAt: user.requestAt != null
|
||||
? Value(user.requestAt)
|
||||
: const Value.absent(),
|
||||
deletedAt: user.deletedAt != null
|
||||
? Value(user.deletedAt)
|
||||
: const Value.absent(),
|
||||
email: user.email != null ? Value(user.email) : const Value.absent(),
|
||||
recoveryEmail: user.recoveryEmail != null
|
||||
? Value(user.recoveryEmail)
|
||||
: const Value.absent(),
|
||||
remark: user.remark != null ? Value(user.remark) : const Value.absent(),
|
||||
source: user.source != null ? Value(user.source) : const Value.absent(),
|
||||
addIndex: user.addIndex != null
|
||||
? Value(user.addIndex)
|
||||
: const Value.absent(),
|
||||
incomingSoundId: user.incomingSoundId != null
|
||||
? Value(user.incomingSoundId!)
|
||||
: const Value.absent(),
|
||||
outgoingSoundId: user.outgoingSoundId != null
|
||||
? Value(user.outgoingSoundId!)
|
||||
: const Value.absent(),
|
||||
notificationSoundId: user.notificationSoundId != null
|
||||
? Value(user.notificationSoundId!)
|
||||
: const Value.absent(),
|
||||
sendMessageSoundId: user.sendMessageSoundId != null
|
||||
? Value(user.sendMessageSoundId!)
|
||||
: const Value.absent(),
|
||||
groupNotificationSoundId: user.groupNotificationSoundId != null
|
||||
? Value(user.groupNotificationSoundId!)
|
||||
: const Value.absent(),
|
||||
groupTags: user.groupTags != null
|
||||
? Value(user.groupTags!)
|
||||
: const Value.absent(),
|
||||
friendTags: user.friendTags != null
|
||||
? Value(user.friendTags!)
|
||||
: const Value.absent(),
|
||||
publicKey: user.publicKey != null
|
||||
? Value(user.publicKey)
|
||||
: const Value.absent(),
|
||||
configBits: user.configBits != null
|
||||
? Value(user.configBits!)
|
||||
: const Value.absent(),
|
||||
hint: user.hint != null ? Value(user.hint) : const Value.absent(),
|
||||
);
|
||||
|
||||
// ── 监听 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// 监听单个用户
|
||||
@override
|
||||
Stream<User?> watchUser(int uid) {
|
||||
return _storage
|
||||
@@ -108,7 +191,6 @@ class UserRepositoryImpl implements UserRepository {
|
||||
.map((row) => row != null ? _toEntity(row) : null);
|
||||
}
|
||||
|
||||
/// 监听指定 uid 列表
|
||||
@override
|
||||
Stream<List<User>> watchUsers(List<int> uids) {
|
||||
return _storage
|
||||
@@ -116,7 +198,6 @@ class UserRepositoryImpl implements UserRepository {
|
||||
.map((rows) => rows.map(_toEntity).toList());
|
||||
}
|
||||
|
||||
/// 监听所有用户
|
||||
@override
|
||||
Stream<List<User>> watchAllUsers() {
|
||||
return _storage.watchAll<DriftUser>().map(
|
||||
@@ -199,34 +280,80 @@ class UserRepositoryImpl implements UserRepository {
|
||||
// ── 写入 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
Future<void> saveUser(User user) async {
|
||||
await _storage.insertOrReplace<DriftUser>(_toCompanion(user));
|
||||
Future<void> insertOrReplaceUser(User user) async {
|
||||
if (user.requireUpsert) {
|
||||
await _storage.insertOrReplace<DriftUser>(_toCompanion(user));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveUsers(List<User> users) async {
|
||||
await _storage.batchInsertOrReplace<DriftUser>(
|
||||
users.map(_toCompanion).toList(),
|
||||
);
|
||||
Future<void> insertOrReplaceUsers(List<User> users) async {
|
||||
final List<User> upsertList = [];
|
||||
final List<User> insertList = [];
|
||||
for (final user in users) {
|
||||
user.requireUpsert ? upsertList.add(user) : insertList.add(user);
|
||||
}
|
||||
|
||||
if (insertList.isNotEmpty) {
|
||||
await _storage.batchInsertOrReplace<DriftUser>(
|
||||
insertList.map(_toCompanion).toList(),
|
||||
);
|
||||
}
|
||||
if (upsertList.isNotEmpty) {
|
||||
await upsertUsers(upsertList);
|
||||
}
|
||||
}
|
||||
|
||||
/// 仅更新指定列,其他列不变
|
||||
///
|
||||
/// 示例:
|
||||
/// ```dart
|
||||
/// await userRepo.updateFields(uid, UsersCompanion(
|
||||
/// nickname: Value('New Name'),
|
||||
/// lastOnline: Value(DateTime.now().millisecondsSinceEpoch),
|
||||
/// ));
|
||||
/// ```
|
||||
@override
|
||||
Future<void> updateFields(int uid, UsersCompanion companion) async {
|
||||
Future<void> updateUser(User user) async {
|
||||
await _storage.updateWhere<DriftUser, $UsersTable>(
|
||||
companion,
|
||||
(t) => t.uid.equals(uid),
|
||||
_toCompanion(user),
|
||||
(t) => t.uid.equals(user.uid),
|
||||
);
|
||||
}
|
||||
|
||||
/// 批量更新已存在的用户记录。
|
||||
///
|
||||
/// 仅更新,不插入——调用方应确保这些用户已存在于 DB 中。
|
||||
/// 如需 upsert 语义,请使用 [upsertUsers]。
|
||||
///
|
||||
/// 所有更新在同一事务内完成,保证原子性。
|
||||
@override
|
||||
Future<void> updateUsersBatch(List<User> users) async {
|
||||
await _storage.transaction(() async {
|
||||
for (final user in users) {
|
||||
await updateUser(user);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 单条 upsert:不存在则插入,存在则仅更新非 null 字段。
|
||||
///
|
||||
/// 内部委托给 [upsertUsers]。
|
||||
@override
|
||||
Future<void> upsertUser(User user) => upsertUsers([user]);
|
||||
|
||||
/// 批量 upsert:不存在则插入,存在则仅更新非 null 字段。
|
||||
///
|
||||
/// 与 [insertOrReplaceUsers] 的区别:
|
||||
/// - [insertOrReplaceUsers] → INSERT OR REPLACE,全字段覆盖
|
||||
/// - [upsertUsers] → INSERT OR IGNORE + UPDATE,仅更新传入的非 null 字段
|
||||
///
|
||||
/// 所有操作在同一事务内完成,保证原子性。
|
||||
/// 适用场景:从服务端收到部分用户信息,不希望覆盖本地已有的其他字段。
|
||||
@override
|
||||
Future<void> upsertUsers(List<User> users) async {
|
||||
await _storage.transaction(() async {
|
||||
for (final user in users) {
|
||||
await _storage.insert<DriftUser>(_toCompanion(user));
|
||||
await _storage.updateWhere<DriftUser, $UsersTable>(
|
||||
_toPartialCompanion(user),
|
||||
(t) => t.uid.equals(user.uid),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ── 删除 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@override
|
||||
|
||||
Reference in New Issue
Block a user