Przeglądaj źródła

feat: 增加签出,置忙,置闲逻辑

余尚辉 4 miesięcy temu
rodzic
commit
de54051ab2
1 zmienionych plików z 150 dodań i 14 usunięć
  1. 150 14
      src/core/callcenter/agent.py

+ 150 - 14
src/core/callcenter/agent.py

@@ -2,6 +2,7 @@
 # encoding:utf-8
 
 import traceback
+from collections import defaultdict
 from src.core.callcenter.constant import START_AGENT_NUM
 from src.core.callcenter.enumeration import AgentState, AgentCheck, AgentHeartState, AgentServiceState, AgentLogState, \
     AgentScene, BizErrorCode, WorkStatus, DownEvent, HumanState
@@ -66,15 +67,50 @@ class AgentOperService:
             self._handle_idle(req.scene, agent)
         return self._push_event_for_checkin(agent, agent_monitor, phone, req.scene)
 
-    def checkout(self, request: AgentActionRequest):
-        pass
+    def checkout(self, req: AgentActionRequest):
+        agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
+        if not agent or agent.agent_state == AgentState.DISABLE.code:
+            raise BizException(BizErrorCode.AGENT_DISABLE_NOT_ALLOW_OPERATE)
 
-    def busy(self, request: AgentActionRequest):
-        pass
+        agent_monitor = _get_agent_monitor(req.saas_id, req.agent_number)
+        if not agent_monitor or agent_monitor.service_state == AgentServiceState.CALLING.code:
+            raise BizException(BizErrorCode.AGENT_CALLING_NOT_ALLOW_OPERATE)
 
-    def idle(self, request: AgentActionRequest):
-        pass
+        if agent_monitor.check_state == AgentCheck.OUT.code:
+            return self._push_event_for_checkout(agent, req.scene)
+
+        self.agent_monitor_service.update_checkout(agent_monitor)
+        self.agent_actionlog_service.insert_check_state(agent_monitor, AgentCheck.OUT, AgentLogState.CHECKOUT)
+        self.agent_state_service.checkout(agent.saas_id, agent.out_id, agent.phone_num)
+
+        return self._push_event_for_checkout(agent, req.scene)
+
+    def busy(self, req: AgentActionRequest):
+        agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
+        if not agent or agent.agent_state == AgentState.DISABLE.code:
+            raise BizException(BizErrorCode.AGENT_DISABLE_NOT_ALLOW_OPERATE)
+
+        agent_monitor = _get_agent_monitor(req.saas_id, req.agent_number)
+        if not agent_monitor or agent_monitor.check_state == AgentCheck.OUT.code:
+            raise BizException(BizErrorCode.AGENT_CHECK_OUT_NOT_ALLOW_OPERATE)
+
+        if agent_monitor.service_state == AgentServiceState.CALLING.code:
+            raise BizException(BizErrorCode.AGENT_CALLING_NOT_ALLOW_OPERATE)
+
+        self.agent_state_service.busy(agent.saas_id, agent.out_id, agent.phone_num)
+
+        if agent_monitor.service_state == AgentServiceState.BUSY.code:
+            self._push_event_for_busy(agent, req.scene)
+            return
+        self.agent_monitor_service.update_busy(agent_monitor)
+        self.agent_actionlog_service.insert_service_state(agent_monitor, AgentServiceState.BUSY, AgentLogState.BUSY)
+        self._push_event_for_busy(agent, req.scene)
 
+    def idle(self, req: AgentActionRequest):
+        agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
+        if not agent or agent.agent_state == AgentState.DISABLE.code:
+            raise BizException(BizErrorCode.AGENT_DISABLE_NOT_ALLOW_OPERATE)
+        self._handle_idle(req.scene, agent)
     def assign(self, request: AgentActionRequest):
         pass
 
@@ -119,6 +155,24 @@ class AgentOperService:
                                    }
                            }
         return event_data.__dict__
+    def _push_event_for_checkout(self,agent,scene):
+        """签出事件推送"""
+        agent_scene = AgentScene.get_by_code(scene)
+        self.push_handler.push_on_agent_work_report(agent.saas_id, "", agent.out_id, "", agent_scene, WorkStatus.NO_INIT, '签出成功')
+
+        event_data = AgentEventData(agent.saas_id, agent.out_id)
+        event_data.data = {'eventName': DownEvent.ON_INITAL_FAILURE.code,
+                           'ext': {'saasId': agent.saas_id,
+                                   'agentId': agent.out_id,
+                                   }
+                           }
+        return event_data.__dict__
+
+    def _push_event_for_busy(self,agent,scene):
+        """置忙事件推送"""
+        agent_scene = AgentScene.get_by_code(scene)
+        self.push_handler.push_on_agent_work_report(agent.saas_id, "", agent.out_id, "", agent_scene, WorkStatus.AGENT_BUSY)
+        pass
 
     def _push_event_for_idle(self, agent, scene):
         """坐席置闲事件推送"""
@@ -405,17 +459,27 @@ class AgentActionLogService:
 
 
 class AgentStateService:
-
+    state_service_id_data_map = defaultdict(lambda: defaultdict(int))  # 作为静态变量
     def __init__(self, client, logger):
         self.inbound_client = client
         self.logger = logger
         self.redis_handler = RedisHandler()
     
     def idle(self, saas_id, agent_id, phone_num):
-        pass
+        # HumanService 这个地方没实现 没看明白
+        human_service = HumanService(saas_id, agent_id)
+        if human_service is None:
+            print(f"agent engine idle not have human service {saas_id} {agent_id}")  # 使用print替代log
+            return
+        self.idle_hash(saas_id, agent_id, phone_num, human_service.service_id)
 
     def busy(self, saas_id, agent_id, phone_num):
-        pass
+        # HumanService 这个地方没实现 没看明白
+        human_service = HumanService(saas_id, agent_id)
+        if human_service is None:
+            print(f"agent engine busy not hava human service {saas_id} {agent_id}")  # 使用print替代log
+            return
+        self.busy_hash(saas_id, agent_id, phone_num, human_service.service_id)
 
     def idle_by_human(self, saas_id, agent_id, service_id):
         pass
@@ -432,7 +496,8 @@ class AgentStateService:
         self.redis_handler.redis.expire(key, self._get_expire_time())
 
     def checkout(self, saas_id, agent_id, phone_num):
-        pass
+        key = self._check_in_key(saas_id)
+        self.redis_handler.redis.hdel(key, phone_num)
 
     def assign_agent(self, saas_id, service_id, ivr_id, task_id, called, cbp):
         pass
@@ -441,19 +506,90 @@ class AgentStateService:
         pass
 
     def idle_hash(self, saas_id, agent_id, phone_num, service_id):
-        pass
+        key =  self._key(saas_id, service_id)
+        state_data = AgentStateData()
+
+        cache_agent_map = self.get_cache_agent_map(saas_id, service_id)
+        if cache_agent_map and phone_num in cache_agent_map:
+            state_data = cache_agent_map[phone_num]
+
+        state_data.status = HumanState.IDLE.code
+        state_data.time = datetime.utcnow().timestamp()
+        self.redis_handler.redis.hset(key, phone_num, state_data.to_json_string())
+        self.redis_handler.redis.expire(key, self._get_expire_time())
+        self.update_report_state(saas_id, service_id)
 
     def busy_hash(self, saas_id, agent_id, phone_num, service_id):
         pass
 
+    def get_cache_agent_map(self, saas_id, service_id):
+        cache_agent_list = self.get_cache_agent_list(saas_id, service_id)
+        # 检查列表是否为空,如果为空返回空字典
+        if not cache_agent_list:
+            return {}
+
+        # 使用字典推导式将 cache_agent_list 转换为字典
+        return {agent.phone_num: agent for agent in cache_agent_list}
+
     def get_agent_service_idle_size(self, saas_id, service_id):
-        pass
+        idle_agents_size = 0
+        cache_agent_list = self.get_cache_agent_list(saas_id, service_id)
+        if cache_agent_list:  # 检查列表是否非空
+            idle_agents = self.get_idle_agents(cache_agent_list)
+            idle_agents_size = len(idle_agents)  # 获取空闲代理的数量
+        return idle_agents_size
+    def get_cache_agent_list(self, saas_id, service_id):
+        redis_key = self._key(saas_id, service_id)
+        map_cache_by_key = self.redis_handler.redis.get(redis_key, str)
+
+        if not map_cache_by_key:  # 检查字典是否为空
+            return []  # 返回空列表
+
+        free_agents = []
+        for phone_num, json_value in map_cache_by_key.items():
+            agent_status_data_dict = json.loads(json_value)
+            agent_status_data = AgentStateData(**agent_status_data_dict)  # 解析 JSON 为 AgentStateData 对象
+            agent_status_data.phone_num = phone_num  # 设置电话号码
+            free_agents.append(agent_status_data)
+        return free_agents
 
     def get_agent_service_busy_size(self, saas_id, service_id):
-        pass
-
+        busy_agents_size = 0
+        cache_agent_list = self.get_cache_agent_list(saas_id, service_id)
+        if cache_agent_list:  # 检查列表是否非空
+            idle_agents = self.get_idle_agents(cache_agent_list)
+            busy_agents = [agent for agent in cache_agent_list if agent not in idle_agents]  # 计算忙碌代理
+            busy_agents_size = len(busy_agents)  # 获取忙碌代理的数量
+        return busy_agents_size
+
+    def update_report_state(self, saas_id, service_id):
+        key = self._key(saas_id, service_id)
+        # data_map 这个地方有疑问
+        data_map = self.state_service_id_data_map.setdefault(key, {})
+        idle = HumanState.IDLE
+        if idle.value not in data_map:
+            data_map[idle.code] = self.do_report_real_time_human_service_id(saas_id, service_id, idle)
+        busy = HumanState.BUSY
+        if busy.value not in data_map:
+            data_map[busy.code] = self.do_report_real_time_human_service_id(saas_id, service_id, busy)
+
+    def do_report_real_time_human_service_id(self, saas_id, service_id, human_state):
+        name = "cti_center_real_time_human_service_state"
+        tag_list = {
+            "vcc_id": saas_id,
+            "service_id": service_id,
+            "state": human_state.code,
+        }
+        if human_state == HumanState.IDLE:
+            # meter_registry 这块疑问
+            self.meter_registry.gauge(name, tag_list, self, lambda ctx: ctx.get_agent_service_busy_size(saas_id, service_id))
+        elif human_state == HumanState.BUSY:
+            self.meter_registry.gauge(name, tag_list, self, lambda ctx: ctx.get_agent_service_idle_size(saas_id, service_id))
+        return 0
     def _check_in_key(self, saas_id):
         return "CTI:%s:HUMAN:AGENT".format(saas_id.upper())
+    def _key(self, saas_id, service_id):
+        return "CTI:%s:HUMAN:%s".format(saas_id.upper(), service_id)
 
     def _get_expire_time(self):
         now = datetime.now()