rwadurian/frontend/mining-app/lib/presentation/providers/contribution_providers.dart

203 lines
5.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<GetUserContribution>((ref) {
return getIt<GetUserContribution>();
});
final contributionRepositoryProvider = Provider<ContributionRepository>((ref) {
return getIt<ContributionRepository>();
});
final contributionProvider = FutureProvider.family<Contribution?, String>(
(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<ContributionRecordsPage?, ContributionRecordsParams>(
(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<ContributionStats?>((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<EstimatedEarnings, String>((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;
}
// 每日预估 = 每秒收益 × 8640060×60×24
final dailyShares = perSecondEarning * 86400;
return EstimatedEarnings(
dailyShares: dailyShares.toStringAsFixed(4),
perSecondShares: perSecondEarning.toStringAsFixed(8),
isValid: true,
);
});
/// 控制是否隐藏金额显示的状态
final hideAmountsProvider = StateProvider<bool>((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<String, dynamic> 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<SharePoolBalance>((ref) async {
final client = getIt<ApiClient>();
try {
final response = await client.get(ApiEndpoints.sharePoolBalance);
return SharePoolBalance.fromJson(response.data as Map<String, dynamic>);
} catch (e) {
return SharePoolBalance.zero;
}
});