refactor(reward): 前端直接从 reward-service 查询奖励数据

移除 reward-service 和 wallet-service 之间的 Kafka 同步机制:
- 屏蔽 reward-service 的 outbox 发布逻辑
- 屏蔽 wallet-service 的 RewardEventConsumerController 订阅

Flutter 前端改为直接调用 reward-service API:
- 新增 RewardService 调用 /rewards/summary
- profile_page 改用 rewardService.getMyRewardSummary()

这样简化了架构,数据从源头获取,避免同步问题。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-11 00:36:53 -08:00
parent a1ca490ff4
commit 7d9837fc22
5 changed files with 161 additions and 37 deletions

View File

@ -78,7 +78,8 @@ export class RewardApplicationService {
}
// 4. 写入 Outbox 发送到 wallet-service 同步汇总数据
await this.publishSummaryUpdatesToOutbox(updatedSummaries);
// [已屏蔽] 前端直接从 reward-service 查询,不再同步到 wallet-service
// await this.publishSummaryUpdatesToOutbox(updatedSummaries);
// 5. 发布领域事件
for (const reward of rewards) {
@ -123,9 +124,10 @@ export class RewardApplicationService {
await this.rewardSummaryRepository.save(summary);
// 写入 Outbox 同步到 wallet-service
if (claimedCount > 0) {
await this.publishSummaryUpdatesToOutbox([summary]);
}
// [已屏蔽] 前端直接从 reward-service 查询,不再同步到 wallet-service
// if (claimedCount > 0) {
// await this.publishSummaryUpdatesToOutbox([summary]);
// }
this.logger.log(`Claimed ${claimedCount} rewards for user ${userId}, total ${totalUsdtClaimed} USDT`);
@ -198,7 +200,8 @@ export class RewardApplicationService {
await this.rewardSummaryRepository.save(summary);
// 写入 Outbox 同步到 wallet-service
await this.publishSummaryUpdatesToOutbox([summary]);
// [已屏蔽] 前端直接从 reward-service 查询,不再同步到 wallet-service
// await this.publishSummaryUpdatesToOutbox([summary]);
}
this.logger.log(`Settled ${totalUsdt} USDT for accountSequence ${params.accountSequence}`);
@ -269,9 +272,10 @@ export class RewardApplicationService {
}
// 写入 Outbox 同步到 wallet-service
if (updatedSummaries.length > 0) {
await this.publishSummaryUpdatesToOutbox(updatedSummaries);
}
// [已屏蔽] 前端直接从 reward-service 查询,不再同步到 wallet-service
// if (updatedSummaries.length > 0) {
// await this.publishSummaryUpdatesToOutbox(updatedSummaries);
// }
this.logger.log(`Expired ${expiredRewards.length} rewards, total ${totalUsdtExpired} USDT`);

View File

@ -3,8 +3,9 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { EventPublisherService } from './event-publisher.service';
import { DepositEventConsumerService } from './deposit-event-consumer.service';
import { RewardEventConsumerController } from './reward-event-consumer.controller';
import { EventAckPublisher } from './event-ack.publisher';
// [已屏蔽] 前端直接从 reward-service 查询,不再订阅 reward-service 消息
// import { RewardEventConsumerController } from './reward-event-consumer.controller';
// import { EventAckPublisher } from './event-ack.publisher';
import { PrismaService } from '../persistence/prisma/prisma.service';
@Global()
@ -30,8 +31,10 @@ import { PrismaService } from '../persistence/prisma/prisma.service';
},
]),
],
controllers: [RewardEventConsumerController],
providers: [PrismaService, EventPublisherService, DepositEventConsumerService, EventAckPublisher],
exports: [EventPublisherService, DepositEventConsumerService, EventAckPublisher, ClientsModule],
// [已屏蔽] 前端直接从 reward-service 查询,不再订阅 reward-service 消息
// controllers: [RewardEventConsumerController],
controllers: [],
providers: [PrismaService, EventPublisherService, DepositEventConsumerService],
exports: [EventPublisherService, DepositEventConsumerService, ClientsModule],
})
export class KafkaModule {}

View File

@ -8,6 +8,7 @@ import '../services/authorization_service.dart';
import '../services/deposit_service.dart';
import '../services/wallet_service.dart';
import '../services/planting_service.dart';
import '../services/reward_service.dart';
// Storage Providers
final secureStorageProvider = Provider<SecureStorage>((ref) {
@ -65,6 +66,12 @@ final plantingServiceProvider = Provider<PlantingService>((ref) {
return PlantingService(apiClient: apiClient);
});
// Reward Service Provider ( reward-service)
final rewardServiceProvider = Provider<RewardService>((ref) {
final apiClient = ref.watch(apiClientProvider);
return RewardService(apiClient: apiClient);
});
// Override provider with initialized instance
ProviderContainer createProviderContainer(LocalStorage localStorage) {
return ProviderContainer(

View File

@ -0,0 +1,110 @@
import 'package:flutter/foundation.dart';
import '../network/api_client.dart';
/// ( reward-service )
class RewardSummary {
final double pendingUsdt;
final double pendingHashpower;
final DateTime? pendingExpireAt;
final int pendingRemainingTimeMs;
final double settleableUsdt;
final double settleableHashpower;
final double settledTotalUsdt;
final double settledTotalHashpower;
final double expiredTotalUsdt;
final double expiredTotalHashpower;
RewardSummary({
required this.pendingUsdt,
required this.pendingHashpower,
this.pendingExpireAt,
required this.pendingRemainingTimeMs,
required this.settleableUsdt,
required this.settleableHashpower,
required this.settledTotalUsdt,
required this.settledTotalHashpower,
required this.expiredTotalUsdt,
required this.expiredTotalHashpower,
});
factory RewardSummary.fromJson(Map<String, dynamic> json) {
return RewardSummary(
pendingUsdt: (json['pendingUsdt'] ?? 0).toDouble(),
pendingHashpower: (json['pendingHashpower'] ?? 0).toDouble(),
pendingExpireAt: json['pendingExpireAt'] != null
? DateTime.tryParse(json['pendingExpireAt'])
: null,
pendingRemainingTimeMs: (json['pendingRemainingTimeMs'] ?? 0).toInt(),
settleableUsdt: (json['settleableUsdt'] ?? 0).toDouble(),
settleableHashpower: (json['settleableHashpower'] ?? 0).toDouble(),
settledTotalUsdt: (json['settledTotalUsdt'] ?? 0).toDouble(),
settledTotalHashpower: (json['settledTotalHashpower'] ?? 0).toDouble(),
expiredTotalUsdt: (json['expiredTotalUsdt'] ?? 0).toDouble(),
expiredTotalHashpower: (json['expiredTotalHashpower'] ?? 0).toDouble(),
);
}
///
int get pendingRemainingSeconds => (pendingRemainingTimeMs / 1000).round();
}
///
///
/// reward-service API
/// wallet-service
class RewardService {
final ApiClient _apiClient;
RewardService({required ApiClient apiClient}) : _apiClient = apiClient;
///
///
/// GET /rewards/summary (reward-service)
Future<RewardSummary> getMyRewardSummary() async {
try {
debugPrint('[RewardService] ========== 获取奖励汇总 ==========');
debugPrint('[RewardService] 请求: GET /rewards/summary');
final response = await _apiClient.get('/rewards/summary');
debugPrint('[RewardService] 响应状态码: ${response.statusCode}');
debugPrint('[RewardService] 响应数据类型: ${response.data.runtimeType}');
if (response.statusCode == 200) {
final responseData = response.data as Map<String, dynamic>;
debugPrint('[RewardService] 原始响应数据: $responseData');
// API : { success: true, data: {...}, timestamp: "..." }
// data
final data = responseData['data'] as Map<String, dynamic>? ?? responseData;
debugPrint('[RewardService] 解包后数据: $data');
final summary = RewardSummary.fromJson(data);
debugPrint('[RewardService] -------- 解析结果 --------');
debugPrint('[RewardService] pendingUsdt: ${summary.pendingUsdt}');
debugPrint('[RewardService] pendingHashpower: ${summary.pendingHashpower}');
debugPrint('[RewardService] pendingExpireAt: ${summary.pendingExpireAt}');
debugPrint('[RewardService] pendingRemainingTimeMs: ${summary.pendingRemainingTimeMs}');
debugPrint('[RewardService] pendingRemainingSeconds: ${summary.pendingRemainingSeconds}');
debugPrint('[RewardService] settleableUsdt: ${summary.settleableUsdt}');
debugPrint('[RewardService] settleableHashpower: ${summary.settleableHashpower}');
debugPrint('[RewardService] settledTotalUsdt: ${summary.settledTotalUsdt}');
debugPrint('[RewardService] settledTotalHashpower: ${summary.settledTotalHashpower}');
debugPrint('[RewardService] expiredTotalUsdt: ${summary.expiredTotalUsdt}');
debugPrint('[RewardService] expiredTotalHashpower: ${summary.expiredTotalHashpower}');
debugPrint('[RewardService] ================================');
return summary;
}
debugPrint('[RewardService] 请求失败,状态码: ${response.statusCode}');
debugPrint('[RewardService] 响应内容: ${response.data}');
throw Exception('获取奖励汇总失败: ${response.statusCode}');
} catch (e, stackTrace) {
debugPrint('[RewardService] !!!!!!!!!! 获取奖励汇总异常 !!!!!!!!!!');
debugPrint('[RewardService] 错误: $e');
debugPrint('[RewardService] 堆栈: $stackTrace');
rethrow;
}
}
}

View File

@ -58,7 +58,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
int _communityMonthlyTarget = 10; //
int _communityMonthIndex = 0; //
// wallet-service
// reward-service
double _pendingUsdt = 0.0;
double _pendingPower = 0.0;
double _settleableUsdt = 0.0;
@ -344,41 +344,41 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
}
}
/// (from wallet-service)
/// ( reward-service )
Future<void> _loadWalletData() async {
try {
debugPrint('[ProfilePage] ========== 加载钱包数据 ==========');
debugPrint('[ProfilePage] ========== 加载收益数据 ==========');
debugPrint('[ProfilePage] mounted: $mounted');
setState(() {
_isLoadingWallet = true;
_walletError = null;
});
debugPrint('[ProfilePage] 获取 walletServiceProvider...');
final walletService = ref.read(walletServiceProvider);
debugPrint('[ProfilePage] 调用 getMyWallet()...');
final wallet = await walletService.getMyWallet();
debugPrint('[ProfilePage] 获取 rewardServiceProvider...');
final rewardService = ref.read(rewardServiceProvider);
debugPrint('[ProfilePage] 调用 getMyRewardSummary()...');
final summary = await rewardService.getMyRewardSummary();
debugPrint('[ProfilePage] -------- 钱包数据加载成功 --------');
debugPrint('[ProfilePage] 待领取 USDT: ${wallet.rewards.pendingUsdt}');
debugPrint('[ProfilePage] 待领取 算力: ${wallet.rewards.pendingHashpower}');
debugPrint('[ProfilePage] 可结算 USDT: ${wallet.rewards.settleableUsdt}');
debugPrint('[ProfilePage] 已结算 USDT: ${wallet.rewards.settledTotalUsdt}');
debugPrint('[ProfilePage] 已过期 USDT: ${wallet.rewards.expiredTotalUsdt}');
debugPrint('[ProfilePage] 已过期 算力: ${wallet.rewards.expiredTotalHashpower}');
debugPrint('[ProfilePage] 过期时间: ${wallet.rewards.pendingExpireAt}');
debugPrint('[ProfilePage] 剩余秒数: ${wallet.rewards.pendingRemainingSeconds}');
debugPrint('[ProfilePage] -------- 收益数据加载成功 --------');
debugPrint('[ProfilePage] 待领取 USDT: ${summary.pendingUsdt}');
debugPrint('[ProfilePage] 待领取 算力: ${summary.pendingHashpower}');
debugPrint('[ProfilePage] 可结算 USDT: ${summary.settleableUsdt}');
debugPrint('[ProfilePage] 已结算 USDT: ${summary.settledTotalUsdt}');
debugPrint('[ProfilePage] 已过期 USDT: ${summary.expiredTotalUsdt}');
debugPrint('[ProfilePage] 已过期 算力: ${summary.expiredTotalHashpower}');
debugPrint('[ProfilePage] 过期时间: ${summary.pendingExpireAt}');
debugPrint('[ProfilePage] 剩余秒数: ${summary.pendingRemainingSeconds}');
if (mounted) {
debugPrint('[ProfilePage] 更新 UI 状态...');
setState(() {
_pendingUsdt = wallet.rewards.pendingUsdt;
_pendingPower = wallet.rewards.pendingHashpower;
_settleableUsdt = wallet.rewards.settleableUsdt;
_settledUsdt = wallet.rewards.settledTotalUsdt;
_expiredUsdt = wallet.rewards.expiredTotalUsdt;
_expiredPower = wallet.rewards.expiredTotalHashpower;
_remainingSeconds = wallet.rewards.pendingRemainingSeconds;
_pendingUsdt = summary.pendingUsdt;
_pendingPower = summary.pendingHashpower;
_settleableUsdt = summary.settleableUsdt;
_settledUsdt = summary.settledTotalUsdt;
_expiredUsdt = summary.expiredTotalUsdt;
_expiredPower = summary.expiredTotalHashpower;
_remainingSeconds = summary.pendingRemainingSeconds;
_isLoadingWallet = false;
});
@ -398,7 +398,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
}
debugPrint('[ProfilePage] ================================');
} catch (e, stackTrace) {
debugPrint('[ProfilePage] !!!!!!!!!! 加载钱包数据失败 !!!!!!!!!!');
debugPrint('[ProfilePage] !!!!!!!!!! 加载收益数据失败 !!!!!!!!!!');
debugPrint('[ProfilePage] 错误类型: ${e.runtimeType}');
debugPrint('[ProfilePage] 错误信息: $e');
debugPrint('[ProfilePage] 堆栈跟踪:');