#include "StdAfx.h" #include "AcdCore.h" #include "Config.h" #include "Agent.h" #include "Exten.h" #include "NetworkAcd.h" #include "MsgCenter.h" #include "../Public/Auth/AuthMgr.h" #include "../Public/DaemonClient/DaemonClient.h" #include "SoftAuther.h" SINGLETON_IMPLEMENT(CAcdCore) CAcdCore::CAcdCore(void) : m_SubjectRepository(&m_AgentOffice, &m_ExtenCtrl, &m_QueueMgr) { //AllocConsole();//为当前的窗口程序申请一个Console窗口 //freopen("CONOUT$", "w+", stdout); } CAcdCore::~CAcdCore(void) { } /***************************************************************** **【函数名称】 __onAgentStateUpdated **【函数功能】 座席状态变化事件响应 **【参数】 AgentId: 座席工号 ExtId: 分机号 Status: 座席当前状态 **【返回值】 ****************************************************************/ void CAcdCore::__onAgentStateUpdated( UINT AgentId ) { CAgent* pAgent = m_AgentOffice.getAgentById(AgentId); if(pAgent == NULL) return; AGENT_STATE State = pAgent->state(); // 通知事件订阅 m_SubjectRepository.onAgentStateUpdated(pAgent->id(), pAgent->assoExten(), State); // 如果有座席变为空闲,通知来电队列刷新排队 if(State == AGENT_STATE_FREE) m_QueueMgr.onAgentFree(pAgent); } /***************************************************************** **【函数名称】 __onPhoneStateUpdated **【函数功能】 分机状态变化事件响应 **【参数】 AgentId: 座席工号 ExtId: 分机号 Status: 座席当前状态 **【返回值】 ****************************************************************/ void CAcdCore::__onPhoneStateUpdated( UINT ExtenId ) { CExten* pExten = m_ExtenCtrl.getExten(ExtenId); if(pExten == NULL) return; // 通知事件订阅 m_SubjectRepository.onPhoneStateUpdated(pExten->assoAgent(), pExten->id(), pExten->state()); } /***************************************************************** **【函数名称】 __onQueueStateUpdated **【函数功能】 排队状态变化事件响应 **【参数】 Count: 等待排队的呼叫数量 **【返回值】 ****************************************************************/ void CAcdCore::__onQueueStateUpdated( UINT QueueNo ) { UINT Count = m_QueueMgr.callCount(QueueNo); // 通知事件订阅 m_SubjectRepository.onQueueStateUpdated(QueueNo, Count); } /***************************************************************** **【函数名称】 __removeAgent **【函数功能】 关闭指定座席 **【参数】 AgentId: 要关闭座席的工号 ExtId: 要关闭座席的分机号 HostAgent: 主控制坐席工号 **【返回值】 ****************************************************************/ void CAcdCore::__removeAgent( UINT AgentId, UINT ExtId, UINT HostAgent ) { if(!m_AgentOffice.isAgentExisted(AgentId)) return; // 清空对应的座席信息 m_ExtenCtrl.setAssoAgent(ExtId, 0); // 清除关联分机 m_SubjectRepository.clearObserver(AgentId); // 清除订阅信息 m_AgentOffice.removeAgent(AgentId, HostAgent); // 清除对应座席 } /***************************************************************** **【函数名称】 __onCmdAgentReg **【函数功能】 PDU注册命令 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdAgentReg( CPduEntity* a_pCmd ) { if(a_pCmd->GetLocalDevType() == PDU_DEV_TYPE_AGENT) // 仅处理座席注册 { // 获取发送方的IP CString PeerIp = _T(""); CNetworkAcd::GetInstance().getPduSource(a_pCmd, PeerIp); // 是否为相同主机的座席重复签入 UINT AgentId = a_pCmd->GetLocalDevId(); if(m_AgentOffice.isAgentExisted(AgentId, PeerIp)) { //关闭原座席 ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_WARNING, _T("{Core}: 座席[%ld]重复登录, IP Address = %s"), AgentId, PeerIp); __removeAgent(AgentId, m_AgentOffice.getAgentAssoExten(AgentId), 0); } // end if // 查找当前座席是否已注册 if(m_AgentOffice.isAgentExisted(AgentId)) a_pCmd->SetDataBool(0, false); else a_pCmd->SetDataBool(0, true); // 返回执行结果 a_pCmd->SetToExecReturn(); CNetworkAcd::GetInstance().send2Agent(AgentId, a_pCmd); } // end if else if (a_pCmd->GetLocalDevType()==PDU_DEV_TYPE_SERVER) { ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_NORMAL, _T("-------------------Server已连接---------------------------")); a_pCmd->SetToExecReturn(); a_pCmd->SetDataBool(0, true); CInterfaceWindow::getIocpCommInstance()->Send(a_pCmd, PDU_DEV_TYPE_SERVER, 0); } } /***************************************************************** **【函数名称】 __onCmdAgentDial **【函数功能】 处理呼叫相关的命令 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdAgentDial( CPduEntity* a_pCmd ) { if(a_pCmd->GetIsExecReturn()) // CTI -> Agent 的返回命令 { UINT DestDevId = a_pCmd->GetDataUInt(2); //CNetworkAcd::GetInstance().send2Agent(DestDevId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); } else // Agent -> CTI 的请求命令 { CString DestNum = _T(""); CString CmdHint = _T(""); UINT AgentId = a_pCmd->GetDataUInt(2); switch(a_pCmd->GetCmdType()) { case PDU_CMD_AGENT_TRANSTALK: // 座席转移 { CmdHint = _T("座席转移"); DestNum = a_pCmd->GetDataString(3); } break; case PDU_CMD_AGENT_THREETALK: // 三方会议 { CmdHint = _T("座席三方"); DestNum = a_pCmd->GetDataString(3); } break; case PDU_CMD_AGENT_CONSULTATION_CALL: // 协商呼叫 { CmdHint = _T("座席协商"); DestNum = a_pCmd->GetDataString(4); } break; case PDU_CMD_AGENT_MAKECALL: // 坐席外呼 { CmdHint = _T("座席呼叫"); DestNum = a_pCmd->GetDataString(4); // 如果主叫座席分机非空闲或摘机拨号,禁止呼出 INNER_STATE ExtStatus = m_ExtenCtrl.getExtenState(a_pCmd->GetDataUInt(1)); if (ExtStatus != INNER_STATE_FREE || ExtStatus == INNER_STATE_INIT) { a_pCmd->SetToExecReturn(); a_pCmd->SetDataBool(0, false); //CNetworkAcd::GetInstance().send2Agent(AgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_WARNING, _T("{Core}: 座席[%ld]请求呼叫失败, 关联分机非空闲"), AgentId); return; // 不再向CTI转发 } // end if if(!m_AgentOffice.setAgentState4Calling(AgentId)) { a_pCmd->SetToExecReturn(); a_pCmd->SetDataBool(0, false); //CNetworkAcd::GetInstance().send2Agent(AgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_WARNING, _T("{Core}: 座席[%u]请求呼叫失败, 座席状态异常或已被锁定"), AgentId); return; // 不再向CTI转发 } } break; } // end switch if(!CConfig::isMatchCallPrefix(DestNum)) // 呼叫分机 { if (false == m_ExtenCtrl.isExtenExisted(atoi(DestNum))) // 分机号不存在 { a_pCmd->SetToExecReturn(); a_pCmd->SetDataBool(0, false); // CNetworkAcd::GetInstance().send2Agent(AgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_WARNING, _T("{Core}: 请求呼叫分机 %s 失败,分机号不存在"), DestNum); return; // 不再向CTI转发 } // 查找要呼叫的分机座席工号 UINT DestAgentId = m_ExtenCtrl.getAssoAgent(atoi(DestNum)); if(m_AgentOffice.isAgentExisted(DestAgentId) && !m_AgentOffice.lockAgent(DestAgentId)) // 被叫叫座席非空闲 { a_pCmd->SetToExecReturn(); a_pCmd->SetDataBool(0, false); // CNetworkAcd::GetInstance().send2Agent(AgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_WARNING, _T("{Core}: 座席[%u]请求呼叫失败, 目标座席[%u]非空闲,[%d]"), AgentId, DestAgentId, m_AgentOffice.getAgentState(DestAgentId)); return; // 不再向CTI转发 } } // end if ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: AGENT -> ACD, CMD = [%s], Agent = %lu, Exten = %lu, Dest Number = %s"), CmdHint, AgentId, a_pCmd->GetDataUInt(1), DestNum); CNetworkAcd::GetInstance().send2Cti(a_pCmd); } // end if } /***************************************************************** **【函数名称】 __onCmdAgentLogin **【函数功能】 座席签入命令 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdAgentLogin( CPduEntity* a_pCmd ) { // 得到座席签入信息 UINT ExtId = a_pCmd->GetDataUInt(1); UINT AgentId = a_pCmd->GetDataUInt(2); CString Group = a_pCmd->GetDataString(3); UINT AgentType = a_pCmd->GetDataUInt(4); UINT TimePostProcessing = a_pCmd->GetDataUInt(5); // 显示日志 ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: AGENT -> ACD, CMD = [座席签入], Agent = %lu, Exten = %lu, Group = %s, Type = %lu"), AgentId, ExtId, Group, AgentType); // 校验 bool IsSucceed = false; if(!CNetworkAcd::GetInstance().isCtiConnected()) // CTI是否已连接 { a_pCmd->SetDataBool(0, false); a_pCmd->SetDataUInt(6, LOGIN_ERR_NO_CTI); } //#ifndef _DEBUG else if(m_AgentOffice.getAgentCount() >= m_Auther.GetAgentCount()) //ACD 授权数量验证 { a_pCmd->SetDataBool(0, false); a_pCmd->SetDataUInt(6, LOGIN_ERR_AUTH_FAILED); } //#endif else if(!m_ExtenCtrl.isExtenExisted(ExtId)) // 查找要签入的分机是否存在 { a_pCmd->SetDataBool(0, false); a_pCmd->SetDataUInt(6, LOGIN_ERR_NO_EXT); } else if(m_ExtenCtrl.isExtenUsed(ExtId)) // 分机是否已被占用 { //a_pCmd->SetDataBool(0, false); //a_pCmd->SetDataUInt(6, LOGIN_ERR_BIND_EXT); UINT AgentID = m_ExtenCtrl.getAssoAgent(ExtId); __removeAgent(AgentID, ExtId, AgentID); a_pCmd->SetDataBool(0, true); IsSucceed = true; } else // 可以签入 { a_pCmd->SetDataBool(0, true); IsSucceed = true; } // end if // 如果签入执行成功 if(IsSucceed) { // 生成座席实例 INNER_STATE ExtStatus = m_ExtenCtrl.getExtenState(ExtId); if(!m_AgentOffice.insertAgent(AgentId, ExtId, Group, AgentType, TimePostProcessing, ExtStatus)) { a_pCmd->SetDataBool(0, false); a_pCmd->SetDataUInt(6, LOGIN_ERR_BIND_AGENT); } else { // 通知CTI座席已签入 CNetworkAcd::GetInstance().send2Cti(a_pCmd); CPduEntity pdu(PDU_CMD_AGENT_RESET); pdu.SetDataUInt(1,a_pCmd->GetDataUInt(1)); pdu.SetDataUInt(2,a_pCmd->GetDataUInt(2)); CNetworkAcd::GetInstance().send2Cti(&pdu); // 建立分机与座席的关联 m_ExtenCtrl.setAssoAgent(ExtId, AgentId); } } // end if // 返回签入执行结果 a_pCmd->SetToExecReturn(); //CNetworkAcd::GetInstance().send2Agent(AgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); } /***************************************************************** **【函数名称】 __onCmdAgentLogout **【函数功能】 座席签出命令 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdAgentLogout( CPduEntity* a_pCmd ) { // 得到座席信息 UINT ExtId = a_pCmd->GetDataUInt(1); UINT AgentId = a_pCmd->GetDataUInt(2); // 显示日志 ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: AGENT -> ACD, CMD = [座席签出], Agent = %lu, Exten = %lu"), AgentId, ExtId); // ych 2018.5.16 // m_AgentOffice.setAgentState(AgentId, AGENT_STATE_LOGOUT); if (m_AgentOffice.setAgentState(AgentId, AGENT_STATE_LOGOUT)) { a_pCmd->SetDataBool(0, true); } else { a_pCmd->SetDataBool(0, false); //tj 通知Server CNetworkAcd::GetInstance().send2Server(a_pCmd); return; } // 通知CTI座席签出 CNetworkAcd::GetInstance().send2Cti(a_pCmd); //tj 通知Server CNetworkAcd::GetInstance().send2Server(a_pCmd); // 清空对应的座席信息 Sleep(100); __removeAgent(AgentId, ExtId, AgentId); } /***************************************************************** **【函数名称】 __onCmdAgentSetState **【函数功能】 主动设置座席状态 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdAgentSetState( CPduEntity* a_pCmd ) { // 得到座席信息 UINT AgentId = a_pCmd->GetDataUInt(2); AGENT_STATE State = static_cast(a_pCmd->GetDataUInt(3)); // 显示日志 ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: AGENT -> ACD, CMD = [座席状态设置], Agent = %lu, State = %d"), AgentId, State); // 设置座席逻辑状态 if(m_AgentOffice.setAgentState(AgentId, State)) a_pCmd->SetDataBool(0, true); else a_pCmd->SetDataBool(0, false); // 返回执行结果 a_pCmd->SetToExecReturn(); //CNetworkAcd::GetInstance().send2Agent(a_pCmd->GetLocalDevId(), a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); } /***************************************************************** **【函数名称】 __onCmdAgentSubscribe **【函数功能】 订阅管理 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdAgentSubscribe( CPduEntity* a_pCmd ) { UINT AgentId = a_pCmd->GetDataUInt(2); SUBJECT_TYPE Type = static_cast(a_pCmd->GetDataUInt(4)); int PduType; // 订阅管理 switch(a_pCmd->GetCmdType()) { case PDU_CMD_AGENT_SUBSCRIBE: // 事件订阅 PduType = 1; m_SubjectRepository.registObserver(AgentId, Type, a_pCmd->GetDataInt(3)); break; case PDU_CMD_AGENT_CANCEL_SUBSCRIBE:// 取消订阅 PduType = 2; m_SubjectRepository.removeObserver(AgentId, Type, a_pCmd->GetDataInt(3)); break; } // end switch // 显示日志 ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: AGENT -> ACD, CMD = [座席订阅], Agent = %lu, mode(1-订阅 2-取消) = %d, Type = %d"), AgentId, PduType, Type); } /***************************************************************** **【函数名称】 __onCmdAgentForceLogout **【函数功能】 强制签出指定座席 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdAgentForceLogout( CPduEntity* a_pCmd ) { // 得到主控座席和受控座席信息 UINT HostAgentId = a_pCmd->GetDataUInt(2); UINT TargetAgentId = a_pCmd->GetDataUInt(4); // 显示日志 ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: AGENT -> ACD, CMD = [座席强制签出], Agent = %lu, TargetAgent = %lu"), HostAgentId, TargetAgentId); // 座席是否已签入 if(!m_AgentOffice.isAgentExisted(TargetAgentId)) { a_pCmd->SetDataBool(0, false); // 坐席不存在的情况下对坐席操作的错误日志 ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_WARNING, _T("{Core}: 强制坐席[%d]签出失败, 坐席[%d]未签入"), TargetAgentId, TargetAgentId); } else { // 通知被签出座席 //CNetworkAcd::GetInstance().send2Agent(TargetAgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); UINT TargetExtId = m_AgentOffice.getAgentAssoExten(TargetAgentId); // 发送重置命令到CTI CPduEntity cmd(PDU_CMD_AGENT_RESET); cmd.SetDataUInt(1, TargetExtId); cmd.SetDataUInt(2, TargetAgentId); CNetworkAcd::GetInstance().send2Cti(&cmd); a_pCmd->SetDataBool(0, true); // 关闭座席 __removeAgent(TargetAgentId, TargetExtId, HostAgentId); } // end if // 返回执行结果 a_pCmd->SetToExecReturn(); CNetworkAcd::GetInstance().send2Agent(HostAgentId, a_pCmd); } /***************************************************************** **【函数名称】 __onCmdAgentMonControl **【函数功能】 班长电话控制 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdAgentMonControl( CPduEntity* a_pCmd ) { if(!a_pCmd->GetIsExecReturn()) // OCX发出的命令 { // 得到主控座席和受控座席信息 UINT HostAgentId = a_pCmd->GetDataUInt(2); UINT TargetAgentId = a_pCmd->GetDataUInt(4); // 显示日志 ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: AGENT -> ACD, CMD = [%d](班长控制), Agent = %lu, TargetAgent = %lu"), a_pCmd->GetCmdType(), HostAgentId, TargetAgentId); // 确定目标座席已签入 if(!m_AgentOffice.isAgentExisted(TargetAgentId)) { // 添加坐席不存在的情况下对坐席操作的错误日志 ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_WARNING, _T("{Core}: 班长[%d]对坐席[%d]执行[%d]控制失败, 目标坐席未签入"), HostAgentId, TargetAgentId, a_pCmd->GetCmdType()); // 返回执行结果 a_pCmd->SetDataBool(0, false); a_pCmd->SetToExecReturn(); //CNetworkAcd::GetInstance().send2Agent(HostAgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); return; } // 确保班长座席状态空闲 if (!m_AgentOffice.setAgentState(HostAgentId, AGENT_STATE_FREE)) { // 添加坐席不存在的情况下对坐席操作的错误日志 ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_WARNING, _T("{Core}: 班长[%d]对坐席[%d]执行[%d]控制失败, 班长状态非空闲"), HostAgentId, TargetAgentId, a_pCmd->GetCmdType()); a_pCmd->SetDataBool(0, false); a_pCmd->SetToExecReturn(); // CNetworkAcd::GetInstance().send2Agent(HostAgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); return; } // 转发命令到CTI a_pCmd->SetDataUInt(3, m_AgentOffice.getAgentAssoExten(TargetAgentId)); CNetworkAcd::GetInstance().send2Cti(a_pCmd); } else // CTI的执行结果返回 { //CNetworkAcd::GetInstance().send2Agent(a_pCmd->GetDataUInt(2), a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); } // end if } /***************************************************************** **【函数名称】 __onCmdAgentForceState **【函数功能】 强制座席状态控制 **【参数】 a_pCmd PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdAgentForceState( CPduEntity* a_pCmd ) { // 得到主控座席和受控座席信息 UINT HostAgentId = a_pCmd->GetDataUInt(2); UINT TargetAgentId = a_pCmd->GetDataUInt(4); AGENT_STATE State = static_cast(a_pCmd->GetDataUInt(5)); // 显示日志 ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: AGENT -> ACD, CMD = [座席强制状态转换], Agent = %lu, TargetAgent = %lu, State = %d"), HostAgentId, TargetAgentId, State); // 座席是否已签入 if(!m_AgentOffice.isAgentExisted(TargetAgentId)) { a_pCmd->SetDataBool(0, false); // 添加坐席不存在的情况下对坐席操作的错误日志 ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_WARNING, _T("{Core}: 对坐席[%d]执行强制状态转换失败, 目标坐席没有签入"), TargetAgentId); } else { // 改变受控座席状态 if(m_AgentOffice.setAgentState(TargetAgentId, State)) a_pCmd->SetDataBool(0, true); else a_pCmd->SetDataBool(0, false); } // end if // 返回执行结果 a_pCmd->SetToExecReturn(); //CNetworkAcd::GetInstance().send2Agent(HostAgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); } /***************************************************************** **【函数名称】 __onCmdCtiExtenInfo **【函数功能】 内线分机状态变化命令 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdCtiExtenInfo( CPduEntity* a_pCmd ) { UINT ExtID = a_pCmd->GetDataUInt(0); UINT State = a_pCmd->GetDataUInt(1); UINT Agentid = m_ExtenCtrl.getAssoAgent(ExtID); // 坐席工号 if(State == INNER_STATE_REMOVE) { if (ExtID != 0) { if (Agentid != 0) __removeAgent(Agentid, ExtID, 0); // 迁出坐席 //删除分机 m_ExtenCtrl.removeExten(ExtID); } } else { // 显示日志 ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: CTI -> ACD, CMD = [线路状态信息], Exten = %lu, State = %lu"), ExtID, State); // 通知分机管理类 m_ExtenCtrl.onExtenStateUpdated(a_pCmd); if (Agentid != 0) { m_AgentOffice.onExtenStateUpdated(Agentid, a_pCmd); // 通知座席管理类。报坐席和分机状态 } } } /***************************************************************** **【函数名称】 __onCmdCtiRecord **【函数功能】 CTI通知座席录音信息 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdCtiRecord( CPduEntity* a_pCmd ) { UINT Exten = a_pCmd->GetDataUInt(0); // 显示日志 ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: CTI -> ACD, CMD = [录音信息], Exten = %lu, CallId = %lu, FileName = %s"), Exten, a_pCmd->GetDataULong(1), a_pCmd->GetDataString(2)); // 通过分机号查找对应的座席工号 UINT AgentId = m_ExtenCtrl.getAssoAgent(Exten); if(AgentId != 0) // 有关联座席 { //CNetworkAcd::GetInstance().send2Agent(AgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); } else // 录音未通知日志 { ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_WARNING, _T("{Core}: 录音信息[%s]未通知到坐席, 座席未签入"), a_pCmd->GetDataString(2)); } // end if } /***************************************************************** **【函数名称】 __onCmdCtiDevCall **【函数功能】 CTI通知座席物理外呼信息 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdCtiDevCall( CPduEntity* a_pCmd ) { UINT AgentId = a_pCmd->GetDataUInt(2); // 显示日志 ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: CTI -> ACD, CMD = [外呼信息], Exten = %lu, Agent = %lu, CallId = %lu, Number = %s"), a_pCmd->GetDataUInt(1), AgentId, a_pCmd->GetDataULong(3), a_pCmd->GetDataString(4)); //CNetworkAcd::GetInstance().send2Agent(AgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); } /***************************************************************** **【函数名称】 __onCmdCtiTrunkUsage **【函数功能】 CTI通知呼叫任务中继利用率 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdCtiTrunkUsage( CPduEntity* a_pCmd ) { UINT TaskId = a_pCmd->GetDataUInt(0); UINT Usage = a_pCmd->GetDataUInt(1); m_SubjectRepository.onTrunkUsageUpdated(TaskId, Usage); } /***************************************************************** **【函数名称】 __onCmdIvrQueue **【函数功能】 排队管理操作 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdIvrQueue( CPduEntity* a_pCmd ) { PDU_CMD_TYPE CmdType = a_pCmd->GetCmdType(); ILogger::getInstance().log(LOG_CLASS_SOCKET, LOG_LEVEL_NORMAL, _T("{Core}: CTI -> ACD, CMD = [%d](来电排队)"), CmdType); switch(CmdType) { case PDU_CMD_IVR_WANT_AGENT: // IVR请求座席 { m_QueueMgr.onQueueAsking(a_pCmd); } break; case PDU_CMD_IVR_QUEUE_CONTINUE: // IVR通知CTI继续排队 { m_QueueMgr.onQueueContinue(a_pCmd); } break; case PDU_CMD_IVR_QUEUE_CANCEL: // IVR通知CTI取消排队 { m_QueueMgr.onQueueCancel(a_pCmd); } break; case PDU_CMD_CTI_ACD_QUEUE_PAUSE: // CTI通知ACD暂停排队 { m_QueueMgr.onQueuePause(a_pCmd); } break; } // end switch } /***************************************************************** **【函数名称】 __onCmdNeed2Forward **【函数功能】 处理Agent与CTI相与转发的命令 **【参数】 a_pCmd: PDU命令内容 **【返回值】 ****************************************************************/ void CAcdCore::__onCmdNeed2Forward( CPduEntity* a_pCmd ) { if(a_pCmd->GetIsExecReturn()) // CTI -> Agent 的返回命令 { UINT AgentId = a_pCmd->GetDataUInt(2); //CNetworkAcd::GetInstance().send2Agent(AgentId, a_pCmd); CNetworkAcd::GetInstance().send2Server(a_pCmd); } else // Agent -> CTI 的请求命令 { CNetworkAcd::GetInstance().send2Cti(a_pCmd); } // end if } void CAcdCore::__onCmdDataSendToAgent(CPduEntity * a_pCmd) { int count = a_pCmd->GetDataUInt(0); if (a_pCmd->GetCmdType() == PDU_CMD_CTI_TRUNKUSE_COUNT) { ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_NORMAL, _T("{Core}:收到中继利用数量: %d"),count); } else if (a_pCmd->GetCmdType() == PDU_CMD_IVR_WAITER_COUNT) { ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_NORMAL, _T("{Core}:收到等待数量: %d"),count); } std::vector VecAgentID = m_AgentOffice.getAgentIDAll(); for (auto iter = VecAgentID.begin(); iter != VecAgentID.end(); iter++) { bool isSuccess = CNetworkAcd::GetInstance().send2Server(a_pCmd); if (isSuccess) ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_NORMAL, _T("{Core}: 通知坐席%d 成功,当前数量:%d"), *iter, count); else ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_NORMAL, _T("{Core}: 通知坐席%d 失败,当前数量:%d"), *iter, count); } } void CAcdCore::__onCmdGetAgentList(CPduEntity * a_pCmd) { CString str; m_AgentOffice.getAllAgentStateList(str); a_pCmd->SetDataString(2, str); CNetworkAcd::GetInstance().send2Server(a_pCmd); } /***************************************************************** **【函数名称】 stage1Start **【函数功能】 第一阶段启动 **【参数】 **【返回值】 ****************************************************************/ bool CAcdCore::stage1Start( void ) { // 连接配置数据库 if(!IOtlConnection::getInstance()->Connect()) { AfxMessageBox(STR_ERR_CORE_INIT_DB); return false; } // 加载配置 if(!CConfig::load()) { IOtlConnection::getInstance()->Disconnect(); AfxMessageBox(STR_ERR_CORE_INIT_CFG); return false; } return true; } /***************************************************************** **【函数名称】 stage2Start **【函数功能】 第二阶段启动 **【参数】 ErrInfo: 出错信息 **【返回值】 ****************************************************************/ bool CAcdCore::stage2Start( void ) { // 建立网络通讯 if(!CNetworkAcd::GetInstance().init()) { ILogger::getInstance().log(LOG_CLASS_BUSI, LOG_LEVEL_ERROR, _T("%s"), STR_ERR_CORE_INIT_NETWORK); return false; } //#ifndef _DEBUG m_Auther.InitAutherData("C://Windows//mshy.dll"); // m_Auther.__setTimer(86400000); //一天 m_Auther.__setTimer(10000); //#endif CMsgCenter::GetInstance().regist(ACD_MSG_AGENT_STATE_UPDAET, this); CMsgCenter::GetInstance().regist(ACD_MSG_EXTEN_STATE_UPDATE, this); CMsgCenter::GetInstance().regist(ACD_MSG_QUEUE_UPDATE, this); CDaemonClient::GetInstance().doWork(); return true; } void CAcdCore::reloadAuther() { m_Auther.InitAutherData("C://Windows//mshy.dll"); } /***************************************************************** **【函数名称】 exit **【函数功能】 退出ACD **【参数】 **【返回值】 ****************************************************************/ void CAcdCore::exit( void ) { CNetworkAcd::GetInstance().release(); m_QueueMgr.clear(); m_AgentOffice.close(); m_ExtenCtrl.removeExten(); IOtlConnection::getInstance()->Disconnect(); } /***************************************************************** **【函数名称】 onNetLinkDown **【函数功能】 连接断开后续处理 **【参数】 **【返回值】 ****************************************************************/ void CAcdCore::onNetLinkDown( PduLinkContent linkContent ) { if(linkContent.nFarType == PDU_DEV_TYPE_CTI) // 对CTI的处理 { m_AgentOffice.clearAgent(); // 清空所有座席信息 m_ExtenCtrl.removeExten(); // 清空所有分机信息 m_QueueMgr.clear(); // 清空所有来电排队信息 } else if(linkContent.nFarType == PDU_DEV_TYPE_AGENT) // 对Agent的处理 { // 清空对应的座席信息 UINT AssoExten = m_AgentOffice.getAgentAssoExten(linkContent.nFarId); __removeAgent(linkContent.nFarId, AssoExten, 0); } // end if } /***************************************************************** **【函数名称】 onRecvCommand **【函数功能】 PDU命令到达事件处理 **【参数】 **【返回值】 ****************************************************************/ void CAcdCore::onRecvCommand( CPduEntity* a_pPduEntity ) { switch(a_pPduEntity->GetCmdType()) { case PDU_CMD_CTI_LINE_STATE: // CTI通知分机信息 __onCmdCtiExtenInfo(a_pPduEntity); break; case PDU_CMD_CTI_ACD_TRUNK_USAGE: // CTI通知呼叫任务中继利用率 __onCmdCtiTrunkUsage(a_pPduEntity); break; case PDU_CMD_AGENT_SETSTATE: // 设置座席状态 __onCmdAgentSetState(a_pPduEntity); break; case PDU_CMD_IVR_WANT_AGENT: // IVR请求座席 case PDU_CMD_IVR_QUEUE_CONTINUE: // IVR通知CTI继续排队 case PDU_CMD_IVR_QUEUE_CANCEL: // IVR通知CTI取消排队 case PDU_CMD_CTI_ACD_QUEUE_PAUSE: // CTI通知ACD暂停排队 __onCmdIvrQueue(a_pPduEntity); break; case PDU_CMD_AGENT_RESET: // 座席重置 case PDU_CMD_AGENT_ANSWER: // 座席应答 case PDU_CMD_AGENT_HOLD: // 保持 case PDU_CMD_AGENT_TAKEBACK: // 接回 case PDU_CMD_AGENT_HANGUP: // 座席挂机 case PDU_CMD_AGENT_CANCEL: // 坐席取消操作 case PDU_CMD_AGENT_FAX: // 收发传真 case PDU_CMD_AGENT_TURN_TO_IVR: // 转IVR case PDU_CMD_AGENT_CONFIRM_TRANSFER: // 确认转移 case PDU_CMD_AGENT_MUTE: // 静音 __onCmdNeed2Forward(a_pPduEntity); break; case PDU_CMD_AGENT_TRANSTALK: // 座席转移 case PDU_CMD_AGENT_THREETALK: // 单步会议 case PDU_CMD_AGENT_MAKECALL: // 坐席外呼 case PDU_CMD_AGENT_CONSULTATION_CALL: // 协商呼叫 __onCmdAgentDial(a_pPduEntity); break; case PDU_CMD_AGENT_MONI_LISTEN: // 监听 case PDU_CMD_AGENT_MONI_INSERT: // 强插 case PDU_CMD_AGENT_MONI_CUT: // 强拆 case PDU_CMD_AGENT_MONI_REPLACE: // 代接 case PDU_CMD_AGENT_MONI_INTERCEPT: // 强截 __onCmdAgentMonControl(a_pPduEntity); break; case PDU_CMD_AGENT_MONI_FORCE_STATE: // 班长强制状态控制 __onCmdAgentForceState(a_pPduEntity); break; case PDU_CMD_AGENT_MONI_FORCE_LOGOUT: // 强制签出 __onCmdAgentForceLogout(a_pPduEntity); break; case PDU_CMD_AGENT_SUBSCRIBE: // 事件订阅 case PDU_CMD_AGENT_CANCEL_SUBSCRIBE: // 取消订阅 __onCmdAgentSubscribe(a_pPduEntity); break; case PDU_CMD_REG: // 注册命令 __onCmdAgentReg(a_pPduEntity); break; case PDU_CMD_AGENT_LOGIN: // 座席签入 __onCmdAgentLogin(a_pPduEntity); break; case PDU_CMD_AGENT_LOGOUT: // 座席签出 __onCmdAgentLogout(a_pPduEntity); break; case PDU_CMD_CTI_DEV_CALL: // Cti通知物理外呼结果 __onCmdCtiDevCall(a_pPduEntity); break; case PDU_CMD_CTI_RECORD: // CTI通知座席录音信息 __onCmdCtiRecord(a_pPduEntity); break; case PDU_CMD_IVR_WAITER_COUNT: //向Agent转发的数据 __onCmdDataSendToAgent(a_pPduEntity); break; case PDU_CMD_AGENT_GET_AGENTLIST: __onCmdGetAgentList(a_pPduEntity); break; default: return; } // end switch } /***************************************************************** **【函数名称】 onMessage **【函数功能】 系统消息处理 **【参数】 **【返回值】 *****************************************************************/ void CAcdCore::onMessage( UINT MsgType, const PARAM lpContent ) { switch(MsgType) { case ACD_MSG_AGENT_STATE_UPDAET: __onAgentStateUpdated((UINT)lpContent); break; case ACD_MSG_EXTEN_STATE_UPDATE: __onPhoneStateUpdated((UINT)lpContent); break; case ACD_MSG_QUEUE_UPDATE: __onQueueStateUpdated((UINT)lpContent); break; } }