#include "EslGateway.h" #include #include #include "Config.h" #include "Log.h" #include "JdbcHelper.h" #include "SqlWrite.h" #include "Util.h" #define ESL_CMD_SCAN_INTERNAL "api sofia status profile internal reg\n\n" #define ESL_HEADER_CALLID "Channel-Call-UUID" #define ESL_HEADER_CCID "variable_cc_member_session_uuid" // freeswitchs自带的callcenter模块转接坐席后,callid产生变化,可通过该通道变量进行关联 #define ESL_HEADER_CHANID "Unique-ID" #define ESL_HEADER_CALLER "Caller-Caller-ID-Number" #define ESL_HEADER_CALLEE "Caller-Callee-ID-Number" #define ESL_HEADER_DEST_NUM "Caller-Destination-Number" #define ESL_HEADER_DIRECTION "Call-Direction" #define ESL_HEADER_SUBCLASS "Event-Subclass" #define ESL_HEADER_EXTEN_NO "username" // ESL事件头域常量值 #define ESL_HDR_DIRECTION_INBOUND "inbound" #define ESL_HDR_DIRECTION_OUTBOUND "outbound" #define ESL_HDR_SUBCLASS_SIP_REG "sofia::register" #define ESL_HDR_SUBCLASS_SIP_UNREG "sofia::unregister" #define ESL_HDR_SUBCLASS_ASR "asr" EslGateway::EslGateway() :m_Stop(true), m_pEventThread(nullptr), m_wsServer(nullptr) { } EslGateway::~EslGateway() { } bool EslGateway::initWs() { auto cfg = CConfig::GetInstance(); if (m_wsServer == nullptr) { m_wsServer = std::make_unique(); m_wsServer->setCallbackRecvMsg(std::bind(&this_type::__onWsMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); m_wsServer->setCallbackClose(std::bind(&this_type::__onWsClose, this, std::placeholders::_1)); return m_wsServer->init(cfg->wsPort(), cfg->wssPort(), cfg->sslFile(), cfg->sslPwd()); } return true; } bool EslGateway::runWs() { if (m_wsServer != nullptr) { m_wsServer->run(); } return true; } bool EslGateway::initDB() { auto cfg = CConfig::GetInstance(); if (!JdbcHelper::GetInstance()->init(cfg->dbHost(), cfg->dbUser(), cfg->dbPwd(), cfg->dbName())) { LOG_ERROR("程序运行失败,数据库驱动加载失败"); return false; } if (JdbcHelper::GetInstance()->jdbc_connect(false)) { LOG_ERROR("程序运行失败,数据库连接失败"); return false; } LOG_INFO("数据库连接成功"); std::string strSQL; strSQL = "CREATE TABLE If Not Exists `asrinfo` (" \ "`id` bigint NOT NULL AUTO_INCREMENT," \ "`callid` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL," \ "`agent` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, " \ "`exten` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, " \ "`occur_time` datetime NULL DEFAULT CURRENT_TIMESTAMP, " \ "`msg` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL, " \ "`src` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL, " \ "`dst` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL, " \ "PRIMARY KEY(`id`) USING BTREE " \ ")ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;"; JdbcHelper::GetInstance()->jdbc_executeUpdate(strSQL, nullptr, [=](sql::SQLException &e) { LOG_ERROR_S(boost::str(Format("数据库表初始化失败,错误信息:%s\n%s") % e.what() % strSQL)); }); return true; } bool EslGateway::start() { memset(&m_EslHdl4Listen, '\0', sizeof(esl_handle_t)); memset(&m_EslHdl4Send, '\0', sizeof(esl_handle_t)); auto cfg = CConfig::GetInstance(); LOG_INFO("{EslGateway}: 处理客户端连接, ip = %s,port=%d,pwd=%s", cfg->fsIp().c_str(), cfg->fsPort(), cfg->fsPwd().c_str()); esl_connect(&m_EslHdl4Listen, cfg->fsIp().c_str(), cfg->fsPort(), NULL, cfg->fsPwd().c_str()); if (!m_EslHdl4Listen.connected) return false; LOG_INFO("{EslGateway}: 处理客户端连接m_EslHdl4Listen OK"); esl_events(&m_EslHdl4Listen, ESL_EVENT_TYPE_PLAIN, ("CHANNEL_CREATE CHANNEL_ANSWER CHANNEL_HANGUP_COMPLETE DETECTED_SPEECH CUSTOM sofia::register sofia::unregister callcenter::info")); esl_connect(&m_EslHdl4Send, cfg->fsIp().c_str(), cfg->fsPort(), NULL, cfg->fsPwd().c_str()); if (!m_EslHdl4Send.connected) { esl_disconnect(&m_EslHdl4Listen); return false; } scanExten(); m_Stop = false; if (m_pEventThread == nullptr) { m_pEventThread = std::make_unique(std::bind(&this_type::__eventThread, this)); } return true; } void EslGateway::stop() { m_Stop.store(true); if (m_pEventThread) { m_pEventThread->interrupt(); m_pEventThread->join(); m_pEventThread.reset(); } memset(&m_EslHdl4Listen, '\0', sizeof(esl_handle_t)); memset(&m_EslHdl4Send, '\0', sizeof(esl_handle_t)); } void EslGateway::scanExten() { if (esl_send_recv(&m_EslHdl4Send, ESL_CMD_SCAN_INTERNAL) != ESL_SUCCESS) return; if (m_EslHdl4Send.last_sr_event == nullptr || m_EslHdl4Send.last_sr_event->body == nullptr) return; std::string body = m_EslHdl4Send.last_sr_event->body; std::list rows; boost::split(rows, body, boost::is_any_of("\n")); while (!rows.empty()) { std::string var = rows.front(); rows.pop_front(); std::regex space("\t| "); var = std::regex_replace(var, space, ""); std::regex reg("IP:(.*)"); if (std::regex_match(var, reg)) { std::string extenNo, extenIP; extenIP = std::regex_replace(var, reg, "$1"); while (!rows.empty()) { var = rows.front(); rows.pop_front(); std::regex space("\t| "); var = std::regex_replace(var, space, ""); reg = std::regex("Auth-User:(.*)"); if (std::regex_match(var, reg)) { extenNo = std::regex_replace(var, reg, "$1"); if (!extenNo.empty() && !extenIP.empty()) { LOG_DEBUG("分机注册:%s %s", extenNo.c_str(), extenIP.c_str()); __addExtenChan(extenNo); } break; } } } } } bool EslGateway::excDetectSpeech(const std::string & strChanId) { return true; execte("set", "fire_asr_events=true", strChanId); return execte("detect_speech", "unimrcp:mrcpv2 hello hello", strChanId); } bool EslGateway::excDetectSpeechResume(const std::string & strChanId) { return true; return execte("detect_speech", "resume", strChanId); } bool EslGateway::execte(const std::string & strApp, const std::string & strParam, const std::string & strChanId) { esl_status_t Status = esl_execute(&m_EslHdl4Send, strApp.c_str(), strParam.c_str(), strChanId.c_str()); return Status == ESL_SUCCESS; } void EslGateway::__eventThread(void) { esl_event_t* pEvent = nullptr; esl_handle_t* pListenHandle = &m_EslHdl4Listen; // 下面的设置保证执行同时多个APP时,后面的不会抢占前面的 pListenHandle->event_lock = 1; while (!m_Stop) { // esl_recv_event会阻塞线程,其第二个参数为1,表示优先检查内部缓存列表,防止遗漏事件。 auto status = esl_recv_event(pListenHandle, 1, nullptr); if ((status == ESL_SUCCESS || status == ESL_BREAK) && !m_Stop) { pEvent = pListenHandle->last_ievent; if (pEvent != nullptr) { __onEslEvent(pEvent); } } else // 连接中断 { // 连接中断 LOG_WARN_S("FS链接断开..."); do { LOG_WARN_S("FS 3秒后自动重连..."); std::this_thread::sleep_for(std::chrono::seconds(3)); LOG_WARN_S("FS开始重连..."); } while (!start() && !m_Stop); } } } void EslGateway::__onEslEvent(esl_event_t * pEvent) { switch (pEvent->event_id) { case ESL_EVENT_CHANNEL_CREATE: case ESL_EVENT_CHANNEL_ANSWER: case ESL_EVENT_CHANNEL_HANGUP_COMPLETE: __onEslEvtChanEvent(pEvent); break; case ESL_EVENT_DETECTED_SPEECH: __detectSpeech(pEvent); break; case ESL_EVENT_CUSTOM: __onEslEvtCustom(pEvent); break; default: break; } } void EslGateway::__onEslEvtChanEvent(esl_event_t * pEvent) { // 获取会话ID std::string strCallID = ""; if ((strCallID = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CCID))) == "") { strCallID = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CALLID)); } // 获取通道ID const auto pChanID = esl_event_get_header(pEvent, ESL_HEADER_CHANID); const std::string strChanID = std::to_string(pChanID); // 获取主被叫 std::string strCaller = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CALLER)); std::string strCallee = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_CALLEE)); if (strCallee == "") { strCallee = std::to_string(esl_event_get_header(pEvent, ESL_HEADER_DEST_NUM)); } // 获取通道方向 CALL_DIRECTION emDirection; if (strcmp(esl_event_get_header(pEvent, ESL_HEADER_DIRECTION), ESL_HDR_DIRECTION_INBOUND) == 0) emDirection = CALL_DIRECTION_INBOUND; else emDirection = CALL_DIRECTION_OUTBOUND; switch (pEvent->event_id) { case ESL_EVENT_CHANNEL_CREATE: { LOG_INFO_S(boost::str(Format("ESL通道创建事件[%s][%s]") % strCallID %strChanID)); std::shared_ptr pChan = nullptr; if (emDirection == CALL_DIRECTION_INBOUND) { // 呼入 pChan = __getExtenChan(strCaller); if (pChan == nullptr) { // 中继呼入 pChan = __addTrunkChan(strCaller); LOG_INFO_S(boost::str(Format("中继[%s]呼入[%s]-[%s]") % strCaller %strCaller %strCallee)); } else { LOG_INFO_S(boost::str(Format("分机[%s]外呼[%s]-[%s]") % strCaller % strCaller %strCallee)); } } else { pChan = __getExtenChan(strCallee); if (pChan == nullptr) { // 中继呼出 pChan = __addTrunkChan(strCallee); LOG_INFO_S(boost::str(Format("中继[%s]呼出[%s]-[%s]") % strCallee % strCaller %strCallee)); } else { LOG_INFO_S(boost::str(Format("分机[%s]呼入[%s]-[%s]") % strCallee % strCaller %strCallee)); } } pChan->Create(strCallID, strChanID, strCaller, strCallee, emDirection); LOG_DEBUG_S(boost::str(Format("通道信息[%s]") % pChan->to_string())); const auto spSession = __findSession(strCallID, strChanID); spSession->addChan(pChan); } break; case ESL_EVENT_CHANNEL_ANSWER: { LOG_INFO_S(boost::str(Format("ESL通道接听事件[%s][%s]") % strCallID %strChanID)); const auto spSession = __findSession(strCallID, strChanID); if (spSession->size() == 2) { const auto pChan = spSession->getChan(strChanID); const auto pAssoChan = spSession->getAssoChan(strChanID); if (pChan == nullptr) { LOG_WARN_S(boost::str(Format("SessionID[%s]Caller[%s]Callee[%s]ChanID_old[%s]ChanID_new[%s]") % spSession->Id() % strCaller %strCallee % pChanID %strChanID)); //return; } //if (pChan->Type() == pAssoChan->Type()) { // 分机打分机,两个都是中继 不进行语音识别 // LOG_WARN_S(boost::str(Format("会话[%s]中两条通道类型[%s]相同,不进行语音识别")% spSession->Id() % (pChan->Type()== DEV_RES_TYPE_EXT?"分机":"中继"))); // return; //} if (pChan->Type() == DEV_RES_TYPE_EXT && pAssoChan->Type()== DEV_RES_TYPE_EXT) { // 分机打分机,两个都是中继 不进行语音识别 LOG_WARN_S(boost::str(Format("会话[%s]中两条分机通道[%s][%s],不进行语音识别")% spSession->Id() % pChanID %strChanID)); return; } if (pChan->Type() == DEV_RES_TYPE_EXT && pChan->Direct() == CALL_DIRECTION_OUTBOUND) { // 呼入 pChan->setCallInfo(strCallID, pAssoChan->No(), pChan->No(), "CallIn", "Agent"); pAssoChan->setCallInfo(strCallID, pAssoChan->No(), pChan->No(), "CallIn", "User"); } else if (pChan->Type() != DEV_RES_TYPE_EXT && pChan->Direct() != CALL_DIRECTION_OUTBOUND) { // 呼入 pChan->setCallInfo(strCallID, pChan->No(), pAssoChan->No(), "CallIn", "User"); pAssoChan->setCallInfo(strCallID, pChan->No(), pAssoChan->No(), "CallIn", "Agent"); } else if (pChan->Type() == DEV_RES_TYPE_EXT && pChan->Direct() != CALL_DIRECTION_OUTBOUND) { // 呼出 pChan->setCallInfo(strCallID, pChan->No(), pAssoChan->No(), "CallOut", "Agent"); pAssoChan->setCallInfo(strCallID, pChan->No(), pAssoChan->No(), "CallOut", "User"); } else if (pChan->Type() != DEV_RES_TYPE_EXT && pChan->Direct() == CALL_DIRECTION_OUTBOUND) { // 呼出 pChan->setCallInfo(strCallID, pAssoChan->No(), pChan->No(), "CallOut", "User"); pAssoChan->setCallInfo(strCallID, pAssoChan->No(), pChan->No(), "CallOut", "Agent"); } else { // 未知逻辑 LOG_ERROR_S(boost::str(Format("未知逻辑,CallId[%s]ChanID[%s]Caller[%s]Callee[%s]Direct[%d]") % strCallID %strChanID %strCaller %strCallee %emDirection)); return; } std::string strExtNo = ""; if (pChan->Type() == DEV_RES_TYPE_EXT) { strExtNo = pChan->No(); } else { strExtNo = pAssoChan->No(); } auto it = m_mapExtAgent.find(strExtNo); if (it != m_mapExtAgent.end()) { pChan->bindAgent(it->second); pAssoChan->bindAgent(it->second); } excDetectSpeech(strChanID); excDetectSpeech(pAssoChan->ChanID()); LOG_INFO_S(boost::str(Format("开始识别会话[%s]\n[%s]") % pChan->No() % pChan->to_string())); LOG_INFO_S(boost::str(Format("开始识别会话[%s]\n[%s]") % pAssoChan->No() % pAssoChan->to_string())); } } break; case ESL_EVENT_CHANNEL_HANGUP_COMPLETE: { LOG_INFO_S(boost::str(Format("ESL通道挂机事件[%s][%s]") % strCallID %strChanID)); __delSession(strCallID, strChanID); } break; default: break; } } void EslGateway::__hangupEvent(esl_event_t * pEvent) { } void EslGateway::__detectSpeech(esl_event_t * pEvent) { /*LOG_DEBUG_S("语音识别消息"); char *pContent = new char[2048]; esl_event_serialize(pEvent, &pContent, ESL_TRUE); LOG_DEBUG_S(pContent); if (pContent) { delete pContent; pContent = nullptr; }*/ std::string strCallID = ""; auto pCallID = esl_event_get_header(pEvent, ESL_HEADER_CALLID); strCallID = pCallID == nullptr ? "" : pCallID; auto pCallCCID = esl_event_get_header(pEvent, ESL_HEADER_CCID); if (pCallCCID != nullptr) { strCallID = pCallCCID; } // 获取通道ID std::string strChanID = ""; auto pChanID = esl_event_get_header(pEvent, ESL_HEADER_CHANID); strChanID = pChanID == nullptr ? "" : pChanID; char* speechType = nullptr; speechType = esl_event_get_header(pEvent, "Speech-Type"); if (speechType == nullptr) { LOG_WARN_S(boost::str(Format("ESL语音识别事件[%s][%s],Speech-Type 空") % strCallID %strChanID)); return; } LOG_INFO_S(boost::str(Format("ESL语音识别事件[%s][%s][%s]") % speechType % strCallID %strChanID)); const auto pSession = __findSession(strCallID, strChanID); if (pSession == nullptr) { LOG_WARN_S(boost::str(Format("ESL语音识别事件处理失败,找不到会话[%s][%s]") % strCallID %strChanID)); return; } const auto pChan = pSession->getChan(strChanID); if (pChan == nullptr)return; if (strcmp(speechType, "begin-speaking") == 0) { pChan->setSentenceBeginTime(); } else if (strcmp(speechType, "detected-speech") == 0) { excDetectSpeechResume(strChanID); // 识别结束,继续识别 char* result = esl_event_get_body(pEvent); if (result) { std::string strRes, err; parseASRXml(result, strRes, err); pChan->setTransSentenceText(strRes, pSession->transSentenceIndex()); auto strAsrRes = pChan->asrInfo(); LOG_INFO_S(boost::str(Format("识别结果:\n[%s]")%strAsrRes)); std::string strExtNo = ""; if (pChan->Type() == DEV_RES_TYPE_EXT) { strExtNo = pChan->No(); } else { const auto pAssoChan = pSession->getAssoChan(strChanID); if (pAssoChan) { strExtNo = pAssoChan->No(); } } // 推送 if (strExtNo != "") { if (m_wsServer) { auto pair = m_mapConId.equal_range(strExtNo); for (auto it = pair.first; it != pair.second; ++it) { m_wsServer->sendMsg(it->second, strAsrRes); LOG_INFO_S(boost::str(Format("识别结果发送到[%s]") % strExtNo)); } } std::string strSQL; Format fmt("insert into asrinfo(callid,agent,exten,msg,src,dst) VALUES('%s','%s','%s','%s','%s','%s')"); fmt %pSession->Id() % "" %strExtNo %strRes %result %strAsrRes; strSQL = fmt.str(); SqlWrite::GetInstance()->addSql(strSQL); } } } } void EslGateway::__onEslEvtCustom(esl_event_t * pEvent) { const char* pSubClass = esl_event_get_header(pEvent, ESL_HEADER_SUBCLASS); if (pSubClass == nullptr)return; auto HeaderValue = esl_event_get_header(pEvent, ESL_HEADER_EXTEN_NO); if (strcmp(pSubClass, ESL_HDR_SUBCLASS_SIP_REG) == 0) { // 分机注册 if (HeaderValue == nullptr) return; __addExtenChan(std::to_string(HeaderValue)); } else if (strcmp(pSubClass, ESL_HDR_SUBCLASS_SIP_UNREG) == 0) { // 分机取消注册 if (HeaderValue == nullptr) return; __delExtenChan(std::to_string(HeaderValue)); } } void EslGateway::__addExtenChan(const std::string & strNo) { auto it = std::find_if(m_lstExtenChan.begin(), m_lstExtenChan.end(), [=](const std::shared_ptr&pChan) {return pChan->No().compare(strNo) == 0; }); if (it == m_lstExtenChan.end()) { auto pChan = std::make_shared(DEV_RES_TYPE_EXT, strNo); m_lstExtenChan.emplace_back(pChan); } LOG_INFO_S(boost::str(Format("分机[%s]注册") % strNo)); } void EslGateway::__delExtenChan(const std::string & strNo) { auto it = std::find_if(m_lstExtenChan.begin(), m_lstExtenChan.end(), [=](const std::shared_ptr&pChan) {return pChan->No().compare(strNo) == 0; }); if (it != m_lstExtenChan.end()) { m_lstExtenChan.erase(it); LOG_INFO_S(boost::str(Format("分机[%s]注册取消成功") % strNo)); } else { LOG_INFO_S(boost::str(Format("分机[%s]注册取消失败,分机不存在") % strNo)); } } std::shared_ptr EslGateway::__getExtenChan(const std::string & strNo) { auto it = std::find_if(m_lstExtenChan.begin(), m_lstExtenChan.end(), [=](const std::shared_ptr&pChan) {return pChan->No().compare(strNo) == 0; }); if (it != m_lstExtenChan.end()) { return *it; } return nullptr; } std::shared_ptr EslGateway::__addTrunkChan(const std::string & strNo) { auto pChan = std::make_shared(DEV_RES_TYPE_TRUNK, strNo); m_lstTrunkChan.emplace_back(pChan); LOG_INFO_S(boost::str(Format("添加中继资源[%s]") % strNo)); return pChan; } bool EslGateway::__delTrunkChan(const std::string & strChanID) { auto it = std::find_if(m_lstTrunkChan.begin(), m_lstTrunkChan.end(), [=](const std::shared_ptr&pChan) {return pChan->ChanID().compare(strChanID) == 0; }); if (it != m_lstTrunkChan.end()) { LOG_DEBUG_S(boost::str(Format("删除中继%s")% (*it)->to_string())); m_lstTrunkChan.erase(it); return true; } return false; } std::shared_ptr EslGateway::__findSession(const std::string & strCallID, const std::string&strChanID) { auto it = std::find_if(m_lstSession.begin(), m_lstSession.end(), [=](const std::pair>& p) { return (p.first.compare(strCallID)==0 && strCallID != "") || p.second->getChan(strChanID) != nullptr; }); if (it != m_lstSession.end()) { LOG_DEBUG_S(boost::str(Format("找到会话[%s],通道[%s][%s]") % it->first%strCallID%strChanID)); return it->second; } auto spSession = std::make_shared(strCallID); m_lstSession.insert(std::make_pair(strCallID, spSession)); LOG_DEBUG_S(boost::str(Format("创建新会话[%s]") % strCallID)); return spSession; } void EslGateway::__delSession(const std::string & strCallID, const std::string & strChanID) { auto it = std::find_if(m_lstSession.begin(), m_lstSession.end(), [=](const std::pair>& p) { return p.first.compare(strCallID) == 0 || p.second->getChan(strChanID) != nullptr; }); if (it != m_lstSession.end()) { auto pSession = it->second; auto pChan = pSession->getChan(strChanID); if (pChan) { pSession->delChan(pChan); if (pChan->Type() == DEV_RES_TYPE_EXT) { pChan->Hangup(); } else { __delTrunkChan(strChanID); } } if (pSession->size() == 0) { m_lstSession.erase(it); LOG_INFO_S(boost::str(Format("会话销毁CallID[%s]ChanID[%s]") % strCallID %strChanID)); } } else { LOG_WARN_S(boost::str(Format("会话中未找到通道资源[%s][%s]") % strCallID %strChanID)); if (!__delTrunkChan(strChanID)) { auto it = std::find_if(m_lstExtenChan.begin(), m_lstExtenChan.end(), [=](const std::shared_ptr&pChan) {return pChan->ChanID().compare(strChanID)==0; }); if (it != m_lstExtenChan.end()) { (*it)->Hangup(); } } } } void EslGateway::__onWsMessage(const long & lConId, const std::string & strMsg, const std::string & strIP) { Json::Value root; Json::String errs; Json::CharReaderBuilder builder; builder["collectComments"] = true; std::unique_ptrpReader(builder.newCharReader()); if (!pReader->parse(strMsg.c_str(), strMsg.c_str() + strMsg.length(), &root, &errs) || errs != "" || !root.isObject()) { Format fmt("json字符串格式异常,异常信息[%s]\n[%s]"); fmt %errs %strMsg; LOG_ERROR_S(fmt.str()); return; } const auto type = root["Type"].asString(); const auto AgentID = root["AgentID"].asString(); const auto AgentExten = root["AgentExten"].asString(); bool bOk = false; std::string strDesc = "失败"; if (type == "Login") { auto it = std::find_if(m_mapConId.begin(), m_mapConId.end(), [=](std::pairpair) { return pair.first == AgentExten && pair.second == lConId; }); if (it == m_mapConId.end()) { m_mapConId.insert(std::make_pair(AgentExten, lConId)); } bOk = true; } else if (type == "Logout") { auto it = std::find_if(m_mapConId.begin(), m_mapConId.end(), [=](std::pairpair) { return pair.first == AgentExten && pair.second == lConId; }); if (it != m_mapConId.end()) { m_mapConId.erase(it); } bOk = true; } else if (type == "BindExtAgent") { strDesc = "失败,分机已与其它坐席进行关联"; auto it = m_mapExtAgent.find(AgentExten); if (it == m_mapExtAgent.end()) { strDesc = "成功"; bOk = true; m_mapExtAgent[AgentExten] = AgentID; } LOG_INFO_S(boost::str(Format("绑定分机[%s]坐席[%s]关系[%s]") % AgentExten%AgentID%strDesc)); } else if (type == "UnBindExtAgent") { strDesc = "失败,未进行关联"; auto it = m_mapExtAgent.find(AgentExten); if (it != m_mapExtAgent.end()) { strDesc = "成功"; bOk = true; m_mapExtAgent.erase(it); } LOG_INFO_S(boost::str(Format("解除绑定分机[%s]坐席[%s]关系[%s]") % AgentExten%AgentID%strDesc)); } auto strJson = __createJson(type, bOk, strDesc); m_wsServer->sendMsg(lConId, strJson); } void EslGateway::__onWsClose(const long & lConId) { for (auto it = m_mapConId.begin(); it != m_mapConId.end();) { if (it->second == lConId) { it = m_mapConId.erase(it); } else { ++it; } } } std::string EslGateway::__createJson(const std::string & strType, const bool & bOk, const std::string&strDesc) { Json::Value root; root["Type"] = strType; root["Result"] = bOk; root["Desc"] = strDesc; Json::StreamWriterBuilder builder; return Json::writeString(builder, root); }