From 03ec49bc7e42616f9ffe0d12cb5a9b0cffb68c84 Mon Sep 17 00:00:00 2001 From: digua Date: Sun, 30 Nov 2025 02:13:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E6=80=BB=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/main/ipcMain.ts | 15 ++++ electron/main/worker/dbWorker.ts | 2 + electron/main/worker/index.ts | 1 + electron/main/worker/queryBasic.ts | 52 ++++++++--- electron/main/worker/workerManager.ts | 4 + electron/preload/index.ts | 11 +++ src/components/analysis/OverviewTab.vue | 115 ++++++++++++++++-------- src/types/chat.ts | 8 ++ 8 files changed, 158 insertions(+), 50 deletions(-) diff --git a/electron/main/ipcMain.ts b/electron/main/ipcMain.ts index 5709900c..f7037bf9 100644 --- a/electron/main/ipcMain.ts +++ b/electron/main/ipcMain.ts @@ -344,6 +344,21 @@ const mainIpcMain = (win: BrowserWindow) => { } ) + /** + * 获取月份活跃度分布 + */ + ipcMain.handle( + 'chat:getMonthlyActivity', + async (_, sessionId: string, filter?: { startTs?: number; endTs?: number }) => { + try { + return await worker.getMonthlyActivity(sessionId, filter) + } catch (error) { + console.error('获取月份活跃度失败:', error) + return [] + } + } + ) + /** * 获取消息类型分布 */ diff --git a/electron/main/worker/dbWorker.ts b/electron/main/worker/dbWorker.ts index 07adf8f2..080e0f36 100644 --- a/electron/main/worker/dbWorker.ts +++ b/electron/main/worker/dbWorker.ts @@ -17,6 +17,7 @@ import { getHourlyActivity, getDailyActivity, getWeekdayActivity, + getMonthlyActivity, getMessageTypeDistribution, getTimeRange, getMemberNameHistory, @@ -53,6 +54,7 @@ const handlers: Record any> = { getHourlyActivity: (p) => getHourlyActivity(p.sessionId, p.filter), getDailyActivity: (p) => getDailyActivity(p.sessionId, p.filter), getWeekdayActivity: (p) => getWeekdayActivity(p.sessionId, p.filter), + getMonthlyActivity: (p) => getMonthlyActivity(p.sessionId, p.filter), getMessageTypeDistribution: (p) => getMessageTypeDistribution(p.sessionId, p.filter), getTimeRange: (p) => getTimeRange(p.sessionId), getMemberNameHistory: (p) => getMemberNameHistory(p.sessionId, p.memberId), diff --git a/electron/main/worker/index.ts b/electron/main/worker/index.ts index bdd0a6e6..2c286d24 100644 --- a/electron/main/worker/index.ts +++ b/electron/main/worker/index.ts @@ -13,6 +13,7 @@ export { getHourlyActivity, getDailyActivity, getWeekdayActivity, + getMonthlyActivity, getMessageTypeDistribution, getTimeRange, getMemberNameHistory, diff --git a/electron/main/worker/queryBasic.ts b/electron/main/worker/queryBasic.ts index 5212fa29..9433ca51 100644 --- a/electron/main/worker/queryBasic.ts +++ b/electron/main/worker/queryBasic.ts @@ -6,14 +6,7 @@ import Database from 'better-sqlite3' import * as fs from 'fs' import * as path from 'path' -import { - openDatabase, - getDbDir, - getDbPath, - buildTimeFilter, - buildSystemMessageFilter, - type TimeFilter, -} from './dbCore' +import { openDatabase, getDbDir, getDbPath, buildTimeFilter, buildSystemMessageFilter, type TimeFilter } from './dbCore' // ==================== 基础查询 ==================== @@ -198,6 +191,43 @@ export function getWeekdayActivity(sessionId: string, filter?: TimeFilter): any[ return result } +/** + * 获取月份活跃度分布 + */ +export function getMonthlyActivity(sessionId: string, filter?: TimeFilter): any[] { + const db = openDatabase(sessionId) + if (!db) return [] + + const { clause, params } = buildTimeFilter(filter) + const clauseWithSystem = buildSystemMessageFilter(clause) + + const rows = db + .prepare( + ` + SELECT + CAST(strftime('%m', msg.ts, 'unixepoch', 'localtime') AS INTEGER) as month, + COUNT(*) as messageCount + FROM message msg + JOIN member m ON msg.sender_id = m.id + ${clauseWithSystem} + GROUP BY month + ORDER BY month + ` + ) + .all(...params) as Array<{ month: number; messageCount: number }> + + const result: any[] = [] + for (let m = 1; m <= 12; m++) { + const found = rows.find((r) => r.month === m) + result.push({ + month: m, + messageCount: found ? found.messageCount : 0, + }) + } + + return result +} + /** * 获取消息类型分布 */ @@ -250,10 +280,7 @@ export function getTimeRange(sessionId: string): { start: number; end: number } /** * 获取成员的历史昵称记录 */ -export function getMemberNameHistory( - sessionId: string, - memberId: number -): any[] { +export function getMemberNameHistory(sessionId: string, memberId: number): any[] { const db = openDatabase(sessionId) if (!db) return [] @@ -386,4 +413,3 @@ export function getSession(sessionId: string): any | null { dbPath: getDbPath(sessionId), } } - diff --git a/electron/main/worker/workerManager.ts b/electron/main/worker/workerManager.ts index 5fd68af8..be49d9ef 100644 --- a/electron/main/worker/workerManager.ts +++ b/electron/main/worker/workerManager.ts @@ -191,6 +191,10 @@ export async function getWeekdayActivity(sessionId: string, filter?: any): Promi return sendToWorker('getWeekdayActivity', { sessionId, filter }) } +export async function getMonthlyActivity(sessionId: string, filter?: any): Promise { + return sendToWorker('getMonthlyActivity', { sessionId, filter }) +} + export async function getMessageTypeDistribution(sessionId: string, filter?: any): Promise { return sendToWorker('getMessageTypeDistribution', { sessionId, filter }) } diff --git a/electron/preload/index.ts b/electron/preload/index.ts index 300bf5bc..21cff8d8 100644 --- a/electron/preload/index.ts +++ b/electron/preload/index.ts @@ -7,6 +7,7 @@ import type { HourlyActivity, DailyActivity, WeekdayActivity, + MonthlyActivity, MessageType, ImportProgress, RepeatAnalysis, @@ -128,6 +129,16 @@ const chatApi = { return ipcRenderer.invoke('chat:getWeekdayActivity', sessionId, filter) }, + /** + * 获取月份活跃度分布 + */ + getMonthlyActivity: ( + sessionId: string, + filter?: { startTs?: number; endTs?: number } + ): Promise => { + return ipcRenderer.invoke('chat:getMonthlyActivity', sessionId, filter) + }, + /** * 获取消息类型分布 */ diff --git a/src/components/analysis/OverviewTab.vue b/src/components/analysis/OverviewTab.vue index 15a3daa6..3d2b19c7 100644 --- a/src/components/analysis/OverviewTab.vue +++ b/src/components/analysis/OverviewTab.vue @@ -1,6 +1,13 @@