Initial project import
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:miaoji_zhang/shared/services/local_database.dart';
|
||||
import 'package:miaoji_zhang/shared/services/session_store.dart';
|
||||
|
||||
class LocalExportService {
|
||||
const LocalExportService._();
|
||||
|
||||
static List<int> buildZip() {
|
||||
final snapshot = LocalDatabase.instance.exportSnapshot();
|
||||
final session = SessionStore.instance;
|
||||
return buildZipFromSnapshot(
|
||||
snapshot,
|
||||
profile: {
|
||||
'mode': session.isGuest ? 'guest' : 'account',
|
||||
'userId': session.userId,
|
||||
'username': session.username,
|
||||
'nickname': session.nickname,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static List<int> buildZipFromSnapshot(
|
||||
Map<String, dynamic> snapshot, {
|
||||
Map<String, dynamic> profile = const {},
|
||||
}) {
|
||||
final document = <String, dynamic>{...snapshot, 'profile': profile};
|
||||
final ledgers = _indexById(snapshot['ledgers'] as List<dynamic>);
|
||||
final categories = _indexById(snapshot['categories'] as List<dynamic>);
|
||||
final transactions = (snapshot['transactions'] as List<dynamic>)
|
||||
.cast<Map<String, dynamic>>();
|
||||
final budgets = (snapshot['budgets'] as List<dynamic>)
|
||||
.cast<Map<String, dynamic>>();
|
||||
|
||||
final archive = Archive();
|
||||
archive.addFile(
|
||||
ArchiveFile.bytes(
|
||||
'transactions.csv',
|
||||
_utf8Bom(
|
||||
_csv([
|
||||
const [
|
||||
'id',
|
||||
'ledger',
|
||||
'type',
|
||||
'amount',
|
||||
'category',
|
||||
'note',
|
||||
'payment_method',
|
||||
'source',
|
||||
'occurred_at',
|
||||
'is_deleted',
|
||||
'updated_at',
|
||||
],
|
||||
for (final item in transactions)
|
||||
[
|
||||
item['id'],
|
||||
ledgers[item['ledgerId']]?['name'],
|
||||
item['type'],
|
||||
(item['amount'] as num).toStringAsFixed(2),
|
||||
item['categoryName'],
|
||||
item['note'],
|
||||
item['paymentMethod'],
|
||||
item['source'],
|
||||
item['occurredAt'],
|
||||
item['isDeleted'],
|
||||
item['updatedAt'],
|
||||
],
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
archive.addFile(
|
||||
ArchiveFile.bytes(
|
||||
'budgets.csv',
|
||||
_utf8Bom(
|
||||
_csv([
|
||||
const [
|
||||
'ledger',
|
||||
'period',
|
||||
'category',
|
||||
'amount',
|
||||
'recurring',
|
||||
'updated_at',
|
||||
],
|
||||
for (final item in budgets)
|
||||
[
|
||||
ledgers[item['ledgerId']]?['name'],
|
||||
item['period'],
|
||||
item['categoryId'] == null
|
||||
? 'total'
|
||||
: categories[item['categoryId']]?['name'],
|
||||
(item['amount'] as num).toStringAsFixed(2),
|
||||
item['recurring'],
|
||||
item['updatedAt'],
|
||||
],
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
archive.addFile(
|
||||
ArchiveFile.string(
|
||||
'jizhi-backup.json',
|
||||
const JsonEncoder.withIndent(' ').convert(document),
|
||||
),
|
||||
);
|
||||
return ZipEncoder().encode(archive);
|
||||
}
|
||||
|
||||
static Map<int, Map<String, dynamic>> _indexById(List<dynamic> items) => {
|
||||
for (final item in items.cast<Map<String, dynamic>>())
|
||||
item['id'] as int: item,
|
||||
};
|
||||
|
||||
static List<int> _utf8Bom(String value) => [
|
||||
0xef,
|
||||
0xbb,
|
||||
0xbf,
|
||||
...utf8.encode(value),
|
||||
];
|
||||
|
||||
static String _csv(List<List<Object?>> rows) =>
|
||||
rows.map((row) => row.map(_csvCell).join(',')).join('\r\n');
|
||||
|
||||
static String _csvCell(Object? value) {
|
||||
final text = value?.toString() ?? '';
|
||||
if (!text.contains(RegExp('[,"\r\n]'))) return text;
|
||||
return '"${text.replaceAll('"', '""')}"';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user