179 lines
5.0 KiB
Dart
179 lines
5.0 KiB
Dart
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);
|