You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
767 lines
26 KiB
767 lines
26 KiB
// 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 };
|
|
|