import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import '../core/network/dio_client.dart'; import '../data/models/asset/asset_statement.dart'; import '../data/services/asset_service.dart'; // ══════════════════════════════════════════════════════════════ // 全局缓存:币种列表 + 类型列表(不随页面销毁) // ══════════════════════════════════════════════════════════════ class _StatementFilterCache extends Notifier<({List coins, List types})> { @override ({List coins, List types}) build() { Future.microtask(_load); return (coins: const [], types: const []); } bool get isLoaded => state.coins.isNotEmpty || state.types.isNotEmpty; Future _load() async { try { final dio = ref.read(dioClientProvider); final service = AssetService(dio); final results = await Future.wait([ service.getStatementCoins(), service.getStatementTypes(), ]); state = ( coins: results[0] as List, types: results[1] as List, ); } catch (_) {} } /// 强制刷新(下拉刷新时调用) Future reload() => _load(); } final statementFilterCacheProvider = NotifierProvider<_StatementFilterCache, ({List coins, List types})>( _StatementFilterCache.new, ); // ══════════════════════════════════════════════════════════════ // Statement State // ══════════════════════════════════════════════════════════════ class StatementState { /// 流水记录 final List records; // ── 当前筛选条件(UI 选择,点搜索后才生效)── final String selectedCoinCode; final String selectedTypeId; final DateTime? startDate; final DateTime? endDate; final bool isLoading; final bool hasMore; final int currentPage; final String? errorMessage; const StatementState({ this.records = const [], this.selectedCoinCode = '', this.selectedTypeId = '', this.startDate, this.endDate, this.isLoading = false, this.hasMore = true, this.currentPage = 1, this.errorMessage, }); StatementState copyWith({ List? records, String? selectedCoinCode, String? selectedTypeId, DateTime? startDate, DateTime? endDate, bool clearStartDate = false, bool clearEndDate = false, bool? isLoading, bool? hasMore, int? currentPage, String? errorMessage, }) => StatementState( records: records ?? this.records, selectedCoinCode: selectedCoinCode ?? this.selectedCoinCode, selectedTypeId: selectedTypeId ?? this.selectedTypeId, startDate: clearStartDate ? null : (startDate ?? this.startDate), endDate: clearEndDate ? null : (endDate ?? this.endDate), isLoading: isLoading ?? this.isLoading, hasMore: hasMore ?? this.hasMore, currentPage: currentPage ?? this.currentPage, errorMessage: errorMessage, ); } // ══════════════════════════════════════════════════════════════ // Statement Notifier // ══════════════════════════════════════════════════════════════ class StatementNotifier extends AutoDisposeNotifier { static const _pageSize = 10; static final _dateFmt = DateFormat('yyyy-MM-dd HH:mm:ss'); // 搜索时才生效的筛选参数 String _searchCoin = ''; String _searchType = ''; String _searchStart = ''; String _searchEnd = ''; @override StatementState build() { // 只触发缓存加载,不建立 rebuild 依赖(避免缓存更新时重置用户选择) ref.read(statementFilterCacheProvider.notifier); Future.microtask(_loadRecords); return const StatementState(isLoading: true); } /// 只加载记录(币种/类型从缓存读取) Future _loadRecords() async { state = state.copyWith(isLoading: true, errorMessage: null); try { final dio = ref.read(dioClientProvider); final records = await AssetService(dio).getStatementList( pageNo: 1, pageSize: _pageSize, ); state = state.copyWith( records: records, isLoading: false, currentPage: 1, hasMore: records.length >= _pageSize, ); } catch (e) { state = state.copyWith(isLoading: false, errorMessage: e.toString()); } } // ── 筛选条件选择(仅更新 UI,不请求)── void selectCoin(String code) { state = state.copyWith(selectedCoinCode: code); } void selectType(String typeId) { state = state.copyWith(selectedTypeId: typeId); } void selectStartDate(DateTime date) { if (state.endDate != null && date.isAfter(state.endDate!)) return; state = state.copyWith(startDate: date); } void selectEndDate(DateTime date) { if (state.startDate != null && state.startDate!.isAfter(date)) return; state = state.copyWith(endDate: date); } // ── 搜索(收集筛选条件,重新请求)── Future search() async { if (state.startDate != null && state.endDate == null) { state = state.copyWith(errorMessage: 'errEnterEndTime'); return; } if (state.endDate != null && state.startDate == null) { state = state.copyWith(errorMessage: 'errEnterStartTime'); return; } _searchCoin = state.selectedCoinCode; _searchType = state.selectedTypeId; _searchStart = state.startDate != null ? _dateFmt.format(state.startDate!) : ''; _searchEnd = state.endDate != null ? _dateFmt.format(DateTime(state.endDate!.year, state.endDate!.month, state.endDate!.day, 23, 59, 59)) : ''; state = state.copyWith(isLoading: true, errorMessage: null); try { final dio = ref.read(dioClientProvider); final records = await AssetService(dio).getStatementList( type: _searchType, symbol: _searchCoin, startTime: _searchStart, endTime: _searchEnd, pageNo: 1, pageSize: _pageSize, ); state = state.copyWith( records: records, isLoading: false, currentPage: 1, hasMore: records.length >= _pageSize, ); } catch (e) { state = state.copyWith(isLoading: false, errorMessage: e.toString()); } } // ── 重置 ── void reset() { _searchCoin = ''; _searchType = ''; _searchStart = ''; _searchEnd = ''; state = state.copyWith( selectedCoinCode: '', selectedTypeId: '', clearStartDate: true, clearEndDate: true, ); search(); } // ── 下拉刷新(同时刷新缓存)── Future refresh() async { ref.read(statementFilterCacheProvider.notifier).reload(); await search(); } // ── 上拉加载更多 ── Future loadMore() async { if (state.isLoading || !state.hasMore) return; state = state.copyWith(isLoading: true); try { final nextPage = state.currentPage + 1; final dio = ref.read(dioClientProvider); final records = await AssetService(dio).getStatementList( type: _searchType, symbol: _searchCoin, startTime: _searchStart, endTime: _searchEnd, pageNo: nextPage, pageSize: _pageSize, ); state = state.copyWith( records: [...state.records, ...records], isLoading: false, currentPage: nextPage, hasMore: records.length >= _pageSize, ); } catch (e) { state = state.copyWith(isLoading: false, errorMessage: e.toString()); } } } final statementProvider = AutoDisposeNotifierProvider( StatementNotifier.new, );