diff --git a/src/app/(main)/profile/stats/Client.tsx b/src/app/(main)/profile/stats/Client.tsx index 850faf15eee0..925a8268cfd6 100644 --- a/src/app/(main)/profile/stats/Client.tsx +++ b/src/app/(main)/profile/stats/Client.tsx @@ -14,7 +14,7 @@ import TotalAssistants from './features/TotalAssistants'; import TotalMessages from './features/TotalMessages'; import TotalTopics from './features/TotalTopics'; -const Client = memo<{ mobile?: boolean }>(() => { +const Client = memo<{ mobile?: boolean }>(({ mobile }) => { const { t } = useTranslation('auth'); return ( @@ -26,7 +26,7 @@ const Client = memo<{ mobile?: boolean }>(() => { - + { - const { t } = useTranslation('auth'); + const { t } = useTranslation(['auth', 'chat']); + const theme = useTheme(); + const router = useRouter(); + const { data, isLoading } = useClientDataSWR('rank-sessions', async () => + sessionService.rankSessions(), + ); + return ( { + const link = qs.stringifyUrl({ + query: { + session: item.id, + }, + url: '/chat', + }); + return { + icon: ( + + ), + link, + name: ( + + {item.title || t('defaultAgent', { ns: 'chat' })} + + ), + value: item.count, + }; + }) || [] + } + height={420} leftLabel={t('stats.assistantsRank.left')} + loading={isLoading} + onValueChange={(item) => router.push(item.link)} rightLabel={t('stats.assistantsRank.right')} /> ); diff --git a/src/app/(main)/profile/stats/features/TopicsRank.tsx b/src/app/(main)/profile/stats/features/TopicsRank.tsx index f0edfea094e9..ad9300151e2c 100644 --- a/src/app/(main)/profile/stats/features/TopicsRank.tsx +++ b/src/app/(main)/profile/stats/features/TopicsRank.tsx @@ -1,13 +1,57 @@ import { BarList } from '@lobehub/charts'; +import { Icon } from '@lobehub/ui'; +import { useTheme } from 'antd-style'; +import { MessageSquareIcon } from 'lucide-react'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import qs from 'query-string'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; +import { useClientDataSWR } from '@/libs/swr'; +import { topicService } from '@/services/topic'; + export const TopicsRank = memo(() => { const { t } = useTranslation('auth'); + const theme = useTheme(); + const router = useRouter(); + const { data, isLoading } = useClientDataSWR('rank-topics', async () => + topicService.rankTopics(), + ); + return ( { + const link = qs.stringifyUrl({ + query: { + session: item.sessionId, + topic: item.id, + }, + url: '/chat', + }); + return { + icon: ( + + ), + link, + name: ( + + {item.title} + + ), + value: item.count, + }; + }) || [] + } + height={420} leftLabel={t('stats.topicsRank.left')} + loading={isLoading} + onValueChange={(item) => router.push(item.link)} rightLabel={t('stats.topicsRank.right')} /> ); diff --git a/src/app/(main)/profile/stats/page.tsx b/src/app/(main)/profile/stats/page.tsx index 2789ff7f10e8..9f143ce4739c 100644 --- a/src/app/(main)/profile/stats/page.tsx +++ b/src/app/(main)/profile/stats/page.tsx @@ -1,5 +1,6 @@ import { metadataModule } from '@/server/metadata'; import { translation } from '@/server/translation'; +import { isMobileDevice } from '@/utils/server/responsive'; import Client from './Client'; @@ -13,7 +14,8 @@ export const generateMetadata = async () => { }; const Page = async () => { - return ; + const mobile = await isMobileDevice(); + return ; }; export default Page; diff --git a/src/database/server/models/session.ts b/src/database/server/models/session.ts index 47d803d87dd3..1aa90bca5f97 100644 --- a/src/database/server/models/session.ts +++ b/src/database/server/models/session.ts @@ -25,6 +25,7 @@ import { agentsToSessions, sessionGroups, sessions, + topics, } from '../../schemas'; export class SessionModel { @@ -118,7 +119,31 @@ export class SessionModel { return result[0].count; }; - rank = async () => {}; + rank = async (): Promise< + { + avatar: string | null; + backgroundColor: string | null; + count: number; + id: string; + title: string | null; + }[] + > => { + return this.db + .select({ + avatar: agents.avatar, + backgroundColor: agents.backgroundColor, + count: count(topics.id).as('count'), + id: sessions.id, + title: agents.title, + }) + .from(sessions) + .leftJoin(topics, eq(sessions.id, topics.sessionId)) + .leftJoin(agentsToSessions, eq(sessions.id, agentsToSessions.sessionId)) + .leftJoin(agents, eq(agentsToSessions.agentId, agents.id)) + .groupBy(sessions.id, agentsToSessions.agentId, agents.id) + .orderBy(desc(sql`count`)) + .limit(10); + }; hasMoreThanN = async (n: number): Promise => { const result = await this.db diff --git a/src/database/server/models/topic.ts b/src/database/server/models/topic.ts index c29649472eb5..7fb82aa6a6f9 100644 --- a/src/database/server/models/topic.ts +++ b/src/database/server/models/topic.ts @@ -126,7 +126,27 @@ export class TopicModel { return result[0].count; }; - rank = async () => {}; + rank = async (): Promise< + { + count: number; + id: string; + sessionId: string | null; + title: string | null; + }[] + > => { + return this.db + .select({ + count: count(messages.id).as('count'), + id: topics.id, + sessionId: topics.sessionId, + title: topics.title, + }) + .from(topics) + .leftJoin(messages, eq(topics.id, messages.topicId)) + .groupBy(topics.id) + .orderBy(desc(sql`count`)) + .limit(10); + }; // **************** Create *************** // diff --git a/src/locales/default/auth.ts b/src/locales/default/auth.ts index a4c96cafb58d..bbe7df4b0509 100644 --- a/src/locales/default/auth.ts +++ b/src/locales/default/auth.ts @@ -20,7 +20,7 @@ export default { assistants: '助手数', assistantsRank: { left: '助手名称', - right: '消息数', + right: '话题数', title: '助手使用率', }, heatmaps: { diff --git a/src/services/session/_deprecated.ts b/src/services/session/_deprecated.ts index 8d696676366e..8970a7c91667 100644 --- a/src/services/session/_deprecated.ts +++ b/src/services/session/_deprecated.ts @@ -82,6 +82,11 @@ export class ClientService implements ISessionService { return SessionModel.count(); } + // @ts-ignore + async rankSessions() { + throw new Error('Method not implemented.'); + } + async hasSessions() { return (await this.countSessions()) !== 0; } diff --git a/src/services/session/server.ts b/src/services/session/server.ts index 1fcec7a5d906..224c68adb9d2 100644 --- a/src/services/session/server.ts +++ b/src/services/session/server.ts @@ -25,10 +25,7 @@ export class ServerService implements ISessionService { return (await this.countSessions()) === 0; }; - createSession = async ( - type: LobeSessionType, - data: Partial, - ): Promise => { + createSession = async (type: LobeSessionType, data: Partial) => { const { config, group, meta, ...session } = data; return lambdaClient.session.createSession.mutate({ diff --git a/src/services/session/type.ts b/src/services/session/type.ts index 59528fe138ca..c58f582826c9 100644 --- a/src/services/session/type.ts +++ b/src/services/session/type.ts @@ -30,7 +30,20 @@ export interface ISessionService { * @deprecated */ getSessionsByType(type: 'agent' | 'group' | 'all'): Promise; - countSessions(): Promise; + countSessions(params?: { + endDate?: string; + range?: [string, string]; + startDate?: string; + }): Promise; + rankSessions(): Promise< + { + avatar: string | null; + count: number; + id: string; + title: string | null; + backgroundColor: string | null; + }[] + >; searchSessions(keyword: string): Promise; updateSession( diff --git a/src/services/topic/_deprecated.ts b/src/services/topic/_deprecated.ts index eeb2ffa2e395..bec802f2fb54 100644 --- a/src/services/topic/_deprecated.ts +++ b/src/services/topic/_deprecated.ts @@ -38,6 +38,11 @@ export class ClientService implements ITopicService { return TopicModel.count(); } + // @ts-ignore + async rankTopics() { + throw new Error('Method not implemented.'); + } + async updateTopicFavorite(id: string, favorite?: boolean) { return this.updateTopic(id, { favorite }); } diff --git a/src/services/topic/client.ts b/src/services/topic/client.ts index 27ddd38cbd5c..6bfdd67691d2 100644 --- a/src/services/topic/client.ts +++ b/src/services/topic/client.ts @@ -59,7 +59,14 @@ export class ClientService extends BaseClientService implements ITopicService { return this.topicModel.count(params); } - async rankTopics() { + async rankTopics(): Promise< + { + count: number; + id: string; + sessionId: string | null; + title: string | null; + }[] + > { return this.topicModel.rank(); } diff --git a/src/services/topic/server.ts b/src/services/topic/server.ts index 58319bb2c2a4..b9d0bfc069eb 100644 --- a/src/services/topic/server.ts +++ b/src/services/topic/server.ts @@ -39,7 +39,14 @@ export class ServerService implements ITopicService { return lambdaClient.topic.countTopics.query(params); } - rankTopics() { + rankTopics(): Promise< + { + count: number; + id: string; + sessionId: string | null; + title: string | null; + }[] + > { return lambdaClient.topic.rankTopics.query(); } diff --git a/src/services/topic/type.ts b/src/services/topic/type.ts index a03bc2367316..6bd7ab1b1cb4 100644 --- a/src/services/topic/type.ts +++ b/src/services/topic/type.ts @@ -23,6 +23,14 @@ export interface ITopicService { getTopics(params: QueryTopicParams): Promise; getAllTopics(): Promise; countTopics(): Promise; + rankTopics(): Promise< + { + id: string; + title: string | null; + count: number; + sessionId: string | null; + }[] + >; searchTopics(keyword: string, sessionId?: string): Promise; updateTopic(id: string, data: Partial): Promise;