#include "StdAfx.h" #include "Queue.h" #include "IncomingCall.h" #include "AcdCore.h" #include "StrategyLoop.h" #include "StrategyMaxFree.h" #include "StrategyMinAnswer.h" #include "StrategySpecifiedAgent.h" #include "StrategyLoopByAgentId.h" CQueue::CQueue(UINT QueueNo) : m_QueueNo(QueueNo) { __initStrategy(); } CQueue::~CQueue(void) { __freeStrategy(); } /***************************************************************** **【函数名称】 __initStrategy **【函数功能】 初始化座席分配策略 **【参数】 **【返回值】 ****************************************************************/ void CQueue::__initStrategy(void) { CStrategyAD* pStrategy = NULL; // 循环接听 pStrategy = new CStrategyLoop(); m_StrategyMap.SetAt(AGENT_STRATEGY_LOOP, pStrategy); // 2020-01-13 循环接听,按照座席id大小顺序 pStrategy = new CStrategyLoopByAgentId(); m_StrategyMap.SetAt(AGENT_STRATEGY_LOOPBYAGENTID, pStrategy); // 目前空闲时间最长的座席 pStrategy = new CStrategyMaxFree(); m_StrategyMap.SetAt(AGENT_STRATEGY_MAX_CUR_FREE_TIME, pStrategy); // 目前接听次数最少的坐席 pStrategy = new CStrategyMinAnswer(); m_StrategyMap.SetAt(AGENT_STRATEGY_MINI_CALLS, pStrategy); // 指定座席 pStrategy = new CStrategySpecifiedAgent(); m_StrategyMap.SetAt(AGENT_STRATEGY_KNOWN, pStrategy); } /***************************************************************** **【函数名称】 __freeStrategy **【函数功能】 释放座席分配策略 **【参数】 **【返回值】 ****************************************************************/ void CQueue::__freeStrategy(void) { AGENT_STRATEGY Key; CStrategyAD* pStrategy = NULL; POSITION Pos = m_StrategyMap.GetStartPosition(); while (Pos != NULL) { m_StrategyMap.GetNextAssoc(Pos, Key, pStrategy); ASSERT(pStrategy != NULL); delete pStrategy; pStrategy = NULL; } m_StrategyMap.RemoveAll(); } /***************************************************************** **【函数名称】 __getStrategy **【函数功能】 获取指定的座席分配策略 **【参数】 **【返回值】 ****************************************************************/ CStrategyAD* CQueue::__getStrategy(AGENT_STRATEGY StrategyId) { // 找到对应的座席分配策略 CStrategyAD* pStrategy = NULL; if (!m_StrategyMap.Lookup(StrategyId, pStrategy)) pStrategy = m_StrategyMap[AGENT_STRATEGY_LOOP]; // 如果指定的策略不存在,则选取循环接听策略 return pStrategy; } /***************************************************************** **【函数名称】 __onAgentDispatched **【函数功能】 处理请求座席结果 **【参数】 Result: 请求座席结果 Agent: 请求到的座席ID Exten: 请求到的座席分机号 CallIndex: 请求座席的来电索引 **【返回值】 ****************************************************************/ void CQueue::__onAgentDispatched(QUEUE_AGENT_RESULT Result, UINT Agent, UINT Exten, CIncomingCall* pCall) { switch (Result) { case QUEUE_AGENT_OK: // 请求座席成功 { pCall->onQueueOK(Agent, Exten); m_CallGroup.delCall(pCall->index()); } break; case QUEUE_AGENT_FAILED_NO_AGENT: // 无签入座席 case QUEUE_AGENT_FAILED_NO_FREE: // 座席全忙 case QUEUE_AGETN_FAILED_NO_SPEC: // 在指定策略下无匹配座席 { pCall->onQueueFailed(Result, Agent); } break; } // end switch } /***************************************************************** **【函数名称】 clear **【函数功能】 清除所有资源 **【参数】 **【返回值】 ****************************************************************/ void CQueue::clear(void) { m_CallGroup.clear(); m_AgentGroup.clear(); } CString CQueue::callIndex() { int index = 0; CIncomingCall* pIncomingCall = NULL; CString str; /* POSITION pos = m_CallGroup.GetHeadPosition(); while (pos != NULL) { index++; pIncomingCall = m_CallGroup.GetNext(pos); if (pIncomingCall) { str.Format("%d|%d#%s", pIncomingCall->index(), index,str); } } */ std::stringstream ss; POSITION pos = m_CallGroup.GetHeadPosition(); while (pos != NULL) { index++; pIncomingCall = m_CallGroup.GetNext(pos); if (pIncomingCall) { ss << pIncomingCall->index() << "|" << index << "#"; } } str.Format("%s", ss.str().c_str()); ss.str(""); return str; } /***************************************************************** **【函数名称】 onQueueRequest **【函数功能】 呼叫请求排队响应 **【参数】 pCmd: PDU命令内容 **【返回值】 被操作呼叫 ****************************************************************/ void CQueue::onQueueRequest(CPduEntity* pCmd) { int CallPos = 0; // 是否为重复请求座席 CIncomingCall* pCall = m_CallGroup.getCall(pCmd->GetDataInt(1)); if (pCall != NULL) { pCall->updateQueueInfo(pCmd); // 重置座席分配策略及信息 } else { pCall = m_CallGroup.addCall(pCmd, CallPos); ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_NORMAL, "{CQueue}: 呼叫请求排队, m_QueueNo=%d,Index = %d,Caller=%s,CallPos = %d", m_QueueNo, pCmd->GetDataInt(1), pCmd->GetDataString(6), CallPos); } // end if // 当前来电是否可以请求座席(如果不能,直接返回请求座席失败) if (pCall != m_CallGroup.getTopWaitedCall()) { QUEUE_AGENT_RESULT Result = QUEUE_AGENT_FAILED_NO_FREE; if (m_AgentGroup.GetCount() == 0) Result = QUEUE_AGENT_FAILED_NO_AGENT; __onAgentDispatched(Result, CallPos, 0, pCall); } // end if else { // 处理排队请求 distributeAgent(); } } /***************************************************************** **【函数名称】 onQueueCancel **【函数功能】 呼叫取消排队 **【参数】 pCmd: PDU命令内容 **【返回值】 ****************************************************************/ bool CQueue::onQueueCancel(CPduEntity* pCmd) { // 删除呼叫 ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_NORMAL, "{CQueue}: 呼叫取消排队, m_QueueNo=%d,Index = %d,Caller=%s", m_QueueNo, pCmd->GetDataInt(1), pCmd->GetDataString(6)); return m_CallGroup.delCall(pCmd->GetDataInt(1)); } /***************************************************************** **【函数名称】 onQueuePause **【函数功能】 呼叫暂停排队 **【参数】 pCmd: PDU命令内容 **【返回值】 ****************************************************************/ bool CQueue::onQueuePause(CPduEntity* pCmd) { CIncomingCall* pCall = m_CallGroup.getCall(pCmd->GetDataInt(0)); if (pCall == NULL) return false; pCall->setCallState(OUTER_CALL_PAUSE); return true; } /***************************************************************** **【函数名称】 onQueueContinue **【函数功能】 呼叫继续排队 **【参数】 pCmd: PDU命令内容 **【返回值】 ****************************************************************/ bool CQueue::onQueueContinue(CPduEntity* pCmd) { CIncomingCall* pCall = m_CallGroup.getCall(pCmd->GetDataInt(1)); if (pCall == NULL) return false; if (pCall->callId() != pCmd->GetDataULong(2)) { // 2023-05-25 判断获取到的来电callid是否和请求的相同,不相同删除 m_CallGroup.delCall(pCmd->GetDataInt(1)); ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_ERROR, _T("{继续排队故障,根据ivr索引获取到来电callid[%ld]与真实callid不同[%ld]"), pCall->callId(), pCmd->GetDataULong(2)); return false; } pCall->updateQueueInfo(pCmd); // 主动请求坐席 distributeAgent(); return true; } /***************************************************************** **【函数名称】 addAgent **【函数功能】 添加座席 **【参数】 **【返回值】 ****************************************************************/ void CQueue::addAgent(CAgent* pAgent) { m_AgentGroup.add(pAgent); // 2022-09-07 保存签入后坐席所在组的位置 saveCurAllAgent(); } /***************************************************************** **【函数名称】 delAgent **【函数功能】 删除座席 **【参数】 **【返回值】 ****************************************************************/ void CQueue::delAgent(CAgent* pAgent, bool isSave) { m_AgentGroup.remove(pAgent); // 2022-09-07 保存签入后坐席所在组的位置 if(isSave) saveCurAllAgent(); } /***************************************************************** **【函数名称】 distributeAgent **【函数功能】 **【参数】 **【返回值】 ****************************************************************/ void CQueue::distributeAgent(void) { // 查找当前要排队的呼叫 CIncomingCall* pCall = m_CallGroup.getTopWaitedCall(); if (pCall == NULL) return; pCall->setCallState(OUTER_CALL_QUEUE); CStrategyAD* pStrategy = __getStrategy(pCall->agentStrategy()); // 请求座席的排队策略 ASSERT(pStrategy != NULL); // 显示日志 ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_NORMAL, _T("{坐席排队策略号: %lu, m_QueueNo=%d[%d],Index = %d,CallID=%ld,Caller=%s"), pCall->agentStrategy(), m_QueueNo, pCall->groupAsked(), pCall->index(), pCall->callId(), pCall->callerNum()); // 开始请求座席 UINT Agent = 0; // 要返回的座席工号 UINT Exten = 0; // 要返回的座席分机号 QUEUE_AGENT_RESULT Result = m_AgentGroup.distributeAgent(pStrategy, pCall->infoEx(), Agent, Exten); //2022-01-01 if (QUEUE_AGENT_OK != Result) { int index = 0; POSITION pos, nextPos; pos = m_CallGroup.GetHeadPosition(); while (pos) { index++; auto a = m_CallGroup.GetAt(pos); nextPos = pos; m_CallGroup.GetNext(nextPos); if (a->index() == pCall->index()) { break; } pos = nextPos; } Agent = index; } else { saveCurAllAgent(); ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_NORMAL, _T("{坐席排队成功,策略号: %lu, m_QueueNo=%d[%d],Index=%d,CallID=%ld,Caller=%s,Agent[%u|%u]"), pCall->agentStrategy(), m_QueueNo, pCall->groupAsked(), pCall->index(), pCall->callId(), pCall->callerNum(), Agent, Exten); } // 处理请求结果 __onAgentDispatched(Result, Agent, Exten, pCall); } std::string CQueue::saveCurAllAgent() { // 2022-09-07 保存签入后坐席所在组的位置 std::string allAgent = m_AgentGroup.GetCurAllAgent(); std::string no = std::to_string(m_QueueNo); pushOnlineAgentGroupDetail(no, allAgent); return ""; }