|
@@ -2,6 +2,7 @@
|
|
# encoding:utf-8
|
|
# encoding:utf-8
|
|
|
|
|
|
import traceback
|
|
import traceback
|
|
|
|
+from typing import List
|
|
from collections import defaultdict
|
|
from collections import defaultdict
|
|
from src.core.callcenter.constant import START_AGENT_NUM
|
|
from src.core.callcenter.constant import START_AGENT_NUM
|
|
from src.core.callcenter.enumeration import AgentState, AgentCheck, AgentHeartState, AgentServiceState, AgentLogState, \
|
|
from src.core.callcenter.enumeration import AgentState, AgentCheck, AgentHeartState, AgentServiceState, AgentLogState, \
|
|
@@ -17,6 +18,7 @@ from src.core.datasource import RedisHandler
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
import threading
|
|
import threading
|
|
|
|
|
|
|
|
+
|
|
class AgentOperService:
|
|
class AgentOperService:
|
|
|
|
|
|
def __init__(self, client, logger):
|
|
def __init__(self, client, logger):
|
|
@@ -113,6 +115,7 @@ class AgentOperService:
|
|
if not agent or agent.agent_state == AgentState.DISABLE.code:
|
|
if not agent or agent.agent_state == AgentState.DISABLE.code:
|
|
raise BizException(BizErrorCode.AGENT_DISABLE_NOT_ALLOW_OPERATE)
|
|
raise BizException(BizErrorCode.AGENT_DISABLE_NOT_ALLOW_OPERATE)
|
|
self._handle_idle(req.scene, agent)
|
|
self._handle_idle(req.scene, agent)
|
|
|
|
+
|
|
def assign(self, request: AgentActionRequest):
|
|
def assign(self, request: AgentActionRequest):
|
|
pass
|
|
pass
|
|
|
|
|
|
@@ -133,6 +136,7 @@ class AgentOperService:
|
|
self.agent_monitor_service.update_calling(agent_monitor)
|
|
self.agent_monitor_service.update_calling(agent_monitor)
|
|
self.agent_state_service.busy(agent.saas_id, agent.out_id, agent.phone_num)
|
|
self.agent_state_service.busy(agent.saas_id, agent.out_id, agent.phone_num)
|
|
self.push_handler.push_on_agent_report(agent.saas_id, agent.out_id, agent_scene, AgentServiceState.BUSY)
|
|
self.push_handler.push_on_agent_report(agent.saas_id, agent.out_id, agent_scene, AgentServiceState.BUSY)
|
|
|
|
+
|
|
def _handle_idle(self, scene, agent):
|
|
def _handle_idle(self, scene, agent):
|
|
agent_monitor = _get_agent_monitor(agent.saas_id, agent.agent_num)
|
|
agent_monitor = _get_agent_monitor(agent.saas_id, agent.agent_num)
|
|
if agent_monitor.check_state == AgentCheck.OUT.code:
|
|
if agent_monitor.check_state == AgentCheck.OUT.code:
|
|
@@ -168,6 +172,7 @@ class AgentOperService:
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return event_data.__dict__
|
|
return event_data.__dict__
|
|
|
|
+
|
|
def _push_event_for_checkout(self,agent,scene):
|
|
def _push_event_for_checkout(self,agent,scene):
|
|
"""签出事件推送"""
|
|
"""签出事件推送"""
|
|
agent_scene = AgentScene.get_by_code(scene)
|
|
agent_scene = AgentScene.get_by_code(scene)
|
|
@@ -192,6 +197,7 @@ class AgentOperService:
|
|
agent_scene = AgentScene.get_by_code(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_READY)
|
|
self.push_handler.push_on_agent_work_report(agent.saas_id, "", agent.out_id, "", agent_scene, WorkStatus.AGENT_READY)
|
|
|
|
|
|
|
|
+
|
|
class AgentService:
|
|
class AgentService:
|
|
|
|
|
|
def __init__(self, client, logger):
|
|
def __init__(self, client, logger):
|
|
@@ -479,7 +485,9 @@ class AgentStateService:
|
|
self.assigned_recycle_millisecond = 60000
|
|
self.assigned_recycle_millisecond = 60000
|
|
self.state_service_id_data_map = defaultdict(dict)
|
|
self.state_service_id_data_map = defaultdict(dict)
|
|
self.executor = ThreadPoolExecutor(max_workers=10)
|
|
self.executor = ThreadPoolExecutor(max_workers=10)
|
|
-
|
|
|
|
|
|
+ self.agent_monitor_service = AgentMonitorService(client, logger)
|
|
|
|
+ self.agent_actionlog_service = AgentActionLogService(client, logger)
|
|
|
|
+
|
|
def idle(self, saas_id, agent_id, phone_num):
|
|
def idle(self, saas_id, agent_id, phone_num):
|
|
human_service = _get_human_service_service(saas_id, agent_id)
|
|
human_service = _get_human_service_service(saas_id, agent_id)
|
|
if human_service is None:
|
|
if human_service is None:
|
|
@@ -495,10 +503,29 @@ class AgentStateService:
|
|
self.busy_hash(saas_id, agent_id, phone_num, human_service.service_id)
|
|
self.busy_hash(saas_id, agent_id, phone_num, human_service.service_id)
|
|
|
|
|
|
def idle_by_human(self, saas_id, agent_id, service_id):
|
|
def idle_by_human(self, saas_id, agent_id, service_id):
|
|
- pass
|
|
|
|
|
|
+ agent = _get_agent(saas_id, out_id=agent_id)
|
|
|
|
+ if not agent:
|
|
|
|
+ return
|
|
|
|
+ agent_monitor = _get_agent_monitor(saas_id, agent_number=agent.agent_num)
|
|
|
|
+ if not agent_monitor:
|
|
|
|
+ return
|
|
|
|
+ if agent_monitor.check_state == AgentCheck.IN.code:
|
|
|
|
+ self.agent_monitor_service.update_idle(agent_monitor)
|
|
|
|
+ self.agent_actionlog_service.insert_service_state(agent_monitor, AgentServiceState.IDLE, AgentLogState.ACTIVE_HUMAN_SERVICE)
|
|
|
|
+ self.idle_hash(saas_id, agent_id, agent.phone_num, service_id)
|
|
|
|
|
|
def busy_by_human(self, saas_id, service_id, agent_id=None):
|
|
def busy_by_human(self, saas_id, service_id, agent_id=None):
|
|
- pass
|
|
|
|
|
|
+ agent = _get_agent(saas_id, out_id=agent_id)
|
|
|
|
+ if not agent:
|
|
|
|
+ return
|
|
|
|
+ agent_monitor = _get_agent_monitor(saas_id, agent_number=agent.agent_num)
|
|
|
|
+ if not agent_monitor:
|
|
|
|
+ return
|
|
|
|
+ if agent_monitor.check_state == AgentCheck.IN.code:
|
|
|
|
+ self.agent_monitor_service.update_busy(agent_monitor)
|
|
|
|
+ self.agent_actionlog_service.insert_service_state(agent_monitor, AgentServiceState.BUSY,
|
|
|
|
+ AgentLogState.DEL_HUMAN_SERVICE)
|
|
|
|
+ self.busy_hash(saas_id, agent_id, agent.phone_num, service_id)
|
|
|
|
|
|
def checkin(self, saas_id, agent_id, phone_num):
|
|
def checkin(self, saas_id, agent_id, phone_num):
|
|
key = self._check_in_key(saas_id)
|
|
key = self._check_in_key(saas_id)
|
|
@@ -512,14 +539,25 @@ class AgentStateService:
|
|
key = self._check_in_key(saas_id)
|
|
key = self._check_in_key(saas_id)
|
|
self.redis_handler.redis.hdel(key, phone_num)
|
|
self.redis_handler.redis.hdel(key, phone_num)
|
|
|
|
|
|
- def assign_agent(self, saas_id, service_id, ivr_id, task_id, called, cbp):
|
|
|
|
- pass
|
|
|
|
|
|
+ def assign_agent(self, saas_id, service_id, called, ivr_id=None, task_id=None, cbp=None):
|
|
|
|
+ choose_phone_num = ''
|
|
|
|
+ self.logger.info("assignAgent %s %s %s", saas_id, service_id, called)
|
|
|
|
+ idle_agents = self.idle_agents(saas_id, service_id)
|
|
|
|
+ if len(idle_agents) <= 0:
|
|
|
|
+ return choose_phone_num
|
|
|
|
+ choose_phone_num = self._choose_max_idle_time(idle_agents)
|
|
|
|
+ return choose_phone_num
|
|
|
|
|
|
def idle_agents(self, saas_id, service_id):
|
|
def idle_agents(self, saas_id, service_id):
|
|
- pass
|
|
|
|
|
|
+ cache_agent_list = self.get_cache_agent_list(saas_id, service_id)
|
|
|
|
+ if not cache_agent_list:
|
|
|
|
+ return []
|
|
|
|
+ agent_str = '\n'.join(["%s-%s-%s-%s".format(x.phone_num, x.status, x.time, x.assign_time) for x in cache_agent_list])
|
|
|
|
+ self.logger.info("assignAgent %s %s idleAgents:%s ", saas_id, service_id, agent_str)
|
|
|
|
+ return self.get_idle_agents(cache_agent_list)
|
|
|
|
|
|
def idle_hash(self, saas_id, agent_id, phone_num, service_id):
|
|
def idle_hash(self, saas_id, agent_id, phone_num, service_id):
|
|
- key = self._key(saas_id, service_id)
|
|
|
|
|
|
+ key = self._key(saas_id, service_id)
|
|
state_data = AgentStateData()
|
|
state_data = AgentStateData()
|
|
|
|
|
|
cache_agent_map = self.get_cache_agent_map(saas_id, service_id)
|
|
cache_agent_map = self.get_cache_agent_map(saas_id, service_id)
|
|
@@ -530,7 +568,7 @@ class AgentStateService:
|
|
state_data.time = datetime.utcnow().timestamp()
|
|
state_data.time = datetime.utcnow().timestamp()
|
|
self.redis_handler.redis.hset(key, phone_num, state_data.to_json_string())
|
|
self.redis_handler.redis.hset(key, phone_num, state_data.to_json_string())
|
|
self.redis_handler.redis.expire(key, self._get_expire_time())
|
|
self.redis_handler.redis.expire(key, self._get_expire_time())
|
|
- self.update_report_state(saas_id, service_id)
|
|
|
|
|
|
+ # self.update_report_state(saas_id, service_id)
|
|
|
|
|
|
def busy_hash(self, saas_id, agent_id, phone_num, service_id):
|
|
def busy_hash(self, saas_id, agent_id, phone_num, service_id):
|
|
cache_agent_map = self.get_cache_agent_map(saas_id, service_id)
|
|
cache_agent_map = self.get_cache_agent_map(saas_id, service_id)
|
|
@@ -541,7 +579,7 @@ class AgentStateService:
|
|
state_data.status = HumanState.BUSY.code
|
|
state_data.status = HumanState.BUSY.code
|
|
self.redis_handler.redis.hset(key, phone_num, state_data.to_json_string())
|
|
self.redis_handler.redis.hset(key, phone_num, state_data.to_json_string())
|
|
self.redis_handler.redis.expire(key, self._get_expire_time())
|
|
self.redis_handler.redis.expire(key, self._get_expire_time())
|
|
- self.update_report_state(saas_id, service_id)
|
|
|
|
|
|
+ # self.update_report_state(saas_id, service_id)
|
|
|
|
|
|
def get_cache_agent_map(self, saas_id, service_id):
|
|
def get_cache_agent_map(self, saas_id, service_id):
|
|
cache_agent_list = self.get_cache_agent_list(saas_id, service_id)
|
|
cache_agent_list = self.get_cache_agent_list(saas_id, service_id)
|
|
@@ -552,7 +590,6 @@ class AgentStateService:
|
|
# 使用字典推导式将 cache_agent_list 转换为字典
|
|
# 使用字典推导式将 cache_agent_list 转换为字典
|
|
return {agent.phone_num: agent for agent in cache_agent_list}
|
|
return {agent.phone_num: agent for agent in cache_agent_list}
|
|
|
|
|
|
-
|
|
|
|
def get_cache_agent_list(self, saas_id, service_id):
|
|
def get_cache_agent_list(self, saas_id, service_id):
|
|
redis_key = self._key(saas_id, service_id)
|
|
redis_key = self._key(saas_id, service_id)
|
|
map_cache_by_key = self.redis_handler.redis.hgetall(redis_key)
|
|
map_cache_by_key = self.redis_handler.redis.hgetall(redis_key)
|
|
@@ -624,8 +661,10 @@ class AgentStateService:
|
|
elif human_state == HumanState.BUSY:
|
|
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))
|
|
self.meter_registry.gauge(name, tag_list, self, lambda ctx: ctx.get_agent_service_idle_size(saas_id, service_id))
|
|
return 0
|
|
return 0
|
|
|
|
+
|
|
def _check_in_key(self, saas_id):
|
|
def _check_in_key(self, saas_id):
|
|
return "CTI:%s:HUMAN:AGENT".format(saas_id.upper())
|
|
return "CTI:%s:HUMAN:AGENT".format(saas_id.upper())
|
|
|
|
+
|
|
def _key(self, saas_id, service_id):
|
|
def _key(self, saas_id, service_id):
|
|
return "CTI:%s:HUMAN:%s".format(saas_id.upper(), service_id)
|
|
return "CTI:%s:HUMAN:%s".format(saas_id.upper(), service_id)
|
|
|
|
|
|
@@ -635,6 +674,10 @@ class AgentStateService:
|
|
expire_time = (end_of_day - now).total_seconds() * 1000 # Convert to milliseconds
|
|
expire_time = (end_of_day - now).total_seconds() * 1000 # Convert to milliseconds
|
|
return int(expire_time)
|
|
return int(expire_time)
|
|
|
|
|
|
|
|
+ def _choose_max_idle_time(self, idle_agents: List[AgentStateData]) -> str:
|
|
|
|
+ idle_agents.sort(key=lambda agent: agent.get_assign_time())
|
|
|
|
+ return idle_agents[0].get_phone_num()
|
|
|
|
+
|
|
|
|
|
|
def _get_agent(saas_id, agent_number, out_id):
|
|
def _get_agent(saas_id, agent_number, out_id):
|
|
agent = Agent.query.filter(
|
|
agent = Agent.query.filter(
|
|
@@ -663,6 +706,7 @@ def _get_phone(saas_id, phone_num):
|
|
phone = Phone.query.filter(Phone.saas_id == saas_id, Phone.phone_num == phone_num).first()
|
|
phone = Phone.query.filter(Phone.saas_id == saas_id, Phone.phone_num == phone_num).first()
|
|
return phone
|
|
return phone
|
|
|
|
|
|
|
|
+
|
|
def _get_human_service_service(saas_id, agent_id):
|
|
def _get_human_service_service(saas_id, agent_id):
|
|
human_service_map = HumanServiceMap.query.filter(HumanServiceMap.saas_id == saas_id, HumanServiceMap.agent_id ==agent_id).first()
|
|
human_service_map = HumanServiceMap.query.filter(HumanServiceMap.saas_id == saas_id, HumanServiceMap.agent_id ==agent_id).first()
|
|
- return human_service_map
|
|
|
|
|
|
+ return human_service_map
|