吴树波 1 месяц назад
Родитель
Сommit
9be4084b04

+ 272 - 26
src/assets/callCenterPhoneBarSdk/ccPhoneBarSocket.js

@@ -59,6 +59,83 @@ function ccPhoneBarSocket() {
 	var ws = null;
 	var wsuri = null;
 	var isConnected = false;
+	var wsDebug = {
+		connectId: 0,
+		connectedAt: null,
+		lastOpenAt: null,
+		lastCloseAt: null,
+		lastErrorAt: null,
+		lastSendAt: null,
+		lastSendAction: null,
+		lastRecvAt: null,
+		lastRecvStatus: null,
+		closeCode: null,
+		closeReason: null,
+		closeWasClean: null,
+		recentEvents: []
+	};
+
+	function maskWsUrl(url) {
+		if (!url) {
+			return url;
+		}
+		return url.replace(/loginToken=[^&]+/, 'loginToken=***');
+	}
+
+	function getReadyStateLabel(state) {
+		var map = {0: 'CONNECTING', 1: 'OPEN', 2: 'CLOSING', 3: 'CLOSED'};
+		return map[state] !== undefined ? map[state] : String(state);
+	}
+
+	function pushWsEvent(type, detail) {
+		wsDebug.recentEvents.push({
+			t: new Date().toISOString(),
+			type: type,
+			detail: detail || {}
+		});
+		if (wsDebug.recentEvents.length > 40) {
+			wsDebug.recentEvents.shift();
+		}
+	}
+
+	function logIpccWs(level, tag, payload) {
+		var prefix = '[IPCC-WS][' + tag + ']';
+		if (level === 'error') {
+			console.error(prefix, payload);
+		} else if (level === 'warn') {
+			console.warn(prefix, payload);
+		} else {
+			console.log(prefix, payload);
+		}
+	}
+
+	this.getWsDebugSnapshot = function() {
+		var uptimeMs = wsDebug.connectedAt ? (Date.now() - wsDebug.connectedAt) : null;
+		return {
+			connectId: wsDebug.connectId,
+			wsUrl: maskWsUrl(wsuri),
+			connectedAt: wsDebug.connectedAt,
+			uptimeMs: uptimeMs,
+			lastOpenAt: wsDebug.lastOpenAt,
+			lastCloseAt: wsDebug.lastCloseAt,
+			lastErrorAt: wsDebug.lastErrorAt,
+			lastSendAt: wsDebug.lastSendAt,
+			lastSendAction: wsDebug.lastSendAction,
+			lastRecvAt: wsDebug.lastRecvAt,
+			lastRecvStatus: wsDebug.lastRecvStatus,
+			closeCode: wsDebug.closeCode,
+			closeReason: wsDebug.closeReason,
+			closeWasClean: wsDebug.closeWasClean,
+			isConnected: isConnected,
+			isCalling: _cc.isCalling,
+			callConnected: callConnected,
+			readyState: ws ? ws.readyState : null,
+			readyStateLabel: ws ? getReadyStateLabel(ws.readyState) : 'NO_WS',
+			extnum: _cc.callConfig.extnum,
+			opnum: _cc.callConfig.opnum,
+			recentEvents: wsDebug.recentEvents.slice()
+		};
+	};
 	/* 通话已建立 */
 	var callConnected = false;
 	/*
@@ -189,14 +266,29 @@ function ccPhoneBarSocket() {
 	this.setHeartbeat = function()
 	{
 		setInterval(function(){
-			//如果启用了心跳,而且用户已经登录上线,则发送心跳数据
-			if(_cc.callConfig.enableHeartBeat && _cc.getIsConnected()){
-				console.debug("try to send heartbeat.");
-				var heartBeat = {};
-				heartBeat.action="setHearBeat";
-				heartBeat.body = "{}";
-				_cc.sendMsg(heartBeat);
+			if (!_cc.callConfig.enableHeartBeat) {
+				return;
 			}
+			var readyState = ws ? ws.readyState : null;
+			if (!_cc.getIsConnected()) {
+				logIpccWs('warn', 'heartbeat-skip', {
+					reason: 'not_connected',
+					readyState: readyState,
+					readyStateLabel: getReadyStateLabel(readyState)
+				});
+				return;
+			}
+			logIpccWs('log', 'heartbeat-send', {
+				intervalSecs: _cc.callConfig.heartBeatIntervalSecs,
+				readyState: readyState,
+				readyStateLabel: getReadyStateLabel(readyState),
+				isCalling: _cc.isCalling,
+				callConnected: callConnected
+			});
+			var heartBeat = {};
+			heartBeat.action="setHearBeat";
+			heartBeat.body = "{}";
+			_cc.sendMsg(heartBeat, 'heartbeat');
 		}, _cc.callConfig.heartBeatIntervalSecs * 1000);
 	};
 
@@ -262,10 +354,19 @@ function ccPhoneBarSocket() {
 	};
 
 	//断开到呼叫控制服务器的连接
-	this.disconnect = function(){
+	this.disconnect = function(reason){
+		pushWsEvent('disconnect_client', {
+			reason: reason || 'manual',
+			readyState: ws ? ws.readyState : null,
+			snapshot: _cc.getWsDebugSnapshot()
+		});
+		logIpccWs('warn', 'disconnect', {
+			reason: reason || 'manual',
+			snapshot: _cc.getWsDebugSnapshot()
+		});
 		// 检查 WebSocket 是否存在且连接正常
 		if (!ws || ws.readyState !== WebSocket.OPEN) {
-			console.log('WebSocket 未连接或已关闭,无需发送断开指令');
+			logIpccWs('log', 'disconnect-skip', 'WebSocket 未连接或已关闭,无需发送断开指令');
 			return;
 		}
 		var cmdInfo = {};
@@ -273,7 +374,7 @@ function ccPhoneBarSocket() {
 		cmdInfo.body = {"cmd" : "disconnect", "args" : { "msg" : "disconnection opt triggered by js client." } };
 		ws.send(JSON.stringify(cmdInfo));
 		// 立即关闭 WebSocket 连接
-		ws.close();
+		ws.close(1000, 'client disconnect');
 	};
 
 	/**
@@ -352,33 +453,95 @@ function ccPhoneBarSocket() {
 	this.connect = function() {
 		// 如果已经在连接中,直接返回
 		if (ws && ws.readyState === WebSocket.CONNECTING) {
-			console.log('[connect] WebSocket 正在连接中,请勿重复操作');
+			logIpccWs('warn', 'connect-skip', 'WebSocket 正在连接中,请勿重复操作');
 			return;
 		}
 
 		// 先关闭旧的连接,避免重复登录
 		if (ws) {
-			console.log('[connect] 检测到旧连接存在,先关闭旧连接');
+			logIpccWs('warn', 'connect-replace', {
+				oldReadyState: ws.readyState,
+				oldReadyStateLabel: getReadyStateLabel(ws.readyState)
+			});
 			try {
-				ws.close();
+				ws.close(1000, 'reconnect');
 			} catch(e) {
-				console.error('[connect] 关闭旧连接失败:', e);
+				logIpccWs('error', 'connect-close-old-failed', e);
 			}
 			ws = null;
 		}
 
-		if ('WebSocket' in window)
-			ws = new WebSocket(wsuri);
-		else {
-			console.log('您的浏览器不支持 websocket,您无法使用本页面的功能!');
+		if (!('WebSocket' in window)) {
+			logIpccWs('error', 'connect-unsupported', '浏览器不支持 WebSocket');
 			return;
 		}
+
+		wsDebug.connectId += 1;
+		pushWsEvent('connect_start', {
+			connectId: wsDebug.connectId,
+			wsUrl: maskWsUrl(wsuri)
+		});
+		logIpccWs('log', 'connect-start', {
+			connectId: wsDebug.connectId,
+			wsUrl: maskWsUrl(wsuri)
+		});
+		ws = new WebSocket(wsuri);
+
+		ws.onerror = function(evt) {
+			wsDebug.lastErrorAt = Date.now();
+			pushWsEvent('error', {
+				connectId: wsDebug.connectId,
+				readyState: ws ? ws.readyState : null,
+				eventType: evt && evt.type
+			});
+			logIpccWs('error', 'onerror', {
+				connectId: wsDebug.connectId,
+				readyState: ws ? ws.readyState : null,
+				readyStateLabel: ws ? getReadyStateLabel(ws.readyState) : 'NO_WS',
+				snapshot: _cc.getWsDebugSnapshot()
+			});
+		};
+
 		//收到消息
 		ws.onmessage = function(evt) {
+			var msg = null;
+			var resp_status = null;
+			try {
+				msg = JSON.parse(evt.data);
+				resp_status = msg["status"];
+			} catch (parseErr) {
+				logIpccWs('error', 'onmessage-parse-failed', {
+					raw: evt.data,
+					error: parseErr && parseErr.message
+				});
+				return;
+			}
+
+			wsDebug.lastRecvAt = Date.now();
+			wsDebug.lastRecvStatus = resp_status;
+			pushWsEvent('recv', {
+				status: resp_status,
+				action: msg.action,
+				uuid: msg.object && msg.object.uuid
+			});
+
+			var isHeartbeatRelated = msg.action === 'setHearBeat' || resp_status === 'heartbeat' || resp_status === 204;
+			if (isHeartbeatRelated) {
+				logIpccWs('log', 'heartbeat-recv', {
+					status: resp_status,
+					msg: msg
+				});
+			} else {
+				logIpccWs('log', 'onmessage', {
+					status: resp_status,
+					uuid: msg.object && msg.object.uuid,
+					callType: msg.object && msg.object.callType,
+					msg: msg
+				});
+			}
+
 			console.log("recv msg from websocket server: ", evt.data);
-			var msg = JSON.parse(evt.data);
 			console.log("parsed json data:", msg);
-			var resp_status = msg["status"];
 			switch(resp_status) {
 				case 200:
 					isConnected = true;
@@ -505,20 +668,97 @@ function ccPhoneBarSocket() {
 		//关闭连接时触发
 		ws.onclose = function(evt) {
 			isConnected = false;
-			_cc.notifyAll(ccPhoneBarSocket.eventList.ws_disconnected, "ipccserver 连接断开.");
+			wsDebug.lastCloseAt = Date.now();
+			wsDebug.closeCode = evt.code;
+			wsDebug.closeReason = evt.reason || '';
+			wsDebug.closeWasClean = evt.wasClean;
+			var snapshot = _cc.getWsDebugSnapshot();
+			pushWsEvent('close', {
+				code: evt.code,
+				reason: evt.reason || '',
+				wasClean: evt.wasClean,
+				connectId: wsDebug.connectId
+			});
+			logIpccWs('error', 'onclose', {
+				code: evt.code,
+				reason: evt.reason || '',
+				wasClean: evt.wasClean,
+				connectId: wsDebug.connectId,
+				msSinceLastSend: wsDebug.lastSendAt ? (Date.now() - wsDebug.lastSendAt) : null,
+				msSinceLastRecv: wsDebug.lastRecvAt ? (Date.now() - wsDebug.lastRecvAt) : null,
+				lastSendAction: wsDebug.lastSendAction,
+				lastRecvStatus: wsDebug.lastRecvStatus,
+				snapshot: snapshot
+			});
+			_cc.notifyAll(ccPhoneBarSocket.eventList.ws_disconnected, snapshot);
 			console.log("ipcc连接断开.", "disconnected");
 			console.log(evt);
-			ws.close();
 		};
 		ws.onopen = function(evt) {
+			wsDebug.lastOpenAt = Date.now();
+			wsDebug.connectedAt = Date.now();
+			pushWsEvent('open', { connectId: wsDebug.connectId });
+			logIpccWs('log', 'onopen', {
+				connectId: wsDebug.connectId,
+				readyState: ws.readyState,
+				readyStateLabel: getReadyStateLabel(ws.readyState)
+			});
 			console.log("ipccserver websocket onopen...");
 		};
 	};
 
 	//发送消息给呼叫控制服务器
-	this.sendMsg = function(jsonObject) {
+	this.sendMsg = function(jsonObject, sendTag) {
+		var action = jsonObject && jsonObject.action;
+		var payload = JSON.stringify(jsonObject);
+		if (!ws) {
+			logIpccWs('error', 'send-failed', {
+				sendTag: sendTag || action,
+				reason: 'ws_is_null',
+				payload: jsonObject
+			});
+			pushWsEvent('send_failed', { sendTag: sendTag || action, reason: 'ws_is_null' });
+			return false;
+		}
+		if (ws.readyState !== WebSocket.OPEN) {
+			logIpccWs('error', 'send-failed', {
+				sendTag: sendTag || action,
+				reason: 'ws_not_open',
+				readyState: ws.readyState,
+				readyStateLabel: getReadyStateLabel(ws.readyState),
+				payload: jsonObject
+			});
+			pushWsEvent('send_failed', {
+				sendTag: sendTag || action,
+				reason: 'ws_not_open',
+				readyState: ws.readyState
+			});
+			return false;
+		}
+		wsDebug.lastSendAt = Date.now();
+		wsDebug.lastSendAction = sendTag || action;
+		pushWsEvent('send', { sendTag: sendTag || action, action: action });
+		if (sendTag === 'heartbeat' || action === 'setHearBeat') {
+			logIpccWs('log', 'heartbeat-send-ok', { payload: jsonObject });
+		} else {
+			logIpccWs('log', 'send', { sendTag: sendTag || action, payload: jsonObject });
+		}
 		console.debug("ws.send:", jsonObject);
-		ws.send(JSON.stringify(jsonObject));
+		try {
+			ws.send(payload);
+			return true;
+		} catch (sendErr) {
+			logIpccWs('error', 'send-exception', {
+				sendTag: sendTag || action,
+				error: sendErr && sendErr.message,
+				payload: jsonObject
+			});
+			pushWsEvent('send_exception', {
+				sendTag: sendTag || action,
+				error: sendErr && sendErr.message
+			});
+			return false;
+		}
 	};
 
 	this.changeUiOnHold = function() {
@@ -907,7 +1147,7 @@ function ccPhoneBarSocket() {
 		var cmdInfo = {};
 		cmdInfo.action="setAgentStatus";
 		cmdInfo.body = {"cmd" : "setStatus", "args" : { "status" : status } };
-		ws.send(JSON.stringify(cmdInfo));
+		_cc.sendMsg(cmdInfo, 'setStatus:' + status);
 	};
 
 	//注销登录;
@@ -1033,7 +1273,13 @@ function ccPhoneBarSocket() {
 		var sessionControl = {};
 		sessionControl.action="call";
 		sessionControl.body = {"cmd" : action, "args" : argsObject };
-		ws.send(JSON.stringify(sessionControl));
+		logIpccWs('log', 'callControl', {
+			cmd: action,
+			args: argsObject,
+			readyState: ws ? ws.readyState : null,
+			readyStateLabel: ws ? getReadyStateLabel(ws.readyState) : 'NO_WS'
+		});
+		_cc.sendMsg(sessionControl, 'call:' + action);
 	};
 
 	this.checkCallConfirmed = function () {

+ 1 - 1
src/utils/common.js

@@ -1,4 +1,4 @@
-/**
+/**
  * 通用js方法封装处理
  * Copyright (c) 2019 fs
  */

+ 13 - 4
src/views/aiSipCall/aiSipCallManualOutbound.vue

@@ -675,7 +675,7 @@ export default {
         // 断开 WebSocket 连接
         if (this.phoneBar) {
             console.log('[beforeDestroy] 断开 WebSocket 连接');
-            this.phoneBar.disconnect();
+            this.phoneBar.disconnect('component beforeDestroy');
         }
         // 移除键盘事件监听
         document.removeEventListener('keyup', this._escKeyHandler);
@@ -796,7 +796,10 @@ export default {
         setupEventListeners() {
             // websocket 通信对象断开事件
             this.phoneBar.on(EventList.WS_DISCONNECTED, (msg) => {
-                console.log("websocket 通信对象断开事件:" + msg);
+                const snapshot = (this.phoneBar && this.phoneBar.getWsDebugSnapshot)
+                    ? this.phoneBar.getWsDebugSnapshot()
+                    : msg;
+                console.error('[IPCC-WS][UI] websocket 通信对象断开', snapshot);
                 this.updatePhoneBar(msg, EventList.WS_DISCONNECTED);
                 this.showTransferArea = false;
                 this.showConferenceArea = false;
@@ -816,7 +819,10 @@ export default {
             });
 
             this.phoneBar.on(EventList.OUTBOUND_START, (msg) => {
-                console.log('outbound_start 事件:' + msg);
+                console.log('[IPCC-WS][UI] outbound_start 事件', msg);
+                if (this.phoneBar && this.phoneBar.getWsDebugSnapshot) {
+                    console.log('[IPCC-WS][UI] outbound_start 时 WS 快照', this.phoneBar.getWsDebugSnapshot());
+                }
                 this.showChatContainer = true;
                 this.showCustomerForm = true;
                 this.loadCommunicationHistory();
@@ -1325,6 +1331,9 @@ export default {
             }
 
             console.log('[开始外呼] 号码:', this.phoneNumber.trim(), '类型:', this.callType, '视频级别:', this.videoLevel);
+            if (this.phoneBar && this.phoneBar.getWsDebugSnapshot) {
+                console.log('[开始外呼] WS 快照', this.phoneBar.getWsDebugSnapshot());
+            }
 
             this.currentCallUuid = '';
             this.callFinishedAt = '';
@@ -1702,7 +1711,7 @@ export default {
                     // 设置不允许自动签入
                     this.allowAutoSignIn = false;
                     // 调用 SDK 的 disconnect 方法 (会自动关闭 WebSocket)
-                    this.phoneBar.disconnect();
+                    this.phoneBar.disconnect('manual sign out');
                     this.onlineButtonText = '签入';
                     this.callStatus = '没有连接';
                     this.agentStatus = '空闲';