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 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 buildZipFromSnapshot( Map snapshot, { Map profile = const {}, }) { final document = {...snapshot, 'profile': profile}; final ledgers = _indexById(snapshot['ledgers'] as List); final categories = _indexById(snapshot['categories'] as List); final transactions = (snapshot['transactions'] as List) .cast>(); final budgets = (snapshot['budgets'] as List) .cast>(); 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> _indexById(List items) => { for (final item in items.cast>()) item['id'] as int: item, }; static List _utf8Bom(String value) => [ 0xef, 0xbb, 0xbf, ...utf8.encode(value), ]; static String _csv(List> 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('"', '""')}"'; } }