diff --git a/app.js b/app.js index 31d98ba..13298e1 100644 --- a/app.js +++ b/app.js @@ -39,19 +39,6 @@ App({ onLaunch: function () { // 初始化应用 console.log('App Launch') - // 初始化WebSocket管理器 - try { - const wsManager = require('./utils/websocket'); - this.globalData.webSocketManager = wsManager; - // 连接WebSocket服务器 - wsManager.connect('ws://localhost:3003', { - maxReconnectAttempts: 5, - reconnectInterval: 3000, - heartbeatTime: 30000 - }); - } catch (e) { - console.error('初始化WebSocket管理器失败:', e); - } // 初始化本地存储的标签和用户数据 if (!wx.getStorageSync('users')) { wx.setStorageSync('users', {}) @@ -111,10 +98,7 @@ App({ console.log('App初始化 - 用户类型:', this.globalData.userType); console.log('App初始化 - 用户信息:', this.globalData.userInfo); - - // 异步获取客服列表并缓存 - this.fetchAndCacheCustomerServices(); - + // 获取用户信息 wx.getSetting({ success: res => { @@ -175,66 +159,7 @@ App({ userType: 'customer', // 默认客户类型 currentTab: 'index', // 当前选中的tab showTabBar: true, // 控制底部tab-bar显示状态 - onNewMessage: null, // 全局新消息处理回调函数 - isConnected: false, - unreadMessages: 0, // 测试环境配置 - isTestMode: false, - // 全局WebSocket连接状态 - wsConnectionState: 'disconnected', // disconnected, connecting, connected, error - // 客服相关状态 - isServiceOnline: false, - onlineServiceCount: 0, - // 客服列表,从服务器获取并缓存 - customerServiceList: [] - }, - - // 获取客服列表并存储到globalData和本地存储 - async fetchAndCacheCustomerServices() { - try { - console.log('开始获取客服列表...'); - - // 使用wx.request直接获取客服列表 - const res = await new Promise((resolve, reject) => { - wx.request({ - url: 'http://localhost:3003/api/managers', - method: 'GET', - timeout: 15000, - header: { - 'content-type': 'application/json' - }, - success: resolve, - fail: reject - }); - }); - - if (res && res.statusCode === 200 && res.data) { - const dataSource = res.data.data || res.data; - if (Array.isArray(dataSource)) { - // 处理客服数据,确保包含必要字段 - const processedData = dataSource.map(item => ({ - id: item.id || `id_${Date.now()}_${Math.random()}`, - managerId: item.managerId || '', - name: item.name || '未知', - alias: item.alias || item.name || '未知', - phoneNumber: item.phoneNumber || '', - avatarUrl: item.avatar || item.avatarUrl || '', - isOnline: !!item.online - })); - - // 更新全局客服列表 - this.globalData.customerServiceList = processedData; - - // 存储到本地存储 - wx.setStorageSync('cached_customer_services', processedData); - - console.log('客服列表获取成功,共', processedData.length, '条数据'); - return processedData; - } - } - } catch (error) { - console.error('获取客服列表失败:', error); - } - return []; + isTestMode: false } -}) +}) \ No newline at end of file diff --git a/app.json b/app.json index 17d41a7..0dc52c6 100644 --- a/app.json +++ b/app.json @@ -11,11 +11,10 @@ "pages/notopen/index", "pages/create-supply/index", "pages/goods-detail/goods-detail", - "pages/customer-service/index", - "pages/message-list/index", "pages/chat/index", "pages/chat-detail/index", - "pages/test-service/test-service" + "pages/message-list/index", + "pages/customer-service/index" ], "subpackages": [ { diff --git a/images/轮播图1.jpg b/images/轮播图1.jpg new file mode 100644 index 0000000..92bf382 Binary files /dev/null and b/images/轮播图1.jpg differ diff --git a/images/轮播图2.jpg b/images/轮播图2.jpg new file mode 100644 index 0000000..c0971d6 Binary files /dev/null and b/images/轮播图2.jpg differ diff --git a/pages/chat-detail/index.js b/pages/chat-detail/index.js index 34b6695..29bad04 100644 --- a/pages/chat-detail/index.js +++ b/pages/chat-detail/index.js @@ -1,3871 +1,122 @@ // pages/chat-detail/index.js -import socketManager from '../../utils/websocket'; -const AuthManager = require('../../utils/auth.js'); - Page({ - - /** - * 页面的初始数据 - */ data: { - userId: '', - userName: '', - avatar: '', + chatId: null, messages: [], inputValue: '', - goodsInfo: null, // 商品信息 - connectionStatus: 'disconnected', // 连接状态: disconnected, connecting, connected, error - connectionMessage: '', // 连接状态提示消息 - isMockMode: false, // 默认为真实WebSocket通信模式 - isManager: false, // 是否是与客服的聊天 - scrollToMessage: '', // 用于滚动到指定消息的ID - scrollTop: 0, // 用于scroll-top属性 - showScrollToBottomBtn: false, // 是否显示"到达最底部"按钮 - isNearBottom: true, // 用户是否接近底部 - lastScrollTop: 0, // 上次滚动位置 - scrollHeight: 0, // 滚动区域高度 - contentHeight: 0 // 内容高度 - }, - - /** - * 从本地存储加载历史消息 - */ - loadMessagesFromStorage: function() { - try { - // 尝试从多个存储键加载消息,提高消息加载成功率 - // 关键修复:优先使用conversationId作为存储键,确保能加载到正确的历史消息 - const storageKeys = [ - `chat_messages_${this.data.conversationId}`, - `chat_messages_${this.data.userId}_${this.data.conversationId}`, - `chat_messages_${this.data.userId}` - ]; - - let storedMessages = []; - let successKey = ''; - - // 尝试从不同的存储键加载消息 - for (const key of storageKeys) { - const messages = wx.getStorageSync(key); - if (messages && messages.length > 0) { - storedMessages = messages; - successKey = key; - break; - } - } - - if (storedMessages && storedMessages.length > 0) { - console.log('从本地存储加载了', storedMessages.length, '条历史消息,存储键:', successKey); - - // 处理消息数据,确保包含必要的字段 - const messagesWithRequiredFields = storedMessages.map((msg, index) => { - // 获取原始时间 - const originalTime = msg.time; - let formattedTime; - - // 调试:记录原始时间 - console.log('=== 历史消息时间调试 ==='); - console.log('原始时间:', originalTime); - console.log('原始时间类型:', typeof originalTime); - - // 无论是否有原始时间,都重新格式化,确保正确的北京时间显示 - if (originalTime) { - // 重新格式化所有时间,确保正确的北京时间 - if (typeof originalTime === 'number') { - // 数字时间戳,重新格式化 - formattedTime = this.formatTimestampToTime(originalTime); - console.log('数字时间戳重新格式化后:', formattedTime); - } else if (typeof originalTime === 'string') { - if (originalTime.includes('T')) { - // ISO格式字符串,重新格式化 - formattedTime = this.formatServerTime(originalTime); - console.log('ISO字符串重新格式化后:', formattedTime); - } else { - // 其他字符串格式,检查是否是"月-日 时:分"格式 - const dateTimeMatch = originalTime.match(/^(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{2})$/); - if (dateTimeMatch) { - // 已经是正确的北京时间格式,直接使用 - formattedTime = originalTime; - console.log('直接使用正确格式的时间:', formattedTime); - } else { - // 其他格式,使用当前时间 - formattedTime = this.getFormattedTime(); - console.log('格式不正确,使用当前时间:', formattedTime); - } - } - } else { - // 其他类型,使用当前时间 - formattedTime = this.getFormattedTime(); - console.log('其他类型,使用当前时间:', formattedTime); - } - } else { - // 没有时间,使用当前时间 - formattedTime = this.getFormattedTime(); - console.log('没有原始时间,使用当前时间:', formattedTime); - } - - return { - ...msg, - // 确保消息有id字段,避免被稳定性检查过滤 - id: msg.id || `local_msg_${Date.now()}_${index}`, - // 确保消息类型正确 - type: msg.type || 'message', - // 确保有sender字段 - sender: msg.sender || (msg.isSelf ? 'me' : 'other'), - // 确保有content字段 - content: msg.content || '', - // 确保有正确格式的time字段(北京时间) - time: formattedTime, - // 重新生成shortTime字段 - shortTime: this.extractShortTime(formattedTime), - // 确保有status字段 - status: msg.status || 'sent', - // 确保有senderType字段 - senderType: msg.senderType || 'unknown', - // 确保有isRead字段 - isRead: msg.isRead || true, - // 确保有timestamp字段 - timestamp: msg.timestamp || Date.now(), - // 确保有showTime字段 - showTime: msg.showTime || false - }; - }); - - // 重新处理时间显示逻辑 - 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); - - // 特殊处理特定用户ID,直接返回对应的手机号 - if (safeUserId === 'user_1765415381781_1iy1ls177') { - return '17828048259'; - } - - // 检查是否是手机号格式 (中国手机号格式) - if (/^1[3-9]\d{9}$/.test(safeUserId)) { - // 如果是手机号,直接显示完整手机号 - return safeUserId; - } - - // 首先从客服列表缓存中查找 - 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 { - // 1. 尝试从用户数据映射中获取 - const users = wx.getStorageSync('users') || {}; - const userData = users[safeUserId]; - if (userData) { - // 优先显示完整手机号 - if (userData.phoneNumber) { - return userData.phoneNumber; - } - if (userData.phone) { - return userData.phone; - } - if (userData.name) { - return userData.name; - } - } - - // 2. 尝试从会话信息中获取 - const sessionInfo = wx.getStorageSync(`session_${safeUserId}`); - if (sessionInfo) { - if (sessionInfo.phoneNumber) { - return sessionInfo.phoneNumber; - } - if (sessionInfo.phone) { - return sessionInfo.phone; - } - if (sessionInfo.userName) { - return sessionInfo.userName; - } - } - - // 3. 尝试从全局用户信息中获取 - const globalUserInfo = wx.getStorageSync('userInfo'); - if (globalUserInfo && typeof globalUserInfo === 'object') { - if (globalUserInfo.userId === safeUserId || globalUserInfo.id === safeUserId) { - if (globalUserInfo.phoneNumber) { - return globalUserInfo.phoneNumber; - } - if (globalUserInfo.phone) { - return globalUserInfo.phone; - } - if (globalUserInfo.userName) { - return globalUserInfo.userName; - } - if (globalUserInfo.name) { - return globalUserInfo.name; - } - } - } - - // 4. 尝试从字符串格式的userInfo中获取 - const stringUserInfo = wx.getStorageSync('userInfo'); - if (stringUserInfo && typeof stringUserInfo === 'string') { - try { - const parsedUserInfo = JSON.parse(stringUserInfo); - if (parsedUserInfo.userId === safeUserId || parsedUserInfo.id === safeUserId) { - if (parsedUserInfo.phoneNumber) { - return parsedUserInfo.phoneNumber; - } - if (parsedUserInfo.phone) { - return parsedUserInfo.phone; - } - if (parsedUserInfo.userName) { - return parsedUserInfo.userName; - } - if (parsedUserInfo.name) { - return parsedUserInfo.name; - } - } - } 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; - } + loading: false }, - /** - * 生命周期函数--监听页面加载 - */ onLoad: function (options) { - // 执行统一身份验证 - AuthManager.authenticate((authResult) => { - console.log('身份验证成功,初始化聊天页面:', authResult); - - // 接收从上一个页面传递的参数 - if (options) { - // 关键修复:区分会话ID和用户ID - let targetId = options.userId || ''; - const conversationId = options.conversationId || targetId; - // 优先使用传递的userName参数,并确保正确解码 - let userName = options.userName ? decodeURIComponent(options.userName) : ''; - - console.log('页面加载原始参数:', { targetId, conversationId, userName, options }); - - // 检查目标ID是否为会话ID(包含连字符且不是用户格式) - const isTargetIdSessionId = targetId.includes('-') && !targetId.startsWith('user_') && !targetId.startsWith('temp_'); - - // 关键修复:如果targetId是会话ID,从会话ID中提取信息或使用其他方式获取真实的用户ID - let realUserId = targetId; - let finalManagerId = null; - let isTargetManager = false; - - // 检查options中是否明确标记为客服 - if (options.isManager === 'true') { - isTargetManager = true; - console.log('明确标记为客服'); - } - - // 检查targetId是否为纯数字(通常是客服ID) - if (/^\d+$/.test(targetId)) { - isTargetManager = true; - finalManagerId = targetId; - console.log('检测到纯数字客服ID:', targetId); - } - - // 检查options中是否包含真实用户ID信息 - if (options.realUserId) { - realUserId = options.realUserId; - console.log('使用options中的realUserId:', realUserId); - } else if (options.originalUserId) { - realUserId = options.originalUserId; - console.log('使用options中的originalUserId:', realUserId); - } - - // 【关键修复】如果realUserId是会话ID,尝试从会话中获取真实用户ID - if (realUserId && realUserId.includes('-') && !realUserId.startsWith('user_') && !realUserId.startsWith('temp_')) { - console.warn('⚠️ 检测到realUserId是会话ID,尝试获取真实用户ID:', realUserId); - - // 1. 尝试从本地存储的会话映射中获取用户ID - try { - const conversationMap = wx.getStorageSync('conversation_user_map') || {}; - for (const [userId, convId] of Object.entries(conversationMap)) { - if (convId === realUserId) { - realUserId = userId; - console.log('从会话映射中找到真实用户ID:', realUserId); - break; - } - } - } catch (e) { - console.error('从会话映射中获取用户ID失败:', e); - } - - // 2. 如果第一步失败,尝试从本地存储的会话列表中获取 - if (realUserId && realUserId.includes('-') && !realUserId.startsWith('user_')) { - try { - const conversations = wx.getStorageSync('conversations') || []; - const conversation = conversations.find(conv => conv.conversationId === realUserId); - if (conversation && conversation.userId) { - realUserId = conversation.userId; - console.log('从会话列表中找到真实用户ID:', realUserId); - } - } catch (e) { - console.error('从会话列表中获取用户ID失败:', e); - } - } - } - - // 处理会话ID情况 - if (isTargetIdSessionId) { - console.warn('检测到targetId是会话ID:', targetId); - - // 从会话ID中尝试提取客服ID - // 会话ID格式:xxx-xxx-xxx-xxx-xxx - // 优先从本地存储的会话映射中获取 - try { - // 尝试从本地存储获取会话映射 - const conversationMap = wx.getStorageSync('conversation_user_map') || {}; - if (conversationMap[targetId]) { - realUserId = conversationMap[targetId]; - console.log('从会话映射中获取到真实ID:', realUserId); - } else { - // 检查options中是否包含真实用户ID - if (options.realUserId) { - realUserId = options.realUserId; - console.log('使用options中的realUserId作为真实ID:', realUserId); - } else if (options.originalUserId) { - realUserId = options.originalUserId; - console.log('使用options中的originalUserId作为真实ID:', realUserId); - } else { - // 会话ID是49ac893e-2b4d-4ea5-9669-03ad6a9b41be,不要从会话ID中提取客服ID - // 直接使用原始的targetId作为realUserId,避免错误提取 - realUserId = targetId; - console.log('使用原始会话ID作为真实ID,避免错误提取客服ID:', realUserId); - } - } - } catch (e) { - console.error('处理会话ID失败:', e); - } - } - - // 修复:移除从会话ID中提取客服ID的错误逻辑 - // 会话ID是49ac893e-2b4d-4ea5-9669-03ad6a9b41be,其中的49只是会话ID的一部分,不是客服ID - console.log('修复:跳过从会话ID中提取客服ID的错误逻辑'); - - // 关键修复:如果realUserId是从会话ID中错误提取的数字49,恢复为原始会话ID - if (realUserId === '49' && targetId.includes('-')) { - // realUserId被错误提取为49,恢复为原始会话ID - realUserId = targetId; - console.log('修复:realUserId被错误提取为49,恢复为原始会话ID:', realUserId); - // 目标不是客服,所以isTargetManager应该为false - isTargetManager = false; - // 清除错误的managerId - finalManagerId = null; - } - - // 关键修复:如果options中明确传递了managerId,使用它 - if (options.managerId) { - finalManagerId = options.managerId; - isTargetManager = true; - // 只有当当前用户是普通用户时,才将realUserId设置为managerId - // 客服用户需要保留真实的聊天对象ID - if (!AuthManager.isCustomerService()) { - realUserId = options.managerId; - } - console.log('使用options中的managerId:', finalManagerId); - } - - // 提前使用AuthManager判断当前用户是否为客服,避免重复调用 - const isCurrentUserManager = AuthManager.isCustomerService(); - - // 关键修复:根据当前用户类型处理会话ID - // 如果当前用户是普通用户,且realUserId是会话ID,才将其转换为客服ID 22 - // 如果当前用户是客服,保留原始会话ID,避免错误处理 - if (realUserId.includes('-') && !realUserId.startsWith('user_') && !realUserId.startsWith('temp_')) { - if (!isCurrentUserManager) { - // 当前用户是普通用户,将会话ID转换为客服ID 22 - console.log('最终处理:将会话ID转换为客服ID 22'); - realUserId = '22'; - isTargetManager = true; - finalManagerId = realUserId; - } else { - // 当前用户是客服,保留原始会话ID - console.log('当前用户是客服,保留原始会话ID:', realUserId); - // 不需要将会话ID转换为客服ID,保留原始会话ID以便正确加载消息 - // 目标不是客服,所以isTargetManager应该为false - isTargetManager = false; - // 客服的managerId应该是自己的ID,从AuthManager获取 - finalManagerId = AuthManager.getManagerId() || ''; - } - } - - // 如果没有传递userName或userName为空,则使用getUserNameById获取默认名称 - if (!userName || userName.trim() === '') { - userName = this.getUserNameById(realUserId); - } - - // 添加isManager变量定义,用于标识当前用户是否为客服 - const isManager = isCurrentUserManager; - - // 【关键修复】特殊处理真实用户ID,特别是格式为user_1763452685075_rea007flq的用户 - const isRealUserId = realUserId.startsWith('user_') && realUserId.includes('_rea'); - - // 如果目标是客服,设置managerId - if (isTargetManager && !finalManagerId) { - finalManagerId = realUserId; - console.log('检测到目标为客服,设置managerId:', finalManagerId); - } - - // 尝试从本地存储获取关联的会话ID - let finalConversationId = conversationId; - try { - const conversationMap = wx.getStorageSync('conversation_user_map') || {}; - if (conversationMap[realUserId]) { - console.log('从映射表中找到关联的会话ID:', conversationMap[realUserId]); - finalConversationId = conversationMap[realUserId]; - } - } catch (e) { - console.error('获取会话映射失败:', e); - } - - // 如果没有找到会话ID,根据用户类型生成合适的会话ID - if (!finalConversationId) { - if (isRealUserId) { - console.log('检测到真实用户ID格式:', realUserId); - finalConversationId = `conv_${realUserId}_${Date.now()}`; - } else { - // 为非真实用户生成临时会话ID,包括客服 - console.log('生成临时会话ID,目标ID:', realUserId, '是否客服:', isTargetManager); - finalConversationId = this.generateTempConversationId(realUserId, isCurrentUserManager); - } - - console.log('生成新的会话ID:', finalConversationId); - - // 保存会话ID和用户ID的映射 - try { - const conversationMap = wx.getStorageSync('conversation_user_map') || {}; - conversationMap[realUserId] = finalConversationId; - wx.setStorageSync('conversation_user_map', conversationMap); - } catch (e) { - console.error('保存会话映射失败:', e); - } - } - - // 保存最终的会话信息 - this.setData({ - userId: realUserId, - managerId: finalManagerId, - userName: userName, - avatar: options.avatar || '', - conversationId: finalConversationId, - 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 (realUserId === 'user_1763452685075_rea007flq') { - console.log('=== 检测到目标测试用户ID,启用特殊验证模式 ==='); - try { - wx.showToast({ - title: '已启用真实用户验证模式', - icon: 'success', - duration: 2000 - }); - } catch (e) { - console.error('显示提示失败:', e); - } - } - - // 首先尝试从本地存储加载历史消息 - const hasLoadedStoredMessages = this.loadMessagesFromStorage(); - - // 如果没有加载到历史消息,并且当前用户不是客服,添加一条客服礼貌用语 - // 客服登录时不显示礼貌用语,只有普通用户首次进入时才显示 - // 直接使用之前定义的isCurrentUserManager变量,避免重复定义 - if (!hasLoadedStoredMessages && !isCurrentUserManager) { - console.log('没有加载到历史消息,添加客服礼貌用语'); - // 延迟添加礼貌用语,避免初始化过程中的冲突 - setTimeout(() => { - try { - this.addWelcomeMessage(); - } catch (e) { - console.error('添加礼貌用语失败:', e); - } - }, 500); - } else { - console.log('不需要添加礼貌用语,原因:', { - hasLoadedStoredMessages, - isCurrentUserManager - }); - } - - // 如果是真实用户且消息列表为空,可以加载测试消息用于验证 - if (isRealUserId && !hasLoadedStoredMessages) { - console.log('真实用户首次进入,准备初始化测试消息'); - // 延迟调用测试方法,避免初始化过程中的冲突 - setTimeout(() => { - try { - this.testRealUserMessageFunctionality(); - } catch (e) { - console.error('调用测试方法失败:', e); - } - }, 1000); - } - - // 初始化WebSocket连接 - this.initWebSocket(); - - // 显示当前模式提示 - wx.showToast({ - title: '真实通信模式', - icon: 'none', - duration: 2000 - }); - } - }, (error) => { - console.warn('身份验证失败,返回上一页:', error); - wx.navigateBack(); - }); - }, - - /** - * 添加客服礼貌用语 - */ - addWelcomeMessage: function() { - console.log('添加客服礼貌用语...'); - - // 创建礼貌用语消息 - const welcomeMessage = { - id: 'welcome_msg_' + Date.now(), - type: 'message', - content: '您好,欢迎来到在线客服,请问有什么可以帮助您的?', - sender: 'other', // 客服发送的消息,显示在左侧 - senderType: 'manager', - time: this.getFormattedTime(), - shortTime: this.getShortTime(), - showTime: true, - status: 'sent', - isRead: true, - timestamp: Date.now(), - isSelf: false - }; - - // 获取当前消息列表 - const messages = [...this.data.messages]; - messages.push(welcomeMessage); - - // 更新UI - this.setData({ messages }); - - // 保存到本地存储 - this.saveMessagesToStorage(messages); - - console.log('客服礼貌用语添加成功'); - - // 滚动到底部 - this.scrollToBottom(); - }, - - /** - * 生成临时会话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连接,不再重新创建连接 - // 检查应用级别连接状态 - const app = getApp(); - if (app.globalData.webSocketManager && app.globalData.webSocketManager.isConnected) { - // 应用级别连接已存在,使用它 - console.log('使用应用级别WebSocket连接'); + if (options.id) { this.setData({ - connectionStatus: 'connected', - connectionMessage: '已连接' - }); - // 发送认证消息 - this.sendAuthMessage(); - } else { - // 应用级别连接不存在,创建新连接 - console.log('创建新的WebSocket连接'); - // 连接WebSocket服务器 - socketManager.connect(wsUrl, { - maxReconnectAttempts: 5, - reconnectInterval: 3000, - heartbeatTime: 30000 + chatId: options.id }); } - - // 监听网络状态变化 - this.startNetworkListener(); - - // 初始化系统稳定性监控 - this.initStabilityMonitor(); - - // 关键修复:确保在WebSocket连接成功后获取历史消息 - // 添加延迟以确保WebSocket连接完全建立 - setTimeout(() => { - console.log('延迟获取历史消息,确保WebSocket连接完全建立'); - this.fetchHistoryMessages(); - }, 1500); - - // 关键修复:注册全局新消息处理函数,确保在聊天页面时能收到新消息 - this.originalGlobalMessageHandler = app.globalData.onNewMessage; - app.globalData.onNewMessage = (newMessage) => { - console.log('全局新消息处理函数被调用,消息:', newMessage); - // 检查消息是否与当前聊天相关 - const isCurrentChatMessage = newMessage.serverData && - (newMessage.serverData.conversationId === this.data.conversationId || - newMessage.serverData.senderId === this.data.userId || - newMessage.serverData.receiverId === this.data.userId); - - if (isCurrentChatMessage) { - // 如果是当前聊天的消息,直接添加到消息列表 - this.processServerMessage(newMessage.serverData); - } - - // 调用原始处理函数 - if (this.originalGlobalMessageHandler && typeof this.originalGlobalMessageHandler === 'function') { - this.originalGlobalMessageHandler(newMessage); - } - }; - }, - - /** - * 手动重新连接 - */ - reconnect: function() { - wx.showLoading({ - title: '正在重连...', - }); - - // 重置重连计数 - socketManager.reconnectAttempts = 0; - - // 重新初始化WebSocket连接 - this.initWebSocket(); - - setTimeout(() => { - wx.hideLoading(); - }, 1000); + this.loadMessages(); }, - - /** - * 开始网络状态监听 - */ - 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(); - } - }); + + onBack: function () { + wx.navigateBack(); }, - - /** - * 设置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)...` - }); - }); + + loadMessages: function () { + this.setData({ loading: true }); - // 重连失败 - socketManager.on('reconnectFailed', () => { - this.setData({ - connectionStatus: 'error', - connectionMessage: '重连失败,请检查网络' - }); + // 模拟加载聊天记录 + setTimeout(() => { + const messages = [ + { + id: 1, + content: '您好,有什么可以帮助您的吗?', + sender: 'other', + time: '刚刚' + }, + { + id: 2, + content: '你好,我想咨询一下产品信息', + sender: 'me', + time: '刚刚' + }, + { + id: 3, + content: '当然可以,请问您想了解哪种产品?', + sender: 'other', + time: '5分钟前' + } + ]; - wx.showModal({ - title: '连接失败', - content: '无法连接到服务器,请检查网络设置后重试。', - showCancel: false, - confirmText: '确定' - }); - }); - - // 监听WebSocket状态更新事件 - socketManager.on('status', (status) => { - console.log('WebSocket状态更新:', status); this.setData({ - connectionMessage: status.message || '状态更新' + messages: messages, + loading: false }); - - // 根据状态类型更新连接状态 - 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 - }); - }); + }, 1000); }, - - /** - * 发送认证消息 - */ - 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'); + + loadMoreMessages: function () { + // 加载更多历史消息 + if (this.data.loading) return; - // 构建与后端匹配的认证信息格式 - 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); + this.setData({ loading: true }); - // 发送认证消息 - socketManager.send({ - type: 'auth', - data: authData.payload, // 注意:这里直接传递payload对象 - success: () => { - console.log('认证消息发送成功'); - this.setData({ - connectionStatus: 'connected', - connectionMessage: '已连接' - }); - - // 认证成功后清空消息队列 - if (socketManager._flushMessageQueue) { - socketManager._flushMessageQueue(); + // 模拟加载更多历史消息 + setTimeout(() => { + const moreMessages = [ + { + id: 4, + content: '你好,我想了解一下你们的鸡蛋产品', + sender: 'me', + time: '1小时前' + }, + { + id: 5, + content: '您好,欢迎咨询我们的鸡蛋产品', + sender: 'other', + time: '1小时前' } - }, - 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); - } - } - }); - - // 关键修复:处理待发送列表中的消息 - if (this.pendingMessages && this.pendingMessages.length > 0) { - console.log('处理待发送列表中的消息:', this.pendingMessages.length, '条'); - // 将待发送消息添加到需要重新发送的列表中 - messagesToResend.push(...this.pendingMessages); - // 清空待发送列表 - this.pendingMessages = []; - } - - // 保存更新后的消息到本地存储 - 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); - // 关键修复:统一处理逻辑,确保handleHistoryMessages总是收到payload对象 - const payload = data.payload || data; - this.handleHistoryMessages(payload); - } else if (data.type === 'error') { - // 处理错误消息 - console.error('收到服务器错误:', data.message); - wx.showToast({ - title: '消息发送失败: ' + data.message, - icon: 'none', - duration: 2000 + messages: [...moreMessages, ...this.data.messages], + loading: false }); - } else { - // 处理其他未定义类型的消息 - console.log('收到未处理的消息类型:', data.type, data); - } + }, 1000); }, - - // 处理聊天消息(旧格式,向后兼容) - 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); + + onInputChange: function (e) { + this.setData({ + inputValue: e.detail.value + }); }, - - // 处理服务器推送的新消息 - 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); - - // 调试:记录服务器返回的原始时间数据 - console.log('=== 时间调试 ==='); - console.log('原始服务器时间:', messageData.createdAt); - console.log('原始服务器时间类型:', typeof messageData.createdAt); - - // 格式化时间,添加详细调试 - let formattedTime; - if (messageData.createdAt) { - console.log('调用formatServerTime处理时间:', messageData.createdAt); - formattedTime = this.formatServerTime(messageData.createdAt); - console.log('格式化后的时间:', formattedTime); - } else { - formattedTime = this.getFormattedTime(); - console.log('使用当前时间:', formattedTime); - } + + sendMessage: function () { + const content = this.data.inputValue.trim(); + if (!content) return; - // 创建符合前端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: formattedTime, - status: 'sent', - serverData: messageData, // 保存完整的服务器数据 - // 【关键修复】添加真实用户消息标记 - isRealUserMessage: isRealUserMessage, - // 【关键修复】添加用于调试和验证的标记 - debugInfo: { - userId: this.data.userId, - isRealUserId: this.data.isRealUserId, - matchedUser: isRealUserMessage, - messageTimestamp: messageData.createdAt || Date.now(), - rawCreatedAt: messageData.createdAt, - formattedTime: formattedTime - } + id: Date.now(), + content: content, + sender: 'me', + time: '刚刚' }; - // 特殊处理客服消息显示逻辑 - 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 }, () => { - // 根据当前滚动位置决定是否自动滚动到底部 - if (this.data.isNearBottom) { - // 如果接近底部,自动滚动到底部 - this.scrollToBottom(); - } else { - // 如果不在底部,显示"到达最底部"按钮 - this.setData({ - showScrollToBottomBtn: true - }); - } - }); - - // 如果是新消息,触发通知更新消息列表 - 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 + this.setData({ + messages: [...this.data.messages, newMessage], + inputValue: '' }); - }, - - /** - * 验证真实用户消息功能的测试方法 - * 用于手动测试系统对真实用户消息的处理能力 - */ - testRealUserMessageFunctionality: function() { - console.log('=== 开始真实用户消息功能测试 ==='); - try { - // 测试模拟消息 - const testMessage = { - id: 'test_msg_' + Date.now(), - type: 'message', - content: '这是一条测试消息,用于验证真实用户消息功能', + // 模拟对方回复 + setTimeout(() => { + const reply = { + id: Date.now() + 1, + 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() - } + time: '刚刚' }; - // 添加测试消息 - 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; - } - - // 关键修复:完全移除消息内容验证,确保所有消息都能通过过滤 - // 当用户清理缓存后,本地存储的消息可能缺少一些字段,但应该保留 - // 从服务器获取的消息可能格式不同,也应该保留 - 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连接状态 - // 修复:直接使用页面顶部引入的socketManager,而不是重新require - 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) { - console.log('=== formatServerTime 调试 ==='); - console.log('输入serverTime:', serverTime); - console.log('输入类型:', typeof serverTime); - - // 处理不同类型的时间输入 - let date; - if (typeof serverTime === 'string' && serverTime.includes('T')) { - // ISO 8601 格式的时间字符串,如 "2025-12-17T14:48:00.000Z" - console.log('ISO 8601格式时间字符串'); - - // 解析ISO字符串为Date对象 - const utcDate = new Date(serverTime); - console.log('UTC Date对象:', utcDate); - - // 转换为北京时间(UTC+8) - const beijingTime = new Date(utcDate.getTime() + (8 * 60 * 60 * 1000)); - console.log('转换为北京时间:', beijingTime); - date = beijingTime; - console.log('Date对象时间戳:', date.getTime()); - } else if (typeof serverTime === 'number') { - // 处理时间戳 - console.log('数字时间戳'); - let adjustedTimestamp = serverTime; - console.log('原始时间戳:', adjustedTimestamp); - // 检查是否是秒级时间戳(小于1e12,毫秒级时间戳通常大于1e12) - if (adjustedTimestamp < 1e12) { - console.log('秒级时间戳,转换为毫秒级'); - adjustedTimestamp *= 1000; - console.log('转换后时间戳:', adjustedTimestamp); - } else { - console.log('已为毫秒级时间戳'); - } - - // 假设时间戳是UTC时间,转换为北京时间 - const utcDate = new Date(adjustedTimestamp); - const beijingTime = new Date(utcDate.getTime() + (8 * 60 * 60 * 1000)); - date = beijingTime; - console.log('转换为北京时间对象:', date); - } else { - console.log('其他格式,使用当前时间'); - // 其他格式,直接使用当前北京时间 - date = new Date(); - console.log('当前时间Date对象:', date); - } - - // 获取北京时间的各个组成部分 - const year = date.getFullYear(); - 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'); - const seconds = date.getSeconds().toString().padStart(2, '0'); - - const formattedTime = `${month}-${day} ${hours}:${minutes}`; - console.log('格式化后的完整时间:', `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`); - console.log('返回的格式化时间:', formattedTime); - - return formattedTime; - }, - - // 解析消息时间字符串为时间戳 - 统一使用北京时间 - parseMessageTime: function(timeStr) { - if (!timeStr) return Date.now(); // 返回当前时间戳 - - console.log('=== parseMessageTime 调试 ==='); - console.log('输入时间字符串:', timeStr); - - // 处理"月-日 时:分"格式 - const dateTimeMatch = timeStr.match(/^(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{2})$/); - if (dateTimeMatch) { - console.log('匹配到月-日 时:分格式'); - 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(); // 使用当前年份 - - console.log('解析结果:', { year, month, day, hours, minutes }); - - // 直接构建Date对象,使用北京时间(不添加T和Z,避免时区问题) - const date = new Date(year, month - 1, day, hours, minutes, 0); - const timestamp = date.getTime(); - - console.log('转换为Date对象:', date); - console.log('返回时间戳:', timestamp); - - return timestamp; - } - - // 对于其他格式,尝试直接解析 - const date = new Date(timeStr); - console.log('直接解析为Date对象:', date); - console.log('返回时间戳:', date.getTime()); - - return date.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) { - console.log('=== formatTimestampToTime 调试 ==='); - console.log('输入timestamp:', timestamp); - console.log('输入类型:', typeof timestamp); - - // 处理不同类型的时间输入 - let date; - if (typeof timestamp === 'string' && timestamp.includes('T')) { - // ISO 8601 格式的时间字符串,如 "2025-12-17T14:48:00.000Z" - console.log('ISO 8601格式时间字符串'); - - // 解析ISO字符串为Date对象 - const utcDate = new Date(timestamp); - console.log('UTC Date对象:', utcDate); - - // 转换为北京时间(UTC+8) - const beijingTime = new Date(utcDate.getTime() + (8 * 60 * 60 * 1000)); - console.log('转换为北京时间:', beijingTime); - date = beijingTime; - } else if (typeof timestamp === 'number') { - // 处理时间戳 - console.log('数字时间戳'); - let adjustedTimestamp = timestamp; - // 检查是否是秒级时间戳(小于1e12,毫秒级时间戳通常大于1e12) - if (adjustedTimestamp < 1e12) { - // 秒级时间戳转换为毫秒级 - adjustedTimestamp *= 1000; - console.log('转换后时间戳:', adjustedTimestamp); - } - - // 假设时间戳是UTC时间,转换为北京时间 - const utcDate = new Date(adjustedTimestamp); - const beijingTime = new Date(utcDate.getTime() + (8 * 60 * 60 * 1000)); - date = beijingTime; - console.log('转换为北京时间对象:', date); - } else { - // 其他格式,直接使用当前北京时间 - date = new Date(); - console.log('其他格式,使用当前时间:', date); - } - - // 获取北京时间的各个组成部分 - 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'); - - const formattedTime = `${month}-${day} ${hours}:${minutes}`; - console.log('返回的格式化时间:', formattedTime); - - return formattedTime; - }, - - /** - * 判断是否应该显示消息时间 - */ - 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; - } - - // 调试:记录发送消息时的时间 - console.log('=== 发送消息时间调试 ==='); - console.log('当前时间(Date对象):', new Date()); - - // 获取当前北京时间,确保时间准确 - const currentTime = this.getFormattedTime(); - const shortTime = this.getShortTime(); - - console.log('getFormattedTime返回:', currentTime); - console.log('getShortTime返回:', shortTime); - - // 创建消息对象,立即设置正确的时间 - const newMessage = { - type: 'message', - sender: 'me', - content: this.data.inputValue.trim(), - time: currentTime, - shortTime: shortTime, - showTime: this.shouldShowTime(), - status: 'sending', // 初始状态为发送中 - // 【关键修复】添加真实用户ID相关字段 - targetUserId: this.data.userId, - // 调试:添加发送时间详情 - sendDebugInfo: { - rawDate: new Date(), - rawTimestamp: Date.now(), - formattedTime: currentTime - } - }; - - console.log('开始发送消息,目标用户ID:', this.data.userId, '会话ID:', this.data.conversationId); - - // 添加新消息到消息列表 - const messages = [...this.data.messages]; - messages.push(newMessage); - - // 重新处理所有消息的时间显示,确保时间显示正确 - const processedMessages = this.processMessageTimes(messages); - - // 立即更新UI,确保滚动在数据更新后执行 - this.setData({ - messages: processedMessages, - inputValue: '' - }, () => { - // 数据更新完成后滚动到底部 - this.scrollToBottom(); - }); - - // 保存消息到本地存储 - this.saveMessagesToStorage(processedMessages); - - // 会话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 if (this.data.userId && this.data.userId !== 'user') { - chatTargetId = this.data.userId; - } else { - console.error('无效的聊天对象ID,无法创建会话:', this.data.userId); - return; - } - - // 确保chatTargetId是有效的客服ID,不是字符串'user' - if (chatTargetId === 'user' || !chatTargetId || chatTargetId === 0) { - console.error('无效的客服ID,无法创建会话:', chatTargetId); - return; - } - - if (currentUserId && chatTargetId) { - console.log('尝试创建正式会话...'); - - // 发送创建会话请求,使用正确的格式 - // 关键修复:确保managerId始终有效,符合服务器要求 - const createConversationMessage = { - type: 'create_conversation', - userId: isManager ? chatTargetId : currentUserId, // 用户ID始终是普通用户的ID - timestamp: Date.now() - }; - - // 确保managerId始终有效,符合服务器要求 - let validManagerId = ''; - if (isManager) { - // 客服模式:当前用户是客服,ID应该作为managerId - validManagerId = currentUserId; - } else { - // 用户模式:聊天对象是客服,ID应该作为managerId - validManagerId = chatTargetId; - } - - // 验证managerId,避免发送无效的managerId导致服务器拒绝 - if (!validManagerId || validManagerId === 'user' || validManagerId === 0) { - console.error('无效的managerId,无法创建会话:', validManagerId); - // 尝试从其他来源获取有效的managerId - // 1. 尝试从页面数据获取 - if (this.data.managerId && this.data.managerId !== 'user' && this.data.managerId !== 0) { - validManagerId = this.data.managerId; - } - // 2. 尝试从聊天对象ID获取 - else if (this.data.userId && this.data.userId !== 'user' && this.data.userId !== 0) { - validManagerId = this.data.userId; - } - } - - // 确保managerId有效后再添加到请求中 - if (validManagerId && validManagerId !== 'user' && validManagerId !== 0) { - createConversationMessage.managerId = validManagerId; - } else { - console.error('无法获取有效的managerId,会话创建失败'); - return; - } - - // 添加待发送消息到pendingMessages,等待会话创建成功后再发送 - if (!this.pendingMessages) { - this.pendingMessages = []; - } - // 将当前消息添加到待发送列表 - this.pendingMessages.push(newMessage); - - console.log('发送创建会话请求:', createConversationMessage); - console.log('消息已添加到待发送列表,等待会话创建成功:', newMessage.content); - - // 使用增强的消息发送函数,确保消息去重和字段验证 - socketManager.sendEnhancedMessage(createConversationMessage); - - // 关键修复:会话创建期间,不立即发送消息,而是等待会话创建成功后再发送 - // 消息已经添加到pendingMessages,会在会话创建成功后通过handleReceivedMessage函数处理 - return; - } - } - - // 根据模式决定发送方式 - 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) { - // 目标是客服,确保使用有效的managerId - chatTargetId = this.data.managerId || ''; - // 如果managerId无效,尝试使用userId作为备选 - if (!chatTargetId || chatTargetId === 'user' || chatTargetId === 0) { - chatTargetId = this.data.userId || ''; - } - } else { - chatTargetId = this.data.userId || ''; - } - - // 确保chatTargetId是有效的,不是'user'或0 - if (chatTargetId === 'user' || chatTargetId === 0) { - console.error('无效的聊天对象ID:', chatTargetId); - return false; - } - - // 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) - }; - - // 关键修复:确保managerId始终有效,符合服务器要求 - // 验证managerId,避免发送无效的managerId导致服务器拒绝 - if (!wsMessage.managerId || wsMessage.managerId === 'user' || wsMessage.managerId === 0) { - console.error('无效的managerId,无法发送消息:', wsMessage.managerId); - // 尝试从其他来源获取有效的managerId - let validManagerId = ''; - - // 1. 尝试从页面数据获取 - if (this.data.managerId && this.data.managerId !== 'user' && this.data.managerId !== 0) { - validManagerId = this.data.managerId; - } - // 2. 尝试从聊天对象ID获取 - else if (chatTargetId && chatTargetId !== 'user' && chatTargetId !== 0) { - validManagerId = chatTargetId; - } - // 3. 尝试从接收者ID获取 - else if (receiverId && receiverId !== 'user' && receiverId !== 0) { - validManagerId = receiverId; - } - - if (validManagerId) { - console.log('使用备用managerId:', validManagerId); - wsMessage.managerId = validManagerId; - } else { - console.error('无法获取有效的managerId,消息发送失败'); - wx.showToast({ - title: '消息发送失败,无法获取有效的客服ID', - icon: 'none', - duration: 2000 - }); - return false; - } - } - - // 额外验证:确保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() { - // 调试:记录当前时间 - console.log('=== getFormattedTime 调试 ==='); - console.log('当前Date对象:', new Date()); - - // 获取当前本地时间(设备的本地时间已经是北京时间) - const now = new Date(); - const year = now.getFullYear(); - 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'); - const seconds = now.getSeconds().toString().padStart(2, '0'); - - const formattedTime = `${month}-${date} ${hours}:${minutes}`; - console.log('格式化后时间:', formattedTime); - console.log('完整时间:', `${year}-${month}-${date} ${hours}:${minutes}:${seconds}`); - - return formattedTime; - }, - - // 获取仅包含时分的时间格式 - 统一使用北京时间 - 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(() => { - // 使用scroll-top属性滚动到底部,这是更可靠的方式 - this.setData({ - scrollTop: 999999, - showScrollToBottomBtn: false - }); - }, 50); - }, - - // 显示表情 - 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 () { - // 页面显示时,初始化滚动区域高度 - this.initScrollHeight(); - // 页面显示时,滚动到最新消息 - console.log('聊天页面显示,滚动到最新消息'); - // 只在页面首次显示时滚动到底部,避免频繁跳转 - if (!this.hasScrolledOnShow) { - this.scrollToBottom(); - this.hasScrolledOnShow = true; - } - }, - - /** - * 初始化滚动区域高度 - */ - initScrollHeight: function() { - wx.createSelectorQuery().select('#message-list').boundingClientRect(res => { - if (res) { - this.setData({ - scrollHeight: res.height - }); - } - }).exec(); - }, - - /** - * 滚动事件处理函数 - */ - onScroll: function(e) { - const { scrollTop, scrollHeight, contentHeight } = e.detail; - - // 更新滚动位置 - this.setData({ - lastScrollTop: scrollTop, - contentHeight: contentHeight - }); - - // 计算是否接近底部(距离底部50px以内) - const distanceToBottom = contentHeight - scrollTop - scrollHeight; - const isNearBottom = distanceToBottom < 50 && distanceToBottom > -100; - this.setData({ - isNearBottom: isNearBottom - }); - - // 如果接近底部,隐藏"到达最底部"按钮 - if (isNearBottom) { - this.setData({ - showScrollToBottomBtn: false - }); - } - }, - - /** - * 生命周期函数--监听页面隐藏 - */ - 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 + this.setData({ + messages: [...this.data.messages, reply] }); }, 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('稳定性检查定时器已清理'); - } - - // 关键修复:恢复原始的全局消息处理函数 - const app = getApp(); - if (this.originalGlobalMessageHandler) { - app.globalData.onNewMessage = this.originalGlobalMessageHandler; - 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_')); - - 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(包含连字符且不是用户格式),尝试修复 - let isChatUserIdSessionId = chatUserId && chatUserId.includes('-') && !chatUserId.startsWith('user_') && !chatUserId.startsWith('temp_'); - - if (isChatUserIdSessionId) { - console.warn('⚠️ 检测到可能的ID混淆:聊天对象ID看起来像会话ID:', chatUserId); - - // 如果没有明确的会话ID,使用这个作为会话ID - if (!conversationId || conversationId === chatUserId) { - conversationId = chatUserId; - console.log('设置会话ID为:', conversationId); - } - - // 【新增】对于客服模式,尝试从会话ID中获取关联的用户ID - if (isManager) { - console.log('客服模式下,会话ID为:', conversationId); - - // 1. 尝试从本地存储的会话映射中获取用户ID - try { - const conversationMap = wx.getStorageSync('conversation_user_map') || {}; - for (const [userId, convId] of Object.entries(conversationMap)) { - if (convId === conversationId) { - chatUserId = userId; - console.log('从会话映射中找到关联的用户ID:', chatUserId); - isRealUserId = chatUserId.startsWith('user_'); - break; - } - } - } catch (e) { - console.error('从会话映射中获取用户ID失败:', e); - } - - // 2. 如果第一步失败,尝试从本地存储的会话列表中获取 - if (!chatUserId.startsWith('user_')) { - try { - const conversations = wx.getStorageSync('conversations') || []; - const conversation = conversations.find(conv => conv.conversationId === conversationId); - if (conversation && conversation.userId) { - chatUserId = conversation.userId; - console.log('从会话列表中获取到关联的用户ID:', chatUserId); - isRealUserId = chatUserId.startsWith('user_'); - } - } catch (e) { - console.error('从会话列表中获取用户ID失败:', e); - } - } - - // 3. 如果仍然失败,尝试从所有聊天消息中查找 - if (!chatUserId.startsWith('user_')) { - try { - const storageInfo = wx.getStorageInfoSync(); - const chatKeys = storageInfo.keys.filter(key => key.startsWith('chat_messages_')); - for (const key of chatKeys) { - const messages = wx.getStorageSync(key); - if (Array.isArray(messages)) { - for (const msg of messages) { - if (msg.conversationId === conversationId && msg.senderId && msg.senderId.startsWith('user_')) { - chatUserId = msg.senderId; - console.log('从聊天消息中获取到关联的用户ID:', chatUserId); - isRealUserId = true; - break; - } - } - if (chatUserId.startsWith('user_')) { - break; - } - } - } - } catch (e) { - console.error('从聊天消息中获取用户ID失败:', e); - } - } - } - } - - // 【关键修复】真实用户ID特殊处理 - if (isRealUserId || chatUserId.startsWith('user_')) { - console.log('🔍 检测到真实用户ID或正式用户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); - } - } - - // 【关键修复】对于客服模式,确保我们有正确的用户ID来查询消息 - if (isManager && isChatUserIdSessionId) { - // 客服模式下,当聊天对象ID是会话ID时,我们需要确保请求参数中包含正确的信息 - console.log('客服模式下,使用会话ID查询消息,添加额外的查询参数'); - this.data.queryByUserId = false; - } - - // 验证必要参数 - 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: 200, // 增加限制,获取更多历史消息 - 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; // 请求包含用户详情 - // 【新增】直接指定要查询的用户ID - if (chatUserId.startsWith('user_')) { - requestData.specific_user_id = chatUserId; - } - console.log('客服模式请求参数:', requestData); - } - - // 【关键修复】对于真实用户或正式用户,添加特殊标记 - if (isRealUserId || chatUserId.startsWith('user_')) { - 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('消息列表为空,尝试从本地存储加载历史消息'); - - // 尝试从本地存储加载消息 - const hasLocalMessages = this.loadMessagesFromStorage(); - if (hasLocalMessages) { - console.log('从本地存储成功加载到历史消息'); - wx.showToast({ - title: '从本地加载历史消息成功', - icon: 'success', - duration: 1500 - }); - } else { - console.log('本地存储也没有消息,显示暂无消息'); - wx.showToast({ - title: '暂无历史消息', - icon: 'none' - }); - } - } - }, 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) { - // 关键修复:即使没有服务器返回的消息,也要检查本地存储的消息 - console.log('没有从服务器获取到消息,检查本地存储的消息'); - // 尝试从本地存储加载消息 - const hasLocalMessages = this.loadMessagesFromStorage(); - if (hasLocalMessages) { - console.log('从本地存储加载了历史消息'); - return; - } - - // 【关键修复】对于客服,无论是否模拟模式,都尝试主动查询所有消息 - if (this.data.isManager) { - console.log('客服模式没有消息,尝试使用managerId直接查询所有相关消息'); - this.sendDirectManagerMessageQuery(); - return; - } - - // 【关键修复】对于真实用户,如果没有消息,尝试主动从数据库查询 - if (this.data.isRealUserId || !this.data.isMockMode) { - console.log('真实用户或非模拟模式没有消息,尝试直接从数据库查询'); - this.tryDirectDatabaseQuery(); - return; - } - // 对于普通用户,显示暂无消息 - console.log('显示暂无历史消息提示'); - 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('消息是自己发送的,显示在右侧'); - } - - // 额外检查:如果消息的message_id包含当前用户ID的部分,也视为自己的消息 - if (!isSelf && currentUserId && (String(msg.message_id || msg.messageId).includes(currentUserId.substring(5, 15)))) { - isSelf = true; - console.log('消息ID包含当前用户ID,显示在右侧'); - } - - // 客服特殊逻辑:只有客服自己发送的消息才视为"自己的"消息 - // 修复:不再将发送给客服的消息视为自己的消息,确保用户发送的消息显示在左侧 - if (isManager && managerId) { - const stringManagerId = String(managerId); - // 只有当senderId是客服ID,或者sender_type是客服时,才视为自己的消息 - isSelf = isSelf || - String(senderId) === stringManagerId || - // 如果消息是客服发送的(sender_type=2),也视为自己的消息 - (msg.sender_type === 2 || msg.senderType === 'manager' || msg.senderType === 2); - console.log('客服模式消息方向判断结果:', { isSelf, senderId, receiverId, currentUserId, managerId }); - } - - 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'); - - // 关键修复:只在页面首次加载或用户接近底部时才滚动到底部,避免频繁跳转 - // 如果是首次加载历史消息,或者用户当前已经接近底部,才自动滚动到底部 - if (this.data.isNearBottom || this.data.messages.length === 0) { - console.log('首次加载历史消息或用户接近底部,自动滚动到底部'); - this.scrollToBottom(); - } else { - console.log('用户不在底部,不自动滚动,保持当前位置'); - } - - // 【关键修复】根据消息情况显示不同的提示 - 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: '尝试备用查询...', - }); - - // 首先尝试从本地存储加载消息,这是最可靠的备用方案 - console.log('优先尝试从本地存储加载消息作为备用方案'); - const hasLocalMessages = this.loadMessagesFromStorage(); - if (hasLocalMessages) { - console.log('从本地存储加载到历史消息,显示成功'); - wx.hideLoading(); - return; - } - - // 如果本地存储没有消息,再尝试其他备用方案 - console.log('本地存储没有消息,尝试其他备用方案'); - - // 构建备用查询请求 - 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); - - // 使用WebSocket发送备用查询请求,避免调用无效的API - const sent = socketManager.send(queryData); - if (sent) { - console.log('WebSocket备用查询请求已发送'); - // 设置超时处理 - setTimeout(() => { - wx.hideLoading(); - console.log('WebSocket备用查询超时,可能没有消息'); - }, 3000); - } else { - console.error('WebSocket备用查询请求发送失败'); - wx.hideLoading(); - console.log('所有查询方式都失败,可能确实没有消息'); - } - } catch (e) { - wx.hideLoading(); - console.error('备用查询过程中出错:', e); - } - }, - - /** - * 客服直接查询所有相关消息(专为客服设计的备用方法) - */ - sendDirectManagerMessageQuery: function() { - console.log('=== sendDirectManagerMessageQuery 开始 ==='); - console.log('客服模式下尝试直接查询所有相关消息...'); - - try { - // 显示加载提示 - wx.showLoading({ - title: '正在查询所有消息...', - }); - - const managerId = wx.getStorageSync('managerId') || ''; - const currentUserId = AuthManager.getUserId() || wx.getStorageSync('userId') || ''; - - // 构建客服专属查询请求 - const queryData = { - type: 'get_messages', - conversationId: this.data.conversationId || '', - targetUserId: this.data.userId || '', - userId: currentUserId, - userType: 'manager', - managerId: managerId, - page: 1, - limit: 200, // 增加限制,获取更多历史消息 - timestamp: Date.now(), - // 客服专属参数 - manager_mode: true, - include_all_messages: true, - include_deleted: false, - with_user_details: true, - // 强制使用managerId查询 - force_manager_query: true - }; - - console.log('发送客服专属消息查询请求:', queryData); - - // 使用WebSocket发送查询请求 - const sent = socketManager.send(queryData); - if (sent) { - console.log('客服专属查询请求已发送'); - // 设置超时处理 - setTimeout(() => { - wx.hideLoading(); - console.log('客服专属查询超时,可能没有消息'); - }, 5000); - } else { - console.error('客服专属查询请求发送失败'); - wx.hideLoading(); - // 如果WebSocket发送失败,尝试直接调用API - this.tryApiMessageQuery(queryData); - } - } catch (e) { - wx.hideLoading(); - console.error('客服专属查询过程中出错:', e); - } - }, - - /** - * 尝试直接调用API查询消息(最后的备用方案) - */ - tryApiMessageQuery: function(queryData) { - console.log('=== tryApiMessageQuery 开始 ==='); - console.log('尝试直接调用API查询消息...'); - - try { - // 显示加载提示 - wx.showLoading({ - title: '正在查询消息...', - }); - - // 构建API请求 - wx.request({ - url: 'http://localhost:3003/api/chat/messages', - method: 'POST', - data: queryData, - header: { - 'content-type': 'application/json' - }, - success: (res) => { - console.log('API查询消息成功:', res.data); - if (res.data && res.data.success) { - // 处理返回的消息 - const messages = res.data.data || []; - if (messages.length > 0) { - // 转换并显示消息 - 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 || ''; - const isSelf = String(senderId) === String(queryData.userId) || String(receiverId) === String(queryData.managerId); - 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', - 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: isSelf ? 'me' : 'other', - senderId: senderId, - receiverId: receiverId, - senderType: msg.sender_type || msg.senderType || 'unknown', - isSelf: isSelf, - time: formattedTime, - shortTime: this.extractShortTime(formattedTime), - status: 'sent', - isRead: msg.is_read || 0, - serverData: msg, - timestamp: timestamp - }; - }); - - // 按时间排序 - formattedMessages.sort((a, b) => { - return Number(a.timestamp) - Number(b.timestamp); - }); - - // 更新消息列表 - this.setData({ - messages: formattedMessages - }, () => { - console.log('API查询消息已成功更新到UI'); - this.scrollToBottom(); - }); - - // 保存到本地存储 - this.saveMessagesToStorage(formattedMessages); - - wx.hideLoading(); - wx.showToast({ - title: `从API加载成功,${formattedMessages.length}条消息`, - icon: 'success', - duration: 2000 - }); - } else { - wx.hideLoading(); - console.log('API查询也没有返回消息'); - wx.showToast({ - title: '暂无历史消息', - icon: 'none' - }); - } - } else { - wx.hideLoading(); - console.error('API查询失败:', res.data.message); - wx.showToast({ - title: '查询失败', - icon: 'none' - }); - } - }, - fail: (error) => { - wx.hideLoading(); - console.error('API请求失败:', error); - console.log('所有查询方式都失败,可能确实没有消息'); - wx.showToast({ - title: '网络请求失败', - icon: 'none' - }); - } - }); - } catch (e) { - wx.hideLoading(); - console.error('API查询过程中出错:', 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: '加载更多消息...', - }); - - // 实现真正的分页加载,根据已有消息数量计算页码 - const page = Math.floor(this.data.messages.length / 50) + 1; - socketManager.send({ - type: 'get_messages', - conversationId: conversationId, - page: page, // 根据已有消息数量计算页码 - limit: 50 - }); - }, - - /** - * 用户点击右上角分享 - */ - onShareAppMessage: function () { - } -}); +}); \ No newline at end of file diff --git a/pages/chat-detail/index.json b/pages/chat-detail/index.json index 383ecfb..796c826 100644 --- a/pages/chat-detail/index.json +++ b/pages/chat-detail/index.json @@ -1,7 +1,3 @@ { - "navigationBarTitleText": "聊天详情", - "navigationBarBackgroundColor": "#ffffff", - "navigationBarTextStyle": "black", - "usingComponents": {}, - "enablePullDownRefresh": false -} + "navigationBarTitleText": "聊天详情" +} \ No newline at end of file diff --git a/pages/chat-detail/index.wxml b/pages/chat-detail/index.wxml index d8d5f49..009a123 100644 --- a/pages/chat-detail/index.wxml +++ b/pages/chat-detail/index.wxml @@ -1,108 +1,44 @@ - - - - {{connectionMessage}} - - - - - - - 模拟模式切换 + + + + + + + 聊天对象 + + + + - - - - - - - - - {{item.time}} + + + + + + - - - - - 谨防诈骗 - {{item.content}} - + + 您好,有什么可以帮助您的吗? + 刚刚 - - - - - {{userName ? userName.charAt(0) : (avatar || '用')}} - - - - - - {{userName}} - - {{item.content}} - - + + + + + + 你好,我想咨询一下产品信息 + 刚刚 - - - - - - - - {{item.content}} - - - - - + + - + - - - 到达最底部 - - - - - - - 😊 - 🔊 - - - - - - + - 发送 - - + + + - + \ No newline at end of file diff --git a/pages/chat-detail/index.wxss b/pages/chat-detail/index.wxss index 2f7ce2e..c94825f 100644 --- a/pages/chat-detail/index.wxss +++ b/pages/chat-detail/index.wxss @@ -1,468 +1,155 @@ -/* 微信聊天界面样式 */ -.chat-detail-container { +.container { + background-color: #f0f0f0; + min-height: 100vh; display: flex; flex-direction: column; - height: 100vh; - background-color: #e5e5e5; - font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; -} - -/* 连接状态显示样式 */ -.connection-status { - padding: 8rpx 0; - text-align: center; - font-size: 24rpx; - color: #fff; - position: relative; - z-index: 10; -} - -.connection-status.connecting { - background-color: #FF9800; -} - -.connection-status.connected { - background-color: #4CAF50; -} - -.connection-status.error { - background-color: #F44336; } -.connection-status.disconnected { - background-color: #9E9E9E; -} - -.status-text { - display: inline-block; - padding: 0 20rpx; - border-radius: 10rpx; -} - -.reconnect-btn { - display: inline-block; - margin-left: 20rpx; - padding: 0 20rpx; - font-size: 24rpx; - line-height: 40rpx; - background-color: rgba(255, 255, 255, 0.3); - color: #fff; - border: 1px solid rgba(255, 255, 255, 0.5); - border-radius: 16rpx; -} - -.test-mode-switch { - padding: 20rpx; - background-color: #f8f8f8; +.chat-header { + background-color: #fff; display: flex; align-items: center; - justify-content: center; + justify-content: space-between; + padding: 16rpx 24rpx; border-bottom: 1rpx solid #e8e8e8; + position: sticky; + top: 0; + z-index: 100; } -.mode-label { - margin-left: 20rpx; - font-size: 28rpx; - color: #666; -} - -/* 消息列表区域 - 微信风格 */ -.wechat-message-list { - flex: 1; - padding: 20rpx 20rpx 40rpx; - box-sizing: border-box; - overflow-y: auto; - background-color: #e5e5e5; -} - -/* 消息容器 */ -.wechat-message-container { - display: flex; - flex-direction: column; - gap: 20rpx; -} - -/* 时间显示 */ -.wechat-time-display { - display: flex; - justify-content: center; - margin: 20rpx 0; -} - -.wechat-time-text { - background-color: rgba(0, 0, 0, 0.1); - color: #fff; - font-size: 24rpx; - padding: 6rpx 20rpx; - border-radius: 12rpx; -} - -/* 系统消息 */ -.wechat-system-message { +.header-back, .header-more { + width: 60rpx; + height: 60rpx; display: flex; + align-items: center; justify-content: center; - margin: 10rpx 0; -} - -.wechat-system-content { - background-color: rgba(255, 255, 255, 0.8); - border-radius: 18rpx; - padding: 16rpx 24rpx; - max-width: 80%; - box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); -} - -.wechat-warning-title { - color: #ff0000; - font-size: 28rpx; + font-size: 32rpx; font-weight: bold; - margin-bottom: 10rpx; -} - -.wechat-system-text { - color: #606266; - font-size: 26rpx; - line-height: 36rpx; -} - -/* 商品信息卡片 */ -.wechat-goods-card { - display: flex; - background-color: #ffffff; - border-radius: 18rpx; - padding: 20rpx; - margin: 10rpx 0; - box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); - align-self: flex-start; - max-width: 80%; } -.wechat-goods-avatar { - width: 120rpx; - height: 120rpx; - background-color: #f0f0f0; - border-radius: 8rpx; - flex-shrink: 0; - margin-right: 20rpx; -} - -.wechat-goods-content { +.header-info { flex: 1; - min-width: 0; + text-align: center; } -.wechat-goods-title { - font-size: 28rpx; - color: #303133; - font-weight: 500; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - text-overflow: ellipsis; - margin-bottom: 10rpx; +.header-name { + font-size: 32rpx; + font-weight: bold; + color: #333; } -.wechat-goods-desc { - font-size: 26rpx; - color: #606266; - display: block; - margin-bottom: 10rpx; - line-height: 36rpx; +.chat-content { + flex: 1; + padding: 20rpx; + overflow-y: auto; } -.wechat-goods-footer { +.chat-messages { display: flex; - justify-content: space-between; - align-items: center; -} - -.wechat-goods-price { - font-size: 32rpx; - color: #e64340; - font-weight: bold; -} - -.wechat-goods-type { - font-size: 24rpx; - color: #909399; - background-color: #f0f9ff; - padding: 4rpx 16rpx; - border-radius: 12rpx; + flex-direction: column; + gap: 20rpx; } -/* 消息项 */ -.wechat-message-item { +.message-item { display: flex; - margin: 10rpx 0; align-items: flex-end; + gap: 16rpx; + max-width: 100%; } -/* 对方消息 */ -.wechat-other-message { +.message-item.other { flex-direction: row; } -/* 我的消息 */ -.wechat-my-message { +.message-item.me { flex-direction: row-reverse; } -/* 头像 - 圆形 */ -.wechat-avatar { - width: 76rpx; - height: 76rpx; +.avatar { + width: 64rpx; + height: 64rpx; border-radius: 50%; - background-color: #9aa5b1; - display: flex; - align-items: center; - justify-content: center; - margin: 0 12rpx; - flex-shrink: 0; overflow: hidden; + flex-shrink: 0; } -.wechat-my-avatar { - background-color: #d8d8d8; -} - -.wechat-avatar-text { - color: #ffffff; - font-size: 32rpx; - font-weight: 500; -} - -/* 消息包装器 */ -.wechat-message-wrapper { - max-width: 75%; - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 6rpx; -} - -/* 客服姓名显示 */ -.wechat-message-name { - font-size: 24rpx; - color: #909399; - margin-left: 4rpx; - margin-bottom: 2rpx; -} - -.wechat-my-wrapper { - align-items: flex-end; +.avatar image { + width: 100%; + height: 100%; } -/* 消息气泡 - 添加三角形箭头 */ -.wechat-message-bubble { +.message-bubble { + max-width: 70%; + padding: 16rpx 24rpx; + border-radius: 20rpx; position: relative; - padding: 14rpx 20rpx; - max-width: 100%; - word-wrap: break-word; - word-break: break-all; -} - -.wechat-other-bubble { - background-color: #ffffff; - border-radius: 18rpx; -} - -.wechat-my-bubble { - background-color: #07c160; - border-radius: 18rpx; } -/* 对方消息气泡三角形 */ -.wechat-other-bubble::before { - content: ''; - position: absolute; - left: -10rpx; - bottom: 18rpx; - width: 0; - height: 0; - border-style: solid; - border-width: 10rpx 10rpx 10rpx 0; - border-color: transparent #ffffff transparent transparent; -} - -/* 我的消息气泡三角形 */ -.wechat-my-bubble::before { - content: ''; - position: absolute; - right: -10rpx; - bottom: 18rpx; - width: 0; - height: 0; - border-style: solid; - border-width: 10rpx 0 10rpx 10rpx; - border-color: transparent transparent transparent #07c160; -} - -/* 消息文本 */ -.wechat-message-text { - font-size: 32rpx; - line-height: 44rpx; - color: #303133; +.message-item.other .message-bubble { + background-color: #fff; + border-bottom-left-radius: 8rpx; } -.wechat-my-text { - color: #ffffff; +.message-item.me .message-bubble { + background-color: #92E3A9; + border-bottom-right-radius: 8rpx; } -/* 图片消息样式 - 使用微信插件风格 */ -.wechat-message-image { - width: 280rpx; - height: 280rpx; - max-width: 75%; - max-height: 500rpx; - border-radius: 16rpx; - margin: 8rpx 0; - box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); - background-color: #f5f5f5; -} - -/* 对方图片消息样式 - 左侧缩进 */ -.wechat-other-image { - margin-left: 16rpx; - align-self: flex-start; -} - -/* 我的图片消息样式 - 右侧缩进 */ -.wechat-my-image { - margin-right: 16rpx; - align-self: flex-end; +.message-content { + display: block; + font-size: 28rpx; + color: #333; + line-height: 1.5; + word-wrap: break-word; } -/* 消息项调整,确保图片和文本消息对齐 */ -.wechat-message-item { - align-items: flex-start; - padding: 4rpx 0; +.message-item.me .message-content { + color: #000; } -/* 消息时间 */ -.wechat-message-time { +.message-time { + display: block; font-size: 22rpx; - color: #909399; - padding: 0 4rpx; -} - -.wechat-my-time { + color: #999; text-align: right; + margin-top: 8rpx; } -/* 输入区域 - 微信风格 */ -.wechat-input-area { - background-color: #f5f5f5; - border-top: 1rpx solid #d8d8d8; - padding: 12rpx 20rpx; - box-sizing: border-box; -} - -/* 输入工具栏 */ -.wechat-input-toolbar { +.chat-input-area { + background-color: #fff; display: flex; align-items: center; - gap: 16rpx; -} - -/* 底部安全区域适配 */ -@media screen and (min-height: 812px) { - .wechat-input-area { - padding-bottom: calc(12rpx + env(safe-area-inset-bottom, 0rpx)); - } -} - -/* 左侧按钮 */ -.wechat-input-left { - display: flex; - align-items: center; - gap: 10rpx; -} - -/* 右侧按钮 */ -.wechat-input-right { - display: flex; - align-items: center; - gap: 10rpx; -} - -/* 表情按钮 */ -.wechat-emoji-btn { - width: 64rpx; - height: 64rpx; - display: flex; - align-items: center; - justify-content: center; - font-size: 36rpx; -} - -/* 语音按钮 */ -.wechat-voice-btn { - width: 64rpx; - height: 64rpx; - display: flex; - align-items: center; - justify-content: center; - font-size: 36rpx; + padding: 16rpx 24rpx; + border-top: 1rpx solid #e8e8e8; + gap: 20rpx; } -/* 更多按钮 */ -.wechat-more-btn { - width: 64rpx; - height: 64rpx; - display: flex; - align-items: center; - justify-content: center; - font-size: 44rpx; - color: #606266; +.message-input { + flex: 1; + background-color: #f5f5f5; + border-radius: 28rpx; + padding: 16rpx 24rpx; + font-size: 28rpx; + color: #333; + min-height: 56rpx; + max-height: 200rpx; + overflow-y: auto; } -/* 发送按钮 */ -.wechat-send-btn { - background-color: #07c160; - color: #ffffff; +.send-button { + background-color: #FF6B81; + color: #fff; font-size: 28rpx; - padding: 0 28rpx; - height: 64rpx; - line-height: 64rpx; - border-radius: 32rpx; + font-weight: bold; + padding: 16rpx 32rpx; + border-radius: 28rpx; + border: none; + min-width: 120rpx; text-align: center; + line-height: normal; + height: auto; } -/* 发送按钮禁用状态 */ -.wechat-send-btn:disabled { - background-color: #c8c8c8; - color: #ffffff; - cursor: not-allowed; -} - -/* 发送按钮点击状态 */ -.wechat-send-btn:active:not(:disabled) { - background-color: #06b354; -} - -/* 输入框包装器 */ -.wechat-input-wrapper { - flex: 1; - background-color: #ffffff; - border: 1rpx solid #d8d8d8; - border-radius: 32rpx; - padding: 0 20rpx; - min-height: 64rpx; - max-height: 180rpx; - display: flex; - align-items: center; - box-shadow: inset 0 2rpx 4rpx rgba(0, 0, 0, 0.05); -} - -/* 消息输入框 */ -.wechat-message-input { - flex: 1; - font-size: 32rpx; - color: #303133; - min-height: 44rpx; - max-height: 160rpx; - line-height: 44rpx; - padding: 0; - margin: 10rpx 0; -} +.send-button:hover { + background-color: #FF526D; +} \ No newline at end of file diff --git a/pages/chat-detail/system-test.js b/pages/chat-detail/system-test.js deleted file mode 100644 index 55a7501..0000000 --- a/pages/chat-detail/system-test.js +++ /dev/null @@ -1,255 +0,0 @@ -// 完整系统测试脚本 - 模拟真实用户消息显示功能 -console.log('========= 开始真实用户消息功能的完整系统测试 ========='); - -// 模拟环境变量和配置 -const CONFIG = { - API_BASE_URL: 'http://localhost:3003', - TEST_USER_ID: 'user_1763452685075_rea007flq', - TEST_MANAGER_ID: 22, - TEST_CONVERSATION_ID: '6257d0f1-9cc3-4e1b-836b-048e9b0ac217' -}; - -// 模拟请求函数 -async function mockRequest(url, options = {}) { - console.log(`\n模拟发送请求到: ${url}`); - console.log('请求参数:', options); - - // 模拟响应 - const mockResponses = { - '/api/chat/history': { - success: true, - messages: [ - { - id: 'msg_001', - sender_id: CONFIG.TEST_USER_ID, - receiver_id: CONFIG.TEST_MANAGER_ID, - content: '你好,我是真实用户,这是我的第一条消息', - created_at: Date.now() - 3600000, - content_type: 1 - }, - { - id: 'msg_002', - sender_id: CONFIG.TEST_USER_ID, - receiver_id: CONFIG.TEST_MANAGER_ID, - content: '这是第二条来自真实用户的消息,应该能正确显示', - created_at: Date.now() - 1800000, - content_type: 1 - } - ], - total: 2, - hasMore: false - }, - '/api/chat/direct_query': { - success: true, - messages: [ - { - id: 'direct_msg_001', - sender_id: CONFIG.TEST_USER_ID, - receiver_id: CONFIG.TEST_MANAGER_ID, - content: '这是通过备用查询获取的消息', - created_at: Date.now() - 3600000, - content_type: 1 - } - ] - } - }; - - // 延迟模拟网络延迟 - await new Promise(resolve => setTimeout(resolve, 500)); - - // 返回模拟响应 - for (const path in mockResponses) { - if (url.includes(path)) { - return { data: mockResponses[path] }; - } - } - - return { data: { success: false, message: '接口不存在' } }; -} - -// 模拟fetchHistoryMessages函数 -async function simulateFetchHistoryMessages(userId, isRealUserId = false) { - console.log(`\n1. 模拟fetchHistoryMessages调用`); - console.log(` - 用户ID: ${userId}`); - console.log(` - 是否真实用户ID: ${isRealUserId}`); - - // 构建请求参数 - const requestData = { - userId: CONFIG.TEST_MANAGER_ID, - targetUserId: userId, - conversationId: CONFIG.TEST_CONVERSATION_ID, - pageSize: 50, - lastMessageId: null, - isRealUser: isRealUserId, - include_all_messages: true - }; - - console.log(` - 构建的请求参数:`, requestData); - - // 模拟发送请求 - try { - const response = await mockRequest(`${CONFIG.API_BASE_URL}/api/chat/history`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(requestData) - }); - - console.log(` - 请求成功,响应数据:`, response); - return response.data; - } catch (error) { - console.error(` - 请求失败:`, error); - return null; - } -} - -// 模拟handleHistoryMessages函数 -function simulateHandleHistoryMessages(response, userId) { - console.log(`\n2. 模拟handleHistoryMessages处理`); - - if (!response || !response.success) { - console.error(` - 响应无效或请求失败`); - return { success: false, messages: [] }; - } - - // 处理消息数据 - const messages = response.messages || []; - console.log(` - 接收到 ${messages.length} 条消息`); - - // 格式化消息 - const formattedMessages = messages.map(msg => { - const isSelf = msg.sender_id === CONFIG.TEST_MANAGER_ID.toString(); - const isRealUserMessage = msg.sender_id === userId; - - return { - id: msg.id, - content: msg.content, - time: new Date(msg.created_at).toLocaleString(), - isSelf, - isRealUserMessage, - senderId: msg.sender_id, - receiverId: msg.receiver_id - }; - }); - - // 按时间排序 - formattedMessages.sort((a, b) => { - return new Date(a.time) - new Date(b.time); - }); - - console.log(` - 格式化后的消息:`); - formattedMessages.forEach(msg => { - const type = msg.isRealUserMessage ? '真实用户消息' : msg.isSelf ? '自己发送' : '其他消息'; - console.log(` - [${type}] ${msg.content} (${msg.time})`); - }); - - // 统计真实用户消息数量 - const realUserMessagesCount = formattedMessages.filter(msg => msg.isRealUserMessage).length; - console.log(` - 识别出的真实用户消息数量: ${realUserMessagesCount}`); - - return { - success: true, - messages: formattedMessages, - realUserMessagesCount - }; -} - -// 模拟备用查询方法 -async function simulateDirectDatabaseQuery(userId) { - console.log(`\n3. 模拟备用数据库查询方法tryDirectDatabaseQuery`); - - const queryData = { - type: 'direct_db_query', - action: 'get_chat_messages', - targetUserId: userId, - conversationId: CONFIG.TEST_CONVERSATION_ID, - userId: CONFIG.TEST_MANAGER_ID, - isManager: true, - limit: 100 - }; - - console.log(` - 构建的查询数据:`, queryData); - - try { - const response = await mockRequest(`${CONFIG.API_BASE_URL}/api/chat/direct_query`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(queryData) - }); - - console.log(` - 备用查询成功,响应数据:`, response); - return response.data; - } catch (error) { - console.error(` - 备用查询失败:`, error); - return null; - } -} - -// 主测试流程 -async function runSystemTest() { - try { - console.log('\n=== 测试1: 使用真实用户ID模拟正常消息加载 ==='); - - // 步骤1: 模拟fetchHistoryMessages - const historyResponse = await simulateFetchHistoryMessages( - CONFIG.TEST_USER_ID, - true // 标记为真实用户ID - ); - - if (!historyResponse) { - console.error('\n❌ 测试失败: 无法获取历史消息'); - return; - } - - // 步骤2: 模拟handleHistoryMessages - const handleResult = simulateHandleHistoryMessages( - historyResponse, - CONFIG.TEST_USER_ID - ); - - if (!handleResult.success) { - console.error('\n❌ 测试失败: 处理历史消息失败'); - return; - } - - // 步骤3: 验证结果 - if (handleResult.realUserMessagesCount > 0) { - console.log(`\n✅ 测试成功: 成功识别并处理了 ${handleResult.realUserMessagesCount} 条真实用户消息`); - } else { - console.warn('\n⚠️ 警告: 未识别到真实用户消息,启动备用查询'); - - // 启动备用查询 - const backupResponse = await simulateDirectDatabaseQuery(CONFIG.TEST_USER_ID); - if (backupResponse && backupResponse.success) { - console.log(`\n✅ 备用查询成功: 获取到 ${backupResponse.messages.length} 条消息`); - } else { - console.error('\n❌ 备用查询也失败'); - } - } - - // 最终验证 - console.log('\n=== 系统测试总结 ==='); - console.log(`✅ fetchHistoryMessages函数成功识别真实用户ID: ${CONFIG.TEST_USER_ID}`); - console.log(`✅ handleHistoryMessages函数成功处理消息数据`); - console.log(`✅ 系统具备备用查询机制以确保数据可用性`); - console.log(`✅ 所有关键功能模块测试通过`); - - console.log('\n========= 系统测试完成 ========='); - return { - success: true, - realUserMessagesDisplayed: handleResult.realUserMessagesCount > 0 - }; - - } catch (error) { - console.error('\n❌ 系统测试失败:', error); - return { success: false, error: error.message }; - } -} - -// 执行测试 -runSystemTest().then(result => { - if (result.success) { - console.log('\n🎉 测试结果: 成功!系统可以正确显示真实用户消息。'); - } else { - console.log('\n❌ 测试结果: 失败!请检查代码实现。'); - } -}); diff --git a/pages/chat-detail/test-backup-query.js b/pages/chat-detail/test-backup-query.js deleted file mode 100644 index 2d8a948..0000000 --- a/pages/chat-detail/test-backup-query.js +++ /dev/null @@ -1,158 +0,0 @@ -/** - * 备用数据库查询方法测试脚本 - * 用于验证tryDirectDatabaseQuery函数的功能 - */ - -console.log('\n=== 备用数据库查询方法测试 ==='); - -// 模拟测试数据 -const mockRealUserData = { - userId: "user_1763452685075_rea007flq", - isRealUserId: true, - isManager: true, - managerId: 22, - conversationId: "test_conversation_123" -}; - -// 模拟wx.request函数 -function mockWxRequest(options) { - console.log('\n模拟wx.request调用:'); - console.log('- URL:', options.url); - console.log('- 方法:', options.method); - console.log('- 请求数据:', options.data); - - // 模拟成功响应 - setTimeout(() => { - const mockSuccessResponse = { - data: { - success: true, - messages: [ - { - message_id: "direct_msg_001", - sender_id: mockRealUserData.userId, - receiver_id: "manager_22", - content: "这是通过备用查询获取的消息", - created_at: Date.now() - 3600000, - content_type: 1 - }, - { - message_id: "direct_msg_002", - sender_id: mockRealUserData.userId, - receiver_id: "manager_22", - content: "这是第二条备用查询消息", - created_at: Date.now() - 3500000, - content_type: 1 - } - ] - } - }; - - console.log('模拟成功响应:', mockSuccessResponse); - options.success(mockSuccessResponse); - }, 500); -} - -// 模拟tryDirectDatabaseQuery函数 -function simulateTryDirectDatabaseQuery(data) { - console.log('\n1. 测试备用查询函数初始化...'); - - try { - console.log('- 正在准备备用数据库查询...'); - console.log('- 目标用户ID:', data.userId); - console.log('- 会话ID:', data.conversationId); - console.log('- 是否真实用户:', data.isRealUserId); - - // 构建查询数据 - const queryData = { - type: 'direct_db_query', - action: 'get_chat_messages', - targetUserId: data.userId, - conversationId: data.conversationId, - userId: "manager_22", - isManager: data.isManager, - limit: 100, - timestamp: Date.now() - }; - - console.log('- 构建的查询数据:', queryData); - - // 模拟API请求 - mockWxRequest({ - url: 'https://api.example.com/direct_query', - method: 'POST', - data: queryData, - success: (res) => { - console.log('\n2. 测试备用查询成功处理...'); - console.log('- 备用查询成功接收到响应'); - - if (res.data && res.data.success && res.data.messages) { - console.log(`- 成功获取到 ${res.data.messages.length} 条消息`); - console.log('- 消息详情:', res.data.messages); - - // 模拟处理这些消息 - console.log('\n3. 测试消息处理流程...'); - const messages = res.data.messages; - const realUserMessages = messages.filter(msg => - msg.sender_id === data.userId || - (msg.sender_id && msg.sender_id.startsWith('user_') && msg.sender_id.includes('_rea')) - ); - - console.log(`- 识别出的真实用户消息数量: ${realUserMessages.length}`); - console.log('- 最终测试结果: ✅ 备用查询方法工作正常'); - } else { - console.log('- 备用查询没有返回消息'); - } - }, - fail: (error) => { - console.log('\n2. 测试备用查询失败处理...'); - console.log('- 备用查询失败:', error.message); - console.log('- 测试重试逻辑...'); - - // 模拟重试逻辑 - setTimeout(() => { - console.log('- 开始重试常规WebSocket查询...'); - console.log('- 重试逻辑测试通过'); - }, 1000); - } - }); - - return true; - } catch (error) { - console.error('\n❌ 备用查询过程中出错:', error.message); - return false; - } -} - -// 运行完整测试 -function runBackupQueryTest() { - console.log('\n🚀 开始执行备用查询方法测试...'); - - // 执行模拟的备用查询 - const testStarted = simulateTryDirectDatabaseQuery(mockRealUserData); - - // 测试启动结果 - if (testStarted) { - console.log('\n✅ 备用查询测试已成功启动'); - console.log('=== 测试正在进行中... ==='); - } else { - console.log('\n❌ 备用查询测试启动失败'); - } - - // 模拟延迟完成测试 - setTimeout(() => { - console.log('\n=== 备用查询方法测试总结 ==='); - console.log('✅ 备用查询方法的主要功能测试完成'); - console.log('✅ 请求数据构建正确'); - console.log('✅ 响应处理逻辑正常'); - console.log('✅ 真实用户消息识别功能正常'); - console.log('\n备用查询方法可以在常规查询失败时作为有效的备用方案'); - }, 2000); -} - -// 执行测试 -runBackupQueryTest(); - -// 导出测试函数 -module.exports = { - runBackupTest: runBackupQueryTest -}; diff --git a/pages/chat-detail/test-real-user.js b/pages/chat-detail/test-real-user.js deleted file mode 100644 index d198f72..0000000 --- a/pages/chat-detail/test-real-user.js +++ /dev/null @@ -1,192 +0,0 @@ -/** - * 真实用户消息功能测试脚本 - * 用于验证fetchHistoryMessages和handleHistoryMessages函数对真实用户ID的处理 - */ - -// 模拟测试数据 -const mockRealUserData = { - userId: "user_1763452685075_rea007flq", - isRealUserId: true, - isManager: true, - managerId: 22 -}; - -// 模拟消息数据 -const mockMessages = [ - { - message_id: "msg_001", - sender_id: "user_1763452685075_rea007flq", - receiver_id: "manager_22", - content: "这是一条来自真实用户的消息", - created_at: Date.now() - 3600000, - content_type: 1 - }, - { - message_id: "msg_002", - sender_id: "manager_22", - receiver_id: "user_1763452685075_rea007flq", - content: "这是客服回复的消息", - created_at: Date.now() - 3500000, - content_type: 1 - }, - { - message_id: "msg_003", - sender_id: "user_1763452685075_rea007flq", - receiver_id: "manager_22", - content: "这是真实用户的第二条消息", - created_at: Date.now() - 3400000, - content_type: 1 - } -]; - -console.log('\n=== 真实用户消息功能测试 ==='); -console.log('测试数据:', mockRealUserData); -console.log('模拟消息数量:', mockMessages.length); - -// 模拟fetchHistoryMessages中对真实用户ID的处理 -function simulateRealUserIdProcessing(data) { - console.log('\n1. 测试真实用户ID识别...'); - - // 模拟识别逻辑 - const isRealUserId = data.isRealUserId || - (data.userId && - data.userId.startsWith('user_') && - data.userId.includes('_rea')); - - console.log('- 识别结果:', isRealUserId ? '✅ 成功识别为真实用户ID' : '❌ 未能识别为真实用户ID'); - return isRealUserId; -} - -// 模拟构建请求数据 -function simulateRequestData(isRealUserId, data) { - console.log('\n2. 测试请求数据构建...'); - - const requestData = { - type: 'get_messages', - conversationId: "test_conversation", - page: 1, - limit: 100, - userId: "manager_22", - targetUserId: data.userId, - userType: data.isManager ? 'manager' : 'user', - managerId: data.isManager ? data.managerId : undefined, - timestamp: Date.now(), - isRealUser: isRealUserId, - queryByUserId: true - }; - - // 真实用户特殊处理 - if (isRealUserId) { - requestData.real_user_id = data.userId; - } - - console.log('- 构建的请求数据:'); - console.log(' - 是否包含isRealUser标志:', requestData.isRealUser !== undefined); - console.log(' - 是否包含real_user_id:', requestData.real_user_id !== undefined); - console.log(' - 是否包含queryByUserId:', requestData.queryByUserId !== undefined); - - return requestData; -} - -// 模拟消息格式化和处理 -function simulateMessageProcessing(messages, userId) { - console.log('\n3. 测试消息格式化和处理...'); - - // 模拟格式化逻辑 - const formattedMessages = messages.map(msg => { - const senderId = msg.sender_id || msg.senderId || ''; - const isRealUserMessage = senderId === userId || - (senderId && senderId.startsWith('user_') && senderId.includes('_rea')); - - return { - id: msg.message_id, - content: msg.content, - senderId: senderId, - receiverId: msg.receiver_id || msg.receiverId || '', - isRealUserMessage: isRealUserMessage, - time: new Date(msg.created_at).toLocaleString(), - timestamp: msg.created_at - }; - }); - - // 模拟排序 - formattedMessages.sort((a, b) => a.timestamp - b.timestamp); - - // 统计真实用户消息 - const realUserMessages = formattedMessages.filter(msg => msg.isRealUserMessage); - - console.log('- 格式化后的消息总数:', formattedMessages.length); - console.log('- 识别出的真实用户消息数量:', realUserMessages.length); - console.log('- 真实用户消息详情:'); - realUserMessages.forEach(msg => { - console.log(` - 消息ID: ${msg.id}, 内容: ${msg.content.substring(0, 20)}...`); - }); - - return { - allMessages: formattedMessages, - realUserMessages: realUserMessages - }; -} - -// 运行完整测试 -function runFullTest() { - try { - console.log('\n🚀 开始执行完整测试流程...'); - - // 1. 测试真实用户ID识别 - const isRealUserId = simulateRealUserIdProcessing(mockRealUserData); - - // 2. 测试请求数据构建 - const requestData = simulateRequestData(isRealUserId, mockRealUserData); - - // 3. 测试消息格式化和处理 - const processedData = simulateMessageProcessing(mockMessages, mockRealUserData.userId); - - // 4. 验证结果 - console.log('\n4. 验证最终结果...'); - - let allTestsPassed = true; - - // 验证真实用户ID识别 - if (!isRealUserId) { - console.log('❌ 测试失败: 未能正确识别真实用户ID'); - allTestsPassed = false; - } - - // 验证请求数据包含必要字段 - if (!requestData.isRealUser || !requestData.real_user_id) { - console.log('❌ 测试失败: 请求数据缺少必要的真实用户字段'); - allTestsPassed = false; - } - - // 验证真实用户消息识别 - if (processedData.realUserMessages.length !== 2) { - console.log(`❌ 测试失败: 预期识别2条真实用户消息,实际识别${processedData.realUserMessages.length}条`); - allTestsPassed = false; - } - - // 输出最终结果 - console.log('\n=== 测试结果汇总 ==='); - if (allTestsPassed) { - console.log('✅ 所有测试通过!真实用户消息功能正常工作'); - } else { - console.log('❌ 部分测试失败,请检查代码实现'); - } - - return allTestsPassed; - } catch (error) { - console.error('\n❌ 测试执行过程中出错:', error.message); - return false; - } -} - -// 执行测试 -const testResult = runFullTest(); -console.log('\n=== 测试完成 ==='); - -// 导出测试函数供其他模块使用 -module.exports = { - runTest: runFullTest, - mockRealUserData, - mockMessages -}; diff --git a/pages/chat/index.js b/pages/chat/index.js index 0660c22..9bd7c59 100644 --- a/pages/chat/index.js +++ b/pages/chat/index.js @@ -1,953 +1,49 @@ // pages/chat/index.js -import socketManager from '../../utils/websocket.js'; -const AuthManager = require('../../utils/auth.js'); - -// 获取用户名的辅助函数,支持手机号格式的ID - 增强版本 -function getUserNameById(userId, isCustomerServiceMode = false) { - // 严格验证用户ID的有效性 - if (!userId || userId === null || userId === undefined) { - // 返回默认名称而不是null,避免unknown显示 - return '系统用户'; - } - - // 确保userId是字符串类型,并去除前后空格 - const safeUserId = String(userId).trim(); - - // 检查是否是空字符串 - if (safeUserId === '' || safeUserId === '0') { - return '系统用户'; - } - - // 检查是否是手机号格式 (中国手机号格式) - if (/^1[3-9]\d{9}$/.test(safeUserId)) { - // 关键修复:将"客户-"改为"用户-",统一用户显示格式 - return `用户-${safeUserId.slice(-4)}`; - } - - // 纯数字ID - 关键修复:默认显示为客服 - // 客服ID通常是纯数字,用户ID通常是字符串格式 - if (/^\d+$/.test(safeUserId)) { - return `客服${safeUserId}`; - } - - // 增强的用户ID格式处理 - if (safeUserId.startsWith('manager_')) { - // 提取客服ID数字部分 - const managerNum = safeUserId.replace('manager_', ''); - if (!isNaN(managerNum) && managerNum > 0) { - return `客服${managerNum}`; - } - return '客服' + safeUserId.substring(8, 12); - } else if (safeUserId.startsWith('user_')) { - // 用户ID格式处理 - const parts = safeUserId.split('_'); - if (parts.length >= 3) { - // 使用最后一部分生成更友好的名称 - const randomPart = parts[parts.length - 1]; - return `用户${randomPart.substring(0, 4).toUpperCase()}`; - } - return '用户' + safeUserId.substring(5, 9); - } else if (safeUserId.startsWith('test_')) { - return '测试用户'; - } else if (safeUserId.includes('_customer_')) { - // 提取客户标识部分 - const parts = safeUserId.split('_customer_'); - // 关键修复:将"客户-"改为"用户-" - return parts.length > 1 ? `用户-${parts[1].substring(0, 4)}` : '用户'; - } - - // 安全的本地存储访问 - try { - if (isCustomerServiceMode) { - // 客服模式下尝试从本地存储获取用户信息 - const userInfo = wx.getStorageSync('userInfo'); - // 检查userInfo类型,避免重复JSON解析 - if (userInfo && typeof userInfo === 'object') { - // 如果用户ID匹配,返回用户名或手机号后四位 - 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); - } - - // 增强的固定用户名映射表 - const userNameMap = { - 'user_1765610275027_qqkb12ws3': '测试用户', - '17780155537': '客服-5537', - '17844569862': '用户-862', // 关键修复:将"客户-"改为"用户-" - // 添加已知用户ID的映射 - 'user_1763452685075_rea007flq': '用户REA0' // 添加特定用户ID的映射 - }; - - if (userNameMap[safeUserId]) { - return userNameMap[safeUserId]; - } - - // 对于未识别的有效用户ID,返回更友好的用户ID标识 - const displayId = safeUserId.length > 8 ? safeUserId.substring(0, 8) : safeUserId; - - // 客服模式下默认显示为用户,除非是自己 - const isSelf = isCustomerServiceMode && String(displayId) === String(wx.getStorageSync('managerId')); - return isSelf ? `客服-${displayId}` : `用户-${displayId}`; -} - Page({ - - /** - * 页面的初始数据 - */ data: { - messages: [], // 初始为空数组,后续通过数据获取和WebSocket更新 - activeTab: 'all', - webSocketUrl: '' // WebSocket服务器地址 - }, - - // WebSocket消息处理函数 - handleWebSocketMessage: function(message) { - console.log('聊天列表页面接收到消息:', message); - - // 判断是否为新消息 - 支持'new_message'类型(服务器实际发送的类型) - if (message.type === 'new_message') { - const newMessage = message.payload; - this.updateMessageList(newMessage); - } else if (message.type === 'chat_message') { - // 保留原有的'chat_message'类型支持,兼容不同的消息格式 - const newMessage = message.data; - this.updateMessageList(newMessage); - } - }, - - // 更新消息列表 - updateMessageList: function(newMessage) { - console.log('更新消息列表:', newMessage); - const messages = [...this.data.messages]; - - // 获取当前用户信息 - const app = getApp(); - const currentUserId = app.globalData.userInfo?.userId || wx.getStorageSync('userId') || 'unknown'; - const userType = app.globalData.userInfo?.userType || wx.getStorageSync('userType'); - const managerId = app.globalData.userInfo?.managerId || wx.getStorageSync('managerId'); - - // 关键修复:处理不同格式的消息数据 - // 提取消息的发送者、接收者和会话ID - const senderId = newMessage.senderId || newMessage.from || newMessage.sender || newMessage.userId; - const receiverId = newMessage.receiverId || newMessage.to || newMessage.receiver || newMessage.managerId; - const conversationId = newMessage.conversationId || newMessage.conversation_id; - - // 关键修复:对于客服端,只处理用户发送的消息,忽略自己发送的消息 - if (userType === 'manager' && managerId) { - // 客服只处理发送者是用户且接收者是当前客服的消息 - const isFromUser = senderId && String(senderId).includes('user_') || !String(senderId).includes('manager_') && senderId !== managerId; - const isToCurrentManager = String(receiverId) === String(managerId) || String(receiverId).includes('manager_') || !receiverId; - - if (!isFromUser || !isToCurrentManager) { - console.log('客服端过滤掉非用户消息:', { senderId, receiverId, managerId }); - return; // 不处理这条消息 - } - } - - // 格式化消息时间 - 服务器使用createdAt字段 - const now = new Date(); - const messageDate = new Date(newMessage.createdAt || newMessage.timestamp || Date.now()); - let displayTime = ''; - - if (messageDate.toDateString() === now.toDateString()) { - // 今天的消息只显示时间 - displayTime = messageDate.getHours().toString().padStart(2, '0') + ':' + - messageDate.getMinutes().toString().padStart(2, '0'); - } else if (messageDate.toDateString() === new Date(now.getTime() - 86400000).toDateString()) { - // 昨天的消息 - displayTime = '昨天'; - } else { - // 其他日期显示月日 - displayTime = (messageDate.getMonth() + 1) + '月' + messageDate.getDate() + '日'; - } - - // 关键修复:确定显示的用户ID - // 对于客服端,总是显示用户ID - let displayUserId; - if (userType === 'manager') { - displayUserId = senderId; // 客服端显示发送者(用户)ID - } else { - // 用户端根据消息方向确定显示ID - displayUserId = senderId === currentUserId ? receiverId : senderId; - } - - // 使用会话ID作为唯一标识,避免重复问题 - const uniqueId = conversationId || displayUserId; - const existingIndex = messages.findIndex(item => - item.conversationId === conversationId || item.id === uniqueId - ); - - // 使用增强的用户名显示函数 - const displayName = getUserNameById(displayUserId, userType === 'manager'); - - // 消息类型处理 - let messageContent = newMessage.content || ''; - if (newMessage.content_type === 2 || newMessage.type === 'image') { - messageContent = '[图片]'; - } else if (newMessage.content_type === 3 || newMessage.type === 'voice') { - messageContent = '[语音]'; - } else if (newMessage.content_type === 4 || newMessage.type === 'video') { - messageContent = '[视频]'; - } - - if (existingIndex >= 0) { - // 存在该会话的消息,更新内容和时间 - messages[existingIndex] = { - ...messages[existingIndex], - content: messageContent, - time: displayTime, - isRead: false, - // 确保有会话ID - conversationId: conversationId || messages[existingIndex].conversationId - }; - // 将更新的消息移到列表顶部 - const [updatedMessage] = messages.splice(existingIndex, 1); - messages.unshift(updatedMessage); - } else { - // 新会话消息,添加到列表顶部 - messages.unshift({ - id: uniqueId, - name: displayName, - avatar: displayName.charAt(0), - content: messageContent, - time: displayTime, - isRead: false, - unreadCount: 1, - conversationId: conversationId - }); - } - - // 使用setData更新视图 - this.setData({ messages }); - - // 触发消息提示振动(可选) - wx.vibrateShort(); - - // 更新TabBar未读消息数(如果需要) - this.updateTabBarBadge(); - }, - - // 更新TabBar未读消息提示 - 使用自定义TabBar兼容方式 - updateTabBarBadge: function() { - console.log('更新TabBar未读提示,当前消息数:', this.data.messages.length); - // 检查是否有未读消息 - const hasUnread = this.data.messages.some(msg => !msg.isRead); - console.log('是否有未读消息:', hasUnread); - - // 对于自定义TabBar,使用全局状态来管理未读标记 - const app = getApp(); - if (app && app.globalData) { - app.globalData.tabBarBadge = { - chat: hasUnread ? ' ' : '' - }; - console.log('已更新全局TabBar未读标记状态:', hasUnread ? '显示' : '隐藏'); - - // 尝试通过getTabBar方法通知自定义TabBar更新 - try { - const tabBar = this.getTabBar(); - if (tabBar) { - tabBar.setData({ - selected: 'buyer', // 假设聊天页是buyer tab - badge: hasUnread ? ' ' : '' - }); - } - } catch (e) { - console.log('TabBar更新失败,将在下一次页面显示时自动更新'); - } - } - }, - - // 清理所有未读消息状态 - clearAllUnreadStatus: function() { - console.log('清理所有未读消息状态'); - try { - // 1. 更新本地消息列表中的未读状态 - const updatedMessages = this.data.messages.map(msg => ({ - ...msg, - isRead: true - })); - - this.setData({ - messages: updatedMessages - }); - - // 2. 对于自定义TabBar,使用全局状态来管理未读标记 - const app = getApp(); - if (app && app.globalData) { - app.globalData.tabBarBadge = { - chat: '' - }; - console.log('已清理全局TabBar未读标记'); - - // 尝试通过getTabBar方法通知自定义TabBar更新 - try { - const tabBar = this.getTabBar(); - if (tabBar) { - tabBar.setData({ - selected: 'buyer', // 假设聊天页是buyer tab - badge: '' - }); - } - } catch (e) { - console.log('TabBar更新失败,将在下一次页面显示时自动更新'); - } - } - - // 3. 显示成功提示 - wx.showToast({ - title: '已清除所有未读提示', - icon: 'success', - duration: 2000 - }); - } catch (error) { - console.error('清理未读状态失败:', error); - wx.showToast({ - title: '清理失败', - icon: 'none', - duration: 2000 - }); - } - }, - - // 初始化WebSocket连接 - initWebSocket: function() { - try { - const app = getApp(); - - // 使用正确的WebSocket服务器地址 - 开发环境通常是ws://localhost:3003 - // 动态构建WebSocket URL,基于全局配置或环境 - let wsProtocol = 'ws://'; - let wsHost = app.globalData.webSocketUrl || 'localhost:3003'; - let wsUrl; - - // 如果wsHost已经包含协议,直接使用 - if (wsHost.startsWith('ws://') || wsHost.startsWith('wss://')) { - wsUrl = wsHost; - } else { - // 否则添加协议前缀 - wsUrl = `${wsProtocol}${wsHost}`; - } - - this.setData({ webSocketUrl: wsUrl }); - - console.log('WebSocket连接初始化,使用地址:', wsUrl); - - // 连接WebSocket - socketManager.connect(wsUrl); - - // 添加消息监听 - socketManager.on('message', this.handleWebSocketMessage.bind(this)); - - // 添加状态监听,以便调试 - socketManager.on('status', (status) => { - console.log('WebSocket状态更新:', status); - }); - - console.log('聊天列表页面WebSocket已初始化'); - } catch (error) { - console.error('初始化WebSocket失败:', error); - wx.showToast({ - title: 'WebSocket初始化失败', - icon: 'none' - }); - } - }, - - // 清理WebSocket连接 - cleanupWebSocket: function() { - socketManager.off('message', this.handleWebSocketMessage); + chatList: [] }, - // 加载聊天列表数据 - 优化版本 - loadChatList: function() { - wx.showLoading({ title: '加载中' }); - - try { - // 从服务器获取真实的聊天列表数据 - const app = getApp(); - const token = app.globalData.token || wx.getStorageSync('token'); - - // 使用正确的API配置,兼容开发环境 - const baseUrl = app.globalData.baseUrl || 'http://localhost:3003'; - const currentUserId = app.globalData.userInfo?.userId || wx.getStorageSync('userId') || 'unknown'; - console.log('使用API地址:', baseUrl); - console.log('当前用户ID:', currentUserId); - - // 根据用户类型选择正确的API端点 - const userType = app.globalData.userInfo?.userType || wx.getStorageSync('userType'); - const managerId = app.globalData.userInfo?.managerId || wx.getStorageSync('managerId'); - - // 构建API路径:客服使用manager端点,普通用户使用user端点 - let apiPath; - if (userType === 'manager' && managerId) { - apiPath = `/api/conversations/manager/${managerId}`; - console.log('客服身份,使用manager API端点'); - } else { - apiPath = `/api/conversations/user/${currentUserId}`; - console.log('普通用户身份,使用user API端点'); - } - - wx.request({ - url: `${baseUrl}${apiPath}`, - method: 'GET', - header: { - 'Authorization': token ? `Bearer ${token}` : '', - 'content-type': 'application/json' - }, - success: (res) => { - console.log('获取聊天列表成功:', res.data); - // 处理不同的API响应格式 - let chatData = []; - - // 更灵活的响应格式处理 - if (res.data && res.data.success && res.data.code === 200 && res.data.data) { - // 标准API响应格式 - chatData = res.data.data; - } else if (res.data && res.data.code === 0 && res.data.data) { - // 备用API响应格式 - chatData = res.data.data; - } else if (Array.isArray(res.data)) { - // 如果直接返回数组 - chatData = res.data; - } else if (res.data) { - // 如果返回的是对象但不是标准格式,尝试直接使用 - chatData = [res.data]; - } - - // 保存原始数据到日志,便于调试 - console.log('处理前的聊天数据:', chatData); - - if (Array.isArray(chatData) && chatData.length > 0) { - // 格式化聊天列表数据 - const formattedMessages = chatData - .filter(item => { - // 跳过无效项 - if (!item) return false; - - // 关键修复:对于客服端,只显示用户的消息,不显示自己的消息 - if (userType === 'manager' && managerId) { - // 获取会话中的用户ID和客服ID - const conversationUserId = item.userId || item.user_id; - const conversationManagerId = item.managerId || item.manager_id || ''; - - // 确保只显示用户发送给当前客服的消息,且不显示自己的消息 - // 1. 确保是当前客服的会话 - // 2. 确保不是客服自己的会话(避免显示客服自己) - // 3. 确保有有效的用户ID - const isCurrentManagerConversation = String(conversationManagerId) === String(managerId); - const isNotSelfConversation = conversationUserId !== currentUserId; - const hasValidUserId = conversationUserId && conversationUserId !== 'unknown'; - - console.log('客服消息过滤:', { - conversationUserId, - conversationManagerId, - currentUserId, - managerId, - keep: isCurrentManagerConversation && isNotSelfConversation && hasValidUserId - }); - - return isCurrentManagerConversation && isNotSelfConversation && hasValidUserId; - } - - return true; - }) - .map(item => { - // 格式化时间 - const now = new Date(); - const messageDate = new Date(item.lastMessageTime || item.createdAt || item.timestamp || Date.now()); - let displayTime = ''; - - if (messageDate.toDateString() === now.toDateString()) { - // 今天的消息只显示时间 - displayTime = messageDate.getHours().toString().padStart(2, '0') + ':' + - messageDate.getMinutes().toString().padStart(2, '0'); - } else if (messageDate.toDateString() === new Date(now.getTime() - 86400000).toDateString()) { - // 昨天的消息 - displayTime = '昨天'; - } else { - // 其他日期显示月日 - displayTime = (messageDate.getMonth() + 1) + '月' + messageDate.getDate() + '日'; - } - - // 获取当前用户ID,确定显示哪个用户 - 重点支持数据库驼峰命名 - const userIdFromData = item.userId || item.user_id; - const managerIdFromData = item.managerId || item.manager_id; - - // 关键修复:对于客服端,总是显示用户ID,确保正确显示用户消息 - let displayUserId; - if (userType === 'manager') { - // 客服端应该显示用户ID - displayUserId = userIdFromData || 'unknown'; - } else { - // 用户端正常处理 - displayUserId = (userIdFromData === currentUserId) ? - (managerIdFromData || 'unknown') : - (userIdFromData || 'unknown'); - } - - // 添加调试信息 - console.log('处理会话项:', { - originalItem: item, - userId: userIdFromData, - managerId: managerIdFromData, - conversationId: item.conversation_id, - selectedUserId: displayUserId, - userType: userType - }); - - // 使用增强的用户名显示函数 - const displayName = getUserNameById(displayUserId, userType === 'manager'); - - // 获取消息内容,支持多种格式 - let lastMessage = item.lastMessage || item.content || ''; - if (item.lastMessage && typeof item.lastMessage === 'object') { - // 如果lastMessage是对象,获取content字段 - lastMessage = item.lastMessage.content || ''; - } - - // 消息类型处理 - 支持数据库字段格式 - let messageContent = lastMessage; - // 检查content_type字段(数据库使用) - if (item.content_type === 2 || item.content_type === 'image') { - messageContent = '[图片]'; - } else if (item.content_type === 3 || item.content_type === 'voice') { - messageContent = '[语音]'; - } else if (item.content_type === 4 || item.content_type === 'video') { - messageContent = '[视频]'; - } else if (item.messageType === 'image' || (item.lastMessage && item.lastMessage.type === 'image')) { - messageContent = '[图片]'; - } else if (item.messageType === 'voice' || (item.lastMessage && item.lastMessage.type === 'voice')) { - messageContent = '[语音]'; - } else if (item.messageType === 'video' || (item.lastMessage && item.lastMessage.type === 'video')) { - messageContent = '[视频]'; - } - - // 确定未读消息数量 - 根据用户类型使用不同字段 - let unreadCount = 0; - if (userType === 'manager') { - // 客服端使用cs_unread_count - unreadCount = item.cs_unread_count || 0; - } else { - // 用户端使用unread_count - unreadCount = typeof item.unreadCount === 'number' ? item.unreadCount : (item.unread_count || 0); - } - - return { - // 使用conversation_id作为唯一标识,避免重复ID问题 - id: item.conversation_id || displayUserId, - name: item.userName || item.name || displayName, - avatar: item.avatar || (item.userName || displayName).charAt(0) || '用', - content: messageContent, - time: displayTime, - isRead: unreadCount === 0, // 有未读数就是未读状态 - unreadCount: unreadCount, - // 添加原始数据引用,便于调试 - _raw: item, - // 保留会话ID用于导航 - conversationId: item.conversation_id || item.id - }; - }); - - // 去重 - 基于conversation_id或userId - const uniqueMessages = []; - const seenIds = new Set(); - - formattedMessages.forEach(msg => { - const uniqueId = msg.conversationId || msg.id; - if (!seenIds.has(uniqueId)) { - seenIds.add(uniqueId); - uniqueMessages.push(msg); - } else { - console.warn(`发现重复的会话ID: ${uniqueId},已过滤`); - } - }); - - // 按时间和未读状态排序 - uniqueMessages.sort((a, b) => { - // 未读消息优先 - if (a.isRead !== b.isRead) { - return a.isRead ? 1 : -1; - } - // 其他按时间排序 - return 0; - }); - - // 保存到本地存储,便于调试和离线访问 - wx.setStorageSync('lastChatList', formattedMessages); - - // 同时为chatList和messages赋值,确保页面模板能正确获取数据 - const processedChatList = uniqueMessages; - this.setData({ - messages: formattedMessages, - chatList: processedChatList, - noChats: processedChatList.length === 0 - }); - // 更新TabBar未读状态 - this.updateTabBarBadge(); - } else { - console.log('暂无聊天消息,但数据在数据库中存在,可能是API请求路径或参数问题'); - console.log('API请求详情:', { - baseUrl, - apiPath, - currentUserId, - userType, - managerId - }); - - // 即使API返回空数组,也要显示空状态,而不是白屏 - this.setData({ - messages: [], - noChats: true - }); - } - }, - fail: (err) => { - console.error('网络请求失败:', err); - wx.showToast({ - title: '获取消息失败,重试中...', - icon: 'none', - duration: 3000 - }); - - // 尝试使用本地缓存数据 - const cachedList = wx.getStorageSync('lastChatList') || []; - if (cachedList.length > 0) { - console.log('使用本地缓存数据:', cachedList.length); - this.setData({ messages: cachedList }); - this.updateTabBarBadge(); - } else { - // 缓存也没有,使用模拟数据 - console.log('使用模拟数据'); - const mockMessages = [ - { - id: '1', - name: '系统消息', - avatar: '系', - content: '欢迎使用聊天功能', - time: '刚刚', - isRead: false, - unreadCount: 0, - conversationId: 'conv_1' // 添加conversationId,确保导航正常 - } - ]; - this.setData({ messages: mockMessages }); - } - // 确保设置了noChats状态,避免白屏 - this.setData({ noChats: this.data.messages.length === 0 }); - }, - complete: () => { - wx.hideLoading(); - // 确保设置了noChats状态,避免白屏 - this.setData({ noChats: this.data.messages.length === 0 }); - } - }); - } catch (error) { - console.error('加载聊天列表异常:', error); - wx.hideLoading(); - wx.showToast({ title: '加载异常', icon: 'none' }); - } + onLoad: function (options) { + this.loadChatList(); }, - // 返回上一页 - onBack: function() { - wx.navigateBack({ - delta: 1 + loadChatList: function () { + // 模拟加载消息列表 + const chatList = [ + { + id: 1, + name: '系统消息', + avatar: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0', + content: '欢迎使用消息中心功能', + time: '刚刚', + unread: true + }, + { + id: 2, + name: '客服小王', + avatar: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0', + content: '您好,有什么可以帮助您的吗?', + time: '10分钟前', + unread: false + } + ]; + + this.setData({ + chatList: chatList }); }, - /** - * 生命周期函数--监听页面加载 - */ - onLoad(options) { - console.log('聊天列表页面onLoad,准备加载数据'); - - try { - // 页面加载时执行身份验证 - AuthManager.authenticate((authResult) => { - console.log('身份验证成功,开始加载聊天列表:', authResult); - this.loadChatList(); - }, (error) => { - console.warn('身份验证失败,使用访客模式:', error); - // 即使身份验证失败,也加载聊天列表,避免白屏 - this.loadChatList(); - }); - } catch (error) { - console.error('身份验证过程中发生错误:', error); - // 即使身份验证过程中发生错误,也加载聊天列表 - this.loadChatList(); - } - - // 初始化WebSocket连接 - setTimeout(() => { - try { - this.initWebSocket(); - } catch (error) { - console.error('初始化WebSocket过程中发生错误:', error); - } - }, 1000); - }, - - /** - * 生命周期函数--监听页面初次渲染完成 - */ - onReady() { - // 设置导航栏右侧按钮的点击事件 - wx.showNavigationBarLoading(); - }, - - // 导航栏左侧按钮点击事件 - onNavigationBarButtonTap(e) { - if (e.type === 'left') { - this.onBack(); - } else if (e.type === 'right') { - // 处理管理按钮点击 - wx.showToast({ - title: '管理功能待开发', - icon: 'none' - }); - } - }, - - /** - * 生命周期函数--监听页面显示 - */ - onShow() { - console.log('聊天列表页面onShow'); - - // 检查是否已登录 - if (AuthManager.isLoggedIn()) { - // 已登录,初始化WebSocket连接 - try { - this.initWebSocket(); - } catch (error) { - console.error('初始化WebSocket过程中发生错误:', error); - } - } - - // 确保消息列表有数据,避免白屏 - if (!this.data.messages || this.data.messages.length === 0) { - console.log('消息列表为空,尝试重新加载数据'); - this.loadChatList(); - } - }, - - /** - * 生命周期函数--监听页面隐藏 - */ - onHide() { - // 页面隐藏时清理WebSocket连接 - this.cleanupWebSocket(); - }, - - /** - * 生命周期函数--监听页面卸载 - */ - onUnload() { - // 页面卸载时清理WebSocket连接 - this.cleanupWebSocket(); + onChatItemTap: function (e) { + // 跳转到聊天详情页 + wx.showToast({ + title: '聊天功能开发中', + icon: 'none' + }); }, - /** - * 页面相关事件处理函数--监听用户下拉动作 - */ - onPullDownRefresh() { - console.log('下拉刷新触发'); - // 重新加载聊天列表数据 + onPullDownRefresh: function () { this.loadChatList(); - // 停止下拉刷新动画 wx.stopPullDownRefresh(); - }, - - /** - * 页面上拉触底事件的处理函数 - */ - onReachBottom() { - - }, - - /** - * 用户点击右上角分享 - */ - onShareAppMessage: function () { - // 页面分享配置 - return { - title: '聊天列表', - path: '/pages/chat/index' - } - }, - - // 跳转到对话详情页面 - navigateToChatDetail: function(e) { - // 关键修复:获取userId和userName - const userId = e.currentTarget.dataset.userId; - const userName = e.currentTarget.dataset.userName; - const conversationId = e.currentTarget.dataset.conversationId; - - // 关键修复:获取原始数据 - const message = this.data.messages.find(item => - item.id === userId || item.conversationId === userId - ); - - // 关键修复:总是从原始数据中获取真实的用户ID - let realUserId = userId; - let isManager = false; - - // 获取当前用户信息,判断自己是否是客服 - const app = getApp(); - const currentUserType = app.globalData.userInfo?.userType || wx.getStorageSync('userType'); - const currentManagerId = app.globalData.userInfo?.managerId || wx.getStorageSync('managerId'); - const isCurrentManager = currentUserType === 'manager' && currentManagerId; - - if (message && message._raw) { - // 从原始数据中获取真实的用户ID或客服ID - if (isCurrentManager) { - // 客服点击会话,应该获取用户ID - if (message._raw.user_id) { - realUserId = message._raw.user_id; - console.log('客服模式:从原始数据中提取真实用户ID:', realUserId); - } else if (message._raw.manager_id) { - realUserId = message._raw.manager_id; - isManager = true; - console.log('客服模式:从原始数据中提取真实客服ID:', realUserId); - } - } else { - // 用户点击会话,应该获取客服ID - if (message._raw.manager_id) { - realUserId = message._raw.manager_id; - isManager = true; - console.log('用户模式:从原始数据中提取真实客服ID:', realUserId); - } else if (message._raw.user_id) { - realUserId = message._raw.user_id; - console.log('用户模式:从原始数据中提取真实用户ID:', realUserId); - } - } - } else { - // 直接检查userId格式 - if (/^\d+$/.test(userId)) { - // 如果userId是纯数字,很可能是客服ID - isManager = true; - console.log('检测到纯数字客服ID:', userId); - } else if (userId.startsWith('user_')) { - // 如果是用户ID格式,直接使用 - realUserId = userId; - console.log('检测到用户ID格式,直接使用:', realUserId); - } else if (userId.includes('-')) { - // 会话ID格式,尝试从原始数据中获取 - console.warn('无法从会话ID中提取真实用户ID,使用原始会话ID:', userId); - } - } - - console.log('导航到聊天详情:', { - originalUserId: userId, - realUserId: realUserId, - userName: userName, - conversationId: conversationId, - isManager: isManager, - currentUserType: currentUserType, - isCurrentManager: isCurrentManager - }); - - // 执行身份验证 - AuthManager.authenticate(() => { - // 将该聊天标记为已读 - const messages = [...this.data.messages]; - // 查找会话时同时考虑id和conversationId - const messageIndex = messages.findIndex(item => - item.id === userId || item.conversationId === conversationId - ); - - if (messageIndex >= 0) { - messages[messageIndex].isRead = true; - messages[messageIndex].unreadCount = 0; - this.setData({ messages }); - - // 更新TabBar未读消息数 - this.updateTabBarBadge(); - - // 通知服务器已读状态(使用userId) - this.markAsRead(userId); - } - - // 关键修复:在URL中同时传递realUserId和conversationId,确保正确打开会话 - wx.navigateTo({ - url: `/pages/chat-detail/index?userId=${realUserId}&userName=${encodeURIComponent(userName)}&conversationId=${conversationId}&isManager=${isManager}` - }); - }); - }, - - // 通知服务器消息已读 - markAsRead: function(userId) { - // 只执行本地标记已读,不再调用服务器API - console.log('执行本地标记已读,不再调用服务器API:', userId); - - // 在本地标记消息已读,确保应用功能正常 - this.localMarkAsRead(userId); - }, - - // 本地标记消息已读 - localMarkAsRead: function(userId) { - try { - // 获取当前的消息列表 (使用messages而不是chatList) - const messages = this.data.messages || []; - // 更新指定用户的未读状态 - const updatedMessages = messages.map(chat => { - if (chat.userId === userId || chat.id === userId || chat.conversationId === userId) { - return { - ...chat, - unreadCount: 0, - lastReadTime: Date.now() - }; - } - return chat; - }); - - // 更新数据和本地存储 (使用messages而不是chatList) - this.setData({ messages: updatedMessages }); - wx.setStorageSync('lastChatList', updatedMessages); - - // 更新TabBar的未读提示 - this.updateTabBarBadge(); - - console.log(`本地标记用户 ${userId} 的消息已读成功`); - } catch (error) { - console.error('本地标记已读失败:', error); - } - }, - - // 切换消息标签页 - switchTab: function(e) { - const tab = e.currentTarget.dataset.tab; - this.setData({ activeTab: tab }); - - // 根据选中的标签页过滤消息列表 - if (tab === 'unread') { - this.filterUnreadMessages(); - } else { - // 显示所有消息 - 从本地存储恢复原始列表 - const cachedList = wx.getStorageSync('lastChatList') || []; - this.setData({ messages: cachedList }); - } - }, - - // 过滤未读消息 - filterUnreadMessages: function() { - const unreadMessages = this.data.messages.filter(item => !item.isRead || item.unreadCount > 0); - this.setData({ messages: unreadMessages }); } -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/pages/chat/index.json b/pages/chat/index.json index 13f2810..8d7b138 100644 --- a/pages/chat/index.json +++ b/pages/chat/index.json @@ -1,6 +1,3 @@ { - "navigationBarTitleText": "消息", - "navigationBarBackgroundColor": "#ffffff", - "navigationBarTextStyle": "black", - "usingComponents": {} + "navigationBarTitleText": "消息中心" } \ No newline at end of file diff --git a/pages/chat/index.wxml b/pages/chat/index.wxml index e143f30..fa11f59 100644 --- a/pages/chat/index.wxml +++ b/pages/chat/index.wxml @@ -1,76 +1,18 @@ - - - - - - - - - - - - 消息 - - - - - - 🔍 - - - ··· - - + + 消息中心 - - - 全部消息 - 未读消息 - 清除未读 - - - - - - - 以下为3天前的消息,提示将弱化 - - - - - - - - {{item.avatar || (item.name && item.name.charAt(0)) || '用'}} - - - - - {{item.unreadCount > 99 ? '99+' : item.unreadCount}} + + + + - - - - - - {{item.name}} - {{item.time}} - - - - - {{item.content || '[图片]'}} - + + 系统消息 + 欢迎使用消息中心功能 + 刚刚 - - - - 💬 - 暂无消息记录 - 您可以开始与客户或客服的对话 - - + \ No newline at end of file diff --git a/pages/chat/index.wxss b/pages/chat/index.wxss index 6b89e89..2cd34b0 100644 --- a/pages/chat/index.wxss +++ b/pages/chat/index.wxss @@ -1,312 +1,72 @@ -/* 微信风格聊天页面样式 */ .container { - display: flex; - flex-direction: column; - height: 100vh; background-color: #f5f5f5; - position: relative; - overflow: hidden; -} - -/* 头部导航栏 - 现代填充风格 */ -.header-full { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - height: 92rpx; - background-color: #ffffff; - border-bottom: 1rpx solid #e0e0e0; - position: relative; - z-index: 10; -} - -.header-left, -.header-right { - width: 100rpx; - display: flex; - align-items: center; -} - -.header-left { - justify-content: flex-start; - padding-left: 20rpx; + min-height: 100vh; } -.header-right { - justify-content: flex-end; - padding-right: 20rpx; -} - -.header-center { - flex: 1; - display: flex; - justify-content: center; - align-items: center; - text-align: center; -} - -.title-large { - font-size: 38rpx; - font-weight: 600; - color: #000000; - max-width: 100%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.header-btn { +.chat-header { + background-color: #fff; + padding: 20rpx; + border-bottom: 1rpx solid #e8e8e8; display: flex; align-items: center; justify-content: center; - width: 70rpx; - height: 70rpx; - border-radius: 35rpx; - transition: background-color 0.2s; } -.header-btn:active { - background-color: #f0f0f0; -} - -.back-btn .icon-back { - font-size: 44rpx; - color: #333; +.chat-title { + font-size: 36rpx; font-weight: bold; -} - -.action-btn { - margin-left: 10rpx; -} - -.action-btn .icon-search { - font-size: 40rpx; - color: #333; -} - -.action-btn .icon-more { - font-size: 40rpx; color: #333; - font-weight: bold; -} - -/* 微信风格的消息类型切换 */ -.tabs-wechat { - display: flex; - justify-content: space-between; - align-items: center; - background-color: #ffffff; - padding: 0 20rpx; - height: 76rpx; - border-bottom: 1rpx solid #e0e0e0; -} - -.tab-item { - font-size: 32rpx; - color: #666666; - padding: 0 20rpx; - height: 100%; - display: flex; - align-items: center; - position: relative; -} - -.tab-item.active { - color: #07c160; - font-weight: 500; } -.tab-item.active::after { - content: ''; - position: absolute; - bottom: 0; - left: 20rpx; - right: 20rpx; - height: 4rpx; - background-color: #07c160; +.chat-list { + padding: 20rpx; } -.clear-unread { - font-size: 28rpx; - color: #1677ff; - padding: 0 10rpx; -} - -/* 消息列表 - 微信风格 */ -.message-list-wechat { - flex: 1; - background-color: #f5f5f5; - overflow: auto; -} - -/* 提示消息 */ -.message-tips { - text-align: center; - padding: 20rpx 0; - margin-bottom: 10rpx; - color: #999999; - font-size: 26rpx; - background-color: #f5f5f5; -} - -/* 消息项 - 微信风格 */ -.message-item-wechat { - display: flex; - padding: 20rpx 30rpx; - background-color: #ffffff; - border-bottom: 1rpx solid #f0f0f0; - position: relative; -} - -/* 头像容器 */ -.avatar-container-wechat { - position: relative; - margin-right: 24rpx; -} - -/* 头像 - 微信风格 */ -.avatar-wechat { - width: 100rpx; - height: 100rpx; +.chat-item { + background-color: #fff; border-radius: 12rpx; - background-color: #07c160; - display: flex; - align-items: center; - justify-content: center; - color: #ffffff; - font-size: 40rpx; - font-weight: 500; -} - -/* 未读红点 */ -.unread-dot { - position: absolute; - top: -6rpx; - right: -6rpx; - width: 28rpx; - height: 28rpx; - border-radius: 14rpx; - background-color: #ff3b30; - border: 3rpx solid #ffffff; -} - -/* 未读数字角标 - 微信风格 */ -.unread-badge-wechat { - position: absolute; - top: -8rpx; - right: -8rpx; - min-width: 36rpx; - height: 36rpx; - padding: 0 10rpx; - border-radius: 18rpx; - background-color: #ff3b30; - border: 3rpx solid #ffffff; - color: #ffffff; - font-size: 24rpx; - font-weight: 500; + padding: 20rpx; display: flex; align-items: center; - justify-content: center; - line-height: 1; -} - -/* 消息内容区域 */ -.message-content-wechat { - flex: 1; - display: flex; - flex-direction: column; - justify-content: space-between; - min-height: 100rpx; + margin-bottom: 20rpx; + box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); } -/* 消息头部:名称和时间 */ -.message-header-wechat { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 10rpx; -} - -.name-wechat { - font-size: 32rpx; - font-weight: 500; - color: #000000; - flex: 1; +.avatar { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + margin-right: 20rpx; overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; } -.time-wechat { - font-size: 24rpx; - color: #999999; - margin-left: 15rpx; -} - -/* 消息文本 */ -.message-text-wechat { - display: flex; - align-items: center; - font-size: 28rpx; - color: #999999; - line-height: 40rpx; - overflow: hidden; -} - -.unread-dot-small { - color: #ff3b30; - font-size: 20rpx; - margin-right: 8rpx; +.avatar image { + width: 100%; + height: 100%; } -.message-content-text { +.chat-info { flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* 空状态提示 - 微信风格 */ -.empty-state-wechat { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 150rpx 0; - color: #999999; -} - -.empty-icon-wechat { - font-size: 120rpx; - margin-bottom: 30rpx; + position: relative; } -.empty-text-wechat { +.chat-name { font-size: 32rpx; - color: #666666; - margin-bottom: 15rpx; + font-weight: bold; + color: #333; + margin-bottom: 8rpx; } -.empty-subtext { +.chat-content { font-size: 28rpx; - color: #999999; -} - -/* 滚动条样式优化 */ -.message-list-wechat::-webkit-scrollbar { - width: 6rpx; -} - -.message-list-wechat::-webkit-scrollbar-track { - background: transparent; + color: #666; + margin-bottom: 8rpx; } -.message-list-wechat::-webkit-scrollbar-thumb { - background: #cccccc; - border-radius: 3rpx; -} - -.message-list-wechat::-webkit-scrollbar-thumb:hover { - background: #999999; +.chat-time { + font-size: 24rpx; + color: #999; + position: absolute; + top: 0; + right: 0; } \ No newline at end of file diff --git a/pages/customer-service/detail/index.js b/pages/customer-service/detail/index.js deleted file mode 100644 index ecd9104..0000000 --- a/pages/customer-service/detail/index.js +++ /dev/null @@ -1,165 +0,0 @@ -// pages/customer-service/detail/index.js -Page({ - data: { - customerData: null, - customerServices: [ - { - id: 1, - managerId: 'PM001', - managercompany: '鸡蛋贸易有限公司', - managerdepartment: '采购部', - organization: '鸡蛋采购组', - projectName: '高级采购经理', - name: '张三', - alias: '张经理', - phoneNumber: '13800138001', - avatarUrl: '', - score: 999, - isOnline: true, - responsibleArea: '华北区鸡蛋采购', - experience: '1-3年', - serviceCount: 200, - purchaseCount: 15000, - profitIncreaseRate: 15, - profitFarmCount: 120, - skills: ['渠道拓展', '供应商维护', '质量把控', '精准把控市场价格'] - }, - { - id: 2, - managerId: 'PM002', - managercompany: '鸡蛋贸易有限公司', - managerdepartment: '采购部', - organization: '全国采购组', - projectName: '采购经理', - name: '李四', - alias: '李经理', - phoneNumber: '13900139002', - avatarUrl: '', - score: 998, - isOnline: true, - responsibleArea: '全国鸡蛋采购', - experience: '2-3年', - serviceCount: 200, - purchaseCount: 20000, - profitIncreaseRate: 18, - profitFarmCount: 150, - skills: ['精准把控市场价格', '渠道拓展', '供应商维护'] - }, - { - id: 3, - managerId: 'PM003', - managercompany: '鸡蛋贸易有限公司', - managerdepartment: '采购部', - organization: '华东采购组', - projectName: '采购专员', - name: '王五', - alias: '王专员', - phoneNumber: '13700137003', - avatarUrl: '', - score: 997, - isOnline: false, - responsibleArea: '华东区鸡蛋采购', - experience: '1-2年', - serviceCount: 150, - purchaseCount: 12000, - profitIncreaseRate: 12, - profitFarmCount: 80, - skills: ['质量把控', '供应商维护'] - }, - { - id: 4, - managerId: 'PM004', - managercompany: '鸡蛋贸易有限公司', - managerdepartment: '采购部', - organization: '华南采购组', - projectName: '高级采购经理', - name: '赵六', - alias: '赵经理', - phoneNumber: '13600136004', - avatarUrl: '', - score: 996, - isOnline: true, - responsibleArea: '华南区鸡蛋采购', - experience: '3-5年', - serviceCount: 250, - purchaseCount: 25000, - profitIncreaseRate: 20, - profitFarmCount: 180, - skills: ['精准把控市场价格', '渠道拓展', '质量把控', '供应商维护'] - } - ] - }, - - onLoad: function (options) { - // 获取传递过来的客服ID - const { id } = options; - // 根据ID查找客服数据 - const customerData = this.data.customerServices.find(item => item.id === parseInt(id)); - - if (customerData) { - this.setData({ - customerData: customerData - }); - // 设置导航栏标题 - wx.setNavigationBarTitle({ - title: `${customerData.alias} - 客服详情`, - }); - } else { - // 如果找不到对应ID的客服,显示错误提示 - wx.showToast({ - title: '未找到客服信息', - icon: 'none' - }); - } - }, - - onShow() { - // 更新自定义tabBar状态 - if (typeof this.getTabBar === 'function' && this.getTabBar()) { - this.getTabBar().setData({ - selected: -1 // 不选中任何tab - }); - } - }, - - // 返回上一页 - onBack: function () { - wx.navigateBack(); - }, - - // 在线沟通 - onChat: function () { - const { customerData } = this.data; - if (customerData) { - wx.navigateTo({ - url: `/pages/chat/index?id=${customerData.id}&name=${customerData.alias}&phone=${customerData.phoneNumber}` - }); - } - }, - - // 拨打电话 - onCall: function () { - const { customerData } = this.data; - if (customerData && customerData.phoneNumber) { - wx.makePhoneCall({ - phoneNumber: customerData.phoneNumber, - success: function () { - console.log('拨打电话成功'); - }, - fail: function () { - console.log('拨打电话失败'); - } - }); - } - }, - - // 分享功能 - onShareAppMessage: function () { - const { customerData } = this.data; - return { - title: `${customerData?.alias || '优秀客服'} - 鸡蛋贸易平台`, - path: `/pages/customer-service/detail/index?id=${customerData?.id}`, - imageUrl: '' - }; - } -}); diff --git a/pages/customer-service/detail/index.json b/pages/customer-service/detail/index.json index 0e81cc4..b8a7eb8 100644 --- a/pages/customer-service/detail/index.json +++ b/pages/customer-service/detail/index.json @@ -1,7 +1,9 @@ { "navigationBarTitleText": "客服详情", - "navigationBarBackgroundColor": "#ffffff", - "navigationBarTextStyle": "black", - "backgroundColor": "#f8f8f8", - "usingComponents": {} -} + "navigationStyle": "custom", + "usingComponents": { + "custom-header": "../../components/custom-header/custom-header", + "service-card": "../../components/service-card/service-card", + "faq-item": "../../components/faq-item/faq-item" + } +} \ No newline at end of file diff --git a/pages/customer-service/detail/index.wxml b/pages/customer-service/detail/index.wxml index 893c478..fecc3e9 100644 --- a/pages/customer-service/detail/index.wxml +++ b/pages/customer-service/detail/index.wxml @@ -1,93 +1,65 @@ - - - - - 返回 - - 客服详情 - - + + + + 客服详情 + - - - - - - - 在线 - 离线 + + + + + 💁 - - - {{customerData.alias}} - {{customerData.score}} 鸡蛋分 - - {{customerData.projectName}} - {{customerData.managercompany}} + + 客服小王 + 在线 - - - - - - - 负责区域 - {{customerData.responsibleArea}} - - - 联系电话 - {{customerData.phoneNumber}} + + + + 服务类型 + 在线客服 - - 工作经验 - 服务平台{{customerData.experience}} + + 服务时间 + 24小时在线 - - 服务规模 - 服务{{customerData.serviceCount}}家鸡场 + + 专业领域 + 产品咨询、订单处理、售后支持 - - - - - 专业技能 - - - {{item}} - + + + 联系方式 + + + - - - - - 业绩数据 - - - {{customerData.purchaseCount}} - 累计采购鸡蛋(件) + + + 常见问题 + + 如何查询订单状态? + > - - {{customerData.profitFarmCount}} - 累计服务鸡场(家) + + 如何申请退款? + > - - {{customerData.profitIncreaseRate}}% - 平均盈利增长 + + 物流配送时间是多久? + > - - - - - 💬 在线沟通 - - - 📞 电话联系 - - - + \ No newline at end of file diff --git a/pages/customer-service/detail/index.wxss b/pages/customer-service/detail/index.wxss index 82c5a70..a84d263 100644 --- a/pages/customer-service/detail/index.wxss +++ b/pages/customer-service/detail/index.wxss @@ -1,317 +1,180 @@ -/* pages/customer-service/detail/index.wxss */ .container { - padding-bottom: 100rpx; - background-color: #f8f8f8; + background-color: #f5f5f5; min-height: 100vh; } -/* 顶部导航栏 */ -.nav-bar { +.service-detail-header { + background-color: #fff; display: flex; - justify-content: space-between; align-items: center; - padding: 44rpx 30rpx 20rpx; - background-color: #fff; - border-bottom: 1rpx solid #f0f0f0; - position: fixed; + justify-content: space-between; + padding: 16rpx 24rpx; + border-bottom: 1rpx solid #e8e8e8; + position: sticky; top: 0; - left: 0; - right: 0; - z-index: 1000; - width: 100%; - box-sizing: border-box; + z-index: 100; } -.nav-left { - width: 80rpx; +.header-back, .header-placeholder { + width: 60rpx; + height: 60rpx; display: flex; - justify-content: flex-start; -} - -.back-icon { + align-items: center; + justify-content: center; font-size: 32rpx; - color: #333; - font-weight: normal; + font-weight: bold; } -.nav-title { - font-size: 36rpx; +.header-title { + font-size: 32rpx; font-weight: bold; color: #333; - flex: 1; - text-align: center; } -.nav-right { - display: flex; - align-items: center; - gap: 30rpx; +.service-detail-content { + padding: 20rpx; } -.share-icon { - font-size: 32rpx; - color: #333; -} - -/* 客服基本信息 */ -.info-section { +.service-info-section { background-color: #fff; - padding: 120rpx 30rpx 30rpx; + border-radius: 16rpx; + padding: 32rpx; margin-bottom: 20rpx; -} - -.header-info { + box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); display: flex; align-items: center; } -.avatar-container { - width: 160rpx; - height: 160rpx; - margin-right: 30rpx; - position: relative; -} - -.avatar { - width: 100%; - height: 100%; - border-radius: 50%; +.service-avatar { + width: 120rpx; + height: 120rpx; background-color: #f0f0f0; - border: 4rpx solid #f0f0f0; -} - -.online-indicator-large { - position: absolute; - bottom: 0; - right: 0; - background-color: #52c41a; - color: white; - font-size: 24rpx; - padding: 8rpx 20rpx; - border-radius: 20rpx; - border: 4rpx solid white; -} - -.offline-indicator-large { - position: absolute; - bottom: 0; - right: 0; - background-color: #999; - color: white; - font-size: 24rpx; - padding: 8rpx 20rpx; - border-radius: 20rpx; - border: 4rpx solid white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 64rpx; + margin-right: 32rpx; } -.header-details { +.service-basic-info { flex: 1; } -.name-score-row { - display: flex; - align-items: center; - margin-bottom: 12rpx; -} - -.name-large { - font-size: 40rpx; +.service-name { + display: block; + font-size: 36rpx; font-weight: bold; color: #333; - margin-right: 16rpx; -} - -.score-large { - font-size: 28rpx; - color: #fff; - background: linear-gradient(135deg, #ffb800, #ff7700); - padding: 6rpx 16rpx; - border-radius: 20rpx; -} - -.position { - font-size: 32rpx; - color: #666; margin-bottom: 8rpx; } -.company-large { +.service-status { + display: block; font-size: 28rpx; - color: #999; + color: #07C160; } -/* 核心信息卡片 */ -.core-info-section { - padding: 0 30rpx; - margin-bottom: 20rpx; -} - -.info-card { +.service-details { background-color: #fff; - border-radius: 24rpx; - padding: 30rpx; - box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); + border-radius: 16rpx; + padding: 24rpx; + margin-bottom: 20rpx; + box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); } -.info-item { +.detail-item { display: flex; justify-content: space-between; align-items: center; - padding: 20rpx 0; + padding: 16rpx 0; border-bottom: 1rpx solid #f0f0f0; } -.info-item:last-child { +.detail-item:last-child { border-bottom: none; } -.info-label { - font-size: 30rpx; +.detail-label { + font-size: 28rpx; color: #666; } -.info-value { - font-size: 30rpx; +.detail-value { + font-size: 28rpx; color: #333; text-align: right; flex: 1; - margin-left: 30rpx; -} - -.phone-number { - color: #1890ff; + margin-left: 40rpx; } -/* 专业技能标签 */ -.skills-section { +.contact-section, .faq-section { background-color: #fff; - padding: 30rpx; + border-radius: 16rpx; + padding: 24rpx; margin-bottom: 20rpx; + box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); } .section-title { + display: block; font-size: 32rpx; font-weight: bold; color: #333; - margin-bottom: 20rpx; -} - -.skills-container { - display: flex; - flex-wrap: wrap; - gap: 16rpx; - padding-bottom: 10rpx; -} - -.skill-tag { - background-color: #f0f9ff; - color: #1890ff; - font-size: 26rpx; - padding: 12rpx 24rpx; - border-radius: 20rpx; - border: 1rpx solid #bae7ff; - transition: all 0.2s ease; -} - -.skill-tag:active { - background-color: #bae7ff; - transform: scale(0.98); -} - -/* 业绩数据 */ -.performance-section { - background-color: #fff; - padding: 30rpx; - margin-bottom: 120rpx; -} - -.performance-cards { - display: flex; - justify-content: space-between; - gap: 20rpx; -} - -.performance-card { - flex: 1; - background-color: #f9f9f9; - padding: 24rpx; - border-radius: 16rpx; - text-align: center; - transition: all 0.2s ease; - position: relative; - overflow: hidden; -} - -.performance-card:active { - transform: translateY(2rpx); - background-color: #f0f0f0; + margin-bottom: 24rpx; } -.performance-number { - font-size: 36rpx; +.contact-button { + display: block; + width: 100%; + background-color: #FF6B81; + color: #fff; + font-size: 32rpx; font-weight: bold; - color: #333; - margin-bottom: 12rpx; -} - -.performance-number.profit-rate { - color: #52c41a; + padding: 24rpx; + border-radius: 12rpx; + border: none; + margin-bottom: 16rpx; + line-height: normal; + height: auto; } -.performance-label { - font-size: 24rpx; - color: #666; - line-height: 1.4; +.contact-button:last-child { + margin-bottom: 0; } -/* 底部操作按钮 */ -.bottom-actions { - position: fixed; - bottom: 0; - left: 0; - right: 0; - display: flex; - background-color: #fff; - padding: 20rpx 30rpx; - border-top: 1rpx solid #f0f0f0; - gap: 20rpx; +.contact-button.phone-button { + background-color: #07C160; } -.action-button { - flex: 1; - padding: 24rpx; - border-radius: 40rpx; - text-align: center; - font-size: 32rpx; - font-weight: 500; - transition: all 0.2s ease; - position: relative; - overflow: hidden; +.contact-button.email-button { + background-color: #1989fa; } -.action-button:active { - transform: scale(0.98); +.contact-button:hover { opacity: 0.9; } -.chat-button:active { - background-color: #096dd9; -} - -.call-button:active { - background-color: #389e0d; +.faq-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 24rpx 0; + border-bottom: 1rpx solid #f0f0f0; } -.chat-button { - background-color: #1890ff; - color: white; +.faq-item:last-child { + border-bottom: none; } -.call-button { - background-color: #52c41a; - color: white; +.faq-question { + font-size: 28rpx; + color: #333; + flex: 1; } -.button-text { - font-size: 30rpx; -} +.faq-arrow { + font-size: 28rpx; + color: #999; + margin-left: 20rpx; +} \ No newline at end of file diff --git a/pages/customer-service/index.js b/pages/customer-service/index.js index 059794d..0a057b0 100644 --- a/pages/customer-service/index.js +++ b/pages/customer-service/index.js @@ -1,378 +1,55 @@ // pages/customer-service/index.js Page({ - // 客服数据,将从后端动态获取 data: { - customerServices: [], - filteredServices: [], - searchKeyword: '', - selectedArea: '全部', - totalCount: 0, - onlineCount: 0 - }, - - // 获取客服列表的方法 - async fetchCustomerServices() { - try { - console.log('开始请求客服列表...'); - // 导入API工具并使用正确的请求方法 - const api = require('../../utils/api'); - - const res = await new Promise((resolve, reject) => { - wx.request({ - url: 'http://localhost:3003/api/managers', - method: 'GET', - timeout: 15000, - header: { - 'content-type': 'application/json' - }, - success: resolve, - fail: (error) => { - console.error('网络请求失败:', error); - reject(error); - } - }); - }); - - console.log('API响应状态码:', res?.statusCode); - console.log('API响应数据:', res?.data ? JSON.stringify(res.data) : 'undefined'); - - // 更宽松的响应检查,确保能处理各种有效的响应格式 - if (res && res.statusCode === 200 && res.data) { - // 无论success字段是否存在,只要有data字段就尝试处理 - const dataSource = res.data.data || res.data; - if (Array.isArray(dataSource)) { - const processedData = dataSource.map(item => ({ - id: item.id || `id_${Date.now()}_${Math.random()}`, // 确保有id - managerId: item.managerId || '', - managercompany: item.managercompany || '', - managerdepartment: item.managerdepartment || '', - organization: item.organization || '', - projectName: item.projectName || '', - name: item.name || '未知', - alias: item.alias || item.name || '未知', - phoneNumber: item.phoneNumber || '', - avatarUrl: item.avatar || item.avatarUrl || '', // 兼容avatar和avatarUrl - score: Math.floor(Math.random() * 20) + 980, // 随机生成分数 - isOnline: !!item.online, // 转换为布尔值 - responsibleArea: `${this.getRandomArea()}鸡蛋采购`, - experience: this.getRandomExperience(), - serviceCount: this.getRandomNumber(100, 300), - purchaseCount: this.getRandomNumber(10000, 30000), - profitIncreaseRate: this.getRandomNumber(10, 25), - profitFarmCount: this.getRandomNumber(50, 200), - skills: this.getRandomSkills() - })); - console.log('处理后的数据数量:', processedData.length); - return processedData; - } else { - console.error('响应数据格式错误,不是预期的数组格式:', dataSource); - return []; - } - } else { - console.error('获取客服列表失败,状态码:', res?.statusCode, '响应数据:', res?.data); - return []; - } - } catch (error) { - console.error('请求客服列表出错:', error); - // 网络错误时显示提示 - wx.showToast({ - title: '网络请求失败,请检查网络连接', - icon: 'none' - }); - return []; - } - }, - - // 辅助函数:生成随机区域 - getRandomArea() { - const areas = ['华北区', '华东区', '华南区', '全国', '西南区', '西北区', '东北区']; - return areas[Math.floor(Math.random() * areas.length)]; - }, - - // 辅助函数:生成随机工作经验 - getRandomExperience() { - const experiences = ['1-2年', '1-3年', '2-3年', '3-5年', '5年以上']; - return experiences[Math.floor(Math.random() * experiences.length)]; - }, - - // 辅助函数:生成随机数字 - getRandomNumber(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; - }, - - // 辅助函数:生成随机技能 - getRandomSkills() { - const allSkills = ['渠道拓展', '供应商维护', '质量把控', '精准把控市场价格', '谈判技巧', '库存管理']; - const skillCount = Math.floor(Math.random() * 3) + 2; // 2-4个技能 - const selectedSkills = []; - - while (selectedSkills.length < skillCount) { - const skill = allSkills[Math.floor(Math.random() * allSkills.length)]; - if (!selectedSkills.includes(skill)) { - selectedSkills.push(skill); - } - } - - return selectedSkills; - }, - - // 设置WebSocket监听客服状态变化 - setupWebSocketListener() { - const ws = getApp().globalData.webSocketManager; - const app = getApp(); - const userInfo = app.globalData.userInfo || {}; - const isManager = userInfo.userType === 'manager' || userInfo.type === 'manager'; - - if (ws) { - // 监听客服状态更新消息 - ws.on('customerServiceStatusUpdate', (data) => { - console.log('收到客服状态更新:', data); - // 更新对应客服的在线状态 - const customerServiceList = this.data.customerServices; - const updatedList = customerServiceList.map(item => { - if (item.id === data.id || item.managerId === data.managerId) { - return { ...item, isOnline: data.isOnline }; - } - return item; - }); - - const onlineCount = updatedList.filter(item => item.isOnline).length; - - this.setData({ - customerServices: updatedList, - onlineCount: onlineCount - }); - - // 重新应用筛选 - this.filterServices(); - }); - - // 如果当前用户是客服,立即进行认证 - if (isManager && userInfo.userId) { - console.log('客服用户登录,进行WebSocket认证:', userInfo.userId); - // 使用userId作为managerId进行认证,确保与服务器端onlineManagers中的键匹配 - ws.authenticate(); - } - } - }, - - // 定期刷新客服列表(每30秒) - startPeriodicRefresh() { - this.refreshTimer = setInterval(() => { - console.log('定期刷新客服列表...'); - this.loadCustomerServices(); - }, 30000); - }, - - // 停止定期刷新 - stopPeriodicRefresh() { - if (this.refreshTimer) { - clearInterval(this.refreshTimer); - this.refreshTimer = null; - } - }, - - // 加载客服列表 - async loadCustomerServices() { - console.log('开始加载客服列表...'); - // 确保在开始时调用hideLoading,防止重复调用showLoading - try { - wx.hideLoading(); - } catch (e) { - console.log('没有正在显示的loading'); - } - - wx.showLoading({ title: '加载中...' }); - try { - const services = await this.fetchCustomerServices(); - console.log('获取到的客服数量:', services.length); - - // 计算在线数量 - const onlineCount = services.filter(item => item.isOnline).length; - console.log('在线客服数量:', onlineCount); - - // 更新数据 - this.setData({ - customerServices: services, - totalCount: services.length, - onlineCount: onlineCount - }); - console.log('数据更新成功'); - - // 应用当前的筛选条件 - this.filterServices(); - console.log('筛选条件应用完成'); - - // 如果没有数据,显示提示(确保在hideLoading后显示) - if (services.length === 0) { - setTimeout(() => { - wx.showToast({ - title: '暂无客服数据', - icon: 'none', - duration: 2000 - }); - }, 100); - } - } catch (error) { - console.error('加载客服列表失败:', error); - // 确保在hideLoading后显示错误提示 - setTimeout(() => { - wx.showToast({ - title: '加载失败,请重试', - icon: 'none', - duration: 2000 - }); - }, 100); - } finally { - wx.hideLoading(); - } - }, - - onLoad: function () { - // 初始化WebSocket连接 - const app = getApp(); - if (!app.globalData.webSocketManager) { - // 如果WebSocket管理器还没初始化,从utils导入 - const WebSocketManager = require('../../utils/websocket').default; - app.globalData.webSocketManager = WebSocketManager; - - // 尝试连接WebSocket - WebSocketManager.connect('ws://localhost:3003'); - } - - // 设置WebSocket监听 - this.setupWebSocketListener(); - - // 加载客服列表 - this.loadCustomerServices(); - - // 启动定期刷新 - this.startPeriodicRefresh(); - - // 检查当前用户身份 - this.checkUserType(); - }, - - /** - * 检查当前用户身份 - */ - checkUserType: function() { - const app = getApp(); - const userInfo = app.globalData.userInfo || {}; - const isManager = userInfo.userType === 'manager' || userInfo.type === 'manager'; - console.log('当前用户身份检查:', { isManager, userType: userInfo.userType }); - this.setData({ - isCurrentUserManager: isManager - }); - }, - - onShow() { - // 更新自定义tabBar状态 - if (typeof this.getTabBar === 'function' && this.getTabBar()) { - this.getTabBar().setData({ - selected: -1 // 不选中任何tab - }); - } - - // 当页面显示时重新加载数据,确保数据最新 - this.loadCustomerServices(); - }, - - onUnload: function() { - // 停止定期刷新 - this.stopPeriodicRefresh(); - - // 清理WebSocket事件监听 - const ws = getApp().globalData.webSocketManager; - if (ws) { - ws.off('customerServiceStatusUpdate'); - } + serviceList: [] }, - onSearch: function (e) { - const keyword = e.detail.value; - this.setData({ - searchKeyword: keyword - }); - this.filterServices(); + onLoad: function (options) { + this.loadServiceInfo(); }, - onAreaFilter: function () { - // 区域筛选弹窗 - 鸡蛋采购区域 - wx.showActionSheet({ - itemList: ['全部', '华北区', '华东区', '华南区', '全国', '西南区', '西北区', '东北区'], - success: res => { - const areas = ['全部', '华北区', '华东区', '华南区', '全国', '西南区', '西北区', '东北区']; - const selectedArea = areas[res.tapIndex]; - this.setData({ - selectedArea: selectedArea - }); - this.filterServices(); + loadServiceInfo: function () { + // 模拟加载客服信息 + const serviceList = [ + { + id: 1, + type: 'phone', + name: '电话客服', + desc: '周一至周日 9:00-18:00', + number: '400-123-4567' + }, + { + id: 2, + type: 'online', + name: '在线客服', + desc: '24小时在线为您服务' + }, + { + id: 3, + type: 'email', + name: '邮箱客服', + desc: 'service@example.com' } - }); - }, - - filterServices: function () { - const { customerServices, searchKeyword, selectedArea } = this.data; - - let filtered = customerServices; - - // 关键词搜索 - if (searchKeyword) { - const keyword = searchKeyword.toLowerCase(); - filtered = filtered.filter(item => { - return item.alias?.toLowerCase().includes(keyword) || - item.name.toLowerCase().includes(keyword) || - item.phoneNumber?.includes(keyword) || - item.managercompany?.toLowerCase().includes(keyword); - }); - } - - // 区域筛选 - if (selectedArea && selectedArea !== '全部') { - filtered = filtered.filter(item => { - return item.responsibleArea?.includes(selectedArea); - }); - } + ]; this.setData({ - filteredServices: filtered + serviceList: serviceList }); }, - onChat: function (e) { - const id = e.currentTarget.dataset.id; - const service = this.data.customerServices.find(item => item.id === id); - // 确保使用managerId作为聊天对象的唯一标识符 - const chatUserId = service?.managerId || id; - wx.navigateTo({ - url: `/pages/chat-detail/index?userId=${chatUserId}&userName=${encodeURIComponent(service?.alias || '')}&phone=${service?.phoneNumber || ''}&isManager=true` + startOnlineChat: function () { + // 跳转到在线聊天页面 + wx.showToast({ + title: '在线客服功能开发中', + icon: 'none' }); - console.log('跳转到聊天页面:', { chatUserId, userName: service?.alias }); }, - onCall: function (e) { - const phone = e.currentTarget.dataset.phone; - wx.makePhoneCall({ - phoneNumber: phone, - success: function () { - console.log('拨打电话成功'); - }, - fail: function () { - console.log('拨打电话失败'); - } + sendEmail: function () { + // 打开邮件客户端 + wx.showToast({ + title: '邮箱客服功能开发中', + icon: 'none' }); - }, - - // 查看客服详情 - onViewDetail: function (e) { - const id = e.currentTarget.dataset.id; - wx.navigateTo({ - url: `/pages/customer-service/detail?id=${id}` - }); - }, - - onBack: function () { - wx.navigateBack(); } -}); +}); \ No newline at end of file diff --git a/pages/customer-service/index.json b/pages/customer-service/index.json index a641449..8728543 100644 --- a/pages/customer-service/index.json +++ b/pages/customer-service/index.json @@ -1,5 +1,3 @@ { - "navigationBarBackgroundColor": "#f8f8f8", - "navigationBarTextStyle": "black", - "usingComponents": {} -} + "navigationBarTitleText": "客服中心" +} \ No newline at end of file diff --git a/pages/customer-service/index.wxml b/pages/customer-service/index.wxml index 7a36e93..47c86a5 100644 --- a/pages/customer-service/index.wxml +++ b/pages/customer-service/index.wxml @@ -1,89 +1,40 @@ - - - - - 返回 - - 客服列表 - - ⚙️ - + + 客服中心 - - - - - 🔍 - + + + + + 📞 + + + 电话客服 + 周一至周日 9:00-18:00 + + - - - 区域 - + + + + 💬 + + 在线客服 + 24小时在线为您服务 + + - - - - - - - - - - 在线 - 离线 - - - - {{item.alias}} - {{item.score}} 鸡蛋分 - (在线) - - {{item.managercompany || '暂无公司信息'}} - {{item.managerdepartment}} · {{item.projectName}} - 负责区域:{{item.responsibleArea}} - 服务平台{{item.experience}} 服务{{item.serviceCount}}家鸡场 - - - - {{item.purchaseCount}} - 累计采购(件) - - | - - {{item.profitFarmCount}} - 服务盈利鸡场(家) - - | - - {{item.profitIncreaseRate}}% - 平均盈利增长 - - - - - - {{item}} - - +{{item.skills.length - 3}} - - - - - - 💬 - - - 📞 - - + + + + 📧 + + + 邮箱客服 + service@example.com - - - 👤 - 暂无匹配的经纪人 + - + \ No newline at end of file diff --git a/pages/customer-service/index.wxss b/pages/customer-service/index.wxss index 8477c4a..35e174d 100644 --- a/pages/customer-service/index.wxss +++ b/pages/customer-service/index.wxss @@ -1,383 +1,80 @@ -/* pages/customer-service/index.wxss */ .container { - padding-bottom: 100rpx; - background-color: #f8f8f8; + background-color: #f5f5f5; min-height: 100vh; } -/* 顶部导航栏 */ -.nav-bar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 44rpx 30rpx 20rpx; +.service-header { background-color: #fff; - border-bottom: 1rpx solid #f0f0f0; - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 1000; - width: 100%; - box-sizing: border-box; -} - -.nav-left { - width: 80rpx; + padding: 20rpx; + border-bottom: 1rpx solid #e8e8e8; display: flex; - justify-content: flex-start; -} - -.back-icon { - font-size: 32rpx; - color: #333; - font-weight: normal; + align-items: center; + justify-content: center; } -.nav-title { +.service-title { font-size: 36rpx; font-weight: bold; color: #333; - flex: 1; - text-align: center; -} - -.nav-right { - display: flex; - align-items: center; - gap: 30rpx; } -.search-icon, .settings-icon { - font-size: 32rpx; - color: #333; +.service-content { + padding: 20rpx; } -/* 搜索区域 */ -.search-container { +.service-item { background-color: #fff; - padding: 20rpx 30rpx; - border-bottom: 10rpx solid #f8f8f8; - position: fixed; - top: 110rpx; - left: 0; - right: 0; - z-index: 999; - width: 100%; - box-sizing: border-box; -} - -.search-bar { - display: flex; - align-items: center; - background-color: #f5f5f5; - border-radius: 40rpx; - padding: 16rpx 24rpx; + border-radius: 12rpx; + padding: 24rpx; margin-bottom: 20rpx; - transition: all 0.3s ease; -} - -.search-bar:focus-within { - background-color: #e6f7ff; - box-shadow: 0 0 0 4rpx rgba(24, 144, 255, 0.1); -} - -.search-icon-small { - font-size: 28rpx; - color: #999; - margin-right: 16rpx; -} - -.search-input { - flex: 1; - font-size: 28rpx; - color: #333; - background: none; - padding: 0; -} - -.search-input::placeholder { - color: #999; -} - -.filter-area { + box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); display: flex; align-items: center; } -.area-picker { - display: flex; - align-items: center; - background-color: #f5f5f5; - padding: 12rpx 24rpx; - border-radius: 24rpx; - font-size: 28rpx; - color: #333; -} - -.picker-arrow { - margin-left: 8rpx; - font-size: 20rpx; - color: #999; -} - -/* 经纪人列表 */ -.broker-list { - background-color: #f8f8f8; - padding: 0 30rpx; - margin-top: 280rpx; /* 为固定导航和搜索区域留出空间 */ -} - -.broker-item { - background-color: #fff; - border-radius: 24rpx; - margin: 20rpx 0; - padding: 28rpx; - box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); - transition: all 0.2s ease; -} - -.broker-item:active { - transform: translateY(2rpx); - box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.03); - background-color: #fafafa; -} - -.broker-info { - display: flex; - margin-bottom: 24rpx; -} - -.avatar-container { - width: 100rpx; - height: 100rpx; - margin-right: 24rpx; - position: relative; -} - -.avatar { - width: 100%; - height: 100%; +.service-icon { + width: 80rpx; + height: 80rpx; border-radius: 50%; background-color: #f0f0f0; - border: 2rpx solid #f0f0f0; -} - -.online-indicator { - position: absolute; - bottom: 0; - right: 0; - background-color: #52c41a; - color: white; - font-size: 18rpx; - padding: 4rpx 12rpx; - border-radius: 12rpx; - border: 2rpx solid white; -} - -.offline-indicator { - position: absolute; - bottom: 0; - right: 0; - background-color: #999; - color: white; - font-size: 18rpx; - padding: 4rpx 12rpx; - border-radius: 12rpx; - border: 2rpx solid white; -} - -.broker-details { - flex: 1; display: flex; - flex-direction: column; + align-items: center; justify-content: center; + margin-right: 24rpx; + font-size: 40rpx; } -.name-row { - display: flex; - align-items: center; - margin-bottom: 8rpx; - flex-wrap: wrap; +.service-info { + flex: 1; } -.name { +.service-name { + display: block; font-size: 32rpx; font-weight: bold; color: #333; - margin-right: 12rpx; -} - -.score-text { - font-size: 24rpx; - color: #fff; - background: linear-gradient(135deg, #ffb800, #ff7700); - padding: 4rpx 12rpx; - border-radius: 12rpx; - margin-right: 8rpx; -} - -.online-status { - font-size: 22rpx; - color: #52c41a; -} - -.company { - font-size: 28rpx; - color: #666; - margin-bottom: 6rpx; - line-height: 1.4; -} - -.department { - font-size: 26rpx; - color: #999; - margin-bottom: 6rpx; - line-height: 1.4; + margin-bottom: 8rpx; } -.area { +.service-desc { + display: block; font-size: 26rpx; - color: #333; - margin-bottom: 6rpx; - line-height: 1.4; - font-weight: 500; -} - -.experience { - font-size: 24rpx; - color: #999; - margin-top: 8rpx; -} - -/* 专业技能标签预览 */ -.skills-preview { - display: flex; - flex-wrap: wrap; - gap: 10rpx; - margin-top: 12rpx; -} - -.skill-tag-small { - background-color: #f0f9ff; - color: #1890ff; - font-size: 22rpx; - padding: 8rpx 16rpx; - border-radius: 16rpx; - border: 1rpx solid #bae7ff; -} - -.skill-more { - background-color: #f5f5f5; - color: #999; - font-size: 22rpx; - padding: 8rpx 16rpx; - border-radius: 16rpx; - border: 1rpx solid #e0e0e0; -} - -/* 业绩数据统计样式 */ -.performance-stats { - display: flex; - justify-content: space-between; - margin-top: 16rpx; - padding-top: 16rpx; - border-top: 1rpx solid #f0f0f0; -} - -.stat-item { - flex: 1; - text-align: center; + color: #666; } -.stat-value { +.call-button, .chat-button, .email-button { + padding: 12rpx 32rpx; + border-radius: 24rpx; font-size: 28rpx; font-weight: bold; - color: #333; - display: block; -} - -.stat-value.profit-rate { - color: #52c41a; -} - -.stat-label { - font-size: 22rpx; - color: #999; - margin-top: 4rpx; - display: block; -} - -.stat-divider { - color: #e0e0e0; - font-size: 24rpx; - margin: 0 10rpx; - display: flex; - align-items: center; - } - -.action-buttons { - display: flex; - justify-content: flex-end; - gap: 24rpx; - padding-top: 20rpx; - border-top: 1rpx solid #f0f0f0; -} - -.button-chat, .button-call { - width: 72rpx; - height: 72rpx; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s ease; - position: relative; - overflow: hidden; -} - -.button-chat { - background-color: #e6f7f0; -} - -.button-call { - background-color: #e6f0f7; -} - -.button-chat:active { - transform: scale(0.95); - opacity: 0.9; - background-color: #c3e6cb; -} - -.button-call:active { - transform: scale(0.95); - opacity: 0.9; - background-color: #b3d8ff; -} - -.button-icon { - font-size: 36rpx; -} - -/* 空状态 */ -.empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 100rpx 0; - background-color: #fff; - margin: 20rpx 0; - border-radius: 24rpx; -} - -.empty-state text:first-child { - font-size: 100rpx; - margin-bottom: 20rpx; + background-color: #FF6B81; + color: #fff; + border: none; + margin: 0; + line-height: normal; + height: auto; } -.empty-text { - font-size: 28rpx; - color: #999; -} +.call-button:hover, .chat-button:hover, .email-button:hover { + background-color: #FF526D; +} \ No newline at end of file diff --git a/pages/message-list/index.js b/pages/message-list/index.js index 8906f8d..97c1ac2 100644 --- a/pages/message-list/index.js +++ b/pages/message-list/index.js @@ -1,756 +1,59 @@ -// 消息列表页面 +// pages/message-list/index.js Page({ data: { messageList: [] }, - onLoad: function() { - this.loadChatList(); - // 注册全局新消息处理函数 - this.registerGlobalMessageHandler(); - }, - - /** - * 注册全局新消息处理函数 - */ - registerGlobalMessageHandler: function() { - try { - const app = getApp(); - const that = this; - - // 保存原有的处理函数(如果有) - this.originalMessageHandler = app.globalData.onNewMessage; - - // 注册新的处理函数 - app.globalData.onNewMessage = function(message) { - console.log('消息列表页面收到新消息:', message); - // 重新加载聊天列表 - that.loadChatList(); - - // 调用原始处理函数(如果有) - if (that.originalMessageHandler && typeof that.originalMessageHandler === 'function') { - that.originalMessageHandler(message); - } - }; - - console.log('已注册全局新消息处理函数'); - } catch (e) { - console.error('注册全局新消息处理函数失败:', e); - } - }, - - onShow: function() { - console.log('消息列表页面显示,开始加载数据...'); - // 每次显示页面时强制刷新聊天列表 - this.loadChatList(); - - // 确保WebSocket连接已建立,用于接收实时消息 - this.ensureWebSocketConnected(); - }, - - /** - * 确保WebSocket连接已建立 - */ - ensureWebSocketConnected: function() { - try { - const app = getApp(); - if (typeof require === 'function') { - const socketManager = require('../../utils/websocket.js').default; - - // 检查连接状态 - if (!socketManager.getConnectionStatus()) { - console.log('WebSocket未连接,尝试重新连接...'); - // 使用正确的WebSocket服务器地址 - const wsUrl = app.globalData.webSocketUrl || 'ws://localhost:3003'; - socketManager.connect(wsUrl); - } - } - } catch (e) { - console.error('确保WebSocket连接失败:', e); - } - }, - - /** - * 加载聊天列表 - 从服务器API获取数据 - */ - loadChatList: function() { - wx.showLoading({ title: '加载中' }); - - try { - // 从服务器获取真实的聊天列表数据 - const app = getApp(); - const token = app.globalData.token || wx.getStorageSync('token'); - const currentUserId = app.globalData.userInfo?.userId || wx.getStorageSync('userId') || 'unknown'; - const currentUserType = app.globalData.userType || wx.getStorageSync('userType') || ''; - const isManager = app.globalData.isManager || currentUserType === 'manager' || currentUserType.includes('manager'); - const isCustomer = app.globalData.isCustomer || (currentUserType.includes('buyer') || currentUserType.includes('seller') || currentUserType.includes('both')); - - console.log('当前用户类型:', currentUserType, 'isManager:', isManager, 'isCustomer:', isCustomer); - - // 使用正确的API配置,兼容开发环境 - const baseUrl = app.globalData.baseUrl || 'http://localhost:3003'; - console.log('使用API地址:', baseUrl); - console.log('当前用户ID:', currentUserId); - - // 根据用户类型选择不同的API端点 - let apiUrl; - if (isManager) { - // 客服类型使用专门的接口获取客户会话列表 - const managerId = wx.getStorageSync('managerId') || currentUserId; - apiUrl = `${baseUrl}/api/conversations/manager/${managerId}`; - console.log('客服模式,使用manager API获取会话列表'); - } else if (isCustomer || !isManager) { - // 普通用户使用user API - apiUrl = `${baseUrl}/api/conversations/user/${currentUserId}`; - console.log('客户模式,使用user API获取会话列表'); - } else { - // 默认使用user API - apiUrl = `${baseUrl}/api/conversations/user/${currentUserId}`; - console.log('默认模式,使用user API获取会话列表'); - } - - wx.request({ - url: apiUrl, - method: 'GET', - header: { - 'Authorization': token ? `Bearer ${token}` : '', - 'content-type': 'application/json' - }, - success: (res) => { - console.log('获取聊天列表成功:', res.data); - // 处理不同的API响应格式 - let chatData = []; - - if (res.data.code === 0 && res.data.data) { - chatData = res.data.data; - } else if (Array.isArray(res.data)) { - // 如果直接返回数组 - chatData = res.data; - } else if (res.data) { - // 如果返回的是对象但不是标准格式,尝试直接使用 - chatData = [res.data]; - } - - // 合理过滤聊天数据,保留有效的会话 - const validChatData = chatData.filter(item => { - // 确保item有效 - if (!item || typeof item !== 'object') return false; - - // 获取显示的用户ID并验证其有效性 - const displayUserId = item.userId === currentUserId ? item.managerId : item.userId || item.id; - if (!displayUserId || displayUserId === 'undefined' || displayUserId === 'null' || String(displayUserId).trim() === '') { - console.log('过滤掉无效的用户ID:', displayUserId, '的会话'); - return false; - } - - // 过滤掉临时用户ID(以temp_开头的ID) - const safeUserId = String(displayUserId).trim(); - if (safeUserId.startsWith('temp_')) { - console.log('过滤掉临时用户ID:', safeUserId, '的会话'); - return false; - } - - return true; - }); - - if (validChatData.length > 0) { - // 格式化聊天列表数据 - const formattedMessages = validChatData.map(item => { - // 获取显示的用户ID和名称 - let displayUserId, displayName; - - // 关键修复:根据用户类型和聊天对象类型确定显示的ID和名称 - if (isManager) { - // 客服模式:显示的是客户信息 - displayUserId = item.userId; - displayName = this.getUserNameById(displayUserId); - } else { - // 普通用户模式:显示的是客服信息 - displayUserId = item.managerId || item.userId || item.id; - displayName = this.getUserNameById(displayUserId); - } - - return { - userId: displayUserId, - userName: displayName || item.userName || item.name || '未知用户', - avatar: item.avatar || (displayName && displayName.charAt(0)) || '用', - lastMessage: item.lastMessage || item.content || '', - lastMessageTime: this.formatMessageTime(item.lastMessageTime || item.createdAt || Date.now()), - messageCount: item.messageCount || 0, - isRead: item.isRead || false, - unreadCount: item.unreadCount || 0 - }; - }).filter(chat => { - // 过滤掉客服之间的会话 - if (isManager) { - // 只过滤真正的客服聊天,保留所有用户消息 - const isChatWithManager = - (chat.userId && (chat.userId.includes('customer_service') || chat.userId.includes('manager'))) || - (chat.userName && chat.userName.includes('客服') && !chat.userName.includes('用户')); - - console.log('过滤检查 - 用户ID:', chat.userId, '用户名:', chat.userName, '是否为客服聊天:', isChatWithManager); - return !isChatWithManager; - } - return true; - }); - - // 按最后消息时间排序(最新的在前) - formattedMessages.sort((a, b) => { - return new Date(b.lastMessageTime) - new Date(a.lastMessageTime); - }); - - this.setData({ messageList: formattedMessages }); - } else { - console.log('暂无有效的聊天消息'); - // 确保清空消息列表 - this.setData({ messageList: [] }); - - // 尝试从本地存储加载数据作为备选,但现在本地加载逻辑也会严格验证 - this.loadFromLocalStorage(); - } - }, - fail: (err) => { - console.error('网络请求失败:', err); - wx.showToast({ - title: '网络请求失败,尝试使用本地数据', - icon: 'none', - duration: 3000 - }); - - // 失败时尝试从本地存储加载数据 - this.loadFromLocalStorage(); - }, - complete: () => { - wx.hideLoading(); - } - }); - } catch (error) { - console.error('加载聊天列表异常:', error); - wx.hideLoading(); - wx.showToast({ title: '加载异常', icon: 'none' }); - - // 异常时尝试从本地存储加载数据 - this.loadFromLocalStorage(); - } - }, - - /** - * 从本地存储加载聊天列表 - 作为备用方案 - */ - loadFromLocalStorage: function() { - try { - // 获取所有存储的聊天记录键 - const storageInfo = wx.getStorageInfoSync(); - const chatKeys = storageInfo.keys.filter(key => key.startsWith('chat_messages_')); - - const messageList = []; - // 获取当前用户信息 - const app = getApp(); - const currentUserId = wx.getStorageSync('userId') || ''; - const currentUserType = app.globalData.userType || wx.getStorageSync('userType') || 'customer'; - - console.log('从本地存储加载聊天列表 - 用户类型:', currentUserType, '聊天记录数量:', chatKeys.length); - - // 遍历每个聊天记录 - chatKeys.forEach(key => { - const chatUserId = key.replace('chat_messages_', ''); - - // 严格验证聊天用户ID - if (!chatUserId || chatUserId === 'undefined' || chatUserId === 'null' || chatUserId.trim() === '') { - console.log('跳过无效的聊天用户ID:', chatUserId); - return; - } - - // 过滤掉临时用户ID(以temp_开头的ID) - if (chatUserId.trim().startsWith('temp_')) { - console.log('跳过临时用户ID:', chatUserId, '的会话'); - return; - } - - // 获取消息列表 - const messages = wx.getStorageSync(key); - - // 严格检查消息列表 - if (!messages || !Array.isArray(messages) || messages.length === 0) { - console.log('跳过无效或空的消息列表:', chatUserId); - return; - } - - // 避免处理自己与自己的聊天 - if (chatUserId === currentUserId) { - console.log('跳过自己与自己的聊天:', chatUserId); - return; - } - - // 过滤并验证每条消息 - const validMessages = messages.filter(msg => { - return msg && - msg.content && - typeof msg.content === 'string' && - msg.content.trim() !== '' && - msg.time; // 确保有时间戳 - }); - - // 如果没有有效消息,跳过 - if (validMessages.length === 0) { - console.log('跳过没有有效消息的对话:', chatUserId); - return; - } - - // 放宽条件,只要有有效消息就显示会话,不再要求必须有对方发送的消息 - // 这样可以显示用户主动发起的聊天会话 - - // 获取最后一条有效消息 - const lastValidMessage = validMessages[validMessages.length - 1]; - - // 再次确认最后一条消息的有效性 - if (!lastValidMessage || !lastValidMessage.content || lastValidMessage.content.trim() === '') { - console.log('跳过最后消息无效的对话:', chatUserId); - return; - } - - // 统计未读消息数量 - const unreadCount = validMessages.filter(msg => { - return msg.sender !== 'me' && !msg.isRead; - }).length; - - // 仅当不是客服之间的对话时添加 - const userName = this.getUserNameById(chatUserId); - if (!(isManager && userName && (userName.includes('客服') || userName.includes('manager')))) { - messageList.push({ - userId: chatUserId, - userName: userName, - avatar: '', - lastMessage: this.formatMessagePreview(lastValidMessage), - lastMessageTime: this.formatMessageTime(lastValidMessage.time), - messageCount: validMessages.length, - unreadCount: unreadCount, - isRead: unreadCount === 0 - }); - } else { - console.log('客服身份,跳过与其他客服的聊天:', chatUserId, userName); - } - }); - - // 按最后消息时间排序(最新的在前) - messageList.sort((a, b) => { - return new Date(b.lastMessageTime) - new Date(a.lastMessageTime); - }); - - if (messageList.length > 0) { - console.log('从本地存储加载了', messageList.length, '条有效聊天记录'); - this.setData({ messageList: messageList }); - } else { - // 确保清空消息列表,避免显示任何默认用户 - console.log('没有找到任何有效的聊天记录,清空消息列表'); - this.setData({ messageList: [] }); - - // 清理可能导致默认用户显示的无效本地存储数据 - this.cleanupInvalidStorageData(); - } - } catch (e) { - console.error('从本地存储加载聊天列表失败:', e); - // 出错时也确保消息列表为空 - this.setData({ messageList: [] }); - } - }, - - /** - * 清理无效的本地存储数据,防止显示默认用户 - */ - cleanupInvalidStorageData: function() { - try { - const storageInfo = wx.getStorageInfoSync(); - const chatKeys = storageInfo.keys.filter(key => key.startsWith('chat_messages_')); - - chatKeys.forEach(key => { - const messages = wx.getStorageSync(key); - - // 删除无效或空的消息列表 - if (!messages || !Array.isArray(messages) || messages.length === 0) { - console.log('删除无效的聊天存储键:', key); - wx.removeStorageSync(key); - return; - } - - // 检查是否只有无效消息 - const allInvalid = messages.every(msg => { - return !msg || - !msg.content || - typeof msg.content !== 'string' || - msg.content.trim() === ''; - }); - - if (allInvalid) { - console.log('删除全是无效消息的聊天记录:', key); - wx.removeStorageSync(key); - } - }); - - console.log('清理无效存储数据完成'); - } catch (e) { - console.error('清理无效存储数据失败:', e); - } - }, - - /** - * 根据用户ID获取用户名 - 增强版,确保不返回默认的'未知用户'名称 - */ - getUserNameById: function(userId) { - try { - // 增强的参数有效性检查 - if (!userId || typeof userId === 'undefined' || userId === null || String(userId).trim() === '') { - return null; - } - - // 确保userId是字符串类型,并去除前后空格 - const safeUserId = String(userId).trim(); - - // 避免显示"undefined"或"null" - if (safeUserId === 'undefined' || safeUserId === 'null') { - return null; - } - - // 特殊处理特定用户ID,直接返回对应的手机号 - if (safeUserId === 'user_1765415381781_1iy1ls177') { - return '17828048259'; - } - - // 获取当前用户类型 - const app = getApp(); - const currentUserType = app.globalData.userType || wx.getStorageSync('userType') || 'customer'; - - // 过滤掉临时用户ID(以temp_开头的ID) - if (safeUserId.startsWith('temp_')) { - console.log('过滤临时用户ID:', safeUserId); - return null; - } - - // 尝试从全局客服列表中获取用户名 - let customerServiceList = app.globalData.customerServiceList || []; - - // 如果全局客服列表为空,尝试从本地存储获取 - if (customerServiceList.length === 0) { - customerServiceList = wx.getStorageSync('cached_customer_services') || []; - // 如果本地存储也没有,尝试从服务器获取 - if (customerServiceList.length === 0) { - console.log('客服列表为空,尝试从服务器获取'); - // 这里可以添加异步获取客服列表的逻辑,但由于getUserNameById是同步函数,暂时不实现 - } - } - - // 从客服列表中查找匹配的客服 - const service = customerServiceList.find(item => { - // 安全地访问对象属性,避免undefined属性错误 - const itemId = item?.id ? String(item.id).trim() : ''; - const itemManagerId = item?.managerId ? String(item.managerId).trim() : ''; - return itemId === safeUserId || itemManagerId === safeUserId; - }); - - if (service) { - return service.alias || service.name || '客服'; - } - - - - // 尝试从用户信息缓存中获取更详细的用户信息 - // 客服模式下,尝试获取客户的手机号或其他标识信息 - if (currentUserType === 'manager' || currentUserType === 'customer_service') { - // 尝试从本地存储获取用户详细信息 - try { - // 尝试获取用户数据映射 - const users = wx.getStorageSync('users') || {}; - const userData = users[safeUserId]; - - // 首先检查是否是手机号格式的用户ID(中国手机号格式) - if (/^1[3-9]\d{9}$/.test(safeUserId)) { - // 如果是手机号,直接显示完整手机号 - return safeUserId; - } - - if (userData) { - // 如果有用户信息,优先使用 - if (userData.phoneNumber) { - return userData.phoneNumber; // 显示完整手机号 - } - if (userData.phone) { - return userData.phone; // 显示完整手机号 - } - if (userData.info && userData.info.nickName) { - return userData.info.nickName; - } - } - - // 尝试获取会话相关的用户信息 - const sessionInfo = wx.getStorageSync(`session_${safeUserId}`); - if (sessionInfo && sessionInfo.userName) { - return sessionInfo.userName; - } - } catch (innerError) { - console.log('获取用户详细信息失败:', innerError); - } - } - - // 固定用户名映射 - const userMap = { - 'user_1': '张三', - 'user_2': '李四', - 'user_3': '王五', - 'user_4': '赵六', - 'user_5': '钱七' - }; - - if (userMap[safeUserId]) { - return userMap[safeUserId]; - } - - // 对于manager_开头的ID,显示为客服 - if (safeUserId.startsWith('manager_') || safeUserId.includes('manager')) { - return '客服-' + (safeUserId.length >= 4 ? safeUserId.slice(-4) : safeUserId); - } - - // 对于test_开头的测试用户ID,显示为测试用户 - if (safeUserId.startsWith('test_')) { - return '测试用户-' + (safeUserId.length >= 8 ? safeUserId.slice(-4) : safeUserId); - } - - // 处理系统通知类型 - if (safeUserId.startsWith('system_')) { - return '系统通知'; - } - - // 处理群组聊天 - if (safeUserId.startsWith('group_')) { - return '群组-' + (safeUserId.replace('group_', '').length >= 4 ? safeUserId.replace('group_', '').slice(-4) : safeUserId.replace('group_', '')); - } - - // 从数据库记录中获取的用户ID格式处理 - if (safeUserId.includes('_customer_')) { - // 提取数字部分 - const match = safeUserId.match(/\d+/); - if (match && match[0]) { - return '用户' + match[0].slice(-4); - } - } - - // 检查是否是手机号格式 - if (/^1[3-9]\d{9}$/.test(safeUserId)) { - // 直接显示完整手机号 - return safeUserId; - } - - // 对于未识别的有效用户ID,返回基于ID的标识而不是默认名称 - // 确保不返回'未知用户',而是使用实际的用户ID信息 - if (safeUserId.length > 10) { - // 对于长ID,截取并添加省略号 - return '用户-' + safeUserId.substring(0, 10) + '...'; - } else { - // 对于短ID,直接使用 - return '用户-' + safeUserId; - } - } catch (e) { - console.error('获取用户名失败:', e); - // 即使出现错误,也不返回'未知用户',而是返回'用户-未知' - return '用户-未知'; - } - }, - - /** - * 格式化消息预览 - */ - formatMessagePreview: function(message) { - if (message.type === 'system') { - return '[系统消息] ' + message.content; - } else { - const senderPrefix = message.sender === 'me' ? '我: ' : ''; - // 限制预览长度 - let preview = senderPrefix + message.content; - if (preview.length > 30) { - preview = preview.substring(0, 30) + '...'; - } - return preview; - } - }, - - /** - * 格式化消息时间 - 增强版,支持多种时间格式 - 统一使用北京时间 - */ - formatMessageTime: function(timeStr) { - try { - if (!timeStr) return ''; - - // 获取当前北京时间 - const now = new Date(); - const nowYear = now.getFullYear(); - const nowMonth = now.getMonth(); - const nowDate = now.getDate(); - - let date; - - // 尝试不同的时间格式解析 - if (typeof timeStr === 'number') { - // 处理时间戳 - let adjustedTimestamp = timeStr; - // 检查是否是秒级时间戳(小于1e12,毫秒级时间戳通常大于1e12) - if (adjustedTimestamp < 1e12) { - // 秒级时间戳转换为毫秒级 - adjustedTimestamp *= 1000; - } - date = new Date(adjustedTimestamp); - } else if (typeof timeStr === 'string') { - if (/^\d+$/.test(timeStr)) { - // 数字字符串,可能是时间戳 - const timestamp = parseInt(timeStr); - let adjustedTimestamp = timestamp; - // 检查是否是秒级时间戳 - if (adjustedTimestamp < 1e12) { - // 秒级时间戳转换为毫秒级 - adjustedTimestamp *= 1000; - } - date = new Date(adjustedTimestamp); - } else if (timeStr.includes('T')) { - // ISO 8601格式的时间字符串,如 "2025-12-18T07:03:00.000Z" - // 先将字符串解析为Date对象(自动转换为本地时间) - date = new Date(timeStr); - } else { - // 其他格式的时间字符串,直接解析 - date = new Date(timeStr); - } - } else { - // 其他类型,返回空 - return ''; - } - - // 验证日期是否有效 - if (isNaN(date.getTime())) { - return ''; - } - - // 获取北京时间的各组成部分 - const beijingYear = date.getFullYear(); - const beijingMonth = date.getMonth(); - const beijingDate = date.getDate(); - const beijingHours = date.getHours(); - const beijingMinutes = date.getMinutes(); - - // 计算天数差 - const diffTime = Math.abs(now.getTime() - date.getTime()); - const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); - - if (diffDays === 0) { - // 今天的消息只显示时间,格式化为 HH:mm - const hours = beijingHours.toString().padStart(2, '0'); - const minutes = beijingMinutes.toString().padStart(2, '0'); - return `${hours}:${minutes}`; - } else if (diffDays === 1) { - // 昨天的消息 - return '昨天'; - } else if (diffDays < 7) { - // 一周内的消息显示星期 - const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; - return weekdays[date.getDay()]; - } else if (diffDays < 365) { - // 今年内的消息显示月日 - const month = (beijingMonth + 1).toString().padStart(2, '0'); - const day = beijingDate.toString().padStart(2, '0'); - return `${month}-${day}`; - } else { - // 超过一年的消息显示完整日期 - const year = beijingYear; - const month = (beijingMonth + 1).toString().padStart(2, '0'); - const day = beijingDate.toString().padStart(2, '0'); - return `${year}-${month}-${day}`; - } - } catch (e) { - console.error('格式化消息时间失败:', e); - return ''; - } - }, - - /** - * 跳转到聊天详情页 - */ - goToChat: function(e) { - const userId = e.currentTarget.dataset.userId; - // 查找对应的用户信息 - const userInfo = this.data.messageList.find(item => item.userId === userId); + onLoad: function (options) { + this.loadMessageList(); + }, + + loadMessageList: function () { + // 模拟加载消息列表 + const messageList = [ + { + id: 1, + type: 'system', + name: '系统通知', + avatar: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0', + content: '您有一条新的系统通知', + time: '刚刚', + unread: 1 + }, + { + id: 2, + type: 'service', + name: '客服中心', + avatar: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0', + content: '您好,请问有什么可以帮助您的?', + time: '5分钟前', + unread: 0 + }, + { + id: 3, + type: 'order', + name: '订单通知', + avatar: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0', + content: '您的订单已发货', + time: '1小时前', + unread: 0 + } + ]; - wx.navigateTo({ - url: `/pages/chat-detail/index?userId=${userId}&userName=${encodeURIComponent(userInfo?.userName || '')}` + this.setData({ + messageList: messageList }); }, - /** - * 页面相关事件处理函数--监听用户下拉动作 - */ - onPullDownRefresh: function() { - console.log('用户下拉刷新,重新加载聊天列表...'); - - // 重新加载聊天列表 - this.loadChatList(); - - // 添加延迟,确保用户能看到刷新效果 - setTimeout(() => { - wx.stopPullDownRefresh(); - console.log('下拉刷新完成'); - }, 1000); - }, - - /** - * 处理清除聊天记录 - */ - onUnload: function() { - // 页面卸载时的清理工作 - // 恢复原始的全局消息处理函数 - try { - const app = getApp(); - if (this.originalMessageHandler) { - app.globalData.onNewMessage = this.originalMessageHandler; - } else { - // 如果没有原始处理函数,则清除当前的 - delete app.globalData.onNewMessage; - } - console.log('已清理全局新消息处理函数'); - } catch (e) { - console.error('清理全局新消息处理函数失败:', e); - } - }, - - handleClearChat: function(e) { - const userId = e.currentTarget.dataset.userId; - - wx.showModal({ - title: '确认清空', - content: '确定要清空与该用户的所有聊天记录吗?此操作不可恢复。', - success: (res) => { - if (res.confirm) { - this.clearChatHistory(userId); - } - } + onMessageTap: function (e) { + // 跳转到消息详情页 + wx.navigateTo({ + url: '/pages/chat-detail/index?id=' + e.currentTarget.dataset.id }); }, - /** - * 清空指定用户的聊天记录 - */ - clearChatHistory: function(userId) { - try { - wx.removeStorageSync(`chat_messages_${userId}`); - console.log('已清空用户', userId, '的聊天记录'); - - // 刷新消息列表 - this.loadChatList(); - - wx.showToast({ - title: '聊天记录已清空', - icon: 'success' - }); - } catch (e) { - console.error('清空聊天记录失败:', e); - wx.showToast({ - title: '清空失败', - icon: 'none' - }); - } + onPullDownRefresh: function () { + this.loadMessageList(); + wx.stopPullDownRefresh(); } -}); +}); \ No newline at end of file diff --git a/pages/message-list/index.json b/pages/message-list/index.json index 3edb3d9..e6cd4ae 100644 --- a/pages/message-list/index.json +++ b/pages/message-list/index.json @@ -1,4 +1,3 @@ { - "navigationBarTitleText": "消息列表", - "usingComponents": {} -} + "navigationBarTitleText": "消息列表" +} \ No newline at end of file diff --git a/pages/message-list/index.wxml b/pages/message-list/index.wxml index d648188..56965ab 100644 --- a/pages/message-list/index.wxml +++ b/pages/message-list/index.wxml @@ -1,30 +1,34 @@ - - - 消息列表 + + + 消息列表 - - - - - - - - - {{item.userName}} - {{item.lastMessageTime}} - - {{item.lastMessage || '暂无消息'}} - - - + + + + + + + 系统通知 + 刚刚 + 您有一条新的系统通知 - + 1 + - - 暂无聊天记录 + + + + + + + 客服中心 + 5分钟前 + + 您好,请问有什么可以帮助您的? + - + \ No newline at end of file diff --git a/pages/message-list/index.wxss b/pages/message-list/index.wxss index d32ef18..fcd98b6 100644 --- a/pages/message-list/index.wxss +++ b/pages/message-list/index.wxss @@ -1,47 +1,43 @@ -.message-list-container { - height: 100vh; +.container { background-color: #f5f5f5; - display: flex; - flex-direction: column; + min-height: 100vh; } -.page-header { - padding: 20rpx 30rpx; +.message-header { background-color: #fff; - border-bottom: 1rpx solid #eee; + padding: 20rpx; + border-bottom: 1rpx solid #e8e8e8; + display: flex; + align-items: center; + justify-content: center; } -.page-title { +.message-title { font-size: 36rpx; font-weight: bold; color: #333; } .message-list { - flex: 1; - overflow-y: auto; + padding: 20rpx; } .message-item { + background-color: #fff; + border-radius: 12rpx; + padding: 24rpx; + margin-bottom: 20rpx; + box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); display: flex; align-items: center; - padding: 20rpx 30rpx; - background-color: #fff; - border-bottom: 1rpx solid #f0f0f0; - transition: background-color 0.2s; -} - -.message-item:active { - background-color: #f8f8f8; } .avatar { - width: 100rpx; - height: 100rpx; + width: 80rpx; + height: 80rpx; border-radius: 50%; + margin-right: 24rpx; overflow: hidden; - margin-right: 20rpx; - background-color: #f0f0f0; } .avatar image { @@ -49,23 +45,21 @@ height: 100%; } -.message-content { +.message-info { flex: 1; - display: flex; - flex-direction: column; - justify-content: center; + position: relative; } -.message-header { +.message-name-time { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8rpx; } -.user-name { +.message-name { font-size: 32rpx; - font-weight: 500; + font-weight: bold; color: #333; } @@ -74,28 +68,23 @@ color: #999; } -.message-preview { +.message-content { + display: block; font-size: 28rpx; color: #666; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + width: 100%; } -.message-actions { - margin-left: 20rpx; -} - -.message-actions button { - font-size: 24rpx; - padding: 0 20rpx; - min-width: 80rpx; - line-height: 50rpx; -} - -.empty-state { - padding: 100rpx 0; +.unread-badge { + background-color: #FF6B81; + color: #fff; + font-size: 22rpx; + font-weight: bold; + padding: 4rpx 12rpx; + border-radius: 14rpx; + min-width: 28rpx; text-align: center; - color: #999; - font-size: 28rpx; -} +} \ No newline at end of file diff --git a/pages/profile/index.js b/pages/profile/index.js index 7f272b8..356dd30 100644 --- a/pages/profile/index.js +++ b/pages/profile/index.js @@ -112,16 +112,11 @@ Page({ if (userType && userType !== '') { let identityLabel = '身份:not_set' - // 优先处理包含manager的类型 - if (userType.includes('manager')) { - identityLabel = '身份:客服'; - } else { - switch (userType) { - case 'buyer': identityLabel = '身份:买家'; break - case 'seller': identityLabel = '身份:卖家'; break - case 'both': identityLabel = '身份:买卖家'; break - case 'buyer+seller': identityLabel = '身份:买卖家'; break - } + switch (userType) { + case 'buyer': identityLabel = '身份:买家'; break + case 'seller': identityLabel = '身份:卖家'; break + case 'both': identityLabel = '身份:买卖家'; break + case 'buyer+seller': identityLabel = '身份:买卖家'; break } filteredTags.push(identityLabel) console.log('加载用户信息 - 根据当前用户类型显示身份标签:', identityLabel) @@ -208,122 +203,11 @@ Page({ users[userId] = {} } - // 获取当前的用户类型,检查是否包含manager - const currentUserType = wx.getStorageSync('userType') || '' - let isManager = currentUserType.includes('manager') - // 移除serverType中的customer(如果存在) let processedServerType = serverType.replace(/,?customer/g, '').replace(/^,|,$/g, '') - // 当服务器返回customer类型时,额外检查用户是否为客服 - if (serverType === 'customer' && !isManager) { - console.log('服务器返回customer类型,检查用户是否为客服...') - const app = getApp() - - // 从多个可能的位置获取手机号,增加获取成功率 - let phoneNumber = '' - // 1. 尝试从storage获取 - phoneNumber = wx.getStorageSync('phoneNumber') || '' - console.log('1. 从storage获取手机号:', phoneNumber) - - // 2. 尝试从globalData获取 - if (!phoneNumber && app.globalData.userInfo && app.globalData.userInfo.phoneNumber) { - phoneNumber = app.globalData.userInfo.phoneNumber - console.log('2. 从globalData获取手机号:', phoneNumber) - } - - // 3. 尝试从userInfo storage获取 - if (!phoneNumber) { - const userInfo = wx.getStorageSync('userInfo') || {} - phoneNumber = userInfo.phoneNumber || '' - console.log('3. 从userInfo storage获取手机号:', phoneNumber) - } - - // 调用客服检查函数 - const api = require('../../utils/api') - api.checkIfUserIsCustomerService(phoneNumber).then(isCS => { - let newUserType = '' - - if (isCS) { - console.log('确认用户为客服,设置为manager类型') - isManager = true - - // 构建新的用户类型,设置为manager - newUserType = 'manager' - - // 更新本地存储 - users[userId].type = newUserType - wx.setStorageSync('users', users) - - // 更新全局用户类型 - app.globalData.userType = newUserType - - // 更新页面显示的用户类型 - this.setData({ - userType: this.formatUserType(newUserType) - }) - - console.log('用户身份已更新为客服:', newUserType) - - // 调用API将用户类型更新同步到服务器 - // 注意:传入空字符串以触发强制同步manager类型的逻辑 - api.updateUserType('').then(res => { - console.log('客服身份已成功同步到服务器:', res) - }).catch(error => { - console.error('同步客服身份到服务器失败:', error) - }) - } else { - console.log('确认用户非客服,使用处理后的类型') - newUserType = processedServerType || '' - - // 对于非客服用户,更新为处理后的类型 - if (users[userId].type !== newUserType) { - users[userId].type = newUserType - wx.setStorageSync('users', users) - - // 更新全局用户类型 - app.globalData.userType = newUserType - - // 更新页面显示的用户类型 - this.setData({ - userType: this.formatUserType(newUserType) - }) - } - } - - // 更新用户标签 - this.updateUserTags(userId, newUserType) - }).catch(error => { - console.error('检查客服身份失败:', error) - // 继续执行后续操作 - let newUserType = processedServerType || '' - if (users[userId].type !== newUserType) { - users[userId].type = newUserType - wx.setStorageSync('users', users) - - // 更新全局用户类型 - app.globalData.userType = newUserType - - // 更新页面显示的用户类型 - this.setData({ - userType: this.formatUserType(newUserType) - }) - } - this.updateUserTags(userId, newUserType) - }) - - // 异步处理已启动,此处返回 - return - } - - // 构建新的用户类型,如果是manager则保留manager标识 + // 构建新的用户类型 let newUserType = processedServerType - if (isManager) { - console.log('检测到用户为客服,保留manager标识') - if (!processedServerType.includes('manager')) { - newUserType = processedServerType ? processedServerType + ',manager' : 'manager' - } - } // 只有当新构建的用户类型与本地不同时才更新 if (users[userId].type !== newUserType) { @@ -350,11 +234,6 @@ Page({ // 格式化用户类型显示 formatUserType(type) { - // 优先处理包含manager的类型 - if (type && type.includes('manager')) { - return '客服'; - } - switch (type) { case 'buyer': return '买家'; case 'seller': return '卖家'; diff --git a/pages/test-service/test-service.js b/pages/test-service/test-service.js deleted file mode 100644 index 80146b0..0000000 --- a/pages/test-service/test-service.js +++ /dev/null @@ -1,382 +0,0 @@ -// 客服功能综合测试页面 - -const socketManager = require('../../utils/websocket'); -const API = require('../../utils/api'); - -Page({ - data: { - // 测试状态 - testResults: [], - currentTest: '', - isTesting: false, - - // 用户信息 - userInfo: null, - userType: 'unknown', - isAuthenticated: false, - - // WebSocket状态 - wsConnected: false, - wsAuthenticated: false, - - // 测试消息 - testMessage: '测试消息', - receivedMessage: null, - - // 测试模式 - testMode: 'customer', // customer 或 customer_service - - // 模拟客服ID - mockServiceId: 'test_service_001' - }, - - onLoad: function() { - console.log('测试页面加载'); - this.setData({ - userInfo: getApp().globalData.userInfo, - userType: getApp().globalData.userType - }); - - // 设置WebSocket事件监听 - this.setupWebSocketListeners(); - }, - - // 设置WebSocket事件监听 - setupWebSocketListeners: function() { - // 连接状态监听 - socketManager.on('status', (status) => { - console.log('WebSocket状态:', status); - this.setData({ - wsConnected: status.type === 'connected' - }); - this.addTestResult(`WebSocket状态: ${status.type} - ${status.message || ''}`); - }); - - // 认证状态监听 - socketManager.on('authenticated', (data) => { - console.log('WebSocket认证成功:', data); - this.setData({ - wsAuthenticated: true, - isAuthenticated: true - }); - this.addTestResult(`WebSocket认证成功,用户类型: ${data.userType || 'unknown'}`); - }); - - // 消息接收监听 - socketManager.on('message', (message) => { - console.log('收到WebSocket消息:', message); - this.setData({ - receivedMessage: message - }); - this.addTestResult(`收到消息: ${JSON.stringify(message).substring(0, 100)}...`); - }); - - // 错误监听 - socketManager.on('error', (error) => { - console.error('WebSocket错误:', error); - this.addTestResult(`错误: ${error.message || JSON.stringify(error)}`, true); - }); - }, - - // 开始综合测试 - startTest: function() { - if (this.data.isTesting) { - wx.showToast({ title: '测试正在进行中', icon: 'none' }); - return; - } - - this.setData({ - isTesting: true, - testResults: [], - currentTest: '' - }); - - this.addTestResult('===== 开始综合测试 ====='); - - // 按顺序执行测试用例 - setTimeout(() => { - this.testUserTypeDetection(); - }, 500); - }, - - // 测试1: 用户类型检测 - testUserTypeDetection: function() { - this.setData({ currentTest: '用户类型检测' }); - this.addTestResult('测试1: 验证用户类型检测功能'); - - const app = getApp(); - const userType = app.globalData.userType || 'unknown'; - const storedType = wx.getStorageSync('userType') || 'unknown'; - - this.addTestResult(`全局用户类型: ${userType}`); - this.addTestResult(`本地存储用户类型: ${storedType}`); - - if (userType === storedType && userType !== 'unknown') { - this.addTestResult('✓ 用户类型检测通过'); - } else { - this.addTestResult('✗ 用户类型检测失败', true); - } - - setTimeout(() => { - this.testWebSocketConnection(); - }, 1000); - }, - - // 测试2: WebSocket连接和认证 - testWebSocketConnection: function() { - this.setData({ currentTest: 'WebSocket连接' }); - this.addTestResult('测试2: 验证WebSocket连接和认证功能'); - - // 检查是否已连接 - if (socketManager.getConnectionStatus()) { - this.addTestResult('✓ WebSocket已连接'); - setTimeout(() => { - this.testAuthentication(); - }, 500); - } else { - this.addTestResult('尝试建立WebSocket连接...'); - // 构建连接URL - const app = getApp(); - const userType = app.globalData.userType || 'customer'; - const userId = wx.getStorageSync('userId') || `test_${Date.now()}`; - - const wsUrl = `ws://localhost:3003?userId=${userId}&userType=${userType}`; - this.addTestResult(`连接URL: ${wsUrl}`); - - socketManager.connect(wsUrl); - - // 等待连接建立 - setTimeout(() => { - if (this.data.wsConnected) { - this.addTestResult('✓ WebSocket连接成功'); - this.testAuthentication(); - } else { - this.addTestResult('✗ WebSocket连接失败', true); - this.completeTest(); - } - }, 3000); - } - }, - - // 测试3: 认证功能 - testAuthentication: function() { - this.setData({ currentTest: '认证功能' }); - this.addTestResult('测试3: 验证WebSocket认证功能'); - - if (this.data.wsAuthenticated) { - this.addTestResult('✓ WebSocket认证成功'); - setTimeout(() => { - this.testMessageSending(); - }, 500); - } else { - this.addTestResult('尝试手动认证...'); - const app = getApp(); - const userType = app.globalData.userType || this.data.testMode; - const userId = wx.getStorageSync('userId') || `test_${Date.now()}`; - - socketManager.authenticate(userType, userId); - - // 等待认证结果 - setTimeout(() => { - if (this.data.wsAuthenticated) { - this.addTestResult('✓ 手动认证成功'); - this.testMessageSending(); - } else { - this.addTestResult('✗ 认证失败', true); - this.completeTest(); - } - }, 2000); - } - }, - - // 测试4: 消息发送功能 - testMessageSending: function() { - this.setData({ currentTest: '消息发送' }); - this.addTestResult('测试4: 验证消息发送功能'); - - const app = getApp(); - const userType = app.globalData.userType || this.data.testMode; - const targetId = userType === 'customer_service' ? - `test_customer_${Date.now()}` : - this.data.mockServiceId; - - this.addTestResult(`当前用户类型: ${userType}, 目标ID: ${targetId}`); - - const testMessage = { - type: 'chat_message', - direction: userType === 'customer_service' ? 'service_to_customer' : 'customer_to_service', - data: { - receiverId: targetId, - senderId: wx.getStorageSync('userId') || `test_${Date.now()}`, - senderType: userType, - content: this.data.testMessage || '测试消息_' + Date.now(), - contentType: 1, - timestamp: Date.now() - } - }; - - const sent = socketManager.send(testMessage); - if (sent) { - this.addTestResult('✓ 消息发送成功'); - // 等待接收消息(如果有回复) - setTimeout(() => { - this.testBidirectionalCommunication(); - }, 3000); - } else { - this.addTestResult('✗ 消息发送失败', true); - this.completeTest(); - } - }, - - // 测试5: 双向通信功能 - testBidirectionalCommunication: function() { - this.setData({ currentTest: '双向通信' }); - this.addTestResult('测试5: 验证双向通信功能'); - - if (this.data.receivedMessage) { - this.addTestResult('✓ 收到响应消息'); - this.addTestResult(`消息内容: ${JSON.stringify(this.data.receivedMessage).substring(0, 150)}...`); - } else { - this.addTestResult('⚠ 未收到响应消息(可能是正常的,取决于服务器配置)'); - } - - setTimeout(() => { - this.testUserTypeSwitching(); - }, 1000); - }, - - // 测试6: 用户类型切换 - testUserTypeSwitching: function() { - this.setData({ currentTest: '用户类型切换' }); - this.addTestResult('测试6: 验证用户类型切换功能'); - - try { - // 模拟切换用户类型 - const app = getApp(); - const originalType = app.globalData.userType; - const newType = originalType === 'customer' ? 'customer_service' : 'customer'; - - app.globalData.userType = newType; - wx.setStorageSync('userType', newType); - - this.addTestResult(`✓ 用户类型切换成功: ${originalType} → ${newType}`); - this.addTestResult(`新的全局用户类型: ${app.globalData.userType}`); - this.addTestResult(`新的存储用户类型: ${wx.getStorageSync('userType')}`); - - // 恢复原始类型 - setTimeout(() => { - app.globalData.userType = originalType; - wx.setStorageSync('userType', originalType); - this.addTestResult(`恢复原始用户类型: ${originalType}`); - this.completeTest(); - }, 1000); - } catch (error) { - this.addTestResult(`✗ 用户类型切换失败: ${error.message}`, true); - this.completeTest(); - } - }, - - // 完成测试 - completeTest: function() { - this.addTestResult('===== 测试完成 ====='); - - // 统计测试结果 - const results = this.data.testResults; - const successCount = results.filter(r => r.includes('✓')).length; - const failCount = results.filter(r => r.includes('✗')).length; - const warnCount = results.filter(r => r.includes('⚠')).length; - - this.addTestResult(`测试统计: 成功=${successCount}, 失败=${failCount}, 警告=${warnCount}`); - - if (failCount === 0) { - this.addTestResult('🎉 所有测试通过!'); - } else { - this.addTestResult('❌ 测试中有失败项,请检查', true); - } - - this.setData({ - isTesting: false, - currentTest: '测试完成' - }); - }, - - // 添加测试结果 - addTestResult: function(message, isError = false) { - const timestamp = new Date().toLocaleTimeString(); - const resultItem = { - id: Date.now(), - time: timestamp, - message: message, - isError: isError - }; - - this.setData({ - testResults: [...this.data.testResults, resultItem] - }); - - console.log(`[${timestamp}] ${message}`); - }, - - // 切换测试模式 - switchTestMode: function() { - const newMode = this.data.testMode === 'customer' ? 'customer_service' : 'customer'; - this.setData({ testMode: newMode }); - wx.showToast({ title: `已切换到${newMode === 'customer' ? '客户' : '客服'}模式` }); - }, - - // 输入测试消息 - onInputChange: function(e) { - this.setData({ testMessage: e.detail.value }); - }, - - // 清理WebSocket连接 - cleanup: function() { - try { - socketManager.close(); - this.addTestResult('WebSocket连接已关闭'); - } catch (error) { - this.addTestResult(`关闭连接失败: ${error.message}`, true); - } - }, - - // 手动发送消息 - sendTestMessage: function() { - if (!this.data.testMessage.trim()) { - wx.showToast({ title: '请输入测试消息', icon: 'none' }); - return; - } - - const app = getApp(); - const userType = app.globalData.userType || this.data.testMode; - const targetId = userType === 'customer_service' ? - `test_customer_${Date.now()}` : - this.data.mockServiceId; - - const message = { - type: 'chat_message', - direction: userType === 'customer_service' ? 'service_to_customer' : 'customer_to_service', - data: { - receiverId: targetId, - senderId: wx.getStorageSync('userId') || `test_${Date.now()}`, - senderType: userType, - content: this.data.testMessage.trim(), - contentType: 1, - timestamp: Date.now() - } - }; - - const sent = socketManager.send(message); - if (sent) { - wx.showToast({ title: '消息已发送' }); - this.addTestResult(`手动发送消息: ${this.data.testMessage}`); - } else { - wx.showToast({ title: '发送失败', icon: 'none' }); - this.addTestResult(`手动发送失败`, true); - } - }, - - onUnload: function() { - // 清理监听器和连接 - this.cleanup(); - } -}); diff --git a/pages/test-service/test-service.json b/pages/test-service/test-service.json deleted file mode 100644 index 8835af0..0000000 --- a/pages/test-service/test-service.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "usingComponents": {} -} \ No newline at end of file diff --git a/pages/test-service/test-service.wxml b/pages/test-service/test-service.wxml deleted file mode 100644 index 3883c8d..0000000 --- a/pages/test-service/test-service.wxml +++ /dev/null @@ -1,97 +0,0 @@ - - - 客服功能综合测试 - 验证客服认证、身份判断和双向沟通功能 - - - - - 当前测试: - {{currentTest || '未开始'}} - - - 用户类型: - {{userType}} - - - WebSocket: - {{wsConnected ? '已连接' : '未连接'}} - - - 认证状态: - {{wsAuthenticated ? '已认证' : '未认证'}} - - - - - - 测试模式: - - - - - - - - 测试消息: - - - - - - - - - - - - 测试结果: - - - {{item.time}} - {{item.message}} - - - 暂无测试结果,点击开始综合测试 - - - - - - 测试提示: - - 1. 测试前请确保已完成登录 - 2. WebSocket服务需要正常运行 - 3. 测试将验证用户类型检测、WebSocket连接、认证和消息发送功能 - 4. 双向通信测试依赖于服务器配置,可能不会收到响应消息 - - - diff --git a/pages/test-service/test-service.wxss b/pages/test-service/test-service.wxss deleted file mode 100644 index 7695bf2..0000000 --- a/pages/test-service/test-service.wxss +++ /dev/null @@ -1,249 +0,0 @@ -.test-container { - padding: 20rpx; - background-color: #f8f8f8; - min-height: 100vh; -} - -.header { - text-align: center; - margin-bottom: 30rpx; - padding: 20rpx 0; - background-color: #fff; - border-radius: 12rpx; - box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1); -} - -.title { - font-size: 36rpx; - font-weight: bold; - color: #333; - display: block; - margin-bottom: 10rpx; -} - -.subtitle { - font-size: 24rpx; - color: #666; - display: block; -} - -.test-status { - background-color: #fff; - border-radius: 12rpx; - padding: 20rpx; - margin-bottom: 30rpx; - box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1); -} - -.status-item { - display: flex; - justify-content: space-between; - padding: 15rpx 0; - border-bottom: 1rpx solid #f0f0f0; -} - -.status-item:last-child { - border-bottom: none; -} - -.status-label { - font-size: 28rpx; - color: #666; -} - -.status-value { - font-size: 28rpx; - color: #333; - font-weight: 500; -} - -.status-item.testing .status-value { - color: #07c160; - animation: pulse 1s infinite; -} - -.status-item.connected .status-value { - color: #07c160; -} - -.status-item.authenticated .status-value { - color: #1989fa; -} - -@keyframes pulse { - 0% { - opacity: 1; - } - 50% { - opacity: 0.6; - } - 100% { - opacity: 1; - } -} - -.test-controls { - background-color: #fff; - border-radius: 12rpx; - padding: 20rpx; - margin-bottom: 30rpx; - box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1); -} - -.control-item { - margin-bottom: 20rpx; -} - -.control-item:last-child { - margin-bottom: 0; -} - -.control-label { - font-size: 28rpx; - color: #333; - display: block; - margin-bottom: 15rpx; - font-weight: 500; -} - -.control-buttons { - display: flex; - justify-content: space-between; - gap: 20rpx; -} - -.control-buttons button { - flex: 1; - font-size: 26rpx; -} - -.message-input { - border: 1rpx solid #ddd; - border-radius: 8rpx; - padding: 20rpx; - font-size: 28rpx; - margin-bottom: 15rpx; - background-color: #f9f9f9; -} - -.action-buttons { - display: flex; - justify-content: space-between; - margin-top: 20rpx; -} - -.action-buttons button { - flex: 1; - margin: 0 10rpx; -} - -.start-button { - background-color: #07c160; -} - -.cleanup-button { - background-color: #ee0a24; -} - -.test-results { - background-color: #fff; - border-radius: 12rpx; - padding: 20rpx; - margin-bottom: 30rpx; - box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1); -} - -.results-title { - font-size: 28rpx; - color: #333; - font-weight: 500; - display: block; - margin-bottom: 15rpx; -} - -.results-list { - height: 400rpx; - background-color: #f9f9f9; - border-radius: 8rpx; - padding: 10rpx; -} - -.result-item { - padding: 15rpx; - margin-bottom: 10rpx; - background-color: #fff; - border-radius: 6rpx; - border-left: 4rpx solid #1989fa; - font-size: 24rpx; -} - -.result-item.error { - border-left-color: #ee0a24; - background-color: #fff1f0; -} - -.result-time { - color: #999; - font-size: 20rpx; - display: block; - margin-bottom: 5rpx; -} - -.result-message { - color: #333; - word-break: break-all; - line-height: 1.5; -} - -.empty-result { - text-align: center; - color: #999; - padding: 60rpx 0; - font-size: 26rpx; -} - -.test-tips { - background-color: #fff; - border-radius: 12rpx; - padding: 20rpx; - box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1); -} - -.tips-title { - font-size: 28rpx; - color: #333; - font-weight: 500; - display: block; - margin-bottom: 15rpx; -} - -.tips-content { - background-color: #f0f9ff; - border-radius: 8rpx; - padding: 20rpx; -} - -.tips-content text { - display: block; - margin-bottom: 10rpx; - font-size: 24rpx; - color: #666; - line-height: 1.5; -} - -.tips-content text:last-child { - margin-bottom: 0; -} - -/* 适配不同屏幕尺寸 */ -@media screen and (min-width: 768px) { - .test-container { - max-width: 900rpx; - margin: 0 auto; - padding: 30rpx; - } - - .results-list { - height: 600rpx; - } -} diff --git a/server-example/check-chat-online-status-detailed.js b/server-example/check-chat-online-status-detailed.js deleted file mode 100644 index f247fed..0000000 --- a/server-example/check-chat-online-status-detailed.js +++ /dev/null @@ -1,108 +0,0 @@ -// 详细检查chat_online_status表的数据 -const { Sequelize } = require('sequelize'); - -console.log('=== 详细检查chat_online_status表数据 ===\n'); - -// 创建Sequelize实例(使用与项目相同的配置) -const sequelize = new Sequelize('wechat_app', 'root', '', { - host: '1.95.162.61', - port: 3306, - dialect: 'mysql', - logging: false -}); - -async function checkChatOnlineStatus() { - try { - // 测试数据库连接 - console.log('正在连接数据库...'); - await sequelize.authenticate(); - console.log('✅ 数据库连接成功\n'); - - // 1. 查询chat_online_status表中的所有客服记录 - console.log('1. 查询所有客服在线状态记录:'); - const [allStatuses] = await sequelize.query( - 'SELECT * FROM chat_online_status WHERE type = 2 ORDER BY updated_at DESC', - { type: Sequelize.QueryTypes.SELECT } - ); - - console.log(`找到 ${allStatuses.length} 条客服在线状态记录\n`); - - // 打印详细信息 - allStatuses.forEach((status, index) => { - console.log(`记录 ${index + 1}:`); - console.log(` userId: ${status.userId}`); - console.log(` type: ${status.type === 2 ? '客服' : status.type}`); - console.log(` socket_id: ${status.socket_id}`); - console.log(` is_online: ${status.is_online === 1 ? '✅ 在线' : '❌ 离线'}`); - console.log(` last_heartbeat: ${status.last_heartbeat}`); - console.log(` updated_at: ${status.updated_at}`); - console.log(` created_at: ${status.created_at}`); - console.log(` device_info: ${status.device_info}`); - console.log('---'); - }); - - // 2. 特别查询managerId=22的客服记录(我们测试的客服) - console.log('\n2. 特别查询测试客服(managerId=22)的状态:'); - const [targetManagerStatus] = await sequelize.query( - 'SELECT * FROM chat_online_status WHERE userId = ? AND type = 2', - { - replacements: ['22'], - type: Sequelize.QueryTypes.SELECT - } - ); - - if (targetManagerStatus) { - console.log(`✅ 找到测试客服记录:`); - console.log(` 在线状态: ${targetManagerStatus.is_online === 1 ? '✅ 在线' : '❌ 离线'}`); - console.log(` 最后心跳: ${targetManagerStatus.last_heartbeat}`); - console.log(` 最后更新: ${targetManagerStatus.updated_at}`); - } else { - console.log(`❌ 未找到managerId=22的客服记录`); - } - - // 3. 查询personnel表中managerId与id的映射关系 - console.log('\n3. 查询personnel表中的managerId与id关系:'); - const [personnelData] = await sequelize.query( - 'SELECT id, managerId, name, phoneNumber FROM userlogin.personnel WHERE projectName = ? AND phoneNumber IS NOT NULL LIMIT 10', - { - replacements: ['采购员'], - type: Sequelize.QueryTypes.SELECT - } - ); - - console.log(`找到 ${personnelData.length} 条采购员记录,查看id与managerId的关系:`); - personnelData.forEach(person => { - console.log(` ${person.name}: id=${person.id}, managerId=${person.managerId || 'null'}`); - }); - - // 4. 检查我们测试使用的managerId=22在personnel表中的对应关系 - console.log('\n4. 检查测试客服(managerId=22)在personnel表中的记录:'); - const [targetPersonnel] = await sequelize.query( - 'SELECT id, managerId, name, phoneNumber FROM userlogin.personnel WHERE managerId = ? OR id = ?', - { - replacements: ['22', '22'], - type: Sequelize.QueryTypes.SELECT - } - ); - - if (targetPersonnel) { - console.log(`✅ 找到记录:`); - console.log(` 姓名: ${targetPersonnel.name}`); - console.log(` id: ${targetPersonnel.id}`); - console.log(` managerId: ${targetPersonnel.managerId || 'null'}`); - console.log(` 手机号: ${targetPersonnel.phoneNumber}`); - } else { - console.log(`❌ 未找到managerId=22或id=22的记录`); - } - - } catch (error) { - console.error('❌ 查询数据库时出错:', error); - } finally { - // 关闭连接 - await sequelize.close(); - console.log('\n✅ 数据库连接已关闭'); - } -} - -// 执行查询 -checkChatOnlineStatus(); diff --git a/server-example/check_chat_data.js b/server-example/check_chat_data.js deleted file mode 100644 index 774ae76..0000000 --- a/server-example/check_chat_data.js +++ /dev/null @@ -1,51 +0,0 @@ -const { Sequelize } = require('sequelize'); - -// 使用与项目相同的数据库配置 -const sequelize = new Sequelize('wechat_app', 'root', 'schl@2025', { - host: '1.95.162.61', - port: 3306, - dialect: 'mysql', - dialectOptions: { - connectTimeout: 30000, - }, -}); - -async function checkChatData() { - try { - console.log('连接数据库...'); - await sequelize.authenticate(); - console.log('数据库连接成功!'); - - console.log('\n=== 最近的聊天会话 ==='); - const [conversations] = await sequelize.query( - 'SELECT * FROM chat_conversations ORDER BY created_at DESC LIMIT 5' - ); - console.log(conversations); - - console.log('\n=== 最近的聊天消息 ==='); - const [messages] = await sequelize.query( - 'SELECT * FROM chat_messages ORDER BY created_at DESC LIMIT 5' - ); - console.log(messages); - - // 检查是否有使用测试ID的记录 - console.log('\n=== 检查测试ID记录 ==='); - const [testRecords] = await sequelize.query( - "SELECT * FROM chat_conversations WHERE userId LIKE '%test_%' OR managerId LIKE '%test_%'" - ); - console.log('测试ID记录数量:', testRecords.length); - if (testRecords.length > 0) { - console.log('发现测试ID记录:', testRecords); - } else { - console.log('未发现测试ID记录'); - } - - } catch (error) { - console.error('查询失败:', error); - } finally { - await sequelize.close(); - console.log('\n数据库连接已关闭'); - } -} - -checkChatData(); diff --git a/server-example/check_messages_in_db.js b/server-example/check_messages_in_db.js deleted file mode 100644 index 668b036..0000000 --- a/server-example/check_messages_in_db.js +++ /dev/null @@ -1,220 +0,0 @@ -// 检查数据库中消息保存情况的脚本 -const { Sequelize, DataTypes } = require('sequelize'); -require('dotenv').config(); - -// 数据库配置 - 使用.env文件中的配置 -const sequelize = new Sequelize(process.env.DB_DATABASE || 'wechat_app', process.env.DB_USER || 'root', process.env.DB_PASSWORD || '', { - host: process.env.DB_HOST || 'localhost', - dialect: 'mysql', - port: process.env.DB_PORT || 3306, - logging: console.log, - dialectOptions: { - multipleStatements: true - } -}); - -// 定义消息模型 -const ChatMessage = sequelize.define('ChatMessage', { - message_id: { - type: DataTypes.STRING, - primaryKey: true - }, - conversation_id: { - type: DataTypes.STRING, - allowNull: false - }, - sender_type: { - type: DataTypes.INTEGER, - allowNull: false - }, - sender_id: { - type: DataTypes.STRING, - allowNull: false - }, - receiver_id: { - type: DataTypes.STRING, - allowNull: false - }, - content_type: { - type: DataTypes.INTEGER, - allowNull: false - }, - content: { - type: DataTypes.TEXT, - allowNull: false - }, - file_url: { - type: DataTypes.STRING - }, - file_size: { - type: DataTypes.INTEGER - }, - duration: { - type: DataTypes.INTEGER - }, - is_read: { - type: DataTypes.INTEGER, - defaultValue: 0 - }, - status: { - type: DataTypes.INTEGER, - defaultValue: 1 - }, - created_at: { - type: DataTypes.DATE, - defaultValue: Sequelize.NOW - }, - updated_at: { - type: DataTypes.DATE, - defaultValue: Sequelize.NOW - } -}, { - tableName: 'chat_messages', - timestamps: false -}); - -// 定义会话模型 -const ChatConversation = sequelize.define('ChatConversation', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - conversation_id: { - type: DataTypes.STRING, - allowNull: false, - unique: true - }, - userId: { - type: DataTypes.STRING, - allowNull: false - }, - managerId: { - type: DataTypes.STRING, - allowNull: false - }, - last_message: { - type: DataTypes.TEXT - }, - last_message_time: { - type: DataTypes.DATE - }, - unread_count: { - type: DataTypes.INTEGER, - defaultValue: 0 - }, - cs_unread_count: { - type: DataTypes.INTEGER, - defaultValue: 0 - }, - status: { - type: DataTypes.INTEGER, - defaultValue: 1 - }, - user_online: { - type: DataTypes.INTEGER, - defaultValue: 0 - }, - cs_online: { - type: DataTypes.INTEGER, - defaultValue: 0 - }, - created_at: { - type: DataTypes.DATE - }, - updated_at: { - type: DataTypes.DATE - } -}, { - tableName: 'chat_conversations', - timestamps: false -}); - -// 查询消息函数 -async function checkMessages() { - try { - console.log('=== 开始检查数据库中的消息记录 ==='); - - // 1. 先查询特定会话ID的消息(从测试脚本中获取的会话ID) - const targetConversationId = '963f9eed-950c-47e9-ade6-97e7e90915dc'; // 从测试结果中获取的正式会话ID - console.log(`\n🔍 查询会话ID: ${targetConversationId} 的消息记录`); - - const messages = await ChatMessage.findAll({ - where: { - conversation_id: targetConversationId - }, - order: [['created_at', 'ASC']] - }); - - if (messages.length === 0) { - console.log('❌ 未找到该会话的消息记录'); - } else { - console.log(`✅ 找到 ${messages.length} 条消息记录:`); - messages.forEach((msg, index) => { - const senderType = msg.sender_type === 1 ? '客户' : '客服'; - console.log(`\n--- 消息 ${index + 1} ---`); - console.log(`发送方类型: ${senderType}`); - console.log(`发送方ID: ${msg.sender_id}`); - console.log(`接收方ID: ${msg.receiver_id}`); - console.log(`内容类型: ${msg.content_type}`); - console.log(`消息内容: ${msg.content}`); - console.log(`是否已读: ${msg.is_read ? '是' : '否'}`); - console.log(`状态: ${msg.status}`); - console.log(`创建时间: ${msg.created_at}`); - }); - } - - // 2. 查询会话信息 - console.log(`\n🔍 查询会话信息: ${targetConversationId}`); - const conversation = await ChatConversation.findOne({ - where: { - conversation_id: targetConversationId - } - }); - - if (conversation) { - console.log('✅ 会话信息:'); - console.log(`会话ID: ${conversation.conversation_id}`); - console.log(`用户ID: ${conversation.userId}`); - console.log(`客服ID: ${conversation.managerId}`); - console.log(`最后一条消息: ${conversation.last_message}`); - console.log(`最后消息时间: ${conversation.last_message_time}`); - console.log(`用户未读数: ${conversation.unread_count}`); - console.log(`客服未读数: ${conversation.cs_unread_count}`); - console.log(`会话状态: ${conversation.status}`); - } else { - console.log('❌ 未找到该会话信息'); - } - - // 3. 查询最近的几条消息,确认系统整体工作正常 - console.log('\n🔍 查询最近的5条消息:'); - const recentMessages = await ChatMessage.findAll({ - limit: 5, - order: [['created_at', 'DESC']] - }); - - if (recentMessages.length > 0) { - console.log(`✅ 找到 ${recentMessages.length} 条最近消息`); - recentMessages.forEach((msg, index) => { - const senderType = msg.sender_type === 1 ? '客户' : '客服'; - console.log(`\n--- 最近消息 ${index + 1} ---`); - console.log(`会话ID: ${msg.conversation_id}`); - console.log(`发送方: ${senderType} (${msg.sender_id})`); - console.log(`内容: ${msg.content.substring(0, 50)}${msg.content.length > 50 ? '...' : ''}`); - console.log(`时间: ${msg.created_at}`); - }); - } - - console.log('\n=== 数据库检查完成 ==='); - console.log('✅ 结论: 消息已成功保存到数据库,可在消息中心查看完整对话'); - - } catch (error) { - console.error('❌ 数据库查询出错:', error.message); - } finally { - // 关闭数据库连接 - await sequelize.close(); - } -} - -// 运行查询 -checkMessages(); diff --git a/server-example/check_specific_conversation.js b/server-example/check_specific_conversation.js deleted file mode 100644 index b1254bc..0000000 --- a/server-example/check_specific_conversation.js +++ /dev/null @@ -1,81 +0,0 @@ -// 检查特定会话和消息的脚本 -const { Sequelize } = require('sequelize'); -require('dotenv').config(); - -// 数据库配置 -const sequelize = new Sequelize(process.env.DB_DATABASE || 'wechat_app', process.env.DB_USER || 'root', process.env.DB_PASSWORD || '', { - host: process.env.DB_HOST || 'localhost', - dialect: 'mysql', - port: process.env.DB_PORT || 3306, - logging: console.log, - dialectOptions: { - multipleStatements: true - } -}); - -// 查询特定会话和相关消息 -async function checkSpecificConversation() { - try { - console.log('=== 开始检查特定会话和消息 ==='); - - // 使用我们刚才创建的会话ID - const targetConversationId = '6a64f35e-2219-41d5-b14e-d6e029529c11'; - console.log(`🔍 查询会话ID: ${targetConversationId}`); - - // 1. 检查会话是否存在 - const [conversations] = await sequelize.query( - 'SELECT * FROM chat_conversations WHERE conversation_id = ?', - { replacements: [targetConversationId] } - ); - - if (conversations && conversations.length > 0) { - console.log('✅ 会话存在:'); - console.log(JSON.stringify(conversations[0], null, 2)); - } else { - console.log('❌ 会话不存在'); - } - - // 2. 检查是否有相关消息 - const [messages] = await sequelize.query( - 'SELECT * FROM chat_messages WHERE conversation_id = ? ORDER BY created_at ASC', - { replacements: [targetConversationId] } - ); - - if (messages && messages.length > 0) { - console.log(`\n✅ 找到 ${messages.length} 条消息:`); - messages.forEach((msg, index) => { - console.log(`\n--- 消息 ${index + 1} ---`); - console.log(`发送方类型: ${msg.sender_type === 1 ? '客户' : '客服'}`); - console.log(`发送方ID: ${msg.sender_id}`); - console.log(`内容: ${msg.content}`); - console.log(`创建时间: ${msg.created_at}`); - }); - } else { - console.log('\n❌ 未找到该会话的消息记录'); - } - - // 3. 检查最近的所有消息,不限于特定会话 - const [allRecentMessages] = await sequelize.query( - 'SELECT * FROM chat_messages ORDER BY created_at DESC LIMIT 10' - ); - - if (allRecentMessages && allRecentMessages.length > 0) { - console.log('\n🔍 最近10条消息:'); - allRecentMessages.forEach(msg => { - console.log(`${msg.created_at} | 会话ID: ${msg.conversation_id} | 发送方: ${msg.sender_id} | 内容: ${msg.content.substring(0, 30)}...`); - }); - } else { - console.log('\n❌ 数据库中没有任何消息记录'); - } - - console.log('\n=== 检查完成 ==='); - - } catch (error) { - console.error('❌ 查询出错:', error.message); - } finally { - await sequelize.close(); - } -} - -// 运行查询 -checkSpecificConversation(); diff --git a/server-example/cleanup_invalid_chat_data.js b/server-example/cleanup_invalid_chat_data.js deleted file mode 100644 index e41b76a..0000000 --- a/server-example/cleanup_invalid_chat_data.js +++ /dev/null @@ -1,76 +0,0 @@ -const mysql = require('mysql2/promise'); - -async function cleanupInvalidChatData() { - let connection; - - try { - // 创建数据库连接 - connection = await mysql.createConnection({ - host: '1.95.162.61', - port: 3306, - user: 'root', - password: 'schl@2025', - database: 'wechat_app' - }); - - console.log('数据库连接成功'); - - // 统计并删除chat_conversations表中userId为'0'或无效的记录 - const [convCount] = await connection.execute( - 'SELECT COUNT(*) as count FROM chat_conversations WHERE userId = ? OR userId IS NULL OR userId = ?', - ['0', ''] - ); - console.log(`chat_conversations表中待清理记录数: ${convCount[0].count}`); - - if (convCount[0].count > 0) { - const [convResult] = await connection.execute( - 'DELETE FROM chat_conversations WHERE userId = ? OR userId IS NULL OR userId = ?', - ['0', ''] - ); - console.log(`成功删除chat_conversations表中的 ${convResult.affectedRows} 条记录`); - } - - // 统计并删除chat_online_status表中userId为'0'或无效的记录 - const [statusCount] = await connection.execute( - 'SELECT COUNT(*) as count FROM chat_online_status WHERE userId = ? OR userId IS NULL OR userId = ?', - ['0', ''] - ); - console.log(`chat_online_status表中待清理记录数: ${statusCount[0].count}`); - - if (statusCount[0].count > 0) { - const [statusResult] = await connection.execute( - 'DELETE FROM chat_online_status WHERE userId = ? OR userId IS NULL OR userId = ?', - ['0', ''] - ); - console.log(`成功删除chat_online_status表中的 ${statusResult.affectedRows} 条记录`); - } - - // 统计并删除chat_messages表中sender_id或receiver_id为'0'或无效的记录 - const [msgCount] = await connection.execute( - 'SELECT COUNT(*) as count FROM chat_messages WHERE sender_id = ? OR sender_id IS NULL OR sender_id = ? OR receiver_id = ? OR receiver_id IS NULL OR receiver_id = ?', - ['0', '', '0', ''] - ); - console.log(`chat_messages表中待清理记录数: ${msgCount[0].count}`); - - if (msgCount[0].count > 0) { - const [msgResult] = await connection.execute( - 'DELETE FROM chat_messages WHERE sender_id = ? OR sender_id IS NULL OR sender_id = ? OR receiver_id = ? OR receiver_id IS NULL OR receiver_id = ?', - ['0', '', '0', ''] - ); - console.log(`成功删除chat_messages表中的 ${msgResult.affectedRows} 条记录`); - } - - console.log('\n清理完成!数据库中的无效聊天数据已被删除。'); - - } catch (error) { - console.error('清理过程中发生错误:', error); - } finally { - if (connection) { - await connection.end(); - console.log('数据库连接已关闭'); - } - } -} - -// 执行清理操作 -cleanupInvalidChatData(); \ No newline at end of file diff --git a/server-example/comprehensive-manager-status-test.js b/server-example/comprehensive-manager-status-test.js deleted file mode 100644 index 25d1f5f..0000000 --- a/server-example/comprehensive-manager-status-test.js +++ /dev/null @@ -1,323 +0,0 @@ -// 全面测试客服在线状态问题 -console.log('=== 全面测试客服在线状态问题 ===\n'); - -const WebSocket = require('ws'); -const http = require('http'); - -// 配置参数 -const TEST_MANAGER_ID = '22'; // 我们测试的客服ID -const API_BASE_URL = 'http://localhost:3003'; -const API_ENDPOINT = '/api/managers'; -const WS_URL = 'ws://localhost:3003'; - -// 全局变量存储连接期间的API检查结果 -let connectionManagerStatus = null; -let duringManager = null; // 用于存储连接期间的API检查结果,供测试分析使用 - -// 步骤1: 先检查API返回的客服列表,看看我们的测试客服是否存在 -async function checkApiResponse() { - console.log('🔍 步骤1: 检查API返回的客服列表'); - - return new Promise((resolve, reject) => { - http.get(`${API_BASE_URL}${API_ENDPOINT}`, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - try { - const result = JSON.parse(data); - console.log(`✅ API请求成功,状态码: ${res.statusCode}`); - console.log(`📋 客服列表长度: ${result.data ? result.data.length : 0}`); - - // 查找我们测试的客服 - if (result.data && result.data.length > 0) { - console.log('\n📝 客服列表详情:'); - result.data.forEach((manager, index) => { - console.log(` ${index + 1}. ${manager.name} (id=${manager.id}, managerId=${manager.managerId}, online=${manager.online ? '✅ 在线' : '❌ 离线'})`); - }); - - // 特别检查我们的测试客服 - const testManager = result.data.find(m => - String(m.id) === TEST_MANAGER_ID || String(m.managerId) === TEST_MANAGER_ID - ); - - if (testManager) { - console.log(`\n🔍 找到测试客服记录:`); - console.log(` 姓名: ${testManager.name}`); - console.log(` id: ${testManager.id}`); - console.log(` managerId: ${testManager.managerId}`); - console.log(` 当前在线状态: ${testManager.online ? '✅ 在线' : '❌ 离线'}`); - resolve(testManager); - } else { - console.log(`\n❌ 未找到id或managerId为${TEST_MANAGER_ID}的客服记录`); - resolve(null); - } - } else { - console.log('❌ API返回的数据为空或格式错误'); - resolve(null); - } - } catch (error) { - console.error('❌ 解析API响应失败:', error.message); - reject(error); - } - }); - }).on('error', (error) => { - console.error(`❌ API请求失败: ${error.message}`); - reject(error); - }); - }); -} - -// 步骤2: 建立WebSocket连接并进行客服认证 -async function testWebSocketConnection() { - console.log('\n🔍 步骤2: 建立WebSocket连接并进行客服认证'); - - return new Promise((resolve, reject) => { - const ws = new WebSocket(WS_URL); - let isAuthenticated = false; - let connectionTimer; - let apiCheckTimer; - - ws.on('open', () => { - console.log('✅ WebSocket连接已建立'); - - // 发送认证消息 - const authMessage = { - type: 'auth', - managerId: TEST_MANAGER_ID, - userType: 'manager' // 明确指定用户类型为客服 - }; - - console.log(`📤 发送认证消息:`, authMessage); - ws.send(JSON.stringify(authMessage)); - - // 设置超时 - connectionTimer = setTimeout(() => { - if (!isAuthenticated) { - console.error('❌ 认证超时'); - ws.close(); - reject(new Error('认证超时')); - } - }, 5000); - }); - - ws.on('message', (message) => { - try { - const data = JSON.parse(message.toString()); - console.log('📥 收到服务器消息:', data); - - if (data.type === 'auth_success') { - console.log('✅ 认证成功!'); - isAuthenticated = true; - clearTimeout(connectionTimer); - - // 发送心跳消息 - setTimeout(() => { - console.log('📤 发送心跳消息'); - ws.send(JSON.stringify({ type: 'ping' })); - - // 保持连接5秒,给服务器足够时间更新状态 - console.log('⏱️ 保持连接5秒,等待状态完全更新...'); - - // 在连接期间执行API检查(优化:在连接中直接进行检查,确保并发控制) - apiCheckTimer = setTimeout(async () => { - console.log('\n🔍 连接期间执行API检查...'); - duringManager = await checkApiDuringConnection(); // 将结果保存到全局变量 - console.log('✅ 连接期间API检查结果已保存,准备后续分析'); - }, 2000); // 认证后2秒进行API检查 - - setTimeout(() => { - console.log('\n🔄 准备关闭连接'); - ws.close(); - }, 5000); - }, 1000); - } else if (data.type === 'pong') { - console.log('✅ 收到心跳响应'); - } - } catch (error) { - console.error('❌ 解析WebSocket消息失败:', error.message); - } - }); - - ws.on('close', (code, reason) => { - console.log(`🔌 WebSocket连接已关闭,代码: ${code}, 原因: ${reason || '未知'}`); - clearTimeout(apiCheckTimer); - resolve(isAuthenticated); - }); - - ws.on('error', (error) => { - console.error('❌ WebSocket错误:', error.message); - clearTimeout(connectionTimer); - clearTimeout(apiCheckTimer); - reject(error); - }); - }); -} - -// 步骤3: 检查连接期间的API响应(在WebSocket连接建立后但未关闭前) -async function checkApiDuringConnection() { - console.log('🔍 检查WebSocket连接期间的API响应(实时检查)'); - // 不再需要额外等待,因为在WebSocket连接中已经控制了时机 - - return new Promise((resolve, reject) => { - http.get(`${API_BASE_URL}${API_ENDPOINT}`, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - try { - const result = JSON.parse(data); - - if (result.data && result.data.length > 0) { - // 查找我们测试的客服 - const testManager = result.data.find(m => - String(m.id) === TEST_MANAGER_ID || String(m.managerId) === TEST_MANAGER_ID - ); - - if (testManager) { - console.log(`\n🔍 连接期间客服状态:`); - console.log(` 姓名: ${testManager.name}`); - console.log(` 当前在线状态: ${testManager.online ? '✅ 在线' : '❌ 离线'}`); - resolve(testManager); - } else { - console.log(`❌ 未找到测试客服记录`); - resolve(null); - } - } else { - resolve(null); - } - } catch (error) { - console.error('❌ 解析API响应失败:', error.message); - resolve(null); - } - }); - }).on('error', (error) => { - console.error(`❌ API请求失败: ${error.message}`); - resolve(null); - }); - }); -} - -// 步骤4: 检查关闭连接后的API响应 -async function checkApiAfterClose() { - console.log('\n🔍 步骤4: 检查关闭连接后的API响应'); - await new Promise(resolve => setTimeout(resolve, 500)); // 等待状态更新 - - return new Promise((resolve, reject) => { - http.get(`${API_BASE_URL}${API_ENDPOINT}`, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - try { - const result = JSON.parse(data); - - if (result.data && result.data.length > 0) { - // 查找我们测试的客服 - const testManager = result.data.find(m => - String(m.id) === TEST_MANAGER_ID || String(m.managerId) === TEST_MANAGER_ID - ); - - if (testManager) { - console.log(`\n🔍 关闭连接后客服状态:`); - console.log(` 姓名: ${testManager.name}`); - console.log(` 当前在线状态: ${testManager.online ? '✅ 在线' : '❌ 离线'}`); - resolve(testManager); - } else { - console.log(`❌ 未找到测试客服记录`); - resolve(null); - } - } else { - resolve(null); - } - } catch (error) { - console.error('❌ 解析API响应失败:', error.message); - resolve(null); - } - }); - }).on('error', (error) => { - console.error(`❌ API请求失败: ${error.message}`); - resolve(null); - }); - }); -} - -// 运行测试 -async function runTest() { - try { - console.log('=== 全面测试客服在线状态问题 ===\n'); - console.log(`🎯 测试目标: managerId = ${TEST_MANAGER_ID}\n`); - - // 步骤1: 检查初始状态 - const beforeManager = await checkApiResponse(); - - if (!beforeManager) { - console.log('\n⚠️ 警告: 未找到测试客服记录,这可能是状态显示问题的原因'); - return; - } - - // 步骤2: 测试WebSocket认证 - const authSuccess = await testWebSocketConnection(); - - if (!authSuccess) { - console.log('\n❌ 认证失败,无法测试在线状态更新'); - return; - } - - // 步骤3: 检查连接期间的状态(已在WebSocket连接中直接执行,这里不再单独调用) - console.log('\n📋 注意: 连接期间的API检查已在WebSocket连接中直接执行,以确保最佳并发控制'); - // duringManager变量将在WebSocket连接中的API检查后通过全局变量更新 - - // 步骤4: 检查关闭连接后的状态 - const afterManager = await checkApiAfterClose(); - - // 分析结果 - console.log('\n=== 测试结果分析 ==='); - console.log(`初始状态: ${beforeManager?.online ? '✅ 在线' : '❌ 离线'}`); - - // 确保duringManager正确被更新 - if (!duringManager) { - console.log('⚠️ 警告: 连接期间状态未正确更新,可能是时序问题'); - } - - console.log(`连接期间状态: ${duringManager?.online ? '✅ 在线' : '❌ 离线'}`); - console.log(`关闭后状态: ${afterManager?.online ? '✅ 在线' : '❌ 离线'}`); - - // 放宽条件:只要在连接期间显示在线,就认为测试成功 - if (duringManager?.online) { - console.log('\n✅ 成功! 客服在连接期间正确显示为在线状态。'); - - if (!beforeManager?.online && !afterManager?.online) { - console.log('🌟 完整流程验证成功: 初始离线 -> 连接期间在线 -> 关闭后离线'); - } else { - console.log('⚠️ 注意: 初始或关闭后的状态可能有延迟,但连接期间状态正确。'); - } - - console.log('\n🎉 测试通过! 客服在线状态系统工作正常。'); - } else { - console.log('\n❌ 失败! 即使WebSocket认证成功,客服在连接期间仍显示离线。'); - - console.log('\n📋 可能的问题原因:'); - console.log(' 1. 服务器中使用的managerId格式与API中使用的不匹配(string vs number)'); - console.log(' 2. onlineManagers Map的key与API查询时使用的key不一致'); - console.log(' 3. 数据库和内存状态不同步'); - console.log(' 4. 服务器认证逻辑有问题,没有正确将manager添加到onlineManagers'); - console.log('\n💡 检查建议: 查看服务器日志中onlineManagers的内容和键类型'); - } - - } catch (error) { - console.error('\n❌ 测试执行失败:', error.message); - } -} - -// 启动测试 -runTest(); diff --git a/server-example/comprehensive_chat_test.js b/server-example/comprehensive_chat_test.js deleted file mode 100644 index 1555749..0000000 --- a/server-example/comprehensive_chat_test.js +++ /dev/null @@ -1,245 +0,0 @@ -// 综合聊天功能验证脚本 -const { Sequelize } = require('sequelize'); -require('dotenv').config(); - -// 数据库连接配置 -const sequelize = new Sequelize('wechat_app', 'root', 'schl@2025', { - host: '1.95.162.61', - port: 3306, - dialect: 'mysql', - logging: false, - dialectOptions: { - connectTimeout: 10000, - } -}); - -// 测试用的ID - 使用字符串类型 -const testUserId = 'test_user_123'; -const testManagerId = 'test_manager_456'; -const testMessageId = `msg_${Date.now()}`; - -async function runComprehensiveTest() { - console.log('========== 开始综合聊天功能验证测试 =========='); - let testPassed = true; - - try { - // 1. 测试数据库连接 - console.log('测试1: 数据库连接...'); - await sequelize.authenticate(); - console.log('✅ 数据库连接成功'); - - // 2. 测试表结构验证 - console.log('\n测试2: 验证表结构...'); - const [chatConversationsSchema] = await sequelize.query( - "SHOW CREATE TABLE chat_conversations" - ); - const [chatMessagesSchema] = await sequelize.query( - "SHOW CREATE TABLE chat_messages" - ); - - console.log('✅ 表结构验证成功'); - console.log(` - chat_conversations.userId类型: VARCHAR`); - console.log(` - chat_conversations.managerId类型: VARCHAR`); - console.log(` - chat_messages.sender_id类型: VARCHAR`); - console.log(` - chat_messages.receiver_id类型: VARCHAR`); - - // 3. 测试会话创建功能 - console.log('\n测试3: 测试会话创建功能...'); - - // 先删除可能存在的测试数据 - await sequelize.query( - "DELETE FROM chat_messages WHERE conversation_id LIKE ?", - { replacements: [`test_conv_%`] } - ); - await sequelize.query( - "DELETE FROM chat_conversations WHERE userId = ? OR managerId = ?", - { replacements: [testUserId, testManagerId] } - ); - console.log(' - 清理旧测试数据完成'); - - // 创建新会话 - const conversationId = `test_conv_${Date.now()}`; - const now = new Date(); - - await sequelize.query( - `INSERT INTO chat_conversations - (conversation_id, userId, managerId, status, user_online, cs_online, created_at, updated_at) - VALUES (?, ?, ?, 1, 1, 1, ?, ?)`, - { - replacements: [conversationId, testUserId, testManagerId, now, now] - } - ); - - // 验证会话创建成功 - const [conversations] = await sequelize.query( - "SELECT * FROM chat_conversations WHERE conversation_id = ?", - { replacements: [conversationId] } - ); - - if (conversations && conversations.length > 0) { - const conv = conversations[0]; - console.log(`✅ 会话创建成功`); - console.log(` - conversation_id: ${conv.conversation_id}`); - console.log(` - userId: ${conv.userId} (类型: ${typeof conv.userId})`); - console.log(` - managerId: ${conv.managerId} (类型: ${typeof conv.managerId})`); - - // 验证ID类型 - if (conv.userId === testUserId && conv.managerId === testManagerId) { - console.log(' ✅ ID字符串类型存储正确'); - } else { - console.error('❌ ID字符串类型存储错误'); - testPassed = false; - } - } else { - console.error('❌ 会话创建失败'); - testPassed = false; - } - - // 4. 测试消息发送功能 - console.log('\n测试4: 测试消息发送功能...'); - - // 用户发送消息给客服 - const messageId1 = `${testMessageId}_1`; - await sequelize.query( - `INSERT INTO chat_messages - (message_id, conversation_id, sender_type, sender_id, receiver_id, - content_type, content, is_read, status, created_at, updated_at) - VALUES (?, ?, 1, ?, ?, 1, ?, 0, 1, ?, ?)`, - { - replacements: [messageId1, conversationId, testUserId, testManagerId, - '你好,这是测试消息内容', now, now] - } - ); - - // 客服回复消息 - const messageId2 = `${testMessageId}_2`; - await sequelize.query( - `INSERT INTO chat_messages - (message_id, conversation_id, sender_type, sender_id, receiver_id, - content_type, content, is_read, status, created_at, updated_at) - VALUES (?, ?, 2, ?, ?, 1, ?, 0, 1, ?, ?)`, - { - replacements: [messageId2, conversationId, testManagerId, testUserId, - '您好,这是客服回复', now, now] - } - ); - - // 验证消息存储成功 - const [messages] = await sequelize.query( - "SELECT * FROM chat_messages WHERE conversation_id = ? ORDER BY created_at", - { replacements: [conversationId] } - ); - - if (messages && messages.length === 2) { - console.log('✅ 消息发送成功,共发送2条消息'); - messages.forEach((msg, index) => { - console.log(` 消息${index + 1}:`); - console.log(` - sender_id: ${msg.sender_id} (类型: ${typeof msg.sender_id})`); - console.log(` - receiver_id: ${msg.receiver_id} (类型: ${typeof msg.receiver_id})`); - console.log(` - content: ${msg.content}`); - }); - - // 验证ID类型 - if (messages[0].sender_id === testUserId && messages[1].sender_id === testManagerId) { - console.log(' ✅ 消息ID字符串类型存储正确'); - } else { - console.error('❌ 消息ID字符串类型存储错误'); - testPassed = false; - } - } else { - console.error(`❌ 消息发送失败,实际发送: ${messages?.length || 0}条`); - testPassed = false; - } - - // 5. 测试查询功能 - console.log('\n测试5: 测试聊天功能查询...'); - - // 查询用户的会话列表 - const [userConversations] = await sequelize.query( - "SELECT * FROM chat_conversations WHERE userId = ?", - { replacements: [testUserId] } - ); - - console.log(`✅ 用户会话查询成功,找到${userConversations.length}个会话`); - - // 查询会话的所有消息 - const [allMessages] = await sequelize.query( - "SELECT * FROM chat_messages WHERE conversation_id = ?", - { replacements: [conversationId] } - ); - - console.log(`✅ 消息查询成功,找到${allMessages.length}条消息`); - - // 6. 测试边界情况 - 非数字字符串ID - console.log('\n测试6: 测试非数字字符串ID处理...'); - const specialUserId = 'special-user-id-with-hyphens_123'; - const specialManagerId = 'manager-id#456'; - const specialConvId = `special_conv_${Date.now()}`; - - // 创建使用特殊ID的会话 - await sequelize.query( - `INSERT INTO chat_conversations - (conversation_id, userId, managerId, status, created_at, updated_at) - VALUES (?, ?, ?, 1, ?, ?)`, - { - replacements: [specialConvId, specialUserId, specialManagerId, now, now] - } - ); - - // 发送消息 - const specialMsgId = `special_msg_${Date.now()}`; - await sequelize.query( - `INSERT INTO chat_messages - (message_id, conversation_id, sender_type, sender_id, receiver_id, - content_type, content, created_at, updated_at) - VALUES (?, ?, 1, ?, ?, 1, ?, ?, ?)`, - { - replacements: [specialMsgId, specialConvId, specialUserId, specialManagerId, - '测试特殊ID消息', now, now] - } - ); - - console.log('✅ 特殊字符ID测试通过'); - console.log(` - 特殊用户ID: ${specialUserId}`); - console.log(` - 特殊客服ID: ${specialManagerId}`); - - // 最终测试结果 - console.log('\n========== 测试总结 =========='); - if (testPassed) { - console.log('🎉 所有测试通过!聊天功能已成功修复,支持字符串类型的ID'); - } else { - console.error('❌ 部分测试失败,需要进一步检查'); - } - - } catch (error) { - console.error('\n❌ 测试过程中出现错误:', error.message); - console.error(error.stack); - testPassed = false; - } finally { - // 清理测试数据 - try { - console.log('\n清理测试数据...'); - await sequelize.query( - "DELETE FROM chat_messages WHERE conversation_id LIKE ?", - { replacements: [`test_conv_%`, `special_conv_%`] } - ); - await sequelize.query( - "DELETE FROM chat_conversations WHERE userId = ? OR managerId = ? OR userId = ? OR managerId = ?", - { replacements: [testUserId, testManagerId, 'special-user-id-with-hyphens_123', 'manager-id#456'] } - ); - console.log('✅ 测试数据清理完成'); - } catch (cleanupError) { - console.error('❌ 测试数据清理失败:', cleanupError.message); - } - - // 关闭数据库连接 - await sequelize.close(); - console.log('✅ 数据库连接已关闭'); - - // 根据测试结果设置退出码 - process.exit(testPassed ? 0 : 1); - } -} - -// 运行测试 -runComprehensiveTest(); diff --git a/server-example/customer-service-status-test.js b/server-example/customer-service-status-test.js deleted file mode 100644 index a09a868..0000000 --- a/server-example/customer-service-status-test.js +++ /dev/null @@ -1,75 +0,0 @@ -// 客服在线状态完整测试脚本 -const WebSocket = require('ws'); - -const serverUrl = 'ws://localhost:3003'; -const customerServiceId = '22'; - -console.log('=== 客服在线状态完整测试 ==='); -console.log(`连接服务器: ${serverUrl}`); -console.log(`客服ID: ${customerServiceId}`); -console.log('=== 开始测试 ==='); - -// 创建WebSocket连接 -const ws = new WebSocket(serverUrl); - -// 连接成功事件 -ws.on('open', () => { - console.log('✅ WebSocket连接已建立'); - - // 发送正确格式的认证消息 - const authMessage = { - type: 'auth', // 必须是'auth'才能被服务器识别为认证消息 - managerId: customerServiceId, - userType: 'manager' // 使用userType表示用户类型 - }; - - const messageString = JSON.stringify(authMessage); - console.log(`📤 发送认证消息: ${messageString}`); - - ws.send(messageString); -}); - -// 接收消息事件 -ws.on('message', (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('📥 收到服务器消息:', JSON.stringify(message, null, 2)); - - if (message.type === 'auth_success') { - console.log('🎉 认证成功!客服应该已在线'); - console.log(`📊 认证信息:`, message.payload); - console.log('✅ 测试成功: 客服已成功设置为在线状态'); - - // 延迟2秒后关闭连接 - setTimeout(() => { - console.log('🔄 关闭测试连接'); - ws.close(); - }, 2000); - } else if (message.type === 'auth_error') { - console.error('❌ 认证失败:', message.message); - } - } catch (error) { - console.error('❌ 解析消息失败:', error); - } -}); - -// 连接关闭事件 -ws.on('close', (code, reason) => { - console.log(`❌ WebSocket连接已关闭`); - console.log(` 关闭代码: ${code}`); - console.log(` 关闭原因: ${reason}`); - console.log('=== 测试结束 ==='); -}); - -// 连接错误事件 -ws.on('error', (error) => { - console.error('❌ WebSocket连接错误:', error.message); -}); - -// 设置超时 -setTimeout(() => { - if (ws.readyState === WebSocket.OPEN) { - console.error('⏰ 30秒超时,测试失败'); - ws.close(); - } -}, 30000); diff --git a/server-example/debug_chat_reply.js b/server-example/debug_chat_reply.js deleted file mode 100644 index 42a76c3..0000000 --- a/server-example/debug_chat_reply.js +++ /dev/null @@ -1,121 +0,0 @@ -// 简化的聊天功能调试脚本 -const WebSocket = require('ws'); - -// 配置信息 -const WS_URL = 'ws://localhost:3003'; -const TEST_MANAGER_ID = '22'; -const TEST_CUSTOMER_ID = 'test_customer_1'; - -// 创建客服WebSocket连接 -function createManagerConnection() { - return new Promise((resolve, reject) => { - const ws = new WebSocket(WS_URL); - - ws.on('open', () => { - console.log('客服WebSocket连接已建立'); - // 发送认证消息 - const authMsg = JSON.stringify({ - type: 'auth', - managerId: TEST_MANAGER_ID, - userType: 'manager' - }); - console.log('客服发送认证消息:', authMsg); - ws.send(authMsg); - }); - - ws.on('message', (data) => { - const message = JSON.parse(data.toString()); - console.log('客服收到消息:', message); - - if (message.type === 'auth_success') { - console.log('客服认证成功'); - resolve(ws); - } - }); - - ws.on('error', (error) => { - console.error('客服WebSocket错误:', error); - reject(error); - }); - - ws.on('close', () => { - console.log('客服WebSocket连接已关闭'); - }); - }); -} - -// 测试客服发送消息 -async function testManagerSendMessage(managerWs, conversationId) { - return new Promise((resolve) => { - const replyMessage = '这是客服的测试回复'; - const messageId = 'debug_reply_' + Date.now(); - - // 简化的消息格式,只包含最基本的字段 - const replyData = { - type: 'chat_message', - conversationId: conversationId, - messageId: messageId, - content: replyMessage, - contentType: 1, - senderType: 2, - senderId: TEST_MANAGER_ID, - receiverId: TEST_CUSTOMER_ID - }; - - console.log('客服准备发送消息:', replyData); - managerWs.send(JSON.stringify(replyData)); - - // 设置消息接收处理 - const messageHandler = (data) => { - const message = JSON.parse(data.toString()); - console.log('客服收到响应:', message); - - if (message.type === 'error') { - console.error('发送失败:', message.message); - } - - // 无论成功失败,5秒后解析 - setTimeout(() => { - managerWs.removeListener('message', messageHandler); - resolve(message); - }, 5000); - }; - - managerWs.on('message', messageHandler); - }); -} - -// 主测试函数 -async function runTest() { - try { - // 使用已知的会话ID (从之前的测试中获取) - const conversationId = '4fa4b92f-df20-40ae-94b9-f906753a4cfd'; - - console.log('=== 开始调试客服发送消息功能 ==='); - console.log('使用会话ID:', conversationId); - - // 创建客服连接 - const managerWs = await createManagerConnection(); - - // 等待2秒确保连接稳定 - await new Promise(resolve => setTimeout(resolve, 2000)); - - // 测试发送消息 - console.log('\n测试客服发送消息...'); - const response = await testManagerSendMessage(managerWs, conversationId); - - console.log('\n=== 测试完成 ==='); - console.log('响应结果:', response); - - // 关闭连接 - setTimeout(() => { - managerWs.close(); - }, 1000); - - } catch (error) { - console.error('测试失败:', error); - } -} - -// 运行测试 -runTest(); diff --git a/server-example/debug_manager_message.js b/server-example/debug_manager_message.js deleted file mode 100644 index 1a59ee8..0000000 --- a/server-example/debug_manager_message.js +++ /dev/null @@ -1,138 +0,0 @@ -// 专用客服消息发送调试脚本 -const WebSocket = require('ws'); - -const WS_URL = 'ws://localhost:3003'; -const TEST_MANAGER_ID = '22'; -const TEST_CUSTOMER_ID = 'test_customer_1'; -let managerWs = null; - -// 启动调试 -async function startDebug() { - console.log('=== 启动客服消息发送调试 ==='); - - try { - // 连接客服WebSocket - await connectManager(); - - // 等待连接稳定 - await new Promise(resolve => setTimeout(resolve, 2000)); - - // 提示输入会话ID - console.log('请先让客户发送一条消息,然后输入获取到的会话ID:'); - - // 模拟会话ID(在实际调试时,这应该从客户消息中获取) - const conversationId = '4fa4b92f-df20-40ae-94b9-f906753a4cfd'; // 这是之前测试中获取的会话ID - - if (conversationId) { - console.log(`使用会话ID: ${conversationId}`); - await sendTestMessage(conversationId); - } - - } catch (error) { - console.error('❌ 调试过程中出现错误:', error); - } finally { - console.log('=== 调试结束 ==='); - if (managerWs && managerWs.readyState === WebSocket.OPEN) { - managerWs.close(); - } - } -} - -// 连接客服WebSocket -function connectManager() { - return new Promise((resolve, reject) => { - managerWs = new WebSocket(WS_URL); - - managerWs.on('open', () => { - console.log('✅ 客服WebSocket连接已建立'); - - // 发送认证消息 - const authMsg = JSON.stringify({ - type: 'auth', - managerId: TEST_MANAGER_ID, - userType: 'manager' - }); - console.log('🔍 发送认证消息:', authMsg); - managerWs.send(authMsg); - }); - - managerWs.on('message', (data) => { - const message = JSON.parse(data.toString()); - console.log('📥 收到消息:', JSON.stringify(message)); - - if (message.type === 'auth_success') { - console.log('✅ 客服认证成功'); - resolve(); - } - }); - - managerWs.on('error', (error) => { - console.error('❌ WebSocket错误:', error); - reject(error); - }); - - setTimeout(() => { - reject(new Error('连接或认证超时')); - }, 5000); - }); -} - -// 发送测试消息 -async function sendTestMessage(conversationId) { - // 尝试多种消息格式 - const testFormats = [ - { - name: '基础格式(使用payload)', - data: { - type: 'chat_message', - payload: { - conversationId: conversationId, - content: '这是测试消息 - 基础格式(使用payload)', - contentType: 1 - } - } - }, - { - name: '带messageId格式(使用payload)', - data: { - type: 'chat_message', - payload: { - messageId: 'test_msg_' + Date.now(), - conversationId: conversationId, - content: '这是测试消息 - 带messageId(使用payload)', - contentType: 1 - } - } - }, - { - name: '完整格式(使用payload)', - data: { - type: 'chat_message', - payload: { - messageId: 'test_msg_' + Date.now(), - conversationId: conversationId, - content: '这是测试消息 - 完整格式(使用payload)', - contentType: 1, - senderType: 2, - senderId: TEST_MANAGER_ID, - receiverId: TEST_CUSTOMER_ID, - createdAt: new Date().toISOString() - } - } - } - ]; - - // 依次测试每种格式 - for (const format of testFormats) { - console.log(`\n🔄 测试格式: ${format.name}`); - console.log(`发送数据: ${JSON.stringify(format.data)}`); - - managerWs.send(JSON.stringify(format.data)); - - // 等待响应 - await new Promise(resolve => setTimeout(resolve, 3000)); - } -} - -// 启动调试 -startDebug(); \ No newline at end of file diff --git a/server-example/debug_new_conversation.js b/server-example/debug_new_conversation.js deleted file mode 100644 index f0f10ac..0000000 --- a/server-example/debug_new_conversation.js +++ /dev/null @@ -1,244 +0,0 @@ -// 全新调试脚本:使用新会话 -const WebSocket = require('ws'); - -const WS_URL = 'ws://localhost:3003'; -const TEST_MANAGER_ID = '22'; -const TEST_CUSTOMER_ID = 'test_customer_1'; -let customerWs = null; -let managerWs = null; -let conversationId = null; -let messageSent = false; - -// 启动调试 -async function startDebug() { - console.log('=== 启动新会话调试 ==='); - - try { - // 连接客服WebSocket - await connectManager(); - - // 等待一会儿 - await new Promise(resolve => setTimeout(resolve, 1000)); - - // 连接客户WebSocket - await connectCustomer(); - - // 等待一会儿 - await new Promise(resolve => setTimeout(resolve, 2000)); - - // 客户发送消息 - await customerSendMessage(); - - // 等待客服收到消息并获取会话ID - await new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error('等待会话ID超时')); - }, 10000); - - const checkConversationId = setInterval(() => { - if (conversationId && !messageSent) { - clearInterval(checkConversationId); - clearTimeout(timeout); - resolve(); - } - }, 500); - }); - - // 客服回复消息 - if (conversationId) { - await managerReplyMessage(); - } - - } catch (error) { - console.error('❌ 调试过程中出现错误:', error); - } finally { - console.log('=== 调试结束 ==='); - cleanup(); - } -} - -// 连接客服WebSocket -function connectManager() { - return new Promise((resolve, reject) => { - managerWs = new WebSocket(WS_URL); - - managerWs.on('open', () => { - console.log('✅ 客服WebSocket连接已建立'); - - // 发送认证消息 - const authMsg = JSON.stringify({ - type: 'auth', - managerId: TEST_MANAGER_ID, - userType: 'manager' - }); - console.log('📤 客服发送认证消息:', authMsg); - managerWs.send(authMsg); - }); - - managerWs.on('message', (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('📥 客服收到消息:', JSON.stringify(message)); - - if (message.type === 'auth_success') { - console.log('✅ 客服认证成功'); - resolve(); - } else if (message.type === 'new_message') { - // 获取会话ID - if (message.payload && message.payload.conversationId) { - conversationId = message.payload.conversationId; - console.log(`📝 获取到新会话ID: ${conversationId}`); - } - } else if (message.type === 'error') { - console.error('❌ 客服收到错误:', message.message); - } - } catch (e) { - console.error('❌ 解析客服消息失败:', e); - } - }); - - managerWs.on('error', (error) => { - console.error('❌ 客服WebSocket错误:', error); - reject(error); - }); - - managerWs.on('close', () => { - console.log('❌ 客服WebSocket连接已关闭'); - }); - - setTimeout(() => { - reject(new Error('客服连接或认证超时')); - }, 10000); - }); -} - -// 连接客户WebSocket -function connectCustomer() { - return new Promise((resolve, reject) => { - customerWs = new WebSocket(WS_URL); - - customerWs.on('open', () => { - console.log('✅ 客户WebSocket连接已建立'); - - // 发送认证消息 - const authMsg = JSON.stringify({ - type: 'auth', - userId: TEST_CUSTOMER_ID, - userType: 'user' - }); - console.log('📤 客户发送认证消息:', authMsg); - customerWs.send(authMsg); - }); - - customerWs.on('message', (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('📥 客户收到消息:', JSON.stringify(message)); - - if (message.type === 'auth_success') { - console.log('✅ 客户认证成功'); - resolve(); - } else if (message.type === 'new_message') { - // 客户收到新消息(可能是客服回复) - console.log('✅ 客户收到客服回复:', message.payload?.content); - } else if (message.type === 'error') { - console.error('❌ 客户收到错误:', message.message); - } - } catch (e) { - console.error('❌ 解析客户消息失败:', e); - } - }); - - customerWs.on('error', (error) => { - console.error('❌ 客户WebSocket错误:', error); - reject(error); - }); - - customerWs.on('close', () => { - console.log('❌ 客户WebSocket连接已关闭'); - }); - - setTimeout(() => { - reject(new Error('客户连接或认证超时')); - }, 10000); - }); -} - -// 客户发送消息 -function customerSendMessage() { - return new Promise((resolve, reject) => { - const messageId = 'test_customer_' + Date.now(); - const message = { - type: 'chat_message', - payload: { - messageId: messageId, - managerId: TEST_MANAGER_ID, // 指定客服ID - content: '你好,我是测试客户,有问题咨询', - contentType: 1 - } - }; - - console.log('📤 客户发送消息:', JSON.stringify(message)); - customerWs.send(JSON.stringify(message)); - - // 等待发送确认 - const messageHandler = (data) => { - try { - const response = JSON.parse(data.toString()); - if (response.type === 'message_sent' && response.payload?.messageId === messageId) { - customerWs.off('message', messageHandler); - console.log('✅ 客户消息发送成功'); - resolve(); - } - } catch (e) { - console.error('❌ 解析客户消息确认失败:', e); - } - }; - - customerWs.on('message', messageHandler); - - setTimeout(() => { - customerWs.off('message', messageHandler); - reject(new Error('客户消息发送超时')); - }, 10000); - }); -} - -// 客服回复消息 -function managerReplyMessage() { - return new Promise((resolve, reject) => { - // 添加随机生成的messageId - const messageId = 'test_manager_' + Date.now(); - const replyMessage = { - type: 'chat_message', - payload: { - messageId: messageId, // 添加messageId - conversationId: conversationId, // 使用新获取的会话ID - content: '您好,我是客服,请问有什么可以帮助您的?', - contentType: 1 - } - }; - - console.log('📤 客服发送回复消息:', JSON.stringify(replyMessage)); - managerWs.send(JSON.stringify(replyMessage)); - messageSent = true; - - // 等待3秒,查看是否有错误回复 - setTimeout(() => { - resolve(); - }, 3000); - }); -} - -// 清理资源 -function cleanup() { - if (customerWs && customerWs.readyState === WebSocket.OPEN) { - customerWs.close(); - } - if (managerWs && managerWs.readyState === WebSocket.OPEN) { - managerWs.close(); - } -} - -// 启动调试 -startDebug(); \ No newline at end of file diff --git a/server-example/diagnose-customer-service.js b/server-example/diagnose-customer-service.js deleted file mode 100644 index 5c8a865..0000000 --- a/server-example/diagnose-customer-service.js +++ /dev/null @@ -1,252 +0,0 @@ -// 客服连接状态诊断工具 -const WebSocket = require('ws'); -const http = require('http'); - -// 服务器地址 -const SERVER_URL = 'ws://localhost:3003'; -const API_URL = 'http://localhost:3003/api/managers'; - -// 用户提供的客服信息 -const customerServicePhone = '17780155537'; // 从用户截图获取的手机号 -const customerServiceId = '22'; // 刘杨的ID(从之前测试中得知) - -console.log('开始客服连接状态诊断...'); -console.log(`测试客服: 手机号 ${customerServicePhone}, ID ${customerServiceId}`); - -// 1. 首先检查API中的客服状态 -function checkCurrentStatus() { - return new Promise((resolve, reject) => { - console.log('\n📊 检查API中的客服当前状态...'); - http.get(API_URL, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - try { - const result = JSON.parse(data); - const targetManager = result.data.find(m => m.managerId === customerServiceId || m.phoneNumber === customerServicePhone); - - if (targetManager) { - console.log(`✅ 找到目标客服: ${targetManager.name}`); - console.log(` 当前在线状态: ${targetManager.online ? '在线' : '离线'}`); - console.log(` 完整信息:`, targetManager); - } else { - console.log('❌ 未找到目标客服信息'); - } - - resolve(result); - } catch (e) { - console.error('❌ 解析API响应失败:', e); - reject(e); - } - }); - }).on('error', (error) => { - console.error('❌ API请求失败:', error); - reject(error); - }); - }); -} - -// 2. 测试不同格式的WebSocket认证消息 -async function testWebSocketAuthentication() { - console.log('\n🔄 测试WebSocket认证(模拟用户登录后连接)...'); - - // 测试多种可能的认证格式 - const authFormats = [ - { - name: '格式1: 直接认证格式', - message: { - type: 'auth', - managerId: customerServiceId, - type: 'manager', - name: '刘杨' - } - }, - { - name: '格式2: 嵌套data对象', - message: { - type: 'auth', - data: { - managerId: customerServiceId, - type: 'manager', - name: '刘杨' - } - } - }, - { - name: '格式3: 双重嵌套data对象', - message: { - type: 'auth', - data: { - data: { - managerId: customerServiceId, - type: 'manager', - name: '刘杨' - } - } - } - }, - { - name: '格式4: 包含手机号', - message: { - type: 'auth', - data: { - managerId: customerServiceId, - phoneNumber: customerServicePhone, - type: 'manager', - name: '刘杨' - } - } - } - ]; - - // 依次测试每种格式 - for (const format of authFormats) { - await new Promise((resolve) => { - console.log(`\n${format.name}:`); - console.log(`发送认证消息:`, JSON.stringify(format.message)); - - const ws = new WebSocket(SERVER_URL); - let responseReceived = false; - - ws.on('open', () => { - ws.send(JSON.stringify(format.message)); - - // 设置超时 - setTimeout(() => { - if (!responseReceived) { - console.log('❌ 未收到响应(超时)'); - ws.close(); - resolve(); - } - }, 3000); - }); - - ws.on('message', (data) => { - responseReceived = true; - try { - const message = JSON.parse(data.toString()); - console.log('✅ 收到响应:', message); - - if (message.type === 'auth_success') { - console.log('✅ 认证成功!'); - } else if (message.type === 'auth_error') { - console.log('❌ 认证失败:', message.message || '未知错误'); - } - - ws.close(); - resolve(); - } catch (e) { - console.error('❌ 解析响应失败:', e); - ws.close(); - resolve(); - } - }); - - ws.on('error', (error) => { - responseReceived = true; - console.error('❌ WebSocket错误:', error.message); - resolve(); - }); - }); - - // 等待1秒后测试下一种格式 - await new Promise(resolve => setTimeout(resolve, 1000)); - } - - console.log('\n🔍 诊断完成!请检查上面的测试结果。'); -} - -// 3. 模拟完整的登录-连接流程 -async function simulateCompleteLoginFlow() { - console.log('\n🎯 模拟完整的登录-连接流程...'); - - // 先检查初始状态 - await checkCurrentStatus(); - - // 进行WebSocket连接和认证 - const ws = new WebSocket(SERVER_URL); - - return new Promise((resolve) => { - ws.on('open', () => { - console.log('✅ WebSocket连接已建立'); - - // 使用最可能成功的格式(嵌套data对象) - const authMessage = { - type: 'auth', - data: { - managerId: customerServiceId, - phoneNumber: customerServicePhone, - type: 'manager', - name: '刘杨' - } - }; - - console.log('发送认证消息:', JSON.stringify(authMessage)); - ws.send(JSON.stringify(authMessage)); - }); - - ws.on('message', async (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('收到响应:', message); - - if (message.type === 'auth_success') { - console.log('✅ 认证成功!等待2秒后检查状态...'); - - // 等待2秒后再次检查状态 - setTimeout(async () => { - await checkCurrentStatus(); - ws.close(); - resolve(); - }, 2000); - } else if (message.type === 'auth_error') { - console.log('❌ 认证失败:', message.message || '未知错误'); - ws.close(); - resolve(); - } - } catch (e) { - console.error('❌ 解析消息失败:', e); - ws.close(); - resolve(); - } - }); - - ws.on('error', (error) => { - console.error('❌ WebSocket错误:', error); - ws.close(); - resolve(); - }); - }); -} - -// 主诊断函数 -async function runDiagnostics() { - try { - console.log('===================================='); - console.log(' 客服连接状态诊断工具'); - console.log('===================================='); - - // 1. 检查当前API状态 - await checkCurrentStatus(); - - // 2. 测试不同认证格式 - await testWebSocketAuthentication(); - - // 3. 模拟完整流程 - await simulateCompleteLoginFlow(); - - console.log('\n===================================='); - console.log('诊断完成!根据测试结果分析问题原因。'); - console.log('===================================='); - - } catch (error) { - console.error('\n❌ 诊断过程中出现错误:', error); - } -} - -// 运行诊断 -runDiagnostics(); \ No newline at end of file diff --git a/server-example/fix_chat_functionality.js b/server-example/fix_chat_functionality.js deleted file mode 100644 index dc99ccb..0000000 --- a/server-example/fix_chat_functionality.js +++ /dev/null @@ -1,236 +0,0 @@ -// 聊天功能修复脚本 -// 此脚本用于清理无效数据并修复聊天功能中的userId类型处理问题 - -const mysql = require('mysql2/promise'); -const fs = require('fs'); -const path = require('path'); - -// 数据库连接配置 -const config = { - host: '1.95.162.61', - port: 3306, - user: 'root', - password: 'schl@2025', - database: 'wechat_app' -}; - -async function fixChatFunctionality() { - let connection; - try { - // 连接数据库 - connection = await mysql.createConnection(config); - console.log('✓ 数据库连接成功'); - - // 1. 验证表结构修改是否成功 - console.log('\n=== 验证表结构修改 ==='); - - // 检查chat_conversations表的userId字段类型 - const [convColumns] = await connection.execute( - "SHOW COLUMNS FROM chat_conversations LIKE 'userId';" - ); - console.log('chat_conversations.userId 字段类型:', convColumns[0]?.Type || '未找到'); - - // 检查chat_online_status表的userId字段类型 - const [onlineColumns] = await connection.execute( - "SHOW COLUMNS FROM chat_online_status LIKE 'userId';" - ); - console.log('chat_online_status.userId 字段类型:', onlineColumns[0]?.Type || '未找到'); - - // 2. 清理无效数据 - console.log('\n=== 清理无效数据 ==='); - - // 统计并删除userId为0的无效会话 - const [delConvResult] = await connection.execute( - 'DELETE FROM chat_conversations WHERE userId = 0 OR userId = \'0\';' - ); - console.log(`已删除 ${delConvResult.affectedRows} 条无效会话记录`); - - // 3. 更新server-mysql.js中的代码逻辑 - console.log('\n=== 更新代码逻辑 ==='); - const serverFilePath = path.join(__dirname, 'server-mysql.js'); - - if (fs.existsSync(serverFilePath)) { - let serverCode = fs.readFileSync(serverFilePath, 'utf8'); - - // 修改1: 在文件顶部添加类型处理辅助函数 - const helperFunctions = `// 类型处理辅助函数 - 添加于修复聊天功能 -function ensureStringId(id) { - return String(id).trim(); -} - -function validateUserId(userId) { - if (typeof userId !== 'string') { - console.warn('警告: userId应该是字符串类型,当前类型:', typeof userId, '值:', userId); - return String(userId).trim(); - } - return userId.trim(); -} - -function validateManagerId(managerId) { - // 确保managerId也是字符串类型 - return String(managerId).trim(); -} -`; - - // 在文件开头添加辅助函数 - serverCode = helperFunctions + '\n' + serverCode; - - // 修改2: 在createOrGetConversation函数中使用类型验证 - const createOrGetConversationRegex = /async function createOrGetConversation\(userId, managerId\) \{[^\}]+\}/s; - serverCode = serverCode.replace(createOrGetConversationRegex, (match) => { - let newFunction = match; - // 在函数开头添加参数验证 - newFunction = newFunction.replace( - '{', - '{' + `\n // 修复: 确保ID类型一致\n userId = validateUserId(userId);\n managerId = validateManagerId(managerId);` - ); - return newFunction; - }); - - // 修改3: 在handleChatMessage函数中使用类型验证 - const handleChatMessageRegex = /async function handleChatMessage\(connection, data\) \{[^\}]+\}/s; - serverCode = serverCode.replace(handleChatMessageRegex, (match) => { - let newFunction = match; - - // 找到获取senderId和receiverId的位置,添加类型验证 - newFunction = newFunction.replace( - /let senderId = data\.senderId;\n let receiverId = data\.receiverId;/, - 'let senderId = validateUserId(data.senderId);\n let receiverId = validateUserId(data.receiverId);' - ); - - // 修改会话用户ID验证逻辑,确保类型一致性 - newFunction = newFunction.replace( - /if \(String\(conversation\.userId\) !== String\(senderId\)\)/, - 'if (conversation.userId !== senderId)' - ); - - return newFunction; - }); - - // 修改4: 在storeMessage函数中使用类型验证 - const storeMessageRegex = /async function storeMessage\(conversationId, senderId, receiverId, message, messageType\) \{[^\}]+\}/s; - serverCode = serverCode.replace(storeMessageRegex, (match) => { - let newFunction = match; - - // 在函数开头添加参数验证 - newFunction = newFunction.replace( - '{', - '{' + `\n // 修复: 确保ID类型一致\n conversationId = String(conversationId).trim();\n senderId = validateUserId(senderId);\n receiverId = validateUserId(receiverId);` - ); - - return newFunction; - }); - - // 保存修改后的代码 - fs.writeFileSync(serverFilePath, serverCode, 'utf8'); - console.log('✓ server-mysql.js 代码更新成功'); - - } else { - console.error('❌ 找不到server-mysql.js文件'); - } - - // 4. 创建验证脚本 - console.log('\n=== 创建功能验证脚本 ==='); - const verifyScript = `// 聊天功能验证脚本 -const mysql = require('mysql2/promise'); - -const config = { - host: '1.95.162.61', - port: 3306, - user: 'root', - password: 'schl@2025', - database: 'wechat_app' -}; - -async function verifyChatFunctionality() { - let connection; - try { - connection = await mysql.createConnection(config); - console.log('验证数据库连接成功'); - - // 1. 测试创建正常会话 - const testUserId = 'test_user_' + Date.now(); - const testManagerId = '22'; - const testConversationId = 'test_conv_' + Date.now(); - - console.log('\n测试创建会话:'); - console.log(' 用户ID:', testUserId); - console.log(' 客服ID:', testManagerId); - - // 插入测试数据 - await connection.execute( - 'INSERT INTO chat_conversations (conversation_id, userId, managerId, status) VALUES (?, ?, ?, 1)', - [testConversationId, testUserId, testManagerId] - ); - console.log('✓ 测试会话创建成功'); - - // 2. 验证数据正确存储 - const [conversations] = await connection.execute( - 'SELECT * FROM chat_conversations WHERE conversation_id = ?', - [testConversationId] - ); - - if (conversations.length > 0) { - const conversation = conversations[0]; - console.log('\n验证会话数据:'); - console.log(' userId类型:', typeof conversation.userId); - console.log(' userId值:', conversation.userId); - console.log(' managerId值:', conversation.managerId); - - if (conversation.userId === testUserId) { - console.log('✓ userId正确存储为字符串'); - } else { - console.log('❌ userId存储不正确'); - } - } - - // 3. 测试查询功能 - const [queryResult] = await connection.execute( - 'SELECT * FROM chat_conversations WHERE userId = ? AND managerId = ?', - [testUserId, testManagerId] - ); - - console.log('\n查询测试:'); - console.log(' Found ' + queryResult.length + ' records.'); - - // 4. 清理测试数据 - await connection.execute( - 'DELETE FROM chat_conversations WHERE conversation_id = ?', - [testConversationId] - ); - console.log('✓ 测试数据清理完成'); - - console.log('\n🎉 聊天功能验证完成,所有测试通过!'); - - } catch (error) { - console.error('验证过程中发生错误:', error); - } finally { - if (connection) await connection.end(); - } -} - -verifyChatFunctionality(); -`; - - fs.writeFileSync(path.join(__dirname, 'verify_chat_fix.js'), verifyScript, 'utf8'); - console.log('✓ 验证脚本创建成功: verify_chat_fix.js'); - - console.log('\n=== 修复完成 ==='); - console.log('1. 数据库表结构已验证'); - console.log('2. 无效数据已清理'); - console.log('3. 代码逻辑已更新'); - console.log('4. 验证脚本已创建'); - console.log('\n请重启服务器并运行验证脚本以确认修复效果'); - - } catch (error) { - console.error('修复过程中发生错误:', error); - } finally { - if (connection) { - await connection.end(); - console.log('数据库连接已关闭'); - } - } -} - -// 运行修复脚本 -fixChatFunctionality(); \ No newline at end of file diff --git a/server-example/minimal_message_test.js b/server-example/minimal_message_test.js deleted file mode 100644 index 5e3f8ca..0000000 --- a/server-example/minimal_message_test.js +++ /dev/null @@ -1,81 +0,0 @@ -// 最小化的消息查询测试脚本 -const WebSocket = require('ws'); - -// 配置 -const SERVER_URL = 'ws://localhost:3003'; -const MANAGER_ID = '22'; // 客服ID - -console.log('=== 最小化消息中心测试 ==='); - -// 创建WebSocket连接 -const ws = new WebSocket(SERVER_URL); - -// 连接建立 -ws.on('open', () => { - console.log('✅ 连接已建立'); - - // 发送认证请求 - const authMsg = { - type: 'auth', - managerId: MANAGER_ID, - userType: 'manager' - }; - - console.log('🔑 发送认证:', JSON.stringify(authMsg)); - ws.send(JSON.stringify(authMsg)); -}); - -// 消息处理 -ws.on('message', (data) => { - const message = JSON.parse(data.toString()); - console.log('📥 收到:', JSON.stringify(message)); - - // 认证成功后发送消息查询 - if (message.type === 'auth_success') { - console.log('✅ 认证成功'); - - // 尝试不同的消息查询格式 - setTimeout(() => { - const query = { - type: 'get_message_list', - managerId: MANAGER_ID - }; - console.log('\n🔍 尝试查询格式1:', JSON.stringify(query)); - ws.send(JSON.stringify(query)); - }, 1000); - - setTimeout(() => { - const query = { - action: 'fetch_chat_list', - managerId: MANAGER_ID - }; - console.log('\n🔍 尝试查询格式2:', JSON.stringify(query)); - ws.send(JSON.stringify(query)); - }, 3000); - - setTimeout(() => { - const query = { - cmd: 'get_chat_history', - managerId: MANAGER_ID - }; - console.log('\n🔍 尝试查询格式3:', JSON.stringify(query)); - ws.send(JSON.stringify(query)); - }, 5000); - } - - // 回复心跳 - else if (message.type === 'heartbeat') { - ws.send(JSON.stringify({ type: 'heartbeat' })); - } -}); - -// 错误处理 -ws.on('error', (error) => { - console.error('❌ 错误:', error); -}); - -// 超时关闭 -setTimeout(() => { - console.log('\n⏰ 测试超时,关闭连接'); - ws.close(); -}, 15000); diff --git a/server-example/server-mysql.js b/server-example/server-mysql.js index 341c49a..a708899 100644 --- a/server-example/server-mysql.js +++ b/server-example/server-mysql.js @@ -6395,37 +6395,9 @@ app.get('/api/managers', async (req, res) => { { replacements: ['采购员'] } ); - // 查询chat_online_status表获取客服在线状态 - const [onlineStatusData] = await sequelize.query( - 'SELECT userId, is_online FROM chat_online_status WHERE type = 2', // type=2表示客服 - { type: sequelize.QueryTypes.SELECT } - ); - // 创建在线状态映射 - const onlineStatusMap = {}; - // 检查onlineStatusData是否存在,防止undefined调用forEach - if (Array.isArray(onlineStatusData)) { - onlineStatusData.forEach(status => { - onlineStatusMap[status.userId] = status.is_online === 1; - }); - } - - // 将获取的数据映射为前端需要的格式,添加online状态(综合考虑内存中的onlineManagers和数据库中的状态) - const isManagerOnline = (id, managerId) => { - // 转换ID为字符串以便正确比较 - const stringId = String(id); - const stringManagerId = managerId ? String(managerId) : null; - - console.log(`检查客服在线状态: id=${id}(${stringId}), managerId=${managerId}(${stringManagerId})`); - - // 首先从内存中的onlineManagers检查(实时性更好) - if (onlineManagers && typeof onlineManagers.has === 'function') { - // 检查id或managerId是否在onlineManagers中 - if (onlineManagers.has(stringId) || (stringManagerId && onlineManagers.has(stringManagerId))) { - console.log(`客服在线(内存检查): id=${id}`); - return true; - } - } + + // 其次从数据库查询结果检查 const dbStatus = onlineStatusMap[stringId] || (stringManagerId && onlineStatusMap[stringManagerId]) || false; diff --git a/server-example/simple-customer-service-test.js b/server-example/simple-customer-service-test.js deleted file mode 100644 index c1df966..0000000 --- a/server-example/simple-customer-service-test.js +++ /dev/null @@ -1,125 +0,0 @@ -// 简化的客服在线状态测试脚本 -const WebSocket = require('ws'); -const http = require('http'); - -// 服务器地址 -const SERVER_URL = 'ws://localhost:3003'; -const API_URL = 'http://localhost:3003/api/managers'; - -// 客服信息 -const customerServicePhone = '17780155537'; -const customerServiceId = '22'; // 刘杨的ID - -console.log('=== 简化的客服在线状态测试 ==='); -console.log(`测试客服ID: ${customerServiceId}`); - -// 检查客服当前状态 -function checkStatus() { - return new Promise((resolve, reject) => { - http.get(API_URL, (res) => { - let data = ''; - res.on('data', chunk => data += chunk); - res.on('end', () => { - try { - const result = JSON.parse(data); - const manager = result.data.find(m => m.managerId === customerServiceId); - if (manager) { - console.log(`客服状态: ${manager.online ? '✅ 在线' : '❌ 离线'}`); - resolve(manager.online); - } else { - console.log('未找到客服信息'); - resolve(false); - } - } catch (e) { - reject(e); - } - }); - }).on('error', reject); - }); -} - -// 使用基础认证格式(与诊断脚本中成功的格式一致) -function testSimpleAuth() { - return new Promise((resolve, reject) => { - console.log('\n--- 使用基础认证格式 ---'); - const ws = new WebSocket(SERVER_URL); - - ws.on('open', () => { - console.log('连接已建立'); - // 使用最简单的认证格式 - const authMessage = { - managerId: customerServiceId, - type: 'manager' - }; - console.log('发送认证:', JSON.stringify(authMessage)); - ws.send(JSON.stringify(authMessage)); - }); - - ws.on('message', (data) => { - const msg = JSON.parse(data); - console.log('收到响应:', msg); - if (msg.type === 'auth_success') { - console.log('✅ 认证成功!'); - resolve({ success: true, ws }); - } else { - console.log('❌ 认证失败'); - ws.close(); - resolve({ success: false }); - } - }); - - ws.on('error', (e) => { - console.log('连接错误:', e.message); - resolve({ success: false }); - }); - - setTimeout(() => { - console.log('连接超时'); - ws.close(); - resolve({ success: false }); - }, 5000); - }); -} - -// 主函数 -async function runTest() { - try { - // 1. 检查初始状态 - console.log('\n1. 检查初始状态:'); - await checkStatus(); - - // 2. 尝试认证 - console.log('\n2. 尝试WebSocket认证:'); - const authResult = await testSimpleAuth(); - - if (authResult.success) { - // 3. 等待3秒后再次检查状态 - console.log('\n3. 等待并检查更新后的状态...'); - setTimeout(async () => { - const newStatus = await checkStatus(); - - console.log('\n=== 测试结果 ==='); - if (newStatus) { - console.log('🎉 成功!客服状态已更新为在线'); - } else { - console.log('❌ 失败!客服仍显示离线'); - } - - // 保持连接15秒 - console.log('\n保持连接15秒,请在前端查看状态...'); - setTimeout(() => { - authResult.ws.close(); - console.log('\n测试完成!'); - }, 15000); - }, 3000); - } else { - console.log('\n=== 测试结果 ==='); - console.log('❌ 认证失败,请检查认证格式或服务器配置'); - } - - } catch (error) { - console.error('测试过程中出错:', error); - } -} - -runTest(); \ No newline at end of file diff --git a/server-example/simple_message_center_test.js b/server-example/simple_message_center_test.js deleted file mode 100644 index 7676441..0000000 --- a/server-example/simple_message_center_test.js +++ /dev/null @@ -1,142 +0,0 @@ -// 简化的客服消息中心测试脚本 -const WebSocket = require('ws'); - -// 服务器配置 -const SERVER_URL = 'ws://localhost:3003'; -const TEST_MANAGER_ID = '22'; // 客服ID -const TEST_CONVERSATION_ID = '963f9eed-950c-47e9-ade6-97e7e90915dc'; // 测试会话ID - -console.log(`=== 启动客服消息中心功能测试 ===`); -console.log(`连接到: ${SERVER_URL}`); -console.log(`客服ID: ${TEST_MANAGER_ID}`); -console.log(`测试会话ID: ${TEST_CONVERSATION_ID}`); - -// 创建WebSocket连接 -const ws = new WebSocket(SERVER_URL); - -// 连接成功 -ws.on('open', () => { - console.log('✅ WebSocket连接已建立'); - - // 发送认证消息 - const authMessage = { - type: 'auth', - managerId: TEST_MANAGER_ID, - userType: 'manager' - }; - - console.log('🔑 发送认证消息:', JSON.stringify(authMessage)); - ws.send(JSON.stringify(authMessage)); -}); - -// 接收消息 -ws.on('message', (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('📥 收到消息:', JSON.stringify(message)); - - // 处理认证成功 - if (message.type === 'auth_success') { - console.log('✅ 客服认证成功'); - - // 认证成功后,尝试获取消息列表 - console.log('\n🔍 尝试获取消息列表...'); - - // 尝试多种消息查询格式 - const queryFormats = [ - // 格式1: 简单查询 - { - type: 'query_chat_list', - managerId: TEST_MANAGER_ID - }, - // 格式2: 直接查询特定会话 - { - type: 'get_conversation_messages', - conversationId: TEST_CONVERSATION_ID, - managerId: TEST_MANAGER_ID - } - ]; - - // 逐一发送查询 - queryFormats.forEach((format, index) => { - setTimeout(() => { - console.log(`\n尝试查询格式 ${index + 1}:`, JSON.stringify(format)); - ws.send(JSON.stringify(format)); - }, index * 2000); - }); - } - - // 处理消息列表响应 - else if (message.type === 'chat_list' || message.type === 'message_list' || message.type === 'conversation_messages') { - console.log('\n🎉 成功接收到消息列表/会话消息!'); - console.log('响应详情:', JSON.stringify(message, null, 2)); - - // 检查是否包含我们的测试会话 - const dataArray = message.data || message.messages || message.payload || []; - if (Array.isArray(dataArray)) { - console.log(`\n📋 共收到 ${dataArray.length} 条记录`); - - // 查找特定会话 - const targetConversation = dataArray.find(item => - item.conversationId === TEST_CONVERSATION_ID || - item.id === TEST_CONVERSATION_ID || - item.conversation_id === TEST_CONVERSATION_ID - ); - - if (targetConversation) { - console.log('\n✅ 找到测试会话!'); - console.log('会话信息:', JSON.stringify(targetConversation, null, 2)); - } else { - console.log('\n📋 所有会话记录:'); - dataArray.forEach((item, i) => { - console.log(`\n--- 会话 ${i + 1} ---`); - console.log(`会话ID: ${item.conversationId || item.id || item.conversation_id || '未知'}`); - if (item.lastMessage || item.last_message) { - console.log(`最后消息: ${item.lastMessage || item.last_message}`); - } - if (item.unreadCount || item.unread_count) { - console.log(`未读数: ${item.unreadCount || item.unread_count}`); - } - }); - } - } - } - - // 处理错误 - else if (message.type === 'error') { - console.error('❌ 收到错误消息:', message.message); - } - - // 处理心跳 - else if (message.type === 'heartbeat') { - console.log('💓 收到心跳消息'); - // 回复心跳以保持连接 - ws.send(JSON.stringify({ type: 'heartbeat_response' })); - } - - // 其他消息类型 - else { - console.log('📝 收到其他类型消息:', message.type); - } - } catch (e) { - console.error('❌ 解析消息失败:', e); - } -}); - -// 连接错误 -ws.on('error', (error) => { - console.error('❌ WebSocket连接错误:', error); -}); - -// 连接关闭 -ws.on('close', () => { - console.log('🔌 WebSocket连接已关闭'); -}); - -// 设置超时 -setTimeout(() => { - console.log('\n⏰ 测试完成,关闭连接'); - if (ws.readyState === WebSocket.OPEN) { - ws.close(); - } -}, 30000); diff --git a/server-example/test-customer-service-online.js b/server-example/test-customer-service-online.js deleted file mode 100644 index d4633cf..0000000 --- a/server-example/test-customer-service-online.js +++ /dev/null @@ -1,134 +0,0 @@ -// 测试客服在线状态功能 -const WebSocket = require('ws'); -const http = require('http'); - -// 服务器地址 -const SERVER_URL = 'ws://localhost:3003'; -const API_URL = 'http://localhost:3003/api/managers'; - -// 模拟客服登录信息 -const testManagerId = '22'; // 刘杨的ID -const testManagerInfo = { - type: 'auth', - data: { - managerId: testManagerId, - type: 'manager', - name: '测试客服' - } -}; - -console.log('开始测试客服在线状态功能...'); -console.log(`目标客服ID: ${testManagerId}`); - -// 连接WebSocket并进行认证 -function connectAndAuthenticate() { - return new Promise((resolve, reject) => { - const ws = new WebSocket(SERVER_URL); - - ws.on('open', () => { - console.log('✅ WebSocket连接已建立'); - - // 发送认证消息 - console.log('发送客服认证消息:', JSON.stringify(testManagerInfo)); - ws.send(JSON.stringify(testManagerInfo)); - }); - - ws.on('message', (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('收到服务器响应:', message); - - // 检查是否认证成功 - if (message.type === 'auth_success' && message.payload && message.payload.type === 'manager') { - console.log('✅ 客服认证成功'); - resolve(ws); - } else if (message.type === 'auth_error') { - console.error('❌ 客服认证失败:', message.message); - reject(new Error('认证失败')); - } - } catch (e) { - console.error('解析消息失败:', e); - reject(e); - } - }); - - ws.on('error', (error) => { - console.error('❌ WebSocket错误:', error); - reject(error); - }); - - // 超时处理 - setTimeout(() => { - reject(new Error('认证超时')); - }, 5000); - }); -} - -// 调用API检查客服在线状态 -function checkManagerOnlineStatus() { - return new Promise((resolve, reject) => { - http.get(API_URL, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - try { - const result = JSON.parse(data); - resolve(result); - } catch (e) { - reject(new Error('解析API响应失败')); - } - }); - }).on('error', (error) => { - reject(error); - }); - }); -} - -// 主测试函数 -async function runTest() { - try { - // 1. 先检查初始状态 - console.log('\n=== 步骤1: 检查初始状态 ==='); - const initialStatus = await checkManagerOnlineStatus(); - const initialManager = initialStatus.data.find(m => m.id.toString() === testManagerId || m.managerId === testManagerId); - console.log(`初始状态 - 客服在线: ${initialManager ? initialManager.online : '未找到'}`); - - // 2. 连接并认证 - console.log('\n=== 步骤2: WebSocket连接和认证 ==='); - const ws = await connectAndAuthenticate(); - - // 3. 等待1秒后再次检查在线状态 - console.log('\n=== 步骤3: 检查连接后的在线状态 ==='); - setTimeout(async () => { - try { - const updatedStatus = await checkManagerOnlineStatus(); - const updatedManager = updatedStatus.data.find(m => m.id.toString() === testManagerId || m.managerId === testManagerId); - - console.log(`更新后状态 - 客服在线: ${updatedManager ? updatedManager.online : '未找到'}`); - - if (updatedManager && updatedManager.online) { - console.log('\n🎉 测试成功!客服在线状态正确显示'); - } else { - console.log('\n❌ 测试失败!客服仍然显示离线'); - } - - // 关闭连接 - ws.close(); - - } catch (error) { - console.error('\n❌ 检查在线状态失败:', error); - ws.close(); - } - }, 1000); - - } catch (error) { - console.error('\n❌ 测试过程中出现错误:', error); - } -} - -// 运行测试 -runTest(); diff --git a/server-example/test-manager-online-check.js b/server-example/test-manager-online-check.js deleted file mode 100644 index 6729455..0000000 --- a/server-example/test-manager-online-check.js +++ /dev/null @@ -1,111 +0,0 @@ -// 测试客服在线状态检查逻辑 -console.log('=== 测试客服在线状态检查逻辑 ===\n'); - -// 模拟onlineManagers Map和onlineStatusMap -const onlineManagers = new Map(); -const onlineStatusMap = {}; - -// 模拟handleAuth函数中添加客服到onlineManagers的逻辑 -function simulateAuth(managerId) { - console.log(`模拟客服认证: managerId=${managerId}`); - // 在handleAuth中是这样添加的:onlineManagers.set(managerId, ws); - const key = String(managerId); // 确保类型一致 - onlineManagers.set(key, { connected: true }); - console.log(` 已添加到onlineManagers: key=${key}`); - - // 模拟更新数据库状态 - onlineStatusMap[key] = true; -} - -// 复制isManagerOnline函数的实现 -function isManagerOnline(id, managerId) { - console.log(`\n调用isManagerOnline: id=${id}, managerId=${managerId}`); - - // 首先从内存中的onlineManagers Map中检查 - const stringId = String(id); - const stringManagerId = String(managerId); - - console.log(` 转换后的ID: stringId=${stringId}, stringManagerId=${stringManagerId}`); - console.log(` onlineManagers中的键:`, [...onlineManagers.keys()]); - - // 检查id或managerId是否在onlineManagers中 - for (const key of onlineManagers.keys()) { - console.log(` 比较key=${key} 与 stringId=${stringId} 或 stringManagerId=${stringManagerId}`); - if (key === stringId || key === stringManagerId) { - console.log(` ✅ 内存匹配成功: key=${key}`); - return true; - } - } - - console.log(` ❌ 内存匹配失败`); - - // 其次从数据库查询结果检查 - const dbStatus = onlineStatusMap[stringId] || (stringManagerId && onlineStatusMap[stringManagerId]) || false; - console.log(` 数据库状态: id=${stringId} -> ${onlineStatusMap[stringId]}, managerId=${stringManagerId} -> ${onlineStatusMap[stringManagerId]}, 最终状态=${dbStatus}`); - return dbStatus; -} - -// 模拟/api/managers接口中的处理逻辑 -function simulateApiResponse(personId, personManagerId) { - console.log(`\n=== 模拟API响应生成 ===`); - console.log(` 客服信息: id=${personId}, managerId=${personManagerId}`); - - const online = isManagerOnline(personId, personManagerId); - - console.log(` API返回: online=${online}`); - return online; -} - -// 测试场景1: 正常匹配 (managerId完全匹配) -console.log('\n🔍 测试场景1: managerId完全匹配'); -simulateAuth(22); -const result1 = simulateApiResponse(1001, 22); -console.log(`\n结果1: ${result1 ? '✅ 在线' : '❌ 离线'} - 应该在线`); - -// 测试场景2: ID格式不匹配 (数字vs字符串) -console.log('\n🔍 测试场景2: ID格式不匹配'); -onlineManagers.clear(); -// 清空onlineStatusMap而不是重新赋值 -Object.keys(onlineStatusMap).forEach(key => delete onlineStatusMap[key]); -simulateAuth('22'); // 使用字符串格式 -const result2 = simulateApiResponse(1001, 22); // 使用数字格式 -console.log(`\n结果2: ${result2 ? '✅ 在线' : '❌ 离线'} - 应该在线`); - -// 测试场景3: 测试不存在的ID -console.log('\n🔍 测试场景3: 测试不存在的ID'); -const result3 = simulateApiResponse(999, 999); -console.log(`\n结果3: ${result3 ? '✅ 在线' : '❌ 离线'} - 应该离线`); - -// 测试场景4: 测试id和managerId都存在 -console.log('\n🔍 测试场景4: 测试id和managerId都存在'); -onlineManagers.clear(); -// 清空onlineStatusMap而不是重新赋值 -Object.keys(onlineStatusMap).forEach(key => delete onlineStatusMap[key]); -simulateAuth(1001); // 使用person.id作为key -simulateAuth(22); // 使用person.managerId作为key -const result4 = simulateApiResponse(1001, 22); -console.log(`\n结果4: ${result4 ? '✅ 在线' : '❌ 离线'} - 应该在线`); - -// 测试场景5: 测试isManagerOnline函数的边界情况 -console.log('\n🔍 测试场景5: 边界情况测试'); -onlineManagers.clear(); -// 清空onlineStatusMap而不是重新赋值 -Object.keys(onlineStatusMap).forEach(key => delete onlineStatusMap[key]); -simulateAuth(22); - -// 测试undefined/null -console.log('\n 测试undefined/null:'); -console.log(` isManagerOnline(undefined, 22): ${isManagerOnline(undefined, 22)}`); -console.log(` isManagerOnline(1001, null): ${isManagerOnline(1001, null)}`); - -// 测试空字符串 -console.log('\n 测试空字符串:'); -console.log(` isManagerOnline("", 22): ${isManagerOnline("", 22)}`); -console.log(` isManagerOnline(1001, ""): ${isManagerOnline(1001, "")}`); - -// 总结 -console.log('\n=== 测试总结 ==='); -console.log(`场景1: ${result1 ? '✅ 通过' : '❌ 失败'} - managerId完全匹配`); -console.log(`场景2: ${result2 ? '✅ 通过' : '❌ 失败'} - ID格式不匹配`); -console.log(`场景3: ${!result3 ? '✅ 通过' : '❌ 失败'} - 不存在的ID`); -console.log(`场景4: ${result4 ? '✅ 通过' : '❌ 失败'} - id和managerId都存在`); diff --git a/server-example/test-managers-api.js b/server-example/test-managers-api.js deleted file mode 100644 index 85365ba..0000000 --- a/server-example/test-managers-api.js +++ /dev/null @@ -1,79 +0,0 @@ -// 测试修复后的/api/managers接口 -const http = require('http'); - -console.log('开始测试/api/managers接口...'); - -const options = { - hostname: 'localhost', - port: 3003, - path: '/api/managers', - method: 'GET', - headers: { - 'Content-Type': 'application/json' - } -}; - -const req = http.request(options, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - console.log(`状态码: ${res.statusCode}`); - - try { - const responseData = JSON.parse(data); - console.log('响应数据:', JSON.stringify(responseData, null, 2)); - - // 验证响应格式 - if (res.statusCode === 200) { - if (responseData.success === true || responseData.code === 200) { - console.log('✅ API接口返回成功状态'); - - // 检查数据字段 - const dataArray = responseData.data || responseData; - if (Array.isArray(dataArray)) { - console.log(`✅ 获取到 ${dataArray.length} 条客服数据`); - - // 检查数据结构 - if (dataArray.length > 0) { - const firstItem = dataArray[0]; - console.log('第一条数据结构:', Object.keys(firstItem)); - - // 检查必要字段 - const requiredFields = ['id', 'managerId', 'name', 'phoneNumber']; - const missingFields = requiredFields.filter(field => !(field in firstItem)); - - if (missingFields.length === 0) { - console.log('✅ 所有必要字段都存在'); - } else { - console.warn('⚠️ 缺少必要字段:', missingFields); - } - } - - console.log('🎉 测试通过!API接口正常工作'); - } else { - console.error('❌ 响应数据不是预期的数组格式'); - } - } else { - console.error('❌ API返回非成功状态:', responseData); - } - } else { - console.error(`❌ 请求失败,状态码: ${res.statusCode}`); - } - } catch (parseError) { - console.error('❌ JSON解析错误:', parseError.message); - console.error('原始响应数据:', data); - } - }); -}); - -req.on('error', (e) => { - console.error('❌ 请求错误:', e.message); -}); - -req.end(); - -console.log('测试脚本已启动,请等待测试结果...'); diff --git a/server-example/test_chat_flow.js b/server-example/test_chat_flow.js deleted file mode 100644 index 1e94885..0000000 --- a/server-example/test_chat_flow.js +++ /dev/null @@ -1,85 +0,0 @@ -// 测试聊天流程脚本 -const mysql = require('mysql2/promise'); - -// 数据库配置 -const dbConfig = { - host: '1.95.162.61', - port: 3306, - user: 'root', - password: 'schl@2025', - database: 'wechat_app' -}; - -async function testChatFlow() { - let connection; - try { - // 连接数据库 - connection = await mysql.createConnection(dbConfig); - console.log('✅ 数据库连接成功'); - - // 1. 检查chat_conversations表中是否有会话 - const [conversations] = await connection.execute( - 'SELECT * FROM chat_conversations ORDER BY created_at DESC LIMIT 5' - ); - - if (conversations.length > 0) { - console.log('\n✅ 最近的5个会话:'); - conversations.forEach((conv, index) => { - console.log(`\n会话 ${index + 1}:`); - console.log(` conversation_id: ${conv.conversation_id}`); - console.log(` userId: ${conv.userId}`); - console.log(` managerId: ${conv.managerId}`); - console.log(` status: ${conv.status}`); - console.log(` created_at: ${conv.created_at}`); - }); - - // 2. 检查最新会话的消息 - const latestConversationId = conversations[0].conversation_id; - const [messages] = await connection.execute( - 'SELECT * FROM chat_messages WHERE conversation_id = ? ORDER BY created_at DESC LIMIT 10', - [latestConversationId] - ); - - console.log(`\n🔍 查询会话 ${latestConversationId} 的消息:`); - if (messages.length > 0) { - console.log(`✅ 找到 ${messages.length} 条消息:`); - messages.forEach((msg, index) => { - console.log(`\n消息 ${index + 1}:`); - console.log(` message_id: ${msg.message_id}`); - console.log(` content: ${msg.content}`); - console.log(` senderId: ${msg.senderId}`); - console.log(` receiverId: ${msg.receiverId}`); - console.log(` senderType: ${msg.senderType}`); - console.log(` created_at: ${msg.created_at}`); - }); - } else { - console.log('❌ 未找到该会话的消息记录'); - } - } else { - console.log('❌ 未找到任何会话记录'); - } - - // 3. 检查所有消息(最近10条) - const [allMessages] = await connection.execute( - 'SELECT * FROM chat_messages ORDER BY created_at DESC LIMIT 10' - ); - - console.log('\n📊 最近的10条消息(所有会话):'); - if (allMessages.length > 0) { - console.log(`✅ 找到 ${allMessages.length} 条消息记录`); - } else { - console.log('❌ 数据库中没有任何消息记录'); - } - - } catch (error) { - console.error('❌ 测试过程中发生错误:', error); - } finally { - if (connection) { - await connection.end(); - console.log('\n✅ 数据库连接已关闭'); - } - } -} - -// 执行测试 -testChatFlow(); diff --git a/server-example/test_chat_functionality_complete.js b/server-example/test_chat_functionality_complete.js deleted file mode 100644 index 5c0e792..0000000 --- a/server-example/test_chat_functionality_complete.js +++ /dev/null @@ -1,215 +0,0 @@ -// 简化的聊天功能测试脚本 -const WebSocket = require('ws'); - -// 配置信息 -const WS_URL = 'ws://localhost:3003'; -const TEST_CUSTOMER_ID = 'test_customer_1'; -const TEST_MANAGER_ID = '22'; - -// 测试结果跟踪 -const testResults = { - customerConnected: false, - customerAuthenticated: false, - managerConnected: false, - managerAuthenticated: false, - customerMessageSent: false, - managerMessageReceived: false, - managerReplySent: false, - customerReplyReceived: false -}; - -// 辅助函数:等待指定时间 -function wait(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -// 主测试函数 -async function runTest() { - console.log('=== 开始聊天功能测试 ===\n'); - - let customerWs = null; - let managerWs = null; - - try { - // 1. 创建客户WebSocket连接并认证 - customerWs = await createConnection(WS_URL, 'customer'); - - // 2. 创建客服WebSocket连接并认证 - managerWs = await createConnection(WS_URL, 'manager'); - - // 3. 等待连接稳定 - await wait(2000); - - // 4. 客户发送消息给客服 - const testMessage = '你好,我是测试客户,有问题咨询'; - console.log('🔍 客户发送消息给客服:', testMessage); - - // 使用更简单的格式,只发送必要字段 - const customerMessageId = 'test_customer_' + Date.now(); - customerWs.send(JSON.stringify({ - type: 'chat_message', - messageId: customerMessageId, - managerId: TEST_MANAGER_ID, - content: testMessage, - contentType: 1 - })); - - testResults.customerMessageSent = true; - console.log('✅ 客户发送消息成功'); - - // 5. 客服接收消息并回复 - let conversationId = null; - let replyResolved = false; - - // 客服监听消息 - managerWs.on('message', (data) => { - const message = JSON.parse(data.toString()); - console.log('📥 客服收到消息:', message); - - // 忽略心跳消息 - if (message.type === 'heartbeat') return; - - // 处理新消息 - if (message.type === 'new_message' && message.payload && message.payload.content === testMessage) { - conversationId = message.payload.conversationId; - testResults.managerMessageReceived = true; - console.log('✅ 客服收到客户消息:', message.payload.content); - - // 客服回复客户 - const replyMessage = '您好,我是客服,请问有什么可以帮助您的?'; - console.log('🔍 客服回复客户:', replyMessage); - - // 简化消息格式,只包含基本必要字段 - // 注意:服务器会验证conversationId的有效性 - const messageId = 'test_reply_' + Date.now(); - const replyData = { - type: 'chat_message', - conversationId: conversationId, - content: replyMessage, - contentType: 1 - }; - - console.log('📤 客服发送回复消息:', JSON.stringify(replyData)); - - console.log('📤 客服发送回复消息:', replyData); - managerWs.send(JSON.stringify(replyData)); - testResults.managerReplySent = true; - console.log('✅ 客服回复消息已发送'); - } - }); - - // 客户监听回复 - customerWs.on('message', (data) => { - const message = JSON.parse(data.toString()); - console.log('📥 客户收到消息:', message); - - // 忽略心跳消息 - if (message.type === 'heartbeat') return; - - // 处理消息发送确认 - if (message.type === 'message_sent') { - console.log('✅ 消息发送确认:', message.payload.status); - } - - // 处理新消息(客服回复) - if (message.type === 'new_message' && message.payload && message.payload.senderId === TEST_MANAGER_ID) { - testResults.customerReplyReceived = true; - console.log('✅ 客户接收客服回复:', message.payload.content); - replyResolved = true; - } - }); - - // 等待消息传递完成或超时 - const startTime = Date.now(); - while (!replyResolved && (Date.now() - startTime) < 15000) { - await wait(500); - } - - if (!replyResolved) { - console.log('❌ 测试超时:未能完成消息传递流程'); - } - - } catch (error) { - console.error('❌ 测试错误:', error); - } finally { - // 输出测试结果 - console.log('\n=== 测试结果汇总 ==='); - console.log(`客户连接: ${testResults.customerConnected ? '✅ 成功' : '❌ 失败'}`); - console.log(`客服连接: ${testResults.managerConnected ? '✅ 成功' : '❌ 失败'}`); - console.log(`客户认证: ${testResults.customerAuthenticated ? '✅ 成功' : '❌ 失败'}`); - console.log(`客服认证: ${testResults.managerAuthenticated ? '✅ 成功' : '❌ 失败'}`); - console.log(`客户发送消息: ${testResults.customerMessageSent ? '✅ 成功' : '❌ 失败'}`); - console.log(`客服接收消息: ${testResults.managerMessageReceived ? '✅ 成功' : '❌ 失败'}`); - console.log(`客服回复消息: ${testResults.managerReplySent ? '✅ 成功' : '❌ 失败'}`); - console.log(`客户接收回复: ${testResults.customerReplyReceived ? '✅ 成功' : '❌ 失败'}`); - - // 关闭连接 - if (customerWs && customerWs.readyState === WebSocket.OPEN) { - customerWs.close(); - console.log('客户WebSocket连接已关闭'); - } - if (managerWs && managerWs.readyState === WebSocket.OPEN) { - managerWs.close(); - console.log('客服WebSocket连接已关闭'); - } - } -} - -// 创建WebSocket连接并认证 -function createConnection(url, userType) { - return new Promise((resolve, reject) => { - const ws = new WebSocket(url); - - ws.on('open', () => { - if (userType === 'customer') { - testResults.customerConnected = true; - console.log('✅ 客户WebSocket连接已建立'); - - // 发送认证消息 - ws.send(JSON.stringify({ - type: 'auth', - userId: TEST_CUSTOMER_ID, - userType: 'user' - })); - } else { - testResults.managerConnected = true; - console.log('✅ 客服WebSocket连接已建立'); - - // 发送认证消息 - ws.send(JSON.stringify({ - type: 'auth', - managerId: TEST_MANAGER_ID, - userType: 'manager' - })); - } - }); - - ws.on('message', (data) => { - const message = JSON.parse(data.toString()); - - if (message.type === 'auth_success') { - if (userType === 'customer') { - testResults.customerAuthenticated = true; - console.log('✅ 客户认证成功'); - } else { - testResults.managerAuthenticated = true; - console.log('✅ 客服认证成功'); - } - resolve(ws); - } - }); - - ws.on('error', (error) => { - console.error(`❌ ${userType === 'customer' ? '客户' : '客服'}WebSocket错误:`, error); - reject(error); - }); - - // 设置超时 - setTimeout(() => { - reject(new Error(`${userType === 'customer' ? '客户' : '客服'}连接或认证超时`)); - }, 5000); - }); -} - -// 运行测试 -runTest(); diff --git a/server-example/test_complete_chat_flow.js b/server-example/test_complete_chat_flow.js deleted file mode 100644 index 3722b7d..0000000 --- a/server-example/test_complete_chat_flow.js +++ /dev/null @@ -1,279 +0,0 @@ -const WebSocket = require('ws'); -const crypto = require('crypto'); - -// 配置信息 -const SERVER_URL = 'ws://localhost:3003'; // 修改为正确的端口 -const USER_ID = 'test_user_1'; -const MANAGER_ID = '22'; - -// 测试消息内容 -const TEST_MESSAGE = '这是一条测试消息,验证聊天功能是否正常工作!'; - -// 跟踪测试状态 -let testResults = { - userConnected: false, - managerConnected: false, - userAuthenticated: false, - managerAuthenticated: false, - messageSent: false, - messageReceivedByManager: false, - messageStoredInDatabase: false, - error: null -}; - -// 用户连接 -let userWs; -// 客服连接 -let managerWs; - -// 开始测试 -console.log('开始测试完整的聊天功能流程...'); - -// 连接客服 -function connectManager() { - return new Promise((resolve, reject) => { - console.log(`\n正在连接客服 (managerId: ${MANAGER_ID})...`); - managerWs = new WebSocket(SERVER_URL); - - managerWs.on('open', () => { - console.log('✅ 客服WebSocket连接已建立'); - testResults.managerConnected = true; - - // 客服认证 - console.log('客服发送认证请求...'); - const authMessage = { - type: 'auth', - managerId: MANAGER_ID, - userType: 'manager' - }; - managerWs.send(JSON.stringify(authMessage)); - }); - - managerWs.on('message', (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('👂 客服收到消息:', JSON.stringify(message)); - - // 处理认证响应 - if (message.type === 'auth_success' && !testResults.managerAuthenticated) { - console.log('✅ 客服认证成功'); - testResults.managerAuthenticated = true; - resolve(); - } - - // 处理心跳响应 - if (message.type === 'pong') { - console.log('⏱️ 客服收到心跳响应'); - } - - // 处理新消息 - if (message.type === 'new_message' && message.payload) { - console.log('📩 客服收到新消息:', message.payload.content); - if (message.payload.content === TEST_MESSAGE) { - testResults.messageReceivedByManager = true; - console.log('✅ 测试成功: 客服收到了用户发送的消息!'); - completeTest(); - } - } - - // 处理错误 - if (message.type === 'error') { - console.error('❌ 客服收到错误:', message.message); - testResults.error = message.message; - reject(new Error(message.message)); - } - } catch (error) { - console.error('❌ 客服消息解析错误:', error); - testResults.error = error.message; - reject(error); - } - }); - - managerWs.on('error', (error) => { - console.error('❌ 客服WebSocket连接错误:', error); - testResults.error = error.message; - reject(error); - }); - - managerWs.on('close', () => { - console.log('❌ 客服WebSocket连接已关闭'); - testResults.managerConnected = false; - }); - }); -} - -// 连接用户 -function connectUser() { - return new Promise((resolve, reject) => { - console.log(`\n正在连接用户 (userId: ${USER_ID})...`); - userWs = new WebSocket(SERVER_URL); - - userWs.on('open', () => { - console.log('✅ 用户WebSocket连接已建立'); - testResults.userConnected = true; - - // 用户认证 - console.log('用户发送认证请求...'); - const authMessage = { - type: 'auth', - userId: USER_ID, - userType: 'user' - }; - userWs.send(JSON.stringify(authMessage)); - }); - - userWs.on('message', (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('👂 用户收到消息:', JSON.stringify(message)); - - // 处理认证响应 - if (message.type === 'auth_success' && !testResults.userAuthenticated) { - console.log('✅ 用户认证成功'); - testResults.userAuthenticated = true; - resolve(); - } - - // 处理心跳响应 - if (message.type === 'pong') { - console.log('⏱️ 用户收到心跳响应'); - } - - // 处理消息发送确认 - if (message.type === 'message_sent') { - console.log('✅ 用户消息发送确认:', message.payload); - testResults.messageSent = true; - // 消息发送成功后,等待客服接收 - setTimeout(() => { - if (!testResults.messageReceivedByManager) { - console.error('❌ 测试失败: 客服未收到用户消息'); - completeTest(); - } - }, 5000); // 等待5秒查看客服是否收到消息 - } - - // 处理错误 - if (message.type === 'error') { - console.error('❌ 用户收到错误:', message.message); - testResults.error = message.message; - reject(new Error(message.message)); - } - } catch (error) { - console.error('❌ 用户消息解析错误:', error); - testResults.error = error.message; - reject(error); - } - }); - - userWs.on('error', (error) => { - console.error('❌ 用户WebSocket连接错误:', error); - testResults.error = error.message; - reject(error); - }); - - userWs.on('close', () => { - console.log('❌ 用户WebSocket连接已关闭'); - testResults.userConnected = false; - }); - }); -} - -// 发送测试消息 -function sendTestMessage() { - return new Promise((resolve) => { - setTimeout(() => { - console.log(`\n正在发送测试消息给客服...`); - const messageId = crypto.randomUUID(); - const message = { - type: 'chat_message', // 确保使用正确的消息类型 - data: { - messageId: messageId, - userId: USER_ID, - managerId: MANAGER_ID, - content: TEST_MESSAGE, - contentType: 1, // 文本消息 - timestamp: Date.now() - } - }; - - console.log('发送消息:', JSON.stringify(message)); - userWs.send(JSON.stringify(message)); - resolve(); - }, 1000); - }); -} - -// 清理连接 -function cleanup() { - if (userWs && userWs.readyState === WebSocket.OPEN) { - userWs.close(); - } - if (managerWs && managerWs.readyState === WebSocket.OPEN) { - managerWs.close(); - } -} - -// 完成测试并输出结果 -function completeTest() { - console.log('\n📊 测试结果摘要:'); - console.log('- 用户连接成功:', testResults.userConnected); - console.log('- 客服连接成功:', testResults.managerConnected); - console.log('- 用户认证成功:', testResults.userAuthenticated); - console.log('- 客服认证成功:', testResults.managerAuthenticated); - console.log('- 消息发送成功:', testResults.messageSent); - console.log('- 客服收到消息:', testResults.messageReceivedByManager); - - if (testResults.error) { - console.log('\n❌ 测试失败: ' + testResults.error); - } else if (testResults.messageReceivedByManager) { - console.log('\n🎉 测试成功! 聊天功能正常工作!'); - } else { - console.log('\n❌ 测试失败: 聊天功能存在问题'); - } - - // 清理并退出 - cleanup(); - setTimeout(() => { - process.exit(testResults.messageReceivedByManager ? 0 : 1); - }, 1000); -} - -// 主测试流程 -async function runTest() { - try { - // 1. 先连接客服 - await connectManager(); - console.log('客服连接和认证成功'); - - // 2. 再连接用户 - await connectUser(); - console.log('用户连接和认证成功'); - - // 3. 发送测试消息 - await sendTestMessage(); - console.log('测试消息已发送'); - - // 4. 设置超时 - setTimeout(() => { - if (!testResults.messageReceivedByManager) { - console.error('❌ 测试超时: 客服在规定时间内未收到消息'); - completeTest(); - } - }, 10000); - - } catch (error) { - console.error('❌ 测试执行失败:', error); - testResults.error = error.message; - completeTest(); - } -} - -// 运行测试 -runTest(); - -// 监听进程退出 -process.on('SIGINT', () => { - console.log('\n正在中断测试...'); - cleanup(); - process.exit(1); -}); \ No newline at end of file diff --git a/server-example/test_correct_message_format.js b/server-example/test_correct_message_format.js deleted file mode 100644 index e5e86be..0000000 --- a/server-example/test_correct_message_format.js +++ /dev/null @@ -1,193 +0,0 @@ -const WebSocket = require('ws'); - -// 测试配置 -const config = { - wsUrl: 'ws://localhost:3003', - managerCredentials: { - managerId: 22, // 使用数字格式的客服ID - userType: 'manager' - }, - testTimeout: 10000, // 10秒超时 - messageTimeout: 5000 // 5秒消息等待超时 -}; - -// 简化的测试函数 -async function testConversationListFormat() { - console.log('开始测试客服会话列表查询(正确消息格式)'); - console.log(`连接到WebSocket服务器: ${config.wsUrl}`); - - return new Promise((resolve, reject) => { - let ws; - let connected = false; - let authenticated = false; - let listMessageSent = false; - - // 超时处理 - const timeoutId = setTimeout(() => { - console.error('测试超时!'); - if (ws && connected) { - ws.close(); - } - reject(new Error('测试超时:未在规定时间内完成所有操作')); - }, config.testTimeout); - - try { - // 创建WebSocket连接 - ws = new WebSocket(config.wsUrl); - - // 连接打开事件 - ws.on('open', () => { - console.log('✓ WebSocket连接已建立'); - connected = true; - - // 发送认证请求 - const authData = { - type: 'auth', - managerId: config.managerCredentials.managerId, - userType: config.managerCredentials.userType - }; - console.log(`发送客服认证请求:`, authData); - ws.send(JSON.stringify(authData)); - }); - - // 接收消息事件 - ws.on('message', (data) => { - try { - const messageStr = data.toString('utf8'); - console.log('收到原始消息数据:', messageStr); - - const message = JSON.parse(messageStr); - console.log('解析后的消息:', message); - - // 处理认证响应 - if (message.type === 'auth_success') { - console.log('✓ 客服认证成功'); - authenticated = true; - - // 只使用'list'操作格式查询会话列表 - setTimeout(() => { - if (!listMessageSent) { - listMessageSent = true; - const listRequest = { - action: 'list' - }; - console.log(`发送会话列表查询请求 (action: 'list'):`, listRequest); - ws.send(JSON.stringify(listRequest)); - - // 为'list'请求设置专用超时 - setTimeout(() => { - console.log('未收到会话列表响应,尝试直接发送消息类型为"get_conversations"的请求'); - const directRequest = { - type: 'get_conversations' - }; - console.log(`发送直接请求 (type: 'get_conversations'):`, directRequest); - ws.send(JSON.stringify(directRequest)); - }, config.messageTimeout); - } - }, 1000); - - } else if (message.type === 'session_list' && message.data) { - // 处理'list'操作的响应 - console.log('✓ 收到会话列表响应 (session_list格式)'); - console.log(`会话列表包含 ${message.data.length} 个会话`); - - // 清理并完成测试 - clearTimeout(timeoutId); - setTimeout(() => { - ws.close(); - resolve({ - success: true, - responseType: 'session_list', - data: message.data - }); - }, 1000); - - } else if (message.type === 'conversations_list' && message.payload && message.payload.conversations) { - // 处理'get_conversations'操作的响应 - console.log('✓ 收到会话列表响应 (conversations_list格式)'); - console.log(`会话列表包含 ${message.payload.conversations.length} 个会话`); - - // 清理并完成测试 - clearTimeout(timeoutId); - setTimeout(() => { - ws.close(); - resolve({ - success: true, - responseType: 'conversations_list', - data: message.payload.conversations - }); - }, 1000); - - } else if (message.type === 'heartbeat') { - // 记录心跳消息 - console.log('收到心跳消息'); - - } else if (message.type === 'error') { - // 处理错误 - console.error('✗ 收到错误消息:', message.message); - - } else { - console.log('收到未预期的消息类型:', message.type); - } - - } catch (parseError) { - console.error('解析消息失败:', parseError); - console.log('原始消息数据:', data.toString('utf8')); - } - }); - - // 错误事件处理 - ws.on('error', (error) => { - console.error('WebSocket错误:', error); - }); - - // 连接关闭事件 - ws.on('close', (code, reason) => { - console.log(`WebSocket连接已关闭: 代码=${code}, 原因=${reason}`); - clearTimeout(timeoutId); - // 即使连接关闭,也尝试完成测试 - resolve({ success: false, message: '连接已关闭但未收到会话列表' }); - }); - - } catch (error) { - console.error('测试初始化失败:', error); - clearTimeout(timeoutId); - reject(error); - } - }); -} - -// 运行测试 -testConversationListFormat() - .then(result => { - console.log('\n=== 测试结果 ==='); - if (result.success) { - console.log('测试成功!'); - console.log(`响应类型: ${result.responseType}`); - console.log(`获取到 ${result.data.length} 个会话`); - - if (result.data.length > 0) { - console.log('=== 会话详情(前3个)==='); - result.data.slice(0, 3).forEach((conv, index) => { - console.log(`\n会话 ${index + 1}:`); - console.log(` 会话ID: ${conv.conversation_id || '未知'}`); - console.log(` 用户ID: ${conv.user_id || '未知'}`); - console.log(` 客服ID: ${conv.manager_id || '未知'}`); - console.log(` 最后消息: ${conv.last_message || '无'}`); - console.log(` 未读计数: ${conv.unread_count || '未知'}`); - console.log(` 用户在线: ${conv.user_online ? '是' : '否'}`); - console.log(` 客服在线: ${conv.cs_online ? '是' : '否'}`); - }); - } - } else { - console.log('测试未完全成功,但完成了连接和认证'); - console.log('消息:', result.message); - } - }) - .catch(error => { - console.error('\n=== 测试执行失败 ==='); - console.error('错误:', error.message); - }) - .finally(() => { - console.log('\n测试完成!'); - }); diff --git a/server-example/test_improved_message_center.js b/server-example/test_improved_message_center.js deleted file mode 100644 index 29f1492..0000000 --- a/server-example/test_improved_message_center.js +++ /dev/null @@ -1,247 +0,0 @@ -const WebSocket = require('ws'); - -// 配置参数 -const CONFIG = { - WS_URL: 'ws://localhost:3003', // WebSocket服务器地址 - MANAGER_ID: 22, // 客服ID - TIMEOUT: 15000, // 总超时时间 - MESSAGE_TIMEOUT: 5000, // 消息响应超时时间 - TEST_MODE: 'manager', // 测试模式: 'manager' 或 'user' - DEBUG: true // 启用详细调试日志 -}; - -// 日志函数 -function log(...args) { - if (CONFIG.DEBUG) { - console.log(`[${new Date().toLocaleTimeString()}]`, ...args); - } -} - -function error(...args) { - console.error(`[${new Date().toLocaleTimeString()}] ERROR:`, ...args); -} - -// 测试函数 -async function testMessageCenter() { - log('开始测试客服消息中心功能'); - log('配置:', CONFIG); - - return new Promise((resolve, reject) => { - // 设置总超时 - const totalTimeout = setTimeout(() => { - error('测试总超时'); - ws.close(); - reject(new Error('测试总超时')); - }, CONFIG.TIMEOUT); - - // 创建WebSocket连接 - let ws; - try { - ws = new WebSocket(CONFIG.WS_URL); - log('正在连接WebSocket服务器...'); - } catch (err) { - clearTimeout(totalTimeout); - error('创建WebSocket连接失败:', err.message); - reject(err); - return; - } - - // 连接建立 - ws.on('open', () => { - log('✅ WebSocket连接已建立'); - - // 发送认证请求 - 使用兼容格式,包含多种可能的ID字段 - const authMessage = { - type: 'auth', - managerId: CONFIG.MANAGER_ID, - userId: CONFIG.MANAGER_ID, - userType: CONFIG.TEST_MODE === 'manager' ? 'manager' : 'user', - data: { - managerId: CONFIG.MANAGER_ID, - userId: CONFIG.MANAGER_ID, - type: CONFIG.TEST_MODE === 'manager' ? 'manager' : 'user', - id: CONFIG.MANAGER_ID - } - }; - - log('发送认证请求:', authMessage); - ws.send(JSON.stringify(authMessage)); - - // 等待认证响应 - const authResponseTimer = setTimeout(() => { - error('认证响应超时'); - ws.close(); - clearTimeout(totalTimeout); - reject(new Error('认证响应超时')); - }, CONFIG.MESSAGE_TIMEOUT); - - // 消息接收处理 - ws.on('message', (data) => { - log('收到消息:', data.toString()); - - try { - const message = JSON.parse(data.toString()); - - // 忽略心跳消息 - if (message.type === 'heartbeat') { - log('💓 收到心跳消息'); - return; - } - - // 处理认证成功响应 - if (message.type === 'auth_success') { - clearTimeout(authResponseTimer); - log('✅ 认证成功'); - - // 发送会话列表查询请求 (正确的session格式) - setTimeout(() => { - const listRequest = { - type: 'session', - action: 'list' - }; - log('发送会话列表查询请求 (session list格式):', listRequest); - ws.send(JSON.stringify(listRequest)); - - // 等待会话列表响应 - const listResponseTimer = setTimeout(() => { - log('⚠️ 未收到会话列表响应 (list格式),尝试使用另一种格式'); - - // 尝试使用 type: 'session' + type: 'get_conversations' 格式 - const conversationsRequest = { - type: 'session', - data: { type: 'get_conversations' } - }; - log('发送会话列表查询请求 (session get_conversations格式):', conversationsRequest); - ws.send(JSON.stringify(conversationsRequest)); - - // 等待响应 - const conversationsTimer = setTimeout(() => { - error('未收到会话列表响应'); - ws.close(); - clearTimeout(totalTimeout); - reject(new Error('未收到会话列表响应')); - }, CONFIG.MESSAGE_TIMEOUT); - - // 监听响应 - const handleConversationsResponse = (data) => { - try { - const msg = JSON.parse(data.toString()); - - // 忽略心跳消息 - if (msg.type === 'heartbeat') { - log('💓 收到心跳消息'); - return; - } - - log('收到后续消息:', msg); - - if (msg.type === 'conversations_list' || msg.type === 'session_list') { - clearTimeout(conversationsTimer); - clearTimeout(totalTimeout); - log('✅ 收到会话列表响应:', { - type: msg.type, - hasConversations: msg.data && msg.data.length > 0 || msg.payload && msg.payload.conversations && msg.payload.conversations.length > 0, - conversationCount: msg.data ? msg.data.length : (msg.payload && msg.payload.conversations ? msg.payload.conversations.length : 0) - }); - ws.close(); - resolve({ success: true, response: msg }); - } else if (msg.type === 'error') { - clearTimeout(conversationsTimer); - error('收到错误响应:', msg.message); - ws.close(); - clearTimeout(totalTimeout); - reject(new Error(`错误响应: ${msg.message}`)); - } - } catch (err) { - error('解析响应失败:', err); - } - }; - - // 使用on而不是once,因为可能会收到多个心跳消息 - ws.on('message', handleConversationsResponse); - }, CONFIG.MESSAGE_TIMEOUT); - }, 1000); - - // 第一次响应处理 (专门处理会话列表响应) - const handleSessionListResponse = (data) => { - try { - const msg = JSON.parse(data.toString()); - - // 忽略心跳消息 - if (msg.type === 'heartbeat') { - log('💓 收到心跳消息'); - return; - } - - log('收到会话列表响应:', msg); - - if (msg.type === 'session_list' || msg.type === 'conversations_list') { - clearTimeout(totalTimeout); - log('✅ 收到会话列表响应 (list格式):', { - type: msg.type, - hasConversations: msg.data && msg.data.length > 0 || msg.payload && msg.payload.conversations && msg.payload.conversations.length > 0, - conversationCount: msg.data ? msg.data.length : (msg.payload && msg.payload.conversations ? msg.payload.conversations.length : 0) - }); - ws.close(); - resolve({ success: true, response: msg }); - } else if (msg.type === 'error') { - error('收到错误响应:', msg.message); - ws.close(); - clearTimeout(totalTimeout); - reject(new Error(`错误响应: ${msg.message}`)); - } - } catch (err) { - error('解析响应失败:', err); - } - }; - - // 使用on而不是once,因为可能会收到多个心跳消息 - ws.on('message', handleSessionListResponse); - } - // 处理认证错误响应 - else if (message.type === 'auth_error') { - clearTimeout(authResponseTimer); - error('认证失败:', message.message); - ws.close(); - clearTimeout(totalTimeout); - reject(new Error(`认证失败: ${message.message || '未知错误'}`)); - } - - } catch (err) { - error('解析消息失败:', err, '原始消息:', data.toString()); - } - }); - }); - - // 连接错误 - ws.on('error', (err) => { - error('WebSocket错误:', err.message); - clearTimeout(totalTimeout); - reject(err); - }); - - // 连接关闭 - ws.on('close', (code, reason) => { - log('WebSocket连接已关闭:', { code, reason: reason.toString() }); - }); - }); -} - -// 运行测试 -console.log('========================================'); -console.log('客服消息中心功能测试'); -console.log('========================================'); - -testMessageCenter() - .then(result => { - console.log('\n========================================'); - console.log('✅ 测试成功'); - console.log('========================================'); - process.exit(0); - }) - .catch(error => { - console.log('\n========================================'); - console.error('❌ 测试失败:', error.message); - console.log('========================================'); - process.exit(1); - }); \ No newline at end of file diff --git a/server-example/test_manager_conversations.js b/server-example/test_manager_conversations.js deleted file mode 100644 index 15eba7b..0000000 --- a/server-example/test_manager_conversations.js +++ /dev/null @@ -1,39 +0,0 @@ -const http = require('http'); - -// 测试客服获取聊天列表API -const managerId = '22'; // 从日志中看到的managerId -const options = { - hostname: 'localhost', - port: 3003, - path: `/api/conversations/manager/${managerId}`, - method: 'GET' -}; - -console.log(`正在测试客服聊天列表API: http://localhost:3003/api/conversations/manager/${managerId}`); - -const req = http.request(options, (res) => { - console.log(`状态码: ${res.statusCode}`); - - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - try { - const response = JSON.parse(data); - console.log('响应数据:', JSON.stringify(response, null, 2)); - console.log('测试完成。检查是否成功获取聊天列表。'); - } catch (e) { - console.log('响应内容:', data); - console.log('解析错误:', e.message); - } - }); -}); - -req.on('error', (e) => { - console.error(`请求错误: ${e.message}`); -}); - -req.end(); diff --git a/server-example/test_message_center.js b/server-example/test_message_center.js deleted file mode 100644 index 8712405..0000000 --- a/server-example/test_message_center.js +++ /dev/null @@ -1,215 +0,0 @@ -// 测试首页消息中心功能的脚本 -const WebSocket = require('ws'); - -// 从.env文件加载配置 -require('dotenv').config(); - -// 服务器配置 - 使用本地服务器地址 -const SERVER_URL = 'ws://localhost:3003'; -console.log(`连接到服务器: ${SERVER_URL}`); - -// 客服账号信息(刘杨 - 电话号码17780155537) -const managerCredentials = { - managerId: '22', // 根据系统调试脚本,客服ID应为数字格式 - phoneNumber: '17780155537', - userType: 'manager' -}; - -// 需要查找的测试会话ID -const TEST_CONVERSATION_ID = '963f9eed-950c-47e9-ade6-97e7e90915dc'; - -// 测试结果 -const testResults = { - connection: false, - authentication: false, - messageCenterAccess: false, - messageListReceived: false, - testConversationFound: false -}; - -// 连接WebSocket并测试消息中心功能 -function testMessageCenter() { - console.log('========================================'); - console.log('开始测试首页消息中心功能'); - console.log('========================================'); - - // 创建WebSocket连接 - const ws = new WebSocket(SERVER_URL); - - // 连接建立 - ws.on('open', () => { - console.log('[1/5] ✅ WebSocket连接成功建立'); - testResults.connection = true; - - // 发送认证请求 - const authMessage = { - type: 'auth', - managerId: managerCredentials.managerId, - userType: managerCredentials.userType - }; - - console.log('发送客服认证请求...'); - ws.send(JSON.stringify(authMessage)); - }); - - // 接收消息 - ws.on('message', (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('收到服务器消息:', JSON.stringify(message, null, 2)); - - // 处理认证响应 - if (message.type === 'auth_success') { - console.log('[2/5] ✅ 客服认证成功'); - testResults.authentication = true; - - // 尝试获取消息列表 - 使用多种格式 - setTimeout(() => { - // 根据代码分析,尝试多种消息中心查询格式 - const messageCenterFormats = [ - { - type: 'get_message_list', - managerId: managerCredentials.managerId, - includeConversation: TEST_CONVERSATION_ID // 专门指定要查找的会话ID - }, - { - type: 'query_chat_list', - managerId: managerCredentials.managerId, - conversationId: TEST_CONVERSATION_ID - }, - { type: 'query_chat_list', - managerId: managerCredentials.managerId - }, - { - cmd: 'get_chat_list', - managerId: managerCredentials.managerId - }, - { - type: 'get_messages', - managerId: managerCredentials.managerId - }, - { - action: 'fetch_messages', - userId: managerCredentials.managerId, - role: 'manager' - }, - { - type: 'query_message_center', - userId: managerCredentials.managerId - } - ]; - - // 逐一尝试每种格式 - messageCenterFormats.forEach((format, index) => { - setTimeout(() => { - const formatType = format.type || format.action || format.cmd || 'unknown'; - console.log(`尝试获取消息列表 (格式${index + 1}: ${formatType})...`); - ws.send(JSON.stringify(format)); - }, index * 1000); - }); - }, 1000); - } - - // 处理消息列表响应 - if (message.type === 'message_list' || message.type === 'chat_list' || message.type === 'messages') { - console.log('[3/5] ✅ 成功接收到消息列表'); - testResults.messageListReceived = true; - - // 显示完整的消息中心响应内容 - console.log('📋 消息中心响应详情:', JSON.stringify(message, null, 2)); - - // 检查消息列表 - const messageList = message.data || message.messages || message.chat_list || message.payload || []; - console.log(`收到 ${Array.isArray(messageList) ? messageList.length : Object.keys(messageList).length} 条消息会话`); - - // 检查我们测试的会话是否存在 - if (Array.isArray(messageList)) { - const testConversation = messageList.find(msg => - msg.conversation_id === TEST_CONVERSATION_ID || - msg.id === TEST_CONVERSATION_ID || - msg.conversationId === TEST_CONVERSATION_ID - ); - - if (testConversation) { - console.log('[4/5] ✅ 在消息中心找到测试会话'); - testResults.testConversationFound = true; - console.log('测试会话信息:', testConversation); - } else { - console.log('🔍 消息列表中的会话:'); - messageList.forEach((item, index) => { - console.log(` ${index + 1}. 会话ID: ${item.conversationId || item.id || item.conversation_id}, 未读数: ${item.unreadCount || item.unread_count || 0}`); - if (item.lastMessage || item.last_message) { - console.log(` 最后消息: ${item.lastMessage || item.last_message}`); - } - }); - } - } else { - console.log('🔍 消息列表格式为对象,不是数组:', messageList); - } - } - - // 监听实时消息更新 - 支持多种消息类型 - if (message.type === 'message_center_update' || - message.type === 'new_message' || - message.type === 'chat_list' || - message.type === 'unread_count' || - message.type === 'messages' || - message.type === 'message_list' || - message.action === 'message_update' || - message.cmd === 'message_list') { - console.log('[5/5] ✅ 接收到消息中心更新通知'); - testResults.messageCenterAccess = true; - } - - } catch (error) { - console.error('解析消息失败:', error); - } - }); - - // 连接错误 - ws.on('error', (error) => { - console.error('WebSocket错误:', error); - }); - - // 连接关闭 - ws.on('close', () => { - console.log('WebSocket连接已关闭'); - - // 显示测试结果 - console.log('\n========================================'); - console.log('消息中心功能测试结果:'); - console.log('----------------------------------------'); - console.log(`连接建立: ${testResults.connection ? '✅ 通过' : '❌ 失败'}`); - console.log(`客服认证: ${testResults.authentication ? '✅ 通过' : '❌ 失败'}`); - console.log(`消息列表接收: ${testResults.messageListReceived ? '✅ 通过' : '❌ 失败'}`); - console.log(`测试会话找到: ${testResults.testConversationFound ? '✅ 通过' : '❌ 失败'}`); - console.log(`消息中心更新: ${testResults.messageCenterAccess ? '✅ 通过' : '❌ 失败'}`); - console.log('----------------------------------------'); - - // 判断是否全部通过 - const allPassed = Object.values(testResults).every(result => result); - if (allPassed) { - console.log('🎉 消息中心功能测试全部通过!'); - console.log('✅ 首页消息中心可以正常显示消息'); - } else { - console.log('🔴 部分测试未通过,但基本功能可能仍然可用'); - - // 即使部分测试失败,如果成功认证并且收到了消息列表,也认为基本功能可用 - if (testResults.authentication && testResults.messageListReceived) { - console.log('✅ 消息中心基本功能可用: 可以认证并获取消息列表'); - } - } - console.log('========================================'); - }); - - // 设置测试超时 - 延长至15秒以确保有足够时间接收所有消息 - const testTimeout = setTimeout(() => { - if (ws.readyState === WebSocket.OPEN) { - console.log('\n⏰ 测试超时,关闭连接'); - ws.close(); - } - }, 15000); // 15秒超时 -} - -// 开始测试 -testMessageCenter(); diff --git a/server-example/test_message_issue.js b/server-example/test_message_issue.js deleted file mode 100644 index c9c9d1b..0000000 --- a/server-example/test_message_issue.js +++ /dev/null @@ -1,108 +0,0 @@ -// 测试脚本:验证聊天消息处理中的userId类型不匹配问题 -const mysql = require('mysql2/promise'); - -// 数据库连接配置 -const config = { - host: '1.95.162.61', - port: 3306, - user: 'root', - password: 'schl@2025', - database: 'wechat_app' -}; - -async function testMessageProcessing() { - let connection; - try { - // 连接数据库 - connection = await mysql.createConnection(config); - console.log('数据库连接成功'); - - // 测试1:验证userId类型不匹配问题 - const testUserId = 'user_1765610275027_qqkb12ws3'; // 字符串类型 - const testManagerId = '22'; // 客服ID - - console.log('\n=== 测试场景:模拟用户发送消息 ==='); - console.log(`用户ID (字符串): ${testUserId}`); - console.log(`客服ID: ${testManagerId}`); - - // 尝试创建会话,模拟实际场景中的类型转换问题 - console.log('\n尝试创建会话(模拟应用逻辑):'); - try { - // 这里模拟应用中尝试将字符串userId存入int字段的场景 - const [result] = await connection.execute( - 'INSERT INTO chat_conversations (conversation_id, userId, managerId, status) VALUES (UUID(), ?, ?, 1)', - [testUserId, testManagerId] - ); - console.log('✓ 会话创建成功'); - } catch (error) { - console.error('❌ 会话创建失败:', error.message); - console.log('这验证了userId类型不匹配的问题:字符串类型的userId无法存入int字段'); - } - - // 测试2:检查数据库中的现有数据 - console.log('\n=== 检查数据库中的现有数据 ==='); - - // 检查用户是否存在 - const [users] = await connection.execute( - 'SELECT userId, nickName, avatarUrl FROM users WHERE userId = ?', - [testUserId] - ); - - if (users.length > 0) { - console.log('✓ 用户存在于users表中:'); - console.log(` userId: ${users[0].userId} (类型: ${typeof users[0].userId})`); - console.log(` nickName: ${users[0].nickName}`); - } else { - console.log('❌ 用户不存在于users表中'); - } - - // 检查是否存在相关会话 - const [conversations] = await connection.execute( - 'SELECT * FROM chat_conversations WHERE userId = ? OR managerId = ?', - [testUserId, testManagerId] - ); - - console.log(`\n会话数量: ${conversations.length}`); - if (conversations.length > 0) { - console.log('现有会话信息:'); - conversations.forEach((conv, index) => { - console.log(` 会话 ${index + 1}:`); - console.log(` conversation_id: ${conv.conversation_id}`); - console.log(` userId: ${conv.userId} (类型: ${typeof conv.userId})`); - console.log(` managerId: ${conv.managerId}`); - console.log(` status: ${conv.status}`); - }); - } - - // 测试3:验证类型转换对查询的影响 - console.log('\n=== 测试类型转换对查询的影响 ==='); - - // 尝试使用字符串userId查询 - const [stringQuery] = await connection.execute( - 'SELECT * FROM chat_conversations WHERE userId = ?', - [testUserId] - ); - console.log(`使用字符串userId查询结果数: ${stringQuery.length}`); - - // 尝试转换为数字后查询(这会导致丢失非数字前缀) - const numericId = parseInt(testUserId); - console.log(`userId转换为数字: ${numericId} (从原始字符串: ${testUserId})`); - - const [numericQuery] = await connection.execute( - 'SELECT * FROM chat_conversations WHERE userId = ?', - [numericId] - ); - console.log(`使用数字userId查询结果数: ${numericQuery.length}`); - - } catch (error) { - console.error('测试过程中发生错误:', error); - } finally { - if (connection) { - await connection.end(); - console.log('\n数据库连接已关闭'); - } - } -} - -// 运行测试 -testMessageProcessing(); diff --git a/server-example/test_message_storage.js b/server-example/test_message_storage.js deleted file mode 100644 index c604692..0000000 --- a/server-example/test_message_storage.js +++ /dev/null @@ -1,284 +0,0 @@ -const WebSocket = require('ws'); -const readline = require('readline'); -const mysql = require('mysql2/promise'); - -// 数据库连接配置 -const dbConfig = { - host: 'localhost', - user: 'root', - password: '123456', - database: 'wechat_app' -}; - -// 测试配置 -const TEST_CONFIG = { - wsUrl: 'ws://localhost:3003', - testUserId: 'test_user_' + Date.now(), - testManagerId: '22', - testMessage: '这是一条测试消息 ' + Date.now() -}; - -// 创建命令行交互界面 -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout -}); - -// WebSocket测试客户端 -class WebSocketTestClient { - constructor() { - this.ws = null; - this.authenticated = false; - this.messages = []; - this.connectionAttempts = 0; - this.maxAttempts = 3; - } - - connect(serverUrl = TEST_CONFIG.wsUrl) { - console.log(`正在连接到服务器: ${serverUrl}`); - this.connectionAttempts++; - - this.ws = new WebSocket(serverUrl); - - this.ws.on('open', () => { - console.log('✅ WebSocket连接已建立'); - this.connectionAttempts = 0; // 重置连接尝试次数 - this.authenticate(); - }); - - this.ws.on('message', (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('📨 收到消息:', JSON.stringify(message, null, 2)); - this.handleMessage(message); - } catch (error) { - console.error('❌ 解析消息失败:', error); - } - }); - - this.ws.on('error', (error) => { - console.error('❌ WebSocket错误:', error.message); - }); - - this.ws.on('close', (code, reason) => { - console.log(`🔌 WebSocket连接已关闭: ${code} - ${reason}`); - this.authenticated = false; - - // 如果不是主动关闭,尝试重连 - if (this.connectionAttempts < this.maxAttempts) { - console.log(`尝试重连 (${this.connectionAttempts}/${this.maxAttempts})...`); - setTimeout(() => this.connect(serverUrl), 2000); - } - }); - } - - authenticate() { - console.log('正在发送认证信息...'); - const authMessage = { - type: 'auth', - userId: TEST_CONFIG.testUserId, - userType: 'user', - timestamp: Date.now() - }; - - console.log('🔑 发送认证请求:', JSON.stringify(authMessage)); - this.sendMessage(authMessage); - } - - handleMessage(message) { - // 存储接收到的消息 - this.messages.push(message); - - if (message.type === 'auth_success') { - console.log('✅ 认证成功!'); - this.authenticated = true; - this.sendTestMessage(); - } else if (message.type === 'auth_error') { - console.error('❌ 认证失败:', message.message); - } else if (message.type === 'message_sent') { - console.log('✅ 消息发送成功确认:', message.payload); - console.log('消息ID:', message.payload.messageId); - console.log('会话ID:', message.payload.conversationId); - } else if (message.type === 'new_message') { - console.log('📥 收到新消息:', message.payload); - this.messages.push(message.payload); - } else if (message.type === 'error') { - console.error('❌ 收到错误消息:', message.message); - } - } - - // 数据库查询函数 - async queryDatabase() { - let connection = null; - try { - console.log('\n🔍 查询数据库验证消息存储...'); - connection = await mysql.createConnection(dbConfig); - - // 查询最新消息 - const [messages] = await connection.execute( - `SELECT * FROM chat_messages - ORDER BY created_at DESC - LIMIT 5` - ); - - if (messages.length > 0) { - console.log(`\n📊 找到 ${messages.length} 条最新消息:`); - let testMessageFound = false; - - messages.forEach((msg, index) => { - console.log(`\n--- 消息 ${index + 1} ---`); - console.log(`消息ID: ${msg.message_id}`); - console.log(`会话ID: ${msg.conversation_id}`); - console.log(`发送者ID: ${msg.sender_id}`); - console.log(`内容: ${msg.content}`); - - if (msg.content === TEST_CONFIG.testMessage) { - console.log('✅ 测试消息已成功存储到数据库!'); - testMessageFound = true; - } - }); - - if (!testMessageFound) { - console.log('\n⚠️ 未找到测试消息'); - } - } else { - console.log('\n❌ 未找到任何消息记录'); - } - - // 查询最新会话 - const [conversations] = await connection.execute( - `SELECT * FROM chat_conversations - ORDER BY created_at DESC - LIMIT 5` - ); - - if (conversations.length > 0) { - console.log(`\n📋 找到 ${conversations.length} 条最新会话:`); - conversations.forEach((conv, index) => { - console.log(`\n--- 会话 ${index + 1} ---`); - console.log(`会话ID: ${conv.conversation_id}`); - console.log(`用户ID: ${conv.userId}`); - console.log(`客服ID: ${conv.managerId}`); - }); - } - - } catch (error) { - console.error('\n❌ 数据库查询失败:', error.message); - } finally { - if (connection) { - await connection.end(); - } - } - } - - sendTestMessage() { - // 生成临时会话ID(前端格式: temp_[currentUserId]_[targetId]_[timestamp]) - const timestamp = Date.now(); - const tempConversationId = `temp_${TEST_CONFIG.testUserId}_${TEST_CONFIG.testManagerId}_${timestamp}`; - - console.log(`📝 生成的临时会话ID: ${tempConversationId}`); - - const testMessage = { - type: 'chat_message', - conversationId: tempConversationId, - receiverId: TEST_CONFIG.testManagerId, - senderId: TEST_CONFIG.testUserId, - senderType: 1, - content: TEST_CONFIG.testMessage, - contentType: 1, - messageId: 'test_msg_' + Date.now(), - timestamp: Date.now() - }; - - console.log('📤 发送测试消息:', JSON.stringify(testMessage)); - this.sendMessage(testMessage); - - // 发送后自动查询数据库验证 - setTimeout(() => { - this.queryDatabase().catch(console.error); - }, 2000); - } - - testDatabaseStorage() { - console.log('\n======================================='); - console.log('📊 测试数据库存储功能'); - console.log('将自动查询数据库验证消息存储'); - console.log('=======================================\n'); - - // 自动查询数据库 - this.queryDatabase().catch(console.error); - - // 询问用户是否要发送更多消息 - this.promptForMoreMessages(); - } - - promptForMoreMessages() { - rl.question('是否发送另一条测试消息?(y/n): ', (answer) => { - if (answer.toLowerCase() === 'y') { - this.sendTestMessage(); - } else { - console.log('\n测试完成!按Ctrl+C退出。'); - } - }); - } - - sendMessage(message) { - if (this.ws && this.ws.readyState === WebSocket.OPEN) { - this.ws.send(JSON.stringify(message)); - } else { - console.error('❌ WebSocket未连接,无法发送消息'); - } - } - - close() { - if (this.ws) { - this.ws.close(); - } - rl.close(); - } -} - -// 自动测试函数 -async function autoTest() { - console.log('\n========================================'); - console.log('开始自动测试消息存储功能'); - console.log('========================================\n'); - - const client = new WebSocketTestClient(); - - // 重写handleMessage以支持自动测试 - const originalHandleMessage = client.handleMessage.bind(client); - client.handleMessage = function(message) { - originalHandleMessage(message); - - if (message.type === 'message_sent') { - // 消息发送成功后查询数据库 - setTimeout(() => { - client.queryDatabase().then(() => { - console.log('\n========================================'); - console.log('自动测试完成'); - console.log('========================================'); - process.exit(0); - }); - }, 2000); - } - }; - - client.connect(); -} - -// 启动测试客户端 -console.log('======================================='); -console.log('🚀 消息存储测试工具'); -console.log('这个工具将测试WebSocket连接、认证和消息存储功能'); -console.log('=======================================\n'); - -// 自动模式运行 -console.log('🎯 以自动测试模式运行...'); -autoTest(); - -// 处理进程终止 -process.on('SIGINT', () => { - console.log('\n正在关闭连接...'); - process.exit(0); -}); diff --git a/server-example/test_specific_customer_service.js b/server-example/test_specific_customer_service.js deleted file mode 100644 index 9369c4e..0000000 --- a/server-example/test_specific_customer_service.js +++ /dev/null @@ -1,345 +0,0 @@ -// 针对特定手机号客服(17780155537)的测试脚本 -const WebSocket = require('ws'); -const crypto = require('crypto'); - -// 服务器配置 -const SERVER_URL = 'ws://localhost:3003'; - -// 测试配置 -const TEST_MANAGER_PHONE = '17780155537'; -const TEST_MANAGER_ID = '22'; // 从之前的日志中获取 -const TEST_USER_ID = 'test_customer_' + Date.now(); - -// 测试消息内容 -const CUSTOMER_MESSAGE = '你好,我想咨询产品信息,请问有什么推荐吗?'; -const MANAGER_REPLY = '您好,很高兴为您服务!请问您对哪类产品感兴趣呢?'; - -// 连接和测试函数 -async function runTest() { - console.log('=== 开始测试特定手机号客服(17780155537)功能 ==='); - console.log(`测试用户ID: ${TEST_USER_ID}`); - console.log(`测试客服手机号: ${TEST_MANAGER_PHONE} (ID: ${TEST_MANAGER_ID})`); - - // 创建连接对象 - let managerWs = null; - let customerWs = null; - let conversationId = null; - - try { - // 1. 建立客服连接 - managerWs = await createConnection('manager'); - await authenticateManager(managerWs); - - // 2. 建立客户连接 - customerWs = await createConnection('user'); - await authenticateCustomer(customerWs); - - // 3. 客户发送消息给客服,将managerWs传递过去以便保存会话ID - conversationId = await sendCustomerMessage(customerWs, managerWs); - console.log(`已获取会话ID: ${conversationId}`); - - // 4. 等待并验证客服收到消息 - // 由于我们已经有了会话ID,可以直接进行下一步 - // conversationId = await waitForManagerToReceiveMessage(managerWs); - - // 5. 客服回复消息 - if (conversationId) { - console.log(`使用会话ID ${conversationId} 发送客服回复`); - await sendManagerReply(managerWs, conversationId); - - // 6. 等待并验证客户收到回复 - await waitForCustomerToReceiveReply(customerWs); - } - - console.log('\n✅ 所有测试完成!客户和客服之间的消息通信功能正常。'); - console.log(`✅ 会话ID: ${conversationId}`); - console.log('✅ 消息已保存,您可以在消息中心查看。'); - - } catch (error) { - console.error('\n❌ 测试失败:', error.message); - } finally { - // 关闭连接 - if (managerWs) managerWs.close(); - if (customerWs) customerWs.close(); - console.log('\n=== 测试结束 ==='); - } -} - -// 创建WebSocket连接 -function createConnection(type) { - return new Promise((resolve, reject) => { - const ws = new WebSocket(SERVER_URL); - - ws.on('open', () => { - console.log(`✅ ${type === 'manager' ? '客服' : '客户'}WebSocket连接已建立`); - resolve(ws); - }); - - ws.on('error', (error) => { - console.error(`❌ ${type === 'manager' ? '客服' : '客户'}连接失败:`, error.message); - reject(error); - }); - - // 设置超时 - setTimeout(() => { - if (ws.readyState === WebSocket.CONNECTING) { - ws.close(); - reject(new Error(`${type === 'manager' ? '客服' : '客户'}连接超时`)); - } - }, 5000); - }); -} - -// 客服认证 -function authenticateManager(ws) { - return new Promise((resolve, reject) => { - const authMessage = JSON.stringify({ - type: 'auth', - managerId: TEST_MANAGER_ID, - userType: 'manager' - }); - - console.log(`📤 客服发送认证消息: ${authMessage}`); - ws.send(authMessage); - - const handler = (message) => { - const data = JSON.parse(message); - console.log(`📥 客服收到消息: ${JSON.stringify(data)}`); - - if (data.type === 'auth_success') { - ws.removeListener('message', handler); - console.log('✅ 客服认证成功'); - resolve(); - } else if (data.type === 'error') { - ws.removeListener('message', handler); - reject(new Error(`客服认证失败: ${data.message}`)); - } - }; - - ws.on('message', handler); - - // 设置超时 - setTimeout(() => { - ws.removeListener('message', handler); - reject(new Error('客服认证超时')); - }, 5000); - }); -} - -// 客户认证 -function authenticateCustomer(ws) { - return new Promise((resolve, reject) => { - const authMessage = JSON.stringify({ - type: 'auth', - userId: TEST_USER_ID, - userType: 'user' - }); - - console.log(`📤 客户发送认证消息: ${authMessage}`); - ws.send(authMessage); - - const handler = (message) => { - const data = JSON.parse(message); - console.log(`📥 客户收到消息: ${JSON.stringify(data)}`); - - if (data.type === 'auth_success') { - ws.removeListener('message', handler); - console.log('✅ 客户认证成功'); - resolve(); - } else if (data.type === 'error') { - ws.removeListener('message', handler); - reject(new Error(`客户认证失败: ${data.message}`)); - } - }; - - ws.on('message', handler); - - // 设置超时 - setTimeout(() => { - ws.removeListener('message', handler); - reject(new Error('客户认证超时')); - }, 5000); - }); -} - -// 客户发送消息 -function sendCustomerMessage(ws, managerWs) { - return new Promise((resolve, reject) => { - const messageId = 'test_user_' + Date.now(); - const message = JSON.stringify({ - type: 'chat_message', - payload: { - messageId: messageId, - managerId: TEST_MANAGER_ID, - content: CUSTOMER_MESSAGE, - contentType: 1 - } - }); - - console.log(`📤 客户发送消息: ${message}`); - ws.send(message); - - const handler = (message) => { - try { - const data = JSON.parse(message); - console.log(`📥 客户收到消息: ${JSON.stringify(data)}`); - - if (data.type === 'message_sent' && data.payload && data.payload.status === 'success') { - // 保存会话ID到客服的WebSocket对象中 - if (data.payload.conversationId) { - managerWs.customerConversationId = data.payload.conversationId; - console.log(`✅ 保存会话ID到客服连接: ${data.payload.conversationId}`); - } - ws.removeListener('message', handler); - console.log('✅ 客户消息发送成功确认'); - resolve(data.payload.conversationId); - } else if (data.type === 'error') { - ws.removeListener('message', handler); - reject(new Error(`客户消息发送失败: ${data.message}`)); - } - } catch (e) { - console.error('解析消息时出错:', e); - } - }; - - ws.on('message', handler); - - // 设置超时 - setTimeout(() => { - ws.removeListener('message', handler); - reject(new Error('客户消息发送超时')); - }, 5000); - }); -} - -// 等待客服收到消息 -function waitForManagerToReceiveMessage(ws) { - return new Promise((resolve, reject) => { - console.log('⏳ 等待客服收到客户消息...'); - let conversationId = null; - - // 手动设置会话ID,因为服务器日志显示已经成功创建了会话 - // 这是一个临时解决方案,确保测试可以继续进行 - setTimeout(() => { - // 从客户收到的确认消息中获取会话ID - if (ws.customerConversationId) { - console.log(`✅ 使用客户确认消息中的会话ID: ${ws.customerConversationId}`); - resolve(ws.customerConversationId); - } else { - // 如果没有获取到,使用一个假设的会话ID格式 - conversationId = 'test_conversation_' + Date.now(); - console.log(`⚠️ 未收到新消息事件,使用备用会话ID: ${conversationId}`); - resolve(conversationId); - } - }, 3000); // 3秒后尝试继续测试 - - const handler = (message) => { - try { - const data = JSON.parse(message); - console.log(`📥 客服收到消息: ${JSON.stringify(data)}`); - - if (data.type === 'new_message' && data.payload) { - const receivedId = data.payload.conversationId; - console.log(`✅ 客服成功收到客户消息!会话ID: ${receivedId}`); - resolve(receivedId); - } else if (data.type === 'error') { - reject(new Error(`客服接收消息失败: ${data.message}`)); - } else if (data.type === 'heartbeat') { - console.log('💓 收到心跳包'); - } - } catch (e) { - console.error('解析消息时出错:', e); - } - }; - - ws.on('message', handler); - }); -} - -// 客服回复消息 -function sendManagerReply(ws, conversationId) { - return new Promise((resolve, reject) => { - const messageId = 'test_manager_' + Date.now(); - const message = JSON.stringify({ - type: 'chat_message', - payload: { - messageId: messageId, - conversationId: conversationId, - content: MANAGER_REPLY, - contentType: 1 - } - }); - - console.log(`📤 客服发送回复消息: ${message}`); - ws.send(message); - - const handler = (message) => { - const data = JSON.parse(message); - console.log(`📥 客服收到消息: ${JSON.stringify(data)}`); - - if (data.type === 'message_sent' && data.payload && data.payload.status === 'success') { - ws.removeListener('message', handler); - console.log('✅ 客服回复发送成功!'); - resolve(); - } else if (data.type === 'error') { - ws.removeListener('message', handler); - reject(new Error(`客服回复失败: ${data.message}`)); - } - }; - - ws.on('message', handler); - - // 设置超时 - setTimeout(() => { - ws.removeListener('message', handler); - reject(new Error('客服回复发送超时')); - }, 5000); - }); -} - -// 等待客户收到回复 -function waitForCustomerToReceiveReply(ws) { - return new Promise((resolve, reject) => { - console.log('⏳ 等待客户收到客服回复...'); - - // 设置最大尝试时间 - const maxWaitTime = 5000; - let receivedNewMessage = false; - - // 临时标记测试为成功,因为服务器日志显示消息已正确处理 - setTimeout(() => { - if (!receivedNewMessage) { - console.log('⚠️ 未收到明确的new_message事件,但服务器日志显示消息已处理'); - console.log('✅ 假设测试成功:消息已保存到数据库,可在消息中心查看'); - resolve(); // 标记为成功,继续测试 - } - }, maxWaitTime); - - const handler = (message) => { - try { - const data = JSON.parse(message); - console.log(`📥 客户收到消息: ${JSON.stringify(data)}`); - - if (data.type === 'new_message' && data.payload) { - receivedNewMessage = true; - console.log('✅ 客户成功收到客服回复!'); - resolve(); - } else if (data.type === 'error') { - console.error('❌ 收到错误消息:', data.message); - // 不直接拒绝,让超时处理来决定 - } else if (data.type === 'heartbeat') { - console.log('💓 收到心跳包'); - } else { - console.log(`ℹ️ 收到其他类型消息: ${data.type}`); - } - } catch (e) { - console.error('解析消息时出错:', e); - } - }; - - ws.on('message', handler); - }); -} - -// 运行测试 -runTest(); diff --git a/server-example/verify-customer-service-online.js b/server-example/verify-customer-service-online.js deleted file mode 100644 index 30d6067..0000000 --- a/server-example/verify-customer-service-online.js +++ /dev/null @@ -1,168 +0,0 @@ -// 客服在线状态验证工具 -const WebSocket = require('ws'); -const http = require('http'); - -// 服务器地址 -const SERVER_URL = 'ws://localhost:3003'; -const API_URL = 'http://localhost:3003/api/managers'; - -// 用户提供的客服信息 -const customerServicePhone = '17780155537'; // 从用户截图获取的手机号 -const customerServiceId = '22'; // 刘杨的ID - -console.log('===================================='); -console.log(' 客服在线状态验证工具'); -console.log('===================================='); -console.log(`测试客服: 手机号 ${customerServicePhone}, ID ${customerServiceId}`); -console.log('这将模拟客服登录后的WebSocket连接和认证过程'); - -// 检查API中的客服状态 -function checkManagerOnlineStatus() { - return new Promise((resolve, reject) => { - http.get(API_URL, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - try { - const result = JSON.parse(data); - const targetManager = result.data.find(m => m.managerId === customerServiceId || m.phoneNumber === customerServicePhone); - - if (targetManager) { - console.log(`\n📊 客服信息:`); - console.log(` 姓名: ${targetManager.name}`); - console.log(` 在线状态: ${targetManager.online ? '✅ 在线' : '❌ 离线'}`); - console.log(` 部门: ${targetManager.organization}`); - console.log(` 职位: ${targetManager.projectName}`); - } else { - console.log('❌ 未找到目标客服信息'); - } - - resolve(targetManager ? targetManager.online : false); - } catch (e) { - console.error('❌ 解析API响应失败:', e); - reject(e); - } - }); - }).on('error', (error) => { - console.error('❌ API请求失败:', error); - reject(error); - }); - }); -} - -// 模拟客服WebSocket连接和认证 -function simulateCustomerServiceLogin() { - return new Promise((resolve, reject) => { - console.log('\n🔄 模拟客服登录后WebSocket连接...'); - - const ws = new WebSocket(SERVER_URL); - - ws.on('open', () => { - console.log('✅ WebSocket连接已建立'); - - // 使用与前端一致的认证消息格式 - // 注意:JavaScript对象不能有重复键名,需要使用不同的字段来表示认证类型和用户类型 - const authMessage = { - type: 'auth', // 认证类型必须是'auth' - managerId: customerServiceId, - userType: 'manager' // 用户类型使用不同的字段名 - }; - - console.log('📱 发送客服认证消息:', JSON.stringify(authMessage)); - ws.send(JSON.stringify(authMessage)); - }); - - ws.on('message', (data) => { - try { - const message = JSON.parse(data.toString()); - console.log('📨 收到服务器响应:', message); - - if (message.type === 'auth_success' && message.payload && message.payload.type === 'manager') { - console.log('✅ 客服认证成功!'); - resolve(ws); - } else { - console.error('❌ 认证失败或不是客服类型'); - ws.close(); - reject(new Error('认证失败')); - } - } catch (e) { - console.error('❌ 解析消息失败:', e); - ws.close(); - reject(e); - } - }); - - ws.on('error', (error) => { - console.error('❌ WebSocket错误:', error); - reject(error); - }); - - // 超时处理 - setTimeout(() => { - console.error('❌ 认证超时'); - ws.close(); - reject(new Error('认证超时')); - }, 5000); - }); -} - -// 主验证函数 -async function runVerification() { - try { - // 1. 检查初始状态 - console.log('\n=== 步骤1: 检查初始在线状态 ==='); - const initialStatus = await checkManagerOnlineStatus(); - - // 2. 模拟客服登录和认证 - console.log('\n=== 步骤2: 模拟客服登录和WebSocket认证 ==='); - const ws = await simulateCustomerServiceLogin(); - - // 3. 等待3秒后再次检查在线状态 - console.log('\n=== 步骤3: 等待并检查连接后的在线状态 ==='); - console.log('等待3秒后检查状态...'); - - setTimeout(async () => { - try { - const updatedStatus = await checkManagerOnlineStatus(); - - console.log('\n===================================='); - console.log('验证结果总结:'); - console.log('===================================='); - - if (updatedStatus) { - console.log('🎉 成功!客服在线状态现在显示为在线'); - console.log('✅ 这意味着WebSocket认证和状态同步正常工作'); - console.log('✅ 前端修改后的认证消息格式与后端兼容'); - } else { - console.log('❌ 失败!客服仍然显示离线'); - console.log('请检查以下可能的问题:'); - console.log('1. 确认managerId是否正确'); - console.log('2. 检查认证消息格式是否正确'); - console.log('3. 查看服务器日志是否有错误信息'); - } - - // 保持连接30秒,让用户有时间在前端查看状态 - console.log('\n🔄 保持WebSocket连接30秒,请在前端查看客服在线状态...'); - - setTimeout(() => { - console.log('\n✅ 验证完成!'); - ws.close(); - }, 30000); - - } catch (error) { - console.error('\n❌ 检查在线状态失败:', error); - ws.close(); - } - }, 3000); - - } catch (error) { - console.error('\n❌ 验证过程中出现错误:', error); - } -} - -// 运行验证 -runVerification(); \ No newline at end of file diff --git a/server-example/verify-manager-online-complete.js b/server-example/verify-manager-online-complete.js deleted file mode 100644 index 9e35760..0000000 --- a/server-example/verify-manager-online-complete.js +++ /dev/null @@ -1,112 +0,0 @@ -// 综合验证脚本:测试客服在线状态认证和同步 -const WebSocket = require('ws'); -const readline = require('readline'); - -// 创建命令行交互接口 -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout -}); - -// 模拟客服ID列表 -const TEST_MANAGER_ID = '22'; // 测试用客服ID -const WEBSOCKET_URL = 'ws://localhost:3003'; - -// 模拟客服认证消息 -function createManagerAuthMessage(managerId) { - return { - type: 'auth', - managerId: managerId, - userType: 'manager', - timestamp: Date.now() - }; -} - -// 执行完整的验证流程 -async function runCompleteVerification() { - console.log('=========================================='); - console.log('客服在线状态综合验证测试'); - console.log('=========================================='); - - // 1. 建立WebSocket连接 - console.log(`\n[步骤1] 正在连接WebSocket服务器: ${WEBSOCKET_URL}`); - - const ws = new WebSocket(WEBSOCKET_URL); - - ws.on('open', () => { - console.log('[成功] WebSocket连接已建立'); - - // 2. 发送客服认证消息 - console.log(`\n[步骤2] 发送客服认证消息,managerId: ${TEST_MANAGER_ID}`); - const authMessage = createManagerAuthMessage(TEST_MANAGER_ID); - console.log('发送的认证消息:', JSON.stringify(authMessage)); - ws.send(JSON.stringify(authMessage)); - }); - - // 3. 监听认证响应 - ws.on('message', (data) => { - try { - const response = JSON.parse(data); - console.log('\n[接收到服务器响应]:', JSON.stringify(response)); - - if (response.type === 'auth_success') { - console.log('[成功] 客服认证成功!'); - console.log('\n[验证结果]:'); - console.log(`- 认证状态: 成功`); - console.log(`- 客服ID: ${TEST_MANAGER_ID}`); - console.log(`- 用户类型: ${response.payload?.type || '未知'}`); - - // 5. 说明下一步操作 - console.log('\n=========================================='); - console.log('验证完成!'); - console.log('请前往服务器日志查看:'); - console.log('1. 客服认证是否成功'); - console.log('2. 客服在线状态是否正确更新'); - console.log('3. isManagerOnline函数是否返回true'); - console.log('=========================================='); - - // 保持连接一段时间以便观察心跳 - setTimeout(() => { - console.log('\n验证连接将在5秒后关闭...'); - setTimeout(() => { - ws.close(); - rl.close(); - }, 5000); - }, 1000); - - } else if (response.type === 'auth_error') { - console.error('[失败] 客服认证失败:', response.message); - ws.close(); - rl.close(); - } else if (response.type === 'heartbeat') { - console.log('[状态] 收到心跳消息,连接保持活跃'); - // 发送心跳响应(如果需要) - ws.send(JSON.stringify({ type: 'pong' })); - } - } catch (error) { - console.error('[错误] 解析服务器响应失败:', error); - } - }); - - // 4. 监听错误和关闭事件 - ws.on('error', (error) => { - console.error('[错误] WebSocket连接错误:', error.message); - }); - - ws.on('close', (code, reason) => { - console.log(`[状态] WebSocket连接已关闭,代码: ${code}, 原因: ${reason || '正常关闭'}`); - console.log('\n测试结束。请检查服务器日志以确认客服在线状态是否正确更新。'); - }); -} - -// 开始验证 -runCompleteVerification().catch(err => { - console.error('验证过程中发生错误:', err); - rl.close(); -}); - -// 处理用户中断 -rl.on('SIGINT', () => { - console.log('\n验证已取消'); - rl.close(); -}); diff --git a/server-example/verify_chat_fix.js b/server-example/verify_chat_fix.js deleted file mode 100644 index fa40773..0000000 --- a/server-example/verify_chat_fix.js +++ /dev/null @@ -1,83 +0,0 @@ -// 聊天功能验证脚本 -const mysql = require('mysql2/promise'); - -const config = { - host: '1.95.162.61', - port: 3306, - user: 'root', - password: 'schl@2025', - database: 'wechat_app' -}; - -async function verifyChatFunctionality() { - let connection; - try { - connection = await mysql.createConnection(config); - console.log('验证数据库连接成功'); - - // 1. 测试创建正常会话 - const testUserId = 'test_user_' + Date.now(); - const testManagerId = '22'; - const testConversationId = 'test_conv_' + Date.now(); - - console.log(' -测试创建会话:'); - console.log(' 用户ID:', testUserId); - console.log(' 客服ID:', testManagerId); - - // 插入测试数据 - await connection.execute( - 'INSERT INTO chat_conversations (conversation_id, userId, managerId, status) VALUES (?, ?, ?, 1)', - [testConversationId, testUserId, testManagerId] - ); - console.log('✓ 测试会话创建成功'); - - // 2. 验证数据正确存储 - const [conversations] = await connection.execute( - 'SELECT * FROM chat_conversations WHERE conversation_id = ?', - [testConversationId] - ); - - if (conversations.length > 0) { - const conversation = conversations[0]; - console.log(' -验证会话数据:'); - console.log(' userId类型:', typeof conversation.userId); - console.log(' userId值:', conversation.userId); - console.log(' managerId值:', conversation.managerId); - - if (conversation.userId === testUserId) { - console.log('✓ userId正确存储为字符串'); - } else { - console.log('❌ userId存储不正确'); - } - } - - // 3. 测试查询功能 - const [queryResult] = await connection.execute( - 'SELECT * FROM chat_conversations WHERE userId = ? AND managerId = ?', - [testUserId, testManagerId] - ); - - console.log(' -查询测试:'); - console.log(' Found ' + queryResult.length + ' records.'); - - // 4. 清理测试数据 - await connection.execute( - 'DELETE FROM chat_conversations WHERE conversation_id = ?', - [testConversationId] - ); - console.log('✓ 测试数据清理完成'); - - console.log(' -🎉 聊天功能验证完成,所有测试通过!'); - - } catch (error) { - console.error('验证过程中发生错误:', error); - } finally { - if (connection) await connection.end(); - } -} - -verifyChatFunctionality(); diff --git a/server-example/verify_message_fix.js b/server-example/verify_message_fix.js deleted file mode 100644 index 99a70f0..0000000 --- a/server-example/verify_message_fix.js +++ /dev/null @@ -1,86 +0,0 @@ -const mysql = require('mysql2/promise'); - -// 数据库配置 -const dbConfig = { - host: '1.95.162.61', - port: 3306, - user: 'root', - password: 'schl@2025', - database: 'wechat_app' -}; - -async function verifyMessageStorage() { - let connection; - try { - console.log('开始验证消息存储修复...'); - - // 连接数据库 - connection = await mysql.createConnection(dbConfig); - console.log('✅ 数据库连接成功'); - - // 查询会话表中的最新会话 - console.log('\n📊 查询最新会话信息:'); - const [conversations] = await connection.execute( - 'SELECT conversation_id, userId, managerId, last_message, last_message_time, updated_at FROM chat_conversations ORDER BY updated_at DESC LIMIT 5' - ); - - console.log(`找到 ${conversations.length} 个会话记录`); - conversations.forEach((conv, index) => { - console.log(`\n会话 ${index + 1}:`); - console.log(` 会话ID: ${conv.conversation_id}`); - console.log(` 用户ID: ${conv.userId}`); - console.log(` 客服ID: ${conv.managerId}`); - console.log(` 最后消息: ${conv.last_message ? conv.last_message.substring(0, 30) + '...' : '无'}`); - console.log(` 最后消息时间: ${new Date(conv.last_message_time).toLocaleString('zh-CN')}`); - }); - - // 查询消息表中的最新消息 - console.log('\n📨 查询最新消息记录:'); - const [messages] = await connection.execute( - 'SELECT message_id, conversation_id, sender_type, sender_id, receiver_id, content, created_at FROM chat_messages ORDER BY created_at DESC LIMIT 10' - ); - - console.log(`找到 ${messages.length} 条消息记录`); - messages.forEach((msg, index) => { - const senderTypeText = msg.sender_type === 1 ? '用户' : '客服'; - console.log(`\n消息 ${index + 1}:`); - console.log(` 消息ID: ${msg.message_id}`); - console.log(` 会话ID: ${msg.conversation_id}`); - console.log(` 发送类型: ${senderTypeText}`); - console.log(` 发送者ID: ${msg.sender_id}`); - console.log(` 接收者ID: ${msg.receiver_id}`); - console.log(` 内容: ${msg.content ? msg.content.substring(0, 30) + '...' : '无'}`); - console.log(` 创建时间: ${new Date(msg.created_at).toLocaleString('zh-CN')}`); - }); - - // 检查特定会话的消息 - if (conversations.length > 0) { - const targetConversationId = conversations[0].conversation_id; - console.log(`\n🔍 检查特定会话 ${targetConversationId} 的消息:`); - const [specificMessages] = await connection.execute( - 'SELECT message_id, sender_type, sender_id, content, created_at FROM chat_messages WHERE conversation_id = ? ORDER BY created_at DESC', - [targetConversationId] - ); - console.log(` 该会话有 ${specificMessages.length} 条消息`); - } - - // 总结 - console.log('\n✅ 验证完成!'); - if (messages.length > 0) { - console.log('🎉 消息存储功能正常工作,已成功存储消息到chat_messages表!'); - } else { - console.log('⚠️ 未找到新的消息记录,请确认前端是否发送了消息进行测试'); - } - - } catch (error) { - console.error('❌ 验证过程中出错:', error.message); - } finally { - if (connection) { - await connection.end(); - console.log('\n📤 数据库连接已关闭'); - } - } -} - -// 执行验证 -verifyMessageStorage(); \ No newline at end of file diff --git a/server-example/聊天功能实现逻辑分析文档.md b/server-example/聊天功能实现逻辑分析文档.md deleted file mode 100644 index 43e09a5..0000000 --- a/server-example/聊天功能实现逻辑分析文档.md +++ /dev/null @@ -1,333 +0,0 @@ -# 聊天功能实现逻辑分析文档 - -## 一、核心问题分析 - -### 1.1 userId与managerId混用问题 - -在当前系统中,存在一个关键问题:**当客服进行WebSocket认证时,如果未提供`managerId`但提供了`userId`,系统会将`userId`作为`managerId`使用**。这是一种容错处理,但不是理想的设计,因为: - -- `userId`和`managerId`分别来自不同的数据源和表 -- `userId`用于users表(普通用户),`managerId`用于userlogin.personnel表(客服人员) -- 两者在数据类型和业务含义上存在本质区别 - -## 二、认证流程详解 - -### 2.1 WebSocket认证核心逻辑 - -认证逻辑位于server-mysql.js文件中,主要包括以下部分: - -#### 用户认证流程 - -```javascript -// 用户认证逻辑 -defaultUserType === 'user' || finalUserType.includes('customer')) && userId) { - // 1. 数据库验证用户ID是否存在 - const [existingUsers] = await sequelize.query( - 'SELECT userId FROM users WHERE userId = ? LIMIT 1', - { replacements: [userId] } - ); - - // 2. 查询用户是否在personnel表中存在 - const [personnelData] = await sequelize.query( - 'SELECT id FROM userlogin.personnel WHERE userId = ? LIMIT 1', - { replacements: [userId] } - ); - - // 3. 设置连接信息 - connection.userId = userId; - connection.isUser = true; - connection.userType = 'user'; - onlineUsers.set(userId, ws); - - // 4. 发送认证成功消息 - ws.send(JSON.stringify({ - type: 'auth_success', - payload: { userId, type: 'user' } - })); -} -``` - -#### 客服认证流程(含容错处理) - -```javascript -// 客服认证逻辑 - 包含userId作为managerId的容错处理 -else if (finalUserType === 'manager' || finalUserType.includes('customer_service')) { - let stringManagerId; - if (managerId) { - stringManagerId = String(managerId).trim(); - } else if (userId) { - // 问题点:如果没有提供managerId但提供了userId,尝试使用userId作为managerId - stringManagerId = String(userId).trim(); - console.log(`⚠️ 客服认证使用userId作为managerId: ${stringManagerId}`); - } else { - // 缺少必要的managerId或userId - ws.send(JSON.stringify({ - type: 'auth_error', - message: '客服认证失败:缺少必要的managerId或userId' - })); - return; - } - - // 验证managerId是否在personnel表中存在 - const [existingManagers] = await sequelize.query( - 'SELECT id FROM userlogin.personnel WHERE id = ? LIMIT 1', - { replacements: [stringManagerId] } - ); - - // 设置连接信息 - connection.managerId = stringManagerId; - connection.isManager = true; - connection.userType = 'manager'; - onlineManagers.set(stringManagerId, ws); -} -``` - -## 三、消息处理与存储流程 - -### 3.1 会话创建与会话管理 - -会话创建逻辑由`createOrGetConversation`函数实现: - -```javascript -async function createOrGetConversation(userId, managerId) { - // 确保ID类型一致 - userId = validateUserId(userId); - managerId = validateManagerId(managerId); - - // 1. 尝试查找已存在的会话 - const [existingConversations] = await sequelize.query( - 'SELECT * FROM chat_conversations WHERE userId = ? AND managerId = ? LIMIT 1', - { replacements: [userId, managerId] } - ); - - if (existingConversations && existingConversations.length > 0) { - // 如果会话已结束,重新激活 - if (conversation.status !== 1) { - await sequelize.query( - 'UPDATE chat_conversations SET status = 1 WHERE conversation_id = ?', - { replacements: [conversation.conversation_id] } - ); - } - return conversation; - } - - // 2. 创建新会话 - const conversationId = crypto.randomUUID(); - await sequelize.query( - `INSERT INTO chat_conversations - (conversation_id, userId, managerId, status, user_online, cs_online, created_at, updated_at) - VALUES (?, ?, ?, 1, ?, ?, ?, ?)`, - { - replacements: [ - conversationId, - userId, - managerId, - onlineUsers.has(userId) ? 1 : 0, - onlineManagers.has(managerId) ? 1 : 0, - now, - now - ] - } - ); - - return { conversation_id: conversationId, userId, managerId, ... }; -} -``` - -### 3.2 消息处理核心逻辑 - -消息处理由`handleChatMessage`函数实现,包含以下关键步骤: - -1. **确定发送者和接收者**: - - 用户发送:senderId = userId,receiverId = managerId - - 客服发送:senderId = managerId,receiverId = userId - -2. **会话管理**: - - 如果没有提供会话ID,创建新会话 - - 如果会话中userId不匹配,进行修复 - -3. **消息存储**: - - 调用`storeMessage`函数将消息存入数据库 - -4. **消息转发**: - - 将消息转发给在线的接收者 - -### 3.3 消息存储实现 - -```javascript -async function storeMessage(messageData) { - const { messageId, conversationId, senderType, senderId, receiverId, - contentType, content, fileUrl, fileSize, duration, createdAt } = messageData; - - // 参数验证 - if (!messageId || !conversationId || !senderType || !senderId || !receiverId || !content) { - throw new Error('消息数据不完整,缺少必要字段'); - } - - // 确保所有ID都是字符串类型 - const stringSenderId = validateUserId(senderId); - const stringReceiverId = String(receiverId).trim(); - const stringConversationId = String(conversationId).trim(); - - // 存储消息到数据库 - const result = await sequelize.query( - `INSERT INTO chat_messages - (message_id, conversation_id, sender_type, sender_id, receiver_id, - content_type, content, file_url, file_size, duration, is_read, status, - created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 1, ?, ?)`, - { - replacements: [/* 参数列表 */] - } - ); - - return { success: true, messageId, affectedRows }; -} -``` - -## 四、数据模型关系 - -### 4.1 核心数据表结构 - -1. **users表**: - - 存储普通用户信息 - - 主键:userId(字符串类型) - -2. **userlogin.personnel表**: - - 存储客服人员信息 - - 主键:id(数字类型,作为managerId使用) - -3. **chat_conversations表**: - - 存储会话信息 - - 字段:conversation_id, userId, managerId, status, last_message, last_message_time等 - - userId和managerId分别关联users表和personnel表 - -4. **chat_messages表**: - - 存储消息信息 - - 字段:message_id, conversation_id, sender_type, sender_id, receiver_id, content等 - - conversation_id关联chat_conversations表 - -### 4.2 表关系图 - -``` -+------------+ +-------------------+ +-------------------+ -| users表 | | chat_conversations表 | | chat_messages表 | -+------------+ +-------------------+ +-------------------+ -| userId (PK)|1 N | conversation_id |1 N | message_id | -| nickName |<--------| userId (FK) |<--------| conversation_id | -| avatarUrl | | managerId (FK) | | sender_type | -+------------+ | status | | sender_id | - | last_message | | receiver_id | - +-------------------+ | content | - ^ +-------------------+ - | -+-------------------+ | -| personnel表 | | -+-------------------+ | -| id (PK/managerId) |---------+ -| name | -| userId (可选) | -+-------------------+ -``` - -## 五、问题分析与建议修复方案 - -### 5.1 为什么会使用userId作为managerId? - -通过代码分析,我们发现以下几个原因导致了userId被用作managerId: - -1. **前端认证数据不一致**: - - 从`utils/websocket.js`的`authenticate`函数可以看出,前端可能会发送不完整的认证信息 - - 客服认证本应使用managerId,但在某些情况下可能只提供了userId - -2. **容错处理设计**: - - 后端代码添加了容错逻辑,当缺少managerId时尝试使用userId作为替代 - - 这是为了避免因认证失败而导致功能完全不可用 - -3. **历史数据类型问题**: - - 从`聊天功能问题分析与解决方案.md`可知,系统曾存在userId类型不匹配的问题 - - 这可能导致了数据结构设计上的混乱,进而影响了认证逻辑 - -### 5.2 当前实现的问题 - -1. **数据源混淆**: - - userId和managerId分别来自不同的表,混用会导致数据关联错误 - - 当使用userId作为managerId时,系统会在personnel表中查询,可能无法找到对应的记录 - -2. **业务逻辑混乱**: - - 客服身份验证应该基于personnel表中的managerId,而不是userId - - 混用导致身份验证逻辑变得模糊和不可靠 - -3. **潜在的数据一致性问题**: - - 使用错误的ID可能导致会话创建失败或消息发送到错误的接收者 - -### 5.3 建议修复方案 - -1. **前端改进**: - - 确保客服认证时总是正确提供managerId - - 从utils/websocket.js中可以看出,前端应该优先使用storedManagerId - -2. **后端逻辑修复**: - ```javascript - // 改进后的客服认证逻辑 - else if (finalUserType === 'manager' || finalUserType.includes('customer_service')) { - // 明确要求提供managerId - if (!managerId) { - ws.send(JSON.stringify({ - type: 'auth_error', - message: '客服认证失败:缺少必要的managerId' - })); - return; - } - - const stringManagerId = String(managerId).trim(); - - // 严格验证managerId是否在personnel表中存在 - const [existingManagers] = await sequelize.query( - 'SELECT id FROM userlogin.personnel WHERE id = ? LIMIT 1', - { replacements: [stringManagerId] } - ); - - if (!existingManagers || existingManagers.length === 0) { - ws.send(JSON.stringify({ - type: 'auth_error', - message: `客服认证失败:managerId ${stringManagerId} 不存在` - })); - return; - } - - // 设置正确的连接信息 - connection.managerId = stringManagerId; - connection.isManager = true; - connection.userType = 'manager'; - onlineManagers.set(stringManagerId, ws); - } - ``` - -3. **用户-客服映射机制**: - - 建立明确的userId和managerId映射关系 - - 如果需要,可以在personnel表中添加userId字段,实现双向关联 - - 示例查询: - ```javascript - // 当只有userId时,查询对应的managerId - const [personnelData] = await sequelize.query( - 'SELECT id FROM userlogin.personnel WHERE userId = ? LIMIT 1', - { replacements: [userId] } - ); - if (personnelData && personnelData.length > 0) { - const managerId = String(personnelData[0].id); - // 使用查询到的managerId进行认证 - } - ``` - -4. **数据清理与验证**: - - 定期清理因混用ID导致的无效会话和消息 - - 添加数据验证逻辑,确保会话和消息中的ID引用关系正确 - -## 六、总结 - -当前聊天功能实现中,将userId作为managerId进行认证是一种临时的容错处理,虽然能够让系统在某些情况下继续工作,但会导致数据关联错误和业务逻辑混乱。 - -理想的解决方案是建立清晰的用户和客服身份验证机制,确保前端正确提供必要的认证信息,并在后端严格验证这些信息的有效性,避免不同数据源ID的混用。 - -通过实施建议的修复方案,可以提高系统的可靠性和数据一致性,确保聊天功能正常工作,消息能够正确存储和传递。 \ No newline at end of file diff --git a/test-time-backend.js b/test-time-backend.js deleted file mode 100644 index 9b7cc69..0000000 --- a/test-time-backend.js +++ /dev/null @@ -1,39 +0,0 @@ -// 后端时间处理函数测试脚本 - -// 复制后端的时间处理函数 -function getBeijingTime() { - const now = new Date(); - return new Date(now.getTime() + 8 * 60 * 60 * 1000); // 手动加8小时 -} - -function getBeijingTimeISOString() { - return getBeijingTime().toISOString(); -} - -function getBeijingTimeTimestamp() { - return getBeijingTime().getTime(); -} - -console.log('=== 后端时间处理函数测试 ==='); - -// 测试当前时间 -const now = Date.now(); -console.log('当前时间戳 (UTC):', now); -console.log('当前时间 (UTC):', new Date(now).toISOString()); - -// 测试getBeijingTime -const beijingTime = getBeijingTime(); -console.log('UTC+8 Date对象:', beijingTime); -console.log('UTC+8 ISO字符串:', beijingTime.toISOString()); - -// 测试getBeijingTimeISOString -const beijingTimeISOString = getBeijingTimeISOString(); -console.log('getBeijingTimeISOString():', beijingTimeISOString); - -// 测试getBeijingTimeTimestamp -const beijingTimeTimestamp = getBeijingTimeTimestamp(); -console.log('UTC+8时间戳 (手动加8小时):', beijingTimeTimestamp); -console.log('时间差 (毫秒):', beijingTimeTimestamp - now); -console.log('时间差 (小时):', (beijingTimeTimestamp - now) / (1000 * 60 * 60)); - -console.log('=== 测试完成 ==='); diff --git a/test-time-frontend.js b/test-time-frontend.js deleted file mode 100644 index 6241b91..0000000 --- a/test-time-frontend.js +++ /dev/null @@ -1,34 +0,0 @@ -// 前端时间处理工具测试脚本 -const timeUtils = require('./utils/time.js'); - -console.log('=== 前端时间处理工具测试 ==='); - -// 测试当前时间 -const now = Date.now(); -console.log('当前时间戳 (UTC):', now); -console.log('当前时间 (UTC):', new Date(now).toISOString()); - -// 测试getUtc8Timestamp -const utc8Timestamp = timeUtils.getUtc8Timestamp(); -console.log('UTC+8时间戳 (手动加8小时):', utc8Timestamp); -console.log('时间差 (毫秒):', utc8Timestamp - now); -console.log('时间差 (小时):', (utc8Timestamp - now) / (1000 * 60 * 60)); - -// 测试getUtc8Date -const utc8Date = timeUtils.getUtc8Date(); -console.log('UTC+8 Date对象:', utc8Date); -console.log('UTC+8 ISO字符串:', utc8Date.toISOString()); - -// 测试toUtc8ISOString -const utc8ISOString = timeUtils.toUtc8ISOString(); -console.log('toUtc8ISOString():', utc8ISOString); - -// 测试toUtc8String -const utc8String = timeUtils.toUtc8String(); -console.log('toUtc8String():', utc8String); - -// 测试toBeijingTimeISOString (别名) -const beijingTimeISOString = timeUtils.toBeijingTimeISOString(); -console.log('toBeijingTimeISOString():', beijingTimeISOString); - -console.log('=== 测试完成 ==='); diff --git a/utils/auth.js b/utils/auth.js deleted file mode 100644 index 75a66f6..0000000 --- a/utils/auth.js +++ /dev/null @@ -1,269 +0,0 @@ -// 统一身份验证工具函数 -const API = require('./api.js'); - -/** - * 统一的身份验证工具 - * 用于在聊天、电话、消息中心等操作前检查用户登录状态 - */ -const AuthManager = { - /** - * 检查用户是否已登录 - * @returns {boolean} 是否已登录 - */ - isLoggedIn: function() { - const userInfo = wx.getStorageSync('userInfo'); - const openid = userInfo && userInfo.openid; - return !!openid; - }, - - /** - * 获取当前用户ID - * @returns {string|null} 用户ID - */ - getUserId: function() { - const userInfo = wx.getStorageSync('userInfo'); - return userInfo && userInfo.userId ? String(userInfo.userId) : null; - }, - - /** - * 获取当前用户类型 - * @returns {string} 用户类型 - */ - getUserType: function() { - return wx.getStorageSync('userType') || ''; - }, - - /** - * 判断是否为客服 - * @returns {boolean} 是否为客服 - */ - isCustomerService: function() { - const userType = this.getUserType(); - return userType.includes('manager') || userType === 'manager'; - }, - - /** - * 获取客服managerId - * @returns {string|null} managerId - */ - getManagerId: function() { - return wx.getStorageSync('managerId') || null; - }, - - /** - * 执行统一的身份验证 - * @param {Function} successCallback - 验证成功后的回调 - * @param {Function} failCallback - 验证失败后的回调 - */ - authenticate: function(successCallback, failCallback) { - console.log('执行统一身份验证...'); - - // 检查是否已登录 - if (!this.isLoggedIn()) { - console.log('用户未登录,引导用户去登录页面'); - - // 显示登录模态框 - wx.showModal({ - title: '需要登录', - content: '请先授权登录后再继续操作', - showCancel: true, - cancelText: '取消', - confirmText: '去登录', - success: (res) => { - if (res.confirm) { - // 跳转到登录页面,让用户完整完成登录流程 - wx.switchTab({ - url: '/pages/index/index', - success: function() { - console.log('成功跳转到登录页面'); - }, - fail: function(error) { - console.error('跳转到登录页面失败:', error); - wx.showToast({ - title: '跳转失败,请稍后重试', - icon: 'none' - }); - } - }); - } else if (failCallback) { - failCallback(new Error('用户取消登录')); - } - } - }); - return; - } - - // 已登录,继续后续处理 - this.handlePostLogin(successCallback); - }, - - /** - * 执行登录操作 - * @param {Object} loginParams - 登录参数 - * @param {string} loginParams.encryptedData - 加密的手机号数据 - * @param {string} loginParams.iv - 解密向量 - * @param {Function} callback - 登录回调 - */ - doLogin: function(loginParams, callback) { - console.log('执行微信登录...'); - - // 验证必要参数 - if (!loginParams || !loginParams.encryptedData || !loginParams.iv) { - console.error('登录失败: 缺少必要的加密数据参数'); - if (callback) { - callback({ success: false, error: '登录失败: 缺少必要的加密数据参数' }); - } - return; - } - - API.login(loginParams.encryptedData, loginParams.iv) - .then(res => { - if (callback) { - callback(res); - } - }) - .catch(err => { - console.error('登录失败:', err); - if (callback) { - callback({ success: false, error: err.message }); - } - }); - }, - - /** - * 登录后的处理逻辑 - * @param {Function} successCallback - 成功回调 - */ - handlePostLogin: function(successCallback) { - const userId = this.getUserId(); - const userType = this.getUserType(); - - console.log('登录后信息:', { userId, userType }); - - // 如果是客服,确保有managerId - if (this.isCustomerService() && !this.getManagerId()) { - console.warn('客服身份但缺少managerId,尝试重新获取'); - this.syncCustomerServiceInfo(() => { - if (successCallback) { - successCallback({ userId, userType, managerId: this.getManagerId() }); - } - // 登录成功后请求位置授权 - this.requestLocationAuth(); - }); - } else { - if (successCallback) { - successCallback({ userId, userType, managerId: this.getManagerId() }); - } - // 登录成功后请求位置授权 - this.requestLocationAuth(); - } - }, - - /** - * 同步客服信息 - * @param {Function} callback - 完成回调 - */ - syncCustomerServiceInfo: function(callback) { - const userInfo = wx.getStorageSync('userInfo'); - const phoneNumber = userInfo && userInfo.phoneNumber; - - if (!phoneNumber) { - console.warn('没有手机号,无法同步客服信息'); - if (callback) callback(); - return; - } - - // 重新获取managerId - Promise.all([ - API.checkIfUserIsCustomerService(phoneNumber), - API.getManagerIdByPhone(phoneNumber) - ]).then(([isCustomerService, managerId]) => { - if (isCustomerService && managerId) { - console.log('同步客服信息成功:', { managerId }); - wx.setStorageSync('managerId', managerId); - } - if (callback) callback(); - }).catch(err => { - console.error('同步客服信息失败:', err); - if (callback) callback(); - }); - }, - - /** - * 清理登录状态 - */ - clearLoginStatus: function() { - wx.removeStorageSync('userInfo'); - wx.removeStorageSync('userType'); - wx.removeStorageSync('managerId'); - wx.removeStorageSync('phoneNumber'); - console.log('登录状态已清理'); - }, - - /** - * 请求位置授权 - */ - requestLocationAuth: function() { - console.log('登录成功后请求位置授权'); - - wx.authorize({ - scope: 'scope.userLocation', - success() { - // 授权成功,获取用户位置 - wx.getLocation({ - type: 'gcj02', - success(res) { - const latitude = res.latitude; - const longitude = res.longitude; - console.log('获取位置成功:', { latitude, longitude }); - // 可以将位置信息存储到本地 - wx.setStorageSync('userLocation', { latitude, longitude }); - }, - fail() { - console.error('获取位置失败'); - } - }); - }, - fail() { - // 授权失败,弹出模态框引导用户重新授权 - wx.showModal({ - title: '需要位置授权', - content: '请在设置中开启位置授权,以便我们为您提供相关服务', - showCancel: true, - cancelText: '取消', - confirmText: '去授权', - success: (res) => { - if (res.confirm) { - // 打开设置页面让用户手动开启授权 - wx.openSetting({ - success: (settingRes) => { - if (settingRes.authSetting['scope.userLocation']) { - // 用户在设置中开启了位置授权 - wx.getLocation({ - type: 'gcj02', - success(res) { - const latitude = res.latitude; - const longitude = res.longitude; - console.log('获取位置成功:', { latitude, longitude }); - // 可以将位置信息存储到本地 - wx.setStorageSync('userLocation', { latitude, longitude }); - }, - fail() { - console.error('获取位置失败'); - } - }); - } - }, - fail: () => { - console.error('打开设置失败'); - } - }); - } - } - }); - } - }); - } -}; - -module.exports = AuthManager; diff --git a/utils/websocket.js b/utils/websocket.js deleted file mode 100644 index d9909da..0000000 --- a/utils/websocket.js +++ /dev/null @@ -1,767 +0,0 @@ -// utils/websocket.js -// WebSocket连接管理器 - -class WebSocketManager { - constructor() { - this.socket = null; - this.url = ''; - this.isConnected = false; - this.isAuthenticated = false; // 新增:认证状态标记 - this.reconnectAttempts = 0; - this.maxReconnectAttempts = 5; - this.reconnectInterval = 3000; // 3秒后重连 - this.heartbeatInterval = null; - this.heartbeatTime = 30000; // 30秒心跳 - this.messageQueue = []; // 未发送的消息队列 - this.listeners = {}; // 事件监听器 - this.lastHeartbeatTime = 0; // 最后一次心跳响应时间 - this.isManualDisconnect = false; // 是否手动断开连接 - - // 清理可能导致端口错误的存储配置 - this._cleanupStorage(); - } - - // 清理可能导致端口错误的存储配置 - _cleanupStorage() { - try { - // 尝试在小程序环境中清理存储 - if (typeof wx !== 'undefined' && wx.removeStorageSync) { - wx.removeStorageSync('__TEST_MODE__'); - wx.removeStorageSync('__TEST_SERVER_IP__'); - wx.removeStorageSync('__DEVICE_TYPE__'); - console.log('WebSocket: 已清理可能导致端口错误的本地存储配置'); - } - } catch (e) { - console.warn('WebSocket: 清理存储时出错:', e); - } - } - - /** - * 初始化WebSocket连接 - * @param {string} url - WebSocket服务器地址 - * @param {object} options - 配置选项 - */ - connect(url, options = {}) { - if (this.socket && this.isConnected) { - console.log('WebSocket已经连接'); - return; - } - - this.url = url; - this.maxReconnectAttempts = options.maxReconnectAttempts || this.maxReconnectAttempts; - this.reconnectInterval = options.reconnectInterval || this.reconnectInterval; - this.heartbeatTime = options.heartbeatTime || this.heartbeatTime; - this.isManualDisconnect = false; // 重置手动断开标志 - - try { - console.log('尝试连接WebSocket:', url); - this._trigger('status', { type: 'connecting', message: '正在连接服务器...' }); - - this.socket = wx.connectSocket({ - url: url, - success: () => { - console.log('WebSocket连接请求已发送'); - }, - fail: (error) => { - console.error('WebSocket连接请求失败:', error); - this._trigger('error', error); - this._trigger('status', { type: 'error', message: '连接服务器失败' }); - this._reconnect(); - } - }); - - this._setupEventHandlers(); - } catch (error) { - console.error('WebSocket初始化失败:', error); - this._trigger('error', error); - this._trigger('status', { type: 'error', message: '连接异常' }); - this._reconnect(); - } - } - - /** - * 设置WebSocket事件处理器 - */ - _setupEventHandlers() { - if (!this.socket) return; - - // 连接成功 - this.socket.onOpen(() => { - console.log('WebSocket连接已打开'); - this.isConnected = true; - this.isAuthenticated = false; // 重置认证状态 - this.reconnectAttempts = 0; - this.lastHeartbeatTime = Date.now(); // 记录最后心跳时间 - this._trigger('open'); - this._trigger('status', { type: 'connected', message: '连接成功' }); - - // 连接成功后立即进行认证 - this.authenticate(); - - this._startHeartbeat(); - }); - - // 接收消息 - this.socket.onMessage((res) => { - try { - let data = JSON.parse(res.data); - // 处理心跳响应 - if (data.type === 'pong') { - this.lastHeartbeatTime = Date.now(); // 更新心跳时间 - return; - } - - // 处理认证响应(兼容auth_response和auth_success两种消息格式) - if (data.type === 'auth_response') { - if (data.success) { - console.log('WebSocket认证成功(auth_response)'); - this.isAuthenticated = true; - // 触发认证成功事件,并传递用户类型信息 - this._trigger('authenticated', { userType: data.userType || 'customer' }); - // 认证成功后发送队列中的消息 - this._flushMessageQueue(); - } else { - console.error('WebSocket认证失败:', data.message); - this.isAuthenticated = false; - this._trigger('authFailed', { message: data.message, userType: data.userType || 'unknown' }); - } - return; - } - - // 处理auth_success格式的认证成功消息(与后端实际返回格式匹配) - if (data.type === 'auth_success') { - console.log('WebSocket认证成功(auth_success)'); - this.isAuthenticated = true; - // 从payload中提取用户类型信息 - const userType = data.payload && data.payload.type ? data.payload.type : 'customer'; - // 触发认证成功事件 - this._trigger('authenticated', { userType: userType }); - // 认证成功后发送队列中的消息 - 关键修复! - this._flushMessageQueue(); - return; - } - - // 处理客服状态更新消息 - if (data.type === 'customerServiceStatusUpdate') { - console.log('处理客服状态更新:', data); - this._trigger('customerServiceStatusUpdate', data); - return; - } - - console.log('接收到消息:', data); - this._trigger('message', data); - } catch (error) { - console.error('消息解析失败:', error); - this._trigger('error', error); - } - }); - - // 连接关闭 - this.socket.onClose((res) => { - console.log('WebSocket连接已关闭:', res); - this.isConnected = false; - this._stopHeartbeat(); - this._trigger('close', res); - this._trigger('status', { type: 'disconnected', message: '连接已关闭' }); - // 尝试重连 - if (res.code !== 1000 && !this.isManualDisconnect) { // 非正常关闭且不是手动断开 - this._reconnect(); - } - }); - - // 连接错误 - this.socket.onError((error) => { - console.error('WebSocket错误:', error); - this._trigger('error', error); - this._trigger('status', { type: 'error', message: '连接发生错误' }); - }); - } - - /** - * 发送认证消息 - * @param {string} userType - 用户类型,如'user'或'manager' - * @param {string} userId - 用户ID或managerId - */ - authenticate(userType = null, userId = null) { - try { - // 获取登录用户信息或token - const app = getApp(); - const globalUserInfo = app.globalData.userInfo || {}; - - // 如果传入了参数,优先使用传入的参数 - let finalUserType = userType || globalUserInfo.userType || globalUserInfo.type || 'customer'; - - // 构建认证消息 - 严格区分用户类型和认证信息 - let authMessage; - - // 检查是否为客服身份 - const storedManagerId = wx.getStorageSync('managerId'); - const isManager = finalUserType === 'manager' || storedManagerId; - - if (isManager) { - // 客服认证:必须使用有效的managerId,不允许使用普通userId作为容错 - if (!storedManagerId) { - console.error('客服认证失败:未找到有效的managerId'); - this._trigger('authFailed', { message: '客服认证失败:未找到有效的managerId' }); - return; - } - - authMessage = { - type: 'auth', - managerId: storedManagerId, - userType: 'manager', - timestamp: Date.now() - }; - console.log('客服用户认证:', { managerId: storedManagerId, userType: 'manager' }); - } else { - // 普通用户认证:必须使用users表中的正式userId - // 聊天功能必须在用户授权登录后使用,因此必须有有效的userId - let finalUserId = null; - - // 优先级1:使用传入的userId(应该是从服务器获取的正式ID) - if (userId) { - finalUserId = String(userId); - console.log('使用传入的用户ID:', finalUserId); - } - // 优先级2:从全局用户信息获取(应该包含服务器返回的userId) - else if (globalUserInfo && globalUserInfo.userId) { - finalUserId = String(globalUserInfo.userId); - console.log('从globalData获取用户ID:', finalUserId); - } - // 优先级3:从本地存储获取(应该存储了服务器返回的userId) - else { - finalUserId = wx.getStorageSync('userId'); - if (finalUserId) { - finalUserId = String(finalUserId); - console.log('从本地存储获取用户ID:', finalUserId); - } - } - - // 验证是否有有效的用户ID - if (!finalUserId || finalUserId === 'undefined' || finalUserId === 'null') { - console.error('认证失败:未获取到有效的用户ID'); - this._trigger('authFailed', { - message: '用户未授权登录,请先完成登录', - code: 'NO_VALID_USER_ID' - }); - return; - } - - // 确保使用正确的用户类型 - // 根据用户在users表中的类型设置,支持customer、buyer、seller、both - let authUserType = finalUserType; - if (!authUserType || authUserType === 'user') { - authUserType = 'customer'; - } - - console.log('准备认证 - 用户ID:', finalUserId, '用户类型:', authUserType); - - authMessage = { - type: 'auth', - userId: finalUserId, - userType: authUserType, - timestamp: Date.now() - }; - console.log('普通用户认证:', { userId: finalUserId, userType: authUserType }); - } - - console.log('发送WebSocket认证消息:', authMessage); - - // 直接发送认证消息,不经过常规消息队列 - if (this.isConnected && this.socket) { - this.socket.send({ - data: JSON.stringify(authMessage), - success: () => { - console.log('认证消息发送成功'); - }, - fail: (error) => { - console.error('认证消息发送失败:', error); - this._trigger('authFailed', { message: '认证消息发送失败' }); - // 认证失败后尝试重新认证 - setTimeout(() => { - this.authenticate(userType, userId); - }, 2000); - } - }); - } - } catch (error) { - console.error('发送认证消息异常:', error); - this._trigger('authFailed', { message: '认证处理异常' }); - } - } - - /** - * 发送消息 - * @param {object} data - 要发送的数据 - * @returns {boolean} 消息是否已成功放入发送队列(不保证实际发送成功) - */ - send(data) { - // 验证消息格式 - if (!data || typeof data !== 'object') { - console.error('WebSocket发送消息失败: 消息格式不正确'); - return false; - } - - // 为消息添加时间戳 - if (!data.timestamp) { - data.timestamp = Date.now(); - } - - // 确保消息使用正式用户ID,严格区分sender_id和receiver_id - try { - const app = getApp(); - const globalUserInfo = app.globalData.userInfo || {}; - let currentUserId = String(globalUserInfo.userId || wx.getStorageSync('userId') || ''); - - // 重要:确保使用的是正式用户ID,不允许使用临时ID - if (currentUserId.startsWith('temp_') || currentUserId.includes('temp') || currentUserId.includes('test_')) { - console.error('严重错误:消息对象中检测到临时或测试用户ID,聊天功能必须使用正式userId'); - // 尝试从本地存储获取正式用户ID - const userInfo = wx.getStorageSync('userInfo'); - if (userInfo && userInfo.userId && !String(userInfo.userId).includes('temp') && !String(userInfo.userId).includes('test_')) { - currentUserId = String(userInfo.userId); - console.log('已更新为正式用户ID:', currentUserId); - } else { - console.error('无法获取有效的正式用户ID,消息发送失败'); - return false; - } - } - - // 确保消息中包含正确的sender_id,并保留userId字段供服务器使用 - if (data.userId && !data.sender_id) { - console.warn('消息使用了userId字段,应改为使用sender_id字段'); - data.sender_id = data.userId; - // 关键修复:保留userId字段,因为服务器需要它来处理消息 - // delete data.userId; // 不再删除userId字段 - } - - // 如果没有指定sender_id,则设置为当前用户ID - if (!data.sender_id) { - data.sender_id = currentUserId; - } - - // 确保接收者ID使用receiver_id字段 - if (data.targetUserId && !data.receiver_id) { - console.warn('消息使用了targetUserId字段,应改为使用receiver_id字段'); - data.receiver_id = data.targetUserId; - delete data.targetUserId; - } - - // 【修复】确保聊天消息使用正确的数据库字段名 - // 数据库使用下划线命名法,前端代码中可能使用驼峰命名法 - if (data.receiverId) { - console.warn('检测到使用了驼峰命名的receiverId,将转换为下划线命名的receiver_id'); - data.receiver_id = data.receiver_id || data.receiverId; - // 保留原始字段以保持兼容性 - } - if (data.senderId) { - console.warn('检测到使用了驼峰命名的senderId,将转换为下划线命名的sender_id'); - data.sender_id = data.sender_id || data.senderId; - // 保留原始字段以保持兼容性 - } - } catch (e) { - console.error('处理消息用户ID时出错:', e); - } - - // 如果是认证消息或连接未建立,直接处理 - if (data.type === 'auth' || data.type === 'ping') { - // 认证消息和心跳消息不需要等待认证 - if (this.isConnected && this.socket) { - try { - this.socket.send({ - data: JSON.stringify(data), - success: () => { - console.log('特殊消息发送成功:', data); - this._trigger('sendSuccess', data); - }, - fail: (error) => { - console.error('特殊消息发送失败:', error); - this._trigger('sendError', error); - } - }); - return true; - } catch (error) { - console.error('发送特殊消息异常:', error); - this._trigger('error', error); - return false; - } - } - } else if (this.isConnected && this.socket) { - // 非特殊消息需要检查认证状态 - if (!this.isAuthenticated) { - console.log('WebSocket未认证,消息已加入队列等待认证'); - this.messageQueue.push(data); - // 如果未认证,尝试重新认证 - if (!this.isAuthenticated) { - this.authenticate(); - } - return true; - } - - try { - this.socket.send({ - data: JSON.stringify(data), - success: () => { - console.log('消息发送成功:', data); - this._trigger('sendSuccess', data); - }, - fail: (error) => { - console.error('消息发送失败:', error); - // 将失败的消息加入队列 - this.messageQueue.push(data); - this._trigger('sendError', error); - } - }); - return true; - } catch (error) { - console.error('发送消息异常:', error); - this.messageQueue.push(data); - this._trigger('error', error); - return false; - } - } else { - // 连接未建立,加入消息队列 - console.log('WebSocket未连接,消息已加入队列'); - this.messageQueue.push(data); - // 尝试重连 - if (!this.isConnected) { - this._reconnect(); - } - return true; - } - } - - /** - * 关闭WebSocket连接 - */ - close() { - if (this.socket) { - this._stopHeartbeat(); - this.isManualDisconnect = true; // 标记为手动断开 - this.socket.close(); - this.socket = null; - this.isConnected = false; - this.isAuthenticated = false; // 重置认证状态 - console.log('WebSocket已主动关闭'); - this._trigger('status', { type: 'disconnected', message: '连接已断开' }); - } - } - - /** - * 开始心跳检测 - */ - _startHeartbeat() { - this._stopHeartbeat(); - this.heartbeatInterval = setInterval(() => { - // 检查是否超过3倍心跳间隔未收到心跳响应 - if (Date.now() - this.lastHeartbeatTime > this.heartbeatTime * 3) { - console.warn('WebSocket心跳超时,可能已断开连接'); - this._stopHeartbeat(); - this._reconnect(); - return; - } - - if (this.isConnected) { - this.send({ type: 'ping', timestamp: Date.now() }); - console.log('发送心跳包'); - } - }, this.heartbeatTime); - } - - /** - * 停止心跳检测 - */ - _stopHeartbeat() { - if (this.heartbeatInterval) { - clearInterval(this.heartbeatInterval); - this.heartbeatInterval = null; - } - } - - /** - * 尝试重新连接 - */ - _reconnect() { - if (this.reconnectAttempts >= this.maxReconnectAttempts) { - console.error('WebSocket重连次数已达上限,停止重连'); - this._trigger('reconnectFailed'); - this._trigger('status', { - type: 'error', - isWarning: true, - message: `已达到最大重连次数(${this.maxReconnectAttempts}次)` - }); - return; - } - - this.reconnectAttempts++; - // 增加重连时间间隔(指数退避) - const currentInterval = this.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1); - - console.log(`WebSocket第${this.reconnectAttempts}次重连... 间隔: ${currentInterval}ms`); - this._trigger('reconnecting', this.reconnectAttempts); - this._trigger('status', { - type: 'reconnecting', - message: `正在重连(${this.reconnectAttempts}/${this.maxReconnectAttempts})` - }); - - setTimeout(() => { - this.connect(this.url); - }, currentInterval); - } - - /** - * 发送队列中的消息 - */ - _flushMessageQueue() { - if (this.messageQueue.length > 0) { - console.log('发送队列中的消息,队列长度:', this.messageQueue.length); - - // 循环发送队列中的消息,使用小延迟避免消息发送过快 - const sendMessage = () => { - if (this.messageQueue.length === 0 || !this.isConnected) { - return; - } - - const messageData = this.messageQueue.shift(); - const message = JSON.stringify(messageData); - - this.socket.send({ - data: message, - success: () => { - console.log('队列消息发送成功:', messageData); - // 继续发送下一条消息,添加小延迟 - setTimeout(sendMessage, 50); - }, - fail: (error) => { - console.error('队列消息发送失败:', error); - // 发送失败,重新加入队列 - this.messageQueue.unshift(messageData); - } - }); - }; - - // 开始发送队列中的第一条消息 - sendMessage(); - } - } - - /** - * 触发事件 - * @param {string} event - 事件名称 - * @param {*} data - 事件数据 - */ - _trigger(event, data = null) { - if (this.listeners[event]) { - this.listeners[event].forEach(callback => { - try { - callback(data); - } catch (error) { - console.error(`事件处理错误 [${event}]:`, error); - } - }); - } - } - - /** - * 监听事件 - * @param {string} event - 事件名称 - * @param {Function} callback - 回调函数 - */ - on(event, callback) { - if (!this.listeners[event]) { - this.listeners[event] = []; - } - this.listeners[event].push(callback); - } - - /** - * 移除事件监听 - * @param {string} event - 事件名称 - * @param {Function} callback - 回调函数,不传则移除所有该事件的监听器 - */ - off(event, callback) { - if (!this.listeners[event]) return; - - if (callback) { - this.listeners[event] = this.listeners[event].filter(cb => cb !== callback); - } else { - this.listeners[event] = []; - } - } - - /** - * 获取连接状态 - * @returns {boolean} 是否连接 - */ - getConnectionStatus() { - return this.isConnected; - } - - /** - * 获取认证状态 - * @returns {boolean} 是否已认证 - */ - getAuthStatus() { - return this.isAuthenticated; - } - - /** - * 获取重连次数 - * @returns {number} 重连次数 - */ - getReconnectAttempts() { - return this.reconnectAttempts; - } - - /** - * 清空消息队列 - */ - clearMessageQueue() { - this.messageQueue = []; - } -} - -// 消息发送状态管理 - 全局作用域 -const messageStatus = new Map(); -const MESSAGE_SENDING = 'sending'; -const MESSAGE_SENT = 'sent'; -const MESSAGE_FAILED = 'failed'; - -// 消息去重函数 - 防止重复发送 -function shouldSendMessage(messageId) { - const status = messageStatus.get(messageId); - // 如果消息正在发送中,不重复发送 - if (status === MESSAGE_SENDING) { - console.log(`[WebSocket] 消息 ${messageId} 正在发送中,跳过重复发送`); - return false; - } - // 设置消息状态为发送中 - messageStatus.set(messageId, MESSAGE_SENDING); - return true; -} - -// 更新消息发送状态 -function updateMessageStatus(messageId, status) { - messageStatus.set(messageId, status); - // 定期清理已完成的消息状态 - if (messageStatus.size > 100) { - cleanupMessageStatus(); - } -} - -// 清理已完成的消息状态 -function cleanupMessageStatus() { - for (const [messageId, status] of messageStatus.entries()) { - if (status === MESSAGE_SENT || status === MESSAGE_FAILED) { - messageStatus.delete(messageId); - } - } -} - - - -// 创建单例实例 -const websocketManager = new WebSocketManager(); - -// 增强的消息发送函数 -function sendEnhancedMessage(messageData) { - // 确保消息数据有效 - if (!messageData || typeof messageData !== 'object') { - console.error('[WebSocket] 无效的消息数据:', messageData); - return false; - } - - // 确保消息有唯一ID - const actualMessageData = messageData; - if (!actualMessageData.messageId) { - // 如果是create_conversation消息,使用userId+managerId+timestamp生成临时ID - if (actualMessageData.type === 'create_conversation') { - actualMessageData.messageId = `create_${actualMessageData.userId}_${actualMessageData.managerId}_${actualMessageData.timestamp}`; - } - // 如果是get_messages消息,使用特殊格式生成ID,避免重复发送 - else if (actualMessageData.type === 'get_messages') { - actualMessageData.messageId = `getmsg_${actualMessageData.conversationId || actualMessageData.targetUserId || 'unknown'}_${Date.now()}`; - } - else { - // 其他消息类型生成随机ID - actualMessageData.messageId = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - } - } - - // 【关键修复】对于get_messages消息,不进行去重检查,确保每次都能获取最新消息 - if (actualMessageData.type !== 'get_messages') { - // 消息去重检查 - if (!shouldSendMessage(actualMessageData.messageId)) { - return false; - } - } - - try { - // 确保包含必要字段 - if (actualMessageData.type === 'chat_message') { - // 客服消息不需要userId,只需要managerId - const isManager = actualMessageData.userType === 'manager' || actualMessageData.manager_mode; - if (!isManager && (!actualMessageData.userId || !actualMessageData.managerId)) { - console.error('[WebSocket] 聊天消息缺少必要的userId或managerId字段'); - updateMessageStatus(actualMessageData.messageId, MESSAGE_FAILED); - return false; - } - // 客服消息只需要managerId - if (isManager && !actualMessageData.managerId) { - console.error('[WebSocket] 客服消息缺少必要的managerId字段'); - updateMessageStatus(actualMessageData.messageId, MESSAGE_FAILED); - return false; - } - } - - // 【关键修复】确保消息使用正确的字段名 - if (actualMessageData.userId && !actualMessageData.sender_id) { - actualMessageData.sender_id = actualMessageData.userId; - } - if (actualMessageData.targetUserId && !actualMessageData.receiver_id) { - actualMessageData.receiver_id = actualMessageData.targetUserId; - } - - // 设置消息状态为发送中 - updateMessageStatus(actualMessageData.messageId, 'sending'); - - // 发送消息 - 使用WebSocketManager实例 - const socketManager = websocketManager; - if (socketManager.send) { - socketManager.send(actualMessageData); - console.log(`[WebSocket] 消息 ${actualMessageData.messageId} 发送成功`); - } else { - console.error('[WebSocket] 无法访问send方法'); - updateMessageStatus(actualMessageData.messageId, MESSAGE_FAILED); - return false; - } - - // 设置消息发送超时检测 - setTimeout(() => { - const status = messageStatus.get(actualMessageData.messageId); - if (status === MESSAGE_SENDING) { - console.warn(`[WebSocket] 消息 ${actualMessageData.messageId} 发送超时,可能需要重试`); - updateMessageStatus(actualMessageData.messageId, MESSAGE_FAILED); - } - }, 10000); // 增加超时时间到10秒,确保历史消息请求有足够时间返回 - - return true; - } catch (error) { - console.error(`[WebSocket] 发送消息失败: ${error.message}`); - updateMessageStatus(actualMessageData.messageId, MESSAGE_FAILED); - return false; - } -} - -// 修复导出问题,确保正确支持ES6默认导入 -// CommonJS导出方式 -module.exports = websocketManager; -// 添加消息处理相关函数到导出对象 -module.exports.sendEnhancedMessage = sendEnhancedMessage; -module.exports.updateMessageStatus = updateMessageStatus; -module.exports.MESSAGE_SENT = MESSAGE_SENT; -module.exports.MESSAGE_FAILED = MESSAGE_FAILED; - -// ES6模块导出 -export default websocketManager; -export { sendEnhancedMessage, updateMessageStatus, MESSAGE_SENT, MESSAGE_FAILED };