From fe54f79b21e866ec379969216a01050e8c8e9c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Happi=20=28=E5=93=88=E6=AF=94=29?= Date: Mon, 9 Mar 2026 14:59:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=92=8C=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=A1=88=E4=BE=8B=E6=8C=89=E7=85=A7=E6=9E=B6=E6=9E=84=E6=9D=A5?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../di/user_provider.dart | 15 ++++- .../notifiers/user_notifier.dart | 2 +- .../usecases/insert_users_use_case.dart | 55 +++++++++++++++++++ .../presentation/chat_db_test_view_model.dart | 50 ++++++++--------- .../chat/usecases/insert_users_use_case.dart | 55 +++++++++++++++++++ .../login/presentation/login_view_model.dart | 2 +- 6 files changed, 147 insertions(+), 32 deletions(-) rename apps/im_app/lib/{domain/presentation => app}/di/user_provider.dart (64%) rename apps/im_app/lib/{domain/presentation => app}/notifiers/user_notifier.dart (97%) create mode 100644 apps/im_app/lib/domain/usecases/insert_users_use_case.dart create mode 100644 apps/im_app/lib/features/chat/usecases/insert_users_use_case.dart diff --git a/apps/im_app/lib/domain/presentation/di/user_provider.dart b/apps/im_app/lib/app/di/user_provider.dart similarity index 64% rename from apps/im_app/lib/domain/presentation/di/user_provider.dart rename to apps/im_app/lib/app/di/user_provider.dart index b9a823a..9d6b95c 100644 --- a/apps/im_app/lib/domain/presentation/di/user_provider.dart +++ b/apps/im_app/lib/app/di/user_provider.dart @@ -1,3 +1,5 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:im_app/domain/usecases/insert_users_use_case.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:im_app/app/di/db_provider.dart'; import 'package:im_app/data/repositories/user_repository_impl.dart'; @@ -13,15 +15,22 @@ UserRepository userRepository(Ref ref) { return UserRepositoryImpl(ref.watch(storageSdkProvider)); } -// ── Multiple Users ──────────────────────────────────────────────────────────── +// ── Use Cases ───────────────────────────────────────────────────────────────── + +/// 批量插入用户用例 Provider +/// +/// 封装去重 + 分批插入逻辑,ViewModel 只需传入用户列表 +final insertUsersUseCaseProvider = Provider((ref) { + return InsertUsersUseCase(userRepository: ref.read(userRepositoryProvider)); +}); + +// ── Streams ─────────────────────────────────────────────────────────────────── @riverpod Stream> users(Ref ref, Set uids) { return ref.watch(userRepositoryProvider).watchUsers(uids.toList()); } -// ── All Users ───────────────────────────────────────────────────────────────── - @riverpod Stream> allUsers(Ref ref) { return ref.watch(userRepositoryProvider).watchAllUsers(); diff --git a/apps/im_app/lib/domain/presentation/notifiers/user_notifier.dart b/apps/im_app/lib/app/notifiers/user_notifier.dart similarity index 97% rename from apps/im_app/lib/domain/presentation/notifiers/user_notifier.dart rename to apps/im_app/lib/app/notifiers/user_notifier.dart index 2d2321a..56ce957 100644 --- a/apps/im_app/lib/domain/presentation/notifiers/user_notifier.dart +++ b/apps/im_app/lib/app/notifiers/user_notifier.dart @@ -1,4 +1,4 @@ -import 'package:im_app/domain/presentation/di/user_provider.dart'; +import 'package:im_app/app/di/user_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:im_app/data/local/drift/app_database.dart'; import 'package:im_app/domain/entities/user.dart'; diff --git a/apps/im_app/lib/domain/usecases/insert_users_use_case.dart b/apps/im_app/lib/domain/usecases/insert_users_use_case.dart new file mode 100644 index 0000000..7223b1b --- /dev/null +++ b/apps/im_app/lib/domain/usecases/insert_users_use_case.dart @@ -0,0 +1,55 @@ +import 'package:im_app/domain/entities/user.dart'; +import 'package:im_app/domain/repositories/user_repository.dart'; + +/// 批量插入用户用例 +/// +/// ## 职责 +/// - 封装用户插入的业务规则 +/// - 去重(uid 相同时保留最后一个) +/// - 分批插入,避免单次写入过大 +/// +/// ## 数据流 +/// ``` +/// ViewModel +/// → InsertUsersUseCase.execute(users) +/// → 去重 +/// → UserRepository.saveUsers(chunk) ← 分批写入 +/// → onProgress(completed, total) ← 可选进度回调 +/// ← 实际插入数量 +/// ``` +class InsertUsersUseCase { + final UserRepository _repo; + static const _chunkSize = 200; + + InsertUsersUseCase({required UserRepository userRepository}) + : _repo = userRepository; + + /// 批量插入用户 + /// + /// [users] 要插入的用户列表 + /// [onProgress] 可选回调,每批完成后触发 + /// + /// 返回实际插入数量 + Future execute( + List users, { + void Function(int completed, int total, List chunk)? onProgress, + }) async { + if (users.isEmpty) return 0; + + final deduped = {for (final u in users) u.uid: u}.values.toList(); + final total = deduped.length; + int completed = 0; + + while (completed < total) { + final end = (completed + _chunkSize).clamp(0, total); + final chunk = deduped.sublist(completed, end); + + await _repo.saveUsers(chunk); + completed += chunk.length; + + onProgress?.call(completed, total, chunk); + } + + return completed; + } +} diff --git a/apps/im_app/lib/features/chat/presentation/chat_db_test_view_model.dart b/apps/im_app/lib/features/chat/presentation/chat_db_test_view_model.dart index d012867..e9a60e9 100644 --- a/apps/im_app/lib/features/chat/presentation/chat_db_test_view_model.dart +++ b/apps/im_app/lib/features/chat/presentation/chat_db_test_view_model.dart @@ -1,8 +1,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:im_app/app/di/user_provider.dart'; import 'package:im_app/domain/entities/user.dart'; -import 'package:im_app/domain/presentation/di/user_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'chat_db_test_view_model.g.dart'; @@ -101,42 +101,38 @@ class ChatDbTestViewModel extends _$ChatDbTestViewModel { } Future _testDBInsert() async { + final useCase = ref.read(insertUsersUseCaseProvider); final repo = ref.read(userRepositoryProvider); const count = 10000; - const chunkSize = 200; final stopwatch = Stopwatch()..start(); + final baseUid = DateTime.now().microsecondsSinceEpoch; - debugPrint('开始测试: $count 条,每批 $chunkSize 条'); + debugPrint('开始测试: $count 条'); final workingList = List.from(state.users); - int completed = 0; - while (completed < count) { - if (!_isTesting) break; + final allUsers = List.generate( + count, + (i) => User(uid: baseUid + i, nickname: 'User ${_random.nextInt(9999)}'), + ); - final chunk = List.generate( - min(chunkSize, count - completed), - (_) => User( - uid: _random.nextInt(999999), - nickname: 'User ${_random.nextInt(9999)}', - ), - ); + await useCase.execute( + allUsers, + onProgress: (completed, total, chunk) { + workingList.addAll(chunk); - await repo.saveUsers(chunk); - completed += chunk.length; - workingList.addAll(chunk); - - debugPrint( - '已完成: $completed / $count (${stopwatch.elapsedMilliseconds}ms)', - ); - - if (ref.mounted) { - state = state.copyWith( - users: List.unmodifiable(workingList), - currentState: '已插入 $completed / $count 条', + debugPrint( + '已完成: $completed / $total (${stopwatch.elapsedMilliseconds}ms)', ); - } - } + + if (ref.mounted && _isTesting) { + state = state.copyWith( + users: List.unmodifiable(workingList), + currentState: '已插入 $completed / $total 条', + ); + } + }, + ); _isTesting = false; final elapsed = stopwatch.elapsedMilliseconds; diff --git a/apps/im_app/lib/features/chat/usecases/insert_users_use_case.dart b/apps/im_app/lib/features/chat/usecases/insert_users_use_case.dart new file mode 100644 index 0000000..7223b1b --- /dev/null +++ b/apps/im_app/lib/features/chat/usecases/insert_users_use_case.dart @@ -0,0 +1,55 @@ +import 'package:im_app/domain/entities/user.dart'; +import 'package:im_app/domain/repositories/user_repository.dart'; + +/// 批量插入用户用例 +/// +/// ## 职责 +/// - 封装用户插入的业务规则 +/// - 去重(uid 相同时保留最后一个) +/// - 分批插入,避免单次写入过大 +/// +/// ## 数据流 +/// ``` +/// ViewModel +/// → InsertUsersUseCase.execute(users) +/// → 去重 +/// → UserRepository.saveUsers(chunk) ← 分批写入 +/// → onProgress(completed, total) ← 可选进度回调 +/// ← 实际插入数量 +/// ``` +class InsertUsersUseCase { + final UserRepository _repo; + static const _chunkSize = 200; + + InsertUsersUseCase({required UserRepository userRepository}) + : _repo = userRepository; + + /// 批量插入用户 + /// + /// [users] 要插入的用户列表 + /// [onProgress] 可选回调,每批完成后触发 + /// + /// 返回实际插入数量 + Future execute( + List users, { + void Function(int completed, int total, List chunk)? onProgress, + }) async { + if (users.isEmpty) return 0; + + final deduped = {for (final u in users) u.uid: u}.values.toList(); + final total = deduped.length; + int completed = 0; + + while (completed < total) { + final end = (completed + _chunkSize).clamp(0, total); + final chunk = deduped.sublist(completed, end); + + await _repo.saveUsers(chunk); + completed += chunk.length; + + onProgress?.call(completed, total, chunk); + } + + return completed; + } +} diff --git a/apps/im_app/lib/features/login/presentation/login_view_model.dart b/apps/im_app/lib/features/login/presentation/login_view_model.dart index 46689f1..83b7012 100644 --- a/apps/im_app/lib/features/login/presentation/login_view_model.dart +++ b/apps/im_app/lib/features/login/presentation/login_view_model.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'package:flutter/services.dart'; +import 'package:im_app/app/di/user_provider.dart'; import 'package:im_app/data/remote/login_request.dart'; -import 'package:im_app/domain/presentation/di/user_provider.dart'; import 'package:networks_sdk/networks_sdk.dart'; import 'package:im_app/app/di/db_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';