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:
parent
834a1fc0b0
commit
36074948d7
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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] ================================');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue