|
@@ -1,10 +1,15 @@
|
|
|
#!/usr/bin/env python3
|
|
|
# encoding:utf-8
|
|
|
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, \
|
|
|
+ AgentScene, BizErrorCode, WorkStatus, DownEvent, HumanState
|
|
|
from sqlalchemy import or_
|
|
|
from src.core.callcenter.dao import *
|
|
|
-from src.core.callcenter.model import AgentActionRequest, AgentInfo, AgentQueryRequest, AgentRequest
|
|
|
+from src.core.callcenter.exception import BizException
|
|
|
+from src.core.callcenter.api import AgentActionRequest, AgentInfo, AgentQueryRequest, AgentRequest, AgentEventData, \
|
|
|
+ AgentStateData, HumanServiceQueryRequest, AgentMonitorData
|
|
|
+from src.core.callcenter.push import PushHandler
|
|
|
+from src.core.datasource import RedisHandler
|
|
|
|
|
|
|
|
|
class AgentOperService:
|
|
@@ -12,8 +17,10 @@ class AgentOperService:
|
|
|
def __init__(self, client, logger):
|
|
|
self.inbound_client = client
|
|
|
self.logger = logger
|
|
|
+ self.push_handler = PushHandler(logger)
|
|
|
self.agent_monitor_service = AgentMonitorService(client, logger)
|
|
|
self.agent_actionlog_service = AgentActionLogService(client, logger)
|
|
|
+ self.agent_state_service = AgentStateService(client, logger)
|
|
|
|
|
|
def enable(self, req: AgentActionRequest):
|
|
|
agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
|
|
@@ -26,6 +33,11 @@ class AgentOperService:
|
|
|
agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
|
|
|
if agent.agent_state == AgentState.DISABLE.code:
|
|
|
return
|
|
|
+ agent_monitor = _get_agent_monitor(req.saas_id, req.agent_number)
|
|
|
+ if agent_monitor.check_state == AgentCheck.IN.code and \
|
|
|
+ agent_monitor.service_state == AgentServiceState.CALLING.code:
|
|
|
+ raise BizException(BizErrorCode.AGENT_CALLING_NOT_ALLOW_OPERATE)
|
|
|
+
|
|
|
agent.phone_num = ''
|
|
|
agent.agent_state = AgentState.DISABLE.code
|
|
|
db.session.commit()
|
|
@@ -36,7 +48,8 @@ class AgentOperService:
|
|
|
|
|
|
def checkin(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.ERROR_NOT_FOLLOW_CHECK_IN)
|
|
|
phone = _get_phone(req.saas_id, agent.phone_num)
|
|
|
|
|
|
agent_monitor = _get_agent_monitor(req.saas_id, req.agent_number)
|
|
@@ -44,9 +57,13 @@ class AgentOperService:
|
|
|
agent_monitor.check_scene = req.scene
|
|
|
self.agent_monitor_service.update_checkin(agent_monitor)
|
|
|
self.agent_actionlog_service.insert_check_state(agent_monitor, AgentCheck.IN, AgentLogState.CHECKIN)
|
|
|
+ self.agent_state_service.checkin(agent.saas_id, agent.out_id, agent.phone_num)
|
|
|
|
|
|
+ if req.scene == AgentScene.MANUAL.code:
|
|
|
+ # 如果是手动外呼增加置闲
|
|
|
+ self._handle_idle(req.scene, agent)
|
|
|
|
|
|
- return agent.to_dict()
|
|
|
+ return self._push_event_for_checkin(agent, agent_monitor, phone, req.scene)
|
|
|
|
|
|
def checkout(self, request: AgentActionRequest):
|
|
|
pass
|
|
@@ -66,12 +83,54 @@ class AgentOperService:
|
|
|
def turn_on(self, request: AgentActionRequest):
|
|
|
pass
|
|
|
|
|
|
+ def _handle_idle(self, scene, agent):
|
|
|
+ agent_monitor = _get_agent_monitor(agent.saas_id, agent.agent_num)
|
|
|
+ if agent_monitor.check_state == AgentCheck.OUT.code:
|
|
|
+ raise BizException(BizErrorCode.AGENT_CHECK_OUT_NOT_ALLOW_OPERATE)
|
|
|
+ if agent_monitor.service_state == AgentServiceState.CALLING.code or agent_monitor.service_state == AgentServiceState.DIALING.code:
|
|
|
+ raise BizException(BizErrorCode.AGENT_CALLING_NOT_HANG)
|
|
|
+
|
|
|
+ if scene == AgentScene.ROBOT.code:
|
|
|
+ self.agent_state_service.idle(agent.saas_id, agent.out_id, agent.phone_num)
|
|
|
+ else:
|
|
|
+ self.agent_state_service.busy(agent.saas_id, agent.out_id, agent.phone_num)
|
|
|
+
|
|
|
+ if agent_monitor.service_state == AgentServiceState.IDLE.code:
|
|
|
+ self._push_event_for_idle(agent, scene)
|
|
|
+ return
|
|
|
+
|
|
|
+ self.agent_monitor_service.update_idle(agent_monitor)
|
|
|
+ self.agent_actionlog_service.insert_service_state(agent_monitor, AgentServiceState.IDLE, AgentLogState.IDLE)
|
|
|
+ self._push_event_for_idle(agent, scene)
|
|
|
+
|
|
|
+ def _push_event_for_checkin(self, agent, agent_monitor, phone, 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.LOGIN_SUCCESS)
|
|
|
+
|
|
|
+ event_data = AgentEventData(agent.saas_id, agent.out_id)
|
|
|
+ event_data.data = {'eventName': DownEvent.ON_INITAL_SUCCESS.code,
|
|
|
+ 'ext': {'saasId': agent.saas_id,
|
|
|
+ 'agentId': agent.out_id,
|
|
|
+ 'phoneNum': phone.phone_num,
|
|
|
+ 'phonePwd': phone.phone_pwd,
|
|
|
+ 'sipServer': phone.sip_server
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return event_data
|
|
|
+
|
|
|
+ def _push_event_for_idle(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_READY)
|
|
|
+
|
|
|
|
|
|
class AgentService:
|
|
|
|
|
|
def __init__(self, client, logger):
|
|
|
self.inbound_client = client
|
|
|
self.logger = logger
|
|
|
+ self.agent_monitor_service = AgentMonitorService(client, logger)
|
|
|
|
|
|
def get_and_check(self, req: AgentActionRequest):
|
|
|
agent = _get_agent(req.saas_id, req.agent_number, req.out_id)
|
|
@@ -80,6 +139,13 @@ class AgentService:
|
|
|
phone = _get_phone(req.saas_id, agent.phone_num)
|
|
|
return phone.to_dict()
|
|
|
|
|
|
+ def watch_agent_state(self, req: HumanServiceQueryRequest):
|
|
|
+ pos = HumanServiceMap.query.filter(HumanServiceMap.is_delete == 0, HumanServiceMap.saas_id == req.saasId,
|
|
|
+ HumanServiceMap.service_id == req.serviceId).all()
|
|
|
+ agent_ids = [x.agent_id for x in pos]
|
|
|
+ monitors = self.agent_monitor_service.detail_monitor_out_ids(req.saasId, agent_ids)
|
|
|
+ return monitors
|
|
|
+
|
|
|
def add(self, req: AgentRequest):
|
|
|
new_agent_num = self._get_newest_agent_number(req.saas_id)
|
|
|
agent = Agent(saas_id=req.saas_id, agent_num=new_agent_num, agent_name=req.agent_name, out_id=req.out_id,
|
|
@@ -174,6 +240,47 @@ class AgentMonitorService:
|
|
|
self.inbound_client = client
|
|
|
self.logger = logger
|
|
|
|
|
|
+ def detail_monitor_out_ids(self, saas_id, out_ids, check_scene=None):
|
|
|
+ if not out_ids:
|
|
|
+ return []
|
|
|
+ agents = Agent.query.filter(Agent.is_delete == 0, Agent.saas_id == saas_id, Agent.out_id.in_(out_ids)).all()
|
|
|
+ if not agents:
|
|
|
+ raise BizException(BizErrorCode.RECORD_NOT_EXIST_ERROR)
|
|
|
+
|
|
|
+ agent_num_map = {x.agent_num: x for x in agents}
|
|
|
+ agent_nums = [x.agent_num for x in agents]
|
|
|
+ agent_monitors = AgentMonitor.query.filter(AgentMonitor.is_delete == 0, AgentMonitor.agent_num.in_(agent_nums)).all()
|
|
|
+ res = []
|
|
|
+ for agent_monitor in agent_monitors:
|
|
|
+ agent = agent_num_map.get(agent_monitor.agent_num)
|
|
|
+ data = AgentMonitorData()
|
|
|
+ data.saas_id = saas_id
|
|
|
+ data.agent_num = agent_monitor.agent_num
|
|
|
+ data.agent_name = agent.agent_name
|
|
|
+ data.out_id = agent_monitor.out_id
|
|
|
+ data.check_state = agent_monitor.check_state
|
|
|
+ data.check_scene = agent_monitor.check_scene
|
|
|
+ data.online_state = 1 if agent_monitor.check_state == AgentCheck.IN.code else 0
|
|
|
+
|
|
|
+ if agent_monitor.check_in_time:
|
|
|
+ data.check_in_time = agent_monitor.check_in_time.timestamp()
|
|
|
+ day_start = self._get_day_start()
|
|
|
+ if agent_monitor.check_in_time.timestamp() > day_start and \
|
|
|
+ agent_monitor.check_state == AgentCheck.OUT.code:
|
|
|
+ data.online_state = 2
|
|
|
+
|
|
|
+ data.check_out_time = agent_monitor.check_out_time.timestamp() if agent_monitor.check_out_time else None
|
|
|
+ data.service_state = agent_monitor.service_state
|
|
|
+ data.busy_time = agent_monitor.busy_time.timestamp() if agent_monitor.busy_time else None
|
|
|
+ data.idle_time = agent_monitor.idle_time.timestamp() if agent_monitor.idle_time else None
|
|
|
+ data.call_time = agent_monitor.call_time.timestamp() if agent_monitor.call_time else None
|
|
|
+ data.hang_time = agent_monitor.hang_time.timestamp() if agent_monitor.hang_time else None
|
|
|
+ data.heart_state = agent_monitor.heart_state
|
|
|
+ data.heart_time = agent_monitor.heart_time.timestamp() if agent_monitor.heart_time else None
|
|
|
+ data.update_time = agent_monitor.update_time.timestamp() if agent_monitor.update_time else None
|
|
|
+ res.append(data)
|
|
|
+ return res
|
|
|
+
|
|
|
def update_check_in(self, agent_monitor):
|
|
|
agent_monitor.check_state = AgentCheck.IN.code
|
|
|
agent_monitor.check_in_time = datetime.utcnow
|
|
@@ -222,6 +329,10 @@ class AgentMonitorService:
|
|
|
agent_monitor.heart_time = datetime.utcnow
|
|
|
db.session.commit()
|
|
|
|
|
|
+ def _get_day_start(self):
|
|
|
+ today_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
|
+ return int(today_start.timestamp() * 1000) # Return milliseconds
|
|
|
+
|
|
|
|
|
|
class AgentActionLogService:
|
|
|
|
|
@@ -233,11 +344,122 @@ class AgentActionLogService:
|
|
|
action_log = AgentActionLog()
|
|
|
action_log.saas_id = agent_monitor.saas_id
|
|
|
action_log.agent_num = agent_monitor.agent_num
|
|
|
+ action_log.out_id = agent_monitor.out_id
|
|
|
+ action_log.action_type = 0
|
|
|
+ action_log.check_state = agent_check_enum.code
|
|
|
+ action_log.pre_check_state = agent_monitor.check_state
|
|
|
+ if agent_log_enum:
|
|
|
+ action_log.event_type = agent_log_enum.code
|
|
|
+ action_log.event_desc = agent_log_enum.description
|
|
|
+ now = datetime.utcnow
|
|
|
+ pre_date = None
|
|
|
+ if agent_monitor.check_state == AgentCheck.IN.code:
|
|
|
+ pre_date = agent_monitor.check_in_time
|
|
|
+ if agent_monitor.check_state == AgentCheck.OUT.code:
|
|
|
+ pre_date = agent_monitor.check_out_time
|
|
|
+ if pre_date is None:
|
|
|
+ pre_date = agent_monitor.update_time
|
|
|
+ action_log.check_state_time = now
|
|
|
+ action_log.pre_check_state_time = pre_date
|
|
|
+ action_log.check_state_duration = now.timestamp() - pre_date.timestamp()
|
|
|
+ db.session.add(action_log)
|
|
|
+ db.session.commit()
|
|
|
+
|
|
|
+ def insert_service_state(self, agent_monitor, agent_service_state: AgentServiceState, agent_log_enum: AgentLogState,
|
|
|
+ task_id=None, service_id=None):
|
|
|
+ if agent_monitor.service_state == agent_service_state.code:
|
|
|
+ self.logger.info("agent action log insert service state same %s %s", agent_monitor.service_state, agent_service_state.getValue())
|
|
|
+
|
|
|
+ action_log = AgentActionLog()
|
|
|
+ action_log.saas_id = agent_monitor.saas_id
|
|
|
+ action_log.agent_num = agent_monitor.agent_num
|
|
|
+ action_log.out_id = agent_monitor.out_id
|
|
|
+ action_log.action_type = 1
|
|
|
+ action_log.service_state = agent_service_state.code
|
|
|
+ action_log.pre_service_state = agent_monitor.service_state
|
|
|
+ action_log.task_id = task_id
|
|
|
+ action_log.service_id = service_id
|
|
|
+ if agent_log_enum:
|
|
|
+ action_log.event_type = agent_log_enum.code
|
|
|
+ action_log.event_desc = agent_log_enum.description
|
|
|
+ now = datetime.utcnow
|
|
|
+ pre_date = None
|
|
|
+ if agent_monitor.service_state == AgentServiceState.IDLE.code:
|
|
|
+ pre_date = agent_monitor.idle_time
|
|
|
+ if agent_monitor.service_state == AgentServiceState.BUSY.code:
|
|
|
+ pre_date = agent_monitor.busy_time
|
|
|
+ if agent_monitor.service_state == AgentServiceState.CALLING.code:
|
|
|
+ pre_date = agent_monitor.call_time
|
|
|
+ if agent_monitor.service_state == AgentServiceState.LOGOUT.code:
|
|
|
+ pre_date = agent_monitor.check_out_time
|
|
|
+ if agent_monitor.service_state == AgentServiceState.REPROCESSING.code:
|
|
|
+ pre_date = agent_monitor.hang_time
|
|
|
+ action_log.service_state_time = now
|
|
|
+ if pre_date is None:
|
|
|
+ pre_date = agent_monitor.update_time
|
|
|
+ action_log.pre_service_state_time = pre_date
|
|
|
+ action_log.service_state_duration = now.timestamp() - pre_date.timestamp()
|
|
|
+ db.session.add(action_log)
|
|
|
+ db.session.commit()
|
|
|
|
|
|
|
|
|
- def insert_service_state(self, agent_monitor, agent_check_enum: AgentServiceState, agent_log_enum: AgentLogState):
|
|
|
+class AgentStateService:
|
|
|
+
|
|
|
+ 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
|
|
|
+
|
|
|
+ def busy(self, saas_id, agent_id, phone_num):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def idle_by_human(self, saas_id, agent_id, service_id):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def busy_by_human(self, saas_id, service_id, agent_id=None):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def checkin(self, saas_id, agent_id, phone_num):
|
|
|
+ key = self._check_in_key(saas_id)
|
|
|
+ state_data = AgentStateData()
|
|
|
+ state_data.status = HumanState.DEFAULT.code
|
|
|
+ state_data.time = datetime.utcnow.timestamp()
|
|
|
+ self.redis_handler.redis.hset(key, phone_num, json.dumps(state_data))
|
|
|
+ self.redis_handler.redis.expire(key, self._get_expire_time())
|
|
|
+
|
|
|
+ def checkout(self, saas_id, agent_id, phone_num):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def assign_agent(self, saas_id, service_id, ivr_id, task_id, called, cbp):
|
|
|
pass
|
|
|
|
|
|
+ def idle_agents(self, saas_id, service_id):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def idle_hash(self, saas_id, agent_id, phone_num, service_id):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def busy_hash(self, saas_id, agent_id, phone_num, service_id):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def get_agent_service_idle_size(self, saas_id, service_id):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def get_agent_service_busy_size(self, saas_id, service_id):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def _check_in_key(self, saas_id):
|
|
|
+ return "CTI:%s:HUMAN:AGENT".format(saas_id.upper())
|
|
|
+
|
|
|
+ def _get_expire_time(self):
|
|
|
+ now = datetime.now()
|
|
|
+ end_of_day = now.replace(hour=23, minute=59, second=59, microsecond=0)
|
|
|
+ expire_time = (end_of_day - now).total_seconds() * 1000 # Convert to milliseconds
|
|
|
+ return int(expire_time)
|
|
|
+
|
|
|
|
|
|
def _get_agent(saas_id, agent_number, out_id):
|
|
|
agent = Agent.query.filter(
|