feat: 消息管理器支持显示系统消息

This commit is contained in:
digua
2025-12-29 01:41:35 +08:00
parent 1172a0977f
commit c3cfe83a38
10 changed files with 117 additions and 16 deletions
+1 -1
View File
@@ -135,7 +135,7 @@ export function trackDailyActive(): void {
}
// 上报每日活跃事件
trackEvent(isNew ? 'app_daily_active_new' : 'app_daily_active')
trackEvent(isNew ? 'app_active_new' : 'app_active')
data.lastReportDate = today
saveAnalyticsData(data)
+16 -1
View File
@@ -49,7 +49,7 @@ export function registerMessagesHandlers({ win }: IpcContext): void {
)
/**
* 获取最近消息
* 获取最近消息AI Agent 专用)
*/
ipcMain.handle(
'ai:getRecentMessages',
@@ -63,6 +63,21 @@ export function registerMessagesHandlers({ win }: IpcContext): void {
}
)
/**
* 获取所有最近消息(消息查看器专用)
*/
ipcMain.handle(
'ai:getAllRecentMessages',
async (_, sessionId: string, filter?: { startTs?: number; endTs?: number }, limit?: number) => {
try {
return await worker.getAllRecentMessages(sessionId, filter, limit)
} catch (error) {
console.error('获取所有最近消息失败:', error)
return { messages: [], total: 0 }
}
}
)
/**
* 获取两人之间的对话
*/
+2
View File
@@ -36,6 +36,7 @@ import {
searchMessages,
getMessageContext,
getRecentMessages,
getAllRecentMessages,
getConversationBetween,
getMessagesBefore,
getMessagesAfter,
@@ -106,6 +107,7 @@ const syncHandlers: Record<string, (payload: any) => any> = {
searchMessages: (p) => searchMessages(p.sessionId, p.keywords, p.filter, p.limit, p.offset, p.senderId),
getMessageContext: (p) => getMessageContext(p.sessionId, p.messageIds, p.contextSize),
getRecentMessages: (p) => getRecentMessages(p.sessionId, p.filter, p.limit),
getAllRecentMessages: (p) => getAllRecentMessages(p.sessionId, p.filter, p.limit),
getConversationBetween: (p) => getConversationBetween(p.sessionId, p.memberId1, p.memberId2, p.filter, p.limit),
getMessagesBefore: (p) => getMessagesBefore(p.sessionId, p.beforeId, p.limit, p.filter, p.senderId, p.keywords),
getMessagesAfter: (p) => getMessagesAfter(p.sessionId, p.afterId, p.limit, p.filter, p.senderId, p.keywords),
+1
View File
@@ -41,6 +41,7 @@ export {
searchMessages,
getMessageContext,
getRecentMessages,
getAllRecentMessages,
getConversationBetween,
getMessagesBefore,
getMessagesAfter,
+61 -5
View File
@@ -113,7 +113,7 @@ const TEXT_ONLY_FILTER = 'AND msg.type = 0 AND msg.content IS NOT NULL AND msg.c
// ==================== 查询函数 ====================
/**
* 获取最近的消息
* 获取最近的消息(AI Agent 专用,只返回文本消息)
* @param sessionId 会话 ID
* @param filter 时间过滤器
* @param limit 返回数量限制
@@ -176,6 +176,66 @@ export function getRecentMessages(
}
}
/**
* 获取所有最近的消息(消息查看器专用,包含所有类型消息)
* @param sessionId 会话 ID
* @param filter 时间过滤器
* @param limit 返回数量限制
*/
export function getAllRecentMessages(
sessionId: string,
filter?: TimeFilter,
limit: number = 100
): MessagesWithTotal {
// 确保数据库有 avatar 字段(兼容旧数据库)
ensureAvatarColumn(sessionId)
const db = openDatabase(sessionId)
if (!db) return { messages: [], total: 0 }
// 构建时间过滤条件
const { clause: timeClause, params: timeParams } = buildTimeFilter(filter)
const timeCondition = timeClause ? timeClause.replace('WHERE', 'AND') : ''
// 查询总数
const countSql = `
SELECT COUNT(*) as total
FROM message msg
JOIN member m ON msg.sender_id = m.id
WHERE 1=1
${timeCondition}
`
const totalRow = db.prepare(countSql).get(...timeParams) as { total: number }
const total = totalRow?.total || 0
// 查询最近消息(按时间降序)
const sql = `
SELECT
msg.id,
COALESCE(m.group_nickname, m.account_name, m.platform_id) as senderName,
m.platform_id as senderPlatformId,
m.aliases,
m.avatar,
msg.content,
msg.ts as timestamp,
msg.type
FROM message msg
JOIN member m ON msg.sender_id = m.id
WHERE 1=1
${timeCondition}
ORDER BY msg.ts DESC
LIMIT ?
`
const rows = db.prepare(sql).all(...timeParams, limit) as DbMessageRow[]
// 返回时按时间正序排列(便于阅读)
return {
messages: rows.map(sanitizeMessageRow).reverse(),
total,
}
}
/**
* 关键词搜索消息
* @param sessionId 会话 ID
@@ -221,7 +281,6 @@ export function searchMessages(
JOIN member m ON msg.sender_id = m.id
WHERE ${keywordCondition}
${timeCondition}
${SYSTEM_FILTER}
${senderCondition}
`
const totalRow = db.prepare(countSql).get(...keywordParams, ...timeParams, ...senderParams) as { total: number }
@@ -242,7 +301,6 @@ export function searchMessages(
JOIN member m ON msg.sender_id = m.id
WHERE ${keywordCondition}
${timeCondition}
${SYSTEM_FILTER}
${senderCondition}
ORDER BY msg.ts DESC
LIMIT ? OFFSET ?
@@ -384,7 +442,6 @@ export function getMessagesBefore(
${timeCondition}
${keywordCondition}
${senderCondition}
${SYSTEM_FILTER}
ORDER BY msg.id DESC
LIMIT ?
`
@@ -450,7 +507,6 @@ export function getMessagesAfter(
${timeCondition}
${keywordCondition}
${senderCondition}
${SYSTEM_FILTER}
ORDER BY msg.id ASC
LIMIT ?
`
+11
View File
@@ -425,6 +425,17 @@ export async function getRecentMessages(
return sendToWorker('getRecentMessages', { sessionId, filter, limit })
}
/**
* 获取所有最近消息(消息查看器专用,包含所有类型消息)
*/
export async function getAllRecentMessages(
sessionId: string,
filter?: any,
limit?: number
): Promise<{ messages: SearchMessageResult[]; total: number }> {
return sendToWorker('getAllRecentMessages', { sessionId, filter, limit })
}
/**
* 获取两个成员之间的对话
*/
+7
View File
@@ -159,6 +159,11 @@ interface AiApi {
filter?: TimeFilter,
limit?: number
) => Promise<{ messages: SearchMessageResult[]; total: number }>
getAllRecentMessages: (
sessionId: string,
filter?: TimeFilter,
limit?: number
) => Promise<{ messages: SearchMessageResult[]; total: number }>
getConversationBetween: (
sessionId: string,
memberId1: number,
@@ -380,6 +385,8 @@ interface CacheApi {
filename: string,
dataUrl: string
) => Promise<{ success: boolean; filePath?: string; error?: string }>
getLatestImportLog: () => Promise<{ success: boolean; path?: string; name?: string; error?: string }>
showInFolder: (filePath: string) => Promise<{ success: boolean; error?: string }>
}
// Network API 类型 - 网络代理配置
+12 -1
View File
@@ -501,7 +501,7 @@ const aiApi = {
},
/**
* 获取最近消息
* 获取最近消息AI Agent 专用)
*/
getRecentMessages: (
sessionId: string,
@@ -511,6 +511,17 @@ const aiApi = {
return ipcRenderer.invoke('ai:getRecentMessages', sessionId, filter, limit)
},
/**
* 获取所有最近消息(消息查看器专用)
*/
getAllRecentMessages: (
sessionId: string,
filter?: { startTs?: number; endTs?: number },
limit?: number
): Promise<{ messages: SearchMessageResult[]; total: number }> => {
return ipcRenderer.invoke('ai:getAllRecentMessages', sessionId, filter, limit)
},
/**
* 获取两人之间的对话
*/
@@ -106,7 +106,7 @@ async function loadInitialMessages() {
// 没有目标消息和关键词,加载最新的 100 条
isSearchMode.value = false
searchOffset.value = 0
const result = await window.aiApi.getRecentMessages(sessionId, filter, 100)
const result = await window.aiApi.getAllRecentMessages(sessionId, filter, 100)
messages.value = result.messages
hasMoreBefore.value = result.messages.length >= 100
hasMoreAfter.value = false
+5 -7
View File
@@ -353,10 +353,13 @@ const file2Name = computed(() => files.value[1]?.name || '文件 2')
<div class="rounded-xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
<!-- 标题 -->
<div class="border-b border-gray-200 px-5 py-4 dark:border-gray-800">
<h2 class="font-semibold text-gray-900 dark:text-white">合并聊天记录</h2>
<h2 class="font-semibold text-gray-900 dark:text-white">群工局合并聊天记录</h2>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
将多个聊天记录文件合并为一个支持 JSONJSONLTXT 等多种格式
</p>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
该工具的用途是如果你们群有多份聊天记录每个人的聊天记录都不一样可以试试使用这个工具进行合并合并后将会得到该群最完整的聊天记录
</p>
</div>
<!-- 文件上传区域 -->
@@ -474,12 +477,7 @@ const file2Name = computed(() => files.value[1]?.name || '文件 2')
: 'border-gray-200 hover:border-gray-300 dark:border-gray-700 dark:hover:border-gray-600',
]"
>
<input
v-model="outputFormat"
type="radio"
:value="opt.value"
class="h-4 w-4 text-primary-600"
/>
<input v-model="outputFormat" type="radio" :value="opt.value" class="h-4 w-4 text-primary-600" />
<div>
<div class="text-sm font-medium text-gray-900 dark:text-white">{{ opt.label }}</div>
<div class="text-xs text-gray-500 dark:text-gray-400">{{ opt.description }}</div>