From a992ff45bd6f96cb53f2f1ddc8941f7638d9cf2e Mon Sep 17 00:00:00 2001 From: Default User Date: Mon, 15 Dec 2025 16:16:55 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=B4=E6=97=B6=E4=BC=9A?= =?UTF-8?q?=E8=AF=9DID=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=89=8D=E7=AB=AF=E6=A0=BC=E5=BC=8Ftemp=5F[c?= =?UTF-8?q?urrentUserId]=5F[targetId]=5F[timestamp]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server-example/server-mysql.js | 184 +++++++++++----- server-example/test_message_storage.js | 284 +++++++++++++++++++++++++ 2 files changed, 419 insertions(+), 49 deletions(-) create mode 100644 server-example/test_message_storage.js diff --git a/server-example/server-mysql.js b/server-example/server-mysql.js index 4f4eb01..c5882e1 100644 --- a/server-example/server-mysql.js +++ b/server-example/server-mysql.js @@ -6253,48 +6253,55 @@ wss.on('connection', (ws, req) => { return; } + // 处理创建会话请求 + if (data.type === 'create_conversation') { + console.log('接收到创建会话请求:', JSON.stringify(data)); + try { + const { userId, managerId, timestamp } = data; + + // 验证必要参数 + if (!userId || !managerId) { + console.error('错误: 创建会话缺少必要参数'); + ws.send(JSON.stringify({ + type: 'error', + message: '创建会话失败: 缺少用户ID或客服ID' + })); + return; + } + + // 创建或获取会话 + const conversation = await createOrGetConversation(userId, managerId); + console.log('会话创建成功:', conversation); + + // 返回会话创建成功响应 + ws.send(JSON.stringify({ + type: 'conversation_created', + conversationId: conversation.conversation_id, + userId: conversation.userId, + managerId: conversation.managerId, + status: conversation.status, + timestamp: timestamp || Date.now() + })); + } catch (error) { + console.error('创建会话失败:', error); + ws.send(JSON.stringify({ + type: 'error', + message: '创建会话失败: ' + error.message + })); + } + return; + } + // 处理聊天消息 if (data.type === 'chat_message') { console.log('接收到聊天消息:', JSON.stringify(data)); - // 从前端嵌套格式中提取payload - let payload = data.data || data.payload || data; - - // 构建完整的payload对象,合并顶层和嵌套数据 - const completePayload = { - ...payload, - // 从顶层data复制重要字段 - direction: data.direction || payload.direction || 'customer_to_service', - messageType: data.messageType || payload.messageType || 'text', - // 确保必要的ID字段存在 - userId: payload.senderId || payload.userId, - receiverId: payload.receiverId, - // 确保消息内容存在 - content: payload.content || '', - contentType: payload.contentType || 1, - timestamp: payload.timestamp || Date.now() - }; - - // 关键修复:正确映射ID字段 - // 对于用户发送给客服的消息,receiverId就是managerId - if (completePayload.receiverId && !completePayload.managerId) { - completePayload.managerId = completePayload.receiverId; - console.log('已将receiverId映射为managerId:', completePayload.managerId); - } - - // 转换senderType从字符串到数字(确保兼容) - if (typeof completePayload.senderType === 'string') { - if (completePayload.senderType === 'customer' || completePayload.senderType === 'user') { - completePayload.senderType = 1; // 普通用户 - } else if (completePayload.senderType === 'customer_service' || completePayload.senderType === 'manager') { - completePayload.senderType = 2; // 客服 - } - console.log('已转换senderType:', completePayload.senderType); - } + // 直接使用接收到的数据作为payload,确保格式正确 + const payload = data; // 确保必要字段都已设置 - if (!completePayload.userId) { - console.error('错误: 缺少userId字段'); + if (!payload.senderId && !payload.userId) { + console.error('错误: 缺少用户ID字段'); ws.send(JSON.stringify({ type: 'error', message: '消息缺少用户ID' @@ -6302,11 +6309,32 @@ wss.on('connection', (ws, req) => { return; } - console.log('处理聊天消息 - 完整payload:', JSON.stringify(completePayload)); + // 确保senderType是数字类型 + if (payload.senderType && typeof payload.senderType === 'string') { + if (payload.senderType.includes('customer') || payload.senderType.includes('user')) { + payload.senderType = 1; // 普通用户 + } else if (payload.senderType.includes('service') || payload.senderType.includes('manager')) { + payload.senderType = 2; // 客服 + } + } else if (!payload.senderType) { + // 设置默认值 + const connection = connections.get(ws.connectionId); + if (connection && connection.isManager) { + payload.senderType = 2; + } else { + payload.senderType = 1; + } + } + + // 确保所有数字字段都是数字类型 + payload.senderType = Number(payload.senderType); + payload.contentType = Number(payload.contentType || 1); + + console.log('处理聊天消息 - 准备传递的payload:', JSON.stringify(payload)); // 调用handleChatMessage处理消息 try { - await handleChatMessage(ws, completePayload); + await handleChatMessage(ws, payload); console.log('消息处理完成'); } catch (error) { console.error('处理聊天消息时出错:', error); @@ -6552,11 +6580,15 @@ async function handleAuth(ws, data) { 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 + // 缺少必要的managerId或userId ws.send(JSON.stringify({ type: 'auth_error', - message: '客服认证失败:缺少必要的managerId' + message: '客服认证失败:缺少必要的managerId或userId' })); return; } @@ -7048,16 +7080,70 @@ async function handleChatMessage(ws, payload) { } else { // 获取会话信息以确定接收者 console.log('查询现有会话:', { conversationId }); - const [conversations] = await sequelize.query( - 'SELECT * FROM chat_conversations WHERE conversation_id = ?', - { replacements: [conversationId] } - ); - if (!conversations || conversations.length === 0) { - throw new Error('会话不存在'); + + // 检查是否是临时会话ID + if (conversationId && conversationId.startsWith('temp_')) { + console.log('检测到临时会话ID,需要创建真实会话:', conversationId); + + // 从临时会话ID中提取信息 + // 支持前端临时会话ID格式: temp_[currentUserId]_[targetId]_[timestamp] + const tempIdParts = conversationId.split('_'); + if (tempIdParts.length >= 4) { + // 根据连接类型确定正确的userId和managerId + let tempUserId, tempManagerId; + + if (connection.isUser) { + // 用户连接: currentUserId是用户ID,targetId是客服ID + tempUserId = tempIdParts[1]; + tempManagerId = tempIdParts[2]; + } else if (connection.isManager) { + // 客服连接: currentUserId是客服ID,targetId是用户ID + tempManagerId = tempIdParts[1]; + tempUserId = tempIdParts[2]; + } else { + // 默认情况,尝试判断哪个是用户ID哪个是客服ID + if (tempIdParts[1].includes('user_')) { + tempUserId = tempIdParts[1]; + tempManagerId = tempIdParts[2]; + } else { + tempUserId = tempIdParts[2]; + tempManagerId = tempIdParts[1]; + } + } + + console.log('从临时ID提取信息:', { tempManagerId, tempUserId }); + + // 创建或获取真实会话 + conversation = await createOrGetConversation(tempUserId, tempManagerId); + console.log('创建的真实会话:', conversation); + receiverId = tempManagerId; + } else { + console.error('无法解析临时会话ID:', conversationId); + throw new Error('无效的临时会话ID格式'); + } + } else { + // 正常查询现有会话 + const [conversations] = await sequelize.query( + 'SELECT * FROM chat_conversations WHERE conversation_id = ?', + { replacements: [conversationId] } + ); + if (!conversations || conversations.length === 0) { + console.warn('会话不存在,尝试从userId和managerId创建'); + + // 尝试从payload中获取managerId + if (payload.managerId) { + receiverId = validateManagerId(payload.managerId); + console.log('尝试使用payload中的managerId创建会话:', { userId: senderId, managerId: receiverId }); + conversation = await createOrGetConversation(senderId, receiverId); + } else { + throw new Error('会话不存在且无法确定客服ID'); + } + } else { + conversation = conversations[0]; + console.log('查询到的会话详情:', conversation); + receiverId = conversation.managerId; + } } - conversation = conversations[0]; - console.log('查询到的会话详情:', conversation); - receiverId = conversation.managerId; // 验证会话的userId是否与当前用户匹配,不匹配则修复 if (conversation.userId !== senderId) { diff --git a/server-example/test_message_storage.js b/server-example/test_message_storage.js new file mode 100644 index 0000000..c604692 --- /dev/null +++ b/server-example/test_message_storage.js @@ -0,0 +1,284 @@ +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); +});