diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 0330df20..096917b4 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -601,7 +601,33 @@ "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(wallet-service\\): 实现 Unit of Work 模式保证 settleToBalance 事务原子性\n\n- 新增 UnitOfWork 接口和实现,使用 Prisma Interactive Transaction\n- 修改 IWalletAccountRepository 和 ILedgerEntryRepository 接口支持可选事务参数\n- 修改仓库实现,支持在事务中执行数据库操作\n- 修改 settleToBalance 方法使用 UnitOfWork,确保钱包更新和流水记录原子性\n- 注册 UnitOfWorkService 到 InfrastructureModule\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")", "Bash(ls -la \"c:\\\\Users\\\\dong\\\\Desktop\\\\rwadurian\\\\backend\\\\services\\\\wallet-service\\\\prisma\\\\migrations\"\" 2>/dev/null || dir \"c:UsersdongDesktoprwadurianbackendserviceswallet-serviceprismamigrations \")", "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(admin-web\\): 添加系统账户收益类型汇总统计功能\n\n在数据统计-系统账户中新增5个统计Tab:\n- 手续费账户汇总:统计成本费、运营费、总部社区基础费、RWAD底池注入\n- 省团队收益汇总:统计省团队权益收益\n- 市团队收益汇总:统计市团队权益收益\n- 分享引荐收益汇总:统计分享权益收益\n- 社区收益汇总:统计社区权益收益\n\n后端变更:\n- reward-service: 添加 getRewardsSummaryByType、getAllRewardTypeSummaries 方法\n- reporting-service: 聚合收益类型汇总统计接口\n\n前端变更:\n- 添加 RewardTypeSummary、FeeAccountSummary 类型定义\n- 添加 getRewardTypeSummaries API 方法\n- 添加 FeeAccountSection、RewardTypeSummarySection 组件\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")", - "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(wallet-service\\): 实现手续费归集账户功能\n\n- 新增系统账户 S0000000006 \\(user_id=-6\\) 用于归集提现手续费\n- 新增 FEE_COLLECTION 流水类型记录手续费归集\n- 区块链提现完成时使用 UnitOfWork 事务归集手续费\n- 法币提现完成时在事务中归集手续费\n- WithdrawalOrderRepository 添加事务支持\n- 所有手续费归集操作使用乐观锁保护\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")" + "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(wallet-service\\): 实现手续费归集账户功能\n\n- 新增系统账户 S0000000006 \\(user_id=-6\\) 用于归集提现手续费\n- 新增 FEE_COLLECTION 流水类型记录手续费归集\n- 区块链提现完成时使用 UnitOfWork 事务归集手续费\n- 法币提现完成时在事务中归集手续费\n- WithdrawalOrderRepository 添加事务支持\n- 所有手续费归集操作使用乐观锁保护\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")", + "Bash(backend/services/blockchain-service/src/application/application.module.ts )", + "Bash(backend/services/blockchain-service/src/application/event-handlers/system-withdrawal-requested.handler.ts )", + "Bash(backend/services/blockchain-service/src/infrastructure/kafka/withdrawal-event-consumer.service.ts )", + "Bash(backend/services/wallet-service/src/api/api.module.ts )", + "Bash(backend/services/wallet-service/src/api/controllers/index.ts )", + "Bash(backend/services/wallet-service/src/api/controllers/system-withdrawal.controller.ts )", + "Bash(backend/services/wallet-service/src/application/services/index.ts )", + "Bash(backend/services/wallet-service/src/application/services/system-withdrawal-application.service.ts )", + "Bash(backend/services/wallet-service/src/application/event-handlers/system-withdrawal-status.handler.ts )", + "Bash(backend/services/wallet-service/src/infrastructure/external/identity/identity-client.service.ts )", + "Bash(backend/services/wallet-service/src/infrastructure/kafka/withdrawal-event-consumer.service.ts)", + "Bash(backend/services/planting-service/src/api/controllers/planting-stats.controller.ts )", + "Bash(backend/services/planting-service/src/api/dto/response/planting-stats.response.ts )", + "Bash(backend/services/planting-service/src/domain/repositories/planting-order.repository.interface.ts )", + "Bash(backend/services/planting-service/src/infrastructure/persistence/repositories/planting-order.repository.impl.ts )", + "Bash(frontend/admin-web/src/app/\\\\\\(dashboard\\\\\\)/statistics/page.tsx )", + "Bash(frontend/admin-web/src/app/\\\\\\(dashboard\\\\\\)/statistics/statistics.module.scss )", + "Bash(frontend/admin-web/src/services/dashboardService.ts )", + "Bash(frontend/admin-web/src/types/dashboard.types.ts)", + "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(wallet/blockchain/identity\\): implement system account withdrawal feature\n\n- Add SystemWithdrawalApplicationService to handle system account transfers\n- Add SystemWithdrawalController with endpoints for request, query, and account listing\n- Add SystemWithdrawalStatusHandler to process blockchain confirmation/failure events\n- Add SystemWithdrawalRequestedHandler in blockchain-service to execute ERC20 transfers\n- Add getUserByAccountSequence endpoint in identity-service for user lookup\n- Support dynamic memo generation based on actual source account name\n- Dual-sided ledger entries for system account transfers\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")", + "Bash(frontend/admin-web/src/hooks/index.ts )", + "Bash(frontend/admin-web/src/hooks/useSystemWithdrawal.ts )", + "Bash(frontend/admin-web/src/services/systemWithdrawalService.ts )", + "Bash(frontend/admin-web/src/types/system-withdrawal.types.ts )", + "Bash(\"frontend/admin-web/src/app/\\(dashboard\\)/system-transfer/\")", + "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat\\(admin-web\\): add system account transfer management page\n\n- Add system-transfer page with transfer form and order history\n- Add SystemWithdrawalService for API calls\n- Add useSystemWithdrawal hooks for React Query integration\n- Add system-withdrawal types definitions\n- Add navigation menu item for system transfer\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")" ], "deny": [], "ask": [] diff --git a/backend/services/wallet-service/scripts/fix-missing-transfer-in.ts b/backend/services/wallet-service/scripts/fix-missing-transfer-in.ts index 2a47de05..f9ca289d 100644 --- a/backend/services/wallet-service/scripts/fix-missing-transfer-in.ts +++ b/backend/services/wallet-service/scripts/fix-missing-transfer-in.ts @@ -152,7 +152,7 @@ async function main() { log('DEBUG', '转出方流水:', { id: transferOutEntry.id, amount: transferOutEntry.amount.toString(), - balanceAfter: transferOutEntry.balanceAfter.toString(), + balanceAfter: transferOutEntry.balanceAfter?.toString() ?? 'null', createdAt: transferOutEntry.createdAt.toISOString(), }); } @@ -172,7 +172,7 @@ async function main() { log('ERROR', '流水详情:', { id: existingTransferIn.id, amount: existingTransferIn.amount.toString(), - balanceAfter: existingTransferIn.balanceAfter.toString(), + balanceAfter: existingTransferIn.balanceAfter?.toString() ?? 'null', createdAt: existingTransferIn.createdAt.toISOString(), }); log('ERROR', ''); diff --git a/frontend/admin-web/src/app/(dashboard)/dashboard/page.tsx b/frontend/admin-web/src/app/(dashboard)/dashboard/page.tsx index ca4dd416..acdc150f 100644 --- a/frontend/admin-web/src/app/(dashboard)/dashboard/page.tsx +++ b/frontend/admin-web/src/app/(dashboard)/dashboard/page.tsx @@ -1,5 +1,10 @@ 'use client'; +/** + * 仪表板页面 + * [2026-01-06] 更新:统计卡片和趋势图改用 planting-service 数据(源数据) + */ + import { useState } from 'react'; import { Button } from '@/components/common'; import { PageContainer } from '@/components/layout'; @@ -8,11 +13,10 @@ import { TrendChart } from '@/components/features/dashboard/TrendChart'; import { RegionDistribution } from '@/components/features/dashboard/RegionDistribution'; import { RecentActivity } from '@/components/features/dashboard/RecentActivity'; import { - useDashboardStats, - useDashboardTrend, useDashboardRegion, useDashboardActivities, usePlantingStats, + usePlantingTrendForDashboard, } from '@/hooks'; import type { DashboardPeriod } from '@/types'; import styles from './dashboard.module.scss'; @@ -45,23 +49,34 @@ const EmptyData = ({ message }: { message: string }) => ( ); +/** 将 DashboardPeriod 转换为天数 */ +function periodToDays(period: DashboardPeriod): 7 | 30 | 90 { + switch (period) { + case '7d': return 7; + case '30d': return 30; + case '90d': return 90; + default: return 7; + } +} + export default function DashboardPage() { const [trendPeriod, setTrendPeriod] = useState('7d'); - // 使用 React Query hooks 获取数据 + // [2026-01-06] 直接使用 planting-service 数据(源数据) const { - data: statsData, + data: plantingStatsData, isLoading: statsLoading, error: statsError, refetch: refetchStats, - } = useDashboardStats(); + } = usePlantingStats(); + // [2026-01-06] 趋势数据也使用 planting-service const { data: trendData, isLoading: trendLoading, error: trendError, refetch: refetchTrend, - } = useDashboardTrend(trendPeriod); + } = usePlantingTrendForDashboard(periodToDays(trendPeriod)); const { data: regionData, @@ -77,28 +92,37 @@ export default function DashboardPage() { refetch: refetchActivities, } = useDashboardActivities(5); - // 从 planting-service 获取可靠的认种统计数据 - const { - data: plantingStatsData, - isLoading: plantingStatsLoading, - error: plantingStatsError, - refetch: refetchPlantingStats, - } = usePlantingStats(); - - // 合并统计数据:总认种量使用 planting-service 的数据 - const mergedStatsData = statsData?.map((stat) => { - if (stat.title === '总认种量' && plantingStatsData) { - return { - ...stat, - value: plantingStatsData.totalTreeCount, - }; - } - return stat; - }); - - // 判断是否正在加载统计数据 - const isStatsLoading = statsLoading || plantingStatsLoading; - const hasStatsError = statsError || plantingStatsError; + // [2026-01-06] 基于 planting-service 数据构建统计卡片数据 + const statsCards = plantingStatsData ? [ + { + title: '总认种量', + value: plantingStatsData.totalTreeCount, + suffix: '棵', + change: { value: plantingStatsData.todayStats?.treeCount ?? 0, trend: 'up' as const }, + color: '#10b981', + }, + { + title: '总订单数', + value: plantingStatsData.totalOrderCount, + suffix: '单', + change: { value: plantingStatsData.todayStats?.orderCount ?? 0, trend: 'up' as const }, + color: '#3b82f6', + }, + { + title: '今日认种', + value: plantingStatsData.todayStats?.treeCount ?? 0, + suffix: '棵', + change: { value: 0, trend: 'up' as const }, + color: '#f59e0b', + }, + { + title: '本月认种', + value: plantingStatsData.monthStats?.treeCount ?? 0, + suffix: '棵', + change: { value: 0, trend: 'up' as const }, + color: '#8b5cf6', + }, + ] : []; const headerActions = ( <> @@ -121,29 +145,26 @@ export default function DashboardPage() { actions={headerActions} >
- {/* 统计卡片区 */} + {/* 统计卡片区 - [2026-01-06] 使用 planting-service 数据 */}
- {isStatsLoading ? ( + {statsLoading ? ( // 加载状态显示骨架屏 <> {[1, 2, 3, 4].map((i) => ( ))} - ) : hasStatsError ? ( + ) : statsError ? ( // 错误状态
{ - refetchStats(); - refetchPlantingStats(); - }} + onRetry={() => refetchStats()} />
- ) : mergedStatsData && mergedStatsData.length > 0 ? ( - // 正常显示数据(总认种量使用 planting-service 的可靠数据) - mergedStatsData.map((stat, index) => ( + ) : statsCards.length > 0 ? ( + // 正常显示数据(直接使用 planting-service 源数据) + statsCards.map((stat, index) => ( )) ) : ( @@ -152,7 +173,7 @@ export default function DashboardPage() { )}
- {/* 图表区 */} + {/* 图表区 - [2026-01-06] 使用 planting-service 数据 */}
{trendError ? ( @@ -163,7 +184,7 @@ export default function DashboardPage() { ) : ( [...dashboardKeys.all, 'region'] as const, activities: (limit: number) => [...dashboardKeys.all, 'activities', limit] as const, plantingStats: () => [...dashboardKeys.all, 'plantingStats'] as const, + // [2026-01-06] 新增:planting-service 趋势数据(用于仪表板) + plantingTrend: (days: number) => [...dashboardKeys.all, 'plantingTrend', days] as const, }; /** @@ -114,3 +116,31 @@ export function usePlantingStats() { gcTime: 5 * 60 * 1000, }); } + +/** + * 获取认种趋势数据(用于仪表板趋势图) + * [2026-01-06] 新增:直接使用 planting-service 数据 + * @param days 天数:7, 30, 90 对应仪表板的三个选项 + */ +export function usePlantingTrendForDashboard(days: 7 | 30 | 90 = 7) { + return useQuery({ + queryKey: dashboardKeys.plantingTrend(days), + queryFn: async () => { + // 使用 day 维度获取趋势数据 + const response = await dashboardService.getPlantingTrendData('day'); + if (!response?.data) return []; + + // 根据 days 参数截取最近的数据 + const allData = response.data; + const slicedData = allData.slice(-days); + + // 转换为 TrendChart 组件需要的格式 + return slicedData.map(item => ({ + date: item.label.slice(5), // 2026-01-06 -> 01-06 + value: item.treeCount, + })); + }, + staleTime: 60 * 1000, + gcTime: 5 * 60 * 1000, + }); +}