feat(admin-web): 添加系统账户收益类型汇总统计功能
在数据统计-系统账户中新增5个统计Tab: - 手续费账户汇总:统计成本费、运营费、总部社区基础费、RWAD底池注入 - 省团队收益汇总:统计省团队权益收益 - 市团队收益汇总:统计市团队权益收益 - 分享引荐收益汇总:统计分享权益收益 - 社区收益汇总:统计社区权益收益 后端变更: - reward-service: 添加 getRewardsSummaryByType、getAllRewardTypeSummaries 方法 - reporting-service: 聚合收益类型汇总统计接口 前端变更: - 添加 RewardTypeSummary、FeeAccountSummary 类型定义 - 添加 getRewardTypeSummaries API 方法 - 添加 FeeAccountSection、RewardTypeSummarySection 组件 🤖 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
bbafe58e86
commit
99b725db0a
|
|
@ -98,4 +98,15 @@ export class SystemAccountReportController {
|
||||||
endDate,
|
endDate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:收益类型汇总统计接口
|
||||||
|
// 用于系统账户报表中的:手续费汇总、省团队收益汇总、市团队收益汇总、分享引荐收益汇总、社区收益汇总
|
||||||
|
// 回滚方式:删除以下 API 方法即可
|
||||||
|
@Get('reward-type-summaries')
|
||||||
|
@ApiOperation({ summary: '获取所有收益类型汇总统计' })
|
||||||
|
@ApiResponse({ status: 200, description: '所有收益类型汇总' })
|
||||||
|
async getAllRewardTypeSummaries() {
|
||||||
|
this.logger.log(`[getAllRewardTypeSummaries] 请求所有收益类型汇总统计`);
|
||||||
|
return this.systemAccountReportService.getAllRewardTypeSummaries();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,12 @@
|
||||||
* 系统账户报表应用服务
|
* 系统账户报表应用服务
|
||||||
* [2026-01-04] 新增:用于聚合各服务的系统账户报表数据
|
* [2026-01-04] 新增:用于聚合各服务的系统账户报表数据
|
||||||
* [2026-01-05] 修改:改为从 wallet-service 获取系统账户数据(实际余额所在位置)
|
* [2026-01-05] 修改:改为从 wallet-service 获取系统账户数据(实际余额所在位置)
|
||||||
|
* [2026-01-06] 更新:添加收益类型汇总统计接口
|
||||||
* 回滚方式:删除此文件,并从 application.module.ts 中移除注册
|
* 回滚方式:删除此文件,并从 application.module.ts 中移除注册
|
||||||
*/
|
*/
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { WalletServiceClient, OfflineSettlementSummary, AllSystemAccountsResponse, AllSystemAccountsLedgerResponse } from '../../infrastructure/external/wallet-service/wallet-service.client';
|
import { WalletServiceClient, OfflineSettlementSummary, AllSystemAccountsResponse, AllSystemAccountsLedgerResponse } from '../../infrastructure/external/wallet-service/wallet-service.client';
|
||||||
import { RewardServiceClient, ExpiredRewardsSummary } from '../../infrastructure/external/reward-service/reward-service.client';
|
import { RewardServiceClient, ExpiredRewardsSummary, AllRewardTypeSummaries } from '../../infrastructure/external/reward-service/reward-service.client';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 固定系统账户信息
|
* 固定系统账户信息
|
||||||
|
|
@ -200,6 +201,18 @@ export class SystemAccountReportApplicationService {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:获取所有收益类型汇总统计
|
||||||
|
/**
|
||||||
|
* 获取所有收益类型的汇总统计
|
||||||
|
* 包括:手续费汇总、省团队收益汇总、市团队收益汇总、分享引荐收益汇总、社区收益汇总
|
||||||
|
*/
|
||||||
|
async getAllRewardTypeSummaries(): Promise<AllRewardTypeSummaries> {
|
||||||
|
this.logger.log('[getAllRewardTypeSummaries] 开始获取所有收益类型汇总...');
|
||||||
|
const result = await this.rewardServiceClient.getAllRewardTypeSummaries();
|
||||||
|
this.logger.log(`[getAllRewardTypeSummaries] 完成`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组装固定账户数据
|
* 组装固定账户数据
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Reward Service HTTP 客户端
|
* Reward Service HTTP 客户端
|
||||||
* [2026-01-04] 新增:用于系统账户报表统计
|
* [2026-01-04] 新增:用于系统账户报表统计
|
||||||
|
* [2026-01-06] 更新:添加收益类型汇总统计接口
|
||||||
* 回滚方式:删除此文件
|
* 回滚方式:删除此文件
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -24,6 +25,37 @@ export interface ExpiredRewardsSummary {
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:收益类型汇总接口类型
|
||||||
|
export interface RewardTypeSummary {
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
totalSettleableAmount: number;
|
||||||
|
totalSettledAmount: number;
|
||||||
|
byMonth: Array<{
|
||||||
|
month: string;
|
||||||
|
amount: number;
|
||||||
|
count: number;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FeeAccountSummary {
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
breakdown: Array<{
|
||||||
|
rightType: string;
|
||||||
|
amount: number;
|
||||||
|
count: number;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AllRewardTypeSummaries {
|
||||||
|
feeAccountSummary: FeeAccountSummary;
|
||||||
|
provinceTeamSummary: RewardTypeSummary;
|
||||||
|
cityTeamSummary: RewardTypeSummary;
|
||||||
|
shareReferralSummary: RewardTypeSummary;
|
||||||
|
communitySummary: RewardTypeSummary;
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RewardServiceClient {
|
export class RewardServiceClient {
|
||||||
private readonly logger = new Logger(RewardServiceClient.name);
|
private readonly logger = new Logger(RewardServiceClient.name);
|
||||||
|
|
@ -70,4 +102,43 @@ export class RewardServiceClient {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:获取所有收益类型汇总统计
|
||||||
|
/**
|
||||||
|
* 获取所有收益类型的汇总统计
|
||||||
|
* 包括:手续费汇总、省团队收益汇总、市团队收益汇总、分享引荐收益汇总、社区收益汇总
|
||||||
|
*/
|
||||||
|
async getAllRewardTypeSummaries(): Promise<AllRewardTypeSummaries> {
|
||||||
|
try {
|
||||||
|
const url = `${this.baseUrl}/api/v1/internal/statistics/reward-type-summaries`;
|
||||||
|
this.logger.debug(`[getAllRewardTypeSummaries] 请求: ${url}`);
|
||||||
|
|
||||||
|
const response = await firstValueFrom(
|
||||||
|
this.httpService.get<AllRewardTypeSummaries>(url),
|
||||||
|
);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`[getAllRewardTypeSummaries] 失败: ${error.message}`);
|
||||||
|
// 返回默认值,不阻塞报表
|
||||||
|
const defaultSummary: RewardTypeSummary = {
|
||||||
|
totalAmount: 0,
|
||||||
|
totalCount: 0,
|
||||||
|
totalSettleableAmount: 0,
|
||||||
|
totalSettledAmount: 0,
|
||||||
|
byMonth: [],
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
feeAccountSummary: {
|
||||||
|
totalAmount: 0,
|
||||||
|
totalCount: 0,
|
||||||
|
breakdown: [],
|
||||||
|
},
|
||||||
|
provinceTeamSummary: defaultSummary,
|
||||||
|
cityTeamSummary: defaultSummary,
|
||||||
|
shareReferralSummary: defaultSummary,
|
||||||
|
communitySummary: defaultSummary,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,4 +96,26 @@ export class InternalController {
|
||||||
this.logger.log(`过期收益统计结果: totalAmount=${result.totalAmount}, totalCount=${result.totalCount}`);
|
this.logger.log(`过期收益统计结果: totalAmount=${result.totalAmount}, totalCount=${result.totalCount}`);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:收益类型汇总统计接口
|
||||||
|
// 用于系统账户报表中的:手续费汇总、省团队收益汇总、市团队收益汇总、分享引荐收益汇总、社区收益汇总
|
||||||
|
// 回滚方式:删除以下 API 方法即可
|
||||||
|
|
||||||
|
@Get('statistics/reward-type-summaries')
|
||||||
|
@ApiOperation({ summary: '所有收益类型汇总统计(内部接口)- 用于系统账户报表' })
|
||||||
|
@ApiResponse({ status: 200, description: '所有收益类型汇总' })
|
||||||
|
async getAllRewardTypeSummaries() {
|
||||||
|
this.logger.log(`========== statistics/reward-type-summaries 请求 ==========`);
|
||||||
|
|
||||||
|
const result = await this.rewardService.getAllRewardTypeSummaries();
|
||||||
|
|
||||||
|
this.logger.log(`收益类型汇总统计结果:`);
|
||||||
|
this.logger.log(` - 手续费汇总: ${result.feeAccountSummary.totalAmount}`);
|
||||||
|
this.logger.log(` - 省团队收益: ${result.provinceTeamSummary.totalAmount}`);
|
||||||
|
this.logger.log(` - 市团队收益: ${result.cityTeamSummary.totalAmount}`);
|
||||||
|
this.logger.log(` - 分享引荐收益: ${result.shareReferralSummary.totalAmount}`);
|
||||||
|
this.logger.log(` - 社区收益: ${result.communitySummary.totalAmount}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -905,6 +905,225 @@ export class RewardApplicationService {
|
||||||
// [2026-01-04] 新增:用于 reporting-service 聚合系统账户报表数据
|
// [2026-01-04] 新增:用于 reporting-service 聚合系统账户报表数据
|
||||||
// 回滚方式:删除以下方法和构造函数中的 prisma 注入即可
|
// 回滚方式:删除以下方法和构造函数中的 prisma 注入即可
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:按权益类型统计收益汇总
|
||||||
|
// 用于系统账户报表中的:手续费汇总、省团队收益汇总、市团队收益汇总、分享引荐收益汇总、社区收益汇总
|
||||||
|
// 回滚方式:删除以下方法即可
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取按权益类型的收益统计汇总
|
||||||
|
* 统计所有已发放(SETTLEABLE + SETTLED)的奖励按权益类型分组
|
||||||
|
*
|
||||||
|
* @param rightType 权益类型
|
||||||
|
*/
|
||||||
|
async getRewardsSummaryByType(rightType: string): Promise<{
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
totalSettleableAmount: number;
|
||||||
|
totalSettledAmount: number;
|
||||||
|
byMonth: Array<{
|
||||||
|
month: string;
|
||||||
|
amount: number;
|
||||||
|
count: number;
|
||||||
|
}>;
|
||||||
|
}> {
|
||||||
|
this.logger.log(`[getRewardsSummaryByType] 查询权益类型收益统计: ${rightType}`);
|
||||||
|
|
||||||
|
// 查询总计(SETTLEABLE + SETTLED 状态)
|
||||||
|
const aggregateResult = await this.prisma.rewardLedgerEntry.aggregate({
|
||||||
|
where: {
|
||||||
|
rightType: rightType,
|
||||||
|
rewardStatus: { in: ['SETTLEABLE', 'SETTLED'] },
|
||||||
|
},
|
||||||
|
_sum: {
|
||||||
|
usdtAmount: true,
|
||||||
|
},
|
||||||
|
_count: {
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 查询可结算金额
|
||||||
|
const settleableResult = await this.prisma.rewardLedgerEntry.aggregate({
|
||||||
|
where: {
|
||||||
|
rightType: rightType,
|
||||||
|
rewardStatus: 'SETTLEABLE',
|
||||||
|
},
|
||||||
|
_sum: {
|
||||||
|
usdtAmount: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 查询已结算金额
|
||||||
|
const settledResult = await this.prisma.rewardLedgerEntry.aggregate({
|
||||||
|
where: {
|
||||||
|
rightType: rightType,
|
||||||
|
rewardStatus: 'SETTLED',
|
||||||
|
},
|
||||||
|
_sum: {
|
||||||
|
usdtAmount: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalAmount = aggregateResult._sum.usdtAmount
|
||||||
|
? Number(aggregateResult._sum.usdtAmount)
|
||||||
|
: 0;
|
||||||
|
const totalCount = aggregateResult._count.id || 0;
|
||||||
|
const totalSettleableAmount = settleableResult._sum.usdtAmount
|
||||||
|
? Number(settleableResult._sum.usdtAmount)
|
||||||
|
: 0;
|
||||||
|
const totalSettledAmount = settledResult._sum.usdtAmount
|
||||||
|
? Number(settledResult._sum.usdtAmount)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
// 查询按月统计
|
||||||
|
const byMonth = await this.prisma.$queryRaw<Array<{
|
||||||
|
month: string;
|
||||||
|
amount: any;
|
||||||
|
count: any;
|
||||||
|
}>>`
|
||||||
|
SELECT
|
||||||
|
TO_CHAR(created_at, 'YYYY-MM') as month,
|
||||||
|
SUM(usdt_amount) as amount,
|
||||||
|
COUNT(*) as count
|
||||||
|
FROM reward_ledger_entries
|
||||||
|
WHERE right_type = ${rightType}
|
||||||
|
AND reward_status IN ('SETTLEABLE', 'SETTLED')
|
||||||
|
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
|
||||||
|
ORDER BY month DESC
|
||||||
|
LIMIT 12
|
||||||
|
`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalAmount,
|
||||||
|
totalCount,
|
||||||
|
totalSettleableAmount,
|
||||||
|
totalSettledAmount,
|
||||||
|
byMonth: byMonth.map(row => ({
|
||||||
|
month: row.month,
|
||||||
|
amount: Number(row.amount) || 0,
|
||||||
|
count: Number(row.count) || 0,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有收益类型的汇总统计
|
||||||
|
* 用于系统账户报表中的各类收益汇总Tab
|
||||||
|
*/
|
||||||
|
async getAllRewardTypeSummaries(): Promise<{
|
||||||
|
feeAccountSummary: {
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
breakdown: Array<{ rightType: string; amount: number; count: number }>;
|
||||||
|
};
|
||||||
|
provinceTeamSummary: {
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
totalSettleableAmount: number;
|
||||||
|
totalSettledAmount: number;
|
||||||
|
byMonth: Array<{ month: string; amount: number; count: number }>;
|
||||||
|
};
|
||||||
|
cityTeamSummary: {
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
totalSettleableAmount: number;
|
||||||
|
totalSettledAmount: number;
|
||||||
|
byMonth: Array<{ month: string; amount: number; count: number }>;
|
||||||
|
};
|
||||||
|
shareReferralSummary: {
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
totalSettleableAmount: number;
|
||||||
|
totalSettledAmount: number;
|
||||||
|
byMonth: Array<{ month: string; amount: number; count: number }>;
|
||||||
|
};
|
||||||
|
communitySummary: {
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
totalSettleableAmount: number;
|
||||||
|
totalSettledAmount: number;
|
||||||
|
byMonth: Array<{ month: string; amount: number; count: number }>;
|
||||||
|
};
|
||||||
|
}> {
|
||||||
|
this.logger.log('[getAllRewardTypeSummaries] 查询所有收益类型汇总');
|
||||||
|
|
||||||
|
// 并行查询各类收益汇总
|
||||||
|
const [
|
||||||
|
provinceTeamSummary,
|
||||||
|
cityTeamSummary,
|
||||||
|
shareReferralSummary,
|
||||||
|
communitySummary,
|
||||||
|
feeBreakdown,
|
||||||
|
] = await Promise.all([
|
||||||
|
this.getRewardsSummaryByType(RightType.PROVINCE_TEAM_RIGHT),
|
||||||
|
this.getRewardsSummaryByType(RightType.CITY_TEAM_RIGHT),
|
||||||
|
this.getRewardsSummaryByType(RightType.SHARE_RIGHT),
|
||||||
|
this.getRewardsSummaryByType(RightType.COMMUNITY_RIGHT),
|
||||||
|
// 手续费汇总:包括系统费用类型
|
||||||
|
this.getFeeAccountSummary(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
feeAccountSummary: feeBreakdown,
|
||||||
|
provinceTeamSummary,
|
||||||
|
cityTeamSummary,
|
||||||
|
shareReferralSummary,
|
||||||
|
communitySummary,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取手续费账户汇总(系统费用类型)
|
||||||
|
* 包括:成本费、运营费、总部社区基础费、RWAD底池注入
|
||||||
|
*/
|
||||||
|
private async getFeeAccountSummary(): Promise<{
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
breakdown: Array<{ rightType: string; amount: number; count: number }>;
|
||||||
|
}> {
|
||||||
|
const feeTypes = [
|
||||||
|
RightType.COST_FEE,
|
||||||
|
RightType.OPERATION_FEE,
|
||||||
|
RightType.HEADQUARTERS_BASE_FEE,
|
||||||
|
RightType.RWAD_POOL_INJECTION,
|
||||||
|
];
|
||||||
|
|
||||||
|
// 使用 Prisma 的 groupBy 查询各手续费类型的汇总
|
||||||
|
const groupedResults = await this.prisma.rewardLedgerEntry.groupBy({
|
||||||
|
by: ['rightType'],
|
||||||
|
where: {
|
||||||
|
rightType: { in: feeTypes },
|
||||||
|
rewardStatus: { in: ['SETTLEABLE', 'SETTLED'] },
|
||||||
|
},
|
||||||
|
_sum: {
|
||||||
|
usdtAmount: true,
|
||||||
|
},
|
||||||
|
_count: {
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let totalAmount = 0;
|
||||||
|
let totalCount = 0;
|
||||||
|
const formattedBreakdown = groupedResults.map(row => {
|
||||||
|
const amount = Number(row._sum.usdtAmount) || 0;
|
||||||
|
const count = Number(row._count.id) || 0;
|
||||||
|
totalAmount += amount;
|
||||||
|
totalCount += count;
|
||||||
|
return {
|
||||||
|
rightType: row.rightType,
|
||||||
|
amount,
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalAmount,
|
||||||
|
totalCount,
|
||||||
|
breakdown: formattedBreakdown,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取过期收益统计汇总
|
* 获取过期收益统计汇总
|
||||||
* 用于系统账户报表展示过期收益总额
|
* 用于系统账户报表展示过期收益总额
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
* 系统账户报表Tab组件
|
* 系统账户报表Tab组件
|
||||||
* [2026-01-04] 新增:显示系统账户统计数据
|
* [2026-01-04] 新增:显示系统账户统计数据
|
||||||
* [2026-01-05] 更新:添加分类账明细显示
|
* [2026-01-05] 更新:添加分类账明细显示
|
||||||
|
* [2026-01-06] 更新:添加收益类型汇总统计(手续费、省团队、市团队、分享引荐、社区)
|
||||||
* 回滚方式:删除此文件及整个 system-account-report 目录
|
* 回滚方式:删除此文件及整个 system-account-report 目录
|
||||||
*/
|
*/
|
||||||
'use client';
|
'use client';
|
||||||
|
|
@ -15,8 +16,11 @@ import type {
|
||||||
FixedAccountLedger,
|
FixedAccountLedger,
|
||||||
RegionAccountLedger,
|
RegionAccountLedger,
|
||||||
LedgerEntryDTO,
|
LedgerEntryDTO,
|
||||||
|
AllRewardTypeSummariesResponse,
|
||||||
|
RewardTypeSummary,
|
||||||
|
FeeAccountSummary,
|
||||||
} from '@/types';
|
} from '@/types';
|
||||||
import { ENTRY_TYPE_LABELS, ACCOUNT_TYPE_LABELS } from '@/types';
|
import { ENTRY_TYPE_LABELS, ACCOUNT_TYPE_LABELS, FEE_TYPE_LABELS } from '@/types';
|
||||||
import styles from './SystemAccountsTab.module.scss';
|
import styles from './SystemAccountsTab.module.scss';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -48,13 +52,19 @@ const getRightTypeName = (type: string): string => {
|
||||||
/**
|
/**
|
||||||
* 系统账户报表Tab组件
|
* 系统账户报表Tab组件
|
||||||
*/
|
*/
|
||||||
|
// Tab类型定义
|
||||||
|
type TabType = 'fixed' | 'province' | 'city' | 'settlement' | 'expired' | 'ledger' | 'feeAccount' | 'provinceTeam' | 'cityTeam' | 'shareReferral' | 'community';
|
||||||
|
|
||||||
export default function SystemAccountsTab() {
|
export default function SystemAccountsTab() {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [reportData, setReportData] = useState<SystemAccountReportResponse | null>(null);
|
const [reportData, setReportData] = useState<SystemAccountReportResponse | null>(null);
|
||||||
const [ledgerData, setLedgerData] = useState<AllSystemAccountsLedgerResponse | null>(null);
|
const [ledgerData, setLedgerData] = useState<AllSystemAccountsLedgerResponse | null>(null);
|
||||||
const [ledgerLoading, setLedgerLoading] = useState(false);
|
const [ledgerLoading, setLedgerLoading] = useState(false);
|
||||||
const [activeTab, setActiveTab] = useState<'fixed' | 'province' | 'city' | 'settlement' | 'expired' | 'ledger'>('fixed');
|
// [2026-01-06] 新增:收益类型汇总数据
|
||||||
|
const [rewardTypeSummaries, setRewardTypeSummaries] = useState<AllRewardTypeSummariesResponse | null>(null);
|
||||||
|
const [rewardTypeSummariesLoading, setRewardTypeSummariesLoading] = useState(false);
|
||||||
|
const [activeTab, setActiveTab] = useState<TabType>('fixed');
|
||||||
|
|
||||||
// 加载报表数据
|
// 加载报表数据
|
||||||
const loadReportData = useCallback(async () => {
|
const loadReportData = useCallback(async () => {
|
||||||
|
|
@ -88,6 +98,21 @@ export default function SystemAccountsTab() {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:加载收益类型汇总数据
|
||||||
|
const loadRewardTypeSummaries = useCallback(async () => {
|
||||||
|
setRewardTypeSummariesLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await systemAccountReportService.getRewardTypeSummaries();
|
||||||
|
if (response.data) {
|
||||||
|
setRewardTypeSummaries(response.data);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to load reward type summaries:', err);
|
||||||
|
} finally {
|
||||||
|
setRewardTypeSummariesLoading(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadReportData();
|
loadReportData();
|
||||||
}, [loadReportData]);
|
}, [loadReportData]);
|
||||||
|
|
@ -99,6 +124,14 @@ export default function SystemAccountsTab() {
|
||||||
}
|
}
|
||||||
}, [activeTab, ledgerData, ledgerLoading, loadLedgerData]);
|
}, [activeTab, ledgerData, ledgerLoading, loadLedgerData]);
|
||||||
|
|
||||||
|
// [2026-01-06] 切换到收益类型汇总Tab时加载数据
|
||||||
|
const rewardTypeTabs: TabType[] = ['feeAccount', 'provinceTeam', 'cityTeam', 'shareReferral', 'community'];
|
||||||
|
useEffect(() => {
|
||||||
|
if (rewardTypeTabs.includes(activeTab) && !rewardTypeSummaries && !rewardTypeSummariesLoading) {
|
||||||
|
loadRewardTypeSummaries();
|
||||||
|
}
|
||||||
|
}, [activeTab, rewardTypeSummaries, rewardTypeSummariesLoading, loadRewardTypeSummaries]);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.loading}>
|
<div className={styles.loading}>
|
||||||
|
|
@ -163,6 +196,37 @@ export default function SystemAccountsTab() {
|
||||||
>
|
>
|
||||||
分类账明细
|
分类账明细
|
||||||
</button>
|
</button>
|
||||||
|
{/* [2026-01-06] 新增:收益类型汇总Tab */}
|
||||||
|
<button
|
||||||
|
className={`${styles.tab} ${activeTab === 'feeAccount' ? styles.active : ''}`}
|
||||||
|
onClick={() => setActiveTab('feeAccount')}
|
||||||
|
>
|
||||||
|
手续费账户汇总
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`${styles.tab} ${activeTab === 'provinceTeam' ? styles.active : ''}`}
|
||||||
|
onClick={() => setActiveTab('provinceTeam')}
|
||||||
|
>
|
||||||
|
省团队收益汇总
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`${styles.tab} ${activeTab === 'cityTeam' ? styles.active : ''}`}
|
||||||
|
onClick={() => setActiveTab('cityTeam')}
|
||||||
|
>
|
||||||
|
市团队收益汇总
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`${styles.tab} ${activeTab === 'shareReferral' ? styles.active : ''}`}
|
||||||
|
onClick={() => setActiveTab('shareReferral')}
|
||||||
|
>
|
||||||
|
分享引荐收益汇总
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`${styles.tab} ${activeTab === 'community' ? styles.active : ''}`}
|
||||||
|
onClick={() => setActiveTab('community')}
|
||||||
|
>
|
||||||
|
社区收益汇总
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 内容区域 */}
|
{/* 内容区域 */}
|
||||||
|
|
@ -173,6 +237,46 @@ export default function SystemAccountsTab() {
|
||||||
{activeTab === 'settlement' && <OfflineSettlementSection data={reportData.offlineSettlement} />}
|
{activeTab === 'settlement' && <OfflineSettlementSection data={reportData.offlineSettlement} />}
|
||||||
{activeTab === 'expired' && <ExpiredRewardsSection data={reportData.expiredRewards} />}
|
{activeTab === 'expired' && <ExpiredRewardsSection data={reportData.expiredRewards} />}
|
||||||
{activeTab === 'ledger' && <LedgerSection data={ledgerData} loading={ledgerLoading} onRefresh={loadLedgerData} />}
|
{activeTab === 'ledger' && <LedgerSection data={ledgerData} loading={ledgerLoading} onRefresh={loadLedgerData} />}
|
||||||
|
{/* [2026-01-06] 新增:收益类型汇总Tab内容 */}
|
||||||
|
{activeTab === 'feeAccount' && (
|
||||||
|
<FeeAccountSection
|
||||||
|
data={rewardTypeSummaries?.feeAccountSummary}
|
||||||
|
loading={rewardTypeSummariesLoading}
|
||||||
|
onRefresh={loadRewardTypeSummaries}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{activeTab === 'provinceTeam' && (
|
||||||
|
<RewardTypeSummarySection
|
||||||
|
title="省团队收益汇总"
|
||||||
|
data={rewardTypeSummaries?.provinceTeamSummary}
|
||||||
|
loading={rewardTypeSummariesLoading}
|
||||||
|
onRefresh={loadRewardTypeSummaries}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{activeTab === 'cityTeam' && (
|
||||||
|
<RewardTypeSummarySection
|
||||||
|
title="市团队收益汇总"
|
||||||
|
data={rewardTypeSummaries?.cityTeamSummary}
|
||||||
|
loading={rewardTypeSummariesLoading}
|
||||||
|
onRefresh={loadRewardTypeSummaries}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{activeTab === 'shareReferral' && (
|
||||||
|
<RewardTypeSummarySection
|
||||||
|
title="分享引荐收益汇总"
|
||||||
|
data={rewardTypeSummaries?.shareReferralSummary}
|
||||||
|
loading={rewardTypeSummariesLoading}
|
||||||
|
onRefresh={loadRewardTypeSummaries}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{activeTab === 'community' && (
|
||||||
|
<RewardTypeSummarySection
|
||||||
|
title="社区收益汇总"
|
||||||
|
data={rewardTypeSummaries?.communitySummary}
|
||||||
|
loading={rewardTypeSummariesLoading}
|
||||||
|
onRefresh={loadRewardTypeSummaries}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 报表生成时间 */}
|
{/* 报表生成时间 */}
|
||||||
|
|
@ -670,3 +774,189 @@ function LedgerAccountCard({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:手续费账户汇总组件
|
||||||
|
/**
|
||||||
|
* 手续费账户汇总区域
|
||||||
|
*/
|
||||||
|
function FeeAccountSection({
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
onRefresh,
|
||||||
|
}: {
|
||||||
|
data: FeeAccountSummary | undefined;
|
||||||
|
loading: boolean;
|
||||||
|
onRefresh: () => void;
|
||||||
|
}) {
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className={styles.loading}>
|
||||||
|
<div className={styles.spinner} />
|
||||||
|
<span>加载手续费账户汇总中...</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return (
|
||||||
|
<div className={styles.section}>
|
||||||
|
<div className={styles.emptyTable}>
|
||||||
|
<span>暂无手续费账户数据</span>
|
||||||
|
<button onClick={onRefresh} className={styles.retryButton}>
|
||||||
|
加载数据
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.section}>
|
||||||
|
<div className={styles.sectionHeader}>
|
||||||
|
<h3 className={styles.sectionTitle}>手续费账户汇总</h3>
|
||||||
|
<button onClick={onRefresh} className={styles.refreshButton}>
|
||||||
|
刷新
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 汇总卡片 */}
|
||||||
|
<div className={styles.summaryCards}>
|
||||||
|
<div className={styles.summaryCard}>
|
||||||
|
<span className={styles.summaryLabel}>总金额</span>
|
||||||
|
<span className={styles.summaryValue}>{formatAmount(data.totalAmount)} 绿积分</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.summaryCard}>
|
||||||
|
<span className={styles.summaryLabel}>总笔数</span>
|
||||||
|
<span className={styles.summaryValue}>{data.totalCount}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 按类型明细 */}
|
||||||
|
{data.breakdown && data.breakdown.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<h4 className={styles.subTitle}>按类型明细</h4>
|
||||||
|
<div className={styles.tableWrapper}>
|
||||||
|
<table className={styles.table}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>费用类型</th>
|
||||||
|
<th>金额 (绿积分)</th>
|
||||||
|
<th>笔数</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{data.breakdown.map((item) => (
|
||||||
|
<tr key={item.rightType}>
|
||||||
|
<td>{FEE_TYPE_LABELS[item.rightType] || item.rightType}</td>
|
||||||
|
<td>{formatAmount(item.amount)}</td>
|
||||||
|
<td>{item.count}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className={styles.emptyTable}>暂无手续费数据</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:收益类型汇总组件
|
||||||
|
/**
|
||||||
|
* 收益类型汇总区域(省团队、市团队、分享引荐、社区)
|
||||||
|
*/
|
||||||
|
function RewardTypeSummarySection({
|
||||||
|
title,
|
||||||
|
data,
|
||||||
|
loading,
|
||||||
|
onRefresh,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
data: RewardTypeSummary | undefined;
|
||||||
|
loading: boolean;
|
||||||
|
onRefresh: () => void;
|
||||||
|
}) {
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className={styles.loading}>
|
||||||
|
<div className={styles.spinner} />
|
||||||
|
<span>加载{title}中...</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return (
|
||||||
|
<div className={styles.section}>
|
||||||
|
<div className={styles.emptyTable}>
|
||||||
|
<span>暂无数据</span>
|
||||||
|
<button onClick={onRefresh} className={styles.retryButton}>
|
||||||
|
加载数据
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.section}>
|
||||||
|
<div className={styles.sectionHeader}>
|
||||||
|
<h3 className={styles.sectionTitle}>{title}</h3>
|
||||||
|
<button onClick={onRefresh} className={styles.refreshButton}>
|
||||||
|
刷新
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 汇总卡片 */}
|
||||||
|
<div className={styles.summaryCards}>
|
||||||
|
<div className={styles.summaryCard}>
|
||||||
|
<span className={styles.summaryLabel}>总金额</span>
|
||||||
|
<span className={styles.summaryValue}>{formatAmount(data.totalAmount)} 绿积分</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.summaryCard}>
|
||||||
|
<span className={styles.summaryLabel}>总笔数</span>
|
||||||
|
<span className={styles.summaryValue}>{data.totalCount}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.summaryCard}>
|
||||||
|
<span className={styles.summaryLabel}>可结算金额</span>
|
||||||
|
<span className={styles.summaryValue}>{formatAmount(data.totalSettleableAmount)} 绿积分</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.summaryCard}>
|
||||||
|
<span className={styles.summaryLabel}>已结算金额</span>
|
||||||
|
<span className={styles.summaryValue}>{formatAmount(data.totalSettledAmount)} 绿积分</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 按月统计 */}
|
||||||
|
{data.byMonth && data.byMonth.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<h4 className={styles.subTitle}>按月统计</h4>
|
||||||
|
<div className={styles.tableWrapper}>
|
||||||
|
<table className={styles.table}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>月份</th>
|
||||||
|
<th>金额 (绿积分)</th>
|
||||||
|
<th>笔数</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{data.byMonth.map((item) => (
|
||||||
|
<tr key={item.month}>
|
||||||
|
<td>{item.month}</td>
|
||||||
|
<td>{formatAmount(item.amount)}</td>
|
||||||
|
<td>{item.count}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className={styles.emptyTable}>暂无月度数据</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -216,5 +216,7 @@ export const API_ENDPOINTS = {
|
||||||
EXPIRED_REWARDS: '/v1/system-account-reports/expired-rewards',
|
EXPIRED_REWARDS: '/v1/system-account-reports/expired-rewards',
|
||||||
// [2026-01-05] 新增:所有系统账户分类账明细
|
// [2026-01-05] 新增:所有系统账户分类账明细
|
||||||
ALL_LEDGER: '/v1/system-account-reports/all-ledger',
|
ALL_LEDGER: '/v1/system-account-reports/all-ledger',
|
||||||
|
// [2026-01-06] 新增:收益类型汇总统计
|
||||||
|
REWARD_TYPE_SUMMARIES: '/v1/system-account-reports/reward-type-summaries',
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* 系统账户报表服务
|
* 系统账户报表服务
|
||||||
* [2026-01-04] 新增:负责系统账户报表数据的API调用
|
* [2026-01-04] 新增:负责系统账户报表数据的API调用
|
||||||
|
* [2026-01-06] 更新:添加收益类型汇总统计接口
|
||||||
* 回滚方式:删除此文件
|
* 回滚方式:删除此文件
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -14,6 +15,7 @@ import type {
|
||||||
OfflineSettlementSummary,
|
OfflineSettlementSummary,
|
||||||
ExpiredRewardsSummary,
|
ExpiredRewardsSummary,
|
||||||
AllSystemAccountsLedgerResponse,
|
AllSystemAccountsLedgerResponse,
|
||||||
|
AllRewardTypeSummariesResponse,
|
||||||
} from '@/types';
|
} from '@/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -77,6 +79,15 @@ export const systemAccountReportService = {
|
||||||
async getAllLedger(params?: DateRangeParams & { pageSize?: number }): Promise<ApiResponse<AllSystemAccountsLedgerResponse>> {
|
async getAllLedger(params?: DateRangeParams & { pageSize?: number }): Promise<ApiResponse<AllSystemAccountsLedgerResponse>> {
|
||||||
return apiClient.get(API_ENDPOINTS.SYSTEM_ACCOUNT_REPORTS.ALL_LEDGER, { params });
|
return apiClient.get(API_ENDPOINTS.SYSTEM_ACCOUNT_REPORTS.ALL_LEDGER, { params });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:获取所有收益类型汇总统计
|
||||||
|
/**
|
||||||
|
* 获取所有收益类型的汇总统计
|
||||||
|
* 包括:手续费汇总、省团队收益汇总、市团队收益汇总、分享引荐收益汇总、社区收益汇总
|
||||||
|
*/
|
||||||
|
async getRewardTypeSummaries(): Promise<ApiResponse<AllRewardTypeSummariesResponse>> {
|
||||||
|
return apiClient.get(API_ENDPOINTS.SYSTEM_ACCOUNT_REPORTS.REWARD_TYPE_SUMMARIES);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default systemAccountReportService;
|
export default systemAccountReportService;
|
||||||
|
|
|
||||||
|
|
@ -186,3 +186,53 @@ export const ENTRY_TYPE_LABELS: Record<string, string> = {
|
||||||
ALLOCATION_IN: '分配收入',
|
ALLOCATION_IN: '分配收入',
|
||||||
ALLOCATION_OUT: '分配支出',
|
ALLOCATION_OUT: '分配支出',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// [2026-01-06] 新增:收益类型汇总统计类型
|
||||||
|
/**
|
||||||
|
* 收益类型汇总
|
||||||
|
*/
|
||||||
|
export interface RewardTypeSummary {
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
totalSettleableAmount: number;
|
||||||
|
totalSettledAmount: number;
|
||||||
|
byMonth: Array<{
|
||||||
|
month: string;
|
||||||
|
amount: number;
|
||||||
|
count: number;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手续费账户汇总
|
||||||
|
*/
|
||||||
|
export interface FeeAccountSummary {
|
||||||
|
totalAmount: number;
|
||||||
|
totalCount: number;
|
||||||
|
breakdown: Array<{
|
||||||
|
rightType: string;
|
||||||
|
amount: number;
|
||||||
|
count: number;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有收益类型汇总响应
|
||||||
|
*/
|
||||||
|
export interface AllRewardTypeSummariesResponse {
|
||||||
|
feeAccountSummary: FeeAccountSummary;
|
||||||
|
provinceTeamSummary: RewardTypeSummary;
|
||||||
|
cityTeamSummary: RewardTypeSummary;
|
||||||
|
shareReferralSummary: RewardTypeSummary;
|
||||||
|
communitySummary: RewardTypeSummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权益类型(系统费用)显示名称映射
|
||||||
|
*/
|
||||||
|
export const FEE_TYPE_LABELS: Record<string, string> = {
|
||||||
|
COST_FEE: '成本费',
|
||||||
|
OPERATION_FEE: '运营费',
|
||||||
|
HEADQUARTERS_BASE_FEE: '总部社区基础费',
|
||||||
|
RWAD_POOL_INJECTION: 'RWAD底池注入',
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue