diff --git a/backend/api/platform_router.py b/backend/api/platform_router.py
index 04289a6..ab522bc 100644
--- a/backend/api/platform_router.py
+++ b/backend/api/platform_router.py
@@ -10,6 +10,7 @@ from services.platform_service import (
build_platform_overview,
create_or_update_system_setting,
delete_system_setting,
+ get_bot_activity_stats,
get_platform_settings,
list_system_settings,
list_activity_events,
@@ -67,6 +68,11 @@ def get_platform_usage(
return list_usage(session, bot_id=bot_id, limit=limit, offset=offset)
+@router.get("/api/platform/activity-stats")
+def get_platform_activity_stats(session: Session = Depends(get_session)):
+ return {"items": get_bot_activity_stats(session)}
+
+
@router.get("/api/platform/events")
def get_platform_events(bot_id: Optional[str] = None, limit: int = 100, session: Session = Depends(get_session)):
return {"items": list_activity_events(session, bot_id=bot_id, limit=limit)}
diff --git a/backend/services/platform_service.py b/backend/services/platform_service.py
index 7752003..20e5faf 100644
--- a/backend/services/platform_service.py
+++ b/backend/services/platform_service.py
@@ -1,4 +1,5 @@
from services.platform_activity_service import (
+ get_bot_activity_stats,
list_activity_events,
prune_expired_activity_events,
record_activity_event,
diff --git a/frontend/src/modules/platform/PlatformAdminDashboardPage.tsx b/frontend/src/modules/platform/PlatformAdminDashboardPage.tsx
index 7dd1212..035a160 100644
--- a/frontend/src/modules/platform/PlatformAdminDashboardPage.tsx
+++ b/frontend/src/modules/platform/PlatformAdminDashboardPage.tsx
@@ -37,7 +37,7 @@ export function PlatformAdminDashboardPage({ compactMode }: PlatformAdminDashboa
diff --git a/frontend/src/modules/platform/hooks/usePlatformDashboard.ts b/frontend/src/modules/platform/hooks/usePlatformDashboard.ts
index c9e7169..4bf808b 100644
--- a/frontend/src/modules/platform/hooks/usePlatformDashboard.ts
+++ b/frontend/src/modules/platform/hooks/usePlatformDashboard.ts
@@ -12,6 +12,7 @@ import {
writeCachedPlatformPageSize,
} from '../../../utils/platformPageSize';
import type {
+ BotActivityStatsItem,
PlatformBotResourceSnapshot,
PlatformOverviewResponse,
PlatformSettings,
@@ -52,6 +53,8 @@ export function usePlatformDashboard({ compactMode }: UsePlatformDashboardOption
const [resourceError, setResourceError] = useState('');
const [usageData, setUsageData] = useState(null);
const [usageLoading, setUsageLoading] = useState(false);
+ const [activityStatsData, setActivityStatsData] = useState(null);
+ const [activityLoading, setActivityLoading] = useState(false);
const [usagePage, setUsagePage] = useState(1);
const [usagePageSize, setUsagePageSize] = useState(() => readCachedPlatformPageSize(10));
const [pageSizeReady, setPageSizeReady] = useState(true);
@@ -131,6 +134,18 @@ export function usePlatformDashboard({ compactMode }: UsePlatformDashboardOption
}
}, [isZh, notify, usagePageSize]);
+ const loadActivityStats = useCallback(async () => {
+ setActivityLoading(true);
+ try {
+ const res = await axios.get<{ items: BotActivityStatsItem[] }>(`${APP_ENDPOINTS.apiBase}/platform/activity-stats`);
+ setActivityStatsData(Array.isArray(res.data?.items) ? res.data.items : []);
+ } catch (error: any) {
+ notify(error?.response?.data?.detail || (isZh ? '读取 Bot 活跃度统计失败。' : 'Failed to load bot activity analytics.'), { tone: 'error' });
+ } finally {
+ setActivityLoading(false);
+ }
+ }, [isZh, notify]);
+
const loadSelectedBotUsageSummary = useCallback(async (botId: string) => {
if (!botId) {
setSelectedBotUsageSummary(null);
@@ -174,6 +189,10 @@ export function usePlatformDashboard({ compactMode }: UsePlatformDashboardOption
void loadUsage(1);
}, [loadUsage, pageSizeReady, usagePageSize]);
+ useEffect(() => {
+ void loadActivityStats();
+ }, [loadActivityStats]);
+
useEffect(() => {
setBotListPage(1);
}, [search, botListPageSize]);
@@ -254,7 +273,7 @@ export function usePlatformDashboard({ compactMode }: UsePlatformDashboardOption
const overviewBots = overview?.summary.bots;
const overviewImages = overview?.summary.images;
const overviewResources = overview?.summary.resources;
- const activityStats = overview?.activity_stats;
+ const activityStats = activityStatsData || overview?.activity_stats;
const usageSummary = usageData?.summary || overview?.usage.summary;
const usageAnalytics = usageData?.analytics || overview?.usage.analytics || null;
@@ -283,10 +302,10 @@ export function usePlatformDashboard({ compactMode }: UsePlatformDashboardOption
const usageAnalyticsTicks = useMemo(() => buildPlatformUsageAnalyticsTicks(usageAnalyticsMax), [usageAnalyticsMax]);
const refreshAll = useCallback(async () => {
- const jobs: Promise[] = [loadOverview(), loadBots(), loadUsage(usagePage)];
+ const jobs: Promise[] = [loadOverview(), loadBots(), loadUsage(usagePage), loadActivityStats()];
if (selectedBotId) jobs.push(loadSelectedBotUsageSummary(selectedBotId));
await Promise.allSettled(jobs);
- }, [loadBots, loadOverview, loadSelectedBotUsageSummary, loadUsage, selectedBotId, usagePage]);
+ }, [loadActivityStats, loadBots, loadOverview, loadSelectedBotUsageSummary, loadUsage, selectedBotId, usagePage]);
const toggleBot = useCallback(async (bot: BotState) => {
setOperatingBotId(bot.id);
@@ -496,6 +515,7 @@ export function usePlatformDashboard({ compactMode }: UsePlatformDashboardOption
toggleBot,
usageAnalytics,
activityStats,
+ activityLoading,
usageAnalyticsMax,
usageAnalyticsSeries,
usageAnalyticsTicks,