import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../domain/entities/contribution.dart'; import '../../domain/entities/contribution_record.dart'; import '../../domain/entities/contribution_stats.dart'; import '../../domain/usecases/contribution/get_user_contribution.dart'; import '../../domain/repositories/contribution_repository.dart'; import '../../core/di/injection.dart'; import '../../core/network/api_client.dart'; import '../../core/network/api_endpoints.dart'; import 'mining_providers.dart'; final getUserContributionUseCaseProvider = Provider((ref) { return getIt(); }); final contributionRepositoryProvider = Provider((ref) { return getIt(); }); final contributionProvider = FutureProvider.family( (ref, accountSequence) async { // 空字符串不请求 if (accountSequence.isEmpty) return null; final useCase = ref.watch(getUserContributionUseCaseProvider); final result = await useCase(accountSequence); // 保持 provider 活跃,避免重复请求 ref.keepAlive(); // 5 分钟后自动失效 final timer = Timer(const Duration(minutes: 5), () { ref.invalidateSelf(); }); ref.onDispose(() => timer.cancel()); return result.fold( (failure) => throw Exception(failure.message), (contribution) => contribution, ); }, ); /// 贡献值记录请求参数 class ContributionRecordsParams { final String accountSequence; final int page; final int pageSize; const ContributionRecordsParams({ required this.accountSequence, this.page = 1, this.pageSize = 10, }); @override bool operator ==(Object other) => identical(this, other) || other is ContributionRecordsParams && runtimeType == other.runtimeType && accountSequence == other.accountSequence && page == other.page && pageSize == other.pageSize; @override int get hashCode => accountSequence.hashCode ^ page.hashCode ^ pageSize.hashCode; } /// 贡献值记录 Provider final contributionRecordsProvider = FutureProvider.family( (ref, params) async { // 空字符串不请求 if (params.accountSequence.isEmpty) { return null; } final repository = ref.watch(contributionRepositoryProvider); final result = await repository.getContributionRecords( params.accountSequence, page: params.page, pageSize: params.pageSize, ); // 保持 provider 活跃 ref.keepAlive(); // 5 分钟后自动失效 final timer = Timer(const Duration(minutes: 5), () { ref.invalidateSelf(); }); ref.onDispose(() => timer.cancel()); return result.fold( (failure) => throw Exception(failure.message), (records) => records, ); }, ); /// 全网贡献值统计 Provider final contributionStatsProvider = FutureProvider((ref) async { final repository = ref.watch(contributionRepositoryProvider); final result = await repository.getContributionStats(); // 保持 provider 活跃 ref.keepAlive(); // 5 分钟后自动失效 final timer = Timer(const Duration(minutes: 5), () { ref.invalidateSelf(); }); ref.onDispose(() => timer.cancel()); return result.fold( (failure) => null, (stats) => stats, ); }); /// 计算用户今日预估收益 /// 公式: 用户每秒收益 × 60分钟 × 60秒 × 24小时 class EstimatedEarnings { final String dailyShares; final String perSecondShares; final bool isValid; const EstimatedEarnings({ required this.dailyShares, required this.perSecondShares, required this.isValid, }); static const zero = EstimatedEarnings( dailyShares: '0', perSecondShares: '0', isValid: false, ); } final estimatedEarningsProvider = Provider.family((ref, accountSequence) { // 从 mining-service 获取用户每秒收益 final shareAccountAsync = ref.watch(shareAccountProvider(accountSequence)); final shareAccount = shareAccountAsync.valueOrNull; if (shareAccount == null) { return EstimatedEarnings.zero; } final perSecondEarning = double.tryParse(shareAccount.perSecondEarning) ?? 0; if (perSecondEarning <= 0) { return EstimatedEarnings.zero; } // 每日预估 = 每秒收益 × 86400(60×60×24) final dailyShares = perSecondEarning * 86400; return EstimatedEarnings( dailyShares: dailyShares.toStringAsFixed(4), perSecondShares: perSecondEarning.toStringAsFixed(8), isValid: true, ); }); /// 控制是否隐藏金额显示的状态 final hideAmountsProvider = StateProvider((ref) => false); /// 积分股池余量数据类 class SharePoolBalance { final String poolA; final String poolB; final String total; const SharePoolBalance({ required this.poolA, required this.poolB, required this.total, }); factory SharePoolBalance.fromJson(Map json) { return SharePoolBalance( poolA: json['poolA'] ?? '0', poolB: json['poolB'] ?? '0', total: json['total'] ?? '0', ); } static const zero = SharePoolBalance(poolA: '0', poolB: '0', total: '0'); } /// 积分股池总余量 Provider final sharePoolBalanceProvider = FutureProvider((ref) async { final client = getIt(); try { final response = await client.get(ApiEndpoints.sharePoolBalance); return SharePoolBalance.fromJson(response.data as Map); } catch (e) { return SharePoolBalance.zero; } });