// pages/chat-detail/index.js import socketManager from '../../utils/websocket'; const AuthManager = require('../../utils/auth.js'); Page({ /** * 页面的初始数据 */ data: { userId: '', userName: '', avatar: '', messages: [], inputValue: '', goodsInfo: null, // 商品信息 connectionStatus: 'disconnected', // 连接状态: disconnected, connecting, connected, error connectionMessage: '', // 连接状态提示消息 isMockMode: false, // 默认为真实WebSocket通信模式 isManager: false // 是否是与客服的聊天 }, /** * 从本地存储加载历史消息 */ loadMessagesFromStorage: function() { try { const storedMessages = wx.getStorageSync(`chat_messages_${this.data.userId}`); if (storedMessages && storedMessages.length > 0) { console.log('从本地存储加载了', storedMessages.length, '条历史消息'); // 处理消息数据,确保包含必要的字段 const messagesWithRequiredFields = storedMessages.map(msg => ({ ...msg, // 重新生成shortTime字段 shortTime: msg.time ? this.extractShortTime(msg.time) : this.getShortTime() })); // 重新处理时间显示逻辑 const processedMessages = this.processMessageTimes(messagesWithRequiredFields); this.setData({ messages: processedMessages }); return true; } } catch (e) { console.error('从本地存储加载消息失败:', e); } return false; }, /** * 根据用户ID获取用户名 * @param {string|number} userId 用户ID * @returns {string|null} 用户名或null(无效用户时) */ getUserNameById: function(userId) { try { // 参数有效性检查 if (!userId || typeof userId === 'undefined') { return null; } // 确保userId是字符串类型 const safeUserId = String(userId); // 检查是否是手机号格式 (中国手机号格式) if (/^1[3-9]\d{9}$/.test(safeUserId)) { // 如果是手机号,显示"客户-后四位" return `客户-${safeUserId.slice(-4)}`; } // 首先从客服列表缓存中查找 const app = getApp(); if (app.globalData.customerServiceList) { const service = app.globalData.customerServiceList.find(item => item.id === safeUserId || item.managerId === safeUserId || String(item.id) === safeUserId || String(item.managerId) === safeUserId ); if (service) { return service.alias || service.name || service.id || '未知客服'; } } // 尝试从本地存储获取客服列表 const cachedCustomerServices = wx.getStorageSync('cached_customer_services') || []; const service = cachedCustomerServices.find(item => item.id === safeUserId || item.managerId === safeUserId || String(item.id) === safeUserId || String(item.managerId) === safeUserId ); if (service) { return service.alias || service.name || service.id || '未知客服'; } // 对于manager_开头的ID,显示为客服 if (safeUserId.startsWith('manager_') || safeUserId.includes('manager')) { return '客服-' + (safeUserId.length >= 4 ? safeUserId.slice(-4) : safeUserId); } // 处理其他格式的用户ID if (safeUserId.includes('_customer_')) { // 提取客户标识部分 const parts = safeUserId.split('_customer_'); return parts.length > 1 ? `客户-${parts[1].substring(0, 4)}` : '客户'; } // 尝试从本地存储获取用户信息 try { const userInfo = wx.getStorageSync('userInfo'); // 检查userInfo类型,避免重复JSON解析 if (userInfo && typeof userInfo === 'object') { if (userInfo.userId === safeUserId || userInfo.id === safeUserId) { return userInfo.name || (userInfo.phone && `客户-${userInfo.phone.slice(-4)}`) || '客户'; } } else if (userInfo && typeof userInfo === 'string') { try { const parsedUserInfo = JSON.parse(userInfo); if (parsedUserInfo.userId === safeUserId || parsedUserInfo.id === safeUserId) { return parsedUserInfo.name || (parsedUserInfo.phone && `客户-${parsedUserInfo.phone.slice(-4)}`) || '客户'; } } catch (parseError) { console.warn('解析userInfo字符串失败,忽略此步骤:', parseError); } } } catch (e) { console.error('获取本地用户信息失败:', e); } // 对于数字格式的ID(通常是客服ID),显示为客服 if (/^\d+$/.test(safeUserId)) { return '客服-' + safeUserId; } // 如果都没有找到,返回默认名称,避免出现undefined if (safeUserId.length >= 4) { return '用户' + safeUserId.slice(-4); } else { return '用户' + safeUserId; } } catch (e) { console.error('获取用户名出错:', e); return null; } }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { // 执行统一身份验证 AuthManager.authenticate((authResult) => { console.log('身份验证成功,初始化聊天页面:', authResult); // 接收从上一个页面传递的参数 if (options) { const targetId = options.userId || ''; // 优先使用传递的userName参数,并确保正确解码 let userName = options.userName ? decodeURIComponent(options.userName) : ''; // 如果没有传递userName或userName为空,则使用getUserNameById获取默认名称 if (!userName || userName.trim() === '') { userName = this.getUserNameById(targetId); } // 检查是否传递了测试模式参数和是否是客服 const isTestMode = options.isTestMode === 'true'; // 使用AuthManager判断当前用户是否为客服 const isCurrentUserManager = AuthManager.isCustomerService(); // 添加isManager变量定义,用于标识当前用户是否为客服 const isManager = isCurrentUserManager; // 检查目标ID是否为客服ID(数字格式的ID通常是客服managerId) const isTargetManager = /^\d+$/.test(targetId) || options.isManager === 'true'; // 【关键修复】特殊处理真实用户ID,特别是格式为user_1763452685075_rea007flq的用户 let finalUserId = targetId; let finalManagerId = null; let finalConversationId = options.conversationId; const isRealUserId = targetId.startsWith('user_') && targetId.includes('_rea'); // 如果目标是客服,设置managerId if (isTargetManager) { finalManagerId = targetId; console.log('检测到目标为客服,设置managerId:', finalManagerId); } // 尝试从本地存储获取关联的会话ID try { const conversationMap = wx.getStorageSync('conversation_user_map') || {}; if (conversationMap[targetId]) { console.log('从映射表中找到关联的会话ID:', conversationMap[targetId]); finalConversationId = conversationMap[targetId]; } } catch (e) { console.error('获取会话映射失败:', e); } // 如果没有找到会话ID,根据用户类型生成合适的会话ID if (!finalConversationId) { if (isRealUserId) { console.log('检测到真实用户ID格式:', targetId); finalConversationId = `conv_${targetId}_${Date.now()}`; } else { // 为非真实用户生成临时会话ID,包括客服 console.log('生成临时会话ID,目标ID:', targetId, '是否客服:', isTargetManager); finalConversationId = this.generateTempConversationId(targetId, isCurrentUserManager); } console.log('生成新的会话ID:', finalConversationId); // 保存会话ID和用户ID的映射 try { const conversationMap = wx.getStorageSync('conversation_user_map') || {}; conversationMap[targetId] = finalConversationId; wx.setStorageSync('conversation_user_map', conversationMap); } catch (e) { console.error('保存会话映射失败:', e); } } this.setData({ userId: finalUserId, managerId: finalManagerId, // 添加managerId字段 userName: userName, avatar: options.avatar || '', conversationId: finalConversationId, // 添加会话ID isMockMode: false, // 默认使用真实模式,确保消息能发送到服务器 isManager: isManager, // 设置是否是与客服的聊天 isTargetManager: isTargetManager, // 设置目标是否是客服 isRealUserId: isRealUserId // 添加真实用户ID标记 }); console.log('聊天页面初始化完成 - 会话信息:', { userId: this.data.userId, managerId: this.data.managerId, userName: this.data.userName, isManager: this.data.isManager, isTargetManager: this.data.isTargetManager, conversationId: this.data.conversationId, isRealUserId: this.data.isRealUserId }); // 更新导航栏标题 wx.setNavigationBarTitle({ title: userName || '在线联系' }); // 【测试验证】添加用户ID为user_1763452685075_rea007flq的特殊标记 if (finalUserId === 'user_1763452685075_rea007flq') { console.log('=== 检测到目标测试用户ID,启用特殊验证模式 ==='); try { wx.showToast({ title: '已启用真实用户验证模式', icon: 'success', duration: 2000 }); } catch (e) { console.error('显示提示失败:', e); } } // 首先尝试从本地存储加载历史消息 const hasLoadedStoredMessages = this.loadMessagesFromStorage(); // 如果是真实用户且消息列表为空,可以加载测试消息用于验证 if (isRealUserId && !hasLoadedStoredMessages) { console.log('真实用户首次进入,准备初始化测试消息'); // 延迟调用测试方法,避免初始化过程中的冲突 setTimeout(() => { try { this.testRealUserMessageFunctionality(); } catch (e) { console.error('调用测试方法失败:', e); } }, 1000); } // 如果没有历史消息,初始化模拟消息数据 if (!hasLoadedStoredMessages && isTestMode) { this.initMockMessages(); } // 初始化WebSocket连接 this.initWebSocket(); // 显示当前模式提示 wx.showToast({ title: isTestMode ? '开发测试模式' : '真实通信模式', icon: 'none', duration: 2000 }); } }, (error) => { console.warn('身份验证失败,返回上一页:', error); wx.navigateBack(); }); }, /** * 生成临时会话ID * @param {string} targetId - 聊天对象ID * @param {boolean} isManager - 当前是否为客服模式 * @returns {string} 临时会话ID */ generateTempConversationId: function(targetId, isManager) { const app = getApp(); const currentUserId = AuthManager.getUserId() || wx.getStorageSync('userId') || (app.globalData.userInfo?.userId || 'user_' + Date.now()); // 生成基于用户ID和目标ID的临时会话ID // 确保会话ID的唯一性和一致性,保证不同客服的聊天记录独立存储 const ids = [currentUserId, targetId].sort(); // 使用固定前缀,不包含时间戳,确保相同用户间会话ID一致性 return 'temp_' + ids.join('_'); }, /** * 初始化WebSocket连接 */ initWebSocket: function() { // 使用AuthManager获取当前用户ID const currentUserId = AuthManager.getUserId(); const isManager = AuthManager.isCustomerService(); const managerId = AuthManager.getManagerId(); // 保存当前用户ID到本地存储 if (currentUserId) { wx.setStorageSync('userId', currentUserId); } // 确定目标ID:如果目标是客服,使用managerId;否则使用userId const targetId = this.data.isTargetManager ? this.data.managerId : this.data.userId; // 构建WebSocket连接地址 // 注意:在实际部署时,需要替换为真实的WebSocket服务地址 // 这里使用本地服务器地址作为示例 let wsUrl = `ws://localhost:3003/ws?userId=${currentUserId}&targetId=${targetId}&type=chat&userType=${isManager ? 'manager' : 'user'}`; // 如果是客服,添加managerId参数 if (isManager && managerId) { wsUrl += `&managerId=${managerId}`; } // 如果目标是客服,添加目标managerId参数 if (this.data.isTargetManager && this.data.managerId) { wsUrl += `&targetManagerId=${this.data.managerId}`; } console.log('WebSocket连接参数:', { currentUserId, targetId, isManager, managerId, targetManagerId: this.data.managerId }); this.setData({ connectionStatus: 'connecting', connectionMessage: '正在连接服务器...' }); // 设置WebSocket事件监听 this.setupWebSocketListeners(); // 连接WebSocket服务器 socketManager.connect(wsUrl, { maxReconnectAttempts: 5, reconnectInterval: 3000, heartbeatTime: 30000 }); // 监听网络状态变化 this.startNetworkListener(); // 初始化系统稳定性监控 this.initStabilityMonitor(); }, /** * 手动重新连接 */ reconnect: function() { wx.showLoading({ title: '正在重连...', }); // 重置重连计数 socketManager.reconnectAttempts = 0; // 重新初始化WebSocket连接 this.initWebSocket(); setTimeout(() => { wx.hideLoading(); }, 1000); }, /** * 开始网络状态监听 */ startNetworkListener: function() { // 监听网络状态变化 wx.onNetworkStatusChange((res) => { console.log('网络状态变化:', res); if (!res.isConnected) { // 网络断开 this.setData({ connectionStatus: 'error', connectionMessage: '网络已断开' }); // 显示网络断开提示 wx.showToast({ title: '网络连接已断开', icon: 'none', duration: 2000 }); } else if (res.isConnected && this.data.connectionStatus === 'error') { // 网络恢复且之前连接错误,尝试重连 wx.showToast({ title: '网络已恢复,正在重连', icon: 'none', duration: 2000 }); this.reconnect(); } }); }, /** * 设置WebSocket事件监听器 */ setupWebSocketListeners: function() { // 连接成功 socketManager.on('open', () => { const app = getApp(); const isManager = this.data.isManager || (app.globalData.userInfo?.userType === 'manager' || app.globalData.userInfo?.type === 'manager'); // 如果是客服,更新全局用户信息 if (isManager && app.globalData.userInfo) { app.globalData.userInfo.userType = 'manager'; app.globalData.userInfo.type = 'manager'; } this.setData({ connectionStatus: 'connected', connectionMessage: '已连接' }); console.log('WebSocket连接成功', { isManager }); // 发送认证消息 this.sendAuthMessage(); // WebSocket连接成功后,从服务器获取历史消息 console.log('WebSocket连接成功,准备获取历史消息'); // 延迟调用以确保认证完成 setTimeout(() => { console.log('执行fetchHistoryMessages调用'); this.fetchHistoryMessages(); }, 1000); // 增加延迟时间确保认证流程完成 wx.showToast({ title: '连接成功', icon: 'success', duration: 1500 }); }); // 接收消息 socketManager.on('message', (data) => { console.log('收到WebSocket消息:', data); // 处理历史消息列表响应 if (data.type === 'messages_list' && data.payload) { this.handleHistoryMessages(data.payload); } else { this.handleReceivedMessage(data); } }); // 连接关闭 socketManager.on('close', (res) => { if (this.data.connectionStatus !== 'disconnected') { this.setData({ connectionStatus: 'disconnected', connectionMessage: `连接已断开(${res.code || ''})` }); } }); // 连接错误 socketManager.on('error', (error) => { console.error('WebSocket错误:', error); this.setData({ connectionStatus: 'error', connectionMessage: '连接错误' }); }); // 重连中 socketManager.on('reconnecting', (attempts) => { this.setData({ connectionStatus: 'connecting', connectionMessage: `重连中(${attempts}/5)...` }); }); // 重连失败 socketManager.on('reconnectFailed', () => { this.setData({ connectionStatus: 'error', connectionMessage: '重连失败,请检查网络' }); wx.showModal({ title: '连接失败', content: '无法连接到服务器,请检查网络设置后重试。', showCancel: false, confirmText: '确定' }); }); // 监听WebSocket状态更新事件 socketManager.on('status', (status) => { console.log('WebSocket状态更新:', status); this.setData({ connectionMessage: status.message || '状态更新' }); // 根据状态类型更新连接状态 switch(status.type) { case 'connecting': this.setData({ connectionStatus: 'connecting' }); break; case 'connected': this.setData({ connectionStatus: 'connected' }); break; case 'disconnected': this.setData({ connectionStatus: 'disconnected' }); break; case 'error': this.setData({ connectionStatus: 'error' }); if (status.isWarning) { wx.showToast({ title: status.message, icon: 'none', duration: 2000 }); } break; case 'reconnecting': this.setData({ connectionStatus: 'reconnecting' }); break; } }); // 监听消息发送成功事件 socketManager.on('sendSuccess', (data) => { console.log('消息发送成功:', data); // 可以在这里更新UI状态,如显示已发送标记等 }); // 监听消息发送失败事件 socketManager.on('sendError', (error) => { console.error('消息发送失败:', error); wx.showToast({ title: '消息发送失败,请重试', icon: 'none', duration: 2000 }); }); }, /** * 发送认证消息 */ sendAuthMessage: function() { // 严格使用app.globalData.userInfo中的用户ID(从服务器获取的正式ID) const app = getApp(); let currentUserId = ''; // 获取用户ID优先级: // 1. 从全局userInfo获取(已登录用户的正式ID) // 2. 从本地存储的userInfo获取 // 注意:不再生成任何形式的临时ID或本地ID if (app.globalData.userInfo && app.globalData.userInfo.userId) { currentUserId = String(app.globalData.userInfo.userId); console.log('从全局userInfo获取正式用户ID进行认证:', currentUserId); } else { const userInfo = wx.getStorageSync('userInfo'); if (userInfo && userInfo.userId) { currentUserId = String(userInfo.userId); console.log('从本地userInfo获取正式用户ID进行认证:', currentUserId); } else { console.error('警告:未找到有效的用户ID,无法进行WebSocket认证'); return false; } } const userInfo = app.globalData.userInfo || {}; const isManager = this.data.isManager || (userInfo.userType === 'manager' || userInfo.type === 'manager'); // 构建与后端匹配的认证信息格式 const authData = { type: 'auth', payload: { userId: currentUserId, managerId: isManager ? (userInfo.managerId || currentUserId) : null, userName: userInfo.userName || userInfo.name || (isManager ? '客服' : '用户'), userType: isManager ? 2 : 1, // 1=用户,2=客服 conversationId: this.data.conversationId, targetId: this.data.userId } }; console.log('发送认证消息:', authData); // 发送认证消息 socketManager.send({ type: 'auth', data: authData.payload, // 注意:这里直接传递payload对象 success: () => { console.log('认证消息发送成功'); this.setData({ connectionStatus: 'connected', connectionMessage: '已连接' }); // 认证成功后清空消息队列 if (socketManager._flushMessageQueue) { socketManager._flushMessageQueue(); } }, fail: (err) => { console.error('认证消息发送失败:', err); this.setData({ connectionStatus: 'error', connectionMessage: '连接失败,请重试' }); } }); }, /** * 处理接收到的消息 */ handleReceivedMessage: function(data) { console.log('收到消息:', data); // 确保数据格式正确 if (!data || typeof data !== 'object') { console.error('收到无效消息:', data); return; } // 添加消息数据格式验证 if (!data.type) { console.error('消息缺少必要字段:', data); return; } // 根据消息类型处理 if (data.type === 'new_message' && data.payload) { // 处理服务器推送的新消息 this.processServerMessage(data.payload); } else if (data.type === 'message') { // 处理旧格式的普通消息(向后兼容) this.processChatMessage(data); } else if (data.type === 'system') { // 处理系统消息 const systemMessage = { type: 'system', content: data.content || '', time: this.getFormattedTime(), showTime: true }; // 如果是警告消息,添加警告标记 if (data.isWarning) { systemMessage.isWarning = true; } const messages = [...this.data.messages]; messages.push(systemMessage); this.setData({ messages }); // 保存消息到本地存储 this.saveMessagesToStorage(messages); this.scrollToBottom(); } else if (data.type === 'status') { // 处理状态更新消息 this.setData({ connectionMessage: data.message || '连接状态更新' }); wx.showToast({ title: data.message || '状态更新', icon: 'none' }); } else if (data.type === 'message_sent') { // 处理服务器确认消息 console.log('服务器确认消息已送达:', data.payload); } else if (data.type === 'conversation_created') { // 处理会话创建成功响应 console.log('会话创建成功,收到会话ID:', data.conversationId); if (data.conversationId) { // 更新本地会话ID const newConversationId = data.conversationId; this.setData({ conversationId: newConversationId }); console.log('本地会话ID已更新:', newConversationId); // 更新所有本地存储的消息,使用新的会话ID const messages = [...this.data.messages]; const currentUserId = wx.getStorageSync('userId') || ''; // 查找需要重新发送的消息(使用临时会话ID且发送失败的消息) const messagesToResend = []; // 遍历并更新消息中的会话ID messages.forEach(msg => { if (msg.conversationId && msg.conversationId.startsWith('temp_')) { msg.conversationId = newConversationId; // 收集需要重新发送的消息(用户自己发送的,并且不是系统消息) if (msg.senderId === currentUserId && msg.type === 'chat_message') { messagesToResend.push(msg); } } }); // 保存更新后的消息到本地存储 this.saveMessagesToStorage(messages); console.log('需要重新发送的消息数量:', messagesToResend.length); // 导入socketManager const socketManager = require('../../utils/websocket'); // 逐个重新发送消息 messagesToResend.forEach(msg => { // 创建新的消息对象,使用正式会话ID const newMsg = { type: 'chat_message', conversationId: newConversationId, receiverId: msg.receiverId, senderId: currentUserId, userId: currentUserId, senderType: msg.senderType || 2, // 客服类型 content: msg.content, contentType: msg.contentType || 1, timestamp: Date.now(), messageId: 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9) }; console.log('准备重新发送的消息:', newMsg); // 通过WebSocket重新发送消息 socketManager.send(newMsg); }); wx.showToast({ title: '会话已创建', icon: 'success', duration: 1500 }); } } else if (data.type === 'messages_list') { // 处理历史消息列表响应 console.log('收到历史消息列表:', data); this.handleHistoryMessages(data); } else if (data.type === 'error') { // 处理错误消息 console.error('收到服务器错误:', data.message); wx.showToast({ title: '消息发送失败: ' + data.message, icon: 'none', duration: 2000 }); } else { // 处理其他未定义类型的消息 console.log('收到未处理的消息类型:', data.type, data); } }, // 处理聊天消息(旧格式,向后兼容) processChatMessage: function(message) { // 确保消息对象包含必要的字段 const content = message.content || ''; const isImage = message.isImage || false; const app = getApp(); // 增强的用户ID获取逻辑,确保能正确获取当前用户ID const currentUser = wx.getStorageSync('userId') || (app.globalData.userInfo?.userId || '') || AuthManager.getUserId() || ''; const currentUserType = app.globalData.userType || wx.getStorageSync('userType') || 'customer'; const isManager = currentUserType === 'manager' || currentUserType === 'customer_service'; const isCustomer = currentUserType === 'buyer' || currentUserType === 'seller' || currentUserType === 'both'; console.log('处理聊天消息 - 用户类型:', currentUserType, '是否客服:', isManager, '是否普通用户:', isCustomer, '当前用户ID:', currentUser); console.log('消息数据:', { messageFrom: message.from, messageSenderId: message.senderId, messageReceiverId: message.receiverId }); // 确定消息发送方(me或other) let sender = 'other'; // 自己发送的消息始终显示在右侧(me) - 增强的判断逻辑 const isSelfMessage = message.from === currentUser || message.senderId === currentUser || (message.sender_id && message.sender_id === currentUser); if (isSelfMessage) { sender = 'me'; console.log('消息是自己发送的,显示在右侧'); } else { sender = 'other'; console.log('消息是他人发送的,显示在左侧'); } // 客服接收到的发送给自己的消息显示在左侧(other) // 只有当消息不是自己发送的时候,才应用这个规则 if (!isSelfMessage && isManager && message.receiverId && message.receiverId === this.data.userId) { sender = 'other'; console.log('客服接收到的消息,显示在左侧'); } // 【关键修复】针对真实用户ID的特殊处理逻辑 let isRealUserMessage = false; if (this.data.isRealUserId) { // 确保消息关联到正确的用户 console.log('处理真实用户相关消息:', { senderId: message.senderId, receiverId: message.receiverId, targetUserId: this.data.userId, isSelfMessage: sender === 'me' }); // 对于目标测试用户ID的特殊处理 if (this.data.userId === 'user_1763452685075_rea007flq') { // 确保消息被正确关联到该用户 if (message.senderId === 'user_1763452685075_rea007flq' || message.receiverId === 'user_1763452685075_rea007flq') { console.log('✓ 消息与目标测试用户ID匹配'); isRealUserMessage = true; } } } // 创建新消息对象 const newMessage = { id: message.id || 'msg_' + Date.now(), type: 'message', content: content, isImage: isImage, sender: sender, time: message.timestamp ? this.formatTimestampToTime(message.timestamp) : this.getFormattedTime(), showTime: this.shouldShowTime(), shortTime: this.extractShortTime(this.getFormattedTime()), status: 'sent', // 接收的消息默认已发送 senderType: message.senderType || 'unknown', // 保存发送方类型 serverData: message, // 保存完整的服务器数据 // 【关键修复】添加消息来源标记 isRealUserMessage: isRealUserMessage, isRealServerMessage: !!message.messageId || !!message.id }; this.addReceivedMessage(newMessage); }, // 处理服务器推送的新消息 processServerMessage: function(messageData) { console.log('处理服务器推送的新消息:', messageData); // 获取当前用户信息 const app = getApp(); // 增强的用户ID获取逻辑,确保能正确获取当前用户ID const currentUser = wx.getStorageSync('userId') || (app.globalData.userInfo?.userId || '') || AuthManager.getUserId() || ''; const currentUserType = app.globalData.userType || wx.getStorageSync('userType') || 'customer'; const isManager = currentUserType === 'manager' || currentUserType === 'customer_service'; const isCustomer = currentUserType === 'buyer' || currentUserType === 'seller' || currentUserType === 'both'; console.log('处理服务器消息 - 用户类型:', currentUserType, '是否客服:', isManager, '是否普通用户:', isCustomer, '当前用户ID:', currentUser); console.log('消息数据 - senderId:', messageData.senderId, 'receiverId:', messageData.receiverId); // 确定消息发送方(me或other) let sender = 'other'; // 增强的自己发送的消息判断逻辑,支持多种字段名和格式 const isSelfMessage = String(messageData.senderId) === String(currentUser) || String(messageData.sender_id) === String(currentUser) || String(messageData.from) === String(currentUser); if (isSelfMessage) { sender = 'me'; console.log('消息是自己发送的,显示在右侧'); } else { sender = 'other'; console.log('消息是他人发送的,显示在左侧'); } // 【关键修复】针对真实用户ID的特殊处理逻辑 let isRealUserMessage = false; if (this.data.isRealUserId) { // 检查消息是否与当前真实用户关联 if (messageData.senderId === this.data.userId || messageData.receiverId === this.data.userId) { console.log('✓ 消息与当前真实用户ID匹配'); isRealUserMessage = true; // 对于目标测试用户ID的特殊处理 if (this.data.userId === 'user_1763452685075_rea007flq') { console.log('=== 处理目标测试用户的消息 ==='); // 确保消息被正确关联并显示 } } } // 处理消息内容,支持嵌套的data字段 const messageContent = messageData.content || (messageData.data && messageData.data.content) || ''; const contentType = messageData.contentType || (messageData.data && messageData.data.contentType) || 1; const isImage = contentType === 2 || (messageData.fileUrl && contentType !== 1); // 创建符合前端UI要求的消息对象 const newMessage = { id: messageData.messageId || messageData.id || 'msg_' + Date.now(), type: 'message', content: isImage && messageData.fileUrl ? messageData.fileUrl : messageContent, isImage: isImage, sender: sender, senderType: messageData.senderType || 'unknown', // 保存发送方类型 direction: messageData.direction || 'unknown', // 保存消息方向 time: messageData.createdAt ? this.formatServerTime(messageData.createdAt) : this.getFormattedTime(), status: 'sent', serverData: messageData, // 保存完整的服务器数据 // 【关键修复】添加真实用户消息标记 isRealUserMessage: isRealUserMessage, // 【关键修复】添加用于调试和验证的标记 debugInfo: { userId: this.data.userId, isRealUserId: this.data.isRealUserId, matchedUser: isRealUserMessage, messageTimestamp: messageData.createdAt || Date.now() } }; // 特殊处理客服消息显示逻辑 if (isManager && sender === 'other') { console.log('客服收到客户消息:', messageContent); // 确保客服消息可见性 this.ensureManagerMessageVisibility(newMessage); } this.addReceivedMessage(newMessage); }, // 通用的添加接收消息方法 addReceivedMessage: function(newMessage) { // 【关键修复】增强的消息去重逻辑 const existingMessage = this.data.messages.find(msg => { // 多种去重条件 const sameId = msg.id === newMessage.id; const sameServerMessageId = newMessage.serverData && msg.serverData && (msg.serverData.messageId === newMessage.serverData.messageId || msg.serverData.id === newMessage.serverData.id); const sameContentAndTime = msg.content === newMessage.content && msg.time === newMessage.time && msg.sender === newMessage.sender; return sameId || sameServerMessageId || sameContentAndTime; }); if (existingMessage) { console.log('消息已存在,跳过添加:', { id: newMessage.id, content: newMessage.content.substring(0, 20) + '...', senderId: newMessage.serverData?.senderId, receiverId: newMessage.serverData?.receiverId }); return; } const messages = [...this.data.messages]; // 确定是否显示时间 let showTime = false; if (messages.length === 0) { showTime = true; } else { const lastMessage = messages[messages.length - 1]; const currentTime = this.parseMessageTime(newMessage.time); const lastTime = this.parseMessageTime(lastMessage.time); // 如果时间相差超过5分钟,则显示时间 if (Math.abs(currentTime - lastTime) > 5 * 60 * 1000) { showTime = true; } } newMessage.showTime = showTime; newMessage.shortTime = this.extractShortTime(newMessage.time); // 【关键修复】针对真实用户消息的特殊处理 if (newMessage.isRealUserMessage || this.data.isRealUserId) { console.log('添加真实用户相关消息:', { id: newMessage.id, isRealUserMessage: newMessage.isRealUserMessage, currentUserId: this.data.userId, messageSenderId: newMessage.serverData?.senderId, messageReceiverId: newMessage.serverData?.receiverId }); // 对于目标测试用户的消息,添加可视化标记 if (this.data.userId === 'user_1763452685075_rea007flq') { // 添加测试标记 newMessage.testMarker = 'REAL_USER_MESSAGE'; console.log('✓ 已为目标测试用户消息添加标记'); } } // 添加到消息列表 messages.push(newMessage); // 保存到本地存储 this.saveMessagesToStorage(messages); // 更新页面数据 this.setData({ messages }); // 滚动到底部 this.scrollToBottom(); // 如果是新消息,触发通知更新消息列表 if (newMessage.sender === 'other') { try { wx.vibrateShort(); // 轻微震动提示 const app = getApp(); if (app.globalData.onNewMessage) { app.globalData.onNewMessage(newMessage); } } catch (e) { console.error('触发新消息事件失败:', e); } } // 强制更新消息列表,确保聊天记录能在消息中心显示 this.updateMessageListGlobal(); console.log('添加新消息成功:', { id: newMessage.id, content: newMessage.content.substring(0, 20) + '...', sender: newMessage.sender, isRealUserMessage: newMessage.isRealUserMessage }); }, /** * 验证真实用户消息功能的测试方法 * 用于手动测试系统对真实用户消息的处理能力 */ testRealUserMessageFunctionality: function() { console.log('=== 开始真实用户消息功能测试 ==='); try { // 测试模拟消息 const testMessage = { id: 'test_msg_' + Date.now(), type: 'message', content: '这是一条测试消息,用于验证真实用户消息功能', sender: 'other', time: this.getFormattedTime(), shortTime: this.extractShortTime(this.getFormattedTime()), status: 'sent', isRealUserMessage: true, serverData: { messageId: 'test_' + Date.now(), senderId: this.data.userId, receiverId: wx.getStorageSync('userId'), createdAt: Date.now() }, debugInfo: { testMode: true, testTime: Date.now() } }; // 添加测试消息 this.addReceivedMessage(testMessage); // 显示测试成功提示 wx.showToast({ title: '真实用户消息测试完成', icon: 'success', duration: 2000 }); console.log('=== 真实用户消息功能测试完成 ==='); return true; } catch (e) { console.error('测试过程中出现错误:', e); wx.showToast({ title: '测试失败: ' + e.message, icon: 'none', duration: 3000 }); return false; } }, /** * 系统稳定性综合检查 * 用于检测和修复潜在的系统稳定性问题 */ performStabilityCheck: function() { console.log('=== 开始系统稳定性检查 ==='); try { // 1. 检查必要的数据结构是否存在 if (!this.data || !Array.isArray(this.data.messages)) { console.warn('消息列表结构异常,重新初始化'); this.setData({ messages: [] }); } // 2. 清理无效的消息数据 const validMessages = this.data.messages.filter(msg => { if (!msg || typeof msg !== 'object') { console.warn('检测到无效消息对象,已过滤'); return false; } // 确保消息至少有ID和内容 if (!msg.id || !msg.content && !msg.isImage) { console.warn('消息缺少必要字段,已过滤:', msg); return false; } return true; }); // 如果过滤掉了无效消息,更新数据 if (validMessages.length !== this.data.messages.length) { console.log(`清理了 ${this.data.messages.length - validMessages.length} 条无效消息`); this.setData({ messages: validMessages }); // 保存清理后的消息列表 this.saveMessagesToStorage(validMessages); } // 3. 检查WebSocket连接状态 const socketManager = require('../../utils/websocket.js').default; const isConnected = socketManager.getConnectionStatus() || false; if (!isConnected) { console.warn('WebSocket连接异常,准备重新连接'); // 如果不在模拟模式且WebSocket未连接,尝试重连 if (!this.data.isMockMode) { setTimeout(() => { try { this.reconnect(); } catch (reconnectError) { console.error('WebSocket重连失败:', reconnectError); } }, 1000); } } // 4. 检查本地存储完整性 try { const storedMessages = wx.getStorageSync(`chat_messages_${this.data.conversationId}`); if (storedMessages && storedMessages.length !== this.data.messages.length) { console.log('本地存储与内存消息数量不一致,同步中...'); this.saveMessagesToStorage(this.data.messages); } } catch (storageError) { console.error('本地存储检查失败:', storageError); // 尝试清理可能损坏的存储 try { wx.removeStorageSync(`chat_messages_${this.data.conversationId}`); console.log('已清理可能损坏的本地存储'); } catch (e) { console.error('清理本地存储失败:', e); } } // 5. 检查会话ID和用户ID的有效性 if (!this.data.conversationId || !this.data.userId) { console.error('会话ID或用户ID缺失,系统可能无法正常工作'); // 尝试重新生成会话ID if (!this.data.conversationId) { const newConversationId = this.generateTempConversationId(this.data.userId || 'unknown', this.data.isManager); this.setData({ conversationId: newConversationId }); console.log('已重新生成会话ID:', newConversationId); } } // 6. 检查消息发送队列 if (this.pendingMessages && this.pendingMessages.length > 0) { console.log(`检测到 ${this.pendingMessages.length} 条待发送消息`); // 尝试重新发送 this.retryPendingMessages(); } console.log('=== 系统稳定性检查完成 ==='); return true; } catch (e) { console.error('稳定性检查过程中出现错误:', e); return false; } }, /** * 重试发送队列中的待发消息 */ retryPendingMessages: function() { if (!this.pendingMessages || this.pendingMessages.length === 0) { return; } console.log('开始重试发送待发消息...'); try { // 检查WebSocket连接状态 const socketManager = require('../../utils/websocket.js').default; const isConnected = socketManager.getConnectionStatus() || false; if (!isConnected) { console.warn('WebSocket未连接,延迟重试'); setTimeout(() => this.retryPendingMessages(), 2000); return; } // 复制队列进行处理 const messagesToRetry = [...this.pendingMessages]; this.pendingMessages = []; // 逐个重新发送消息 messagesToRetry.forEach((message, index) => { setTimeout(() => { try { console.log(`重试发送消息[${index + 1}/${messagesToRetry.length}]:`, message.content.substring(0, 20) + '...'); // 获取用户信息确定用户类型 const userInfo = getApp().globalData.userInfo || wx.getStorageSync('userInfo') || {}; const isManager = userInfo.userType === 'manager' || userInfo.type === 'manager'; const senderId = wx.getStorageSync('userId'); // 重新构建消息格式,使用正确的格式并包含managerId const wsMessage = { type: 'chat_message', // 使用正确的消息类型 content: message.content, contentType: message.isImage ? 2 : 1, senderId: senderId, receiverId: message.targetUserId || this.data.managerId || this.data.userId, // 关键修复:在重试消息时也包含managerId managerId: this.data.isTargetManager && !isManager ? (this.data.managerId || message.targetUserId) : undefined, userId: isManager ? (message.targetUserId || this.data.userId) : senderId, conversationId: this.data.conversationId, messageId: message.id || 'msg_' + Date.now(), timestamp: Date.now() }; // 发送消息 const socketManager = require('../../utils/websocket.js').default; socketManager.send(wsMessage); // 更新原始消息状态 this.updateMessageStatus(message.id, 'sending'); } catch (e) { console.error(`重试发送消息失败[${index}]:`, e); // 将失败的消息重新加入队列 if (!this.pendingMessages) this.pendingMessages = []; this.pendingMessages.push(message); } }, index * 1000); // 每条消息间隔1秒发送,避免消息堆积 }); console.log('待发消息重试处理完成'); } catch (e) { console.error('重试消息队列处理失败:', e); } }, /** * 初始化系统稳定性监控 */ initStabilityMonitor: function() { console.log('初始化系统稳定性监控...'); try { // 简化的稳定性监控,只保留定期检查 this.stabilityCheckInterval = setInterval(() => { this.performStabilityCheck(); }, 60000); // 每分钟执行一次稳定性检查 // 首次执行稳定性检查 this.performStabilityCheck(); } catch (error) { console.error('初始化稳定性监控失败:', error); console.error('Error stack:', error.stack); } }, /** * 强制更新全局消息列表 */ updateMessageListGlobal: function() { try { console.log('强制更新全局消息列表'); // 触发全局事件,通知消息列表页面更新 const app = getApp(); if (app.globalData.onNewMessage) { app.globalData.onNewMessage({ userId: this.data.userId, userName: this.data.userName }); } // 直接调用消息列表页面的loadChatList方法(如果能访问到) // 这里我们通过重新加载存储来确保数据同步 console.log('消息已保存到存储,消息列表页面下次显示时会自动刷新'); } catch (e) { console.error('更新全局消息列表失败:', e); } }, // 格式化服务器时间 formatServerTime: function(serverTime) { const date = new Date(serverTime); const month = date.getMonth() + 1; const day = date.getDate(); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${month}-${day} ${hours}:${minutes}`; }, // 解析消息时间字符串为时间戳 parseMessageTime: function(timeStr) { if (!timeStr) return Date.now(); // 处理iOS不支持的"月-日 时:分"格式 const dateTimeMatch = timeStr.match(/^(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{2})$/); if (dateTimeMatch) { const month = dateTimeMatch[1].padStart(2, '0'); const day = dateTimeMatch[2].padStart(2, '0'); const hours = dateTimeMatch[3].padStart(2, '0'); const minutes = dateTimeMatch[4]; const year = new Date().getFullYear(); // 使用当前年份 // 转换为iOS支持的格式 const iosCompatibleDate = `${year}-${month}-${day}T${hours}:${minutes}:00`; return new Date(iosCompatibleDate).getTime(); } // 对于其他格式,尝试直接解析 return new Date(timeStr).getTime(); }, // 提取短时间格式 (HH:MM) extractShortTime: function(timeStr) { if (!timeStr) return this.getShortTime(); // 如果时间格式包含小时分钟,直接提取 const timeMatch = timeStr.match(/(\d{1,2}):(\d{2})/); if (timeMatch) { return `${timeMatch[1].padStart(2, '0')}:${timeMatch[2]}`; } return this.getShortTime(); }, /** * 将时间戳格式化为时间字符串 */ formatTimestampToTime: function(timestamp) { const date = new Date(timestamp); const month = date.getMonth() + 1; const day = date.getDate(); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${month}-${day} ${hours}:${minutes}`; }, /** * 判断是否应该显示消息时间 */ shouldShowTime: function() { const messages = this.data.messages; if (messages.length === 0) { return true; } const lastMessage = messages[messages.length - 1]; const currentTime = this.parseMessageTime(this.getFormattedTime()); const lastTime = this.parseMessageTime(lastMessage.time); // 如果时间差超过5分钟,显示时间 return currentTime - lastTime > 5 * 60 * 1000; }, // 初始化模拟消息数据 initMockMessages: function() { // 只有从客服列表页面进入(isManager=true)且没有历史消息时,才显示开场白 if (this.data.isManager) { // 获取当前时间作为消息时间 const currentTime = this.getFormattedTime(); // 模拟历史消息,适用于鸡蛋交易平台 const mockMessages = [ { type: 'message', sender: 'other', content: '您好!欢迎来到鸡蛋交易平台,我是' + this.data.userName + ',请问有什么可以帮助您的吗?', time: currentTime }, ]; // 处理消息时间显示逻辑 const processedMessages = this.processMessageTimes(mockMessages); this.setData({ messages: processedMessages }); // 滚动到底部 this.scrollToBottom(); } else { // 从消息中心进入时,不显示开场白,设置空消息列表 this.setData({ messages: [] }); } }, // 输入框内容变化 onInput: function(e) { this.setData({ inputValue: e.detail.value }); }, /** * 优化消息数据结构,移除不必要的字段 */ optimizeMessagesForStorage: function(messages) { // 限制每条聊天最多保存1000条消息 const MAX_MESSAGES_PER_CHAT = 1000; let optimizedMessages = messages.slice(-MAX_MESSAGES_PER_CHAT); // 移除不必要的字段,只保留核心数据 return optimizedMessages.map(msg => ({ type: msg.type, sender: msg.sender, content: msg.content, time: msg.time, showTime: msg.showTime // 移除shortTime字段,因为可以从time字段中提取 })); }, /** * 保存消息到本地存储 */ saveMessagesToStorage: function(messages) { try { // 优化消息数据结构 const optimizedMessages = this.optimizeMessagesForStorage(messages); // 获取当前聊天对象的标识 const chatIdentifier = this.data.userId; const chatKey = `chat_messages_${chatIdentifier}`; // 保存消息到当前聊天的存储 wx.setStorageSync(chatKey, optimizedMessages); console.log('消息已优化并保存到本地存储,当前消息数量:', optimizedMessages.length); console.log('存储键名:', chatKey); // 获取应用实例和用户信息 const app = getApp(); const currentUserId = wx.getStorageSync('userId') || ''; const isCurrentUserManager = app.globalData.userInfo?.userType === 'manager' || app.globalData.userInfo?.type === 'manager'; const currentManagerId = app.globalData.userInfo?.managerId || ''; // 确保双向消息同步 // 1. 对于客服发送的消息,确保创建反向记录 if (isCurrentUserManager && currentManagerId && currentManagerId !== chatIdentifier) { this.ensureManagerMessageVisibility(chatIdentifier, currentManagerId, messages); } // 2. 对于普通用户发送给客服的消息,创建特殊的反向记录 if (!isCurrentUserManager && currentUserId && currentUserId !== chatIdentifier) { // 创建反向消息记录(客服视角) const reversedKey = `chat_messages_${currentUserId}`; const reversedMessages = wx.getStorageSync(reversedKey) || []; // 获取用户发送的最新消息 const userMessages = messages.filter(msg => msg.sender === 'me'); if (userMessages.length > 0) { const latestUserMessage = userMessages[userMessages.length - 1]; // 检查消息是否已存在 const isMessageExists = reversedMessages.some(msg => msg.time === latestUserMessage.time && msg.sender === 'other' ); if (!isMessageExists) { // 创建反向消息(从客服视角看是对方发送的) const reversedMessage = { type: 'message', sender: 'other', content: latestUserMessage.content, time: latestUserMessage.time, showTime: reversedMessages.length === 0, shortTime: latestUserMessage.shortTime }; reversedMessages.push(reversedMessage); const optimizedReversed = this.optimizeMessagesForStorage(reversedMessages); wx.setStorageSync(reversedKey, optimizedReversed); console.log('已为用户创建反向消息记录,确保客服能看到'); } } } // 强制更新全局消息列表 this.updateMessageListGlobal(); } catch (e) { console.error('保存消息到本地存储失败:', e); } }, /** * 确保客服能看到自己发送的消息 * 当客服向其他用户发送消息时,确保消息也能在自己的消息列表中显示 */ ensureManagerMessageVisibility: function(targetId, currentManagerId, messages) { try { // 从消息中提取该客服发送的最新消息 const managerMessages = messages.filter(msg => msg.sender === 'me'); if (managerMessages.length > 0) { // 获取最新的客服发送消息 const latestMessage = managerMessages[managerMessages.length - 1]; // 创建反向的消息记录(客服视角) const reversedKey = `chat_messages_${targetId}`; let reversedMessages = wx.getStorageSync(reversedKey) || []; // 检查这条消息是否已经存在,避免重复添加 const isMessageExists = reversedMessages.some(msg => msg.time === latestMessage.time && msg.sender === 'me' ); if (!isMessageExists) { // 添加新消息到反向消息列表 const newReversedMessage = { ...latestMessage }; // 设置是否显示时间 newReversedMessage.showTime = reversedMessages.length === 0; reversedMessages.push(newReversedMessage); // 保存反向消息记录 const optimizedReversedMessages = this.optimizeMessagesForStorage(reversedMessages); wx.setStorageSync(reversedKey, optimizedReversedMessages); console.log('已为客服创建反向消息记录,确保消息可见性'); } // 更新消息列表 this.updateMessageListGlobal(); } } catch (e) { console.error('确保客服消息可见性失败:', e); } }, /** * 清理指定聊天的历史消息 */ clearChatHistory: function() { try { wx.removeStorageSync(`chat_messages_${this.data.userId}`); this.setData({ messages: [] }); console.log('聊天历史已清空'); wx.showToast({ title: '聊天历史已清空', icon: 'success' }); } catch (e) { console.error('清空聊天历史失败:', e); wx.showToast({ title: '清空失败', icon: 'none' }); } }, // 发送消息 sendMessage: function() { // 执行身份验证 AuthManager.authenticate(() => { if (!this.data.inputValue.trim()) { wx.showToast({ title: '消息内容不能为空', icon: 'none' }); return; } // 创建消息对象 const newMessage = { type: 'message', sender: 'me', content: this.data.inputValue.trim(), time: this.getFormattedTime(), status: 'sending', // 初始状态为发送中 // 【关键修复】添加真实用户ID相关字段 targetUserId: this.data.userId }; console.log('开始发送消息,目标用户ID:', this.data.userId, '会话ID:', this.data.conversationId); // 添加新消息到消息列表 const messages = [...this.data.messages]; // 设置是否显示新消息的时间 let showTime = false; if (messages.length === 0) { showTime = true; } else { const lastMessage = messages[messages.length - 1]; const currentTime = this.parseMessageTime(this.getFormattedTime()); const lastTime = this.parseMessageTime(lastMessage.time); // 如果时间差超过5分钟,显示时间 if (currentTime - lastTime > 5 * 60 * 1000) { showTime = true; } } newMessage.showTime = showTime; newMessage.shortTime = this.getShortTime(); messages.push(newMessage); // 立即更新UI this.setData({ messages: messages, inputValue: '' }); // 保存消息到本地存储 this.saveMessagesToStorage(messages); this.scrollToBottom(); // 会话ID检查和处理 if (!this.data.conversationId || this.data.conversationId.startsWith('temp_')) { console.warn('会话ID无效或为临时ID:', this.data.conversationId); // 使用标准的会话创建流程,确保使用服务器返回的正式用户ID const app = getApp(); const userInfo = app.globalData.userInfo || wx.getStorageSync('userInfo') || {}; const isManager = userInfo.userType === 'manager' || userInfo.type === 'manager'; const currentUserId = userInfo.userId ? String(userInfo.userId) : ''; // 关键修复:根据是否是客服对话使用正确的目标ID // 优先使用managerId作为聊天对象ID(当目标是客服时) let chatTargetId; if (this.data.isTargetManager && this.data.managerId) { chatTargetId = this.data.managerId; } else { chatTargetId = this.data.userId; } // 确保chatTargetId是有效的客服ID,不是字符串'user' if (chatTargetId === 'user' || !chatTargetId) { console.error('无效的客服ID,无法创建会话:', chatTargetId); return; } if (currentUserId && chatTargetId) { console.log('尝试创建正式会话...'); // 发送创建会话请求,使用正确的格式 const createConversationMessage = { type: 'create_conversation', userId: isManager ? chatTargetId : currentUserId, // 用户ID始终是普通用户的ID managerId: isManager ? currentUserId : chatTargetId, // 客服ID始终是manager的ID timestamp: Date.now() }; console.log('发送创建会话请求:', createConversationMessage); // 使用增强的消息发送函数,确保消息去重和字段验证 socketManager.sendEnhancedMessage(createConversationMessage); } } // 根据模式决定发送方式 if (this.data.isMockMode) { // 模拟模式:仅用于开发测试,消息不会真正发送 console.log('模拟模式:消息未通过WebSocket发送', newMessage); // 更新消息状态为已发送(模拟) setTimeout(() => { this.updateMessageStatus(messages.length - 1, 'sent'); }, 500); wx.showToast({ title: '测试模式:消息仅在本地显示', icon: 'none', duration: 1500 }); } else { // 真实模式:通过WebSocket发送消息 // 【关键修复】确保消息对象包含正确的会话ID和目标用户ID newMessage.conversationId = this.data.conversationId; newMessage.targetUserId = this.data.userId; // 【关键修复】特殊处理真实用户ID(例如user_1763452685075_rea007flq) if (this.data.userId && this.data.userId.startsWith('user_') && this.data.userId.includes('_rea')) { console.log('检测到真实用户ID,确保正确发送消息...'); // 确保消息发送时优先使用这个真实用户ID newMessage.priorityTargetId = this.data.userId; } const sent = this.sendWebSocketMessage(newMessage); // 更新消息状态为已发送 setTimeout(() => { this.updateMessageStatus(messages.length - 1, sent ? 'sent' : 'failed'); }, 500); } console.log('消息发送请求已触发:', { content: newMessage.content, conversationId: this.data.conversationId }); }); }, /** * 更新消息状态 * @param {number} index - 消息索引 * @param {string} status - 状态值(sending/sent/failed) */ updateMessageStatus: function(index, status) { const messages = [...this.data.messages]; if (messages[index]) { messages[index].status = status; this.setData({ messages }); this.saveMessagesToStorage(messages); } }, /** * 通过WebSocket发送消息 */ sendWebSocketMessage: function(message) { const app = getApp(); // 获取用户信息和确定用户类型 const userInfo = app.globalData.userInfo || wx.getStorageSync('userInfo') || {}; const isManager = userInfo.userType === 'manager' || userInfo.type === 'manager'; // 1. 获取当前用户ID(严格使用users表中的userId) let currentUserId = ''; if (userInfo.userId) { currentUserId = String(userInfo.userId); } else { console.error('严重错误:未找到有效的用户ID,请确保用户已授权登录'); wx.showToast({ title: '请先登录再发送消息', icon: 'none', duration: 2000 }); return false; } // 2. 确定聊天对象ID - 【修复】根据是否是客服对话使用正确的ID let chatTargetId; // 优先使用managerId作为聊天对象ID(当目标是客服时) if (this.data.isTargetManager && this.data.managerId) { chatTargetId = this.data.managerId; } else { chatTargetId = this.data.userId; } // 3. 根据用户类型设置正确的发送者和接收者信息 let senderId, receiverId, senderType; if (isManager) { // 客服模式: // - sender_id 应该是 managerId // - receiver_id 应该是 userId // - sender_type 应该是 manager senderId = currentUserId; receiverId = chatTargetId; // 聊天对象是用户ID senderType = 'manager'; // 使用字符串类型与后端保持一致 } else { // 用户模式: // - sender_id 应该是 userId // - receiver_id 应该是 managerId // - sender_type 应该是 customer/buyer/seller/both senderId = currentUserId; // 【关键修复】确保当聊天对象是客服时,使用managerId作为接收者ID // 在用户向客服发送消息的场景下,接收者必须是客服ID,不能使用userId if (this.data.isTargetManager) { // 优先使用页面中已确定的managerId if (this.data.managerId) { receiverId = this.data.managerId; console.log('确认使用页面中的managerId作为接收者ID:', receiverId); } else if (chatTargetId) { // 如果managerId未设置但有chatTargetId,使用chatTargetId作为receiverId receiverId = chatTargetId; console.log('使用chatTargetId作为接收者ID:', receiverId); } else { // 如果都为空,这是一个错误状态,需要提示 console.error('严重错误:向客服发送消息时未找到有效的接收者ID(客服ID)'); receiverId = ''; // 保持为空以便后续逻辑能够正确处理错误 } } else { // 非客服对话,使用chatTargetId receiverId = chatTargetId || ''; } // 从用户信息中获取类型,默认为customer senderType = userInfo.type || 'customer'; } console.log('发送消息 - 模式:', isManager ? '客服' : '用户'); console.log('- senderId:', senderId); console.log('- receiverId:', receiverId); console.log('- senderType:', senderType); // 检查会话ID是否是临时的,如果是,需要先创建正式会话 if (this.data.conversationId && this.data.conversationId.startsWith('temp_')) { console.log('检测到临时会话ID,需要先创建正式会话'); // 构建创建会话的请求,确保userId和managerId不为空 // 【修复】:根据ID特征来判断用户类型,而不是简单依赖isManager标志 // 假设managerId通常是数字,而userId通常是字符串格式(如user_开头) let userId = ''; let managerId = ''; // 判断当前用户是否为客服 if (isManager) { // 客服模式: // - 当前用户是客服,ID应该作为managerId // - 聊天对象是用户,ID应该作为userId managerId = senderId; userId = receiverId; } else { // 用户模式: // - 当前用户是普通用户,ID应该作为userId // - 聊天对象是客服,ID应该作为managerId userId = senderId; managerId = receiverId; } // 额外验证:确保userId和managerId符合预期格式 // 如果userId是纯数字且managerId不是纯数字,则交换它们 if (/^\d+$/.test(userId) && !/^\d+$/.test(managerId)) { console.warn('检测到可能的字段混淆,交换userId和managerId'); const temp = userId; userId = managerId; managerId = temp; } // 安全检查:确保userId不为空 if (!userId) { // 尝试从全局数据或本地存储获取用户ID userId = app.globalData.userInfo?.userId || wx.getStorageSync('userId') || ''; console.warn('userId为空,尝试从其他来源获取:', userId); } // 安全检查:确保managerId不为空 if (!managerId) { // 对于普通用户,尝试从页面数据获取客服ID if (!isManager) { managerId = this.data.userId || ''; } else { // 对于客服,使用自己的ID managerId = senderId || ''; } console.warn('managerId为空,尝试从其他来源获取:', managerId); } // 不再在此处重复创建会话,已在sendMessage方法中处理 // 会话创建逻辑统一由sendMessage方法中的enhancedMessage处理 } // 验证连接状态 const connectionStatus = socketManager.getConnectionStatus(); console.log('发送消息前的连接状态:', connectionStatus); if (connectionStatus) { // 构建符合服务器要求的标准消息格式 const wsMessage = { type: 'chat_message', conversationId: this.data.conversationId, receiverId: receiverId, // 接收者ID - 关键修复:确保正确设置 senderId: senderId, // 发送者ID - 关键修复:确保正确设置 // 确保字段清晰明确,避免混淆 // 用户向客服发送消息时: // - senderId: 用户ID // - receiverId: 客服ID // - userId: 用户ID // - managerId: 客服ID userId: isManager ? receiverId : senderId, // 始终是普通用户的ID managerId: isManager ? senderId : receiverId, // 始终是客服的ID senderType: senderType, // 发送者类型 content: message.content, contentType: message.isImage ? 2 : 1, // 1:文本 2:图片 timestamp: Date.now(), messageId: 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9) }; // 额外验证:确保userId和managerId符合预期格式 if (wsMessage.userId && wsMessage.managerId && /^\d+$/.test(wsMessage.userId) && !/^\d+$/.test(wsMessage.managerId)) { console.warn('检测到聊天消息中可能的字段混淆,交换userId和managerId'); const temp = wsMessage.userId; wsMessage.userId = wsMessage.managerId; wsMessage.managerId = temp; } // 确保contentType是数字类型 wsMessage.contentType = Number(wsMessage.contentType); // 如果是图片消息,添加URL if (message.isImage && message.content.startsWith('http')) { wsMessage.fileUrl = message.content; } console.log('最终消息对象:', JSON.stringify(wsMessage)); const sent = socketManager.send(wsMessage); if (sent) { // 添加发送成功的提示 wx.showToast({ title: '消息已发送', icon: 'success', duration: 1000 }); return true; } else { console.error('消息发送失败,可能是队列已满或连接断开'); wx.showToast({ title: '消息发送失败,已加入重试队列', icon: 'none', duration: 2000 }); return false; } } else { console.error('WebSocket未连接,无法发送消息'); wx.showToast({ title: '连接未建立,发送失败', icon: 'none', duration: 1500 }); // 尝试重新连接 console.log('尝试重新初始化WebSocket连接...'); this.initWebSocket(); // 3秒后检查连接状态并尝试重新发送 setTimeout(() => { const newConnectionStatus = socketManager.getConnectionStatus(); if (newConnectionStatus) { console.log('WebSocket重连成功,尝试重新发送消息'); // 重发消息时使用相同的格式 const resendMessage = { type: 'chat_message', conversationId: this.data.conversationId, receiverId: receiverId, senderId: senderId, userId: isManager ? receiverId : senderId, senderType: senderType, content: message.content, contentType: message.isImage ? 2 : 1, timestamp: Date.now(), messageId: 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9) }; if (message.isImage && message.content.startsWith('http')) { resendMessage.fileUrl = message.content; } socketManager.send(resendMessage); } }, 3000); return false; } }, /** * 模拟对方回复(用于开发测试) */ simulateReply: function() { setTimeout(() => { const replyMessage = { type: 'message', sender: 'other', content: this.getRandomReply(), time: this.getFormattedTime(), showTime: false, // 回复消息通常不显示时间 shortTime: this.getShortTime() }; const messages = [...this.data.messages]; messages.push(replyMessage); this.setData({ messages: messages }); // 保存消息到本地存储 this.saveMessagesToStorage(messages); this.scrollToBottom(); }, 1000); }, // 获取格式化时间(带日期) getFormattedTime: function() { const now = new Date(); const month = now.getMonth() + 1; const date = now.getDate(); const hours = now.getHours().toString().padStart(2, '0'); const minutes = now.getMinutes().toString().padStart(2, '0'); return `${month}-${date} ${hours}:${minutes}`; }, // 获取仅包含时分的时间格式 getShortTime: function() { const now = new Date(); const hours = now.getHours().toString().padStart(2, '0'); const minutes = now.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; }, // 处理消息时间显示逻辑 processMessageTimes: function(messages) { return messages.map((message, index) => { // 复制消息对象,避免直接修改原数据 const processedMessage = { ...message }; // 设置是否显示消息时间 processedMessage.showTime = false; // 第一条消息总是显示时间 if (index === 0) { processedMessage.showTime = true; } else { // 与上一条消息比较时间差 const currentTime = this.parseMessageTime(message.time); const previousTime = this.parseMessageTime(messages[index - 1].time); // 如果时间差超过5分钟,显示时间 if (currentTime - previousTime > 5 * 60 * 1000) { processedMessage.showTime = true; } } // 为每条消息添加短时间格式(用于消息气泡旁显示) processedMessage.shortTime = this.extractShortTime(message.time); return processedMessage; }); }, // 获取随机回复 getRandomReply: function() { const replies = [ '好的,了解了', '稍等,我马上回复', '这个没问题', '您还有其他问题吗?', '好的,我知道了' ]; return replies[Math.floor(Math.random() * replies.length)]; }, // 预览图片 - 使用微信插件进行预览 previewImage: function(e) { let imageUrl = e.currentTarget.dataset.src; // 处理URL格式,确保能正确预览 if (typeof imageUrl === 'string') { // 移除可能的引号 imageUrl = imageUrl.replace(/^["']|["']$/g, ''); // 检查URL协议 const isHttpProtocol = imageUrl.startsWith('http://') || imageUrl.startsWith('https://'); const isWxfileProtocol = imageUrl.startsWith('wxfile://'); // 如果URL缺少协议且不是wxfile协议,添加https前缀 if (!isHttpProtocol && !isWxfileProtocol) { imageUrl = 'https://' + imageUrl; } } // 使用微信原生预览功能 wx.previewImage({ current: imageUrl, // 当前显示图片的链接 urls: [imageUrl], // 需要预览的图片链接列表 showmenu: true, // 显示菜单,允许用户保存图片等操作 success: function() { console.log('图片预览成功'); }, fail: function(error) { console.error('图片预览失败:', error); wx.showToast({ title: '图片预览失败', icon: 'none' }); } }); }, // 滚动到底部 scrollToBottom: function() { setTimeout(() => { wx.createSelectorQuery().select('#message-container').boundingClientRect(res => { if (res) { wx.createSelectorQuery().select('#message-list').scrollOffset(res => { if (res) { wx.createSelectorQuery().select('#message-list').context(context => { context.scrollTo({ scrollTop: res.scrollHeight, animated: true }); }).exec(); } }).exec(); } }).exec(); }, 100); }, // 显示表情 showEmoji: function() { // 表情功能占位符 wx.showToast({ title: '表情功能开发中', icon: 'none' }); }, // 切换语音模式 toggleVoiceMode: function() { // 语音模式切换功能占位符 wx.showToast({ title: '语音功能开发中', icon: 'none' }); }, // 显示更多操作 showMoreOptions: function() { wx.showActionSheet({ itemList: ['发送图片', '发送位置'], success: (res) => { if (!res.cancel) { if (res.tapIndex === 0) { // 发送图片 wx.chooseImage({ count: 1, success: (res) => { console.log('选择的图片路径:', res.tempFilePaths[0]); this.sendImageMessage(res.tempFilePaths[0]); } }); } else if (res.tapIndex === 1) { // 发送位置 wx.chooseLocation({ success: function(res) { console.log('选择的位置:', res); // 这里可以添加发送位置的逻辑 } }); } } } }); }, // 发送图片消息 sendImageMessage: function(imagePath) { const newMessage = { type: 'message', sender: 'me', content: imagePath, time: this.getFormattedTime(), isImage: true // 标记为图片消息 }; // 添加新消息到消息列表 const messages = [...this.data.messages]; // 设置是否显示新消息的时间 let showTime = false; if (messages.length === 0) { showTime = true; } else { const lastMessage = messages[messages.length - 1]; const currentTime = this.parseMessageTime(this.getFormattedTime()); const lastTime = this.parseMessageTime(lastMessage.time); // 如果时间差超过5分钟,显示时间 if (currentTime - lastTime > 5 * 60 * 1000) { showTime = true; } } newMessage.showTime = showTime; newMessage.shortTime = this.getShortTime(); messages.push(newMessage); // 立即更新UI this.setData({ messages: messages }); // 保存消息到本地存储 this.saveMessagesToStorage(messages); this.scrollToBottom(); // 根据模式决定发送方式 if (this.data.isMockMode) { // 模拟模式:不再自动回复,让用户和客服可以真正沟通 // 移除了 this.simulateReply() 调用 } else { // WebSocket模式:通过WebSocket发送消息(实际项目中需要先上传图片获取URL) this.sendWebSocketMessage(newMessage); } }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { }, /** * 生命周期函数--监听页面显示 */ onShow: function () { }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { }, /** * 切换模拟模式 */ toggleMockMode: function(e) { const checked = e.detail.value; wx.showLoading({ title: checked ? '切换到WebSocket模式...' : '切换到模拟模式...', }); // 如果是切换到模拟模式 if (!checked) { // 清理WebSocket连接和监听器 socketManager.off('open'); socketManager.off('message'); socketManager.off('close'); socketManager.off('error'); socketManager.off('reconnecting'); socketManager.off('reconnectFailed'); // 移除网络状态监听 wx.offNetworkStatusChange(); // 可选:关闭连接 socketManager.close(); this.setData({ isMockMode: true, connectionStatus: '', connectionMessage: '' }); } else { // 切换到WebSocket模式 this.setData({ isMockMode: false }); // 初始化WebSocket连接 this.initWebSocket(); } setTimeout(() => { wx.hideLoading(); wx.showToast({ title: checked ? '已切换到WebSocket模式' : '已切换到模拟模式', icon: 'none', duration: 1500 }); }, 1000); }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { // 清理WebSocket连接和监听器 if (!this.data.isMockMode) { socketManager.off('open'); socketManager.off('message'); socketManager.off('close'); socketManager.off('error'); socketManager.off('reconnecting'); socketManager.off('reconnectFailed'); // 移除网络状态监听 wx.offNetworkStatusChange(); // 清理稳定性检查定时器 if (this.stabilityCheckInterval) { clearInterval(this.stabilityCheckInterval); console.log('稳定性检查定时器已清理'); } // 关闭WebSocket连接 // 注意:这里可以根据实际需求决定是否关闭连接 // 如果是多页面共用一个连接,可以不在这里关闭 // socketManager.close(); } }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh: function () { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { this.loadMoreMessages(); }, /** * 从服务器获取历史消息 */ fetchHistoryMessages: function() { try { let conversationId = this.data.conversationId; const app = getApp(); const currentUserId = AuthManager.getUserId() || wx.getStorageSync('userId'); const isManager = this.data.isManager || (app.globalData.userInfo?.userType === 'manager'); const managerId = wx.getStorageSync('managerId') || (app.globalData.userInfo?.managerId || ''); let chatUserId = this.data.userId; const isRealUserId = this.data.isRealUserId || (chatUserId && chatUserId.startsWith('user_') && chatUserId.includes('_rea')); console.log('=== fetchHistoryMessages 开始 ==='); console.log('- 当前用户ID:', currentUserId); console.log('- 是否客服:', isManager); console.log('- 客服ID:', managerId); console.log('- 会话ID:', conversationId); console.log('- 聊天对象ID:', chatUserId); console.log('- 是否真实用户ID:', isRealUserId); // 【关键修复】检测和处理ID混淆问题 // 如果chatUserId看起来像会话ID(包含连字符且不是用户格式),尝试修复 if (chatUserId && chatUserId.includes('-') && !chatUserId.startsWith('user_') && !chatUserId.startsWith('temp_')) { console.warn('⚠️ 检测到可能的ID混淆:聊天对象ID看起来像会话ID:', chatUserId); // 如果没有明确的会话ID,使用这个作为会话ID if (!conversationId || conversationId === chatUserId) { conversationId = chatUserId; console.log('设置会话ID为:', conversationId); } } // 【关键修复】真实用户ID特殊处理 if (isRealUserId) { console.log('🔍 检测到真实用户ID,执行特殊处理'); // 对于真实用户,我们需要确保能够找到正确的会话ID try { // 1. 先尝试从本地存储查找映射关系 const conversationMap = wx.getStorageSync('conversation_user_map') || {}; if (conversationMap[chatUserId]) { console.log('从本地存储找到真实用户的会话ID:', conversationMap[chatUserId]); conversationId = conversationMap[chatUserId]; } // 2. 如果是客服,并且有明确的用户ID,我们可以直接根据用户ID查询 if (isManager && chatUserId.startsWith('user_')) { console.log('客服模式下,优先根据用户ID查询消息'); // 这里我们可以添加一个标志,让服务器知道我们要查询特定用户的所有消息 this.data.queryByUserId = true; } } catch (e) { console.error('处理真实用户ID时出错:', e); } } // 验证必要参数 if (!conversationId && !isManager) { console.error('无法获取历史消息:会话ID不存在'); wx.showToast({ title: '会话ID无效', icon: 'none' }); return; } if (!currentUserId) { console.error('无法获取历史消息:用户未登录'); wx.showToast({ title: '用户未登录', icon: 'none' }); return; } // 显示加载提示 wx.showLoading({ title: '加载历史消息...', }); // 【关键修复】构建更完整的请求数据,支持客服特殊逻辑和真实用户 const requestData = { type: 'get_messages', conversationId: conversationId, page: 1, limit: 100, // 增加限制,获取更多历史消息 userId: currentUserId, targetUserId: chatUserId, // 添加目标用户ID userType: isManager ? 'manager' : 'user', managerId: isManager ? managerId : undefined, timestamp: Date.now(), // 【新增】添加真实用户相关参数 isRealUser: isRealUserId, // 【新增】允许根据用户ID查询 queryByUserId: this.data.queryByUserId || false }; // 【关键修复】对于客服,添加额外的参数以获取真实用户消息 if (isManager) { requestData.manager_mode = true; requestData.include_all_messages = true; // 请求所有消息,包括客服和用户的 requestData.include_deleted = false; // 不包含已删除消息 requestData.with_user_details = true; // 请求包含用户详情 console.log('客服模式请求参数:', requestData); } // 【关键修复】对于真实用户,添加特殊标记 if (isRealUserId) { requestData.real_user_id = chatUserId; console.log('真实用户模式请求参数:', requestData); } console.log('准备发送历史消息请求:', requestData); // 检查WebSocket连接状态 console.log('检查WebSocket连接状态'); const isConnected = socketManager.getConnectionStatus() || false; console.log('WebSocket连接状态:', isConnected); if (!isConnected) { console.error('WebSocket未连接,尝试重新初始化连接...'); // 尝试重新初始化连接 this.initWebSocket(); // 延迟发送请求 setTimeout(() => { this.sendHistoryMessageRequest(requestData); }, 1500); } else { // 直接发送请求 this.sendHistoryMessageRequest(requestData); } } catch (error) { console.error('fetchHistoryMessages异常:', error); wx.hideLoading(); wx.showToast({ title: '加载消息失败', icon: 'none' }); } }, /** * 发送历史消息请求的辅助方法 */ sendHistoryMessageRequest: function(requestData) { try { console.log('开始发送历史消息请求...'); const sent = socketManager.send(requestData); if (sent) { console.log('历史消息请求已成功发送'); // 设置请求超时处理 if (this.historyRequestTimeout) { clearTimeout(this.historyRequestTimeout); } this.historyRequestTimeout = setTimeout(() => { console.warn('历史消息请求超时(5秒)'); wx.hideLoading(); // 如果超时且消息列表为空,显示提示并尝试再次请求 if (this.data.messages.length === 0) { console.log('消息列表为空,尝试再次请求历史消息'); wx.showToast({ title: '消息加载超时,正在重试...', icon: 'none', duration: 1500 }); // 延迟1秒后再次尝试 setTimeout(() => { this.fetchHistoryMessages(); }, 1000); } }, 5000); } else { console.error('历史消息请求发送失败'); wx.hideLoading(); wx.showToast({ title: '消息请求发送失败', icon: 'none' }); } } catch (error) { console.error('sendHistoryMessageRequest异常:', error); wx.hideLoading(); } }, /** * 处理服务器返回的历史消息 */ handleHistoryMessages: function(payload) { console.log('=== handleHistoryMessages 开始 ==='); console.log('收到服务器响应:', payload); // 清除超时定时器 if (this.historyRequestTimeout) { clearTimeout(this.historyRequestTimeout); console.log('已清除历史消息请求超时定时器'); } wx.hideLoading(); // 确保payload是对象 if (!payload || typeof payload !== 'object') { console.error('handleHistoryMessages - 无效的响应格式:', payload); wx.showToast({ title: '消息格式错误', icon: 'none' }); return; } // 【关键修复】支持多种响应格式 - 修改为let以便后续可能的重新赋值 let messages = payload.messages || payload.data || payload.items || []; const pagination = payload.pagination || {}; console.log('收到服务器历史消息:', { messageCount: messages.length, pagination, rawPayloadKeys: Object.keys(payload) }); // 添加详细调试日志,打印payload的完整结构 console.log('payload详细结构:', JSON.stringify(payload, null, 2)); // 【关键修复】如果没有消息,尝试从其他可能的字段获取 if (!Array.isArray(messages) || messages.length === 0) { console.log('暂无历史消息,尝试从其他字段查找...'); // 检查是否有其他可能的消息字段 for (const key in payload) { if (Array.isArray(payload[key]) && payload[key].length > 0) { console.log(`发现可能的消息数组在字段: ${key}`); if (typeof payload[key][0] === 'object' && (payload[key][0].content || payload[key][0].message_id)) { messages = payload[key]; console.log(`从字段 ${key} 找到 ${messages.length} 条消息`); break; } } } // 如果仍然没有消息 if (!Array.isArray(messages) || messages.length === 0) { // 【关键修复】对于真实用户,如果没有消息,尝试主动从数据库查询 if (this.data.isRealUserId) { console.log('真实用户没有消息,尝试直接从数据库查询'); this.tryDirectDatabaseQuery(); return; } wx.showToast({ title: '暂无历史消息', icon: 'none' }); return; } } // 打印第一条消息的详细信息用于调试 if (messages.length > 0) { console.log('第一条消息示例:', messages[0]); // 【关键修复】特别关注真实用户消息 const realUserMessages = messages.filter(msg => { const senderId = msg.sender_id || msg.senderId || ''; return senderId === this.data.userId || senderId.includes('_rea'); }); if (realUserMessages.length > 0) { console.log(`🔍 发现 ${realUserMessages.length} 条真实用户消息`); console.log('真实用户消息示例:', realUserMessages[0]); } } const app = getApp(); // 增强的用户ID获取逻辑,确保能正确获取当前用户ID const currentUserId = AuthManager.getUserId() || wx.getStorageSync('userId') || (app.globalData.userInfo?.userId || '') || ''; const isManager = this.data.isManager || (app.globalData.userInfo?.userType === 'manager'); const managerId = wx.getStorageSync('managerId') || (app.globalData.userInfo?.managerId || ''); console.log('处理历史消息的用户信息:', { currentUserId, isManager, managerId }); // 转换服务器消息格式为客户端需要的格式 const formattedMessages = messages.map(msg => { // 适配不同的字段名格式 - 【关键修复】支持更多可能的字段名 const senderId = msg.sender_id || msg.senderId || msg.from || msg.userId || ''; const receiverId = msg.receiver_id || msg.receiverId || msg.to || ''; console.log('消息原始数据:', { messageId: msg.message_id || msg.messageId, senderId: senderId, receiverId: receiverId, content: msg.content }); // 【关键修复】改进isSelf判断逻辑,确保自己发送的消息显示在右侧 let isSelf = false; // 基本判断:如果senderId等于currentUserId,就是自己的消息 if (String(senderId) === String(currentUserId)) { isSelf = true; console.log('消息是自己发送的,显示在右侧'); } // 客服特殊逻辑:如果是客服,并且消息是发送给这个客服的,也视为"自己的"消息 if (isManager && managerId) { const stringManagerId = String(managerId); // 如果消息是发送给当前客服的,或者是当前客服发送的,都视为自己的消息 isSelf = isSelf || String(senderId) === stringManagerId || String(receiverId) === stringManagerId || // 如果是客服,并且消息的sender_type是客服,也视为自己的消息 (msg.sender_type === 2 || msg.senderType === 'manager' || msg.senderType === 2); } console.log('消息方向判断结果:', { isSelf, senderId: String(senderId), currentUserId: String(currentUserId), isEqual: String(senderId) === String(currentUserId) }); // 【关键修复】特别标记真实用户消息 const isRealUserMessage = senderId === this.data.userId || (senderId && senderId.startsWith('user_') && senderId.includes('_rea')); // 记录详细的判断信息用于调试 console.log('消息归属判断:', { messageId: msg.message_id || msg.messageId, senderId: senderId, receiverId: receiverId, currentUserId: currentUserId, isManager: isManager, managerId: managerId, isSelf: isSelf, isRealUserMessage: isRealUserMessage }); // 确定消息发送方(me或other) const sender = isSelf ? 'me' : 'other'; // 【关键修复】确保从数据库获取的时间戳正确处理 const timestamp = msg.created_at || msg.timestamp || msg.send_time || Date.now(); const formattedTime = this.formatTimestampToTime(timestamp); return { id: msg.message_id || msg.messageId || msg.id || 'msg_' + Date.now() + '_' + Math.floor(Math.random() * 1000), type: 'message', // 确保类型为message,而不是chat_message content: msg.content || msg.message || '', isImage: msg.content_type === 2 || msg.contentType === 2 || (msg.fileUrl && msg.content_type !== 1), fileUrl: msg.file_url || msg.fileUrl, sender: sender, senderId: senderId, receiverId: receiverId, senderType: msg.sender_type || msg.senderType || 'unknown', isSelf: isSelf, isRealUserMessage: isRealUserMessage, time: formattedTime, shortTime: this.extractShortTime(formattedTime), status: 'sent', isRead: msg.is_read || 0, serverData: msg, // 保存原始服务器数据 // 【新增】保留原始时间戳用于排序 timestamp: timestamp }; }); // 【关键修复】按时间戳排序消息,确保顺序正确 formattedMessages.sort((a, b) => { const timeA = a.timestamp || 0; const timeB = b.timestamp || 0; return Number(timeA) - Number(timeB); }); // 【关键修复】验证格式化后的消息 if (formattedMessages.length > 0) { // 特别统计真实用户消息 const realUserMessages = formattedMessages.filter(msg => msg.isRealUserMessage); console.log('格式化后消息样本:', { count: formattedMessages.length, realUserMessageCount: realUserMessages.length, hasSelfMessages: formattedMessages.some(msg => msg.isSelf), hasOtherMessages: formattedMessages.some(msg => !msg.isSelf), senderIds: [...new Set(formattedMessages.map(msg => msg.senderId))], realUserIds: [...new Set(realUserMessages.map(msg => msg.senderId))] }); // 【关键修复】如果找到真实用户消息,显示特殊提示 if (realUserMessages.length > 0) { console.log('✅ 成功加载到真实用户消息!'); } } // 处理消息时间显示逻辑 const processedMessages = this.processMessageTimes(formattedMessages); console.log('格式化后的消息数量:', processedMessages.length); // 更新消息列表 this.setData({ messages: processedMessages }, () => { // 数据更新完成后的回调 console.log('消息列表已成功更新到UI'); // 滚动到底部 this.scrollToBottom(); // 【关键修复】根据消息情况显示不同的提示 const realUserMessages = processedMessages.filter(msg => msg.isRealUserMessage); if (realUserMessages.length > 0) { wx.showToast({ title: `加载成功,${realUserMessages.length}条真实用户消息`, icon: 'success', duration: 2000 }); } else { wx.showToast({ title: '加载历史消息成功', icon: 'success', duration: 1500 }); } }); // 保存到本地存储 this.saveMessagesToStorage(processedMessages); console.log('历史消息加载完成,共', processedMessages.length, '条'); }, /** * 尝试直接从数据库查询消息(备用方法) */ tryDirectDatabaseQuery: function() { console.log('=== tryDirectDatabaseQuery 开始 ==='); console.log('尝试直接从数据库查询真实用户消息...'); try { // 显示加载提示 wx.showLoading({ title: '尝试备用查询...', }); // 构建备用查询请求 const queryData = { type: 'direct_db_query', action: 'get_chat_messages', targetUserId: this.data.userId, conversationId: this.data.conversationId, userId: AuthManager.getUserId() || wx.getStorageSync('userId'), isManager: this.data.isManager, limit: 100, timestamp: Date.now() }; console.log('发送备用数据库查询请求:', queryData); // 使用api.js中的请求方法或直接发送 wx.request({ url: 'https://api.example.com/direct_query', // 替换为实际的API地址 method: 'POST', data: queryData, success: (res) => { wx.hideLoading(); console.log('备用查询成功响应:', res.data); if (res.data && res.data.success && res.data.messages) { console.log('从备用查询获取到', res.data.messages.length, '条消息'); // 直接使用handleHistoryMessages处理响应 this.handleHistoryMessages({ messages: res.data.messages }); } else { console.log('备用查询没有返回消息'); wx.showToast({ title: '暂无历史消息', icon: 'none' }); } }, fail: (error) => { wx.hideLoading(); console.error('备用查询失败:', error); console.log('备用查询失败,继续使用WebSocket方式'); // 尝试再次使用WebSocket方式 setTimeout(() => { this.fetchHistoryMessages(); }, 1000); } }); } catch (e) { wx.hideLoading(); console.error('备用查询过程中出错:', e); } }, /** * 加载更多历史消息功能 */ loadMoreMessages: function() { const conversationId = this.data.conversationId; if (!conversationId) { console.warn('无法加载更多历史消息:会话ID不存在'); wx.showToast({ title: '加载失败', icon: 'none' }); return; } console.log('加载更多历史消息,会话ID:', conversationId); // 显示加载提示 wx.showLoading({ title: '加载更多消息...', }); // 简单实现,实际可以添加分页逻辑 socketManager.send({ type: 'get_messages', conversationId: conversationId, page: 1, // 实际应用中应该根据已有消息数量计算页码 limit: 50 }); }, /** * 用户点击右上角分享 */ onShareAppMessage: function () { } });