const fs = require('fs'); const path = require('path'); const { app } = require('electron'); class MessageStore { constructor() { this.dataDir = path.join(app.getPath('userData'), 'messages'); this.messagesFile = path.join(this.dataDir, 'messages.json'); this.messages = []; this.maxMessages = 10000; // 最多保留 10000 条消息 // 初始化存储 this.init(); } init() { // 创建目录 if (!fs.existsSync(this.dataDir)) { fs.mkdirSync(this.dataDir, { recursive: true }); } // 加载现有消息 if (fs.existsSync(this.messagesFile)) { try { const data = fs.readFileSync(this.messagesFile, 'utf-8'); this.messages = JSON.parse(data); console.log(`[MessageStore] 加载了 ${this.messages.length} 条历史消息`); } catch (error) { console.error('[MessageStore] 加载消息失败:', error.message); this.messages = []; } } } // 保存消息 saveMessage(message) { const msg = { id: message.id || `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, timestamp: message.timestamp || new Date().toISOString(), direction: message.direction, // 'inbound' | 'outbound' source: message.source, // 'wecom' | 'openclaw' sessionId: message.sessionId || '', chatId: message.chatId || '', senderId: message.senderId || '', senderName: message.senderName || '', messageType: message.messageType || 'text', // 'text' | 'image' | 'file' | 'voice' content: message.content || '', attachments: message.attachments || [], metadata: message.metadata || {}, delivered: message.delivered !== false, read: message.read || false }; this.messages.push(msg); // 限制消息数量 if (this.messages.length > this.maxMessages) { this.messages = this.messages.slice(-this.maxMessages); } // 异步保存到文件 this.persist(); return msg; } // 批量保存消息 saveMessages(messages) { messages.forEach(msg => this.saveMessage(msg)); } // 持久化到文件 persist() { // 使用异步写入,避免阻塞 setImmediate(() => { try { const tempFile = this.messagesFile + '.tmp'; fs.writeFileSync(tempFile, JSON.stringify(this.messages, null, 2), 'utf-8'); fs.renameSync(tempFile, this.messagesFile); console.log(`[MessageStore] 已保存 ${this.messages.length} 条消息`); } catch (error) { console.error('[MessageStore] 保存消息失败:', error.message); } }); } // 获取消息列表 getMessages(options = {}) { let filtered = [...this.messages]; // 按会话过滤 if (options.sessionId) { filtered = filtered.filter(m => m.sessionId === options.sessionId); } // 按方向过滤 if (options.direction) { filtered = filtered.filter(m => m.direction === options.direction); } // 按来源过滤 if (options.source) { filtered = filtered.filter(m => m.source === options.source); } // 按时间范围过滤 if (options.startTime) { filtered = filtered.filter(m => new Date(m.timestamp) >= new Date(options.startTime)); } if (options.endTime) { filtered = filtered.filter(m => new Date(m.timestamp) <= new Date(options.endTime)); } // 分页 const limit = options.limit || 100; const offset = options.offset || 0; const paginated = filtered.slice(offset, offset + limit); // 按时间倒序(最新的在前) paginated.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)); return { messages: paginated, total: filtered.length, limit, offset }; } // 获取会话列表 getSessions() { const sessionMap = new Map(); this.messages.forEach(msg => { if (!msg.sessionId) return; if (!sessionMap.has(msg.sessionId)) { sessionMap.set(msg.sessionId, { sessionId: msg.sessionId, chatId: msg.chatId, lastMessageTime: msg.timestamp, messageCount: 0, unreadCount: 0 }); } const session = sessionMap.get(msg.sessionId); session.messageCount++; if (!msg.read) { session.unreadCount++; } if (new Date(msg.timestamp) > new Date(session.lastMessageTime)) { session.lastMessageTime = msg.timestamp; } }); // 按最后消息时间排序 return Array.from(sessionMap.values()) .sort((a, b) => new Date(b.lastMessageTime) - new Date(a.lastMessageTime)); } // 标记消息为已读 markAsRead(messageIds) { const ids = Array.isArray(messageIds) ? messageIds : [messageIds]; let updated = 0; this.messages.forEach(msg => { if (ids.includes(msg.id) && !msg.read) { msg.read = true; updated++; } }); if (updated > 0) { this.persist(); } return updated; } // 搜索消息 searchMessages(query, options = {}) { const lowerQuery = query.toLowerCase(); const filtered = this.messages.filter(msg => { // 搜索内容 if (msg.content && msg.content.toLowerCase().includes(lowerQuery)) { return true; } // 搜索发送者 if (msg.senderName && msg.senderName.toLowerCase().includes(lowerQuery)) { return true; } // 搜索会话 ID if (msg.sessionId && msg.sessionId.toLowerCase().includes(lowerQuery)) { return true; } return false; }); // 按相关性排序(内容匹配优先) filtered.sort((a, b) => { const aScore = (a.content.toLowerCase().includes(lowerQuery) ? 2 : 0) + (a.senderName.toLowerCase().includes(lowerQuery) ? 1 : 0); const bScore = (b.content.toLowerCase().includes(lowerQuery) ? 2 : 0) + (b.senderName.toLowerCase().includes(lowerQuery) ? 1 : 0); return bScore - aScore; }); const limit = options.limit || 50; const offset = options.offset || 0; return { messages: filtered.slice(offset, offset + limit), total: filtered.length, query }; } // 导出消息 exportMessages(options = {}) { const result = this.getMessages(options); return JSON.stringify(result.messages, null, 2); } // 清空消息 clear(options = {}) { if (options.sessionId) { this.messages = this.messages.filter(m => m.sessionId !== options.sessionId); } else { this.messages = []; } this.persist(); } // 获取统计数据 getStats() { const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); return { total: this.messages.length, today: this.messages.filter(m => new Date(m.timestamp) >= today).length, yesterday: this.messages.filter(m => new Date(m.timestamp) >= yesterday && new Date(m.timestamp) < today ).length, inbound: this.messages.filter(m => m.direction === 'inbound').length, outbound: this.messages.filter(m => m.direction === 'outbound').length, wecom: this.messages.filter(m => m.source === 'wecom').length, openclaw: this.messages.filter(m => m.source === 'openclaw').length, sessions: this.getSessions().length }; } } // 导出单例 module.exports = new MessageStore();