import 'package:flutter/foundation.dart'; import 'package:miaoji_zhang/shared/api/api_client.dart'; import 'package:miaoji_zhang/shared/services/local_database.dart'; import 'package:miaoji_zhang/shared/services/session_store.dart'; class LedgerInfo { final int id; final String name; final String iconKey; final bool isDefault; final int transactionCount; const LedgerInfo({ required this.id, required this.name, required this.iconKey, required this.isDefault, required this.transactionCount, }); factory LedgerInfo.fromJson(Map json) => LedgerInfo( id: (json['id'] as num).toInt(), name: json['name'] as String, iconKey: json['iconKey'] as String? ?? 'wallet', isDefault: json['isDefault'] as bool? ?? false, transactionCount: (json['txCount'] as num?)?.toInt() ?? 0, ); } class CurrentLedgerStore extends ChangeNotifier { CurrentLedgerStore._(); static final instance = CurrentLedgerStore._(); final _dio = ApiClient.instance.dio; List _ledgers = const []; LedgerInfo? _current; Future? _loading; List get ledgers => List.unmodifiable(_ledgers); LedgerInfo? get current => _current; int? get currentId => _current?.id; String get currentName => _current?.name ?? '账本'; Future ensureLoaded({bool force = false}) { if (!force && _current != null) return Future.value(); return _loading ??= _load().whenComplete(() => _loading = null); } Future loadCached({bool force = false}) { if (!force && _current != null) return Future.value(); return _loading ??= _loadCached().whenComplete(() => _loading = null); } Future refreshRemote() async { if (!SessionStore.instance.isAccount || SessionStore.instance.shouldUseLocalOnly) { return; } final response = await _dio.get('/api/ledgers'); final values = response.data as List; LocalDatabase.instance.cacheLedgers(values); _apply(values); } Future _loadCached() async { _apply(LocalDatabase.instance.ledgers()); } Future _load() async { final session = SessionStore.instance; List values; if (session.shouldUseLocalOnly) { values = LocalDatabase.instance.ledgers(); } else { try { final response = await _dio.get('/api/ledgers'); values = response.data as List; LocalDatabase.instance.cacheLedgers(values); } catch (error) { if (!isConnectivityError(error) || !session.isAccount) rethrow; values = LocalDatabase.instance.ledgers(); } } _apply(values); } void _apply(List values) { final items = values .map((item) => LedgerInfo.fromJson(item as Map)) .toList(); final current = items.where((item) => item.isDefault).firstOrNull ?? (items.isEmpty ? null : items.first); final changed = _current?.id != current?.id || _ledgers.length != items.length || Iterable.generate(items.length).any((index) { final before = _ledgers[index]; final after = items[index]; return before.id != after.id || before.name != after.name || before.iconKey != after.iconKey || before.isDefault != after.isDefault || before.transactionCount != after.transactionCount; }); _ledgers = items; _current = current; if (changed) notifyListeners(); } Future select(int id) async { if (_current?.id == id) return; final session = SessionStore.instance; if (session.shouldUseLocalOnly || id < 0) { LocalDatabase.instance.selectLedger(id); if (session.isAccount && session.cloudSyncEnabled) { LocalDatabase.instance.enqueueSync('ledger', id, 'select', {'id': id}); } await ensureLoaded(force: true); return; } try { await _dio.put('/api/ledgers/$id/default'); } catch (error) { if (!isConnectivityError(error)) rethrow; LocalDatabase.instance.selectLedger(id); LocalDatabase.instance.enqueueSync('ledger', id, 'select', {'id': id}); } await ensureLoaded(force: true); } Future create(String name) async { final session = SessionStore.instance; if (session.shouldUseLocalOnly) { final id = LocalDatabase.instance.createLedger(name.trim()); if (session.isAccount && session.cloudSyncEnabled) { LocalDatabase.instance.enqueueSync('ledger', id, 'create', { 'name': name.trim(), 'iconKey': 'wallet', }); } await ensureLoaded(force: true); return; } try { await _dio.post( '/api/ledgers', data: {'name': name.trim(), 'iconKey': 'wallet'}, ); } catch (error) { if (!isConnectivityError(error)) rethrow; final id = LocalDatabase.instance.createLedger(name.trim()); LocalDatabase.instance.enqueueSync('ledger', id, 'create', { 'name': name.trim(), 'iconKey': 'wallet', }); } await ensureLoaded(force: true); } Future rename(int id, String name, String iconKey) async { final session = SessionStore.instance; if (session.shouldUseLocalOnly || id < 0) { LocalDatabase.instance.renameLedger(id, name.trim(), iconKey); if (session.isAccount && session.cloudSyncEnabled) { LocalDatabase.instance.enqueueSync('ledger', id, 'update', { 'name': name.trim(), 'iconKey': iconKey, }); } await ensureLoaded(force: true); return; } try { await _dio.put( '/api/ledgers/$id', data: {'name': name.trim(), 'iconKey': iconKey}, ); } catch (error) { if (!isConnectivityError(error)) rethrow; LocalDatabase.instance.renameLedger(id, name.trim(), iconKey); LocalDatabase.instance.enqueueSync('ledger', id, 'update', { 'name': name.trim(), 'iconKey': iconKey, }); } await ensureLoaded(force: true); } Future delete(int id) async { final session = SessionStore.instance; if (session.shouldUseLocalOnly || id < 0) { LocalDatabase.instance.deleteLedger(id); if (session.isAccount && session.cloudSyncEnabled) { LocalDatabase.instance.enqueueSync('ledger', id, 'delete', {'id': id}); } await ensureLoaded(force: true); return; } try { await _dio.delete('/api/ledgers/$id'); } catch (error) { if (!isConnectivityError(error)) rethrow; LocalDatabase.instance.deleteLedger(id); LocalDatabase.instance.enqueueSync('ledger', id, 'delete', {'id': id}); } await ensureLoaded(force: true); } void clear() { _ledgers = const []; _current = null; notifyListeners(); } }