更新数据库
This commit is contained in:
@@ -11,14 +11,22 @@ import '../../data/local/drift/app_database.dart';
|
||||
/// 用法:
|
||||
/// ```dart
|
||||
/// // 登录后开库
|
||||
/// await ref.read(storageSdkProvider).openDatabase(user.id);
|
||||
/// await ref.read(storageSdkLifecycleProvider).openDatabase(user.id);
|
||||
///
|
||||
/// // CRUD 示例
|
||||
/// final db = ref.read(storageSdkProvider);
|
||||
/// await db.insertOrReplace(appDb.users, companion);
|
||||
/// await db.insertOrReplace<UsersCompanion>(companion);
|
||||
/// final users = await db.selectAll<User>();
|
||||
/// ```
|
||||
|
||||
final storageSdkProvider = Provider<StorageSdkApi>((ref) {
|
||||
return StorageSdkApi(
|
||||
databaseFactory: (executor) => AppDatabase(executor),
|
||||
tableRegistry: (db) => AppDatabase.getTableRegistry(db),
|
||||
);
|
||||
});
|
||||
|
||||
/// 生命周期管理,仅供登录/登出使用。
|
||||
final storageSdkLifecycleProvider = Provider<StorageSdkLifecycle>((ref) {
|
||||
return ref.read(storageSdkProvider) as StorageSdkLifecycle;
|
||||
});
|
||||
@@ -64,6 +64,7 @@ enum AppRouteName {
|
||||
chatDetail('/chat/detail'),
|
||||
// 路径参数形式:导航用 AppRouteName.chatDetailByIdPath(id),不直接用 .path
|
||||
chatDetailById('/chat/:id'),
|
||||
chatDBTest('/chat/dbTest'),
|
||||
|
||||
// ── Settings 子路由 ───────────────────────────────────────────────────────
|
||||
settingsTheme('/settings/theme'),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:im_app/features/chat/view/chat_db_test_page.dart';
|
||||
|
||||
import '../../features/app_tab/view/app_tab.dart';
|
||||
import '../../features/chat/view/chat_detail_page.dart';
|
||||
@@ -115,6 +116,13 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
// parentNavigatorKey: _rootKey 确保路由覆盖 Shell,TabBar 消失
|
||||
//
|
||||
// extra 传参:接收 ({String conversationId, String title})
|
||||
GoRoute(
|
||||
parentNavigatorKey: _rootKey,
|
||||
path: AppRouteName.chatDBTest.path,
|
||||
builder: (context, state) {
|
||||
return const ChatDbTestPage();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
parentNavigatorKey: _rootKey,
|
||||
path: AppRouteName.chatDetail.path,
|
||||
|
||||
@@ -34,13 +34,13 @@ String? authGuard(AuthNotifier authNotifier, GoRouterState state) {
|
||||
case AppRouteName.login:
|
||||
// 已登录还在登录页 → 跳聊天页
|
||||
return isLoggedIn ? AppRouteName.chat.path : null;
|
||||
|
||||
case AppRouteName.chat:
|
||||
case AppRouteName.chatDetail:
|
||||
case AppRouteName.chatDetailById:
|
||||
case AppRouteName.contact:
|
||||
case AppRouteName.settings:
|
||||
case AppRouteName.settingsTheme:
|
||||
case AppRouteName.chatDBTest:
|
||||
// 受保护路由 → 未登录跳登录页
|
||||
return isLoggedIn ? null : AppRouteName.login.path;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:im_app/data/local/drift/tables/users.dart';
|
||||
import 'package:im_app/data/local/drift/tables/test_tables.dart';
|
||||
|
||||
part 'app_database.g.dart';
|
||||
|
||||
@DriftDatabase(tables: [Users])
|
||||
@DriftDatabase(tables: [Users,TestTables]) //update mapping here
|
||||
class AppDatabase extends _$AppDatabase {
|
||||
|
||||
static Map<Type, TableInfo> getTableRegistry(GeneratedDatabase database) {
|
||||
if (database is! AppDatabase) {
|
||||
return {
|
||||
};
|
||||
}
|
||||
return {
|
||||
User: database.users,
|
||||
TestTable: database.testTables,
|
||||
};
|
||||
}
|
||||
|
||||
AppDatabase(super.e);
|
||||
|
||||
@override
|
||||
@@ -37,4 +50,6 @@ class AppDatabase extends _$AppDatabase {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
41
apps/im_app/lib/data/local/drift/tables/test_tables.dart
Normal file
41
apps/im_app/lib/data/local/drift/tables/test_tables.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
@DataClassName('TestTable')
|
||||
class TestTables extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
IntColumn get uid => integer().nullable()();
|
||||
TextColumn get uuid => text().nullable()();
|
||||
IntColumn get lastOnline => integer().nullable()();
|
||||
TextColumn get profilePic => text().nullable()();
|
||||
TextColumn get profilePicGaussian => text().withDefault(const Constant(''))();
|
||||
TextColumn get nickname => text().nullable()();
|
||||
TextColumn get depositName => text().nullable()();
|
||||
IntColumn get hasSetDepositName => integer().withDefault(const Constant(0))();
|
||||
TextColumn get contact => text().nullable()();
|
||||
TextColumn get countryCode => text().nullable()();
|
||||
TextColumn get username => text().nullable()();
|
||||
IntColumn get role => integer().nullable()();
|
||||
IntColumn get relationship => integer().nullable()();
|
||||
IntColumn get friendStatus => integer().nullable()();
|
||||
TextColumn get bio => text().nullable()();
|
||||
TextColumn get userAlias => text().nullable()();
|
||||
IntColumn get requestAt => integer().nullable()();
|
||||
IntColumn get deletedAt => integer().nullable()();
|
||||
TextColumn get email => text().nullable()();
|
||||
TextColumn get recoveryEmail => text().nullable()();
|
||||
TextColumn get remark => text().nullable()();
|
||||
TextColumn get source => text().nullable()();
|
||||
IntColumn get addIndex => integer().nullable()();
|
||||
IntColumn get incomingSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get outgoingSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get notificationSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get sendMessageSoundId => integer().withDefault(const Constant(0))();
|
||||
IntColumn get groupNotificationSoundId => integer().withDefault(const Constant(0))();
|
||||
TextColumn get groupTags => text().withDefault(const Constant('[]'))();
|
||||
TextColumn get friendTags => text().withDefault(const Constant('[]'))();
|
||||
TextColumn get publicKey => text().nullable()();
|
||||
IntColumn get configBits => integer().withDefault(const Constant(0))();
|
||||
TextColumn get hint => text().nullable()();
|
||||
@override
|
||||
String get tableName => 'test_tables';
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:im_app/app/di/db_provider.dart';
|
||||
import 'package:im_app/data/local/drift/app_database.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'chat_db_test_view_model.g.dart';
|
||||
|
||||
class TestResult {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String duration;
|
||||
|
||||
TestResult({
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.duration,
|
||||
});
|
||||
}
|
||||
|
||||
class ChatDbTestState {
|
||||
final bool testStarted;
|
||||
final List<TestResult> testResults;
|
||||
final String currentState;
|
||||
|
||||
const ChatDbTestState({
|
||||
this.testStarted = false,
|
||||
this.testResults = const [],
|
||||
this.currentState = '',
|
||||
});
|
||||
|
||||
ChatDbTestState copyWith({
|
||||
bool? testStarted,
|
||||
List<TestResult>? testResults,
|
||||
String? currentState,
|
||||
}) => ChatDbTestState(
|
||||
testStarted: testStarted ?? this.testStarted,
|
||||
testResults: testResults ?? this.testResults,
|
||||
currentState: currentState ?? this.currentState,
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class ChatDbTestViewModel extends _$ChatDbTestViewModel {
|
||||
|
||||
@override
|
||||
ChatDbTestState build() {
|
||||
// 这里就是 onInit
|
||||
final List<TestResult> testResults = List.generate(
|
||||
1000,
|
||||
(i) => TestResult(
|
||||
title: '用户 ${Random().nextInt(9999)}',
|
||||
subtitle: 'uid: ${Random().nextInt(999999)}',
|
||||
duration: '${Random().nextInt(500)}ms',
|
||||
),
|
||||
);
|
||||
return ChatDbTestState(testResults: testResults);
|
||||
}
|
||||
|
||||
// ── 导航(Demo 按钮,正式开发后随 UI 一并替换) ──────────────────────────
|
||||
|
||||
/// 开始测试
|
||||
void startDBTest(BuildContext context) {
|
||||
state = state.copyWith(testStarted: true, currentState: '开始测试');
|
||||
_testDBInsert();
|
||||
}
|
||||
|
||||
/// 结束测试
|
||||
void stopDBTest(BuildContext context) {
|
||||
state = state.copyWith(testStarted: false, currentState: '结束测试');
|
||||
}
|
||||
|
||||
Future<void> _testDBInsert() async {
|
||||
final db = ref.read(storageSdkProvider);
|
||||
const count = 10000;
|
||||
const chunkSize = 50;
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
debugPrint('开始测试: $count 条,每批 $chunkSize 条');
|
||||
|
||||
int completed = 0;
|
||||
|
||||
for (var i = 0; i < count; i += chunkSize) {
|
||||
final chunk = List.generate(
|
||||
chunkSize.clamp(0, count - i),
|
||||
(j) => UsersCompanion.insert(
|
||||
uid: Value(i + j),
|
||||
nickname: Value('User ${i + j}'),
|
||||
),
|
||||
);
|
||||
|
||||
await db.batchInsertOrReplace<User>(chunk);
|
||||
completed += chunk.length;
|
||||
|
||||
// 让出主线程
|
||||
await Future.delayed(Duration.zero);
|
||||
|
||||
debugPrint('已完成: $completed / $count (${stopwatch.elapsedMilliseconds}ms)');
|
||||
|
||||
// 更新 UI 状态
|
||||
if (ref.mounted) {
|
||||
state = state.copyWith(
|
||||
currentState: '已插入 $completed / $count 条',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('全部完成: ${stopwatch.elapsedMilliseconds}ms');
|
||||
if (ref.mounted) {
|
||||
state = state.copyWith(
|
||||
testStarted: false,
|
||||
currentState: '完成!共 $count 条,耗时 ${stopwatch.elapsedMilliseconds}ms',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,11 @@ class ChatViewModel extends _$ChatViewModel {
|
||||
context.go(AppRouteName.settings.path);
|
||||
}
|
||||
|
||||
/// 测试数据库性能
|
||||
void goToDatabaseTest(BuildContext context) {
|
||||
context.push(AppRouteName.chatDBTest.path);
|
||||
}
|
||||
|
||||
// ── 业务 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/// 退出登录
|
||||
|
||||
71
apps/im_app/lib/features/chat/view/chat_db_test_page.dart
Normal file
71
apps/im_app/lib/features/chat/view/chat_db_test_page.dart
Normal file
@@ -0,0 +1,71 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:im_app/features/chat/presentation/chat_db_test_view_model.dart';
|
||||
|
||||
import '../../../core/ui/components/app_button.dart';
|
||||
import '../presentation/chat_view_model.dart';
|
||||
|
||||
/// 聊天页(Demo 按钮)
|
||||
///
|
||||
/// 包含五个演示按钮,覆盖 go_router 的常见导航场景:
|
||||
/// - 「切换 Tab」 — go,替换历史,不可返回
|
||||
/// - 「有参 push(extra)」 — push + extra(Dart Record),可返回
|
||||
/// - 「有参 push(路径参数)」— push + URL 内嵌 id,可返回
|
||||
/// - 「无参 push」 — push,可返回
|
||||
/// - 「退出登录」 — 守卫自动重定向到 /login
|
||||
///
|
||||
/// 所有操作通过 [ChatViewModel] 处理,View 不直接调用路由。
|
||||
/// 正式开发后替换为会话列表,按钮相关代码一并清除。
|
||||
class ChatDbTestPage extends ConsumerWidget {
|
||||
const ChatDbTestPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final vm = ref.read(chatDbTestViewModelProvider.notifier);
|
||||
final state = ref.watch(chatDbTestViewModelProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('测试数据库'), ),
|
||||
body: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
spacing: 16,
|
||||
children: [
|
||||
SizedBox(height: 4),
|
||||
Padding(
|
||||
padding: EdgeInsetsGeometry.symmetric(horizontal: 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
AppButton.inverse(
|
||||
label: state.testStarted ? '结束' : '开始',
|
||||
onPressed: () => state.testStarted ? vm.stopDBTest(context) : vm.startDBTest(context),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
state.currentState,
|
||||
textAlign: TextAlign.end,
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: state.testResults.length,
|
||||
itemBuilder: (context, index) {
|
||||
final result = state.testResults[index];
|
||||
return ListTile(
|
||||
titleAlignment: ListTileTitleAlignment.center,
|
||||
title: Text(result.title),
|
||||
subtitle: Text(result.subtitle),
|
||||
// trailing: Text(result.duration),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,10 @@ class ChatPage extends ConsumerWidget {
|
||||
label: '无参 go',
|
||||
onPressed: () => vm.goToSettings(context),
|
||||
),
|
||||
AppButton.inverse(
|
||||
label: '测试数据库性能',
|
||||
onPressed: () => vm.goToDatabaseTest(context),
|
||||
),
|
||||
AppButton.secondary(
|
||||
label: '退出登录',
|
||||
fullWidth: false,
|
||||
|
||||
Reference in New Issue
Block a user