|
|
|
|
// pages/chat-detail/index.js
|
|
|
|
|
import socketManager from '../../utils/websocket';
|
|
|
|
|
const AuthManager = require('../../utils/auth.js');
|
|
|
|
|
|
|
|
|
|
Page({
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 页面的初始数据
|
|
|
|
|
*/
|
|
|
|
|
data: {
|
|
|
|
|
userId: '',
|
|
|
|
|
userName: '',
|
|
|
|
|
avatar: '',
|
|
|
|
|
messages: [],
|
|
|
|
|
inputValue: '',
|
|
|
|
|
goodsInfo: null, // 商品信息
|
|
|
|
|
connectionStatus: 'disconnected', // 连接状态: disconnected, connecting, connected, error
|
|
|
|
|
connectionMessage: '', // 连接状态提示消息
|
|
|
|
|
isMockMode: false, // 默认为真实WebSocket通信模式
|
|
|
|
|
isManager: false, // 是否是与客服的聊天
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生命周期函数--监听页面加载
|
|
|
|
|
*/
|
|
|
|
|
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连接');
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionStatus: 'connected',
|
|
|
|
|
connectionMessage: '已连接'
|
|
|
|
|
});
|
|
|
|
|
// 发送认证消息
|
|
|
|
|
this.sendAuthMessage();
|
|
|
|
|
} else {
|
|
|
|
|
// 应用级别连接不存在,创建新连接
|
|
|
|
|
console.log('创建新的WebSocket连接');
|
|
|
|
|
// 连接WebSocket服务器
|
|
|
|
|
socketManager.connect(wsUrl, {
|
|
|
|
|
maxReconnectAttempts: 5,
|
|
|
|
|
reconnectInterval: 3000,
|
|
|
|
|
heartbeatTime: 30000
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 监听网络状态变化
|
|
|
|
|
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);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 开始网络状态监听
|
|
|
|
|
*/
|
|
|
|
|
startNetworkListener: function() {
|
|
|
|
|
// 监听网络状态变化
|
|
|
|
|
wx.onNetworkStatusChange((res) => {
|
|
|
|
|
console.log('网络状态变化:', res);
|
|
|
|
|
|
|
|
|
|
if (!res.isConnected) {
|
|
|
|
|
// 网络断开
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionStatus: 'error',
|
|
|
|
|
connectionMessage: '网络已断开'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 显示网络断开提示
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '网络连接已断开',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
} else if (res.isConnected && this.data.connectionStatus === 'error') {
|
|
|
|
|
// 网络恢复且之前连接错误,尝试重连
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '网络已恢复,正在重连',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
this.reconnect();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 设置WebSocket事件监听器
|
|
|
|
|
*/
|
|
|
|
|
setupWebSocketListeners: function() {
|
|
|
|
|
// 连接成功
|
|
|
|
|
socketManager.on('open', () => {
|
|
|
|
|
const app = getApp();
|
|
|
|
|
const isManager = this.data.isManager ||
|
|
|
|
|
(app.globalData.userInfo?.userType === 'manager' ||
|
|
|
|
|
app.globalData.userInfo?.type === 'manager');
|
|
|
|
|
|
|
|
|
|
// 如果是客服,更新全局用户信息
|
|
|
|
|
if (isManager && app.globalData.userInfo) {
|
|
|
|
|
app.globalData.userInfo.userType = 'manager';
|
|
|
|
|
app.globalData.userInfo.type = 'manager';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionStatus: 'connected',
|
|
|
|
|
connectionMessage: '已连接'
|
|
|
|
|
});
|
|
|
|
|
console.log('WebSocket连接成功', { isManager });
|
|
|
|
|
|
|
|
|
|
// 发送认证消息
|
|
|
|
|
this.sendAuthMessage();
|
|
|
|
|
|
|
|
|
|
// WebSocket连接成功后,从服务器获取历史消息
|
|
|
|
|
console.log('WebSocket连接成功,准备获取历史消息');
|
|
|
|
|
// 延迟调用以确保认证完成
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('执行fetchHistoryMessages调用');
|
|
|
|
|
this.fetchHistoryMessages();
|
|
|
|
|
}, 1000); // 增加延迟时间确保认证流程完成
|
|
|
|
|
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '连接成功',
|
|
|
|
|
icon: 'success',
|
|
|
|
|
duration: 1500
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 接收消息
|
|
|
|
|
socketManager.on('message', (data) => {
|
|
|
|
|
console.log('收到WebSocket消息:', data);
|
|
|
|
|
|
|
|
|
|
// 处理历史消息列表响应
|
|
|
|
|
if (data.type === 'messages_list' && data.payload) {
|
|
|
|
|
this.handleHistoryMessages(data.payload);
|
|
|
|
|
} else {
|
|
|
|
|
this.handleReceivedMessage(data);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 连接关闭
|
|
|
|
|
socketManager.on('close', (res) => {
|
|
|
|
|
if (this.data.connectionStatus !== 'disconnected') {
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionStatus: 'disconnected',
|
|
|
|
|
connectionMessage: `连接已断开(${res.code || ''})`
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 连接错误
|
|
|
|
|
socketManager.on('error', (error) => {
|
|
|
|
|
console.error('WebSocket错误:', error);
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionStatus: 'error',
|
|
|
|
|
connectionMessage: '连接错误'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 重连中
|
|
|
|
|
socketManager.on('reconnecting', (attempts) => {
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionStatus: 'connecting',
|
|
|
|
|
connectionMessage: `重连中(${attempts}/5)...`
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 重连失败
|
|
|
|
|
socketManager.on('reconnectFailed', () => {
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionStatus: 'error',
|
|
|
|
|
connectionMessage: '重连失败,请检查网络'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
wx.showModal({
|
|
|
|
|
title: '连接失败',
|
|
|
|
|
content: '无法连接到服务器,请检查网络设置后重试。',
|
|
|
|
|
showCancel: false,
|
|
|
|
|
confirmText: '确定'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听WebSocket状态更新事件
|
|
|
|
|
socketManager.on('status', (status) => {
|
|
|
|
|
console.log('WebSocket状态更新:', status);
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionMessage: status.message || '状态更新'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 根据状态类型更新连接状态
|
|
|
|
|
switch(status.type) {
|
|
|
|
|
case 'connecting':
|
|
|
|
|
this.setData({ connectionStatus: 'connecting' });
|
|
|
|
|
break;
|
|
|
|
|
case 'connected':
|
|
|
|
|
this.setData({ connectionStatus: 'connected' });
|
|
|
|
|
break;
|
|
|
|
|
case 'disconnected':
|
|
|
|
|
this.setData({ connectionStatus: 'disconnected' });
|
|
|
|
|
break;
|
|
|
|
|
case 'error':
|
|
|
|
|
this.setData({ connectionStatus: 'error' });
|
|
|
|
|
if (status.isWarning) {
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: status.message,
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'reconnecting':
|
|
|
|
|
this.setData({ connectionStatus: 'reconnecting' });
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听消息发送成功事件
|
|
|
|
|
socketManager.on('sendSuccess', (data) => {
|
|
|
|
|
console.log('消息发送成功:', data);
|
|
|
|
|
// 可以在这里更新UI状态,如显示已发送标记等
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 监听消息发送失败事件
|
|
|
|
|
socketManager.on('sendError', (error) => {
|
|
|
|
|
console.error('消息发送失败:', error);
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '消息发送失败,请重试',
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 发送认证消息
|
|
|
|
|
*/
|
|
|
|
|
sendAuthMessage: function() {
|
|
|
|
|
// 严格使用app.globalData.userInfo中的用户ID(从服务器获取的正式ID)
|
|
|
|
|
const app = getApp();
|
|
|
|
|
let currentUserId = '';
|
|
|
|
|
|
|
|
|
|
// 获取用户ID优先级:
|
|
|
|
|
// 1. 从全局userInfo获取(已登录用户的正式ID)
|
|
|
|
|
// 2. 从本地存储的userInfo获取
|
|
|
|
|
// 注意:不再生成任何形式的临时ID或本地ID
|
|
|
|
|
if (app.globalData.userInfo && app.globalData.userInfo.userId) {
|
|
|
|
|
currentUserId = String(app.globalData.userInfo.userId);
|
|
|
|
|
console.log('从全局userInfo获取正式用户ID进行认证:', currentUserId);
|
|
|
|
|
} else {
|
|
|
|
|
const userInfo = wx.getStorageSync('userInfo');
|
|
|
|
|
if (userInfo && userInfo.userId) {
|
|
|
|
|
currentUserId = String(userInfo.userId);
|
|
|
|
|
console.log('从本地userInfo获取正式用户ID进行认证:', currentUserId);
|
|
|
|
|
} else {
|
|
|
|
|
console.error('警告:未找到有效的用户ID,无法进行WebSocket认证');
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const userInfo = app.globalData.userInfo || {};
|
|
|
|
|
const isManager = this.data.isManager || (userInfo.userType === 'manager' || userInfo.type === 'manager');
|
|
|
|
|
|
|
|
|
|
// 构建与后端匹配的认证信息格式
|
|
|
|
|
const authData = {
|
|
|
|
|
type: 'auth',
|
|
|
|
|
payload: {
|
|
|
|
|
userId: currentUserId,
|
|
|
|
|
managerId: isManager ? (userInfo.managerId || currentUserId) : null,
|
|
|
|
|
userName: userInfo.userName || userInfo.name || (isManager ? '客服' : '用户'),
|
|
|
|
|
userType: isManager ? 2 : 1, // 1=用户,2=客服
|
|
|
|
|
conversationId: this.data.conversationId,
|
|
|
|
|
targetId: this.data.userId
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
console.log('发送认证消息:', authData);
|
|
|
|
|
|
|
|
|
|
// 发送认证消息
|
|
|
|
|
socketManager.send({
|
|
|
|
|
type: 'auth',
|
|
|
|
|
data: authData.payload, // 注意:这里直接传递payload对象
|
|
|
|
|
success: () => {
|
|
|
|
|
console.log('认证消息发送成功');
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionStatus: 'connected',
|
|
|
|
|
connectionMessage: '已连接'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 认证成功后清空消息队列
|
|
|
|
|
if (socketManager._flushMessageQueue) {
|
|
|
|
|
socketManager._flushMessageQueue();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
console.error('认证消息发送失败:', err);
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionStatus: 'error',
|
|
|
|
|
connectionMessage: '连接失败,请重试'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理接收到的消息
|
|
|
|
|
*/
|
|
|
|
|
handleReceivedMessage: function(data) {
|
|
|
|
|
console.log('收到消息:', data);
|
|
|
|
|
|
|
|
|
|
// 确保数据格式正确
|
|
|
|
|
if (!data || typeof data !== 'object') {
|
|
|
|
|
console.error('收到无效消息:', data);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加消息数据格式验证
|
|
|
|
|
if (!data.type) {
|
|
|
|
|
console.error('消息缺少必要字段:', data);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据消息类型处理
|
|
|
|
|
if (data.type === 'new_message' && data.payload) {
|
|
|
|
|
// 处理服务器推送的新消息
|
|
|
|
|
this.processServerMessage(data.payload);
|
|
|
|
|
} else if (data.type === 'message') {
|
|
|
|
|
// 处理旧格式的普通消息(向后兼容)
|
|
|
|
|
this.processChatMessage(data);
|
|
|
|
|
} else if (data.type === 'system') {
|
|
|
|
|
// 处理系统消息
|
|
|
|
|
const systemMessage = {
|
|
|
|
|
type: 'system',
|
|
|
|
|
content: data.content || '',
|
|
|
|
|
time: this.getFormattedTime(),
|
|
|
|
|
showTime: true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 如果是警告消息,添加警告标记
|
|
|
|
|
if (data.isWarning) {
|
|
|
|
|
systemMessage.isWarning = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const messages = [...this.data.messages];
|
|
|
|
|
messages.push(systemMessage);
|
|
|
|
|
|
|
|
|
|
this.setData({ messages });
|
|
|
|
|
|
|
|
|
|
// 保存消息到本地存储
|
|
|
|
|
this.saveMessagesToStorage(messages);
|
|
|
|
|
|
|
|
|
|
this.scrollToBottom();
|
|
|
|
|
} else if (data.type === 'status') {
|
|
|
|
|
// 处理状态更新消息
|
|
|
|
|
this.setData({
|
|
|
|
|
connectionMessage: data.message || '连接状态更新'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: data.message || '状态更新',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
} else if (data.type === 'message_sent') {
|
|
|
|
|
// 处理服务器确认消息
|
|
|
|
|
console.log('服务器确认消息已送达:', data.payload);
|
|
|
|
|
} else if (data.type === 'conversation_created') {
|
|
|
|
|
// 处理会话创建成功响应
|
|
|
|
|
console.log('会话创建成功,收到会话ID:', data.conversationId);
|
|
|
|
|
|
|
|
|
|
if (data.conversationId) {
|
|
|
|
|
// 更新本地会话ID
|
|
|
|
|
const newConversationId = data.conversationId;
|
|
|
|
|
this.setData({
|
|
|
|
|
conversationId: newConversationId
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log('本地会话ID已更新:', newConversationId);
|
|
|
|
|
|
|
|
|
|
// 更新所有本地存储的消息,使用新的会话ID
|
|
|
|
|
const messages = [...this.data.messages];
|
|
|
|
|
const currentUserId = wx.getStorageSync('userId') || '';
|
|
|
|
|
|
|
|
|
|
// 查找需要重新发送的消息(使用临时会话ID且发送失败的消息)
|
|
|
|
|
const messagesToResend = [];
|
|
|
|
|
|
|
|
|
|
// 遍历并更新消息中的会话ID
|
|
|
|
|
messages.forEach(msg => {
|
|
|
|
|
if (msg.conversationId && msg.conversationId.startsWith('temp_')) {
|
|
|
|
|
msg.conversationId = newConversationId;
|
|
|
|
|
// 收集需要重新发送的消息(用户自己发送的,并且不是系统消息)
|
|
|
|
|
if (msg.senderId === currentUserId && msg.type === 'chat_message') {
|
|
|
|
|
messagesToResend.push(msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 关键修复:处理待发送列表中的消息
|
|
|
|
|
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
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// 处理其他未定义类型的消息
|
|
|
|
|
console.log('收到未处理的消息类型:', data.type, data);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 处理聊天消息(旧格式,向后兼容)
|
|
|
|
|
processChatMessage: function(message) {
|
|
|
|
|
// 确保消息对象包含必要的字段
|
|
|
|
|
const content = message.content || '';
|
|
|
|
|
const isImage = message.isImage || false;
|
|
|
|
|
const app = getApp();
|
|
|
|
|
|
|
|
|
|
// 增强的用户ID获取逻辑,确保能正确获取当前用户ID
|
|
|
|
|
const currentUser = wx.getStorageSync('userId') ||
|
|
|
|
|
(app.globalData.userInfo?.userId || '') ||
|
|
|
|
|
AuthManager.getUserId() ||
|
|
|
|
|
'';
|
|
|
|
|
|
|
|
|
|
const currentUserType = app.globalData.userType || wx.getStorageSync('userType') || 'customer';
|
|
|
|
|
const isManager = currentUserType === 'manager' || currentUserType === 'customer_service';
|
|
|
|
|
const isCustomer = currentUserType === 'buyer' || currentUserType === 'seller' || currentUserType === 'both';
|
|
|
|
|
|
|
|
|
|
console.log('处理聊天消息 - 用户类型:', currentUserType, '是否客服:', isManager, '是否普通用户:', isCustomer, '当前用户ID:', currentUser);
|
|
|
|
|
console.log('消息数据:', { messageFrom: message.from, messageSenderId: message.senderId, messageReceiverId: message.receiverId });
|
|
|
|
|
|
|
|
|
|
// 确定消息发送方(me或other)
|
|
|
|
|
let sender = 'other';
|
|
|
|
|
|
|
|
|
|
// 自己发送的消息始终显示在右侧(me) - 增强的判断逻辑
|
|
|
|
|
const isSelfMessage = message.from === currentUser ||
|
|
|
|
|
message.senderId === currentUser ||
|
|
|
|
|
(message.sender_id && message.sender_id === currentUser);
|
|
|
|
|
|
|
|
|
|
if (isSelfMessage) {
|
|
|
|
|
sender = 'me';
|
|
|
|
|
console.log('消息是自己发送的,显示在右侧');
|
|
|
|
|
} else {
|
|
|
|
|
sender = 'other';
|
|
|
|
|
console.log('消息是他人发送的,显示在左侧');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 客服接收到的发送给自己的消息显示在左侧(other)
|
|
|
|
|
// 只有当消息不是自己发送的时候,才应用这个规则
|
|
|
|
|
if (!isSelfMessage && isManager && message.receiverId && message.receiverId === this.data.userId) {
|
|
|
|
|
sender = 'other';
|
|
|
|
|
console.log('客服接收到的消息,显示在左侧');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 【关键修复】针对真实用户ID的特殊处理逻辑
|
|
|
|
|
let isRealUserMessage = false;
|
|
|
|
|
if (this.data.isRealUserId) {
|
|
|
|
|
// 确保消息关联到正确的用户
|
|
|
|
|
console.log('处理真实用户相关消息:', {
|
|
|
|
|
senderId: message.senderId,
|
|
|
|
|
receiverId: message.receiverId,
|
|
|
|
|
targetUserId: this.data.userId,
|
|
|
|
|
isSelfMessage: sender === 'me'
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 对于目标测试用户ID的特殊处理
|
|
|
|
|
if (this.data.userId === 'user_1763452685075_rea007flq') {
|
|
|
|
|
// 确保消息被正确关联到该用户
|
|
|
|
|
if (message.senderId === 'user_1763452685075_rea007flq' || message.receiverId === 'user_1763452685075_rea007flq') {
|
|
|
|
|
console.log('✓ 消息与目标测试用户ID匹配');
|
|
|
|
|
isRealUserMessage = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建新消息对象
|
|
|
|
|
const newMessage = {
|
|
|
|
|
id: message.id || 'msg_' + Date.now(),
|
|
|
|
|
type: 'message',
|
|
|
|
|
content: content,
|
|
|
|
|
isImage: isImage,
|
|
|
|
|
sender: sender,
|
|
|
|
|
time: message.timestamp ? this.formatTimestampToTime(message.timestamp) : this.getFormattedTime(),
|
|
|
|
|
showTime: this.shouldShowTime(),
|
|
|
|
|
shortTime: this.extractShortTime(this.getFormattedTime()),
|
|
|
|
|
status: 'sent', // 接收的消息默认已发送
|
|
|
|
|
senderType: message.senderType || 'unknown', // 保存发送方类型
|
|
|
|
|
serverData: message, // 保存完整的服务器数据
|
|
|
|
|
// 【关键修复】添加消息来源标记
|
|
|
|
|
isRealUserMessage: isRealUserMessage,
|
|
|
|
|
isRealServerMessage: !!message.messageId || !!message.id
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.addReceivedMessage(newMessage);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 处理服务器推送的新消息
|
|
|
|
|
processServerMessage: function(messageData) {
|
|
|
|
|
console.log('处理服务器推送的新消息:', messageData);
|
|
|
|
|
|
|
|
|
|
// 获取当前用户信息
|
|
|
|
|
const app = getApp();
|
|
|
|
|
|
|
|
|
|
// 增强的用户ID获取逻辑,确保能正确获取当前用户ID
|
|
|
|
|
const currentUser = wx.getStorageSync('userId') ||
|
|
|
|
|
(app.globalData.userInfo?.userId || '') ||
|
|
|
|
|
AuthManager.getUserId() ||
|
|
|
|
|
'';
|
|
|
|
|
|
|
|
|
|
const currentUserType = app.globalData.userType || wx.getStorageSync('userType') || 'customer';
|
|
|
|
|
const isManager = currentUserType === 'manager' || currentUserType === 'customer_service';
|
|
|
|
|
const isCustomer = currentUserType === 'buyer' || currentUserType === 'seller' || currentUserType === 'both';
|
|
|
|
|
|
|
|
|
|
console.log('处理服务器消息 - 用户类型:', currentUserType, '是否客服:', isManager, '是否普通用户:', isCustomer, '当前用户ID:', currentUser);
|
|
|
|
|
console.log('消息数据 - senderId:', messageData.senderId, 'receiverId:', messageData.receiverId);
|
|
|
|
|
|
|
|
|
|
// 确定消息发送方(me或other)
|
|
|
|
|
let sender = 'other';
|
|
|
|
|
|
|
|
|
|
// 增强的自己发送的消息判断逻辑,支持多种字段名和格式
|
|
|
|
|
const isSelfMessage = String(messageData.senderId) === String(currentUser) ||
|
|
|
|
|
String(messageData.sender_id) === String(currentUser) ||
|
|
|
|
|
String(messageData.from) === String(currentUser);
|
|
|
|
|
|
|
|
|
|
if (isSelfMessage) {
|
|
|
|
|
sender = 'me';
|
|
|
|
|
console.log('消息是自己发送的,显示在右侧');
|
|
|
|
|
} else {
|
|
|
|
|
sender = 'other';
|
|
|
|
|
console.log('消息是他人发送的,显示在左侧');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 【关键修复】针对真实用户ID的特殊处理逻辑
|
|
|
|
|
let isRealUserMessage = false;
|
|
|
|
|
if (this.data.isRealUserId) {
|
|
|
|
|
// 检查消息是否与当前真实用户关联
|
|
|
|
|
if (messageData.senderId === this.data.userId || messageData.receiverId === this.data.userId) {
|
|
|
|
|
console.log('✓ 消息与当前真实用户ID匹配');
|
|
|
|
|
isRealUserMessage = true;
|
|
|
|
|
|
|
|
|
|
// 对于目标测试用户ID的特殊处理
|
|
|
|
|
if (this.data.userId === 'user_1763452685075_rea007flq') {
|
|
|
|
|
console.log('=== 处理目标测试用户的消息 ===');
|
|
|
|
|
// 确保消息被正确关联并显示
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理消息内容,支持嵌套的data字段
|
|
|
|
|
const messageContent = messageData.content || (messageData.data && messageData.data.content) || '';
|
|
|
|
|
const contentType = messageData.contentType || (messageData.data && messageData.data.contentType) || 1;
|
|
|
|
|
const isImage = contentType === 2 || (messageData.fileUrl && contentType !== 1);
|
|
|
|
|
|
|
|
|
|
// 调试:记录服务器返回的原始时间数据
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建符合前端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
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 特殊处理客服消息显示逻辑
|
|
|
|
|
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
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 验证真实用户消息功能的测试方法
|
|
|
|
|
* 用于手动测试系统对真实用户消息的处理能力
|
|
|
|
|
*/
|
|
|
|
|
testRealUserMessageFunctionality: function() {
|
|
|
|
|
console.log('=== 开始真实用户消息功能测试 ===');
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 测试模拟消息
|
|
|
|
|
const testMessage = {
|
|
|
|
|
id: 'test_msg_' + Date.now(),
|
|
|
|
|
type: 'message',
|
|
|
|
|
content: '这是一条测试消息,用于验证真实用户消息功能',
|
|
|
|
|
sender: 'other',
|
|
|
|
|
time: this.getFormattedTime(),
|
|
|
|
|
shortTime: this.extractShortTime(this.getFormattedTime()),
|
|
|
|
|
status: 'sent',
|
|
|
|
|
isRealUserMessage: true,
|
|
|
|
|
serverData: {
|
|
|
|
|
messageId: 'test_' + Date.now(),
|
|
|
|
|
senderId: this.data.userId,
|
|
|
|
|
receiverId: wx.getStorageSync('userId'),
|
|
|
|
|
createdAt: Date.now()
|
|
|
|
|
},
|
|
|
|
|
debugInfo: {
|
|
|
|
|
testMode: true,
|
|
|
|
|
testTime: Date.now()
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 添加测试消息
|
|
|
|
|
this.addReceivedMessage(testMessage);
|
|
|
|
|
|
|
|
|
|
// 显示测试成功提示
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '真实用户消息测试完成',
|
|
|
|
|
icon: 'success',
|
|
|
|
|
duration: 2000
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
console.log('=== 真实用户消息功能测试完成 ===');
|
|
|
|
|
return true;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('测试过程中出现错误:', e);
|
|
|
|
|
wx.showToast({
|
|
|
|
|
title: '测试失败: ' + e.message,
|
|
|
|
|
icon: 'none',
|
|
|
|
|
duration: 3000
|
|
|
|
|
});
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 系统稳定性综合检查
|
|
|
|
|
* 用于检测和修复潜在的系统稳定性问题
|
|
|
|
|
*/
|
|
|
|
|
performStabilityCheck: function() {
|
|
|
|
|
console.log('=== 开始系统稳定性检查 ===');
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 1. 检查必要的数据结构是否存在
|
|
|
|
|
if (!this.data || !Array.isArray(this.data.messages)) {
|
|
|
|
|
console.warn('消息列表结构异常,重新初始化');
|
|
|
|
|
this.setData({ messages: [] });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 清理无效的消息数据
|
|
|
|
|
const validMessages = this.data.messages.filter(msg => {
|
|
|
|
|
if (!msg || typeof msg !== 'object') {
|
|
|
|
|
console.warn('检测到无效消息对象,已过滤');
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 关键修复:完全移除消息内容验证,确保所有消息都能通过过滤
|
|
|
|
|
// 当用户清理缓存后,本地存储的消息可能缺少一些字段,但应该保留
|
|
|
|
|
// 从服务器获取的消息可能格式不同,也应该保留
|
|
|
|
|
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
|
|
|
|
|
});
|
|
|
|
|
}, 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 () {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
});
|