Initial project

This commit is contained in:
Cody
2026-03-06 14:56:17 +08:00
parent 977b627b15
commit bf9e099747
1180 changed files with 50973 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
/// 管理每个用户对应的 SQLite 文件连接。
///
/// [databaseFactory] 由调用方App 层)提供,负责把 [QueryExecutor]
/// 包装成具体的 [GeneratedDatabase] 子类(含表定义和迁移策略)。
///
/// 这样 storage_sdk 只负责连接机制,不感知业务表结构。
///
/// 示例:
/// ```dart
/// final ds = DatabaseDataSource(
/// databaseFactory: (executor) => AppDatabase(executor),
/// );
/// await ds.openDatabase(userId);
/// ```
class DatabaseDataSource {
final GeneratedDatabase Function(QueryExecutor) _databaseFactory;
DatabaseDataSource({
required GeneratedDatabase Function(QueryExecutor) databaseFactory,
}) : _databaseFactory = databaseFactory;
GeneratedDatabase? _db;
/// 当前已打开的数据库实例;未开库时为 null。
GeneratedDatabase? get current => _db;
/// 打开指定用户的数据库文件,若已有连接则先关闭。
///
/// 数据库文件路径:`<ApplicationSupport>/../databases/<uid>.sqlite`
Future<GeneratedDatabase> openDatabase(int uid) async {
await _db?.close();
_db = null;
///TODO: iOS路径
final dir = await getApplicationSupportDirectory();
final dbDir = Directory('${dir.parent.path}/databases');
if (!dbDir.existsSync()) {
dbDir.createSync(recursive: true);
}
final file = File('${dbDir.path}/$uid.sqlite');
_db = _databaseFactory(NativeDatabase(file));
return _db!;
}
/// 关闭当前数据库连接并清空引用。
Future<void> closeDatabase() async {
await _db?.close();
_db = null;
}
}

View File

@@ -0,0 +1,183 @@
import 'package:drift/drift.dart';
import '../../domain/repositories/database_repository.dart';
import '../local/datasources/database_datasource.dart';
/// [DatabaseRepository] 的 Drift 实现。
///
/// 通过 [DatabaseDataSource.current] 获取当前数据库实例。
/// 数据库未打开null时所有方法静默返回不抛异常。
class DatabaseRepositoryImpl implements DatabaseRepository {
final DatabaseDataSource _dataSource;
DatabaseRepositoryImpl(this._dataSource);
GeneratedDatabase? get _db => _dataSource.current;
// ── 插入 ─────────────────────────────────────────────────────────────────
@override
Future<void> insertOrReplace<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
) async {
final db = _db;
if (db == null) return;
await db.into(table).insertOnConflictUpdate(companion);
}
@override
Future<void> insert<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
) async {
final db = _db;
if (db == null) return;
await db.into(table).insert(companion, mode: InsertMode.insertOrIgnore);
}
@override
Future<void> batchInsertOrReplace<T extends Table, D>(
TableInfo<T, D> table,
List<Insertable<D>> companions,
) async {
final db = _db;
if (db == null) return;
await db.batch(
(batch) => batch.insertAllOnConflictUpdate(table, companions),
);
}
// ── 更新 ─────────────────────────────────────────────────────────────────
@override
Future<void> updateWhere<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
Expression<bool> Function(T) filter,
) async {
final db = _db;
if (db == null) return;
await (db.update(table)..where(filter)).write(companion);
}
// ── 删除 ─────────────────────────────────────────────────────────────────
@override
Future<void> deleteWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
) async {
final db = _db;
if (db == null) return;
await (db.delete(table)..where(filter)).go();
}
@override
Future<void> deleteAll<T extends Table, D>(TableInfo<T, D> table) async {
final db = _db;
if (db == null) return;
await db.delete(table).go();
}
// ── 查询 ─────────────────────────────────────────────────────────────────
@override
Future<List<D>> selectAll<T extends Table, D>(TableInfo<T, D> table) async {
final db = _db;
if (db == null) return [];
return db.select(table).get();
}
@override
Future<List<D>> selectWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
) async {
final db = _db;
if (db == null) return [];
return (db.select(table)..where(filter)).get();
}
@override
Future<D?> selectFirst<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
) async {
final db = _db;
if (db == null) return null;
return (db.select(table)..where(filter)..limit(1)).getSingleOrNull();
}
// ── 监听 ─────────────────────────────────────────────────────────────────
@override
Stream<List<D>> watchAll<T extends Table, D>(TableInfo<T, D> table) {
final db = _db;
if (db == null) return const Stream.empty();
return db.select(table).watch();
}
@override
Stream<List<D>> watchWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
) {
final db = _db;
if (db == null) return const Stream.empty();
return (db.select(table)..where(filter)).watch();
}
@override
Stream<D?> watchFirst<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
) {
final db = _db;
if (db == null) return const Stream.empty();
return (db.select(table)..where(filter)..limit(1)).watchSingleOrNull();
}
// ── 原始 SQL ─────────────────────────────────────────────────────────────
@override
Future<List<QueryRow>> rawQuery(
String sql, [
List<Object?> args = const [],
]) async {
final db = _db;
if (db == null) return [];
return db
.customSelect(
sql,
variables: args.map((e) => Variable(e)).toList(),
)
.get();
}
@override
Future<void> rawExecute(
String sql, [
List<Object?> args = const [],
]) async {
final db = _db;
if (db == null) return;
await db.customStatement(sql, args);
}
// ── 统计 ─────────────────────────────────────────────────────────────────
@override
Future<int> count<T extends Table, D>(
TableInfo<T, D> table, {
Expression<bool> Function(T)? filter,
}) async {
final db = _db;
if (db == null) return 0;
final countExpr = table.$columns.first.count();
final query = db.selectOnly(table)..addColumns([countExpr]);
if (filter != null) query.where(filter(table.asDslTable));
final row = await query.getSingle();
return row.read(countExpr) ?? 0;
}
}

View File

@@ -0,0 +1,103 @@
import 'package:drift/drift.dart';
/// 通用数据库 CRUD 接口。
///
/// 所有方法通过 Drift 的 [TableInfo]、[Insertable]、[Expression] 泛型操作,
/// 与具体表定义解耦App 层按需传入对应 Table 即可。
///
/// 示例:
/// ```dart
/// await repo.insertOrReplace(db.users, companion);
/// final list = await repo.selectWhere(db.messages, (t) => t.chatId.equals(id));
/// ```
abstract class DatabaseRepository {
// ── 插入 ─────────────────────────────────────────────────────────────────
/// 插入或替换(主键冲突时覆盖)。
Future<void> insertOrReplace<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
);
/// 插入或忽略(主键冲突时跳过)。
Future<void> insert<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
);
/// 批量插入或替换。
Future<void> batchInsertOrReplace<T extends Table, D>(
TableInfo<T, D> table,
List<Insertable<D>> companions,
);
// ── 更新 ─────────────────────────────────────────────────────────────────
/// 按条件更新。
Future<void> updateWhere<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
Expression<bool> Function(T) filter,
);
// ── 删除 ─────────────────────────────────────────────────────────────────
/// 按条件删除。
Future<void> deleteWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
);
/// 清空整张表。
Future<void> deleteAll<T extends Table, D>(TableInfo<T, D> table);
// ── 查询 ─────────────────────────────────────────────────────────────────
/// 查询全部记录。
Future<List<D>> selectAll<T extends Table, D>(TableInfo<T, D> table);
/// 按条件查询。
Future<List<D>> selectWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
);
/// 查询第一条匹配记录。
Future<D?> selectFirst<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
);
// ── 监听 ─────────────────────────────────────────────────────────────────
/// 监听全部记录(实时流)。
Stream<List<D>> watchAll<T extends Table, D>(TableInfo<T, D> table);
/// 按条件监听(实时流)。
Stream<List<D>> watchWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
);
/// 监听第一条匹配记录(实时流)。
Stream<D?> watchFirst<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
);
// ── 原始 SQL ─────────────────────────────────────────────────────────────
/// 执行原始查询,返回行数据。
Future<List<QueryRow>> rawQuery(String sql, [List<Object?> args]);
/// 执行原始语句(无返回值)。
Future<void> rawExecute(String sql, [List<Object?> args]);
// ── 统计 ─────────────────────────────────────────────────────────────────
/// 统计记录数。
Future<int> count<T extends Table, D>(
TableInfo<T, D> table, {
Expression<bool> Function(T)? filter,
});
}

View File

@@ -0,0 +1,130 @@
import 'package:drift/drift.dart';
import '../wiring/storage_sdk_wiring.dart';
/// 本地数据库的统一公开接口。
///
/// 通过 [StorageSdkApi] 工厂方法获取实例,传入 App 侧的数据库工厂即可:
///
/// ```dart
/// final api = StorageSdkApi(
/// databaseFactory: (executor) => AppDatabase(executor),
/// );
/// ```
///
/// ## 生命周期
/// - 用户登录后调用 [openDatabase] 开库;
/// - 用户登出时调用 [closeDatabase] 关库;
/// - [isDatabaseOpen] 可查询当前状态。
///
/// 内部接口,仅供 App 层生命周期管理使用
abstract class StorageSdkLifecycle {
Future<void> openDatabase(int uid);
Future<void> closeDatabase();
bool get isDatabaseOpen;
}
///
///
/// ## CRUD
/// 所有 CRUD 方法均为泛型,接受 Drift 的 [TableInfo],与具体业务表解耦。
/// App 层传入自己定义的 Table 即可复用全部操作。
abstract class StorageSdkApi {
/// 创建 SDK 实例。
///
/// [databaseFactory] 由 App 层提供:接受 [QueryExecutor]
/// 返回自定义的 [GeneratedDatabase] 子类(含表定义和迁移策略)。
factory StorageSdkApi({
required GeneratedDatabase Function(QueryExecutor) databaseFactory,
}) =>
StorageSdkWiring.build(databaseFactory: databaseFactory);
// ── 插入 ─────────────────────────────────────────────────────────────────
/// 插入或替换(主键冲突时覆盖)。
Future<void> insertOrReplace<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
);
/// 插入或忽略(主键冲突时跳过)。
Future<void> insert<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
);
/// 批量插入或替换。
Future<void> batchInsertOrReplace<T extends Table, D>(
TableInfo<T, D> table,
List<Insertable<D>> companions,
);
// ── 更新 ─────────────────────────────────────────────────────────────────
/// 按条件更新。
Future<void> updateWhere<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
Expression<bool> Function(T) filter,
);
// ── 删除 ─────────────────────────────────────────────────────────────────
/// 按条件删除。
Future<void> deleteWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
);
/// 清空整张表。
Future<void> deleteAll<T extends Table, D>(TableInfo<T, D> table);
// ── 查询 ─────────────────────────────────────────────────────────────────
/// 查询全部记录。
Future<List<D>> selectAll<T extends Table, D>(TableInfo<T, D> table);
/// 按条件查询。
Future<List<D>> selectWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
);
/// 查询第一条匹配记录。
Future<D?> selectFirst<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
);
// ── 监听 ─────────────────────────────────────────────────────────────────
/// 监听全部记录(实时流)。
Stream<List<D>> watchAll<T extends Table, D>(TableInfo<T, D> table);
/// 按条件监听(实时流)。
Stream<List<D>> watchWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
);
/// 监听第一条匹配记录(实时流)。
Stream<D?> watchFirst<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
);
// ── 原始 SQL ─────────────────────────────────────────────────────────────
/// 执行原始查询,返回行数据。
Future<List<QueryRow>> rawQuery(String sql, [List<Object?> args]);
/// 执行原始语句(无返回值)。
Future<void> rawExecute(String sql, [List<Object?> args]);
// ── 统计 ─────────────────────────────────────────────────────────────────
/// 统计记录数。
Future<int> count<T extends Table, D>(
TableInfo<T, D> table, {
Expression<bool> Function(T)? filter,
});
}

View File

@@ -0,0 +1,128 @@
import 'package:drift/drift.dart';
import '../facade/storage_sdk_api.dart';
import 'storage_sdk_core.dart';
/// [StorageSdkApi] 的实现,委托给 [StorageSdkCore]。
class StorageSdkApiImpl implements StorageSdkApi, StorageSdkLifecycle {
final StorageSdkCore _core;
StorageSdkApiImpl({required StorageSdkCore core}) : _core = core;
// ── 生命周期 ─────────────────────────────────────────────────────────────
@override
Future<void> openDatabase(int uid) =>
_core.dataSource.openDatabase(uid);
@override
Future<void> closeDatabase() => _core.dataSource.closeDatabase();
@override
bool get isDatabaseOpen => _core.dataSource.current != null;
// ── 插入 ─────────────────────────────────────────────────────────────────
@override
Future<void> insertOrReplace<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
) =>
_core.repo.insertOrReplace(table, companion);
@override
Future<void> insert<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
) =>
_core.repo.insert(table, companion);
@override
Future<void> batchInsertOrReplace<T extends Table, D>(
TableInfo<T, D> table,
List<Insertable<D>> companions,
) =>
_core.repo.batchInsertOrReplace(table, companions);
// ── 更新 ─────────────────────────────────────────────────────────────────
@override
Future<void> updateWhere<T extends Table, D>(
TableInfo<T, D> table,
Insertable<D> companion,
Expression<bool> Function(T) filter,
) =>
_core.repo.updateWhere(table, companion, filter);
// ── 删除 ─────────────────────────────────────────────────────────────────
@override
Future<void> deleteWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
) =>
_core.repo.deleteWhere(table, filter);
@override
Future<void> deleteAll<T extends Table, D>(TableInfo<T, D> table) =>
_core.repo.deleteAll(table);
// ── 查询 ─────────────────────────────────────────────────────────────────
@override
Future<List<D>> selectAll<T extends Table, D>(TableInfo<T, D> table) =>
_core.repo.selectAll(table);
@override
Future<List<D>> selectWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
) =>
_core.repo.selectWhere(table, filter);
@override
Future<D?> selectFirst<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
) =>
_core.repo.selectFirst(table, filter);
// ── 监听 ─────────────────────────────────────────────────────────────────
@override
Stream<List<D>> watchAll<T extends Table, D>(TableInfo<T, D> table) =>
_core.repo.watchAll(table);
@override
Stream<List<D>> watchWhere<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
) =>
_core.repo.watchWhere(table, filter);
@override
Stream<D?> watchFirst<T extends Table, D>(
TableInfo<T, D> table,
Expression<bool> Function(T) filter,
) =>
_core.repo.watchFirst(table, filter);
// ── 原始 SQL ─────────────────────────────────────────────────────────────
@override
Future<List<QueryRow>> rawQuery(String sql, [List<Object?> args = const []]) =>
_core.repo.rawQuery(sql, args);
@override
Future<void> rawExecute(String sql, [List<Object?> args = const []]) =>
_core.repo.rawExecute(sql, args);
// ── 统计 ─────────────────────────────────────────────────────────────────
@override
Future<int> count<T extends Table, D>(
TableInfo<T, D> table, {
Expression<bool> Function(T)? filter,
}) =>
_core.repo.count(table, filter: filter);
}

View File

@@ -0,0 +1,13 @@
import '../../data/local/datasources/database_datasource.dart';
import '../../domain/repositories/database_repository.dart';
/// SDK 核心依赖容器。
class StorageSdkCore {
final DatabaseDataSource dataSource;
final DatabaseRepository repo;
const StorageSdkCore({
required this.dataSource,
required this.repo,
});
}

View File

@@ -0,0 +1,30 @@
import 'package:drift/drift.dart';
import '../facade/storage_sdk_api.dart';
import '../../data/local/datasources/database_datasource.dart';
import '../../data/repositories/database_repository_impl.dart';
import 'storage_sdk_core.dart';
import 'storage_sdk_api_impl.dart';
/// SDK 依赖装配入口。
///
/// 调用方传入数据库工厂SDK 负责连接生命周期和 CRUD 机制。
///
/// 示例im_app 的 DI 层):
/// ```dart
/// StorageSdkWiring.build(
/// databaseFactory: (executor) => AppDatabase(executor),
/// );
/// ```
class StorageSdkWiring {
StorageSdkWiring._();
static StorageSdkApi build({
required GeneratedDatabase Function(QueryExecutor) databaseFactory,
}) {
final dataSource = DatabaseDataSource(databaseFactory: databaseFactory);
final repo = DatabaseRepositoryImpl(dataSource);
final core = StorageSdkCore(dataSource: dataSource, repo: repo);
return StorageSdkApiImpl(core: core);
}
}

View File

@@ -0,0 +1,2 @@
export 'src/presentation/facade/storage_sdk_api.dart';