no message
This commit is contained in:
parent
a4a0e8df26
commit
4d49e11023
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ChatHistory> 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,41 +162,104 @@ 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<ManualServiceSessions> 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<String, Object> 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<UserSessions> list = customerServiceService.selectUserSessionsList(userSessions);
|
||||
ExcelUtil<UserSessions> util = new ExcelUtil<UserSessions>(UserSessions.class);
|
||||
return util.exportExcel(list, "会话数据");
|
||||
try {
|
||||
logger.info("[导出会话] 收到导出请求 - 查询条件: {}", userSessions);
|
||||
|
||||
List<UserSessions> list = customerServiceService.selectUserSessionsList(userSessions);
|
||||
logger.info("[导出会话] 查询到数据条数: {}", list != null ? list.size() : 0);
|
||||
|
||||
ExcelUtil<UserSessions> util = new ExcelUtil<UserSessions>(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("获取当前用户信息失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, CustomerServiceWebSocket> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -229,12 +229,12 @@
|
|||
|
||||
<div class="chat-input">
|
||||
<div class="input-toolbar">
|
||||
<button type="button" class="btn btn-sm btn-default" onclick="insertEmoji('😊')">
|
||||
<!-- <button type="button" class="btn btn-sm btn-default" onclick="insertEmoji('😊')">
|
||||
<i class="fa fa-smile-o"></i> 表情
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-default" onclick="uploadFile()">
|
||||
<i class="fa fa-paperclip"></i> 文件
|
||||
</button>
|
||||
</button> -->
|
||||
<!-- <button type="button" class="btn btn-sm btn-warning pull-right" onclick="transferToManual()">
|
||||
<i class="fa fa-user"></i> 转人工
|
||||
</button> -->
|
||||
|
|
@ -271,7 +271,16 @@
|
|||
var wsConnection = null;
|
||||
|
||||
$(document).ready(function() {
|
||||
initWebSocket();
|
||||
// 获取当前客服ID并建立WebSocket连接
|
||||
getCurrentUserId(function(serviceId) {
|
||||
if (serviceId) {
|
||||
console.log('[DEBUG] 页面加载时获取到客服ID:', serviceId);
|
||||
initWebSocket(serviceId);
|
||||
} else {
|
||||
console.error('[ERROR] 无法获取当前客服ID,WebSocket连接失败');
|
||||
}
|
||||
});
|
||||
|
||||
loadSessionList();
|
||||
loadManualRequests();
|
||||
|
||||
|
|
@ -283,22 +292,76 @@
|
|||
});
|
||||
|
||||
// 初始化WebSocket连接
|
||||
function initWebSocket() {
|
||||
console.log("[DEBUG] 开始初始化WebSocket连接...");
|
||||
function initWebSocket(serviceId) {
|
||||
console.log("[DEBUG] 开始初始化WebSocket连接,客服ID:", serviceId);
|
||||
|
||||
if (typeof(WebSocket) == "undefined") {
|
||||
console.error("[ERROR] 您的浏览器不支持WebSocket");
|
||||
return;
|
||||
}
|
||||
|
||||
var wsUrl = "ws://bwy.opsoul.com/websocket/customer-service";
|
||||
if (serviceId) {
|
||||
createWebSocketConnection(serviceId);
|
||||
} else {
|
||||
console.error("[ERROR] 客服ID为空,无法建立WebSocket连接");
|
||||
}
|
||||
}
|
||||
|
||||
// 创建WebSocket连接
|
||||
function createWebSocketConnection(userId) {
|
||||
// 根据当前页面协议动态选择WebSocket协议
|
||||
var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
var wsUrl = protocol + "//bwy.opsoul.com/websocket/customer/" + userId;
|
||||
console.log("[DEBUG] WebSocket连接URL:", wsUrl);
|
||||
console.log("[DEBUG] 当前页面协议:", window.location.protocol, "选择WebSocket协议:", protocol);
|
||||
|
||||
try {
|
||||
// 如果已有连接,先关闭
|
||||
if (wsConnection && wsConnection.readyState !== WebSocket.CLOSED) {
|
||||
wsConnection.close();
|
||||
}
|
||||
|
||||
wsConnection = new WebSocket(wsUrl);
|
||||
console.log("[DEBUG] WebSocket对象创建成功");
|
||||
} catch (e) {
|
||||
console.error("[ERROR] WebSocket对象创建失败:", e);
|
||||
// 添加更详细的错误信息
|
||||
if (e.name === 'SecurityError') {
|
||||
console.error("[ERROR] 安全错误: HTTPS页面无法连接到不安全的WebSocket端点");
|
||||
console.error("[ERROR] 请确保服务器支持WSS连接或在HTTP环境下访问页面");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setupWebSocketHandlers();
|
||||
}
|
||||
|
||||
// 获取当前用户ID
|
||||
function getCurrentUserId(callback) {
|
||||
$.ajax({
|
||||
url: ctx + "customer/service/current-user",
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
success: function(response) {
|
||||
if (response.code === 0 && response && response.userId) {
|
||||
console.log("[DEBUG] 获取到当前用户ID:", response.userId);
|
||||
callback(response.userId);
|
||||
} else {
|
||||
console.error("[ERROR] 获取用户ID失败:", response);
|
||||
callback(null);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("[ERROR] 获取用户ID请求失败:", error);
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 设置WebSocket事件处理器
|
||||
function setupWebSocketHandlers() {
|
||||
if (!wsConnection) {
|
||||
console.error("[ERROR] WebSocket连接对象不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -372,6 +435,40 @@
|
|||
console.log('[DEBUG] 使用alert显示通知');
|
||||
}
|
||||
break;
|
||||
case 'MANUAL_ACCEPTED':
|
||||
// 处理转人工请求被接受的通知
|
||||
console.log('[SUCCESS] 转人工请求已被接受:', message);
|
||||
// 刷新转人工请求列表
|
||||
loadManualRequests();
|
||||
// 显示成功通知
|
||||
if (typeof layer !== 'undefined') {
|
||||
layer.msg('您的转人工请求已被客服接受,即将为您服务');
|
||||
} else {
|
||||
alert('您的转人工请求已被客服接受,即将为您服务');
|
||||
}
|
||||
// 如果有会话ID,可以切换到对应会话
|
||||
if (message.sessionId && message.sessionId != currentSessionId) {
|
||||
selectSession(message.sessionId);
|
||||
}
|
||||
break;
|
||||
case 'message':
|
||||
// 处理APP端发送的普通消息
|
||||
console.log('[DEBUG] 处理message类型消息:', message);
|
||||
console.log('[DEBUG] 消息内容:', message.message);
|
||||
console.log('[DEBUG] 发送者ID:', message.fromUserId);
|
||||
console.log('[DEBUG] 会话ID:', message.sessionId);
|
||||
|
||||
// 如果消息属于当前会话,添加到聊天区域
|
||||
if (message.sessionId && message.sessionId == currentSessionId) {
|
||||
appendMessage(message.message, true, message.timestamp);
|
||||
console.log('[DEBUG] message类型消息已添加到当前会话');
|
||||
} else {
|
||||
console.log('[DEBUG] message类型消息不属于当前会话,sessionId:', message.sessionId, '当前会话:', currentSessionId);
|
||||
}
|
||||
|
||||
// 刷新会话列表以更新最新消息
|
||||
loadSessionList();
|
||||
break;
|
||||
default:
|
||||
console.warn('[WARN] 未知的消息类型:', message.type, '完整消息:', message);
|
||||
break;
|
||||
|
|
@ -390,7 +487,7 @@
|
|||
var avatar = session.customerName ? session.customerName.charAt(0).toUpperCase() : 'U';
|
||||
var activeClass = session.sessionId == currentSessionId ? 'active' : '';
|
||||
|
||||
html += '<div class="session-item ' + activeClass + '" onclick="selectSession(' + session.sessionId + ')">';
|
||||
html += '<div class="session-item ' + activeClass + '" onclick="selectSession(\'' + session.sessionId + '\')">';
|
||||
html += ' <div style="display: flex; align-items: center;">';
|
||||
html += ' <div class="session-avatar">' + avatar + '</div>';
|
||||
html += ' <div class="session-info">';
|
||||
|
|
@ -412,8 +509,11 @@
|
|||
function selectSession(sessionId) {
|
||||
currentSessionId = sessionId;
|
||||
$('.session-item').removeClass('active');
|
||||
$('[onclick="selectSession(' + sessionId + ')"]').addClass('active');
|
||||
$('[onclick="selectSession(\'' + sessionId + '\')"]').addClass('active');
|
||||
|
||||
console.log('[DEBUG] 选择会话:', sessionId, '不重新建立WebSocket连接');
|
||||
|
||||
// 直接加载聊天历史和更新会话信息,不重新建立WebSocket连接
|
||||
loadChatHistory(sessionId);
|
||||
updateCurrentSessionInfo(sessionId);
|
||||
}
|
||||
|
|
@ -457,6 +557,28 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// 检查WebSocket连接状态
|
||||
if (!wsConnection || wsConnection.readyState !== WebSocket.OPEN) {
|
||||
layer.confirm('WebSocket连接已断开,消息可能无法实时推送给客户。是否继续发送?', {
|
||||
btn: ['继续发送', '重新连接']
|
||||
}, function(index) {
|
||||
// 继续发送
|
||||
doSendMessage(content);
|
||||
layer.close(index);
|
||||
}, function(index) {
|
||||
// 重新连接
|
||||
initWebSocket();
|
||||
layer.close(index);
|
||||
layer.msg('正在重新连接WebSocket...');
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
doSendMessage(content);
|
||||
}
|
||||
|
||||
// 执行发送消息
|
||||
function doSendMessage(content) {
|
||||
var messageData = {
|
||||
sessionId: currentSessionId,
|
||||
content: content,
|
||||
|
|
@ -471,6 +593,8 @@
|
|||
} else {
|
||||
layer.msg('发送失败: ' + data.msg);
|
||||
}
|
||||
}).fail(function() {
|
||||
layer.msg('网络错误,消息发送失败');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -579,6 +703,41 @@
|
|||
$.post('/customer/service/manual-requests/' + requestId + '/accept', function(data) {
|
||||
if (data.code === 0) {
|
||||
layer.msg('已接受转人工请求');
|
||||
|
||||
// 获取转人工请求的详细信息,包含sessionId
|
||||
console.log('[DEBUG] 开始获取转人工请求详情 - requestId:', requestId);
|
||||
$.get('/customer/service/manual-requests/' + requestId)
|
||||
.done(function(requestData) {
|
||||
console.log('[DEBUG] 获取转人工请求详情成功:', requestData);
|
||||
|
||||
if (requestData.code === 0 && requestData.data) {
|
||||
var sessionId = requestData.data.sessionId;
|
||||
if (sessionId) {
|
||||
console.log('[DEBUG] 获取到sessionId:', sessionId);
|
||||
|
||||
// 直接切换到对应的会话,不重新建立WebSocket连接
|
||||
console.log('[DEBUG] 接受转人工请求,切换到会话:', sessionId);
|
||||
selectSession(sessionId);
|
||||
} else {
|
||||
console.error('[ERROR] 转人工请求数据中缺少sessionId:', requestData.data);
|
||||
layer.msg('转人工请求数据异常:缺少会话ID');
|
||||
}
|
||||
} else {
|
||||
console.error('[ERROR] 转人工请求数据格式错误:', requestData);
|
||||
layer.msg('获取转人工请求详情失败:' + (requestData.msg || '数据格式错误'));
|
||||
}
|
||||
})
|
||||
.fail(function(xhr, status, error) {
|
||||
console.error('[ERROR] 获取转人工请求详情失败:', {
|
||||
requestId: requestId,
|
||||
status: xhr.status,
|
||||
statusText: xhr.statusText,
|
||||
error: error,
|
||||
response: xhr.responseText
|
||||
});
|
||||
layer.msg('获取转人工请求详情失败,请检查网络连接或联系管理员');
|
||||
});
|
||||
|
||||
loadManualRequests();
|
||||
loadSessionList();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public interface ICustomerServiceService
|
|||
* @param sessionId 用户会话ID
|
||||
* @return 用户会话
|
||||
*/
|
||||
public UserSessions selectUserSessionsById(Long sessionId);
|
||||
public UserSessions selectUserSessionsById(String sessionId);
|
||||
|
||||
/**
|
||||
* 查询用户会话列表
|
||||
|
|
@ -52,13 +52,21 @@ public interface ICustomerServiceService
|
|||
*/
|
||||
public int updateUserSessions(UserSessions userSessions);
|
||||
|
||||
/**
|
||||
* 检查会话ID是否已存在
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @return 是否存在
|
||||
*/
|
||||
public boolean isSessionIdExists(String sessionId);
|
||||
|
||||
/**
|
||||
* 批量删除用户会话
|
||||
*
|
||||
* @param sessionIds 需要删除的用户会话ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteUserSessionsByIds(Long[] sessionIds);
|
||||
public int deleteUserSessionsByIds(String[] sessionIds);
|
||||
|
||||
/**
|
||||
* 删除用户会话信息
|
||||
|
|
@ -66,7 +74,7 @@ public interface ICustomerServiceService
|
|||
* @param sessionId 用户会话ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteUserSessionsById(Long sessionId);
|
||||
public int deleteUserSessionsById(String sessionId);
|
||||
|
||||
/**
|
||||
* 根据会话ID获取聊天记录
|
||||
|
|
@ -74,7 +82,7 @@ public interface ICustomerServiceService
|
|||
* @param sessionId 会话ID
|
||||
* @return 聊天记录列表
|
||||
*/
|
||||
public List<ChatHistory> getChatHistoryBySessionId(Long sessionId);
|
||||
public List<ChatHistory> getChatHistoryBySessionId(String sessionId);
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
|
|
@ -239,4 +247,61 @@ public interface ICustomerServiceService
|
|||
* @return 结果
|
||||
*/
|
||||
public int deleteManualServiceSessionsById(Long id);
|
||||
|
||||
/**
|
||||
* 清理用户的待处理转人工记录
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 清理数量
|
||||
*/
|
||||
public int cleanupPendingManualRequests(Long userId);
|
||||
|
||||
/**
|
||||
* 根据会话ID获取待处理的转人工记录
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @return 转人工记录列表
|
||||
*/
|
||||
public List<ManualServiceSessions> getPendingManualRequestsBySession(String sessionId);
|
||||
|
||||
/**
|
||||
* 根据用户ID获取待处理的转人工记录
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 转人工记录列表
|
||||
*/
|
||||
public List<ManualServiceSessions> getPendingManualRequestsByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据用户ID查找活跃的会话ID
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 活跃的会话ID,如果没有则返回null
|
||||
*/
|
||||
public String findActiveSessionByUserId(Long userId);
|
||||
|
||||
/**
|
||||
* 根据ID获取转人工记录
|
||||
*
|
||||
* @param requestId 记录ID
|
||||
* @return 转人工记录
|
||||
*/
|
||||
public ManualServiceSessions getManualRequestById(Long requestId);
|
||||
|
||||
/**
|
||||
* 通知用户转人工请求被接受
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param requestId 请求ID
|
||||
* @param serviceId 客服ID
|
||||
*/
|
||||
public void notifyUserManualRequestAccepted(Long userId, Long requestId, Long serviceId);
|
||||
|
||||
/**
|
||||
* 根据用户ID获取已接受状态的转人工记录
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 已接受的转人工记录,如果没有则返回null
|
||||
*/
|
||||
public ManualServiceSessions getAcceptedManualSessionByUserId(Long userId);
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ public class CustomerServiceServiceImpl implements ICustomerServiceService
|
|||
* @return 用户会话
|
||||
*/
|
||||
@Override
|
||||
public UserSessions selectUserSessionsById(Long sessionId)
|
||||
public UserSessions selectUserSessionsById(String sessionId)
|
||||
{
|
||||
return userSessionsMapper.selectUserSessionsById(sessionId);
|
||||
}
|
||||
|
|
@ -95,6 +95,19 @@ public class CustomerServiceServiceImpl implements ICustomerServiceService
|
|||
return userSessionsMapper.updateUserSessions(userSessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查会话ID是否已存在
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @return 是否存在
|
||||
*/
|
||||
@Override
|
||||
public boolean isSessionIdExists(String sessionId)
|
||||
{
|
||||
UserSessions userSession = userSessionsMapper.selectUserSessionsById(sessionId);
|
||||
return userSession != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户会话对象
|
||||
*
|
||||
|
|
@ -102,7 +115,7 @@ public class CustomerServiceServiceImpl implements ICustomerServiceService
|
|||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteUserSessionsByIds(Long[] sessionIds)
|
||||
public int deleteUserSessionsByIds(String[] sessionIds)
|
||||
{
|
||||
return userSessionsMapper.deleteUserSessionsByIds(sessionIds);
|
||||
}
|
||||
|
|
@ -114,7 +127,7 @@ public class CustomerServiceServiceImpl implements ICustomerServiceService
|
|||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int deleteUserSessionsById(Long sessionId)
|
||||
public int deleteUserSessionsById(String sessionId)
|
||||
{
|
||||
return userSessionsMapper.deleteUserSessionsById(sessionId);
|
||||
}
|
||||
|
|
@ -126,7 +139,7 @@ public class CustomerServiceServiceImpl implements ICustomerServiceService
|
|||
* @return 聊天记录列表
|
||||
*/
|
||||
@Override
|
||||
public List<ChatHistory> getChatHistoryBySessionId(Long sessionId)
|
||||
public List<ChatHistory> getChatHistoryBySessionId(String sessionId)
|
||||
{
|
||||
ChatHistory chatHistory = new ChatHistory();
|
||||
chatHistory.setSessionId(sessionId);
|
||||
|
|
@ -450,4 +463,162 @@ public class CustomerServiceServiceImpl implements ICustomerServiceService
|
|||
{
|
||||
return manualServiceSessionsMapper.deleteManualServiceSessionsById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理用户的待处理转人工记录
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 清理数量
|
||||
*/
|
||||
@Override
|
||||
public int cleanupPendingManualRequests(Long userId)
|
||||
{
|
||||
ManualServiceSessions condition = new ManualServiceSessions();
|
||||
condition.setUserId(userId);
|
||||
condition.setStatus("0"); // 待处理状态
|
||||
|
||||
List<ManualServiceSessions> pendingRequests = manualServiceSessionsMapper.selectManualServiceSessionsList(condition);
|
||||
|
||||
if (pendingRequests != null && !pendingRequests.isEmpty()) {
|
||||
System.out.println("[DEBUG] 清理用户 " + userId + " 的待处理转人工记录,数量: " + pendingRequests.size());
|
||||
|
||||
// 将状态更新为已关闭
|
||||
for (ManualServiceSessions request : pendingRequests) {
|
||||
request.setStatus("3"); // 3-已关闭(WebSocket断开)
|
||||
request.setEndTime(DateUtils.getNowDate());
|
||||
request.setUpdateTime(DateUtils.getNowDate());
|
||||
manualServiceSessionsMapper.updateManualServiceSessions(request);
|
||||
}
|
||||
|
||||
return pendingRequests.size();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话ID获取待处理的转人工记录
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @return 转人工记录列表
|
||||
*/
|
||||
@Override
|
||||
public List<ManualServiceSessions> getPendingManualRequestsBySession(String sessionId)
|
||||
{
|
||||
ManualServiceSessions condition = new ManualServiceSessions();
|
||||
condition.setSessionId(sessionId);
|
||||
condition.setStatus("0"); // 待处理状态
|
||||
return manualServiceSessionsMapper.selectManualServiceSessionsList(condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取转人工记录
|
||||
*
|
||||
* @param requestId 记录ID
|
||||
* @return 转人工记录
|
||||
*/
|
||||
@Override
|
||||
public ManualServiceSessions getManualRequestById(Long requestId)
|
||||
{
|
||||
return manualServiceSessionsMapper.selectManualServiceSessionsById(requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID获取待处理的转人工记录
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 转人工记录列表
|
||||
*/
|
||||
@Override
|
||||
public List<ManualServiceSessions> getPendingManualRequestsByUserId(Long userId)
|
||||
{
|
||||
ManualServiceSessions condition = new ManualServiceSessions();
|
||||
condition.setUserId(userId);
|
||||
condition.setStatus("0"); // 待处理状态
|
||||
return manualServiceSessionsMapper.selectManualServiceSessionsList(condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID查找活跃的会话ID
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 活跃的会话ID,如果没有则返回null
|
||||
*/
|
||||
@Override
|
||||
public String findActiveSessionByUserId(Long userId)
|
||||
{
|
||||
// 查找该用户最近的活跃会话(状态为0-待处理或1-已接受)
|
||||
ManualServiceSessions condition = new ManualServiceSessions();
|
||||
condition.setUserId(userId);
|
||||
|
||||
List<ManualServiceSessions> sessions = manualServiceSessionsMapper.selectManualServiceSessionsList(condition);
|
||||
|
||||
if (sessions != null && !sessions.isEmpty()) {
|
||||
// 按创建时间倒序排列,获取最新的活跃会话
|
||||
for (ManualServiceSessions session : sessions) {
|
||||
if ("0".equals(session.getStatus()) || "1".equals(session.getStatus())) {
|
||||
return session.getSessionId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户ID获取已接受状态的转人工记录
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 已接受的转人工记录,如果没有则返回null
|
||||
*/
|
||||
@Override
|
||||
public ManualServiceSessions getAcceptedManualSessionByUserId(Long userId)
|
||||
{
|
||||
ManualServiceSessions condition = new ManualServiceSessions();
|
||||
condition.setUserId(userId);
|
||||
condition.setStatus("1"); // 1-已接受状态
|
||||
|
||||
List<ManualServiceSessions> sessions = manualServiceSessionsMapper.selectManualServiceSessionsList(condition);
|
||||
|
||||
if (sessions != null && !sessions.isEmpty()) {
|
||||
// 返回最新的已接受会话
|
||||
return sessions.get(0);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知用户转人工请求被接受
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param requestId 请求ID
|
||||
* @param serviceId 客服ID
|
||||
*/
|
||||
@Override
|
||||
public void notifyUserManualRequestAccepted(Long userId, Long requestId, Long serviceId)
|
||||
{
|
||||
try {
|
||||
// 创建WebSocket消息
|
||||
com.alibaba.fastjson.JSONObject message = new com.alibaba.fastjson.JSONObject();
|
||||
message.put("type", "MANUAL_ACCEPTED");
|
||||
message.put("requestId", requestId);
|
||||
message.put("serviceId", serviceId);
|
||||
message.put("message", "您的转人工请求已被客服接受,即将为您服务");
|
||||
message.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
System.out.println("[DEBUG] 通知用户转人工请求被接受 - userId: " + userId + ", message: " + message.toJSONString());
|
||||
|
||||
// 通过反射调用WebSocket发送消息方法
|
||||
Class<?> webSocketClass = Class.forName("com.ruoyi.web.websocket.CustomerServiceWebSocket");
|
||||
java.lang.reflect.Method sendMessageToUserMethod = webSocketClass.getMethod("sendMessageToUser", String.class, String.class);
|
||||
sendMessageToUserMethod.invoke(null, userId.toString(), message.toJSONString());
|
||||
|
||||
System.out.println("[DEBUG] 用户通知发送成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("[DEBUG] 发送用户通知失败: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ public class ManualServiceSessions extends BaseEntity
|
|||
|
||||
/** 原会话ID */
|
||||
@Excel(name = "原会话ID")
|
||||
private Long sessionId;
|
||||
private String sessionId;
|
||||
|
||||
/** 用户ID */
|
||||
@Excel(name = "用户ID")
|
||||
|
|
@ -81,12 +81,12 @@ public class ManualServiceSessions extends BaseEntity
|
|||
{
|
||||
return manualSessionId;
|
||||
}
|
||||
public void setSessionId(Long sessionId)
|
||||
public void setSessionId(String sessionId)
|
||||
{
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public Long getSessionId()
|
||||
public String getSessionId()
|
||||
{
|
||||
return sessionId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public class UserSessions extends BaseEntity
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 会话ID */
|
||||
private Long sessionId;
|
||||
private String sessionId;
|
||||
|
||||
/** 用户ID */
|
||||
@Excel(name = "用户ID")
|
||||
|
|
@ -47,12 +47,12 @@ public class UserSessions extends BaseEntity
|
|||
@Excel(name = "会话令牌")
|
||||
private String sessionToken;
|
||||
|
||||
public void setSessionId(Long sessionId)
|
||||
public void setSessionId(String sessionId)
|
||||
{
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public Long getSessionId()
|
||||
public String getSessionId()
|
||||
{
|
||||
return sessionId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public interface UserSessionsMapper
|
|||
* @param sessionId 用户会话ID
|
||||
* @return 用户会话
|
||||
*/
|
||||
public UserSessions selectUserSessionsById(Long sessionId);
|
||||
public UserSessions selectUserSessionsById(String sessionId);
|
||||
|
||||
/**
|
||||
* 查询用户会话列表
|
||||
|
|
@ -49,7 +49,7 @@ public interface UserSessionsMapper
|
|||
* @param sessionId 用户会话ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteUserSessionsById(Long sessionId);
|
||||
public int deleteUserSessionsById(String sessionId);
|
||||
|
||||
/**
|
||||
* 批量删除用户会话
|
||||
|
|
@ -57,5 +57,5 @@ public interface UserSessionsMapper
|
|||
* @param sessionIds 需要删除的数据ID
|
||||
* @return 结果
|
||||
*/
|
||||
public int deleteUserSessionsByIds(Long[] sessionIds);
|
||||
public int deleteUserSessionsByIds(String[] sessionIds);
|
||||
}
|
||||
|
|
@ -29,14 +29,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectUserSessionsById" parameterType="Long" resultMap="UserSessionsResult">
|
||||
<select id="selectUserSessionsById" parameterType="String" resultMap="UserSessionsResult">
|
||||
<include refid="selectUserSessionsVo"/>
|
||||
where session_id = #{sessionId}
|
||||
</select>
|
||||
|
||||
<insert id="insertUserSessions" parameterType="UserSessions" useGeneratedKeys="true" keyProperty="sessionId">
|
||||
<insert id="insertUserSessions" parameterType="UserSessions">
|
||||
insert into user_sessions
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="sessionId != null">session_id,</if>
|
||||
<if test="userId != null">user_id,</if>
|
||||
<if test="sessionStart != null">session_start,</if>
|
||||
<if test="sessionEnd != null">session_end,</if>
|
||||
|
|
@ -47,6 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
<if test="updateTime != null">update_time,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="sessionId != null">#{sessionId},</if>
|
||||
<if test="userId != null">#{userId},</if>
|
||||
<if test="sessionStart != null">#{sessionStart},</if>
|
||||
<if test="sessionEnd != null">#{sessionEnd},</if>
|
||||
|
|
@ -73,7 +75,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
where session_id = #{sessionId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteUserSessionsById" parameterType="Long">
|
||||
<delete id="deleteUserSessionsById" parameterType="String">
|
||||
delete from user_sessions where session_id = #{sessionId}
|
||||
</delete>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue