no message
This commit is contained in:
parent
4d49e11023
commit
ea9d926a27
|
|
@ -3,15 +3,21 @@ package com.ruoyi.web.websocket;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.websocket.OnClose;
|
import javax.websocket.OnClose;
|
||||||
import javax.websocket.OnError;
|
import javax.websocket.OnError;
|
||||||
import javax.websocket.OnMessage;
|
import javax.websocket.OnMessage;
|
||||||
import javax.websocket.OnOpen;
|
import javax.websocket.OnOpen;
|
||||||
|
import javax.websocket.PongMessage;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
import javax.websocket.server.PathParam;
|
import javax.websocket.server.PathParam;
|
||||||
import javax.websocket.server.ServerEndpoint;
|
import javax.websocket.server.ServerEndpoint;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -41,12 +47,24 @@ public class CustomerServiceWebSocket {
|
||||||
/** concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象 */
|
/** concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象 */
|
||||||
private static final ConcurrentHashMap<String, CustomerServiceWebSocket> webSocketMap = new ConcurrentHashMap<>();
|
private static final ConcurrentHashMap<String, CustomerServiceWebSocket> webSocketMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/** 心跳定时器 */
|
||||||
|
private static final ScheduledExecutorService heartbeatExecutor = Executors.newScheduledThreadPool(10);
|
||||||
|
|
||||||
|
/** 心跳间隔时间(秒) */
|
||||||
|
private static final int HEARTBEAT_INTERVAL = 30;
|
||||||
|
|
||||||
|
/** 连接超时时间(毫秒) */
|
||||||
|
private static final long SESSION_TIMEOUT = 300000; // 5分钟
|
||||||
|
|
||||||
/** 与某个客户端的连接会话,需要通过它来给客户端发送数据 */
|
/** 与某个客户端的连接会话,需要通过它来给客户端发送数据 */
|
||||||
private Session session;
|
private Session session;
|
||||||
|
|
||||||
/** 接收userId */
|
/** 接收userId */
|
||||||
private String userId = "";
|
private String userId = "";
|
||||||
|
|
||||||
|
/** 心跳任务 */
|
||||||
|
private java.util.concurrent.ScheduledFuture<?> heartbeatTask;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注入客服服务
|
* 注入客服服务
|
||||||
*/
|
*/
|
||||||
|
|
@ -62,7 +80,16 @@ public class CustomerServiceWebSocket {
|
||||||
public void onOpen(Session session, @PathParam("userId") String userId) {
|
public void onOpen(Session session, @PathParam("userId") String userId) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
|
|
||||||
|
// 设置session超时时间
|
||||||
|
session.setMaxIdleTimeout(SESSION_TIMEOUT);
|
||||||
|
|
||||||
if (webSocketMap.containsKey(userId)) {
|
if (webSocketMap.containsKey(userId)) {
|
||||||
|
// 如果已存在连接,先清理旧的心跳任务
|
||||||
|
CustomerServiceWebSocket oldWebSocket = webSocketMap.get(userId);
|
||||||
|
if (oldWebSocket != null && oldWebSocket.heartbeatTask != null) {
|
||||||
|
oldWebSocket.heartbeatTask.cancel(true);
|
||||||
|
}
|
||||||
webSocketMap.remove(userId);
|
webSocketMap.remove(userId);
|
||||||
webSocketMap.put(userId, this);
|
webSocketMap.put(userId, this);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -70,7 +97,10 @@ public class CustomerServiceWebSocket {
|
||||||
addOnlineCount();
|
addOnlineCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("用户连接:" + userId + ",当前在线人数为:" + getOnlineCount());
|
// 启动心跳机制
|
||||||
|
startHeartbeat();
|
||||||
|
|
||||||
|
log.info("用户连接:" + userId + ",当前在线人数为:" + getOnlineCount() + ",连接超时设置为:" + SESSION_TIMEOUT + "ms");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sendMessage("连接成功");
|
sendMessage("连接成功");
|
||||||
|
|
@ -84,6 +114,9 @@ public class CustomerServiceWebSocket {
|
||||||
*/
|
*/
|
||||||
@OnClose
|
@OnClose
|
||||||
public void onClose() {
|
public void onClose() {
|
||||||
|
// 停止心跳任务
|
||||||
|
stopHeartbeat();
|
||||||
|
|
||||||
if (webSocketMap.containsKey(userId)) {
|
if (webSocketMap.containsKey(userId)) {
|
||||||
webSocketMap.remove(userId);
|
webSocketMap.remove(userId);
|
||||||
subOnlineCount();
|
subOnlineCount();
|
||||||
|
|
@ -105,6 +138,14 @@ public class CustomerServiceWebSocket {
|
||||||
log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount());
|
log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理Pong消息
|
||||||
|
*/
|
||||||
|
@OnMessage
|
||||||
|
public void onPong(PongMessage pongMessage, Session session) {
|
||||||
|
log.debug("收到用户{}的pong消息,连接正常", this.userId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 收到客户端消息后调用的方法
|
* 收到客户端消息后调用的方法
|
||||||
*
|
*
|
||||||
|
|
@ -280,4 +321,57 @@ public class CustomerServiceWebSocket {
|
||||||
log.error("发送消息给用户{}失败", userId, e);
|
log.error("发送消息给用户{}失败", userId, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动心跳机制
|
||||||
|
*/
|
||||||
|
private void startHeartbeat() {
|
||||||
|
if (heartbeatTask != null) {
|
||||||
|
heartbeatTask.cancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
heartbeatTask = heartbeatExecutor.scheduleWithFixedDelay(() -> {
|
||||||
|
try {
|
||||||
|
if (session != null && session.isOpen()) {
|
||||||
|
// 发送ping消息
|
||||||
|
ByteBuffer pingData = ByteBuffer.wrap("ping".getBytes());
|
||||||
|
session.getBasicRemote().sendPing(pingData);
|
||||||
|
log.debug("向用户{}发送ping消息", userId);
|
||||||
|
} else {
|
||||||
|
log.warn("用户{}的session已关闭,停止心跳", userId);
|
||||||
|
stopHeartbeat();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送心跳消息失败,用户: {}, 错误: {}", userId, e.getMessage());
|
||||||
|
stopHeartbeat();
|
||||||
|
}
|
||||||
|
}, HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
log.info("用户{}启动心跳机制,间隔{}秒", userId, HEARTBEAT_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止心跳机制
|
||||||
|
*/
|
||||||
|
private void stopHeartbeat() {
|
||||||
|
if (heartbeatTask != null && !heartbeatTask.isCancelled()) {
|
||||||
|
heartbeatTask.cancel(true);
|
||||||
|
log.info("用户{}停止心跳机制", userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送ping消息保持连接活跃
|
||||||
|
*/
|
||||||
|
public void sendPing() {
|
||||||
|
try {
|
||||||
|
if (session != null && session.isOpen()) {
|
||||||
|
ByteBuffer pingData = ByteBuffer.wrap("heartbeat".getBytes());
|
||||||
|
session.getBasicRemote().sendPing(pingData);
|
||||||
|
log.debug("向用户{}发送ping消息", userId);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送ping消息失败,用户: {}", userId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -199,6 +199,64 @@
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 会话分隔符样式 */
|
||||||
|
.session-separator {
|
||||||
|
margin: 20px 0;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-separator::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 1px;
|
||||||
|
background: #e7eaec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-separator-text {
|
||||||
|
background: #f8f8f9;
|
||||||
|
padding: 5px 15px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 12px;
|
||||||
|
border: 1px solid #e7eaec;
|
||||||
|
border-radius: 15px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-separator.current-session .session-separator-text {
|
||||||
|
background: #e6f7ff;
|
||||||
|
border-color: #1890ff;
|
||||||
|
color: #1890ff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 历史消息样式 */
|
||||||
|
.history-message {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-message .message-content {
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载状态样式 */
|
||||||
|
.loading-message {
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
font-style: italic;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-history-message {
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
font-style: italic;
|
||||||
|
margin: 40px 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="gray-bg">
|
<body class="gray-bg">
|
||||||
|
|
@ -307,6 +365,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 心跳相关变量
|
||||||
|
var heartbeatTimer = null;
|
||||||
|
var heartbeatInterval = 30000; // 30秒心跳间隔
|
||||||
|
var manualClose = false; // 手动关闭标志
|
||||||
|
|
||||||
// 创建WebSocket连接
|
// 创建WebSocket连接
|
||||||
function createWebSocketConnection(userId) {
|
function createWebSocketConnection(userId) {
|
||||||
// 根据当前页面协议动态选择WebSocket协议
|
// 根据当前页面协议动态选择WebSocket协议
|
||||||
|
|
@ -318,9 +381,12 @@
|
||||||
try {
|
try {
|
||||||
// 如果已有连接,先关闭
|
// 如果已有连接,先关闭
|
||||||
if (wsConnection && wsConnection.readyState !== WebSocket.CLOSED) {
|
if (wsConnection && wsConnection.readyState !== WebSocket.CLOSED) {
|
||||||
|
manualClose = true;
|
||||||
|
stopHeartbeat();
|
||||||
wsConnection.close();
|
wsConnection.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
manualClose = false;
|
||||||
wsConnection = new WebSocket(wsUrl);
|
wsConnection = new WebSocket(wsUrl);
|
||||||
console.log("[DEBUG] WebSocket对象创建成功");
|
console.log("[DEBUG] WebSocket对象创建成功");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -368,10 +434,19 @@
|
||||||
wsConnection.onopen = function() {
|
wsConnection.onopen = function() {
|
||||||
console.log("[SUCCESS] WebSocket连接已建立");
|
console.log("[SUCCESS] WebSocket连接已建立");
|
||||||
console.log("[DEBUG] WebSocket readyState:", wsConnection.readyState);
|
console.log("[DEBUG] WebSocket readyState:", wsConnection.readyState);
|
||||||
|
// 启动心跳机制
|
||||||
|
startHeartbeat();
|
||||||
};
|
};
|
||||||
|
|
||||||
wsConnection.onmessage = function(event) {
|
wsConnection.onmessage = function(event) {
|
||||||
console.log("[DEBUG] 收到WebSocket原始消息:", event.data);
|
console.log("[DEBUG] 收到WebSocket原始消息:", event.data);
|
||||||
|
|
||||||
|
// 处理pong消息
|
||||||
|
if (event.data === 'pong') {
|
||||||
|
console.log("[DEBUG] 收到服务端pong响应");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var message = JSON.parse(event.data);
|
var message = JSON.parse(event.data);
|
||||||
console.log("[DEBUG] 解析后的消息对象:", message);
|
console.log("[DEBUG] 解析后的消息对象:", message);
|
||||||
|
|
@ -387,9 +462,21 @@
|
||||||
reason: event.reason,
|
reason: event.reason,
|
||||||
wasClean: event.wasClean
|
wasClean: event.wasClean
|
||||||
});
|
});
|
||||||
// 尝试重连
|
|
||||||
|
// 停止心跳
|
||||||
|
stopHeartbeat();
|
||||||
|
|
||||||
|
// 如果不是手动关闭,则尝试重连
|
||||||
|
if (!manualClose) {
|
||||||
console.log("[DEBUG] 5秒后尝试重连WebSocket...");
|
console.log("[DEBUG] 5秒后尝试重连WebSocket...");
|
||||||
setTimeout(initWebSocket, 5000);
|
setTimeout(function() {
|
||||||
|
getCurrentUserId(function(serviceId) {
|
||||||
|
if (serviceId) {
|
||||||
|
initWebSocket(serviceId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
wsConnection.onerror = function(error) {
|
wsConnection.onerror = function(error) {
|
||||||
|
|
@ -398,6 +485,34 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 启动心跳机制
|
||||||
|
function startHeartbeat() {
|
||||||
|
console.log("[DEBUG] 启动心跳机制,间隔:", heartbeatInterval + "ms");
|
||||||
|
stopHeartbeat(); // 先停止之前的心跳
|
||||||
|
heartbeatTimer = setInterval(function() {
|
||||||
|
sendPing();
|
||||||
|
}, heartbeatInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止心跳机制
|
||||||
|
function stopHeartbeat() {
|
||||||
|
if (heartbeatTimer) {
|
||||||
|
console.log("[DEBUG] 停止心跳机制");
|
||||||
|
clearInterval(heartbeatTimer);
|
||||||
|
heartbeatTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送ping消息
|
||||||
|
function sendPing() {
|
||||||
|
if (wsConnection && wsConnection.readyState === WebSocket.OPEN) {
|
||||||
|
console.log("[DEBUG] 发送ping心跳消息");
|
||||||
|
wsConnection.send('ping');
|
||||||
|
} else {
|
||||||
|
console.log("[WARN] WebSocket连接不可用,无法发送ping消息");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 处理WebSocket消息
|
// 处理WebSocket消息
|
||||||
function handleWebSocketMessage(message) {
|
function handleWebSocketMessage(message) {
|
||||||
console.log('[DEBUG] 开始处理WebSocket消息:', message);
|
console.log('[DEBUG] 开始处理WebSocket消息:', message);
|
||||||
|
|
@ -491,7 +606,14 @@
|
||||||
html += ' <div style="display: flex; align-items: center;">';
|
html += ' <div style="display: flex; align-items: center;">';
|
||||||
html += ' <div class="session-avatar">' + avatar + '</div>';
|
html += ' <div class="session-avatar">' + avatar + '</div>';
|
||||||
html += ' <div class="session-info">';
|
html += ' <div class="session-info">';
|
||||||
html += ' <div class="session-name">' + (session.customerName || '访客' + session.sessionId) + '</div>';
|
// 构建显示名称:用户名:手机号
|
||||||
|
var displayName = '';
|
||||||
|
if (session.userId) {
|
||||||
|
displayName = '访客:' + session.userId;
|
||||||
|
} else {
|
||||||
|
displayName = '访客:未知号码';
|
||||||
|
}
|
||||||
|
html += ' <div class="session-name">' + displayName + '</div>';
|
||||||
html += ' <div class="session-last-msg">' + (session.lastMessage || '暂无消息') + '</div>';
|
html += ' <div class="session-last-msg">' + (session.lastMessage || '暂无消息') + '</div>';
|
||||||
html += ' <div class="session-time">' + formatTime(session.lastMessageTime) + '</div>';
|
html += ' <div class="session-time">' + formatTime(session.lastMessageTime) + '</div>';
|
||||||
html += ' </div>';
|
html += ' </div>';
|
||||||
|
|
@ -520,21 +642,154 @@
|
||||||
|
|
||||||
// 加载聊天历史
|
// 加载聊天历史
|
||||||
function loadChatHistory(sessionId) {
|
function loadChatHistory(sessionId) {
|
||||||
$.get('/customer/service/messages/' + sessionId, function(data) {
|
console.log('[DEBUG] 开始加载聊天历史 - sessionId:', sessionId);
|
||||||
var html = '';
|
|
||||||
if (data.code === 0 && data.data) {
|
// 显示加载状态
|
||||||
data.data.forEach(function(message) {
|
$('#chatMessages').html('<div class="loading-message">正在加载聊天历史...</div>');
|
||||||
html += createMessageHtml(message.content, message.senderType === 'CUSTOMER', message.createTime);
|
|
||||||
|
// 首先获取会话信息以获取用户ID
|
||||||
|
$.get('/customer/service/sessions')
|
||||||
|
.done(function(sessionsData) {
|
||||||
|
if (sessionsData.code === 0 && sessionsData.data) {
|
||||||
|
// 查找当前会话
|
||||||
|
var currentSession = sessionsData.data.find(function(session) {
|
||||||
|
return session.sessionId === sessionId;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (currentSession && currentSession.userId) {
|
||||||
|
console.log('[DEBUG] 找到会话用户ID:', currentSession.userId);
|
||||||
|
// 通过用户ID获取所有历史记录
|
||||||
|
loadChatHistoryByUserId(currentSession.userId, sessionId);
|
||||||
|
} else {
|
||||||
|
console.log('[DEBUG] 未找到用户ID,使用原有方式加载会话历史');
|
||||||
|
// 如果没有用户ID,回退到原有方式
|
||||||
|
loadChatHistoryBySessionId(sessionId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('[DEBUG] 获取会话列表失败,使用原有方式加载会话历史');
|
||||||
|
loadChatHistoryBySessionId(sessionId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function(xhr, status, error) {
|
||||||
|
console.error('[ERROR] 获取会话列表失败:', {
|
||||||
|
status: xhr.status,
|
||||||
|
statusText: xhr.statusText,
|
||||||
|
error: error
|
||||||
|
});
|
||||||
|
// 回退到原有方式
|
||||||
|
loadChatHistoryBySessionId(sessionId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 通过用户ID加载聊天历史
|
||||||
|
function loadChatHistoryByUserId(userId, currentSessionId) {
|
||||||
|
console.log('[DEBUG] 通过用户ID加载聊天历史 - userId:', userId);
|
||||||
|
|
||||||
|
$.get('/system/chatHistory/app/user/' + userId)
|
||||||
|
.done(function(data) {
|
||||||
|
console.log('[DEBUG] 用户聊天历史API响应:', data);
|
||||||
|
|
||||||
|
var html = '';
|
||||||
|
if (data.code === 0 && data.data && data.data.length > 0) {
|
||||||
|
console.log('[DEBUG] 用户聊天历史消息数量:', data.data.length);
|
||||||
|
|
||||||
|
// 按时间排序
|
||||||
|
var sortedMessages = data.data.sort(function(a, b) {
|
||||||
|
return new Date(a.createTime) - new Date(b.createTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 按会话分组显示
|
||||||
|
var sessionGroups = {};
|
||||||
|
sortedMessages.forEach(function(message) {
|
||||||
|
if (!sessionGroups[message.sessionId]) {
|
||||||
|
sessionGroups[message.sessionId] = [];
|
||||||
|
}
|
||||||
|
sessionGroups[message.sessionId].push(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 渲染消息,当前会话高亮显示
|
||||||
|
Object.keys(sessionGroups).forEach(function(sessionId) {
|
||||||
|
var isCurrentSession = sessionId === currentSessionId;
|
||||||
|
var separatorClass = isCurrentSession ? 'session-separator current-session' : 'session-separator';
|
||||||
|
var separatorText = isCurrentSession ? '当前会话' : '历史会话 - ' + sessionId;
|
||||||
|
|
||||||
|
html += '<div class="' + separatorClass + '">';
|
||||||
|
html += ' <span class="session-separator-text">' + separatorText + '</span>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
sessionGroups[sessionId].forEach(function(message) {
|
||||||
|
console.log('[DEBUG] 消息类型:', message.messageType, '消息内容:', message.content);
|
||||||
|
// messageType为"user"的消息显示在左边(received=true),其他消息显示在右边(received=false)
|
||||||
|
// received样式:左对齐,白色背景;sent样式:右对齐,蓝色背景
|
||||||
|
var isReceived = message.messageType === 'user';
|
||||||
|
html += createMessageHtml(message.content, isReceived, message.createTime, isCurrentSession);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('[DEBUG] 用户聊天历史数据为空');
|
||||||
|
html = '<div class="no-history-message">暂无聊天记录</div>';
|
||||||
|
}
|
||||||
|
|
||||||
$('#chatMessages').html(html);
|
$('#chatMessages').html(html);
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
|
console.log('[DEBUG] 用户聊天历史加载完成');
|
||||||
|
})
|
||||||
|
.fail(function(xhr, status, error) {
|
||||||
|
console.error('[ERROR] 用户聊天历史API调用失败:', {
|
||||||
|
status: xhr.status,
|
||||||
|
statusText: xhr.statusText,
|
||||||
|
error: error,
|
||||||
|
response: xhr.responseText
|
||||||
|
});
|
||||||
|
// 回退到原有方式
|
||||||
|
loadChatHistoryBySessionId(currentSessionId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过会话ID加载聊天历史(原有方式)
|
||||||
|
function loadChatHistoryBySessionId(sessionId) {
|
||||||
|
console.log('[DEBUG] 通过会话ID加载聊天历史 - sessionId:', sessionId);
|
||||||
|
|
||||||
|
$.get('/customer/service/messages/' + sessionId)
|
||||||
|
.done(function(data) {
|
||||||
|
console.log('[DEBUG] 会话聊天历史API响应:', data);
|
||||||
|
|
||||||
|
var html = '';
|
||||||
|
if (data.code === 0 && data.data) {
|
||||||
|
console.log('[DEBUG] 会话聊天历史消息数量:', data.data.length);
|
||||||
|
data.data.forEach(function(message) {
|
||||||
|
console.log('[DEBUG] 消息类型:', message.messageType, '消息内容:', message.content);
|
||||||
|
// messageType为"user"的消息显示在左边(received=true),其他消息显示在右边(received=false)
|
||||||
|
// received样式:左对齐,白色背景;sent样式:右对齐,蓝色背景
|
||||||
|
var isReceived = message.messageType === 'user';
|
||||||
|
html += createMessageHtml(message.content, isReceived, message.createTime, true);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('[DEBUG] 会话聊天历史数据为空或返回错误:', data);
|
||||||
|
html = '<div class="no-history-message">暂无聊天记录</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#chatMessages').html(html);
|
||||||
|
scrollToBottom();
|
||||||
|
console.log('[DEBUG] 会话聊天历史加载完成');
|
||||||
|
})
|
||||||
|
.fail(function(xhr, status, error) {
|
||||||
|
console.error('[ERROR] 会话聊天历史API调用失败:', {
|
||||||
|
status: xhr.status,
|
||||||
|
statusText: xhr.statusText,
|
||||||
|
error: error,
|
||||||
|
response: xhr.responseText
|
||||||
|
});
|
||||||
|
$('#chatMessages').html('<div class="no-history-message" style="color: #f5222d;">加载聊天历史失败,请稍后重试</div>');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建消息HTML
|
// 创建消息HTML
|
||||||
function createMessageHtml(content, isReceived, timestamp) {
|
function createMessageHtml(content, isReceived, timestamp, isCurrentSession) {
|
||||||
var messageClass = isReceived ? 'received' : 'sent';
|
var messageClass = isReceived ? 'received' : 'sent';
|
||||||
|
if (isCurrentSession === false) {
|
||||||
|
messageClass += ' history-message';
|
||||||
|
}
|
||||||
var html = '<div class="message-item ' + messageClass + '">';
|
var html = '<div class="message-item ' + messageClass + '">';
|
||||||
html += ' <div class="message-content">';
|
html += ' <div class="message-content">';
|
||||||
html += ' <div>' + content + '</div>';
|
html += ' <div>' + content + '</div>';
|
||||||
|
|
|
||||||
|
|
@ -304,4 +304,12 @@ public interface ICustomerServiceService
|
||||||
* @return 已接受的转人工记录,如果没有则返回null
|
* @return 已接受的转人工记录,如果没有则返回null
|
||||||
*/
|
*/
|
||||||
public ManualServiceSessions getAcceptedManualSessionByUserId(Long userId);
|
public ManualServiceSessions getAcceptedManualSessionByUserId(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理客服的已接受转人工记录
|
||||||
|
*
|
||||||
|
* @param serviceId 客服ID
|
||||||
|
* @return 清理数量
|
||||||
|
*/
|
||||||
|
public int cleanupAcceptedManualRequestsByServiceId(Long serviceId);
|
||||||
}
|
}
|
||||||
|
|
@ -621,4 +621,36 @@ public class CustomerServiceServiceImpl implements ICustomerServiceService
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理客服的已接受转人工记录
|
||||||
|
*
|
||||||
|
* @param serviceId 客服ID
|
||||||
|
* @return 清理数量
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int cleanupAcceptedManualRequestsByServiceId(Long serviceId)
|
||||||
|
{
|
||||||
|
ManualServiceSessions condition = new ManualServiceSessions();
|
||||||
|
condition.setServiceId(serviceId);
|
||||||
|
condition.setStatus("1"); // 已接受状态
|
||||||
|
|
||||||
|
List<ManualServiceSessions> acceptedRequests = manualServiceSessionsMapper.selectManualServiceSessionsList(condition);
|
||||||
|
|
||||||
|
if (acceptedRequests != null && !acceptedRequests.isEmpty()) {
|
||||||
|
System.out.println("[DEBUG] 清理客服 " + serviceId + " 的已接受转人工记录,数量: " + acceptedRequests.size());
|
||||||
|
|
||||||
|
// 将状态更新为已关闭
|
||||||
|
for (ManualServiceSessions request : acceptedRequests) {
|
||||||
|
request.setStatus("3"); // 3-已关闭(WebSocket断开)
|
||||||
|
request.setEndTime(DateUtils.getNowDate());
|
||||||
|
request.setUpdateTime(DateUtils.getNowDate());
|
||||||
|
manualServiceSessionsMapper.updateManualServiceSessions(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acceptedRequests.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue