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

179 lines
5.0 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';
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,
);
});
/// 计算用户今日预估收益
/// 公式: (用户贡献值 / 全网总贡献值) × 每日发放量
/// 每日发放量 = 每秒发放量 × 86400
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,
);
}
/// 每日发放总量配置(积分股)
/// 第1纪元: 100M / (2年 × 365天) ≈ 136,986 积分股/天
const double dailyAllocationTotal = 136986.0;
final estimatedEarningsProvider = Provider.family<EstimatedEarnings, String>((ref, accountSequence) {
final contributionAsync = ref.watch(contributionProvider(accountSequence));
final statsAsync = ref.watch(contributionStatsProvider);
final contribution = contributionAsync.valueOrNull;
final stats = statsAsync.valueOrNull;
if (contribution == null || stats == null) {
return EstimatedEarnings.zero;
}
final userContribution = double.tryParse(contribution.totalContribution) ?? 0;
final totalContribution = double.tryParse(stats.totalContribution) ?? 0;
if (totalContribution <= 0 || userContribution <= 0) {
return EstimatedEarnings.zero;
}
// 用户占比
final ratio = userContribution / totalContribution;
// 每日预估
final dailyShares = ratio * dailyAllocationTotal;
// 每秒预估
final perSecondShares = dailyShares / 86400;
return EstimatedEarnings(
dailyShares: dailyShares.toStringAsFixed(4),
perSecondShares: perSecondShares.toStringAsFixed(8),
isValid: true,
);
});
/// 控制是否隐藏金额显示的状态
final hideAmountsProvider = StateProvider<bool>((ref) => false);