fix(mining-app): 统一贡献值页与兑换页的剩余积分股计算方式
贡献值页原先使用 sharePoolBalance API (Pool A + Pool B 余额) 显示剩余积分股, 兑换页使用公式 totalShares - totalMined - blackHoleAmount 计算。 两者显示结果不一致 (差异约 228,343 积分股)。 根本原因调查: - mining-wallet-service 的 Kafka 消费者在服务重启期间丢失了部分事件 - Pool B 仅处理了 15.7% 的挖矿分配事件 (28,261/180,497) - Pool A 遗漏了 66,591 笔销毁扣减事件 - 池账户余额是通过 Kafka 事件维护的记账台账,存在消费遗漏 - totalShares - totalMined - blackHoleAmount 公式基于实际挖矿和销毁数据计算,是数学上的 ground truth - 实际用户分配和销毁均 100% 准确,仅池账户记账有偏差 修复方案: - 贡献值页改用 marketOverviewProvider (同兑换页) - 使用公式 totalShares - totalMined - blackHoleAmount 计算剩余积分股 - 两个页面现在显示完全一致的数据 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a7dd926877
commit
0576733579
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:decimal/decimal.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
@ -5,10 +6,12 @@ import '../../../core/constants/app_colors.dart';
|
||||||
import '../../../core/router/routes.dart';
|
import '../../../core/router/routes.dart';
|
||||||
import '../../../core/utils/format_utils.dart';
|
import '../../../core/utils/format_utils.dart';
|
||||||
import '../../../domain/entities/contribution.dart';
|
import '../../../domain/entities/contribution.dart';
|
||||||
|
import '../../../domain/entities/market_overview.dart';
|
||||||
import '../../../domain/entities/share_account.dart';
|
import '../../../domain/entities/share_account.dart';
|
||||||
import '../../providers/user_providers.dart';
|
import '../../providers/user_providers.dart';
|
||||||
import '../../providers/contribution_providers.dart';
|
import '../../providers/contribution_providers.dart';
|
||||||
import '../../providers/mining_providers.dart';
|
import '../../providers/mining_providers.dart';
|
||||||
|
import '../../providers/trading_providers.dart';
|
||||||
import '../../widgets/shimmer_loading.dart';
|
import '../../widgets/shimmer_loading.dart';
|
||||||
|
|
||||||
class ContributionPage extends ConsumerWidget {
|
class ContributionPage extends ConsumerWidget {
|
||||||
|
|
@ -25,8 +28,8 @@ class ContributionPage extends ConsumerWidget {
|
||||||
final contributionAsync = ref.watch(contributionProvider(accountSequence));
|
final contributionAsync = ref.watch(contributionProvider(accountSequence));
|
||||||
// 获取预估收益(基于用户每秒收益计算)
|
// 获取预估收益(基于用户每秒收益计算)
|
||||||
final estimatedEarnings = ref.watch(estimatedEarningsProvider(accountSequence));
|
final estimatedEarnings = ref.watch(estimatedEarningsProvider(accountSequence));
|
||||||
// 获取积分股池余量
|
// 获取市场概览(用于计算剩余积分股 = totalShares - totalMined - blackHoleAmount)
|
||||||
final sharePoolAsync = ref.watch(sharePoolBalanceProvider);
|
final marketAsync = ref.watch(marketOverviewProvider);
|
||||||
// 获取挖矿账户信息(含首次挖矿时间,用于730天倒计时)
|
// 获取挖矿账户信息(含首次挖矿时间,用于730天倒计时)
|
||||||
final shareAccountAsync = ref.watch(shareAccountProvider(accountSequence));
|
final shareAccountAsync = ref.watch(shareAccountProvider(accountSequence));
|
||||||
|
|
||||||
|
|
@ -35,8 +38,8 @@ class ContributionPage extends ConsumerWidget {
|
||||||
final contribution = contributionAsync.valueOrNull;
|
final contribution = contributionAsync.valueOrNull;
|
||||||
final hasError = contributionAsync.hasError;
|
final hasError = contributionAsync.hasError;
|
||||||
final error = contributionAsync.error;
|
final error = contributionAsync.error;
|
||||||
final isSharePoolLoading = sharePoolAsync.isLoading;
|
final isMarketLoading = marketAsync.isLoading;
|
||||||
final sharePoolBalance = sharePoolAsync.valueOrNull;
|
final market = marketAsync.valueOrNull;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColors.backgroundOf(context),
|
backgroundColor: AppColors.backgroundOf(context),
|
||||||
|
|
@ -45,7 +48,7 @@ class ContributionPage extends ConsumerWidget {
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
ref.invalidate(contributionProvider(accountSequence));
|
ref.invalidate(contributionProvider(accountSequence));
|
||||||
ref.invalidate(sharePoolBalanceProvider);
|
ref.invalidate(marketOverviewProvider);
|
||||||
ref.invalidate(shareAccountProvider(accountSequence));
|
ref.invalidate(shareAccountProvider(accountSequence));
|
||||||
},
|
},
|
||||||
child: hasError && contribution == null
|
child: hasError && contribution == null
|
||||||
|
|
@ -74,7 +77,7 @@ class ContributionPage extends ConsumerWidget {
|
||||||
sliver: SliverList(
|
sliver: SliverList(
|
||||||
delegate: SliverChildListDelegate([
|
delegate: SliverChildListDelegate([
|
||||||
// 总贡献值卡片
|
// 总贡献值卡片
|
||||||
_buildTotalContributionCard(context, ref, contribution, isLoading, sharePoolBalance, isSharePoolLoading),
|
_buildTotalContributionCard(context, ref, contribution, isLoading, market, isMarketLoading),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
// 三栏统计
|
// 三栏统计
|
||||||
_buildThreeColumnStats(context, ref, contribution, isLoading),
|
_buildThreeColumnStats(context, ref, contribution, isLoading),
|
||||||
|
|
@ -165,8 +168,8 @@ class ContributionPage extends ConsumerWidget {
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
Contribution? contribution,
|
Contribution? contribution,
|
||||||
bool isLoading,
|
bool isLoading,
|
||||||
SharePoolBalance? sharePoolBalance,
|
MarketOverview? market,
|
||||||
bool isSharePoolLoading,
|
bool isMarketLoading,
|
||||||
) {
|
) {
|
||||||
final isDark = AppColors.isDark(context);
|
final isDark = AppColors.isDark(context);
|
||||||
final total = contribution?.totalContribution ?? '0';
|
final total = contribution?.totalContribution ?? '0';
|
||||||
|
|
@ -219,13 +222,21 @@ class ContributionPage extends ConsumerWidget {
|
||||||
'100亿销毁剩余量: ',
|
'100亿销毁剩余量: ',
|
||||||
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
|
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
|
||||||
),
|
),
|
||||||
isSharePoolLoading
|
isMarketLoading
|
||||||
? const ShimmerText(
|
? const ShimmerText(
|
||||||
placeholder: '----',
|
placeholder: '----',
|
||||||
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: _orange),
|
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: _orange),
|
||||||
)
|
)
|
||||||
: Text(
|
: Text(
|
||||||
hideAmounts ? '******' : formatAmount(sharePoolBalance?.total ?? '0'),
|
hideAmounts
|
||||||
|
? '******'
|
||||||
|
: market != null
|
||||||
|
? formatAmount(
|
||||||
|
(Decimal.parse(market.totalShares) -
|
||||||
|
Decimal.parse(market.totalMined) -
|
||||||
|
Decimal.parse(market.blackHoleAmount))
|
||||||
|
.toString())
|
||||||
|
: '0',
|
||||||
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: _orange),
|
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: _orange),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue