// 消息交互管理器 - 微信小程序专用 // 处理消息表情回应、引用回复、撤回、转发等交互功能 const apiClient = require('./api-client.js'); /** * 消息交互管理器 * 功能: * 1. 消息表情回应 * 2. 消息引用回复 * 3. 消息撤回功能 * 4. 消息转发功能 * 5. 消息收藏功能 * 6. 消息多选操作 */ class MessageInteractionManager { constructor() { this.isInitialized = false; // 交互配置 this.interactionConfig = { // 表情回应配置 reactions: { enabled: true, maxReactions: 6, // 每条消息最多6种表情 maxUsersPerReaction: 100, // 每种表情最多100个用户 commonEmojis: ['👍', '❤️', '😂', '😮', '😢', '😡'], // 常用表情 customEmojis: [] // 自定义表情 }, // 引用回复配置 quote: { enabled: true, maxQuoteLength: 100, // 引用内容最大长度 showOriginalSender: true, // 显示原发送者 showTimestamp: true // 显示时间戳 }, // 撤回配置 recall: { enabled: true, timeLimit: 2 * 60 * 1000, // 2分钟内可撤回 showRecallMessage: true, // 显示撤回提示 allowRecallOthers: false // 是否允许撤回他人消息(群主/管理员) }, // 转发配置 forward: { enabled: true, maxForwardCount: 5, // 最多同时转发5条消息 preserveFormat: true, // 保持原格式 showForwardSource: true // 显示转发来源 }, // 收藏配置 favorite: { enabled: true, maxFavorites: 1000, // 最多收藏1000条 syncToCloud: true // 同步到云端 } }; // 当前操作状态 this.currentOperation = { type: null, // 'reaction', 'quote', 'forward', 'select' messageId: null, data: null }; // 多选状态 this.multiSelectMode = false; this.selectedMessages = new Set(); // 表情回应缓存 this.reactionsCache = new Map(); this.init(); } // 初始化交互管理器 async init() { if (this.isInitialized) return; console.log('✨ 初始化消息交互管理器...'); try { // 加载用户收藏 await this.loadUserFavorites(); // 加载表情回应缓存 await this.loadReactionsCache(); this.isInitialized = true; console.log('✅ 消息交互管理器初始化完成'); } catch (error) { console.error('❌ 消息交互管理器初始化失败:', error); } } // 👍 ===== 表情回应功能 ===== // 添加表情回应 async addReaction(messageId, emoji, userId) { try { console.log('👍 添加表情回应:', messageId, emoji, userId); // 验证参数 if (!messageId || !emoji || !userId) { throw new Error('参数不完整'); } // 检查是否已经回应过 const existingReaction = await this.getUserReaction(messageId, userId); if (existingReaction === emoji) { console.log('⚠️ 用户已经添加过相同表情'); return { success: false, error: '已经添加过相同表情' }; } // 如果用户已有其他表情回应,先移除 if (existingReaction) { await this.removeReaction(messageId, existingReaction, userId); } // 调用API添加表情回应 const response = await apiClient.request({ url: '/api/v1/messages/reactions', method: 'POST', data: { messageId: messageId, emoji: emoji, userId: userId } }); if (response.success) { // 更新本地缓存 this.updateReactionCache(messageId, emoji, userId, 'add'); console.log('✅ 表情回应添加成功'); return { success: true, data: response.data }; } else { throw new Error(response.error || '添加表情回应失败'); } } catch (error) { console.error('❌ 添加表情回应失败:', error); return { success: false, error: error.message }; } } // 移除表情回应 async removeReaction(messageId, emoji, userId) { try { console.log('👎 移除表情回应:', messageId, emoji, userId); // 调用API移除表情回应 const response = await apiClient.request({ url: '/api/v1/messages/reactions', method: 'DELETE', data: { messageId: messageId, emoji: emoji, userId: userId } }); if (response.success) { // 更新本地缓存 this.updateReactionCache(messageId, emoji, userId, 'remove'); console.log('✅ 表情回应移除成功'); return { success: true }; } else { throw new Error(response.error || '移除表情回应失败'); } } catch (error) { console.error('❌ 移除表情回应失败:', error); return { success: false, error: error.message }; } } // 获取消息的所有表情回应 async getMessageReactions(messageId) { try { // 先从缓存获取 const cached = this.reactionsCache.get(messageId); if (cached && (Date.now() - cached.timestamp) < 60000) { // 1分钟缓存 return { success: true, data: cached.reactions }; } // 从服务器获取 const response = await apiClient.request({ url: `/api/v1/messages/${messageId}/reactions`, method: 'GET' }); if (response.success) { const reactions = response.data || []; // 更新缓存 this.reactionsCache.set(messageId, { reactions: reactions, timestamp: Date.now() }); return { success: true, data: reactions }; } else { throw new Error(response.error || '获取表情回应失败'); } } catch (error) { console.error('❌ 获取表情回应失败:', error); return { success: false, error: error.message }; } } // 获取用户对消息的表情回应 async getUserReaction(messageId, userId) { try { const result = await this.getMessageReactions(messageId); if (result.success) { const userReaction = result.data.find(reaction => reaction.users && reaction.users.includes(userId) ); return userReaction ? userReaction.emoji : null; } return null; } catch (error) { console.error('❌ 获取用户表情回应失败:', error); return null; } } // 更新表情回应缓存 updateReactionCache(messageId, emoji, userId, action) { const cached = this.reactionsCache.get(messageId); if (!cached) return; let reactions = cached.reactions; let emojiReaction = reactions.find(r => r.emoji === emoji); if (action === 'add') { if (emojiReaction) { // 添加用户到现有表情 if (!emojiReaction.users.includes(userId)) { emojiReaction.users.push(userId); emojiReaction.count = emojiReaction.users.length; } } else { // 创建新的表情回应 reactions.push({ emoji: emoji, count: 1, users: [userId] }); } } else if (action === 'remove') { if (emojiReaction) { // 从表情中移除用户 emojiReaction.users = emojiReaction.users.filter(id => id !== userId); emojiReaction.count = emojiReaction.users.length; // 如果没有用户了,移除整个表情 if (emojiReaction.count === 0) { reactions = reactions.filter(r => r.emoji !== emoji); } } } // 更新缓存 this.reactionsCache.set(messageId, { reactions: reactions, timestamp: Date.now() }); } // 💬 ===== 引用回复功能 ===== // 创建引用回复 createQuoteReply(originalMessage, replyContent) { try { console.log('💬 创建引用回复:', originalMessage.messageId); // 生成引用内容 const quoteContent = this.generateQuoteContent(originalMessage); // 构建引用回复消息 const quoteReply = { type: 'quote', content: replyContent, quote: { messageId: originalMessage.messageId, senderId: originalMessage.senderId, senderName: originalMessage.senderName, content: quoteContent, timestamp: originalMessage.timestamp, msgType: originalMessage.msgType } }; console.log('✅ 引用回复创建成功'); return { success: true, data: quoteReply }; } catch (error) { console.error('❌ 创建引用回复失败:', error); return { success: false, error: error.message }; } } // 生成引用内容 generateQuoteContent(message) { let content = ''; switch (message.msgType) { case 'text': content = message.content; // 限制长度 if (content.length > this.interactionConfig.quote.maxQuoteLength) { content = content.substring(0, this.interactionConfig.quote.maxQuoteLength) + '...'; } break; case 'image': content = '[图片]'; break; case 'video': content = '[视频]'; break; case 'voice': content = '[语音]'; break; case 'file': content = '[文件]'; break; case 'location': content = '[位置]'; break; default: content = '[消息]'; } return content; } // 🔄 ===== 消息撤回功能 ===== // 撤回消息 async recallMessage(messageId, userId) { try { console.log('🔄 撤回消息:', messageId, userId); // 检查撤回权限和时间限制 const canRecall = await this.checkRecallPermission(messageId, userId); if (!canRecall.allowed) { return { success: false, error: canRecall.reason }; } // 调用API撤回消息 const response = await apiClient.request({ url: `/api/v1/messages/${messageId}/recall`, method: 'POST', data: { userId: userId } }); if (response.success) { console.log('✅ 消息撤回成功'); return { success: true, data: response.data }; } else { throw new Error(response.error || '撤回消息失败'); } } catch (error) { console.error('❌ 撤回消息失败:', error); return { success: false, error: error.message }; } } // 检查撤回权限 async checkRecallPermission(messageId, userId) { try { // 获取消息信息 const messageInfo = await this.getMessageInfo(messageId); if (!messageInfo) { return { allowed: false, reason: '消息不存在' }; } // 检查是否是消息发送者 if (messageInfo.senderId !== userId) { // 检查是否有管理员权限 if (!this.interactionConfig.recall.allowRecallOthers) { return { allowed: false, reason: '只能撤回自己的消息' }; } } // 检查时间限制 const now = Date.now(); const messageTime = messageInfo.timestamp; const timeDiff = now - messageTime; if (timeDiff > this.interactionConfig.recall.timeLimit) { return { allowed: false, reason: '超过撤回时间限制' }; } return { allowed: true }; } catch (error) { console.error('❌ 检查撤回权限失败:', error); return { allowed: false, reason: '检查权限失败' }; } } // 📤 ===== 消息转发功能 ===== // 转发消息 async forwardMessages(messageIds, targetConversations) { try { console.log('📤 转发消息:', messageIds, targetConversations); // 验证参数 if (!messageIds.length || !targetConversations.length) { throw new Error('参数不完整'); } // 检查转发数量限制 if (messageIds.length > this.interactionConfig.forward.maxForwardCount) { throw new Error(`最多只能同时转发${this.interactionConfig.forward.maxForwardCount}条消息`); } // 获取消息详情 const messages = await this.getMessagesInfo(messageIds); if (!messages.length) { throw new Error('获取消息信息失败'); } // 执行转发 const results = []; for (const targetId of targetConversations) { const result = await this.forwardToConversation(messages, targetId); results.push({ targetId: targetId, success: result.success, error: result.error }); } console.log('✅ 消息转发完成'); return { success: true, data: results }; } catch (error) { console.error('❌ 转发消息失败:', error); return { success: false, error: error.message }; } } // 转发到指定会话 async forwardToConversation(messages, targetConversationId) { try { // 构建转发消息 const forwardMessages = messages.map(msg => this.buildForwardMessage(msg)); // 调用API转发 const response = await apiClient.request({ url: '/api/v1/messages/forward', method: 'POST', data: { messages: forwardMessages, targetConversationId: targetConversationId, preserveFormat: this.interactionConfig.forward.preserveFormat, showSource: this.interactionConfig.forward.showForwardSource } }); if (response.success) { return { success: true, data: response.data }; } else { throw new Error(response.error || '转发失败'); } } catch (error) { console.error('❌ 转发到会话失败:', error); return { success: false, error: error.message }; } } // 构建转发消息 buildForwardMessage(originalMessage) { return { originalMessageId: originalMessage.messageId, content: originalMessage.content, msgType: originalMessage.msgType, originalSender: originalMessage.senderName, originalTimestamp: originalMessage.timestamp, forwardedAt: Date.now() }; } // ⭐ ===== 消息收藏功能 ===== // 收藏消息 async favoriteMessage(messageId, userId) { try { console.log('⭐ 收藏消息:', messageId, userId); // 检查是否已收藏 const isFavorited = await this.isMessageFavorited(messageId, userId); if (isFavorited) { return { success: false, error: '消息已收藏' }; } // 调用API收藏 const response = await apiClient.request({ url: '/api/v1/messages/favorite', method: 'POST', data: { messageId: messageId, userId: userId } }); if (response.success) { console.log('✅ 消息收藏成功'); return { success: true, data: response.data }; } else { throw new Error(response.error || '收藏失败'); } } catch (error) { console.error('❌ 收藏消息失败:', error); return { success: false, error: error.message }; } } // 取消收藏消息 async unfavoriteMessage(messageId, userId) { try { console.log('⭐ 取消收藏消息:', messageId, userId); // 调用API取消收藏 const response = await apiClient.request({ url: `/api/v1/messages/favorite/${messageId}`, method: 'DELETE', data: { userId: userId } }); if (response.success) { console.log('✅ 取消收藏成功'); return { success: true }; } else { throw new Error(response.error || '取消收藏失败'); } } catch (error) { console.error('❌ 取消收藏失败:', error); return { success: false, error: error.message }; } } // 检查消息是否已收藏 async isMessageFavorited(messageId, userId) { try { const response = await apiClient.request({ url: `/api/v1/messages/favorite/check`, method: 'GET', data: { messageId: messageId, userId: userId } }); return response.success && response.data.favorited; } catch (error) { console.error('❌ 检查收藏状态失败:', error); return false; } } // 📋 ===== 多选操作功能 ===== // 进入多选模式 enterMultiSelectMode(initialMessageId = null) { console.log('📋 进入多选模式'); this.multiSelectMode = true; this.selectedMessages.clear(); if (initialMessageId) { this.selectedMessages.add(initialMessageId); } this.currentOperation = { type: 'select', messageId: null, data: { selectedCount: this.selectedMessages.size } }; } // 退出多选模式 exitMultiSelectMode() { console.log('📋 退出多选模式'); this.multiSelectMode = false; this.selectedMessages.clear(); this.currentOperation = { type: null, messageId: null, data: null }; } // 切换消息选择状态 toggleMessageSelection(messageId) { if (this.selectedMessages.has(messageId)) { this.selectedMessages.delete(messageId); } else { this.selectedMessages.add(messageId); } this.currentOperation.data.selectedCount = this.selectedMessages.size; console.log('📋 消息选择状态切换:', messageId, this.selectedMessages.size); } // 获取选中的消息 getSelectedMessages() { return Array.from(this.selectedMessages); } // 🔧 ===== 工具方法 ===== // 获取消息信息 async getMessageInfo(messageId) { try { const response = await apiClient.request({ url: `/api/v1/messages/${messageId}`, method: 'GET' }); return response.success ? response.data : null; } catch (error) { console.error('❌ 获取消息信息失败:', error); return null; } } // 获取多条消息信息 async getMessagesInfo(messageIds) { try { const response = await apiClient.request({ url: '/api/v1/messages/batch', method: 'POST', data: { messageIds: messageIds } }); return response.success ? response.data : []; } catch (error) { console.error('❌ 获取消息信息失败:', error); return []; } } // 加载用户收藏 async loadUserFavorites() { try { const favorites = wx.getStorageSync('userFavorites') || []; this.userFavorites = new Set(favorites); } catch (error) { console.error('❌ 加载用户收藏失败:', error); } } // 加载表情回应缓存 async loadReactionsCache() { try { const cached = wx.getStorageSync('reactionsCache') || {}; this.reactionsCache = new Map(Object.entries(cached)); } catch (error) { console.error('❌ 加载表情回应缓存失败:', error); } } // 保存表情回应缓存 saveReactionsCache() { try { const cacheObj = Object.fromEntries(this.reactionsCache); wx.setStorageSync('reactionsCache', cacheObj); } catch (error) { console.error('❌ 保存表情回应缓存失败:', error); } } // 获取当前操作状态 getCurrentOperation() { return { ...this.currentOperation }; } // 获取交互配置 getInteractionConfig() { return { ...this.interactionConfig }; } // 重置管理器 reset() { this.exitMultiSelectMode(); this.reactionsCache.clear(); this.currentOperation = { type: null, messageId: null, data: null }; } } // 创建全局实例 const messageInteractionManager = new MessageInteractionManager(); module.exports = messageInteractionManager;