|
@@ -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()
|