feat(admin-web): 添加面对面结算明细列表功能
- wallet-service: 新增 getOfflineSettlementEntries 方法和 API - reporting-service: 新增客户端方法和 API 转发 - admin-web: 添加明细列表组件和样式,支持展开/收起 🤖 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
9953f0eee5
commit
4e5d9685a1
|
|
@ -79,6 +79,30 @@ export class SystemAccountReportController {
|
|||
return this.systemAccountReportService.getExpiredRewardsSummary({ startDate, endDate });
|
||||
}
|
||||
|
||||
// [2026-01-07] 新增:面对面结算明细列表
|
||||
// 回滚方式:删除此方法
|
||||
@Get('offline-settlement-entries')
|
||||
@ApiOperation({ summary: '获取面对面结算明细列表' })
|
||||
@ApiQuery({ name: 'page', required: false, description: '页码,默认1' })
|
||||
@ApiQuery({ name: 'pageSize', required: false, description: '每页条数,默认50' })
|
||||
@ApiQuery({ name: 'startDate', required: false, description: '开始日期 (YYYY-MM-DD)' })
|
||||
@ApiQuery({ name: 'endDate', required: false, description: '结束日期 (YYYY-MM-DD)' })
|
||||
@ApiResponse({ status: 200, description: '面对面结算明细列表' })
|
||||
async getOfflineSettlementEntries(
|
||||
@Query('page') page?: string,
|
||||
@Query('pageSize') pageSize?: string,
|
||||
@Query('startDate') startDate?: string,
|
||||
@Query('endDate') endDate?: string,
|
||||
) {
|
||||
this.logger.log(`[getOfflineSettlementEntries] 请求面对面结算明细, page=${page}, pageSize=${pageSize}`);
|
||||
return this.systemAccountReportService.getOfflineSettlementEntries({
|
||||
page: page ? parseInt(page, 10) : undefined,
|
||||
pageSize: pageSize ? parseInt(pageSize, 10) : undefined,
|
||||
startDate,
|
||||
endDate,
|
||||
});
|
||||
}
|
||||
|
||||
// [2026-01-05] 新增:所有系统账户分类账明细
|
||||
@Get('all-ledger')
|
||||
@ApiOperation({ summary: '获取所有系统账户的分类账明细' })
|
||||
|
|
@ -133,7 +157,7 @@ export class SystemAccountReportController {
|
|||
}
|
||||
|
||||
@Get('fee-entries-detailed')
|
||||
@ApiOperation({ summary: '获取手续费类型的详细记录列表' })
|
||||
@ApiOperation({ summary: '获取手续费类型的详细记录列表(系统费用,非提现手续费)' })
|
||||
@ApiQuery({ name: 'page', required: false, description: '页码,默认1' })
|
||||
@ApiQuery({ name: 'pageSize', required: false, description: '每页条数,默认50' })
|
||||
@ApiResponse({ status: 200, description: '手续费类型详细记录列表' })
|
||||
|
|
@ -147,4 +171,41 @@ export class SystemAccountReportController {
|
|||
pageSize: pageSize ? parseInt(pageSize, 10) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
// [2026-01-06] 新增:手续费归集账户统计接口(真正的提现手续费)
|
||||
// 来源:wallet-service 中 S0000000006 账户的 FEE_COLLECTION 类型流水
|
||||
// 回滚方式:删除以下 API 方法即可
|
||||
|
||||
@Get('fee-collection-summary')
|
||||
@ApiOperation({ summary: '获取手续费归集账户汇总统计(提现手续费)' })
|
||||
@ApiResponse({ status: 200, description: '手续费归集账户汇总统计' })
|
||||
async getFeeCollectionSummary() {
|
||||
this.logger.log(`[getFeeCollectionSummary] 请求手续费归集账户汇总统计`);
|
||||
return this.systemAccountReportService.getFeeCollectionSummary();
|
||||
}
|
||||
|
||||
@Get('fee-collection-entries')
|
||||
@ApiOperation({ summary: '获取手续费归集账户流水明细(提现手续费)' })
|
||||
@ApiQuery({ name: 'page', required: false, description: '页码,默认1' })
|
||||
@ApiQuery({ name: 'pageSize', required: false, description: '每页条数,默认50' })
|
||||
@ApiQuery({ name: 'feeType', required: false, description: '手续费类型筛选 (WITHDRAWAL_FEE/FIAT_WITHDRAWAL_FEE)' })
|
||||
@ApiQuery({ name: 'startDate', required: false, description: '开始日期' })
|
||||
@ApiQuery({ name: 'endDate', required: false, description: '结束日期' })
|
||||
@ApiResponse({ status: 200, description: '手续费归集流水明细列表' })
|
||||
async getFeeCollectionEntries(
|
||||
@Query('page') page?: string,
|
||||
@Query('pageSize') pageSize?: string,
|
||||
@Query('feeType') feeType?: string,
|
||||
@Query('startDate') startDate?: string,
|
||||
@Query('endDate') endDate?: string,
|
||||
) {
|
||||
this.logger.log(`[getFeeCollectionEntries] 请求手续费归集流水明细, page=${page}, pageSize=${pageSize}, feeType=${feeType}`);
|
||||
return this.systemAccountReportService.getFeeCollectionEntries({
|
||||
page: page ? parseInt(page, 10) : undefined,
|
||||
pageSize: pageSize ? parseInt(pageSize, 10) : undefined,
|
||||
feeType,
|
||||
startDate,
|
||||
endDate,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* 回滚方式:删除此文件,并从 application.module.ts 中移除注册
|
||||
*/
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { WalletServiceClient, OfflineSettlementSummary, AllSystemAccountsResponse, AllSystemAccountsLedgerResponse } from '../../infrastructure/external/wallet-service/wallet-service.client';
|
||||
import { WalletServiceClient, OfflineSettlementSummary, AllSystemAccountsResponse, AllSystemAccountsLedgerResponse, FeeCollectionSummaryResponse, FeeCollectionEntriesResponse, OfflineSettlementEntriesResponse } from '../../infrastructure/external/wallet-service/wallet-service.client';
|
||||
import { RewardServiceClient, ExpiredRewardsSummary, AllRewardTypeSummaries, RewardEntriesResponse } from '../../infrastructure/external/reward-service/reward-service.client';
|
||||
|
||||
/**
|
||||
|
|
@ -185,6 +185,21 @@ export class SystemAccountReportApplicationService {
|
|||
return this.rewardServiceClient.getExpiredRewardsSummary(params);
|
||||
}
|
||||
|
||||
// [2026-01-07] 新增:获取面对面结算明细列表
|
||||
// 回滚方式:删除此方法
|
||||
/**
|
||||
* 获取面对面结算明细列表
|
||||
*/
|
||||
async getOfflineSettlementEntries(params?: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}): Promise<OfflineSettlementEntriesResponse> {
|
||||
this.logger.log('[getOfflineSettlementEntries] 获取面对面结算明细列表');
|
||||
return this.walletServiceClient.getOfflineSettlementEntries(params);
|
||||
}
|
||||
|
||||
// [2026-01-05] 新增:获取所有系统账户的分类账明细
|
||||
/**
|
||||
* 获取所有系统账户的分类账明细
|
||||
|
|
@ -228,6 +243,7 @@ export class SystemAccountReportApplicationService {
|
|||
|
||||
/**
|
||||
* 获取手续费类型的详细记录列表
|
||||
* @deprecated 使用 getFeeCollectionEntries 获取真正的提现手续费记录
|
||||
*/
|
||||
async getFeeEntriesDetailed(params?: {
|
||||
page?: number;
|
||||
|
|
@ -237,6 +253,33 @@ export class SystemAccountReportApplicationService {
|
|||
return this.rewardServiceClient.getFeeEntriesDetailed(params);
|
||||
}
|
||||
|
||||
// [2026-01-06] 新增:获取手续费归集账户统计(真正的提现手续费)
|
||||
// 来源:wallet-service 中 S0000000006 账户的 FEE_COLLECTION 类型流水
|
||||
// 回滚方式:删除以下方法
|
||||
|
||||
/**
|
||||
* 获取手续费归集账户汇总统计
|
||||
* 统计 S0000000006 账户收到的所有提现手续费
|
||||
*/
|
||||
async getFeeCollectionSummary(): Promise<FeeCollectionSummaryResponse> {
|
||||
this.logger.log('[getFeeCollectionSummary] 获取手续费归集账户汇总统计');
|
||||
return this.walletServiceClient.getFeeCollectionSummary();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手续费归集账户流水明细
|
||||
*/
|
||||
async getFeeCollectionEntries(params?: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
feeType?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}): Promise<FeeCollectionEntriesResponse> {
|
||||
this.logger.log('[getFeeCollectionEntries] 获取手续费归集流水明细');
|
||||
return this.walletServiceClient.getFeeCollectionEntries(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装固定账户数据
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -19,6 +19,25 @@ export interface OfflineSettlementSummary {
|
|||
}>;
|
||||
}
|
||||
|
||||
// [2026-01-07] 新增:面对面结算明细条目
|
||||
export interface OfflineSettlementEntryDTO {
|
||||
id: string;
|
||||
accountSequence: string;
|
||||
amount: number;
|
||||
memo: string | null;
|
||||
refOrderId: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
// [2026-01-07] 新增:面对面结算明细列表响应
|
||||
export interface OfflineSettlementEntriesResponse {
|
||||
entries: OfflineSettlementEntryDTO[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
export interface SystemAccountBalance {
|
||||
accountSequence: string;
|
||||
name: string;
|
||||
|
|
@ -179,6 +198,46 @@ export class WalletServiceClient {
|
|||
}
|
||||
}
|
||||
|
||||
// [2026-01-07] 新增:获取面对面结算明细列表
|
||||
// 回滚方式:删除此方法
|
||||
/**
|
||||
* 获取面对面结算明细列表
|
||||
*/
|
||||
async getOfflineSettlementEntries(params?: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}): Promise<OfflineSettlementEntriesResponse> {
|
||||
try {
|
||||
const queryParams = new URLSearchParams();
|
||||
if (params?.page) queryParams.append('page', params.page.toString());
|
||||
if (params?.pageSize) queryParams.append('pageSize', params.pageSize.toString());
|
||||
if (params?.startDate) queryParams.append('startDate', params.startDate);
|
||||
if (params?.endDate) queryParams.append('endDate', params.endDate);
|
||||
|
||||
const url = `${this.baseUrl}/api/v1/wallets/statistics/offline-settlement-entries?${queryParams.toString()}`;
|
||||
this.logger.debug(`[getOfflineSettlementEntries] 请求: ${url}`);
|
||||
|
||||
const response = await firstValueFrom(
|
||||
this.httpService.get<{ success: boolean; data: OfflineSettlementEntriesResponse; timestamp: string }>(url),
|
||||
);
|
||||
|
||||
// wallet-service 返回 { success, data, timestamp } 包装格式
|
||||
return response.data.data;
|
||||
} catch (error) {
|
||||
this.logger.error(`[getOfflineSettlementEntries] 失败: ${error.message}`);
|
||||
// 返回默认值,不阻塞报表
|
||||
return {
|
||||
entries: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
pageSize: params?.pageSize ?? 50,
|
||||
totalPages: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统账户余额
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -372,6 +372,36 @@ export class InternalWalletController {
|
|||
return result;
|
||||
}
|
||||
|
||||
// [2026-01-07] 新增:面对面结算明细列表 API
|
||||
// 回滚方式:删除此方法
|
||||
@Get('statistics/offline-settlement-entries')
|
||||
@Public()
|
||||
@ApiOperation({ summary: '面对面(线下)结算明细列表(内部API) - 用于系统账户报表' })
|
||||
@ApiQuery({ name: 'page', required: false, description: '页码,默认1' })
|
||||
@ApiQuery({ name: 'pageSize', required: false, description: '每页条数,默认50' })
|
||||
@ApiQuery({ name: 'startDate', required: false, description: '开始日期 (YYYY-MM-DD)' })
|
||||
@ApiQuery({ name: 'endDate', required: false, description: '结束日期 (YYYY-MM-DD)' })
|
||||
@ApiResponse({ status: 200, description: '面对面结算明细列表' })
|
||||
async getOfflineSettlementEntries(
|
||||
@Query('page') page?: string,
|
||||
@Query('pageSize') pageSize?: string,
|
||||
@Query('startDate') startDate?: string,
|
||||
@Query('endDate') endDate?: string,
|
||||
) {
|
||||
this.logger.log(`========== statistics/offline-settlement-entries 请求 ==========`);
|
||||
this.logger.log(`page: ${page}, pageSize: ${pageSize}, startDate: ${startDate}, endDate: ${endDate}`);
|
||||
|
||||
const result = await this.walletService.getOfflineSettlementEntries({
|
||||
page: page ? parseInt(page, 10) : undefined,
|
||||
pageSize: pageSize ? parseInt(pageSize, 10) : undefined,
|
||||
startDate: startDate ? new Date(startDate) : undefined,
|
||||
endDate: endDate ? new Date(endDate) : undefined,
|
||||
});
|
||||
|
||||
this.logger.log(`面对面结算明细结果: total=${result.total}, page=${result.page}, totalPages=${result.totalPages}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Get('statistics/system-accounts-balances')
|
||||
@Public()
|
||||
@ApiOperation({ summary: '批量查询系统账户余额(内部API) - 用于系统账户报表' })
|
||||
|
|
@ -463,4 +493,52 @@ export class InternalWalletController {
|
|||
this.logger.log(`所有系统账户分类账查询完成: 固定=${result.fixedAccountsLedger.length}, 省=${result.provinceAccountsLedger.length}, 市=${result.cityAccountsLedger.length}, 总流水=${totalLedgers}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
// [2026-01-06] 新增:手续费归集账户统计 API
|
||||
// 用于系统账户报表中的"手续费账户汇总" Tab
|
||||
// 回滚方式:删除以下 API 方法即可
|
||||
|
||||
@Get('statistics/fee-collection-summary')
|
||||
@Public()
|
||||
@ApiOperation({ summary: '获取手续费归集账户汇总统计(内部API) - 用于系统账户报表' })
|
||||
@ApiResponse({ status: 200, description: '手续费归集账户汇总统计' })
|
||||
async getFeeCollectionSummary() {
|
||||
this.logger.log(`========== statistics/fee-collection-summary 请求 ==========`);
|
||||
|
||||
const result = await this.walletService.getFeeCollectionSummary();
|
||||
|
||||
this.logger.log(`手续费归集统计结果: totalAmount=${result.totalAmount}, totalCount=${result.totalCount}, balance=${result.accountBalance}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Get('statistics/fee-collection-entries')
|
||||
@Public()
|
||||
@ApiOperation({ summary: '获取手续费归集账户流水明细(内部API) - 用于系统账户报表' })
|
||||
@ApiQuery({ name: 'page', required: false, description: '页码,默认1' })
|
||||
@ApiQuery({ name: 'pageSize', required: false, description: '每页条数,默认50' })
|
||||
@ApiQuery({ name: 'feeType', required: false, description: '手续费类型筛选 (WITHDRAWAL_FEE/FIAT_WITHDRAWAL_FEE)' })
|
||||
@ApiQuery({ name: 'startDate', required: false, description: '开始日期' })
|
||||
@ApiQuery({ name: 'endDate', required: false, description: '结束日期' })
|
||||
@ApiResponse({ status: 200, description: '手续费归集流水明细列表' })
|
||||
async getFeeCollectionEntries(
|
||||
@Query('page') page?: string,
|
||||
@Query('pageSize') pageSize?: string,
|
||||
@Query('feeType') feeType?: string,
|
||||
@Query('startDate') startDate?: string,
|
||||
@Query('endDate') endDate?: string,
|
||||
) {
|
||||
this.logger.log(`========== statistics/fee-collection-entries 请求 ==========`);
|
||||
this.logger.log(`page=${page}, pageSize=${pageSize}, feeType=${feeType}`);
|
||||
|
||||
const result = await this.walletService.getFeeCollectionEntries({
|
||||
page: page ? parseInt(page, 10) : 1,
|
||||
pageSize: pageSize ? parseInt(pageSize, 10) : 50,
|
||||
feeType,
|
||||
startDate: startDate ? new Date(startDate) : undefined,
|
||||
endDate: endDate ? new Date(endDate) : undefined,
|
||||
});
|
||||
|
||||
this.logger.log(`手续费归集流水查询结果: ${result.total} 条记录`);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3138,6 +3138,91 @@ export class WalletApplicationService {
|
|||
};
|
||||
}
|
||||
|
||||
// [2026-01-07] 新增:获取面对面结算明细列表
|
||||
// 回滚方式:删除此方法
|
||||
/**
|
||||
* 获取面对面结算明细列表
|
||||
* 查询 SPECIAL_DEDUCTION 类型的流水记录
|
||||
*/
|
||||
async getOfflineSettlementEntries(params: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
}): Promise<{
|
||||
entries: Array<{
|
||||
id: string;
|
||||
accountSequence: string;
|
||||
amount: number;
|
||||
memo: string | null;
|
||||
refOrderId: string | null;
|
||||
createdAt: string;
|
||||
}>;
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}> {
|
||||
const page = params.page ?? 1;
|
||||
const pageSize = params.pageSize ?? 50;
|
||||
const skip = (page - 1) * pageSize;
|
||||
|
||||
this.logger.log(`[getOfflineSettlementEntries] 查询面对面结算明细: page=${page}, pageSize=${pageSize}`);
|
||||
|
||||
// 构建查询条件
|
||||
const where: any = {
|
||||
entryType: 'SPECIAL_DEDUCTION',
|
||||
};
|
||||
|
||||
if (params.startDate || params.endDate) {
|
||||
where.createdAt = {};
|
||||
if (params.startDate) {
|
||||
where.createdAt.gte = params.startDate;
|
||||
}
|
||||
if (params.endDate) {
|
||||
where.createdAt.lte = params.endDate;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
const total = await this.prisma.ledgerEntry.count({ where });
|
||||
|
||||
// 查询数据
|
||||
const entries = await this.prisma.ledgerEntry.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip,
|
||||
take: pageSize,
|
||||
select: {
|
||||
id: true,
|
||||
accountSequence: true,
|
||||
amount: true,
|
||||
memo: true,
|
||||
refOrderId: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
const totalPages = Math.ceil(total / pageSize);
|
||||
|
||||
this.logger.log(`[getOfflineSettlementEntries] 查询结果: total=${total}, page=${page}, totalPages=${totalPages}`);
|
||||
|
||||
return {
|
||||
entries: entries.map(entry => ({
|
||||
id: entry.id.toString(),
|
||||
accountSequence: entry.accountSequence,
|
||||
amount: Math.abs(Number(entry.amount)), // SPECIAL_DEDUCTION 金额是负数,取绝对值
|
||||
memo: entry.memo,
|
||||
refOrderId: entry.refOrderId,
|
||||
createdAt: entry.createdAt.toISOString(),
|
||||
})),
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
totalPages,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询系统账户余额
|
||||
* 用于系统账户报表展示固定账户(总部、成本、运营)的余额
|
||||
|
|
@ -3248,4 +3333,206 @@ export class WalletApplicationService {
|
|||
this.logger.log('[getAllSystemAccounts] 固定: ' + fixedAccounts.length + ', 省: ' + provinceAccounts.length + ', 市: ' + cityAccounts.length);
|
||||
return { fixedAccounts, provinceAccounts, cityAccounts };
|
||||
}
|
||||
|
||||
// =============== 手续费归集账户统计 API ===============
|
||||
// [2026-01-06] 新增:获取手续费归集账户 (S0000000006) 的统计数据
|
||||
// 用于系统账户报表中的"手续费账户汇总" Tab
|
||||
// 回滚方式:删除以下方法
|
||||
|
||||
/**
|
||||
* 获取手续费归集账户汇总统计
|
||||
* 统计 S0000000006 账户收到的所有 FEE_COLLECTION 类型流水
|
||||
*/
|
||||
async getFeeCollectionSummary(): Promise<{
|
||||
accountSequence: string;
|
||||
accountBalance: number;
|
||||
totalAmount: number;
|
||||
totalCount: number;
|
||||
breakdown: Array<{
|
||||
feeType: string;
|
||||
amount: number;
|
||||
count: number;
|
||||
}>;
|
||||
byMonth: Array<{
|
||||
month: string;
|
||||
amount: number;
|
||||
count: number;
|
||||
}>;
|
||||
}> {
|
||||
this.logger.log('[getFeeCollectionSummary] 查询手续费归集账户统计');
|
||||
|
||||
const feeAccountSequence = WalletApplicationService.FEE_COLLECTION_ACCOUNT;
|
||||
|
||||
// 1. 获取账户余额
|
||||
const wallet = await this.prisma.walletAccount.findUnique({
|
||||
where: { accountSequence: feeAccountSequence },
|
||||
select: { usdtAvailable: true },
|
||||
});
|
||||
const accountBalance = Number(wallet?.usdtAvailable) || 0;
|
||||
|
||||
// 2. 统计所有 FEE_COLLECTION 类型的流水总额和总笔数
|
||||
const aggregateResult = await this.prisma.ledgerEntry.aggregate({
|
||||
where: {
|
||||
accountSequence: feeAccountSequence,
|
||||
entryType: 'FEE_COLLECTION',
|
||||
},
|
||||
_sum: {
|
||||
amount: true,
|
||||
},
|
||||
_count: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
const totalAmount = Number(aggregateResult._sum.amount) || 0;
|
||||
const totalCount = aggregateResult._count.id || 0;
|
||||
|
||||
// 3. 按手续费类型分组统计(从 payloadJson 中提取 feeType)
|
||||
// 由于 Prisma 不支持直接 groupBy jsonb 字段,使用原生 SQL
|
||||
const breakdownRaw = await this.prisma.$queryRaw<Array<{
|
||||
feeType: string;
|
||||
amount: any;
|
||||
count: any;
|
||||
}>>`
|
||||
SELECT
|
||||
COALESCE(payload_json->>'feeType', 'UNKNOWN') as "feeType",
|
||||
SUM(amount) as amount,
|
||||
COUNT(*) as count
|
||||
FROM ledger_entries
|
||||
WHERE account_sequence = ${feeAccountSequence}
|
||||
AND entry_type = 'FEE_COLLECTION'
|
||||
GROUP BY COALESCE(payload_json->>'feeType', 'UNKNOWN')
|
||||
ORDER BY amount DESC
|
||||
`;
|
||||
|
||||
// 4. 按月统计
|
||||
const byMonthRaw = await this.prisma.$queryRaw<Array<{
|
||||
month: string;
|
||||
amount: any;
|
||||
count: any;
|
||||
}>>`
|
||||
SELECT
|
||||
TO_CHAR(created_at, 'YYYY-MM') as month,
|
||||
SUM(amount) as amount,
|
||||
COUNT(*) as count
|
||||
FROM ledger_entries
|
||||
WHERE account_sequence = ${feeAccountSequence}
|
||||
AND entry_type = 'FEE_COLLECTION'
|
||||
GROUP BY TO_CHAR(created_at, 'YYYY-MM')
|
||||
ORDER BY month DESC
|
||||
LIMIT 12
|
||||
`;
|
||||
|
||||
this.logger.log(`[getFeeCollectionSummary] 统计完成: totalAmount=${totalAmount}, totalCount=${totalCount}`);
|
||||
|
||||
return {
|
||||
accountSequence: feeAccountSequence,
|
||||
accountBalance,
|
||||
totalAmount,
|
||||
totalCount,
|
||||
breakdown: breakdownRaw.map(row => ({
|
||||
feeType: row.feeType,
|
||||
amount: Number(row.amount) || 0,
|
||||
count: Number(row.count) || 0,
|
||||
})),
|
||||
byMonth: byMonthRaw.map(row => ({
|
||||
month: row.month,
|
||||
amount: Number(row.amount) || 0,
|
||||
count: Number(row.count) || 0,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取手续费归集账户的详细流水列表
|
||||
*/
|
||||
async getFeeCollectionEntries(params: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
feeType?: string;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
}): Promise<{
|
||||
entries: Array<{
|
||||
id: string;
|
||||
feeType: string;
|
||||
amount: number;
|
||||
refOrderId: string | null;
|
||||
memo: string | null;
|
||||
createdAt: string;
|
||||
}>;
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}> {
|
||||
const page = params.page ?? 1;
|
||||
const pageSize = params.pageSize ?? 50;
|
||||
const skip = (page - 1) * pageSize;
|
||||
const feeAccountSequence = WalletApplicationService.FEE_COLLECTION_ACCOUNT;
|
||||
|
||||
this.logger.log(`[getFeeCollectionEntries] 查询手续费归集流水: page=${page}, pageSize=${pageSize}, feeType=${params.feeType}`);
|
||||
|
||||
// 构建查询条件
|
||||
const where: any = {
|
||||
accountSequence: feeAccountSequence,
|
||||
entryType: 'FEE_COLLECTION',
|
||||
};
|
||||
|
||||
if (params.startDate || params.endDate) {
|
||||
where.createdAt = {};
|
||||
if (params.startDate) {
|
||||
where.createdAt.gte = params.startDate;
|
||||
}
|
||||
if (params.endDate) {
|
||||
where.createdAt.lte = params.endDate;
|
||||
}
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
const total = await this.prisma.ledgerEntry.count({ where });
|
||||
|
||||
// 查询数据
|
||||
const entries = await this.prisma.ledgerEntry.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip,
|
||||
take: pageSize,
|
||||
select: {
|
||||
id: true,
|
||||
amount: true,
|
||||
refOrderId: true,
|
||||
memo: true,
|
||||
payloadJson: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 如果指定了 feeType,在应用层过滤(因为 Prisma 不支持 jsonb 字段的 where 条件)
|
||||
let filteredEntries = entries;
|
||||
if (params.feeType) {
|
||||
filteredEntries = entries.filter(entry => {
|
||||
const payload = entry.payloadJson as any;
|
||||
return payload?.feeType === params.feeType;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
entries: filteredEntries.map(entry => {
|
||||
const payload = entry.payloadJson as any;
|
||||
return {
|
||||
id: entry.id.toString(),
|
||||
feeType: payload?.feeType || 'UNKNOWN',
|
||||
amount: Number(entry.amount),
|
||||
refOrderId: entry.refOrderId,
|
||||
memo: entry.memo,
|
||||
createdAt: entry.createdAt.toISOString(),
|
||||
};
|
||||
}),
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
totalPages: Math.ceil(total / pageSize),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,12 +156,31 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.accountInfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
padding: 8px 12px;
|
||||
background: #f3f4f6;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.accountLabel {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.accountValue {
|
||||
font-size: 14px;
|
||||
color: #374151;
|
||||
font-family: monospace;
|
||||
background: #fff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.accountSequence {
|
||||
font-size: 12px;
|
||||
color: #9ca3af;
|
||||
|
|
@ -293,6 +312,43 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* [2026-01-07] 新增:明细列表切换区域样式 */
|
||||
.toggleSection {
|
||||
margin-top: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toggleButton {
|
||||
padding: 8px 20px;
|
||||
background-color: #f3f4f6;
|
||||
color: #374151;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: #e5e7eb;
|
||||
border-color: #9ca3af;
|
||||
}
|
||||
}
|
||||
|
||||
.entriesSection {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.paginationInfo {
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
background-color: #f9fafb;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.footer {
|
||||
text-align: right;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,12 @@ import type {
|
|||
FeeAccountSummary,
|
||||
RewardEntriesResponse,
|
||||
RewardEntryDTO,
|
||||
FeeCollectionSummaryResponse,
|
||||
FeeCollectionEntriesResponse,
|
||||
OfflineSettlementEntriesResponse,
|
||||
OfflineSettlementEntryDTO,
|
||||
} from '@/types';
|
||||
import { ENTRY_TYPE_LABELS, ACCOUNT_TYPE_LABELS, FEE_TYPE_LABELS, REWARD_RIGHT_TYPE_LABELS, REWARD_STATUS_LABELS } from '@/types';
|
||||
import { ENTRY_TYPE_LABELS, ACCOUNT_TYPE_LABELS, FEE_TYPE_LABELS, REWARD_RIGHT_TYPE_LABELS, REWARD_STATUS_LABELS, FEE_COLLECTION_TYPE_LABELS } from '@/types';
|
||||
import styles from './SystemAccountsTab.module.scss';
|
||||
|
||||
/**
|
||||
|
|
@ -239,11 +243,9 @@ 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内容 */}
|
||||
{/* [2026-01-06] 更新:手续费账户汇总改用 wallet-service 的真正提现手续费数据 */}
|
||||
{activeTab === 'feeAccount' && (
|
||||
<FeeAccountSection
|
||||
data={rewardTypeSummaries?.feeAccountSummary}
|
||||
loading={rewardTypeSummariesLoading}
|
||||
onRefresh={loadRewardTypeSummaries}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -406,8 +408,35 @@ function RegionAccountsSection({ data, type }: { data: RegionAccountsSummary; ty
|
|||
/**
|
||||
* 面对面结算统计区域
|
||||
* [2026-01-05] 更新:USDT改为绿积分,添加空值检查
|
||||
* [2026-01-07] 更新:添加明细列表显示
|
||||
*/
|
||||
function OfflineSettlementSection({ data }: { data: SystemAccountReportResponse['offlineSettlement'] | null | undefined }) {
|
||||
const [entriesData, setEntriesData] = useState<OfflineSettlementEntriesResponse | null>(null);
|
||||
const [entriesLoading, setEntriesLoading] = useState(false);
|
||||
const [showEntries, setShowEntries] = useState(false);
|
||||
|
||||
// 加载明细列表
|
||||
const loadEntries = useCallback(async () => {
|
||||
setEntriesLoading(true);
|
||||
try {
|
||||
const response = await systemAccountReportService.getOfflineSettlementEntries({ pageSize: 100 });
|
||||
if (response.data) {
|
||||
setEntriesData(response.data);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load offline settlement entries:', err);
|
||||
} finally {
|
||||
setEntriesLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 切换显示明细时加载数据
|
||||
useEffect(() => {
|
||||
if (showEntries && !entriesData && !entriesLoading) {
|
||||
loadEntries();
|
||||
}
|
||||
}, [showEntries, entriesData, entriesLoading, loadEntries]);
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<div className={styles.section}>
|
||||
|
|
@ -460,7 +489,57 @@ function OfflineSettlementSection({ data }: { data: SystemAccountReportResponse[
|
|||
</>
|
||||
)}
|
||||
|
||||
{(!data.byMonth || data.byMonth.length === 0) && (
|
||||
{/* 明细列表切换按钮 */}
|
||||
<div className={styles.toggleSection}>
|
||||
<button
|
||||
className={styles.toggleButton}
|
||||
onClick={() => setShowEntries(!showEntries)}
|
||||
>
|
||||
{showEntries ? '收起明细' : '查看明细列表'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 明细列表 */}
|
||||
{showEntries && (
|
||||
<div className={styles.entriesSection}>
|
||||
<h4 className={styles.subTitle}>明细列表</h4>
|
||||
{entriesLoading ? (
|
||||
<div className={styles.loading}>加载中...</div>
|
||||
) : entriesData && entriesData.entries.length > 0 ? (
|
||||
<div className={styles.tableWrapper}>
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>账户</th>
|
||||
<th>金额 (绿积分)</th>
|
||||
<th>备注</th>
|
||||
<th>时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{entriesData.entries.map((entry) => (
|
||||
<tr key={entry.id}>
|
||||
<td>{entry.accountSequence}</td>
|
||||
<td>{formatAmount(entry.amount)}</td>
|
||||
<td>{entry.memo || '-'}</td>
|
||||
<td>{new Date(entry.createdAt).toLocaleString('zh-CN')}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{entriesData.total > entriesData.entries.length && (
|
||||
<div className={styles.paginationInfo}>
|
||||
显示 {entriesData.entries.length} / {entriesData.total} 条
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.emptyTable}>暂无明细数据</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(!data.byMonth || data.byMonth.length === 0) && !showEntries && (
|
||||
<div className={styles.emptyTable}>暂无面对面结算数据</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -777,39 +856,68 @@ function LedgerAccountCard({
|
|||
);
|
||||
}
|
||||
|
||||
// [2026-01-06] 新增:手续费账户汇总组件
|
||||
// [2026-01-06] 更新:手续费账户汇总组件 - 使用真正的提现手续费归集数据
|
||||
// 数据来源: wallet-service 的 S0000000006 账户 FEE_COLLECTION 类型流水
|
||||
// 而非 reward-service 的系统费用(成本费、运营费等)
|
||||
/**
|
||||
* 手续费账户汇总区域
|
||||
*/
|
||||
function FeeAccountSection({
|
||||
data,
|
||||
loading,
|
||||
onRefresh,
|
||||
}: {
|
||||
data: FeeAccountSummary | undefined;
|
||||
loading: boolean;
|
||||
onRefresh: () => void;
|
||||
}) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [data, setData] = useState<FeeCollectionSummaryResponse | null>(null);
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const [detailsLoading, setDetailsLoading] = useState(false);
|
||||
const [detailsData, setDetailsData] = useState<RewardEntriesResponse | null>(null);
|
||||
const [detailsData, setDetailsData] = useState<FeeCollectionEntriesResponse | null>(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
// 加载手续费归集汇总数据
|
||||
const loadSummary = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await systemAccountReportService.getFeeCollectionSummary();
|
||||
if (response.data) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load fee collection summary:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 加载详细流水
|
||||
const loadDetails = useCallback(async (page: number = 1) => {
|
||||
setDetailsLoading(true);
|
||||
try {
|
||||
const response = await systemAccountReportService.getFeeEntriesDetailed({ page, pageSize: 20 });
|
||||
const response = await systemAccountReportService.getFeeCollectionEntries({ page, pageSize: 20 });
|
||||
if (response.data) {
|
||||
setDetailsData(response.data);
|
||||
setCurrentPage(page);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load fee entries details:', err);
|
||||
console.error('Failed to load fee collection entries:', err);
|
||||
} finally {
|
||||
setDetailsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 初始加载
|
||||
useEffect(() => {
|
||||
loadSummary();
|
||||
}, [loadSummary]);
|
||||
|
||||
const handleRefresh = () => {
|
||||
loadSummary();
|
||||
if (showDetails) {
|
||||
loadDetails(currentPage);
|
||||
}
|
||||
onRefresh();
|
||||
};
|
||||
|
||||
const handleToggleDetails = () => {
|
||||
if (!showDetails && !detailsData) {
|
||||
loadDetails(1);
|
||||
|
|
@ -831,7 +939,7 @@ function FeeAccountSection({
|
|||
<div className={styles.section}>
|
||||
<div className={styles.emptyTable}>
|
||||
<span>暂无手续费账户数据</span>
|
||||
<button onClick={onRefresh} className={styles.retryButton}>
|
||||
<button onClick={handleRefresh} className={styles.retryButton}>
|
||||
加载数据
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -843,15 +951,25 @@ function FeeAccountSection({
|
|||
<div className={styles.section}>
|
||||
<div className={styles.sectionHeader}>
|
||||
<h3 className={styles.sectionTitle}>手续费账户汇总</h3>
|
||||
<button onClick={onRefresh} className={styles.refreshButton}>
|
||||
<button onClick={handleRefresh} className={styles.refreshButton}>
|
||||
刷新
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 账户信息 */}
|
||||
<div className={styles.accountInfo}>
|
||||
<span className={styles.accountLabel}>手续费归集账户: </span>
|
||||
<span className={styles.accountValue}>{data.accountSequence}</span>
|
||||
</div>
|
||||
|
||||
{/* 汇总卡片 */}
|
||||
<div className={styles.summaryCards}>
|
||||
<div className={styles.summaryCard}>
|
||||
<span className={styles.summaryLabel}>总金额</span>
|
||||
<span className={styles.summaryLabel}>当前余额</span>
|
||||
<span className={styles.summaryValue}>{formatAmount(data.accountBalance)} 绿积分</span>
|
||||
</div>
|
||||
<div className={styles.summaryCard}>
|
||||
<span className={styles.summaryLabel}>累计收取</span>
|
||||
<span className={styles.summaryValue}>{formatAmount(data.totalAmount)} 绿积分</span>
|
||||
</div>
|
||||
<div className={styles.summaryCard}>
|
||||
|
|
@ -860,23 +978,23 @@ function FeeAccountSection({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* 按类型明细 */}
|
||||
{/* 按手续费类型明细 */}
|
||||
{data.breakdown && data.breakdown.length > 0 ? (
|
||||
<>
|
||||
<h4 className={styles.subTitle}>按类型明细</h4>
|
||||
<h4 className={styles.subTitle}>按手续费类型明细</h4>
|
||||
<div className={styles.tableWrapper}>
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>费用类型</th>
|
||||
<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>
|
||||
<tr key={item.feeType}>
|
||||
<td>{FEE_COLLECTION_TYPE_LABELS[item.feeType] || item.feeType}</td>
|
||||
<td>{formatAmount(item.amount)}</td>
|
||||
<td>{item.count}</td>
|
||||
</tr>
|
||||
|
|
@ -889,6 +1007,33 @@ function FeeAccountSection({
|
|||
<div className={styles.emptyTable}>暂无手续费数据</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.detailsSection}>
|
||||
<button onClick={handleToggleDetails} className={styles.toggleDetailsButton}>
|
||||
|
|
@ -909,26 +1054,20 @@ function FeeAccountSection({
|
|||
<thead>
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>账户</th>
|
||||
<th>订单号</th>
|
||||
<th>类型</th>
|
||||
<th>手续费类型</th>
|
||||
<th>关联订单号</th>
|
||||
<th>金额 (绿积分)</th>
|
||||
<th>状态</th>
|
||||
<th>备注</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{detailsData.entries.map((entry) => (
|
||||
<tr key={entry.id}>
|
||||
<td>{new Date(entry.createdAt).toLocaleString('zh-CN')}</td>
|
||||
<td>{entry.accountSequence}</td>
|
||||
<td className={styles.orderId}>{entry.sourceOrderId}</td>
|
||||
<td>{REWARD_RIGHT_TYPE_LABELS[entry.rightType] || entry.rightType}</td>
|
||||
<td>{formatAmount(entry.usdtAmount)}</td>
|
||||
<td>
|
||||
<span className={`${styles.statusBadge} ${entry.rewardStatus === 'SETTLED' ? styles.settled : ''}`}>
|
||||
{REWARD_STATUS_LABELS[entry.rewardStatus] || entry.rewardStatus}
|
||||
</span>
|
||||
</td>
|
||||
<td>{FEE_COLLECTION_TYPE_LABELS[entry.feeType] || entry.feeType}</td>
|
||||
<td className={styles.orderId}>{entry.refOrderId || '-'}</td>
|
||||
<td>{formatAmount(entry.amount)}</td>
|
||||
<td>{entry.memo || '-'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -230,10 +230,15 @@ 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] 新增:收益类型汇总统计
|
||||
// [2026-01-06] 新增:收益类型汇总统计(系统费用:成本费、运营费等)
|
||||
REWARD_TYPE_SUMMARIES: '/v1/system-account-reports/reward-type-summaries',
|
||||
// [2026-01-06] 新增:收益类型详细记录列表
|
||||
REWARD_ENTRIES_BY_TYPE: '/v1/system-account-reports/reward-entries-by-type',
|
||||
FEE_ENTRIES_DETAILED: '/v1/system-account-reports/fee-entries-detailed',
|
||||
// [2026-01-06] 新增:手续费归集账户统计(真正的提现手续费)
|
||||
FEE_COLLECTION_SUMMARY: '/v1/system-account-reports/fee-collection-summary',
|
||||
FEE_COLLECTION_ENTRIES: '/v1/system-account-reports/fee-collection-entries',
|
||||
// [2026-01-07] 新增:面对面结算明细列表
|
||||
OFFLINE_SETTLEMENT_ENTRIES: '/v1/system-account-reports/offline-settlement-entries',
|
||||
},
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ import type {
|
|||
AllSystemAccountsLedgerResponse,
|
||||
AllRewardTypeSummariesResponse,
|
||||
RewardEntriesResponse,
|
||||
FeeCollectionSummaryResponse,
|
||||
FeeCollectionEntriesResponse,
|
||||
OfflineSettlementEntriesResponse,
|
||||
} from '@/types';
|
||||
|
||||
/**
|
||||
|
|
@ -66,6 +69,19 @@ export const systemAccountReportService = {
|
|||
return apiClient.get(API_ENDPOINTS.SYSTEM_ACCOUNT_REPORTS.OFFLINE_SETTLEMENT, { params });
|
||||
},
|
||||
|
||||
// [2026-01-07] 新增:获取面对面结算明细列表
|
||||
/**
|
||||
* 获取面对面结算明细列表
|
||||
*/
|
||||
async getOfflineSettlementEntries(params?: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}): Promise<ApiResponse<OfflineSettlementEntriesResponse>> {
|
||||
return apiClient.get(API_ENDPOINTS.SYSTEM_ACCOUNT_REPORTS.OFFLINE_SETTLEMENT_ENTRIES, { params });
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取过期收益统计
|
||||
*/
|
||||
|
|
@ -103,7 +119,8 @@ export const systemAccountReportService = {
|
|||
},
|
||||
|
||||
/**
|
||||
* 获取手续费类型的详细记录列表
|
||||
* 获取手续费类型的详细记录列表(系统费用:成本费、运营费等)
|
||||
* @deprecated 使用 getFeeCollectionSummary 获取真正的提现手续费
|
||||
*/
|
||||
async getFeeEntriesDetailed(params?: {
|
||||
page?: number;
|
||||
|
|
@ -111,6 +128,31 @@ export const systemAccountReportService = {
|
|||
}): Promise<ApiResponse<RewardEntriesResponse>> {
|
||||
return apiClient.get(API_ENDPOINTS.SYSTEM_ACCOUNT_REPORTS.FEE_ENTRIES_DETAILED, { params });
|
||||
},
|
||||
|
||||
// [2026-01-06] 新增:手续费归集账户统计(真正的提现手续费)
|
||||
// 来源:wallet-service 中 S0000000006 账户的 FEE_COLLECTION 类型流水
|
||||
// 回滚方式:删除以下方法
|
||||
|
||||
/**
|
||||
* 获取手续费归集账户汇总统计(提现手续费)
|
||||
* 统计 S0000000006 账户收到的所有提现手续费
|
||||
*/
|
||||
async getFeeCollectionSummary(): Promise<ApiResponse<FeeCollectionSummaryResponse>> {
|
||||
return apiClient.get(API_ENDPOINTS.SYSTEM_ACCOUNT_REPORTS.FEE_COLLECTION_SUMMARY);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取手续费归集账户流水明细(提现手续费)
|
||||
*/
|
||||
async getFeeCollectionEntries(params?: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
feeType?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
}): Promise<ApiResponse<FeeCollectionEntriesResponse>> {
|
||||
return apiClient.get(API_ENDPOINTS.SYSTEM_ACCOUNT_REPORTS.FEE_COLLECTION_ENTRIES, { params });
|
||||
},
|
||||
};
|
||||
|
||||
export default systemAccountReportService;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,31 @@ export interface OfflineSettlementSummary {
|
|||
}>;
|
||||
}
|
||||
|
||||
// [2026-01-07] 新增:面对面结算明细条目
|
||||
/**
|
||||
* 面对面结算明细条目
|
||||
*/
|
||||
export interface OfflineSettlementEntryDTO {
|
||||
id: string;
|
||||
accountSequence: string;
|
||||
amount: number;
|
||||
memo: string | null;
|
||||
refOrderId: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
// [2026-01-07] 新增:面对面结算明细列表响应
|
||||
/**
|
||||
* 面对面结算明细列表响应
|
||||
*/
|
||||
export interface OfflineSettlementEntriesResponse {
|
||||
entries: OfflineSettlementEntryDTO[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过期收益统计
|
||||
*/
|
||||
|
|
@ -302,3 +327,56 @@ export const REWARD_STATUS_LABELS: Record<string, string> = {
|
|||
SETTLED: '已结算',
|
||||
EXPIRED: '已过期',
|
||||
};
|
||||
|
||||
// [2026-01-06] 新增:手续费归集账户汇总类型(真正的提现手续费)
|
||||
/**
|
||||
* 手续费归集账户汇总响应
|
||||
*/
|
||||
export interface FeeCollectionSummaryResponse {
|
||||
accountSequence: string;
|
||||
accountBalance: number;
|
||||
totalAmount: number;
|
||||
totalCount: number;
|
||||
breakdown: Array<{
|
||||
feeType: string;
|
||||
amount: number;
|
||||
count: number;
|
||||
}>;
|
||||
byMonth: Array<{
|
||||
month: string;
|
||||
amount: number;
|
||||
count: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手续费归集流水条目
|
||||
*/
|
||||
export interface FeeCollectionEntryDTO {
|
||||
id: string;
|
||||
feeType: string;
|
||||
amount: number;
|
||||
refOrderId: string | null;
|
||||
memo: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手续费归集流水列表响应
|
||||
*/
|
||||
export interface FeeCollectionEntriesResponse {
|
||||
entries: FeeCollectionEntryDTO[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手续费类型显示名称映射(提现手续费)
|
||||
*/
|
||||
export const FEE_COLLECTION_TYPE_LABELS: Record<string, string> = {
|
||||
WITHDRAWAL_FEE: '区块链提现手续费',
|
||||
FIAT_WITHDRAWAL_FEE: '法币提现手续费',
|
||||
UNKNOWN: '未知类型',
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue