From 8b319817e0253f496e571c51e2ac60435c1ce40d Mon Sep 17 00:00:00 2001 From: 18226920803 Date: Wed, 10 Jun 2026 14:48:56 +0800 Subject: [PATCH] =?UTF-8?q?=E9=81=97=E6=BC=8F=E4=BB=A3=E7=A0=81=E6=8F=90?= =?UTF-8?q?=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sa/base/config/PhoneWebSocketHandler.java | 225 ++++++++++++++++++ .../sa/base/config/WebSocketConfig.java | 36 +++ 2 files changed, 261 insertions(+) create mode 100644 smart-flow-api/sa-base/src/main/java/net/lab1024/sa/base/config/PhoneWebSocketHandler.java create mode 100644 smart-flow-api/sa-base/src/main/java/net/lab1024/sa/base/config/WebSocketConfig.java diff --git a/smart-flow-api/sa-base/src/main/java/net/lab1024/sa/base/config/PhoneWebSocketHandler.java b/smart-flow-api/sa-base/src/main/java/net/lab1024/sa/base/config/PhoneWebSocketHandler.java new file mode 100644 index 0000000..165dcd4 --- /dev/null +++ b/smart-flow-api/sa-base/src/main/java/net/lab1024/sa/base/config/PhoneWebSocketHandler.java @@ -0,0 +1,225 @@ +package net.lab1024.sa.base.config; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.handler.TextWebSocketHandler; + +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 电话WebSocket处理器 + * + * @Author SmartFlow + * @Date 2026-06-09 + */ +@Slf4j +@Component +public class PhoneWebSocketHandler extends TextWebSocketHandler { + + /** + * 存储所有在线的WebSocket会话,key为坐席ID,value为会话 + */ + private static final ConcurrentHashMap SESSION_MAP = new ConcurrentHashMap<>(); + + @Override + public void afterConnectionEstablished(WebSocketSession session) throws Exception { + String agentId = getAgentId(session); + if (agentId != null) { + SESSION_MAP.put(agentId, session); + log.info("坐席 {} WebSocket连接已建立,当前在线坐席数: {}", agentId, SESSION_MAP.size()); + } + } + + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { + String agentId = getAgentId(session); + if (agentId != null) { + SESSION_MAP.remove(agentId); + log.info("坐席 {} WebSocket连接已关闭,当前在线坐席数: {}", agentId, SESSION_MAP.size()); + } + } + + @Override + protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { + String payload = message.getPayload(); + log.info("收到WebSocket消息: {}", payload); + + // 可以在这里处理客户端发送的消息 + // 例如:坐席状态更新、接听/挂断操作等 + } + + @Override + public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { + log.error("WebSocket传输异常", exception); + String agentId = getAgentId(session); + if (agentId != null) { + SESSION_MAP.remove(agentId); + } + } + + /** + * 向指定坐席发送消息 + * + * @param agentId 坐席ID + * @param message 消息内容 + */ + public void sendMessageToAgent(String agentId, String message) { + WebSocketSession session = SESSION_MAP.get(agentId); + if (session != null && session.isOpen()) { + try { + session.sendMessage(new TextMessage(message)); + log.info("向坐席 {} 发送消息成功", agentId); + } catch (IOException e) { + log.error("向坐席 {} 发送消息失败", agentId, e); + } + } else { + log.warn("坐席 {} 不在线或会话已关闭", agentId); + } + } + + /** + * 向所有在线坐席广播消息 + * + * @param message 消息内容 + */ + public void broadcastToAll(String message) { + SESSION_MAP.forEach((agentId, session) -> { + if (session.isOpen()) { + try { + session.sendMessage(new TextMessage(message)); + } catch (IOException e) { + log.error("向坐席 {} 广播消息失败", agentId, e); + } + } + }); + } + + /** + * 发送来电弹屏消息 + * + * @param agentId 坐席ID + * @param callerInfo 来电信息 + */ + public void sendIncomingCall(String agentId, Object callerInfo) { + String message = JSON.toJSONString(new IncomingCallMessage( + "INCOMING_CALL", + callerInfo, + System.currentTimeMillis() + )); + sendMessageToAgent(agentId, message); + } + + /** + * 发送挂断消息 + * + * @param agentId 坐席ID + * @param callId 通话ID + */ + public void sendCallEnded(String agentId, String callId) { + String message = JSON.toJSONString(new CallEndedMessage( + "CALL_ENDED", + callId, + System.currentTimeMillis() + )); + sendMessageToAgent(agentId, message); + } + + /** + * 从会话中获取坐席ID + */ + private String getAgentId(WebSocketSession session) { + String query = session.getUri() != null ? session.getUri().getQuery() : null; + if (query != null) { + String[] params = query.split("&"); + for (String param : params) { + if (param.startsWith("agentId=")) { + return param.substring("agentId=".length()); + } + } + } + return null; + } + + /** + * 来电消息 + */ + public static class IncomingCallMessage { + private String type; + private Object data; + private long timestamp; + + public IncomingCallMessage(String type, Object data, long timestamp) { + this.type = type; + this.data = data; + this.timestamp = timestamp; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + } + + /** + * 通话结束消息 + */ + public static class CallEndedMessage { + private String type; + private String callId; + private long timestamp; + + public CallEndedMessage(String type, String callId, long timestamp) { + this.type = type; + this.callId = callId; + this.timestamp = timestamp; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + } +} \ No newline at end of file diff --git a/smart-flow-api/sa-base/src/main/java/net/lab1024/sa/base/config/WebSocketConfig.java b/smart-flow-api/sa-base/src/main/java/net/lab1024/sa/base/config/WebSocketConfig.java new file mode 100644 index 0000000..5f01318 --- /dev/null +++ b/smart-flow-api/sa-base/src/main/java/net/lab1024/sa/base/config/WebSocketConfig.java @@ -0,0 +1,36 @@ +package net.lab1024.sa.base.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +/** + * WebSocket配置类 + * + * @Author SmartFlow + * @Date 2026-06-09 + */ +@Configuration +@EnableWebSocket +public class WebSocketConfig implements WebSocketConfigurer { + + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + // 注册WebSocket处理器,允许跨域访问 + registry.addHandler(phoneWebSocketHandler(), "/websocket/phone") + .setAllowedOrigins("*"); + } + + @Bean + public PhoneWebSocketHandler phoneWebSocketHandler() { + return new PhoneWebSocketHandler(); + } +} \ No newline at end of file