From a4a0e8df26bb039ef1eff488c24608ec4a6d6686 Mon Sep 17 00:00:00 2001 From: cb <275647614@qq.com> Date: Tue, 23 Sep 2025 12:22:32 +0800 Subject: [PATCH] no message --- .../customer/CustomerServiceController.java | 35 +++-- .../templates/customer/service/index.html | 136 ++++++++++++++---- .../ruoyi/framework/config/ShiroConfig.java | 9 +- .../impl/CustomerServiceServiceImpl.java | 65 ++++++++- .../system/domain/ManualServiceSessions.java | 29 ++++ .../com/ruoyi/system/domain/UserSessions.java | 15 ++ .../system/ManualServiceSessionsMapper.xml | 38 ++++- .../mapper/system/UserSessionsMapper.xml | 30 ++-- 8 files changed, 296 insertions(+), 61 deletions(-) diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/CustomerServiceController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/CustomerServiceController.java index 3e814628..2f6604bd 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/CustomerServiceController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/CustomerServiceController.java @@ -1,6 +1,7 @@ package com.ruoyi.web.controller.customer; import java.util.List; +import javax.servlet.http.HttpServletRequest; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -134,23 +135,37 @@ public class CustomerServiceController extends BaseController */ @PostMapping("/transfer") @ResponseBody - public AjaxResult transferToManual(Long sessionId, String reason) + public AjaxResult transferToManual(Long sessionId, String reason, HttpServletRequest request) { + logger.info("[转人工服务] 收到转人工请求 - sessionId: {}, reason: {}", sessionId, reason); + logger.info("[转人工服务] 请求头信息 - Authorization: {}, Content-Type: {}", + request.getHeader("Authorization"), request.getHeader("Content-Type")); + try { + // 获取当前用户ID + // Long currentUserId = getUserId(); + // logger.info("[转人工服务] 当前用户ID: {}", currentUserId); + ManualServiceSessions manualSession = new ManualServiceSessions(); manualSession.setSessionId(sessionId); manualSession.setReason(reason); - manualSession.setStatus("PENDING"); - manualSession.setCreateBy(getLoginName()); + manualSession.setStatus("0"); // 0-待处理 + manualSession.setCreateBy(sessionId+""); + + logger.info("[转人工服务] 准备插入数据库 - 转人工会话对象: {}", manualSession); int result = customerServiceService.createManualServiceRequest(manualSession); + logger.info("[转人工服务] 数据库插入结果: {}", result); + if (result > 0) { + logger.info("[转人工服务] 转人工请求提交成功"); return AjaxResult.success("转人工请求已提交"); } else { + logger.error("[转人工服务] 转人工请求提交失败 - 数据库插入返回0"); return AjaxResult.error("转人工请求提交失败"); } } catch (Exception e) { - logger.error("转人工服务失败", e); + logger.error("[转人工服务] 转人工服务异常", e); return AjaxResult.error("转人工服务失败: " + e.getMessage()); } } @@ -180,10 +195,10 @@ public class CustomerServiceController extends BaseController { try { ManualServiceSessions manualSession = new ManualServiceSessions(); - manualSession.setUserId(requestId); - manualSession.setStatus("ACCEPTED"); + manualSession.setManualSessionId(requestId); // 设置主键ID + manualSession.setStatus("1"); // 1-已接受 manualSession.setServiceId(getUserId()); - manualSession.setUpdateBy(getLoginName()); + manualSession.setUpdateBy(requestId+""); int result = customerServiceService.updateManualServiceRequest(manualSession); if (result > 0) { @@ -206,10 +221,10 @@ public class CustomerServiceController extends BaseController { try { ManualServiceSessions manualSession = new ManualServiceSessions(); - manualSession.setUserId(requestId); - manualSession.setStatus("REJECTED"); + manualSession.setManualSessionId(requestId); // 设置主键ID + manualSession.setStatus("2"); // 2-已拒绝 manualSession.setServiceId(getUserId()); - manualSession.setUpdateBy(getLoginName()); + manualSession.setUpdateBy(requestId+""); int result = customerServiceService.updateManualServiceRequest(manualSession); if (result > 0) { diff --git a/ruoyi-admin/src/main/resources/templates/customer/service/index.html b/ruoyi-admin/src/main/resources/templates/customer/service/index.html index ab6f8ca2..8d3ef705 100644 --- a/ruoyi-admin/src/main/resources/templates/customer/service/index.html +++ b/ruoyi-admin/src/main/resources/templates/customer/service/index.html @@ -284,50 +284,100 @@ // 初始化WebSocket连接 function initWebSocket() { + console.log("[DEBUG] 开始初始化WebSocket连接..."); + if (typeof(WebSocket) == "undefined") { - console.log("您的浏览器不支持WebSocket"); + console.error("[ERROR] 您的浏览器不支持WebSocket"); return; } - var wsUrl = "ws://" + window.location.host + "/websocket/customer-service"; - wsConnection = new WebSocket(wsUrl); + var wsUrl = "ws://bwy.opsoul.com/websocket/customer-service"; + console.log("[DEBUG] WebSocket连接URL:", wsUrl); + + try { + wsConnection = new WebSocket(wsUrl); + console.log("[DEBUG] WebSocket对象创建成功"); + } catch (e) { + console.error("[ERROR] WebSocket对象创建失败:", e); + return; + } wsConnection.onopen = function() { - console.log("WebSocket连接已建立"); + console.log("[SUCCESS] WebSocket连接已建立"); + console.log("[DEBUG] WebSocket readyState:", wsConnection.readyState); }; wsConnection.onmessage = function(event) { - var message = JSON.parse(event.data); - handleWebSocketMessage(message); + console.log("[DEBUG] 收到WebSocket原始消息:", event.data); + try { + var message = JSON.parse(event.data); + console.log("[DEBUG] 解析后的消息对象:", message); + handleWebSocketMessage(message); + } catch (e) { + console.error("[ERROR] 解析WebSocket消息失败:", e, "原始数据:", event.data); + } }; - wsConnection.onclose = function() { - console.log("WebSocket连接已关闭"); + wsConnection.onclose = function(event) { + console.log("[WARN] WebSocket连接已关闭", { + code: event.code, + reason: event.reason, + wasClean: event.wasClean + }); // 尝试重连 + console.log("[DEBUG] 5秒后尝试重连WebSocket..."); setTimeout(initWebSocket, 5000); }; wsConnection.onerror = function(error) { - console.log("WebSocket连接错误:", error); + console.error("[ERROR] WebSocket连接错误:", error); + console.log("[DEBUG] WebSocket readyState:", wsConnection ? wsConnection.readyState : 'undefined'); }; } // 处理WebSocket消息 function handleWebSocketMessage(message) { + console.log('[DEBUG] 开始处理WebSocket消息:', message); + console.log('[DEBUG] 消息类型:', message.type); + switch(message.type) { case 'NEW_MESSAGE': + // 处理新消息 + console.log('[DEBUG] 处理新消息:', message); if (message.sessionId == currentSessionId) { appendMessage(message.content, false, message.timestamp); + console.log('[DEBUG] 新消息已添加到当前会话'); + } else { + console.log('[DEBUG] 新消息不属于当前会话,sessionId:', message.sessionId, '当前会话:', currentSessionId); } loadSessionList(); // 刷新会话列表 break; case 'SESSION_UPDATE': + // 处理会话更新 + console.log('[DEBUG] 处理会话更新:', message); loadSessionList(); break; case 'MANUAL_REQUEST': + // 处理转人工请求 + console.log('[SUCCESS] 收到转人工请求通知:', message); + console.log('[DEBUG] 开始刷新转人工请求列表...'); loadManualRequests(); + // 显示通知 + console.log('[DEBUG] 显示转人工请求通知...'); + if (typeof $.modal !== 'undefined') { + $.modal.msg('收到新的转人工请求'); + console.log('[DEBUG] 使用$.modal显示通知'); + } else { + alert('收到新的转人工请求'); + console.log('[DEBUG] 使用alert显示通知'); + } + break; + default: + console.warn('[WARN] 未知的消息类型:', message.type, '完整消息:', message); break; } + + console.log('[DEBUG] WebSocket消息处理完成'); } // 加载会话列表 @@ -474,28 +524,54 @@ // 加载转人工请求列表 function loadManualRequests() { - $.get('/customer/service/manual-requests', function(data) { - var html = ''; - if (data.code === 0 && data.data) { - data.data.forEach(function(request) { - html += '
'; - html += '
' + (request.customerName || '访客' + request.sessionId) + '
'; - html += '
' + (request.reason || '无') + '
'; - html += '
' + formatTime(request.createTime) + '
'; - html += '
'; - html += ' '; - html += ' '; - html += '
'; - html += '
'; + console.log('开始加载转人工请求列表...'); + + $.get('/customer/service/manual-requests') + .done(function(data) { + console.log('转人工请求API响应:', data); + + if (data.code === 0) { + var html = ''; + var requests = data.data || []; + + console.log('转人工请求数量:', requests.length); + + if (requests.length === 0) { + html = '
暂无转人工请求
'; + } else { + requests.forEach(function(request, index) { + console.log('处理转人工请求 ' + (index + 1) + ':', request); + html += '
'; + html += '
'; + html += '
会话ID: ' + request.sessionId + '
'; + html += '
原因: ' + (request.reason || '无') + '
'; + html += '
' + formatTime(request.createTime) + '
'; + html += '
'; + html += '
'; + html += ' '; + html += ' '; + html += '
'; + html += '
'; + }); + } + + $('#manualRequestContainer').html(html); + $('#manualRequestCount').text(requests.length); + console.log('转人工请求列表渲染完成,容器内容:', $('#manualRequestContainer').html()); + } else { + console.error('转人工请求API返回错误:', data.msg); + $('#manualRequestContainer').html('
加载失败: ' + data.msg + '
'); + } + }) + .fail(function(xhr, status, error) { + console.error('转人工请求API调用失败:', { + status: xhr.status, + statusText: xhr.statusText, + error: error, + response: xhr.responseText }); - $('#manualRequestCount').text(data.data.length); - } - $('#manualRequestContainer').html(html); - }); + $('#manualRequestContainer').html('
网络错误,请稍后重试
'); + }); } // 接受转人工请求 diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java index 9043ca07..bd3cd8e3 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ShiroConfig.java @@ -306,8 +306,13 @@ public class ShiroConfig filterChainDefinitionMap.put("/system/material/app/**", "anon"); // 客服回复接口 filterChainDefinitionMap.put("/system/customerServiceReply/app/**", "anon"); - - filterChainDefinitionMap.put("/system/chatHistory/app/**", "anon"); + // 聊天记录接口 + filterChainDefinitionMap.put("/system/chatHistory/**", "anon"); + + // 客服相关接口 + filterChainDefinitionMap.put("/customer/service/**", "anon"); + // WebSocket连接 + filterChainDefinitionMap.put("/websocket/**", "anon"); // 退出 logout地址,shiro去清除session filterChainDefinitionMap.put("/logout", "logout"); diff --git a/ruoyi-system/src/main/java/com/ruoyi/customer/service/impl/CustomerServiceServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/customer/service/impl/CustomerServiceServiceImpl.java index 4429c18f..1fbc67f8 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/customer/service/impl/CustomerServiceServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/customer/service/impl/CustomerServiceServiceImpl.java @@ -155,9 +155,68 @@ public class CustomerServiceServiceImpl implements ICustomerServiceService @Override public int createManualServiceRequest(ManualServiceSessions manualServiceSessions) { + System.out.println("[DEBUG] 开始创建转人工服务请求 - sessionId: " + manualServiceSessions.getSessionId() + ", reason: " + manualServiceSessions.getReason()); + manualServiceSessions.setCreateTime(DateUtils.getNowDate()); - manualServiceSessions.setStatus("pending"); - return manualServiceSessionsMapper.insertManualServiceSessions(manualServiceSessions); + manualServiceSessions.setStatus("0"); // 0-待处理 + + System.out.println("[DEBUG] 准备插入数据库 - 数据: " + manualServiceSessions.toString()); + int result = manualServiceSessionsMapper.insertManualServiceSessions(manualServiceSessions); + System.out.println("[DEBUG] 数据库插入结果: " + result); + + // 如果插入成功,通知所有在线客服 + if (result > 0) { + try { + System.out.println("[DEBUG] 数据库插入成功,开始发送WebSocket通知"); + + // 创建WebSocket消息 + com.alibaba.fastjson.JSONObject message = new com.alibaba.fastjson.JSONObject(); + message.put("type", "MANUAL_REQUEST"); + message.put("sessionId", manualServiceSessions.getSessionId()); + message.put("reason", manualServiceSessions.getReason()); + message.put("timestamp", System.currentTimeMillis()); + + System.out.println("[DEBUG] WebSocket消息内容: " + message.toJSONString()); + + // 通过反射获取WebSocket类并发送广播消息 + Class webSocketClass = Class.forName("com.ruoyi.web.websocket.CustomerServiceWebSocket"); + java.lang.reflect.Method getWebSocketMapMethod = webSocketClass.getMethod("getWebSocketMap"); + @SuppressWarnings("unchecked") + java.util.concurrent.ConcurrentHashMap webSocketMap = + (java.util.concurrent.ConcurrentHashMap) getWebSocketMapMethod.invoke(null); + + System.out.println("[DEBUG] 当前在线WebSocket连接数: " + webSocketMap.size()); + + // 向所有在线客服发送通知 + int successCount = 0; + int failCount = 0; + for (String key : webSocketMap.keySet()) { + Object webSocket = webSocketMap.get(key); + try { + System.out.println("[DEBUG] 向WebSocket连接发送消息: " + key); + java.lang.reflect.Method sendMessageMethod = webSocket.getClass().getMethod("sendMessage", String.class); + sendMessageMethod.invoke(webSocket, message.toJSONString()); + successCount++; + System.out.println("[DEBUG] 消息发送成功: " + key); + } catch (Exception e) { + failCount++; + System.err.println("[DEBUG] 向WebSocket连接发送消息失败: " + key + ", 错误: " + e.getMessage()); + } + } + + System.out.println("[DEBUG] WebSocket通知发送完成 - 成功: " + successCount + ", 失败: " + failCount); + + } catch (Exception e) { + // 记录日志但不影响主要业务流程 + System.err.println("[DEBUG] 发送WebSocket通知失败: " + e.getMessage()); + e.printStackTrace(); + } + } else { + System.err.println("[DEBUG] 数据库插入失败,不发送WebSocket通知"); + } + + System.out.println("[DEBUG] 创建转人工服务请求完成 - 返回结果: " + result); + return result; } /** @@ -182,7 +241,7 @@ public class CustomerServiceServiceImpl implements ICustomerServiceService public List getPendingManualRequests() { ManualServiceSessions manualServiceSessions = new ManualServiceSessions(); - manualServiceSessions.setStatus("pending"); + manualServiceSessions.setStatus("0"); // 0-待处理 return manualServiceSessionsMapper.selectManualServiceSessionsList(manualServiceSessions); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/ManualServiceSessions.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/ManualServiceSessions.java index 2f9e3282..0ac1e8f0 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/ManualServiceSessions.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/ManualServiceSessions.java @@ -32,6 +32,15 @@ public class ManualServiceSessions extends BaseEntity @Excel(name = "客服ID") private Long serviceId; + /** 客服人员 */ + @Excel(name = "客服人员") + private String serviceStaff; + + /** 开始时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + /** 转人工请求时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "转人工请求时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") @@ -99,6 +108,24 @@ public class ManualServiceSessions extends BaseEntity { return serviceId; } + public void setServiceStaff(String serviceStaff) + { + this.serviceStaff = serviceStaff; + } + + public String getServiceStaff() + { + return serviceStaff; + } + public void setStartTime(Date startTime) + { + this.startTime = startTime; + } + + public Date getStartTime() + { + return startTime; + } public void setRequestTime(Date requestTime) { this.requestTime = requestTime; @@ -170,6 +197,8 @@ public class ManualServiceSessions extends BaseEntity .append("sessionId", getSessionId()) .append("userId", getUserId()) .append("serviceId", getServiceId()) + .append("serviceStaff", getServiceStaff()) + .append("startTime", getStartTime()) .append("requestTime", getRequestTime()) .append("acceptTime", getAcceptTime()) .append("endTime", getEndTime()) diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/UserSessions.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/UserSessions.java index 5a5f75a5..e2321dac 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/UserSessions.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/UserSessions.java @@ -43,6 +43,10 @@ public class UserSessions extends BaseEntity @Excel(name = "最后活动时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date lastActivity; + /** 会话令牌 */ + @Excel(name = "会话令牌") + private String sessionToken; + public void setSessionId(Long sessionId) { this.sessionId = sessionId; @@ -98,6 +102,16 @@ public class UserSessions extends BaseEntity return lastActivity; } + public void setSessionToken(String sessionToken) + { + this.sessionToken = sessionToken; + } + + public String getSessionToken() + { + return sessionToken; + } + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) @@ -107,6 +121,7 @@ public class UserSessions extends BaseEntity .append("sessionEnd", getSessionEnd()) .append("status", getStatus()) .append("lastActivity", getLastActivity()) + .append("sessionToken", getSessionToken()) .append("createTime", getCreateTime()) .append("updateTime", getUpdateTime()) .toString(); diff --git a/ruoyi-system/src/main/resources/mapper/system/ManualServiceSessionsMapper.xml b/ruoyi-system/src/main/resources/mapper/system/ManualServiceSessionsMapper.xml index 694b6b97..95a721c3 100644 --- a/ruoyi-system/src/main/resources/mapper/system/ManualServiceSessionsMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/ManualServiceSessionsMapper.xml @@ -5,19 +5,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - + + + + + + + - select id, session_id, user_id, service_staff, start_time, end_time, status, create_time, update_time from manual_service_sessions + select id, session_id, user_id, service_id, service_staff, start_time, end_time, status, request_time, accept_time, reason, rating, feedback, create_time, update_time from manual_service_sessions - + insert into manual_service_sessions session_id, user_id, + service_id, service_staff, start_time, end_time, status, + request_time, + accept_time, + reason, + rating, + feedback, create_time, update_time, #{sessionId}, #{userId}, + #{serviceId}, #{serviceStaff}, #{startTime}, #{endTime}, #{status}, + #{requestTime}, + #{acceptTime}, + #{reason}, + #{rating}, + #{feedback}, #{createTime}, #{updateTime}, @@ -64,24 +82,30 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" session_id = #{sessionId}, user_id = #{userId}, + service_id = #{serviceId}, service_staff = #{serviceStaff}, start_time = #{startTime}, end_time = #{endTime}, status = #{status}, + request_time = #{requestTime}, + accept_time = #{acceptTime}, + reason = #{reason}, + rating = #{rating}, + feedback = #{feedback}, create_time = #{createTime}, update_time = #{updateTime}, - where id = #{id} + where id = #{manualSessionId} - delete from manual_service_sessions where id = #{id} + delete from manual_service_sessions where id = #{manualSessionId} delete from manual_service_sessions where id in - - #{id} + + #{manualSessionId} diff --git a/ruoyi-system/src/main/resources/mapper/system/UserSessionsMapper.xml b/ruoyi-system/src/main/resources/mapper/system/UserSessionsMapper.xml index 698f5be9..a3c1e2f6 100644 --- a/ruoyi-system/src/main/resources/mapper/system/UserSessionsMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/system/UserSessionsMapper.xml @@ -6,15 +6,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - - - - - + + + + + + + + - select session_id, user_id, session_token, create_time, update_time, status from user_sessions + select session_id, user_id, session_start, session_end, last_activity, session_token, status, create_time, update_time from user_sessions