diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/config/WebSocketConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/config/WebSocketConfig.java index ceacb3a7..d3c8bef9 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/config/WebSocketConfig.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/config/WebSocketConfig.java @@ -1,23 +1,41 @@ package com.ruoyi.web.config; -import org.springframework.context.annotation.Bean; +import com.ruoyi.customer.service.ICustomerServiceService; +import com.ruoyi.web.websocket.CustomerServiceWebSocket; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; +import org.springframework.context.annotation.Bean; + +import javax.annotation.PostConstruct; /** - * WebSocket配置 + * WebSocket配置类 + * 用于注入服务到WebSocket端点 * * @author ruoyi + * @date 2024-01-01 */ @Configuration public class WebSocketConfig { + @Autowired + private ICustomerServiceService customerServiceService; + /** - * 注入ServerEndpointExporter, - * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint + * 注入ServerEndpointExporter,这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } + + /** + * 初始化时将服务注入到WebSocket中 + */ + @PostConstruct + public void init() { + CustomerServiceWebSocket customerServiceWebSocket = new CustomerServiceWebSocket(); + customerServiceWebSocket.setCustomerServiceService(customerServiceService); + } } \ No newline at end of file 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 2f6604bd..e13a8acd 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,8 @@ package com.ruoyi.web.controller.customer; import java.util.List; +import java.util.Map; +import java.util.HashMap; import javax.servlet.http.HttpServletRequest; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.beans.factory.annotation.Autowired; @@ -9,7 +11,9 @@ import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.enums.BusinessType; @@ -22,6 +26,8 @@ import com.ruoyi.system.domain.ManualServiceSessions; import com.ruoyi.customer.service.ICustomerServiceService; import com.ruoyi.system.domain.ChatHistory; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.web.controller.customer.dto.TransferToManualRequest; +import com.ruoyi.web.websocket.CustomerServiceWebSocket; /** * 客服系统Controller @@ -79,7 +85,7 @@ public class CustomerServiceController extends BaseController */ @GetMapping("/sessions/{sessionId}") @ResponseBody - public AjaxResult getSession(@PathVariable Long sessionId) + public AjaxResult getSession(@PathVariable String sessionId) { try { UserSessions session = customerServiceService.selectUserSessionsById(sessionId); @@ -95,7 +101,7 @@ public class CustomerServiceController extends BaseController */ @GetMapping("/messages/{sessionId}") @ResponseBody - public AjaxResult getChatHistory(@PathVariable Long sessionId) + public AjaxResult getChatHistory(@PathVariable String sessionId) { try { List messages = customerServiceService.getChatHistoryBySessionId(sessionId); @@ -111,19 +117,40 @@ public class CustomerServiceController extends BaseController */ @PostMapping("/send") @ResponseBody - public AjaxResult sendMessage(ChatHistory chatHistory) - { + public AjaxResult sendMessage(@RequestParam String sessionId, @RequestParam String content, @RequestParam String senderType) { try { - // 设置发送者信息 - chatHistory.setSenderId(getUserId()); - chatHistory.setSenderType("SERVICE"); - - int result = customerServiceService.sendMessage(chatHistory); - if (result > 0) { - return AjaxResult.success("消息发送成功"); - } else { - return AjaxResult.error("消息发送失败"); + // 根据sessionId查找对应的用户ID + UserSessions userSession = customerServiceService.selectUserSessionsById(sessionId); + if (userSession == null) { + return AjaxResult.error("会话不存在"); } + + // 创建聊天记录 + ChatHistory chatHistory = new ChatHistory(); + chatHistory.setSessionId(sessionId); + chatHistory.setContent(content); + chatHistory.setUserId(userSession.getUserId().toString()); // 设置为客户的userId + chatHistory.setSenderId(getUserId()); // 发送者ID为当前登录的客服ID + chatHistory.setSenderType("SERVICE"); // 发送者类型为客服 + chatHistory.setMessageType("service"); // 消息类型为客服消息 + chatHistory.setCreateTime(new java.util.Date()); + + // 保存聊天记录 + customerServiceService.insertChatHistory(chatHistory); + + // 通过WebSocket发送消息给客户端 + // 创建JSON格式的WebSocket消息 + com.alibaba.fastjson.JSONObject wsMessage = new com.alibaba.fastjson.JSONObject(); + wsMessage.put("type", "message"); + wsMessage.put("content", content); + + // 发送JSON格式的WebSocket消息给客户端 + CustomerServiceWebSocket.sendInfo(wsMessage.toJSONString(), userSession.getUserId().toString()); + + logger.info("[发送消息] WebSocket消息已发送 - userId: {}, sessionId: {}, content: {}, messageFormat: JSON", + userSession.getUserId(), sessionId, content); + + return AjaxResult.success(); } catch (Exception e) { logger.error("发送消息失败", e); return AjaxResult.error("发送消息失败: " + e.getMessage()); @@ -135,40 +162,103 @@ public class CustomerServiceController extends BaseController */ @PostMapping("/transfer") @ResponseBody - public AjaxResult transferToManual(Long sessionId, String reason, HttpServletRequest request) + public AjaxResult transferToManual(@RequestBody TransferToManualRequest request, HttpServletRequest httpRequest) { - logger.info("[转人工服务] 收到转人工请求 - sessionId: {}, reason: {}", sessionId, reason); + logger.info("[转人工服务] 收到转人工请求 - 原始请求: {}", request); logger.info("[转人工服务] 请求头信息 - Authorization: {}, Content-Type: {}", - request.getHeader("Authorization"), request.getHeader("Content-Type")); + httpRequest.getHeader("Authorization"), httpRequest.getHeader("Content-Type")); + + // 解析参数 + Long userId = request.getUserIdAsLong(); + String reason = request.getReasonAsString(); + + logger.info("[转人工服务] 解析后参数 - userId: {}, reason: {}", userId, reason); + logger.info("[转人工服务] 请求参数验证 - userId是否为空: {}, reason是否为空: {}", + userId == null, reason == null || reason.trim().isEmpty()); + + // 参数验证 + if (userId == null) { + logger.error("[转人工服务] 参数验证失败 - userId为空或格式错误,原始值: {}", request.getUserId()); + return AjaxResult.error("用户ID不能为空或格式错误"); + } + + if (reason == null || reason.trim().isEmpty()) { + logger.error("[转人工服务] 参数验证失败 - reason为空"); + return AjaxResult.error("转人工原因不能为空"); + } try { - // 获取当前用户ID - // Long currentUserId = getUserId(); - // logger.info("[转人工服务] 当前用户ID: {}", currentUserId); + // 生成或查找SessionId + String sessionId = generateOrFindSessionId(userId); + logger.info("[转人工服务] SessionId生成结果 - userId: {}, sessionId: {}", userId, sessionId); + + // 防重复提交:检查是否已有待处理的转人工记录 + List existingRequests = customerServiceService.getPendingManualRequestsByUserId(userId); + if (existingRequests != null && !existingRequests.isEmpty()) { + logger.warn("[转人工服务] 该用户已有待处理的转人工记录 - userId: {}, 数量: {}", userId, existingRequests.size()); + return AjaxResult.error("您已提交转人工请求,请等待客服处理"); + } ManualServiceSessions manualSession = new ManualServiceSessions(); + manualSession.setUserId(userId); + // 设置会话ID为字符串类型的UUID manualSession.setSessionId(sessionId); - manualSession.setReason(reason); + manualSession.setReason(reason.trim()); manualSession.setStatus("0"); // 0-待处理 - manualSession.setCreateBy(sessionId+""); + manualSession.setRequestTime(new java.util.Date()); + manualSession.setCreateBy(userId.toString()); - logger.info("[转人工服务] 准备插入数据库 - 转人工会话对象: {}", manualSession); + logger.info("[转人工服务] 准备插入数据库 - 转人工会话对象: userId={}, sessionId={}, reason={}, status={}", + manualSession.getUserId(), manualSession.getSessionId(), + manualSession.getReason(), manualSession.getStatus()); int result = customerServiceService.createManualServiceRequest(manualSession); logger.info("[转人工服务] 数据库插入结果: {}", result); if (result > 0) { - logger.info("[转人工服务] 转人工请求提交成功"); - return AjaxResult.success("转人工请求已提交"); + logger.info("[转人工服务] 转人工请求提交成功 - userId: {}, sessionId: {}", userId, sessionId); + // 返回sessionId给前端 + Map responseData = new HashMap<>(); + responseData.put("sessionId", sessionId); + responseData.put("message", "转人工请求已提交,请等待客服接听"); + return AjaxResult.success(responseData); } else { logger.error("[转人工服务] 转人工请求提交失败 - 数据库插入返回0"); return AjaxResult.error("转人工请求提交失败"); } } catch (Exception e) { - logger.error("[转人工服务] 转人工服务异常", e); + logger.error("[转人工服务] 转人工服务异常 - userId: {}, reason: {}", userId, reason, e); return AjaxResult.error("转人工服务失败: " + e.getMessage()); } } + + /** + * 生成或查找SessionId + * 方案1:在转人工接口中按需生成SessionId + */ + private String generateOrFindSessionId(Long userId) { + try { + // 1. 先查找用户是否有活跃的会话 + String existingSessionId = customerServiceService.findActiveSessionByUserId(userId); + if (existingSessionId != null && !existingSessionId.trim().isEmpty()) { + logger.info("[SessionId生成] 找到用户活跃会话 - userId: {}, sessionId: {}", userId, existingSessionId); + return existingSessionId; + } + + // 2. 如果没有活跃会话,生成新的SessionId + String newSessionId = java.util.UUID.randomUUID().toString().replace("-", ""); + logger.info("[SessionId生成] 生成新的SessionId - userId: {}, newSessionId: {}", userId, newSessionId); + + // 3. 可选:将新的SessionId保存到用户会话表中 + // customerServiceService.createUserSession(userId, newSessionId); + + return newSessionId; + } catch (Exception e) { + logger.error("[SessionId生成] 生成SessionId异常 - userId: {}", userId, e); + // 异常情况下返回基于userId的默认SessionId + return "session_" + userId + "_" + System.currentTimeMillis(); + } + } /** * 获取转人工请求列表 @@ -186,6 +276,31 @@ public class CustomerServiceController extends BaseController } } + /** + * 获取单个转人工请求详情 + */ + @GetMapping("/manual-requests/{requestId}") + @ResponseBody + public AjaxResult getManualRequestById(@PathVariable Long requestId) + { + try { + logger.info("[获取转人工请求详情] 开始处理请求 - requestId: {}", requestId); + + ManualServiceSessions request = customerServiceService.getManualRequestById(requestId); + if (request == null) { + logger.error("[获取转人工请求详情] 未找到转人工记录 - requestId: {}", requestId); + return AjaxResult.error("未找到转人工记录"); + } + + logger.info("[获取转人工请求详情] 成功获取记录 - userId: {}, sessionId: {}, status: {}", + request.getUserId(), request.getSessionId(), request.getStatus()); + return AjaxResult.success(request); + } catch (Exception e) { + logger.error("[获取转人工请求详情] 获取转人工请求详情失败 - requestId: {}", requestId, e); + return AjaxResult.error("获取转人工请求详情失败: " + e.getMessage()); + } + } + /** * 接受转人工请求 */ @@ -194,20 +309,80 @@ public class CustomerServiceController extends BaseController public AjaxResult acceptManualRequest(@PathVariable Long requestId) { try { + logger.info("[接受转人工] 开始处理请求 - requestId: {}, serviceId: {}", requestId, getUserId()); + + // 先获取原始记录 + ManualServiceSessions originalRequest = customerServiceService.getManualRequestById(requestId); + if (originalRequest == null) { + logger.error("[接受转人工] 未找到转人工记录 - requestId: {}", requestId); + return AjaxResult.error("未找到转人工记录"); + } + + logger.info("[接受转人工] 原始记录 - userId: {}, sessionId: {}, status: {}", + originalRequest.getUserId(), originalRequest.getSessionId(), originalRequest.getStatus()); + + // 检查状态是否为待处理 + if (!"0".equals(originalRequest.getStatus())) { + logger.warn("[接受转人工] 记录状态不是待处理 - status: {}", originalRequest.getStatus()); + return AjaxResult.error("该请求已被处理"); + } + + // 更新记录状态 ManualServiceSessions manualSession = new ManualServiceSessions(); manualSession.setManualSessionId(requestId); // 设置主键ID manualSession.setStatus("1"); // 1-已接受 manualSession.setServiceId(getUserId()); + manualSession.setAcceptTime(new java.util.Date()); manualSession.setUpdateBy(requestId+""); int result = customerServiceService.updateManualServiceRequest(manualSession); + logger.info("[接受转人工] 数据库更新结果: {}", result); + if (result > 0) { + // 创建或更新UserSessions记录 + try { + String sessionId = originalRequest.getSessionId(); + boolean sessionExists = customerServiceService.isSessionIdExists(sessionId); + + UserSessions userSession = new UserSessions(); + userSession.setSessionId(sessionId); + userSession.setUserId(originalRequest.getUserId()); + userSession.setSessionStart(new java.util.Date()); + userSession.setStatus("active"); + userSession.setLastActivity(new java.util.Date()); + userSession.setSessionToken(sessionId); + + int sessionResult; + if (sessionExists) { + // 如果会话已存在,则更新记录 + sessionResult = customerServiceService.updateUserSessions(userSession); + logger.info("[接受转人工] 更新UserSessions记录结果: {}", sessionResult); + } else { + // 如果会话不存在,则插入新记录 + sessionResult = customerServiceService.insertUserSessions(userSession); + logger.info("[接受转人工] 创建UserSessions记录结果: {}", sessionResult); + } + } catch (Exception e) { + logger.error("[接受转人工] 处理UserSessions记录失败", e); + } + + // 通知用户转人工请求被接受 + if (originalRequest.getUserId() != null) { + logger.info("[接受转人工] 发送用户通知 - userId: {}", originalRequest.getUserId()); + customerServiceService.notifyUserManualRequestAccepted( + originalRequest.getUserId(), requestId, getUserId()); + } else { + logger.warn("[接受转人工] 原始记录中userId为空,无法发送用户通知"); + } + + logger.info("[接受转人工] 处理完成 - 成功接受转人工请求"); return AjaxResult.success("已接受转人工请求"); } else { + logger.error("[接受转人工] 数据库更新失败"); return AjaxResult.error("操作失败"); } } catch (Exception e) { - logger.error("接受转人工请求失败", e); + logger.error("[接受转人工] 处理异常", e); return AjaxResult.error("操作失败: " + e.getMessage()); } } @@ -243,17 +418,21 @@ public class CustomerServiceController extends BaseController */ @PostMapping("/status") @ResponseBody - public AjaxResult updateServiceStatus(String status) + public AjaxResult updateServiceStatus(@RequestParam String status) { try { + logger.info("[更新客服状态] 收到请求参数 - serviceId: {}, status: {}", getUserId(), status); + int result = customerServiceService.updateServiceStatus(getUserId(), status); 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()); } } @@ -265,11 +444,45 @@ public class CustomerServiceController extends BaseController @Log(title = "客服会话", businessType = BusinessType.EXPORT) @PostMapping("/export") @ResponseBody - public AjaxResult export(UserSessions userSessions) + public AjaxResult export(@RequestBody UserSessions userSessions) { - List list = customerServiceService.selectUserSessionsList(userSessions); - ExcelUtil util = new ExcelUtil(UserSessions.class); - return util.exportExcel(list, "会话数据"); + try { + logger.info("[导出会话] 收到导出请求 - 查询条件: {}", userSessions); + + List list = customerServiceService.selectUserSessionsList(userSessions); + logger.info("[导出会话] 查询到数据条数: {}", list != null ? list.size() : 0); + + ExcelUtil util = new ExcelUtil(UserSessions.class); + AjaxResult result = util.exportExcel(list, "会话数据"); + + logger.info("[导出会话] 导出完成"); + return result; + } catch (Exception e) { + logger.error("[导出会话] 导出会话列表异常", e); + return AjaxResult.error("导出失败: " + e.getMessage()); + } + } + + /** + * 获取当前登录用户信息 + */ + @GetMapping("/current-user") + @ResponseBody + public AjaxResult getCurrentUser() + { + try { + Long userId = getUserId(); + String loginName = getLoginName(); + + AjaxResult result = AjaxResult.success(); + result.put("userId", userId); + result.put("loginName", loginName); + + return result; + } catch (Exception e) { + logger.error("获取当前用户信息失败", e); + return AjaxResult.error("获取当前用户信息失败"); + } } /** diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/dto/TransferToManualRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/dto/TransferToManualRequest.java new file mode 100644 index 00000000..ef31d05b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/customer/dto/TransferToManualRequest.java @@ -0,0 +1,101 @@ +package com.ruoyi.web.controller.customer.dto; + +import java.util.List; + +/** + * 转人工服务请求DTO + * + * @author ruoyi + */ +public class TransferToManualRequest +{ + /** 用户ID */ + private String userId; + + /** 转人工原因 */ + private Object reason; + + public TransferToManualRequest() + { + } + + public TransferToManualRequest(String userId, Object reason) + { + this.userId = userId; + this.reason = reason; + } + + public String getUserId() + { + return userId; + } + + public void setUserId(String userId) + { + this.userId = userId; + } + + public Object getReason() + { + return reason; + } + + public void setReason(Object reason) + { + this.reason = reason; + } + + /** + * 获取转换后的用户ID(Long类型) + */ + public Long getUserIdAsLong() + { + if (userId == null || userId.trim().isEmpty()) + { + return null; + } + try + { + return Long.parseLong(userId.trim()); + } + catch (NumberFormatException e) + { + return null; + } + } + + /** + * 获取转换后的原因(String类型) + */ + public String getReasonAsString() + { + if (reason == null) + { + return null; + } + + if (reason instanceof String) + { + return (String) reason; + } + else if (reason instanceof List) + { + // 如果是消息列表,转换为字符串描述 + List messageList = (List) reason; + return "用户最近" + messageList.size() + "条消息记录"; + } + else + { + return reason.toString(); + } + } + + @Override + public String toString() + { + return "TransferToManualRequest{" + + "userId='" + userId + '\'' + + ", reason=" + reason + + '}'; + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/websocket/CustomerServiceWebSocket.java b/ruoyi-admin/src/main/java/com/ruoyi/web/websocket/CustomerServiceWebSocket.java index f7fd668f..7b88a93d 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/websocket/CustomerServiceWebSocket.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/websocket/CustomerServiceWebSocket.java @@ -14,9 +14,12 @@ import javax.websocket.server.ServerEndpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; - import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSON; +import com.ruoyi.customer.service.ICustomerServiceService; +import com.ruoyi.system.domain.ManualServiceSessions; /** * 客服系统WebSocket服务 @@ -29,6 +32,9 @@ public class CustomerServiceWebSocket { private static final Logger log = LoggerFactory.getLogger(CustomerServiceWebSocket.class); + /** 客服服务 - 使用静态变量以便在WebSocket中使用 */ + private static ICustomerServiceService customerServiceService; + /** 静态变量,用来记录当前在线连接数 */ private static final AtomicInteger onlineCount = new AtomicInteger(0); @@ -41,6 +47,14 @@ public class CustomerServiceWebSocket { /** 接收userId */ private String userId = ""; + /** + * 注入客服服务 + */ + @Autowired + public void setCustomerServiceService(ICustomerServiceService customerServiceService) { + CustomerServiceWebSocket.customerServiceService = customerServiceService; + } + /** * 连接建立成功调用的方法 */ @@ -74,6 +88,20 @@ public class CustomerServiceWebSocket { webSocketMap.remove(userId); subOnlineCount(); } + + // 清理该用户的待处理转人工记录 + try { + if (customerServiceService != null && userId != null && !userId.isEmpty()) { + Long userIdLong = Long.valueOf(userId); + int cleanedCount = customerServiceService.cleanupPendingManualRequests(userIdLong); + if (cleanedCount > 0) { + log.info("WebSocket断开,已清理用户{}的{}条待处理转人工记录", userId, cleanedCount); + } + } + } catch (Exception e) { + log.error("清理用户{}的转人工记录失败", userId, e); + } + log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount()); } @@ -94,9 +122,62 @@ public class CustomerServiceWebSocket { String messageContent = jsonObject.getString("message"); String messageType = jsonObject.getString("type"); - // 传输给对应toUserId用户的websocket - if (toUserId != null && webSocketMap.containsKey(toUserId)) { + // 处理消息路由 + if ("service".equals(toUserId)) { + // 当toUserId为'service'时,根据发送者userId查找对应的客服人员 + log.info("收到发送给客服的消息,发送者userId: {}", this.userId); + + // 检查customerServiceService是否为null + if (customerServiceService == null) { + log.error("customerServiceService为null,无法处理消息路由"); + return; + } + log.info("customerServiceService检查通过,开始处理消息路由"); + + try { + Long senderUserId = Long.valueOf(this.userId); + log.info("准备查询用户{}的已接受转人工会话", senderUserId); + + // 查找该用户对应的已接受状态的转人工记录 + ManualServiceSessions acceptedSession = customerServiceService.getAcceptedManualSessionByUserId(senderUserId); + log.info("查询已接受转人工会话完成,结果: {}", acceptedSession != null ? "找到会话" : "未找到会话"); + + if (acceptedSession != null) { + log.info("已接受会话详情 - manualSessionId: {}, sessionId: {}, userId: {}, serviceId: {}, status: {}", + acceptedSession.getManualSessionId(), + acceptedSession.getSessionId(), + acceptedSession.getUserId(), + acceptedSession.getServiceId(), + acceptedSession.getStatus()); + + if (acceptedSession.getServiceId() != null) { + String serviceUserId = acceptedSession.getServiceId().toString(); + log.info("目标客服ID: {}", serviceUserId); + + // 检查对应的客服是否在线 + log.info("当前在线用户列表: {}", webSocketMap.keySet()); + if (webSocketMap.containsKey(serviceUserId)) { + log.info("客服{}在线,准备转发消息", serviceUserId); + webSocketMap.get(serviceUserId).sendMessage(JSON.toJSONString(jsonObject)); + log.info("消息已成功转发给对应的客服人员: serviceId={}", serviceUserId); + } else { + log.warn("对应的客服人员不在线: serviceId={}, 当前在线用户: {}", serviceUserId, webSocketMap.keySet()); + } + } else { + log.warn("已接受会话的serviceId为null"); + } + } else { + log.warn("未找到用户{}对应的已接受状态的转人工记录", this.userId); + } + } catch (NumberFormatException e) { + log.error("用户ID格式错误: {}", this.userId, e); + } catch (Exception e) { + log.error("查找对应客服人员失败,异常详情: ", e); + } + } else if (toUserId != null && webSocketMap.containsKey(toUserId)) { + // 正常的点对点消息转发 webSocketMap.get(toUserId).sendMessage(JSON.toJSONString(jsonObject)); + log.info("消息已转发给指定用户: {}", toUserId); } else { log.error("请求的userId:" + toUserId + "不在该服务器上"); } @@ -126,11 +207,33 @@ public class CustomerServiceWebSocket { * 发送自定义消息 */ public static void sendInfo(String message, @PathParam("userId") String userId) throws IOException { - log.info("发送消息到:" + userId + ",报文:" + message); + log.info("[WebSocket调试] 开始发送消息到用户: {}, 报文: {}", userId, message); + + // 打印当前在线用户列表和数量 + log.info("[WebSocket调试] 当前在线用户数量: {}", webSocketMap.size()); + log.info("[WebSocket调试] 当前在线用户列表: {}", webSocketMap.keySet()); + + // 检查目标用户是否在线 if (userId != null && webSocketMap.containsKey(userId)) { - webSocketMap.get(userId).sendMessage(message); + CustomerServiceWebSocket targetWebSocket = webSocketMap.get(userId); + + // 检查WebSocket连接状态 + if (targetWebSocket != null && targetWebSocket.session != null) { + log.info("[WebSocket调试] 用户{}的WebSocket连接状态: isOpen={}, id={}", + userId, targetWebSocket.session.isOpen(), targetWebSocket.session.getId()); + + try { + targetWebSocket.sendMessage(message); + log.info("[WebSocket调试] 消息发送成功到用户: {}", userId); + } catch (IOException e) { + log.error("[WebSocket调试] 消息发送失败到用户: {}, 错误: {}", userId, e.getMessage()); + throw e; + } + } else { + log.error("[WebSocket调试] 用户{}的WebSocket连接为空或session为空", userId); + } } else { - log.error("用户" + userId + ",不在线!"); + log.error("[WebSocket调试] 用户{}不在线!在线用户列表: {}", userId, webSocketMap.keySet()); } } @@ -161,4 +264,20 @@ public class CustomerServiceWebSocket { public static ConcurrentHashMap getWebSocketMap() { return webSocketMap; } + + /** + * 发送消息给指定用户 + */ + public static void sendMessageToUser(String userId, String message) { + try { + if (userId != null && webSocketMap.containsKey(userId)) { + webSocketMap.get(userId).sendMessage(message); + log.info("已发送消息给用户: userId={}", userId); + } else { + log.warn("用户{}不在线,无法发送消息", userId); + } + } catch (Exception e) { + log.error("发送消息给用户{}失败", userId, e); + } + } } \ No newline at end of file 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 8d3ef705..7b2b6f3a 100644 --- a/ruoyi-admin/src/main/resources/templates/customer/service/index.html +++ b/ruoyi-admin/src/main/resources/templates/customer/service/index.html @@ -229,12 +229,12 @@
-