feat(admin-web): integrate planting-service stats API for dashboard

Use planting-service's reliable database aggregation for total planting count
instead of reporting-service's Kafka event-driven statistics.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-04 07:04:39 -08:00
parent 251fee4f1e
commit 21c8f1906a
5 changed files with 71 additions and 6 deletions

View File

@ -12,6 +12,7 @@ import {
useDashboardTrend, useDashboardTrend,
useDashboardRegion, useDashboardRegion,
useDashboardActivities, useDashboardActivities,
usePlantingStats,
} from '@/hooks'; } from '@/hooks';
import type { DashboardPeriod } from '@/types'; import type { DashboardPeriod } from '@/types';
import styles from './dashboard.module.scss'; import styles from './dashboard.module.scss';
@ -76,6 +77,29 @@ export default function DashboardPage() {
refetch: refetchActivities, refetch: refetchActivities,
} = useDashboardActivities(5); } = 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;
const headerActions = ( const headerActions = (
<> <>
<Button variant="outline" size="sm"> <Button variant="outline" size="sm">
@ -99,24 +123,27 @@ export default function DashboardPage() {
<div className={styles.dashboard}> <div className={styles.dashboard}>
{/* 统计卡片区 */} {/* 统计卡片区 */}
<div className={styles.dashboard__stats}> <div className={styles.dashboard__stats}>
{statsLoading ? ( {isStatsLoading ? (
// 加载状态显示骨架屏 // 加载状态显示骨架屏
<> <>
{[1, 2, 3, 4].map((i) => ( {[1, 2, 3, 4].map((i) => (
<StatCardSkeleton key={i} /> <StatCardSkeleton key={i} />
))} ))}
</> </>
) : statsError ? ( ) : hasStatsError ? (
// 错误状态 // 错误状态
<div className={styles.dashboard__statsError}> <div className={styles.dashboard__statsError}>
<ErrorMessage <ErrorMessage
message="加载统计数据失败" message="加载统计数据失败"
onRetry={() => refetchStats()} onRetry={() => {
refetchStats();
refetchPlantingStats();
}}
/> />
</div> </div>
) : statsData && statsData.length > 0 ? ( ) : mergedStatsData && mergedStatsData.length > 0 ? (
// 正常显示数据 // 正常显示数据(总认种量使用 planting-service 的可靠数据)
statsData.map((stat, index) => ( mergedStatsData.map((stat, index) => (
<StatCard key={index} {...stat} /> <StatCard key={index} {...stat} />
)) ))
) : ( ) : (

View File

@ -15,6 +15,7 @@ export const dashboardKeys = {
trend: (period: DashboardPeriod) => [...dashboardKeys.all, 'trend', period] as const, trend: (period: DashboardPeriod) => [...dashboardKeys.all, 'trend', period] as const,
region: () => [...dashboardKeys.all, 'region'] as const, region: () => [...dashboardKeys.all, 'region'] as const,
activities: (limit: number) => [...dashboardKeys.all, 'activities', limit] as const, activities: (limit: number) => [...dashboardKeys.all, 'activities', limit] as const,
plantingStats: () => [...dashboardKeys.all, 'plantingStats'] as const,
}; };
/** /**
@ -97,3 +98,19 @@ export function useDashboardActivities(limit = 5) {
refetchInterval: 60 * 1000, // 每分钟自动刷新 refetchInterval: 60 * 1000, // 每分钟自动刷新
}); });
} }
/**
* planting-service
*
*/
export function usePlantingStats() {
return useQuery({
queryKey: dashboardKeys.plantingStats(),
queryFn: async () => {
const response = await dashboardService.getPlantingStats();
return response?.data ?? null;
},
staleTime: 60 * 1000, // 1分钟后标记为过期
gcTime: 5 * 60 * 1000,
});
}

View File

@ -111,6 +111,11 @@ export const API_ENDPOINTS = {
REGION: '/v1/dashboard/region', REGION: '/v1/dashboard/region',
}, },
// 认种统计 (planting-service) - 从订单表实时聚合,数据可靠
PLANTING_STATS: {
GLOBAL: '/v1/planting/stats/global',
},
// 通知管理 (admin-service) // 通知管理 (admin-service)
NOTIFICATIONS: { NOTIFICATIONS: {
LIST: '/v1/admin/notifications', LIST: '/v1/admin/notifications',

View File

@ -13,6 +13,7 @@ import type {
RegionDistributionItem, RegionDistributionItem,
DashboardActivity, DashboardActivity,
DashboardStatItem, DashboardStatItem,
PlantingGlobalStats,
} from '@/types'; } from '@/types';
/** 仪表板概览响应 */ /** 仪表板概览响应 */
@ -80,6 +81,14 @@ export const dashboardService = {
params: { limit }, params: { limit },
}); });
}, },
/**
* planting-service
* reporting-service
*/
async getPlantingStats(): Promise<ApiResponse<PlantingGlobalStats>> {
return apiClient.get(API_ENDPOINTS.PLANTING_STATS.GLOBAL);
},
}; };
export default dashboardService; export default dashboardService;

View File

@ -87,3 +87,10 @@ export interface DashboardTrendParams {
export interface DashboardActivitiesParams { export interface DashboardActivitiesParams {
limit?: number; limit?: number;
} }
/** 认种全局统计数据(来自 planting-service */
export interface PlantingGlobalStats {
totalTreeCount: number;
totalOrderCount: number;
totalAmount: string;
}