feat(reward): 可结算列表改为从 reward-service 读取

将前端可结算奖励列表的数据源从 wallet-service 改为 reward-service:
- 后端:在 reward-service 添加 GET /rewards/settleable 接口
- 前端:修改 getSettleableRewards() 调用 /rewards/settleable
- 前端:更新 SettleableRewardItem 字段映射 (rightType, claimedAt, sourceOrderNo, memo)

解决权益收益(社区权益、市区域权益)无法在可结算列表显示的问题。
遵循单一数据源原则,reward-service 是奖励的权威数据源。

🤖 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-17 06:55:41 -08:00
parent 834a1fc0b0
commit 36074948d7
4 changed files with 57 additions and 26 deletions

View File

@ -58,4 +58,12 @@ export class RewardController {
const accountSequence = req.user.accountSequence; const accountSequence = req.user.accountSequence;
return this.rewardService.getPendingRewards(accountSequence); return this.rewardService.getPendingRewards(accountSequence);
} }
@Get('settleable')
@ApiOperation({ summary: '获取可结算奖励列表' })
@ApiResponse({ status: 200, description: '成功' })
async getSettleable(@Request() req) {
const accountSequence = req.user.accountSequence;
return this.rewardService.getSettleableRewards(accountSequence);
}
} }

View File

@ -444,6 +444,24 @@ export class RewardApplicationService {
})); }));
} }
/**
*
*/
async getSettleableRewards(accountSequence: string) {
const rewards = await this.rewardLedgerEntryRepository.findSettleableByAccountSequence(accountSequence);
return rewards.map(r => ({
id: r.id?.toString(),
rightType: r.rewardSource.rightType,
usdtAmount: r.usdtAmount.amount,
hashpowerAmount: r.hashpowerAmount.value,
createdAt: r.createdAt,
claimedAt: r.claimedAt,
sourceOrderNo: r.sourceOrderNo,
memo: r.memo,
}));
}
/** /**
* Outbox wallet-service * Outbox wallet-service
*/ */

View File

@ -63,40 +63,43 @@ class PendingRewardItem {
String get rightTypeName => getAllocationTypeName(rightType); String get rightTypeName => getAllocationTypeName(rightType);
} }
/// ( GET /wallet/settleable-rewards ) /// ( GET /rewards/settleable )
class SettleableRewardItem { class SettleableRewardItem {
final String id; final String id;
final String allocationType; final String rightType;
final double usdtAmount; final double usdtAmount;
final double hashpowerAmount; final double hashpowerAmount;
final DateTime createdAt; final DateTime createdAt;
final DateTime settledAt; final DateTime? claimedAt;
final String sourceOrderId; final String sourceOrderNo;
final String memo;
SettleableRewardItem({ SettleableRewardItem({
required this.id, required this.id,
required this.allocationType, required this.rightType,
required this.usdtAmount, required this.usdtAmount,
required this.hashpowerAmount, required this.hashpowerAmount,
required this.createdAt, required this.createdAt,
required this.settledAt, this.claimedAt,
required this.sourceOrderId, required this.sourceOrderNo,
required this.memo,
}); });
factory SettleableRewardItem.fromJson(Map<String, dynamic> json) { factory SettleableRewardItem.fromJson(Map<String, dynamic> json) {
return SettleableRewardItem( return SettleableRewardItem(
id: json['id']?.toString() ?? '', id: json['id']?.toString() ?? '',
allocationType: json['allocationType'] ?? '', rightType: json['rightType'] ?? '',
usdtAmount: (json['usdtAmount'] ?? 0).toDouble(), usdtAmount: (json['usdtAmount'] ?? 0).toDouble(),
hashpowerAmount: (json['hashpowerAmount'] ?? 0).toDouble(), hashpowerAmount: (json['hashpowerAmount'] ?? 0).toDouble(),
createdAt: DateTime.tryParse(json['createdAt'] ?? '') ?? DateTime.now(), createdAt: DateTime.tryParse(json['createdAt'] ?? '') ?? DateTime.now(),
settledAt: DateTime.tryParse(json['settledAt'] ?? '') ?? DateTime.now(), claimedAt: json['claimedAt'] != null ? DateTime.tryParse(json['claimedAt']) : null,
sourceOrderId: json['sourceOrderId'] ?? '', sourceOrderNo: json['sourceOrderNo'] ?? '',
memo: json['memo'] ?? '',
); );
} }
/// ///
String get allocationTypeName => getAllocationTypeName(allocationType); String get rightTypeName => getAllocationTypeName(rightType);
} }
/// ( GET /wallet/expired-rewards ) /// ( GET /wallet/expired-rewards )
@ -297,14 +300,14 @@ class RewardService {
/// ///
/// ///
/// GET /wallet/settleable-rewards (wallet-service) /// GET /rewards/settleable (reward-service)
/// ///
Future<List<SettleableRewardItem>> getSettleableRewards() async { Future<List<SettleableRewardItem>> getSettleableRewards() async {
try { try {
debugPrint('[RewardService] ========== 获取可结算奖励列表 =========='); debugPrint('[RewardService] ========== 获取可结算奖励列表 ==========');
debugPrint('[RewardService] 请求: GET /wallet/settleable-rewards'); debugPrint('[RewardService] 请求: GET /rewards/settleable');
final response = await _apiClient.get('/wallet/settleable-rewards'); final response = await _apiClient.get('/rewards/settleable');
debugPrint('[RewardService] 响应状态码: ${response.statusCode}'); debugPrint('[RewardService] 响应状态码: ${response.statusCode}');
debugPrint('[RewardService] 响应数据类型: ${response.data.runtimeType}'); debugPrint('[RewardService] 响应数据类型: ${response.data.runtimeType}');
@ -330,7 +333,7 @@ class RewardService {
.toList(); .toList();
for (var item in items) { for (var item in items) {
debugPrint('[RewardService] - ${item.allocationTypeName}: ${item.usdtAmount} USDT, ${item.hashpowerAmount} 算力'); debugPrint('[RewardService] - ${item.rightTypeName}: ${item.usdtAmount} USDT, ${item.hashpowerAmount} 贡献值');
} }
debugPrint('[RewardService] ================================'); debugPrint('[RewardService] ================================');

View File

@ -2210,8 +2210,9 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
/// ///
Widget _buildSettleableRewardItem(SettleableRewardItem item) { Widget _buildSettleableRewardItem(SettleableRewardItem item) {
// // 使 claimedAt使 createdAt
final settledDate = '${item.settledAt.month}/${item.settledAt.day} ${item.settledAt.hour.toString().padLeft(2, '0')}:${item.settledAt.minute.toString().padLeft(2, '0')}'; final displayDate = item.claimedAt ?? item.createdAt;
final settledDate = '${displayDate.month}/${displayDate.day} ${displayDate.hour.toString().padLeft(2, '0')}:${displayDate.minute.toString().padLeft(2, '0')}';
// //
final List<String> amountParts = []; final List<String> amountParts = [];
@ -2242,7 +2243,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
item.allocationTypeName, item.rightTypeName,
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontFamily: 'Inter', fontFamily: 'Inter',
@ -2289,8 +2290,9 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
/// ///
Widget _buildStackedSettleableRewardCard(SettleableRewardItem item, bool isSelected) { Widget _buildStackedSettleableRewardCard(SettleableRewardItem item, bool isSelected) {
// // 使 claimedAt使 createdAt
final settledDate = '${item.settledAt.month}/${item.settledAt.day} ${item.settledAt.hour.toString().padLeft(2, '0')}:${item.settledAt.minute.toString().padLeft(2, '0')}'; final displayDate = item.claimedAt ?? item.createdAt;
final settledDate = '${displayDate.month}/${displayDate.day} ${displayDate.hour.toString().padLeft(2, '0')}:${displayDate.minute.toString().padLeft(2, '0')}';
// //
final List<String> amountParts = []; final List<String> amountParts = [];
@ -2323,7 +2325,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
item.allocationTypeName, item.rightTypeName,
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontFamily: 'Inter', fontFamily: 'Inter',
@ -2373,7 +2375,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
item.allocationTypeName, item.rightTypeName,
style: const TextStyle( style: const TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: 'Inter', fontFamily: 'Inter',
@ -2552,7 +2554,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
item.allocationTypeName, item.rightTypeName,
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontFamily: 'Inter', fontFamily: 'Inter',
@ -2633,7 +2635,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
item.allocationTypeName, item.rightTypeName,
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontFamily: 'Inter', fontFamily: 'Inter',
@ -2683,7 +2685,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
item.allocationTypeName, item.rightTypeName,
style: const TextStyle( style: const TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: 'Inter', fontFamily: 'Inter',