Initial commit
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import '../../contracts/contracts.dart';
|
||||
import '../errors.dart';
|
||||
import '../ports/danmaku_sources_store.dart';
|
||||
import '../ports/script_widget_repository.dart';
|
||||
import '../ports/server_repository.dart';
|
||||
import '../ports/session_repository.dart';
|
||||
|
||||
const int kBackupSchemaVersion = 1;
|
||||
|
||||
class BackupExportResult {
|
||||
final String json;
|
||||
final int serverCount;
|
||||
final int sessionCount;
|
||||
final int danmakuSourceCount;
|
||||
final int scriptWidgetCount;
|
||||
final int scriptWidgetSubscriptionCount;
|
||||
|
||||
const BackupExportResult({
|
||||
required this.json,
|
||||
required this.serverCount,
|
||||
required this.sessionCount,
|
||||
required this.danmakuSourceCount,
|
||||
required this.scriptWidgetCount,
|
||||
required this.scriptWidgetSubscriptionCount,
|
||||
});
|
||||
}
|
||||
|
||||
class BackupImportResult {
|
||||
final int serverCount;
|
||||
final int sessionCount;
|
||||
final int danmakuSourceCount;
|
||||
final int scriptWidgetCount;
|
||||
final int scriptWidgetSubscriptionCount;
|
||||
|
||||
const BackupImportResult({
|
||||
required this.serverCount,
|
||||
required this.sessionCount,
|
||||
required this.danmakuSourceCount,
|
||||
required this.scriptWidgetCount,
|
||||
required this.scriptWidgetSubscriptionCount,
|
||||
});
|
||||
}
|
||||
|
||||
class ExportBackupUseCase {
|
||||
final ServerRepository serverRepository;
|
||||
final SessionRepository sessionRepository;
|
||||
final DanmakuSourcesStore danmakuSourcesStore;
|
||||
final ScriptWidgetRepository scriptWidgetRepository;
|
||||
final DateTime Function() now;
|
||||
|
||||
ExportBackupUseCase({
|
||||
required this.serverRepository,
|
||||
required this.sessionRepository,
|
||||
required this.danmakuSourcesStore,
|
||||
required this.scriptWidgetRepository,
|
||||
DateTime Function()? now,
|
||||
}) : now = (now ?? DateTime.now);
|
||||
|
||||
Future<BackupExportResult> execute() async {
|
||||
final servers = await serverRepository.list();
|
||||
final sessions = await sessionRepository.listAll();
|
||||
final danmakuSources = await danmakuSourcesStore.list();
|
||||
final scriptWidgets = await scriptWidgetRepository.listWidgets();
|
||||
final scriptWidgetSubscriptions =
|
||||
await scriptWidgetRepository.listSubscriptions();
|
||||
final bundle = BackupBundle(
|
||||
version: kBackupSchemaVersion,
|
||||
exportedAt: now().toUtc().toIso8601String(),
|
||||
servers: servers,
|
||||
sessions: sessions,
|
||||
danmakuSources: danmakuSources,
|
||||
scriptWidgets: scriptWidgets,
|
||||
scriptWidgetSubscriptions: scriptWidgetSubscriptions,
|
||||
);
|
||||
final json = const JsonEncoder.withIndent(' ').convert(bundle.toJson());
|
||||
return BackupExportResult(
|
||||
json: json,
|
||||
serverCount: servers.length,
|
||||
sessionCount: sessions.length,
|
||||
danmakuSourceCount: danmakuSources.length,
|
||||
scriptWidgetCount: scriptWidgets.length,
|
||||
scriptWidgetSubscriptionCount: scriptWidgetSubscriptions.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ImportBackupUseCase {
|
||||
final ServerRepository serverRepository;
|
||||
final SessionRepository sessionRepository;
|
||||
final DanmakuSourcesStore danmakuSourcesStore;
|
||||
final ScriptWidgetRepository scriptWidgetRepository;
|
||||
|
||||
ImportBackupUseCase({
|
||||
required this.serverRepository,
|
||||
required this.sessionRepository,
|
||||
required this.danmakuSourcesStore,
|
||||
required this.scriptWidgetRepository,
|
||||
});
|
||||
|
||||
Future<BackupImportResult> execute(String rawJson) async {
|
||||
final BackupBundle bundle;
|
||||
try {
|
||||
final decoded = jsonDecode(rawJson);
|
||||
if (decoded is! Map<String, dynamic>) {
|
||||
throw DomainError(ErrorCode.invalidParams, '备份文件格式不正确');
|
||||
}
|
||||
bundle = BackupBundle.fromJson(decoded);
|
||||
} on DomainError {
|
||||
rethrow;
|
||||
} catch (e) {
|
||||
throw DomainError(
|
||||
ErrorCode.invalidParams,
|
||||
'备份文件解析失败',
|
||||
details: {'reason': e.toString()},
|
||||
);
|
||||
}
|
||||
|
||||
if (bundle.version != kBackupSchemaVersion) {
|
||||
throw DomainError(
|
||||
ErrorCode.invalidParams,
|
||||
'不支持的备份文件版本:${bundle.version}',
|
||||
details: {'expected': kBackupSchemaVersion, 'got': bundle.version},
|
||||
);
|
||||
}
|
||||
|
||||
for (final server in bundle.servers) {
|
||||
await serverRepository.update(server);
|
||||
}
|
||||
await sessionRepository.setMany(bundle.sessions);
|
||||
|
||||
final mergedSources = mergeDanmakuSourcesByUrl(
|
||||
local: await danmakuSourcesStore.list(),
|
||||
remote: bundle.danmakuSources,
|
||||
);
|
||||
await danmakuSourcesStore.replaceAll(mergedSources);
|
||||
|
||||
for (final scriptWidget in bundle.scriptWidgets) {
|
||||
await scriptWidgetRepository.upsertWidget(scriptWidget);
|
||||
}
|
||||
for (final subscription in bundle.scriptWidgetSubscriptions) {
|
||||
await scriptWidgetRepository.upsertSubscription(subscription);
|
||||
}
|
||||
|
||||
return BackupImportResult(
|
||||
serverCount: bundle.servers.length,
|
||||
sessionCount: bundle.sessions.length,
|
||||
danmakuSourceCount: mergedSources.length,
|
||||
scriptWidgetCount: bundle.scriptWidgets.length,
|
||||
scriptWidgetSubscriptionCount: bundle.scriptWidgetSubscriptions.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<DanmakuSource> mergeDanmakuSourcesByUrl({
|
||||
required List<DanmakuSource> local,
|
||||
required List<DanmakuSource> remote,
|
||||
}) {
|
||||
final localByUrl = <String, DanmakuSource>{
|
||||
for (final s in local) s.url.trim(): s,
|
||||
};
|
||||
final out = <DanmakuSource>[];
|
||||
final seen = <String>{};
|
||||
for (final r in remote) {
|
||||
final url = r.url.trim();
|
||||
if (url.isEmpty || seen.contains(url)) continue;
|
||||
seen.add(url);
|
||||
final hit = localByUrl[url];
|
||||
if (hit != null) {
|
||||
out.add(hit.copyWith(name: hit.name.trim().isEmpty ? r.name : hit.name));
|
||||
} else {
|
||||
out.add(
|
||||
DanmakuSource(
|
||||
id: r.id.trim().isEmpty
|
||||
? 'src_${url.hashCode.toUnsigned(32).toRadixString(16)}'
|
||||
: r.id,
|
||||
name: r.name,
|
||||
url: url,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user