mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-04-26 14:27:22 +08:00
495 lines
13 KiB
TypeScript
495 lines
13 KiB
TypeScript
/**
|
||
* 聊天记录 API - 导入、分析、管理聊天记录
|
||
*/
|
||
import { ipcRenderer } from 'electron'
|
||
import type { AnalysisSession, MessageType, ImportProgress } from '../../../src/types/base'
|
||
import type {
|
||
MemberActivity,
|
||
MemberNameHistory,
|
||
HourlyActivity,
|
||
DailyActivity,
|
||
WeekdayActivity,
|
||
MonthlyActivity,
|
||
RepeatAnalysis,
|
||
CatchphraseAnalysis,
|
||
NightOwlAnalysis,
|
||
DragonKingAnalysis,
|
||
DivingAnalysis,
|
||
MentionAnalysis,
|
||
LaughAnalysis,
|
||
CheckInAnalysis,
|
||
MemeBattleAnalysis,
|
||
MemberWithStats,
|
||
ClusterGraphData,
|
||
ClusterGraphOptions,
|
||
} from '../../../src/types/analysis'
|
||
import type { FileParseInfo, ConflictCheckResult, MergeParams, MergeResult } from '../../../src/types/format'
|
||
|
||
// Chat Analysis API
|
||
export const chatApi = {
|
||
// ==================== 数据库迁移 ====================
|
||
|
||
/**
|
||
* 检查是否需要数据库迁移
|
||
*/
|
||
checkMigration: (): Promise<{
|
||
needsMigration: boolean
|
||
count: number
|
||
currentVersion: number
|
||
pendingMigrations: Array<{ version: number; userMessage: string }>
|
||
}> => {
|
||
return ipcRenderer.invoke('chat:checkMigration')
|
||
},
|
||
|
||
/**
|
||
* 执行数据库迁移
|
||
*/
|
||
runMigration: (): Promise<{ success: boolean; migratedCount: number; error?: string }> => {
|
||
return ipcRenderer.invoke('chat:runMigration')
|
||
},
|
||
|
||
// ==================== 聊天记录导入与分析 ====================
|
||
|
||
/**
|
||
* 选择聊天记录文件
|
||
*/
|
||
selectFile: (): Promise<{ filePath?: string; format?: string; error?: string } | null> => {
|
||
return ipcRenderer.invoke('chat:selectFile')
|
||
},
|
||
|
||
/**
|
||
* 导入聊天记录
|
||
*/
|
||
import: (filePath: string): Promise<{ success: boolean; sessionId?: string; error?: string }> => {
|
||
return ipcRenderer.invoke('chat:import', filePath)
|
||
},
|
||
|
||
/**
|
||
* 获取所有分析会话列表
|
||
*/
|
||
getSessions: (): Promise<AnalysisSession[]> => {
|
||
return ipcRenderer.invoke('chat:getSessions')
|
||
},
|
||
|
||
/**
|
||
* 获取单个会话信息
|
||
*/
|
||
getSession: (sessionId: string): Promise<AnalysisSession | null> => {
|
||
return ipcRenderer.invoke('chat:getSession', sessionId)
|
||
},
|
||
|
||
/**
|
||
* 删除会话
|
||
*/
|
||
deleteSession: (sessionId: string): Promise<boolean> => {
|
||
return ipcRenderer.invoke('chat:deleteSession', sessionId)
|
||
},
|
||
|
||
/**
|
||
* 重命名会话
|
||
*/
|
||
renameSession: (sessionId: string, newName: string): Promise<boolean> => {
|
||
return ipcRenderer.invoke('chat:renameSession', sessionId, newName)
|
||
},
|
||
|
||
/**
|
||
* 获取可用年份列表
|
||
*/
|
||
getAvailableYears: (sessionId: string): Promise<number[]> => {
|
||
return ipcRenderer.invoke('chat:getAvailableYears', sessionId)
|
||
},
|
||
|
||
/**
|
||
* 获取成员活跃度排行
|
||
*/
|
||
getMemberActivity: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<MemberActivity[]> => {
|
||
return ipcRenderer.invoke('chat:getMemberActivity', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取成员历史昵称
|
||
*/
|
||
getMemberNameHistory: (sessionId: string, memberId: number): Promise<MemberNameHistory[]> => {
|
||
return ipcRenderer.invoke('chat:getMemberNameHistory', sessionId, memberId)
|
||
},
|
||
|
||
/**
|
||
* 获取每小时活跃度分布
|
||
*/
|
||
getHourlyActivity: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<HourlyActivity[]> => {
|
||
return ipcRenderer.invoke('chat:getHourlyActivity', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取每日活跃度趋势
|
||
*/
|
||
getDailyActivity: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<DailyActivity[]> => {
|
||
return ipcRenderer.invoke('chat:getDailyActivity', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取星期活跃度分布
|
||
*/
|
||
getWeekdayActivity: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number }
|
||
): Promise<WeekdayActivity[]> => {
|
||
return ipcRenderer.invoke('chat:getWeekdayActivity', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取月份活跃度分布
|
||
*/
|
||
getMonthlyActivity: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number }
|
||
): Promise<MonthlyActivity[]> => {
|
||
return ipcRenderer.invoke('chat:getMonthlyActivity', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取年份活跃度分布
|
||
*/
|
||
getYearlyActivity: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number }
|
||
): Promise<Array<{ year: number; messageCount: number }>> => {
|
||
return ipcRenderer.invoke('chat:getYearlyActivity', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取消息长度分布
|
||
*/
|
||
getMessageLengthDistribution: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number }
|
||
): Promise<{
|
||
detail: Array<{ len: number; count: number }>
|
||
grouped: Array<{ range: string; count: number }>
|
||
}> => {
|
||
return ipcRenderer.invoke('chat:getMessageLengthDistribution', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取消息类型分布
|
||
*/
|
||
getMessageTypeDistribution: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number }
|
||
): Promise<Array<{ type: MessageType; count: number }>> => {
|
||
return ipcRenderer.invoke('chat:getMessageTypeDistribution', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取时间范围
|
||
*/
|
||
getTimeRange: (sessionId: string): Promise<{ start: number; end: number } | null> => {
|
||
return ipcRenderer.invoke('chat:getTimeRange', sessionId)
|
||
},
|
||
|
||
/**
|
||
* 获取数据库存储目录
|
||
*/
|
||
getDbDirectory: (): Promise<string | null> => {
|
||
return ipcRenderer.invoke('chat:getDbDirectory')
|
||
},
|
||
|
||
/**
|
||
* 获取支持的格式列表
|
||
*/
|
||
getSupportedFormats: (): Promise<Array<{ name: string; platform: string }>> => {
|
||
return ipcRenderer.invoke('chat:getSupportedFormats')
|
||
},
|
||
|
||
/**
|
||
* 监听导入进度
|
||
*/
|
||
onImportProgress: (callback: (progress: ImportProgress) => void) => {
|
||
const handler = (_event: Electron.IpcRendererEvent, progress: ImportProgress) => {
|
||
callback(progress)
|
||
}
|
||
ipcRenderer.on('chat:importProgress', handler)
|
||
return () => {
|
||
ipcRenderer.removeListener('chat:importProgress', handler)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 获取复读分析数据
|
||
*/
|
||
getRepeatAnalysis: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<RepeatAnalysis> => {
|
||
return ipcRenderer.invoke('chat:getRepeatAnalysis', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取口头禅分析数据
|
||
*/
|
||
getCatchphraseAnalysis: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number }
|
||
): Promise<CatchphraseAnalysis> => {
|
||
return ipcRenderer.invoke('chat:getCatchphraseAnalysis', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取夜猫分析数据
|
||
*/
|
||
getNightOwlAnalysis: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number }
|
||
): Promise<NightOwlAnalysis> => {
|
||
return ipcRenderer.invoke('chat:getNightOwlAnalysis', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取龙王分析数据
|
||
*/
|
||
getDragonKingAnalysis: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number }
|
||
): Promise<DragonKingAnalysis> => {
|
||
return ipcRenderer.invoke('chat:getDragonKingAnalysis', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取潜水分析数据
|
||
*/
|
||
getDivingAnalysis: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<DivingAnalysis> => {
|
||
return ipcRenderer.invoke('chat:getDivingAnalysis', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取 @ 互动分析数据
|
||
*/
|
||
getMentionAnalysis: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<MentionAnalysis> => {
|
||
return ipcRenderer.invoke('chat:getMentionAnalysis', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取 @ 互动关系图数据
|
||
*/
|
||
getMentionGraph: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number }
|
||
): Promise<{
|
||
nodes: Array<{ id: number; name: string; value: number; symbolSize: number }>
|
||
links: Array<{ source: string; target: string; value: number }>
|
||
maxLinkValue: number
|
||
}> => {
|
||
return ipcRenderer.invoke('chat:getMentionGraph', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取小团体关系图数据(基于时间相邻共现)
|
||
*/
|
||
getClusterGraph: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number },
|
||
options?: ClusterGraphOptions
|
||
): Promise<ClusterGraphData> => {
|
||
return ipcRenderer.invoke('chat:getClusterGraph', sessionId, filter, options)
|
||
},
|
||
|
||
/**
|
||
* 获取含笑量分析数据
|
||
*/
|
||
getLaughAnalysis: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number },
|
||
keywords?: string[]
|
||
): Promise<LaughAnalysis> => {
|
||
return ipcRenderer.invoke('chat:getLaughAnalysis', sessionId, filter, keywords)
|
||
},
|
||
|
||
/**
|
||
* 获取斗图分析数据
|
||
*/
|
||
getMemeBattleAnalysis: (
|
||
sessionId: string,
|
||
filter?: { startTs?: number; endTs?: number }
|
||
): Promise<MemeBattleAnalysis> => {
|
||
return ipcRenderer.invoke('chat:getMemeBattleAnalysis', sessionId, filter)
|
||
},
|
||
|
||
/**
|
||
* 获取打卡分析数据(火花榜 + 忠臣榜)
|
||
*/
|
||
getCheckInAnalysis: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<CheckInAnalysis> => {
|
||
return ipcRenderer.invoke('chat:getCheckInAnalysis', sessionId, filter)
|
||
},
|
||
|
||
// ==================== 成员管理 ====================
|
||
|
||
/**
|
||
* 获取所有成员列表(含消息数和别名)
|
||
*/
|
||
getMembers: (sessionId: string): Promise<MemberWithStats[]> => {
|
||
return ipcRenderer.invoke('chat:getMembers', sessionId)
|
||
},
|
||
|
||
/**
|
||
* 获取成员列表(分页版本)
|
||
*/
|
||
getMembersPaginated: (
|
||
sessionId: string,
|
||
params: { page: number; pageSize: number; search?: string; sortOrder?: 'asc' | 'desc' }
|
||
): Promise<{
|
||
members: MemberWithStats[]
|
||
total: number
|
||
page: number
|
||
pageSize: number
|
||
totalPages: number
|
||
}> => {
|
||
return ipcRenderer.invoke('chat:getMembersPaginated', sessionId, params)
|
||
},
|
||
|
||
/**
|
||
* 更新成员别名
|
||
*/
|
||
updateMemberAliases: (sessionId: string, memberId: number, aliases: string[]): Promise<boolean> => {
|
||
return ipcRenderer.invoke('chat:updateMemberAliases', sessionId, memberId, aliases)
|
||
},
|
||
|
||
/**
|
||
* 删除成员及其所有消息
|
||
*/
|
||
deleteMember: (sessionId: string, memberId: number): Promise<boolean> => {
|
||
return ipcRenderer.invoke('chat:deleteMember', sessionId, memberId)
|
||
},
|
||
|
||
/**
|
||
* 更新会话的所有者(ownerId)
|
||
* @param ownerId 成员的 platformId,设置为 null 则清除
|
||
*/
|
||
updateSessionOwnerId: (sessionId: string, ownerId: string | null): Promise<boolean> => {
|
||
return ipcRenderer.invoke('chat:updateSessionOwnerId', sessionId, ownerId)
|
||
},
|
||
|
||
// ==================== SQL 实验室 ====================
|
||
|
||
/**
|
||
* 执行用户 SQL 查询
|
||
*/
|
||
executeSQL: (
|
||
sessionId: string,
|
||
sql: string
|
||
): Promise<{
|
||
columns: string[]
|
||
rows: any[][]
|
||
rowCount: number
|
||
duration: number
|
||
limited: boolean
|
||
}> => {
|
||
return ipcRenderer.invoke('chat:executeSQL', sessionId, sql)
|
||
},
|
||
|
||
/**
|
||
* 获取数据库 Schema
|
||
*/
|
||
getSchema: (
|
||
sessionId: string
|
||
): Promise<
|
||
Array<{
|
||
name: string
|
||
columns: Array<{
|
||
name: string
|
||
type: string
|
||
notnull: boolean
|
||
pk: boolean
|
||
}>
|
||
}>
|
||
> => {
|
||
return ipcRenderer.invoke('chat:getSchema', sessionId)
|
||
},
|
||
|
||
// ==================== 增量导入 ====================
|
||
|
||
/**
|
||
* 分析增量导入(检测去重后能新增多少消息)
|
||
*/
|
||
analyzeIncrementalImport: (
|
||
sessionId: string,
|
||
filePath: string
|
||
): Promise<{
|
||
newMessageCount: number
|
||
duplicateCount: number
|
||
totalInFile: number
|
||
error?: string
|
||
diagnosis?: { suggestion?: string }
|
||
}> => {
|
||
return ipcRenderer.invoke('chat:analyzeIncrementalImport', sessionId, filePath)
|
||
},
|
||
|
||
/**
|
||
* 执行增量导入
|
||
*/
|
||
incrementalImport: (
|
||
sessionId: string,
|
||
filePath: string
|
||
): Promise<{
|
||
success: boolean
|
||
newMessageCount: number
|
||
error?: string
|
||
}> => {
|
||
return ipcRenderer.invoke('chat:incrementalImport', sessionId, filePath)
|
||
},
|
||
|
||
/**
|
||
* 导出多个会话为临时文件(用于批量管理中的合并)
|
||
*/
|
||
exportSessionsToTempFiles: (
|
||
sessionIds: string[]
|
||
): Promise<{
|
||
success: boolean
|
||
tempFiles: string[]
|
||
error?: string
|
||
}> => {
|
||
return ipcRenderer.invoke('chat:exportSessionsToTempFiles', sessionIds)
|
||
},
|
||
|
||
/**
|
||
* 清理临时导出文件
|
||
*/
|
||
cleanupTempExportFiles: (
|
||
filePaths: string[]
|
||
): Promise<{
|
||
success: boolean
|
||
error?: string
|
||
}> => {
|
||
return ipcRenderer.invoke('chat:cleanupTempExportFiles', filePaths)
|
||
},
|
||
}
|
||
|
||
// Merge API - 合并功能
|
||
export const mergeApi = {
|
||
/**
|
||
* 解析文件获取基本信息(用于合并预览)
|
||
* 解析后结果会被缓存,后续合并时无需再次读取原始文件
|
||
*/
|
||
parseFileInfo: (filePath: string): Promise<FileParseInfo> => {
|
||
return ipcRenderer.invoke('merge:parseFileInfo', filePath)
|
||
},
|
||
|
||
/**
|
||
* 检测合并冲突
|
||
*/
|
||
checkConflicts: (filePaths: string[]): Promise<ConflictCheckResult> => {
|
||
return ipcRenderer.invoke('merge:checkConflicts', filePaths)
|
||
},
|
||
|
||
/**
|
||
* 执行合并
|
||
*/
|
||
mergeFiles: (params: MergeParams): Promise<MergeResult> => {
|
||
return ipcRenderer.invoke('merge:mergeFiles', params)
|
||
},
|
||
|
||
/**
|
||
* 清理解析缓存
|
||
* @param filePath 可选,指定文件路径则清理该文件的缓存,否则清理所有缓存
|
||
*/
|
||
clearCache: (filePath?: string): Promise<boolean> => {
|
||
return ipcRenderer.invoke('merge:clearCache', filePath)
|
||
},
|
||
}
|