diff --git a/backend/services/reporting-service/src/api/controllers/system-account-report.controller.ts b/backend/services/reporting-service/src/api/controllers/system-account-report.controller.ts index 958207ef..68d749a2 100644 --- a/backend/services/reporting-service/src/api/controllers/system-account-report.controller.ts +++ b/backend/services/reporting-service/src/api/controllers/system-account-report.controller.ts @@ -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, + }); + } } diff --git a/backend/services/reporting-service/src/application/services/system-account-report-application.service.ts b/backend/services/reporting-service/src/application/services/system-account-report-application.service.ts index cae1ea67..00a54812 100644 --- a/backend/services/reporting-service/src/application/services/system-account-report-application.service.ts +++ b/backend/services/reporting-service/src/application/services/system-account-report-application.service.ts @@ -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 { + 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 { + this.logger.log('[getFeeCollectionSummary] 获取手续费归集账户汇总统计'); + return this.walletServiceClient.getFeeCollectionSummary(); + } + + /** + * 获取手续费归集账户流水明细 + */ + async getFeeCollectionEntries(params?: { + page?: number; + pageSize?: number; + feeType?: string; + startDate?: string; + endDate?: string; + }): Promise { + this.logger.log('[getFeeCollectionEntries] 获取手续费归集流水明细'); + return this.walletServiceClient.getFeeCollectionEntries(params); + } + /** * 组装固定账户数据 */ diff --git a/backend/services/reporting-service/src/infrastructure/external/wallet-service/wallet-service.client.ts b/backend/services/reporting-service/src/infrastructure/external/wallet-service/wallet-service.client.ts index 5c8ba129..d6e84d5e 100644 --- a/backend/services/reporting-service/src/infrastructure/external/wallet-service/wallet-service.client.ts +++ b/backend/services/reporting-service/src/infrastructure/external/wallet-service/wallet-service.client.ts @@ -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 { + 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, + }; + } + } + /** * 获取系统账户余额 */ diff --git a/backend/services/wallet-service/src/api/controllers/internal-wallet.controller.ts b/backend/services/wallet-service/src/api/controllers/internal-wallet.controller.ts index 7e527d4e..cf99d8c4 100644 --- a/backend/services/wallet-service/src/api/controllers/internal-wallet.controller.ts +++ b/backend/services/wallet-service/src/api/controllers/internal-wallet.controller.ts @@ -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; + } } diff --git a/backend/services/wallet-service/src/application/services/wallet-application.service.ts b/backend/services/wallet-service/src/application/services/wallet-application.service.ts index d27c3aad..6851cb51 100644 --- a/backend/services/wallet-service/src/application/services/wallet-application.service.ts +++ b/backend/services/wallet-service/src/application/services/wallet-application.service.ts @@ -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>` + 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>` + 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), + }; + } } diff --git a/frontend/admin-web/src/components/features/system-account-report/SystemAccountsTab.module.scss b/frontend/admin-web/src/components/features/system-account-report/SystemAccountsTab.module.scss index 99f3417e..ceeea931 100644 --- a/frontend/admin-web/src/components/features/system-account-report/SystemAccountsTab.module.scss +++ b/frontend/admin-web/src/components/features/system-account-report/SystemAccountsTab.module.scss @@ -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; diff --git a/frontend/admin-web/src/components/features/system-account-report/SystemAccountsTab.tsx b/frontend/admin-web/src/components/features/system-account-report/SystemAccountsTab.tsx index 04849c97..f966600d 100644 --- a/frontend/admin-web/src/components/features/system-account-report/SystemAccountsTab.tsx +++ b/frontend/admin-web/src/components/features/system-account-report/SystemAccountsTab.tsx @@ -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' && } {activeTab === 'expired' && } {activeTab === 'ledger' && } - {/* [2026-01-06] 新增:收益类型汇总Tab内容 */} + {/* [2026-01-06] 更新:手续费账户汇总改用 wallet-service 的真正提现手续费数据 */} {activeTab === 'feeAccount' && ( )} @@ -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(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 (
@@ -460,7 +489,57 @@ function OfflineSettlementSection({ data }: { data: SystemAccountReportResponse[ )} - {(!data.byMonth || data.byMonth.length === 0) && ( + {/* 明细列表切换按钮 */} +
+ +
+ + {/* 明细列表 */} + {showEntries && ( +
+

明细列表

+ {entriesLoading ? ( +
加载中...
+ ) : entriesData && entriesData.entries.length > 0 ? ( +
+ + + + + + + + + + + {entriesData.entries.map((entry) => ( + + + + + + + ))} + +
账户金额 (绿积分)备注时间
{entry.accountSequence}{formatAmount(entry.amount)}{entry.memo || '-'}{new Date(entry.createdAt).toLocaleString('zh-CN')}
+ {entriesData.total > entriesData.entries.length && ( +
+ 显示 {entriesData.entries.length} / {entriesData.total} 条 +
+ )} +
+ ) : ( +
暂无明细数据
+ )} +
+ )} + + {(!data.byMonth || data.byMonth.length === 0) && !showEntries && (
暂无面对面结算数据
)}
@@ -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(null); const [showDetails, setShowDetails] = useState(false); const [detailsLoading, setDetailsLoading] = useState(false); - const [detailsData, setDetailsData] = useState(null); + const [detailsData, setDetailsData] = useState(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({
暂无手续费账户数据 -
@@ -843,15 +951,25 @@ function FeeAccountSection({

手续费账户汇总

-
+ {/* 账户信息 */} +
+ 手续费归集账户: + {data.accountSequence} +
+ {/* 汇总卡片 */}
- 总金额 + 当前余额 + {formatAmount(data.accountBalance)} 绿积分 +
+
+ 累计收取 {formatAmount(data.totalAmount)} 绿积分
@@ -860,23 +978,23 @@ function FeeAccountSection({
- {/* 按类型明细 */} + {/* 按手续费类型明细 */} {data.breakdown && data.breakdown.length > 0 ? ( <> -

按类型明细

+

按手续费类型明细

- + {data.breakdown.map((item) => ( - - + + @@ -889,6 +1007,33 @@ function FeeAccountSection({
暂无手续费数据
)} + {/* 按月统计 */} + {data.byMonth && data.byMonth.length > 0 && ( + <> +

月度统计

+
+
费用类型手续费类型 金额 (绿积分) 笔数
{FEE_TYPE_LABELS[item.rightType] || item.rightType}
{FEE_COLLECTION_TYPE_LABELS[item.feeType] || item.feeType} {formatAmount(item.amount)} {item.count}
+ + + + + + + + + {data.byMonth.map((item) => ( + + + + + + ))} + +
月份金额 (绿积分)笔数
{item.month}{formatAmount(item.amount)}{item.count}
+
+ + )} + {/* 详细明细列表 */}