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,
|
||||
});
|
||||
}
|
||||
|
||||
// [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-05] 修改:改为从 wallet-service 获取系统账户数据(实际余额所在位置)
|
||||
* [2026-01-06] 更新:添加收益类型汇总统计接口
|
||||
* 回滚方式:删除此文件,并从 application.module.ts 中移除注册
|
||||
*/
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
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;
|
||||
}
|
||||
|
||||
// [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 客户端
|
||||
* [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()
|
||||
export class RewardServiceClient {
|
||||
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}`);
|
||||
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 聚合系统账户报表数据
|
||||
// 回滚方式:删除以下方法和构造函数中的 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组件
|
||||
* [2026-01-04] 新增:显示系统账户统计数据
|
||||
* [2026-01-05] 更新:添加分类账明细显示
|
||||
* [2026-01-06] 更新:添加收益类型汇总统计(手续费、省团队、市团队、分享引荐、社区)
|
||||
* 回滚方式:删除此文件及整个 system-account-report 目录
|
||||
*/
|
||||
'use client';
|
||||
|
|
@ -15,8 +16,11 @@ import type {
|
|||
FixedAccountLedger,
|
||||
RegionAccountLedger,
|
||||
LedgerEntryDTO,
|
||||
AllRewardTypeSummariesResponse,
|
||||
RewardTypeSummary,
|
||||
FeeAccountSummary,
|
||||
} 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';
|
||||
|
||||
/**
|
||||
|
|
@ -48,13 +52,19 @@ const getRightTypeName = (type: string): string => {
|
|||
/**
|
||||
* 系统账户报表Tab组件
|
||||
*/
|
||||
// Tab类型定义
|
||||
type TabType = 'fixed' | 'province' | 'city' | 'settlement' | 'expired' | 'ledger' | 'feeAccount' | 'provinceTeam' | 'cityTeam' | 'shareReferral' | 'community';
|
||||
|
||||
export default function SystemAccountsTab() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [reportData, setReportData] = useState<SystemAccountReportResponse | null>(null);
|
||||
const [ledgerData, setLedgerData] = useState<AllSystemAccountsLedgerResponse | null>(null);
|
||||
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 () => {
|
||||
|
|
@ -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(() => {
|
||||
loadReportData();
|
||||
}, [loadReportData]);
|
||||
|
|
@ -99,6 +124,14 @@ export default function SystemAccountsTab() {
|
|||
}
|
||||
}, [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) {
|
||||
return (
|
||||
<div className={styles.loading}>
|
||||
|
|
@ -163,6 +196,37 @@ export default function SystemAccountsTab() {
|
|||
>
|
||||
分类账明细
|
||||
</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>
|
||||
|
||||
{/* 内容区域 */}
|
||||
|
|
@ -173,6 +237,46 @@ export default function SystemAccountsTab() {
|
|||
{activeTab === 'settlement' && <OfflineSettlementSection data={reportData.offlineSettlement} />}
|
||||
{activeTab === 'expired' && <ExpiredRewardsSection data={reportData.expiredRewards} />}
|
||||
{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>
|
||||
|
||||
{/* 报表生成时间 */}
|
||||
|
|
@ -670,3 +774,189 @@ function LedgerAccountCard({
|
|||
</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',
|
||||
// [2026-01-05] 新增:所有系统账户分类账明细
|
||||
ALL_LEDGER: '/v1/system-account-reports/all-ledger',
|
||||
// [2026-01-06] 新增:收益类型汇总统计
|
||||
REWARD_TYPE_SUMMARIES: '/v1/system-account-reports/reward-type-summaries',
|
||||
},
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
* 系统账户报表服务
|
||||
* [2026-01-04] 新增:负责系统账户报表数据的API调用
|
||||
* [2026-01-06] 更新:添加收益类型汇总统计接口
|
||||
* 回滚方式:删除此文件
|
||||
*/
|
||||
|
||||
|
|
@ -14,6 +15,7 @@ import type {
|
|||
OfflineSettlementSummary,
|
||||
ExpiredRewardsSummary,
|
||||
AllSystemAccountsLedgerResponse,
|
||||
AllRewardTypeSummariesResponse,
|
||||
} from '@/types';
|
||||
|
||||
/**
|
||||
|
|
@ -77,6 +79,15 @@ export const systemAccountReportService = {
|
|||
async getAllLedger(params?: DateRangeParams & { pageSize?: number }): Promise<ApiResponse<AllSystemAccountsLedgerResponse>> {
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -186,3 +186,53 @@ export const ENTRY_TYPE_LABELS: Record<string, string> = {
|
|||
ALLOCATION_IN: '分配收入',
|
||||
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