feat(wallet-service): 添加手续费归集统计的历史数据兼容
当 FEE_COLLECTION 流水为空时,自动从提现订单表查询历史手续费: - getFeeCollectionSummary: 从 withdrawal_orders 和 fiat_withdrawal_orders 聚合统计 - getFeeCollectionEntries: 从两个订单表查询明细列表,支持分页和类型筛选 - 按月统计使用 UNION ALL 合并两种提现订单数据 - 明细记录添加备注说明区分来源(区块链/法币) 回滚方式:删除 fallback 代码块和两个私有方法 🤖 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
4dcdfb8a3c
commit
272b4ffdbf
|
|
@ -3425,6 +3425,13 @@ export class WalletApplicationService {
|
||||||
|
|
||||||
this.logger.log(`[getFeeCollectionSummary] 统计完成: totalAmount=${totalAmount}, totalCount=${totalCount}`);
|
this.logger.log(`[getFeeCollectionSummary] 统计完成: totalAmount=${totalAmount}, totalCount=${totalCount}`);
|
||||||
|
|
||||||
|
// [2026-01-07] 兼容:如果 FEE_COLLECTION 流水为空,从提现订单表查询历史手续费
|
||||||
|
// 回滚方式:删除此 fallback 代码块
|
||||||
|
if (totalCount === 0) {
|
||||||
|
this.logger.log('[getFeeCollectionSummary] FEE_COLLECTION 流水为空,使用提现订单表作为 fallback');
|
||||||
|
return this.getFeeCollectionSummaryFromOrders(feeAccountSequence, accountBalance);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accountSequence: feeAccountSequence,
|
accountSequence: feeAccountSequence,
|
||||||
accountBalance,
|
accountBalance,
|
||||||
|
|
@ -3443,6 +3450,97 @@ export class WalletApplicationService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [2026-01-07] 新增:从提现订单表查询历史手续费(兼容旧数据)
|
||||||
|
// 回滚方式:删除此方法
|
||||||
|
private async getFeeCollectionSummaryFromOrders(
|
||||||
|
accountSequence: string,
|
||||||
|
accountBalance: number,
|
||||||
|
): 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 }>;
|
||||||
|
}> {
|
||||||
|
// 1. 统计区块链提现订单手续费 (CONFIRMED 状态)
|
||||||
|
const withdrawalStats = await this.prisma.withdrawalOrder.aggregate({
|
||||||
|
where: { status: 'CONFIRMED' },
|
||||||
|
_sum: { fee: true },
|
||||||
|
_count: { id: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. 统计法币提现订单手续费 (COMPLETED 状态)
|
||||||
|
const fiatStats = await this.prisma.fiatWithdrawalOrder.aggregate({
|
||||||
|
where: { status: 'COMPLETED' },
|
||||||
|
_sum: { fee: true },
|
||||||
|
_count: { id: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const withdrawalFeeAmount = Number(withdrawalStats._sum.fee) || 0;
|
||||||
|
const withdrawalFeeCount = withdrawalStats._count.id || 0;
|
||||||
|
const fiatFeeAmount = Number(fiatStats._sum.fee) || 0;
|
||||||
|
const fiatFeeCount = fiatStats._count.id || 0;
|
||||||
|
|
||||||
|
const totalAmount = withdrawalFeeAmount + fiatFeeAmount;
|
||||||
|
const totalCount = withdrawalFeeCount + fiatFeeCount;
|
||||||
|
|
||||||
|
// 3. 构建 breakdown
|
||||||
|
const breakdown: Array<{ feeType: string; amount: number; count: number }> = [];
|
||||||
|
if (withdrawalFeeCount > 0) {
|
||||||
|
breakdown.push({
|
||||||
|
feeType: 'WITHDRAWAL_FEE',
|
||||||
|
amount: withdrawalFeeAmount,
|
||||||
|
count: withdrawalFeeCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (fiatFeeCount > 0) {
|
||||||
|
breakdown.push({
|
||||||
|
feeType: 'FIAT_WITHDRAWAL_FEE',
|
||||||
|
amount: fiatFeeAmount,
|
||||||
|
count: fiatFeeCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 按月统计(合并两种提现订单)
|
||||||
|
const byMonthRaw = await this.prisma.$queryRaw<Array<{
|
||||||
|
month: string;
|
||||||
|
amount: any;
|
||||||
|
count: any;
|
||||||
|
}>>`
|
||||||
|
SELECT month, SUM(amount) as amount, SUM(count) as count
|
||||||
|
FROM (
|
||||||
|
SELECT TO_CHAR(confirmed_at, 'YYYY-MM') as month, SUM(fee) as amount, COUNT(*) as count
|
||||||
|
FROM withdrawal_orders
|
||||||
|
WHERE status = 'CONFIRMED' AND confirmed_at IS NOT NULL
|
||||||
|
GROUP BY TO_CHAR(confirmed_at, 'YYYY-MM')
|
||||||
|
UNION ALL
|
||||||
|
SELECT TO_CHAR(completed_at, 'YYYY-MM') as month, SUM(fee) as amount, COUNT(*) as count
|
||||||
|
FROM fiat_withdrawal_orders
|
||||||
|
WHERE status = 'COMPLETED' AND completed_at IS NOT NULL
|
||||||
|
GROUP BY TO_CHAR(completed_at, 'YYYY-MM')
|
||||||
|
) combined
|
||||||
|
GROUP BY month
|
||||||
|
ORDER BY month DESC
|
||||||
|
LIMIT 12
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.logger.log(`[getFeeCollectionSummaryFromOrders] Fallback 统计完成: totalAmount=${totalAmount}, totalCount=${totalCount}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accountSequence,
|
||||||
|
accountBalance,
|
||||||
|
totalAmount,
|
||||||
|
totalCount,
|
||||||
|
breakdown,
|
||||||
|
byMonth: byMonthRaw.map(row => ({
|
||||||
|
month: row.month,
|
||||||
|
amount: Number(row.amount) || 0,
|
||||||
|
count: Number(row.count) || 0,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取手续费归集账户的详细流水列表
|
* 获取手续费归集账户的详细流水列表
|
||||||
*/
|
*/
|
||||||
|
|
@ -3517,6 +3615,13 @@ export class WalletApplicationService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [2026-01-07] 兼容:如果 FEE_COLLECTION 流水为空,从提现订单表查询历史手续费
|
||||||
|
// 回滚方式:删除此 fallback 代码块
|
||||||
|
if (total === 0) {
|
||||||
|
this.logger.log('[getFeeCollectionEntries] FEE_COLLECTION 流水为空,使用提现订单表作为 fallback');
|
||||||
|
return this.getFeeCollectionEntriesFromOrders(params);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entries: filteredEntries.map(entry => {
|
entries: filteredEntries.map(entry => {
|
||||||
const payload = entry.payloadJson as any;
|
const payload = entry.payloadJson as any;
|
||||||
|
|
@ -3535,4 +3640,135 @@ export class WalletApplicationService {
|
||||||
totalPages: Math.ceil(total / pageSize),
|
totalPages: Math.ceil(total / pageSize),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [2026-01-07] 新增:从提现订单表查询历史手续费明细(兼容旧数据)
|
||||||
|
// 回滚方式:删除此方法
|
||||||
|
private async getFeeCollectionEntriesFromOrders(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 dateFilter: any = {};
|
||||||
|
if (params.startDate) {
|
||||||
|
dateFilter.gte = params.startDate;
|
||||||
|
}
|
||||||
|
if (params.endDate) {
|
||||||
|
dateFilter.lte = params.endDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据 feeType 决定查询哪些表
|
||||||
|
const shouldQueryWithdrawal = !params.feeType || params.feeType === 'WITHDRAWAL_FEE';
|
||||||
|
const shouldQueryFiat = !params.feeType || params.feeType === 'FIAT_WITHDRAWAL_FEE';
|
||||||
|
|
||||||
|
// 从两种订单表查询数据
|
||||||
|
const withdrawalEntries: Array<{
|
||||||
|
id: string;
|
||||||
|
feeType: string;
|
||||||
|
amount: number;
|
||||||
|
refOrderId: string | null;
|
||||||
|
memo: string | null;
|
||||||
|
createdAt: Date;
|
||||||
|
}> = [];
|
||||||
|
const fiatEntries: Array<{
|
||||||
|
id: string;
|
||||||
|
feeType: string;
|
||||||
|
amount: number;
|
||||||
|
refOrderId: string | null;
|
||||||
|
memo: string | null;
|
||||||
|
createdAt: Date;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
if (shouldQueryWithdrawal) {
|
||||||
|
const withdrawalWhere: any = { status: 'CONFIRMED' };
|
||||||
|
if (Object.keys(dateFilter).length > 0) {
|
||||||
|
withdrawalWhere.confirmedAt = dateFilter;
|
||||||
|
}
|
||||||
|
const withdrawals = await this.prisma.withdrawalOrder.findMany({
|
||||||
|
where: withdrawalWhere,
|
||||||
|
orderBy: { confirmedAt: 'desc' },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
orderNo: true,
|
||||||
|
fee: true,
|
||||||
|
toAddress: true,
|
||||||
|
chainType: true,
|
||||||
|
confirmedAt: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
withdrawalEntries.push(...withdrawals.map(w => ({
|
||||||
|
id: `W-${w.id.toString()}`,
|
||||||
|
feeType: 'WITHDRAWAL_FEE',
|
||||||
|
amount: Number(w.fee),
|
||||||
|
refOrderId: w.orderNo,
|
||||||
|
memo: `区块链提现手续费 (${w.chainType})`,
|
||||||
|
createdAt: w.confirmedAt!,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldQueryFiat) {
|
||||||
|
const fiatWhere: any = { status: 'COMPLETED' };
|
||||||
|
if (Object.keys(dateFilter).length > 0) {
|
||||||
|
fiatWhere.completedAt = dateFilter;
|
||||||
|
}
|
||||||
|
const fiatOrders = await this.prisma.fiatWithdrawalOrder.findMany({
|
||||||
|
where: fiatWhere,
|
||||||
|
orderBy: { completedAt: 'desc' },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
orderNo: true,
|
||||||
|
fee: true,
|
||||||
|
paymentMethod: true,
|
||||||
|
completedAt: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
fiatEntries.push(...fiatOrders.map(f => ({
|
||||||
|
id: `F-${f.id.toString()}`,
|
||||||
|
feeType: 'FIAT_WITHDRAWAL_FEE',
|
||||||
|
amount: Number(f.fee),
|
||||||
|
refOrderId: f.orderNo,
|
||||||
|
memo: `法币提现手续费 (${f.paymentMethod})`,
|
||||||
|
createdAt: f.completedAt!,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并并按时间排序
|
||||||
|
const allEntries = [...withdrawalEntries, ...fiatEntries]
|
||||||
|
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
||||||
|
|
||||||
|
const total = allEntries.length;
|
||||||
|
const paginatedEntries = allEntries.slice(skip, skip + pageSize);
|
||||||
|
|
||||||
|
this.logger.log(`[getFeeCollectionEntriesFromOrders] Fallback 查询完成: total=${total}, page=${page}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
entries: paginatedEntries.map(entry => ({
|
||||||
|
...entry,
|
||||||
|
createdAt: entry.createdAt.toISOString(),
|
||||||
|
})),
|
||||||
|
total,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
totalPages: Math.ceil(total / pageSize),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue