Przeglądaj źródła

agent call module develop

刘威 6 miesięcy temu
rodzic
commit
4d7f5a3586

+ 63 - 0
src/core/callcenter/agent.py

@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+# encoding:utf-8
+
+
+class AgentService:
+
+    def __init__(self):
+        pass
+
+    def enable(self):
+        pass
+
+    def disable(self):
+        pass
+
+    def checkin(self):
+        pass
+
+    def checkout(self):
+        pass
+
+    def busy(self):
+        pass
+
+    def idle(self):
+        pass
+
+    def assign(self):
+        pass
+
+    def idle_agent_exist(self):
+        pass
+
+    def turn_on(self):
+        pass
+
+    def hangup(self):
+        pass
+
+    def listen(self):
+        pass
+
+    def get_and_check_phone(self):
+        pass
+
+    def add(self):
+        pass
+
+    def update(self):
+        pass
+
+    def detail(self, saas_id, agent_number):
+        pass
+
+    def count(self):
+        pass
+
+    def query_page(self):
+        pass
+
+    def delete(self):
+        pass
+

+ 80 - 0
src/core/callcenter/cache.py

@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+# encoding:utf-8
+
+import json
+
+from src.core.callcenter.constant import *
+from src.core.callcenter.model import AgentInfo, CallInfo, RouteGateway
+from src.core.datasource import RedisHandler
+
+cacheDay = 7
+deviceCall = {}
+redis_handler = RedisHandler()
+print(redis_handler.redis.info())
+
+
+def get_agent_info(agent_number):
+    text = redis_handler.get(AGENT_INFO + agent_number)
+    if not text:
+        return None
+    return AgentInfo.from_json(text)
+
+
+def refresh_agent_token(agent_number, token):
+    redis_handler.set(AGENT_TOKEN + token, agent_number, cacheDay * 24 * 60 * 60)
+
+
+def delete_key(key):
+    redis_handler.redis.delete(key)
+
+
+def get_agent_number(token):
+    return redis_handler.get(AGENT_TOKEN + token)
+
+
+# 缓存坐席
+def add_agent_info(agent: AgentInfo):
+    redis_handler.set(AGENT_INFO + agent.agent_number, json.dumps(agent), cacheDay * 24 * 60 * 60)
+
+
+# 缓存CALL_INFO
+def add_call_info(call: CallInfo):
+    for k, v in call.device_info_map.items():
+        add_device(k, call.call_id)
+    redis_handler.set(CALL_INFO + call.call_id, json.dumps(call), cacheDay * 24 * 60 * 60)
+
+
+def get_call_info_by_device_id(device_id):
+    call_id = deviceCall.get(device_id)
+    if not call_id:
+        return get_call_info(call_id)
+
+
+# 获取callInfo
+def get_call_info(call_id):
+    text = redis_handler.get(CALL_INFO + call_id)
+    if not text:
+        return CallInfo.from_json(text)
+
+
+def remove_call_info(call_id):
+    call_info = get_call_info(call_id)
+    if call_info and call_info.device_info_map:
+        for k, v in call_info.device_info_map.items():
+            deviceCall.pop(k)
+    delete_key(CALL_INFO + call_id)
+
+
+def add_device(device_id, call_id):
+    deviceCall[device_id] = call_id
+
+
+def get_route_gateway(saas_id):
+    return RouteGateway(id=1,
+                        saas_id=saas_id,
+                        name='63366692',
+                        media_host='192.168.20.99',
+                        media_port=5060,
+                        caller_prefix='',
+                        called_prefix='',
+                        status=0)

+ 39 - 33
src/core/callcenter/call.py

@@ -1,35 +1,56 @@
 #!/usr/bin/env python3
 # encoding:utf-8
 
-from src.core.callcenter.enumeration import CallCauseEnum
-from src.core.callcenter.model import MakeCallRequest, AgentInfo, CallInfo, HangupCallRequest, CheckInCallRequest
+import time
+
+import src.core.callcenter.cache as Cache
+from src.core.callcenter.constant import saasId, HOLD_MUSIC_PATH
+from src.core.callcenter.enumeration import CallCauseEnum, Direction, NextType, DeviceType
+from src.core.callcenter.model import MakeCallRequest, AgentInfo, CallInfo, HangupCallRequest, CheckInCallRequest, \
+    DeviceInfo, NextCommand
+from src.core.callcenter.snowflake import Snowflake
 
 
 class CallService:
 
     def __init__(self):
-        pass
+        worker_id = 1
+        data_center_id = 1
+        self.snowflake = Snowflake(worker_id, data_center_id)
+        from src.core.server import inbound_client
+        self.client = inbound_client
 
     def call(self, request: MakeCallRequest, agent: AgentInfo):
-        pass
-
-    def get_call_info(self, call_id):
-        pass
-
-    def add_call_info(self, call_info: CallInfo):
-        pass
-
-    def add_device(self, device_id, call_id):
-        pass
-
-    def get_call_info_by_device_id(self, device_id):
-        pass
+        call_id = 'C' + self.snowflake.next_id()
+        device_id = 'D' + self.snowflake.next_id()
+        now = lambda: int(round(time.time() * 1000))
+
+        route_gateway = Cache.get_route_gateway(saasId)
+        call_info = CallInfo(call_id=call_id, agent_key=agent.agent_number, cti_host=agent.sip_server,
+                             caller=request.caller, called=request.called, direction=Direction.INBOUND,
+                             caller_display=request.caller_display, called_display=request.called_display,
+                             call_type=request.call_type, call_time=now, follow_data=request.follow_data,
+                             uuid1=request.uuid1, uuid2=request.uuid2)
+        device_info = DeviceInfo(device_id=device_id, call_time=now, call_id=call_id, device_type=DeviceType.AGENT,
+                                 agent_key=agent.agent_number)
+        call_info.device_list.append(device_info)
+        call_info.next_commands.append(NextCommand(device_id, NextType.NEXT_CALL_OTHER))
+        call_info.device_info_map[device_id] = device_info
+        Cache.add_call_info(call_info)
+        self.client.make_call(route_gateway, request.called, request.caller, call_id, device_id)
 
     def hold(self, call_info: CallInfo, device_id):
-        pass
+        devices = call_info.device_list
+        devices.remove(device_id)
+        self.client.bridge_break(list.get(0))
+        self.client.hold_play(list.get(0), HOLD_MUSIC_PATH)
 
     def transfer(self, call_info: CallInfo, agent_number, service_id):
-        pass
+        agent = Cache.get_agent_info(call_info.agent_key)
+        device_id = 'D' + self.snowflake.next_id()
+        now = lambda: int(round(time.time() * 1000))
+
+        device = DeviceInfo(device_id=device_id, caller=call_info.called, called=agent_number)
 
     def hangup(self, request: HangupCallRequest):
         pass
@@ -42,18 +63,3 @@ class CallService:
 
     def checkin_call(self, request: CheckInCallRequest):
         pass
-
-    def add_call_set(self, saas_id, call_id, call_type):
-        pass
-
-    def get_call_set(self, saas_id, call_type):
-        pass
-
-    def remove_from_call_set(self, saas_id, call_id, call_type):
-        pass
-
-    def clear_call_set(self, saas_id, call_type):
-        pass
-
-    def load_call_id_by_device_id(self, device_id):
-        pass

+ 3 - 0
src/core/callcenter/constant.py

@@ -2,6 +2,7 @@
 # encoding:utf-8
 
 saasId = "mdj"
+
 UTF_8 = "UTF-8"
 SPACE = " "
 EMPTY = ""
@@ -37,6 +38,8 @@ READY_TIMES = "readyTimes"
 #服务次数
 SEREVICE_TIMES = "serviceTimes"
 
+HOLD_MUSIC_PATH = '/'
+
 EMPTY = ""
 DEFAULT_KEY = ""
 

+ 12 - 0
src/core/callcenter/enumeration.py

@@ -4,6 +4,18 @@
 from enum import Enum
 
 
+class DeviceType(Enum):
+    AGENT = (1, "坐席")
+    CUSTOMER = (2, "客户")
+    ROBOT = (4, "机器人")
+    LISTENER = (5, "监听者")
+    VIRTUAL_AGENT = (6, "虚拟坐席")
+
+    def __init__(self, code, description):
+        self.code = code
+        self.description = description
+
+
 class CallType(Enum):
     IM = (0, 'IM')
     INBOUND_CALL = (1, '呼入')

+ 3 - 5
src/core/callcenter/esl/client.py

@@ -2,7 +2,6 @@
 # encoding:utf-8
 
 import random
-import logging
 import ESL
 import time
 import mmh3
@@ -21,9 +20,6 @@ from src.core.callcenter.enumeration import CallCauseEnum
 from src.core.callcenter.esl.handler.default_esl_event_handler import DefaultEslEventHandler
 
 
-logger = logging.getLogger(__name__)
-
-
 @singleton
 class InboundClient:
 
@@ -35,6 +31,8 @@ class InboundClient:
         self.default_event_handler = DefaultEslEventHandler(self)
         self.host, self.port, self.password = 'localhost', '8021', '4918257983818884358'
         self.executors = {x: concurrent.futures.ThreadPoolExecutor(max_workers=1) for x in range(self.thread_num)}
+        from src.core.server import app
+        self.logger = app.logger
 
         threading.Thread(target=self.start, args=()).start()
         # gw = CacheUtil.getRouteGetway(saasId)
@@ -198,7 +196,7 @@ class InboundClient:
         msg.addHeader("call-command", EXECUTE)
         msg.addHeader("execute-app-name", HANGUP)
         msg.addHeader("execute-app-arg", NORMAL_CLEARING)
-        logger.info("hangup_call挂机 hangup call: {}, device: {}, ctiCauseEnum:{}", call_id, device_id, case_enum)
+        self.logger.info("hangup_call挂机 hangup call: {}, device: {}, ctiCauseEnum:{}", call_id, device_id, case_enum)
         self.send_args(device_id, SET, EslEventUtil.SIP_H_P_WDH_HANGUP_CAUSE + "=" + case_enum.description)
         self.con.sendEvent(msg)
 

+ 42 - 0
src/core/callcenter/esl/handler/channel_answer_handler.py

@@ -1,9 +1,13 @@
 #!/usr/bin/env python3
 # encoding:utf-8
 
+import src.core.callcenter.cache as Cache
+from src.core.callcenter.enumeration import NextType
 from src.core.callcenter.esl.annotation import EslEventName
+import src.core.callcenter.esl.utils.esl_event_util as EslEventUtil
 from src.core.callcenter.esl.constant.event_names import CHANNEL_ANSWER
 from src.core.callcenter.esl.handler.esl_event_handler import EslEventHandler
+from src.core.callcenter.model import CallInfo, DeviceInfo, NextCommand
 
 
 @EslEventName(CHANNEL_ANSWER)
@@ -13,4 +17,42 @@ class ChannelAnswerHandler(EslEventHandler):
         super().__init__(inbound)
 
     def handle(self, address, event, coreUUID):
+        call_id = EslEventUtil.getCallId(event)
+        call_info = Cache.get_call_info(call_id)
+        if not call_info:
+            return
+
+        device_id = EslEventUtil.getDeviceId(event)
+        device_info = call_info.device_info_map.get(device_id)
+        next_command = call_info.next_commands[0] if len(call_info.next_commands) > 0 else None
+        if not next_command:
+            return
+
+        device_info.answer_time = EslEventUtil.getEventDateTimestamp(event)
+        device_info.ring_end_time = EslEventUtil.getEventDateTimestamp(event)
+        call_info.answer_count = call_info.answer_count + 1
+        call_info.next_commands.remove(next_command)
+
+        if NextType.NEXT_CALL_OTHER == next_command.next_type:
+            self.call_other(call_info, device_info)
+        elif NextType.NEXT_TRANSFER_CALL == next_command.next_type:
+            self.transfer_call(call_info, next_command, event)
+        elif NextType.NEXT_CALL_BRIDGE == next_command.next_type:
+            self.call_bridge(call_info, device_info, next_command, event)
+        elif NextType.NEXT_LISTEN_CALL == next_command.next_type:
+            self.listen(call_info, device_info, next_command, event)
+        else:
+            self.logger.warn("can not match command :%s, callId:%s", next_command.next_type, call_id)
+        Cache.add_call_info(call_info)
+
+    def call_other(self, call: CallInfo, device: DeviceInfo):
+        pass
+
+    def transfer_call(self, call: CallInfo, next_command: NextCommand, event):
+        pass
+
+    def call_bridge(self, call: CallInfo, device: DeviceInfo, next_command: NextCommand, event):
+        pass
+
+    def listen(self, call: CallInfo, device: DeviceInfo, next_command: NextCommand, event):
         pass

+ 1 - 1
src/core/callcenter/esl/handler/default_esl_event_handler.py

@@ -12,5 +12,5 @@ class DefaultEslEventHandler(EslEventHandler):
         super().__init__(inbound)
 
     def handle(self, address, event, coreUUID):
-        print('default event handle,', threading.currentThread().name, json.loads(event.serialize('json')))
+        self.logger.info(json.loads(event.serialize('json')))
 

+ 2 - 0
src/core/callcenter/esl/handler/esl_event_handler.py

@@ -7,6 +7,8 @@ from abc import ABC, abstractmethod
 class EslEventHandler(ABC):
     def __init__(self, inbound_client):
         self.inbound_client = inbound_client
+        from src.core.server import app
+        self.logger = app.logger
 
     @abstractmethod
     def handle(self, address, event, coreUUID):

+ 0 - 78
src/core/callcenter/esl/utils/call_cache_util.py

@@ -1,78 +0,0 @@
-#!/usr/bin/env python3
-# encoding:utf-8
-
-import json
-
-from src.core.callcenter.constant import *
-from src.core.callcenter.model import AgentInfo, CallInfo, RouteGateway
-from src.core.datasource import RedisHandler
-
-cacheDay = 7
-deviceCall = {}
-redis_handler = RedisHandler()
-print(redis_handler.redis.info())
-
-
-def getAgentInfo(agentKey):
-    text = redis_handler.get(AGENT_INFO + agentKey)
-    if not text:
-        return None
-    return AgentInfo.from_json(text)
-
-
-def refleshAgentToken(agentKey, token):
-    redis_handler.set(AGENT_TOKEN + token, agentKey, cacheDay*24*60*60)
-
-
-def deleteKey(key):
-    redis_handler.redis.delete(key)
-
-
-def getAgentKey(token):
-    return redis_handler.get(AGENT_TOKEN + token)
-
-
-# 缓存坐席
-def addAgentInfo(agentInfo: AgentInfo):
-    redis_handler.set(AGENT_INFO + agentInfo.agent_number, json.dumps(agentInfo), cacheDay*24*60*60)
-
-
-# 缓存CALL_INFO
-def addCallInfo(callInfo: CallInfo):
-    redis_handler.set(AGENT_INFO + callInfo.call_id, json.dumps(callInfo), cacheDay*24*60*60)
-
-
-def getCallInfoByDeviceId(deviceId):
-    callId = deviceCall.get(deviceId)
-    if not callId:
-        return getCallInfo(callId)
-
-
-# 获取callInfo
-def getCallInfo(callId):
-    text = redis_handler.get(CALL_INFO+callId)
-    if not text:
-        return CallInfo.from_json(text)
-
-
-def removeCallInfo(callId):
-    callInfo = getCallInfo(callId)
-    if callInfo and callInfo.device_info_map:
-        for k, v in callInfo.device_info_map.items():
-            deviceCall.pop(k)
-    deleteKey(CALL_INFO + callId)
-
-
-def addDevice(device, callId):
-    deviceCall[device] = callId
-
-
-def getRouteGetway(saasId):
-    return RouteGateway(id=1,
-                        saas_id=saasId,
-                        name='63366692',
-                        media_host='192.168.20.99',
-                        media_port=5060,
-                        caller_prefix='',
-                        called_prefix='',
-                        status=0)

+ 20 - 20
src/core/callcenter/esl/utils/esl_event_util.py

@@ -72,13 +72,13 @@ VARIABLE_SIP_TERM_STATUS = "variable_sip_term_status"
 VARIABLE_LOCAL_MEDIA_IP = "variable_local_media_ip"
 VARIABLE_SIP_FROM_USER = "variable_sip_from_user"
 
-VARIABLE_SIP_WDH_CALLID = "variable_sip_h_P-WDH-CallId"
-VARIABLE_SIP_WDH_IS_LAST_CALL = "variable_sip_h_P-WDH-IsLastCall"
+VARIABLE_SIP_LIBRA_CALLID = "variable_sip_h_P-LIBRA-CallId"
+VARIABLE_SIP_LIBRA_IS_LAST_CALL = "variable_sip_h_P-LIBRA-IsLastCall"
 VARIABLE_ORIGINATION_UUID = "variable_origination_uuid"
-VARIABLE_SIP_WDH_DEVICE_ID = "variable_sip_h_P-WDH-DeviceId"
+VARIABLE_SIP_LIBRA_DEVICE_ID = "variable_sip_h_P-LIBRA-DeviceId"
 VARIABLE_SIP_WHD_HANGUP_REASON = "variable_sip_bye_h_X-CIN-HangUpReason"
 VARIABLE_SIP_WHD_SERVICE_ID = "variable_sip_bye_h_X-CIN-ServiceId"
-VARIABLE_SIP_H_P_WDH_IS_EAVESDROP = "variable_sip_h_P-WDH-is-eavesdrop"
+VARIABLE_SIP_H_P_LIBRA_IS_EAVESDROP = "variable_sip_h_P-LIBRA-is-eavesdrop"
 
 BRIDGE_A_UNIQUE_ID = "Bridge-A-Unique-ID"
 BRIDGE_B_UNIQUE_ID = "Bridge-B-Unique-ID"
@@ -95,21 +95,21 @@ X_OPERATE_TYPE = "variable_sip_h_X-operateType"
 VARIABLE_SIP_H_X_AGENT_INFO = "variable_sip_h_X-agentInfo"
 VARIABLE_SIP_H_X_CUSTOM_INFO = "variable_sip_h_X-customInfo"
 VARIABLE_SIP_H_X_CALL_SEQUENCE_ID = "variable_sip_h_X-callSequenceId"
-SIP_H_P_WDH_CALL_SEQUENCE_ID = "sip_h_P-WDH-callSequenceId"
-VARIABLE_SIP_H_P_WDH_CALL_SEQUENCE_ID = "variable_sip_h_P-WDH-callSequenceId"
-SIP_H_P_WDH_CALL_ID = "sip_h_P-WDH-CallId"
-VARIABLE_SIP_H_P_WDH_CTI_FLOW_ID = "variable_sip_h_P-WDH-CtiFlowId"
-VARIABLE_SIP_H_P_WDH_NEED_HOLD_MUSIC = "variable_sip_h_P-WDH-need-hold-music"
+SIP_H_P_LIBRA_CALL_SEQUENCE_ID = "sip_h_P-LIBRA-callSequenceId"
+VARIABLE_SIP_H_P_LIBRA_CALL_SEQUENCE_ID = "variable_sip_h_P-LIBRA-callSequenceId"
+SIP_H_P_LIBRA_CALL_ID = "sip_h_P-LIBRA-CallId"
+VARIABLE_SIP_H_P_LIBRA_CTI_FLOW_ID = "variable_sip_h_P-LIBRA-CtiFlowId"
+VARIABLE_SIP_H_P_LIBRA_NEED_HOLD_MUSIC = "variable_sip_h_P-LIBRA-need-hold-music"
 OTHER_LEG_UNIQUE_ID = "Other-Leg-Unique-ID"
 DETECTED_TONE = "Detected-Tone"
 
 VARIABLE_EXECUTE_ON_PRE_ANSWER = "variable_execute_on_pre_answer"
 RECORD_FILE_PATH = "Record-File-Path"
 
-SIP_H_P_WDH_HANGUP_CAUSE = "sip_h_P-WDH-hangupCause"
-VARIABLE_SIP_H_P_WDH_HANGUP_CAUSE = "variable_sip_h_P-WDH-hangupCause"
+SIP_H_P_LIBRA_HANGUP_CAUSE = "sip_h_P-LIBRA-hangupCause"
+VARIABLE_SIP_H_P_LIBRA_HANGUP_CAUSE = "variable_sip_h_P-LIBRA-hangupCause"
 VARIABLE_SIP_INVITE_FAILURE_STATUS = "variable_sip_invite_failure_status"
-VARIABLE_SIP_H_P_WDH_CALLED = "variable_sip_h_P-WDH-called"
+VARIABLE_SIP_H_P_LIBRA_CALLED = "variable_sip_h_P-LIBRA-called"
 
 
 def getCoreUuid(e):
@@ -121,7 +121,7 @@ def getUniqueId(e):
 
 
 def getVariableSipHPWdhCalled(e):
-    return e.getHeader(VARIABLE_SIP_H_P_WDH_CALLED)
+    return e.getHeader(VARIABLE_SIP_H_P_LIBRA_CALLED)
 
 
 def getOtherLegUniqueId(e):
@@ -133,11 +133,11 @@ def getVariableSipHXCallSequenceId(e):
 
 
 def getVariableSipHPWdhCtiFlowId(e):
-    return e.getHeader(VARIABLE_SIP_H_P_WDH_CTI_FLOW_ID)
+    return e.getHeader(VARIABLE_SIP_H_P_LIBRA_CTI_FLOW_ID)
 
 
 def getVariableSipHPWdhCallSequenceId(e):
-    return e.getHeader(VARIABLE_SIP_H_P_WDH_CALL_SEQUENCE_ID)
+    return e.getHeader(VARIABLE_SIP_H_P_LIBRA_CALL_SEQUENCE_ID)
 
 
 def getPlaybackFilePath(e):
@@ -153,7 +153,7 @@ def getVariableSipHXCustomInfo(e):
 
 
 def getIsEavesdrop(e):
-    return e.getHeader(VARIABLE_SIP_H_P_WDH_IS_EAVESDROP)
+    return e.getHeader(VARIABLE_SIP_H_P_LIBRA_IS_EAVESDROP)
 
 
 def getXOperateType(e):
@@ -377,7 +377,7 @@ def getSipFromUser(e):
 
 
 def getCallId(e):
-    return e.getHeader(VARIABLE_SIP_WDH_CALLID)
+    return e.getHeader(VARIABLE_SIP_LIBRA_CALLID)
 
 
 def getOriginationUuid(e):
@@ -385,7 +385,7 @@ def getOriginationUuid(e):
 
 
 def getDeviceId(e):
-    return e.getHeader(VARIABLE_SIP_WDH_DEVICE_ID)
+    return e.getHeader(VARIABLE_SIP_LIBRA_DEVICE_ID)
 
 
 def getWDHHangupReason(e):
@@ -425,7 +425,7 @@ def getVariableDa2Result(e):
 
 
 def getVariableNeedHoldMusic(e):
-    return e.getHeader(VARIABLE_SIP_H_P_WDH_NEED_HOLD_MUSIC)
+    return e.getHeader(VARIABLE_SIP_H_P_LIBRA_NEED_HOLD_MUSIC)
 
 
 def getVariableExecuteOnPreAnswerPath(e):
@@ -445,7 +445,7 @@ def getDetectedTone(e):
 
 
 def getVariableSipHPWdhHangupCause(e):
-    return e.getHeader(VARIABLE_SIP_H_P_WDH_HANGUP_CAUSE)
+    return e.getHeader(VARIABLE_SIP_H_P_LIBRA_HANGUP_CAUSE)
 
 
 def getSipCode(e):

+ 8 - 7
src/core/callcenter/model.py

@@ -13,7 +13,8 @@ class MakeCallRequest:
     呼叫请求对象
     """
 
-    def __init__(self, follow_data: Dict[str, Any], uuid1,uuid2,call_type: CallType,caller,called,caller_display,called_display):
+    def __init__(self, follow_data: Dict[str, Any], uuid1, uuid2, call_type: CallType, caller, called, caller_display,
+                 called_display):
         # 随路数据
         self.follow_data: Dict[str, Any] = follow_data
         # uuid1
@@ -52,13 +53,14 @@ class HangupCallRequest:
 
 class CheckInCallRequest:
     """检查是否在通话中实体"""
+
     def __init__(self, saas_id, agent_number):
         self.saas_id = saas_id
         self.agent_number = agent_number
 
 
 class AgentInfo:
-    def __init__(self, sass_id=None, agent_number=None, realm=None, sip_server=None, server_num=None,
+    def __init__(self, sass_id=None, agent_number=None, realm=None, sip_server=None,
                  call_id=None, device_id=None, real_device_id=None, line_id=None, fs_user=None, domain=None,
                  service_time=0, max_ready_time=0, total_ready_time=0, ready_times=0, not_ready_times=0,
                  total_after_time=0, max_talk_time=0, total_talk_time=0, total_ring_times=0, total_answer_times=0):
@@ -66,7 +68,6 @@ class AgentInfo:
         self.agent_number = agent_number
         self.realm = realm
         self.sip_server = sip_server
-        self.server_num = server_num
         self.call_id = call_id
         self.device_id = device_id
         # 机器人外呼时真实fs
@@ -267,13 +268,13 @@ class DeviceInfo:
 
 class NextCommand:
 
-    def __init__(self, deviceId, nextType, nextValue):
+    def __init__(self, device_id, next_type, next_value):
         # 记录执行设备
-        self.deviceId = deviceId
+        self.device_id = device_id
         # 下一步执行命令
-        self.nextType = nextType
+        self.next_type = next_type
         # 执行参数
-        self.nextValue = nextValue
+        self.next_value = next_value
 
     def to_json_string(self):
         return json.dumps(self.__dict__, ensure_ascii=False)

+ 51 - 0
src/core/callcenter/snowflake.py

@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+# encoding:utf-8
+
+import time
+import random
+
+
+class Snowflake:
+    def __init__(self, worker_id, data_center_id):
+        ### 机器标识ID
+        self.worker_id = worker_id
+        ### 数据中心ID
+        self.data_center_id = data_center_id
+        ### 计数序列号
+        self.sequence = 0
+        ### 时间戳
+        self.last_timestamp = -1
+
+    def next_id(self):
+        timestamp = int(time.time() * 1000)
+        if timestamp < self.last_timestamp:
+            raise Exception("Clock moved backwards. Refusing to generate id for %d milliseconds" % abs(
+                timestamp - self.last_timestamp))
+        if timestamp == self.last_timestamp:
+            self.sequence = (self.sequence + 1) & 4095
+            if self.sequence == 0:
+                timestamp = self.wait_for_next_millis(self.last_timestamp)
+        else:
+            self.sequence = 0
+        self.last_timestamp = timestamp
+        return ((timestamp - 1288834974657) << 22) | (self.data_center_id << 17) | (
+                    self.worker_id << 12) | self.sequence
+
+    def wait_for_next_millis(self, last_timestamp):
+        timestamp = int(time.time() * 1000)
+        while timestamp <= last_timestamp:
+            timestamp = int(time.time() * 1000)
+        return timestamp
+
+    ### test
+
+
+if __name__ == '__main__':
+    worker_id = 1
+    data_center_id = 1
+    snowflake = Snowflake(worker_id, data_center_id)
+    for i in range(10):
+        try:
+            print(snowflake.next_id())
+        except Exception as e:
+            print("Clock moved backwards:", e)

+ 2 - 4
src/core/callcenter/ws.py

@@ -1,13 +1,11 @@
 #!/usr/bin/env python3
 # encoding:utf-8
 
-import logging
-from src.core.callcenter.server import app
+from src.core.server import app
 from flask import request, session
 from flask_socketio import SocketIO, Namespace, join_room, leave_room, emit
 
 socketio = SocketIO(app)
-logger = logging.getLogger(__name__)
 
 
 def common_down_data(user_id, data):
@@ -21,7 +19,7 @@ def common_down_cmd(user_id, data):
 class MyNamespace(Namespace):
 
     def on_connect(self):
-        logger.info('Client connected, {}', request.sid)
+        print('Client connected, ', request.sid)
 
     def on_login(self, msg):
         print('login.Received message: {}, {}', str(msg), request.sid)

+ 44 - 2
src/core/callcenter/server.py → src/core/server.py

@@ -2,12 +2,54 @@
 # encoding:utf-8
 import threading
 
+from logging.config import dictConfig
 from flask import Flask, render_template_string
-from src.core.callcenter.esl.client import InboundClient
+
+
+dictConfig({
+        "version": 1,
+        "disable_existing_loggers": False,  # 不覆盖默认配置
+        "formatters": {  # 日志输出样式
+            "default": {
+                "format": "%(asctime)s - %(module)s.%(lineno)d - %(levelname)s - %(threadName)s: %(message)s"
+            }
+        },
+        "handlers": {
+            "console": {
+                "class": "logging.StreamHandler",  # 控制台输出
+                "level": "DEBUG",
+                "formatter": "default",
+            },
+            "log_file": {
+                "class": "logging.handlers.RotatingFileHandler",
+                "level": "INFO",
+                "formatter": "default",   # 日志输出样式对应formatters
+                "filename": "./logs/flask.log",  # 指定log文件目录
+                "maxBytes": 20*1024*1024,   # 文件最大20M
+                "backupCount": 10,          # 最多10个文件
+                "encoding": "utf8",         # 文件编码
+            },
+
+        },
+        "root": {
+            "level": "DEBUG",  # # handler中的level会覆盖掉这里的level
+            "handlers": ["console", "log_file"],
+        },
+    }
+)
+
 
 app = Flask(__name__)
 app.config['SECRET_KEY'] = ''
-inbound_client = InboundClient()
+
+
+def new_client():
+    from src.core.callcenter.esl.client import InboundClient
+    client = InboundClient()
+    return client
+
+
+inbound_client = new_client()
 
 
 @app.route('/')