#include "..\AsrService\Session.h" #include "..\AsrService\Session.h" #include "..\AsrService\Session.h" #include "..\AsrService\Session.h" #include "Session.h" #include #include "FsProxy.h" #include "ChanTrunk.h" #include "ChanExten.h" #include "JsonStringMaker.h" #include "Util.h" #include "SqlWrite.h" #include "Config.h" Session::Session(CFsProxy* pParent, string Id) : m_pParent(pParent), m_Id(Id), m_IsAnswer(false), m_IsSaveDb(true), m_AtionId(0) { } Session::~Session() { } bool Session::isContainChanId(const std::string & chanId) { std::unique_lock lock(m_ChanLock); for (auto pChan : m_ListChan) { if (pChan->chanId() == chanId) return true; } return false; } void Session::prepare(PCHAN_EVENT_NOTIFY pNotify) { if (m_Id != pNotify->ChanId) // 触发会话的通道不是会话主通道,则查找并添加主通道 { VirtualChan* pChan = m_pParent->getBusyChan(m_Id); if (pChan != nullptr) __addChan(pChan); } } void Session::onChanEvent(PCHAN_EVENT_NOTIFY pNotify) { if (pNotify == nullptr) return; VirtualChan* pChan = __getChan(pNotify->ChanId); if (pChan == nullptr) { pChan = __findChan(pNotify); if (pChan != nullptr) __addChan(pChan); else { Format fmt("{Fs.Session}: 处理通道事件时查找对应通道失败, Caller = %s, Callee = %s"); fmt % pNotify->Caller % pNotify->Callee; LOG_WARN_S(fmt.str()); return; } } __notifyDbSaveChanEvent(pChan, pNotify); // 通话记录保存 pChan->onChanEvent(pNotify); __notifyIncomingChanEvent(pChan, pNotify); // 来电弹屏 __onRecord(pChan, pNotify); // 执行录音 // 过滤无效状态 if (pChan->state() == CHAN_LOGIC_STATE_FREE && pNotify->EventId == CHANNEL_EVENT_CREATE) return; __notifySessionChanEvent(pChan, pNotify); __onPlayAgentNo(pChan, pNotify); // 判断是否报工号 try { if (pChan->isFree()) __onChanFree(pChan, pNotify); } catch (const std::exception& e) { LOG_WARN(("{Fs.Session.onChanEvent}: 通道空闲处理函数执行发生异常[%s], Caller = %s, Callee = %s"),e.what(), pNotify->Caller.c_str(), pNotify->Callee.c_str()); } } void Session::onChanDtmf(PDTMF_NOTIFY pNotify) { } void Session::onChanHold(PHOLD_NOTIFY pNotify) { VirtualChan* pChan = __getChan(pNotify->ChanId); if (pChan != nullptr) { pChan->onChanHold(pNotify->EvtType); } } VirtualChan * Session::getAssoChan(VirtualChan * pChan) { std::unique_lock lock(m_ChanLock); auto it = m_ListChan.begin(); while (it != m_ListChan.end()) { if (*it != pChan) return *it; ++it; } return nullptr; } VirtualChan * Session::getAssoFinalChan() { std::unique_lock lock(m_ChanLock); return m_ListChan.back(); } VirtualChan * Session::getAssoFinalChan(VirtualChan * pChan) { std::unique_lock lock(m_ChanLock); auto pFinalChan = m_ListChan.back(); if (pFinalChan == pChan)return nullptr; // 是同一条,返回空 return m_ListChan.back(); } VirtualChan * Session::__getFirstChan() { std::unique_lock lock(m_ChanLock); if(m_ListChan.empty()) return nullptr; return m_ListChan.front(); } VirtualChan * Session::__getChan(string ChanId) { std::unique_lock lock(m_ChanLock); auto it = m_ListChan.begin(); while (it != m_ListChan.end()) { if ((*it)->chanId() == ChanId) return *it; ++it; } return nullptr; } VirtualChan * Session::__findChan(PCHAN_EVENT_NOTIFY pNotify) { VirtualChan* pChan = nullptr; if (pNotify->EventId == CHANNEL_EVENT_CREATE) { stringstream str; uint32_t ExtenNo = 0; if (pNotify->Direction == CALL_DIRECTION_INBOUND) str << pNotify->Caller; //ExtenNo = stoul(pNotify->Caller); // 呼入FS else str << pNotify->Callee; //ExtenNo = stoul(pNotify->Callee); // FS呼出 str >> ExtenNo; pChan = m_pParent->getExten(ExtenNo); if (pChan == nullptr) // 分机通道中无,则说明是中继通道 { pChan = m_pParent->getFreeTrunk(); if (pChan == nullptr) { Format fmt("{Fs.Session}: 处理通道事件时获取空闲中继通道失败, Caller = %s, Callee = %s"); fmt % pNotify->Caller % pNotify->Callee; LOG_WARN_S(fmt.str()); m_pParent->onChanPoor(this, pNotify); } } } else { pChan = m_pParent->getBusyChan(pNotify->ChanId); } return pChan; } void Session::__addChan(VirtualChan * pChan) { std::unique_lock lock(m_ChanLock); m_ListChan.emplace_back(pChan); } /***************************************************************** **【函数名称】 __delChan **【函数功能】 删除指定通道 **【参数】 **【返回值】 *****************************************************************/ void Session::__delChan(VirtualChan * pChan) { std::unique_lock lock(m_ChanLock); m_ListChan.remove(pChan); } /***************************************************************** **【函数名称】 __clearChan **【函数功能】 清空通道 **【参数】 **【返回值】 *****************************************************************/ void Session::__clearChan(void) { std::unique_lock lock(m_ChanLock); m_ListChan.clear(); } void Session::__notifySessionChanEvent(VirtualChan * pChanHost, PCHAN_EVENT_NOTIFY pNotify) { std::unique_lock lock(m_ChanLock); auto it = m_ListChan.begin(); while (it != m_ListChan.end()) { if ((*it) != pChanHost) { (*it)->onSessionChanEvent(pChanHost, pNotify); } ++it; } } /* ** 分机来电事件 */ void Session::__notifyIncomingChanEvent(VirtualChan * pChanHost, PCHAN_EVENT_NOTIFY pNotify) { if (pNotify->Caller == pNotify->Callee && pNotify->Called.empty()) // 自震分机不弹屏/班长坐席操作不弹屏 return; // 会话有两条通道,且当前通道为分机,当前时间为创建对话,发送来电事件给当前分机 if (m_ListChan.size()==2 && pChanHost->type() == DEV_RES_TYPE_EXT && pNotify->EventId == CHANNEL_EVENT_CREATE) { std::string CallID = pNotify->CcId.empty()==true ? pNotify->CallId : pNotify->CcId; std::string ExtenNo = std::to_string(pChanHost->no()); std::string Number = pNotify->Caller; // 主叫号码 std::string callee = pNotify->Callee; // 被叫号码 std::string TrunkNumber; int inComingType = 0; inComingType = 1; auto pFirstChan = __getFirstChan(); if (pFirstChan != nullptr && pFirstChan->type() == DEV_RES_TYPE_TRUNK) TrunkNumber = pFirstChan->calleeNum(); std::string data = m_pParent->creatJsonIncoming(CallID, Number, callee, TrunkNumber,inComingType); m_pParent->send2Agent(ExtenNo,data); // 发送来电事件到坐席 } if (m_ListChan.size() == 1 && pNotify->EventId == CHANNEL_EVENT_CREATE) { if (pChanHost->type() == DEV_RES_TYPE_EXT) // 分机外呼弹屏 { std::string CallID = pNotify->CcId.empty() == true ? pNotify->CallId : pNotify->CcId; std::string ExtenNo = std::to_string(pChanHost->no()); std::string Number = pChanHost->callerNum(); //pNotify->Caller; // 主叫号码 , 代接,强插时主叫问题 std::string callee = pNotify->Callee; // 被叫号码 if (Number == callee && !pNotify->Called.empty()) { callee = pNotify->Called; } std::string TrunkNumber; int inComingType = 0; if (pNotify->CallType == 0) // 分机外呼 { inComingType = 2; if (pNotify->Caller != pNotify->Callee && pNotify->Direction == CALL_DIRECTION_OUTBOUND) // 该条件下会议 收到来电弹屏 inComingType = 1; } else // 自动外呼 { inComingType = 3; } std::string data = m_pParent->creatJsonIncoming(CallID, Number, callee, TrunkNumber, inComingType); m_pParent->send2Agent(ExtenNo, data); // 发送来电事件到坐席 } } //if (m_ListChan.size() == 2 && pNotify->EventId == CHANNEL_EVENT_CREATE) //{ // std::string CallID = pNotify->CcId.empty() == true ? pNotify->CallId : pNotify->CcId; // std::string ExtenNo = std::to_string(pChanHost->no()); // std::string Number = pNotify->Caller; // 主叫号码 // std::string callee = pNotify->Callee; // 被叫号码 // std::string TrunkNumber; // 中继号码 // int inComingType = 0; // auto pFirstChan = __getFirstChan(); // if (pFirstChan != nullptr && pFirstChan->type() == DEV_RES_TYPE_TRUNK) // { // TrunkNumber = pFirstChan->calleeNum(); // inComingType = 2; // 来电 // } // else if(pFirstChan != nullptr && pFirstChan->type() == DEV_RES_TYPE_EXT) // { // inComingType = 1; // 分机外呼 // if(pNotify->CallType == 2) // 自动外呼 // inComingType = 3; // 分机外呼 // } // std::string data = m_pParent->creatJsonIncoming(CallID, Number, callee, TrunkNumber, inComingType); // m_pParent->send2Agent(ExtenNo, data); // 发送来电事件到坐席 //} } void Session::__notifyDbSaveChanEvent(VirtualChan * pChanHost, PCHAN_EVENT_NOTIFY pNotify) { if (pNotify == nullptr || pChanHost == nullptr) return; // 自动外呼不用该方法保存数据库 if (pNotify->CallType != 0) { __saveAutoCallDB(pChanHost,pNotify); return; } if (pChanHost->isInMeeting()&& pNotify->EventId == CHANNEL_EVENT_HANGUP_COMPLETE) // 会议, { Format fmt("update conference set end_time = '%s' where uuid = '%s'"); fmt %Util::CurTime() % pChanHost->sessionId(); SqlWrite::GetInstance()->addSql(fmt.str()); } if (!pChanHost->opNumber().empty()) // 强插等操作会有操作码 { return; } if (pNotify->EventId == CHANNEL_EVENT_CREATE && pChanHost->type() == DEV_RES_TYPE_EXT && pChanHost == __getFirstChan()) // 分机为主叫时数据库新添加一条记录 { std::string curTime = Util::CurTime(); std::string callid = pNotify->CcId.empty() == true ? pNotify->CallId : pNotify->CcId; std::string agent = m_pParent->getAgentByExten(pNotify->Caller); Format fmt("insert into cdr(uuid,caller_agent,caller,call_type,create_time,action_id) values('%s','%s','%s',%d,'%s',%d)"); // 分机呼叫 call_type=1 fmt %callid %agent % pNotify->Caller % 1 % curTime % 1; SqlWrite::GetInstance()->addSql(fmt.str()); } else if (pNotify->EventId == CHANNEL_EVENT_CREATE && pChanHost != __getFirstChan()) // b腿创建 { m_AtionId++; m_RingTime.clear(); m_AnswerTime.clear(); m_IsSaveDb = true; if (m_AtionId >1 ) { auto pChan = __getFirstChan(); if (pChan != nullptr) { int call_type = 0; if (pChan->type() == DEV_RES_TYPE_EXT) { call_type = 1; } std::string curTime = Util::CurTime(); std::string callid = pNotify->CcId.empty() == true ? pNotify->CallId : pNotify->CcId; std::string agent = m_pParent->getAgentByExten(pNotify->Caller); Format fmt("insert into cdr(uuid,caller_agent,caller,call_type,create_time,action_id) values('%s','%s','%s',%d,'%s',%d)"); // 分机呼叫 call_type=1 fmt %callid %agent % pNotify->Caller % call_type % curTime % m_AtionId; SqlWrite::GetInstance()->addSql(fmt.str()); } } } else if ((pNotify->EventId == CHANNEL_EVENT_PROGRESS || pNotify->EventId == CHANNEL_EVENT_PROGRESS_MEDIA) && pChanHost != __getFirstChan()) // 呼出振铃 { if (m_RingTime.empty()) m_RingTime = Util::CurTime(); } else if (pNotify->EventId == CHANNEL_EVENT_ANSWER && pChanHost != __getFirstChan()) { m_AnswerTime = Util::CurTime(); m_IsAnswer = true; } if (pNotify->EventId == CHANNEL_EVENT_HANGUP_COMPLETE && m_IsSaveDb ) // 收到挂机事件,且第一次更新数据库(update) { std::string curTime = Util::CurTime(); std::string agent; std::string callee; // 被叫 std::string group; // 坐席组 if (pChanHost->type() == DEV_RES_TYPE_TRUNK && pChanHost == __getFirstChan()) // 中继呼入且先挂机 { auto pAssoChan = getAssoChan(pChanHost); // 根据对端线路获取被叫 if (pAssoChan != nullptr) { callee = pAssoChan->calleeNum(); } } else { if (pChanHost->isInMeeting()) // 会议时 callee = pNotify->Caller; else callee = pNotify->Callee; } agent = m_pParent->getAgentByExten(callee,group); auto fun = [](std::string time)->std::string { if (time.empty()) return "null"; else return "'" + time + "'"; }; // 匿名函数用于转化时间 m_RingTime = fun(m_RingTime); m_AnswerTime = fun(m_AnswerTime); if (m_AtionId == 0) m_AtionId = 1; // 增加被叫坐席组 Format fmt("update cdr set group_no = '%s',callee_agent = '%s',callee = '%s', ring_time = %s,answer_time = %s,is_answer = %d,end_time = '%s',record_path = '%s', hangup_cause = '%s' where uuid = '%s' and action_id = %d"); fmt %group %agent %callee % m_RingTime %m_AnswerTime %m_IsAnswer %curTime %m_RecordFile %pNotify->HangupCause %pChanHost->sessionId() % m_AtionId; SqlWrite::GetInstance()->addSql(fmt.str()); // 清空 m_RingTime.clear(); m_AnswerTime.clear(); m_IsAnswer = false; m_IsSaveDb = false; } if (pNotify->EventId == CHANNEL_EVENT_HANGUP_COMPLETE && pChanHost->type() == DEV_RES_TYPE_TRUNK) { std::string curTime = Util::CurTime(); Format fmt("update cdr set hangup_time = '%s' where uuid = '%s'"); fmt %curTime %pChanHost->sessionId(); SqlWrite::GetInstance()->addSql(fmt.str()); } } void Session::__saveAutoCallDB(VirtualChan * pChanHost, PCHAN_EVENT_NOTIFY pNotify) { if (pNotify->EventId == CHANNEL_EVENT_CREATE && pChanHost == __getFirstChan()) // 分机为主叫时数据库新添加一条记录 { std::string curTime = Util::CurTime(); std::string callid = pNotify->CcId.empty() == true ? pNotify->CallId : pNotify->CcId; std::string sql; if (pNotify->CallType == VOICE_CALL) { Format fmt("insert into auto_cdr(uuid,caller,create_time,task_id) values('%s','%s','%s',%ld)"); // 语音呼叫 call_type=1 fmt %callid % pNotify->Caller % curTime %pNotify->TaskId; sql = fmt.str(); } else { std::string agent = m_pParent->getAgentByExten(pNotify->Caller); Format fmt("insert into auto_cdr(uuid,agent,caller,create_time,task_id) values('%s','%s','%s','%s',%ld)"); // 分机呼叫 call_type=2 fmt %callid %agent %pNotify->Callee %curTime %pNotify->TaskId; //Format fmt("insert into auto_cdr(uuid,caller,create_time,task_id) values('%s','%s','%s',%ld)"); // 分机呼叫 call_type=2 //fmt %callid % pNotify->Callee % curTime %pNotify->TaskId; sql = fmt.str(); } SqlWrite::GetInstance()->addSql(sql); } else if ((pNotify->EventId == CHANNEL_EVENT_PROGRESS || pNotify->EventId == CHANNEL_EVENT_PROGRESS_MEDIA) && (pChanHost != __getFirstChan() || pNotify->CallType == VOICE_CALL)) // 呼出振铃 { if (m_RingTime.empty()) m_RingTime = Util::CurTime(); } else if (pNotify->EventId == CHANNEL_EVENT_ANSWER && (pChanHost != __getFirstChan() || pNotify->CallType == VOICE_CALL)) { m_AnswerTime = Util::CurTime(); } else if (pNotify->EventId == CHANNEL_EVENT_HANGUP_COMPLETE && m_IsSaveDb) // 收到挂机事件,且第一次更新数据库(update) { std::string curTime = Util::CurTime(); auto fun = [](std::string time)->std::string { if (time.empty()) return "null"; else return "'" + time + "'"; }; // 匿名函数用于转化时间 m_RingTime = fun(m_RingTime); m_AnswerTime = fun(m_AnswerTime); Format fmt("update auto_cdr set callee = '%s', ring_time = %s,answer_time = %s,end_time = '%s',record_path = '%s', hangup_cause = '%s' where uuid = '%s'"); if (pNotify->CallType == VOICE_CALL) // 语音外呼被叫 接通挂机时取 caller fmt %pNotify->Caller % m_RingTime %m_AnswerTime %curTime %m_RecordFile %pNotify->HangupCause %pChanHost->sessionId(); else fmt %pNotify->Callee % m_RingTime %m_AnswerTime %curTime %m_RecordFile %pNotify->HangupCause %pChanHost->sessionId(); SqlWrite::GetInstance()->addSql(fmt.str()); // 清空 m_RingTime.clear(); m_AnswerTime.clear(); m_IsSaveDb = false; } } void Session::__onChanFree(VirtualChan * pChan, PCHAN_EVENT_NOTIFY pNotify) { //if (m_Id == pNotify->ChanId) // 若会话主通道空闲则清空会话 // __clearChan(); //else // __delChan(pChan); __delChan(pChan); // 防止重复收到挂机事件覆盖之前的有效数据 if (pChan->isVoid()) // 若通道已被丢弃,则删除 m_pParent->delChan(pChan); } void Session::__onPlayAgentNo(VirtualChan * pChan, PCHAN_EVENT_NOTIFY pNotify) { if (pChan == nullptr) { LOG_ERROR_S("通道pChan已被释放!!!"); return; } if(pNotify == nullptr) { LOG_ERROR_S("通道事件pNotify已被释放!!!"); return; } // 事件为接听,通道为分机,线路数为2, 对端为外线,进行播报工号 if (pNotify->EventId != CHANNEL_EVENT_ANSWER || pChan->isInMeeting() || m_ListChan.size() != 2) { //LOG_DEBUG("调用播报工号 %d %d %d ", pNotify->EventId, pChan->isInMeeting(), m_ListChan.size()); return; } VirtualChan* pFirstChan = __getFirstChan(); if (pFirstChan != nullptr && pFirstChan->type() == DEV_RES_TYPE_TRUNK && pChan->type() == DEV_RES_TYPE_EXT) { m_pParent->playAgentNo(pChan); } } void Session::__onRecord(VirtualChan * pChan, PCHAN_EVENT_NOTIFY pNotify) { /* if (pChan == __getFirstChan() && pNotify->EventId == CHANNEL_EVENT_ANSWER) { std::string Number; Number = pNotify->Caller; // 判断是否需要录音 std::string sql = boost::str(Format("select id from record where caller = '%s'") % Number); JdbcHelper::GetInstance()->jdbc_executeQuery(sql, [=](sql::PreparedStatement* stmt) { }, [=](sql::ResultSet* result) { if (!result->next()) // 数据库查不到记录,进行录音 { LOG_DEBUG("操作标识:%ld", pNotify->ChanOpInstance); //if (pNotify->ChanOpInstance == 0) // 会议不进行再次录音 if(CConfig::GetInstance()->isAutoRecord()) COperationReactor::GetInstance()->procOperation(-1, LINE_OP_RECORD, pChan->no(), nullptr); // 执行录音 } }, NULL); } */ // 保持状态过滤 auto Status = (HELD_STATE_FILTER_MASK & pChan->state()); // 通话 if (Status == TRUNK_STATE_TALKING || Status == INNER_STATE_TALKING) { for (auto pTempChan : m_ListChan) { if (pTempChan == NULL || pTempChan == pChan) continue; auto nTempStatus = (HELD_STATE_FILTER_MASK & pTempChan->state()); if (nTempStatus == TRUNK_STATE_TALKING || nTempStatus == INNER_STATE_TALKING) { __onLineTalking(pChan); __onLineTalking(pTempChan); break; } } } } void Session::__createFileName(VirtualChan * pChan) { if (m_RecordFile.empty() || m_RecordFile == "") { std::string strCurTime = Util::CurTime(); strCurTime = std::regex_replace(strCurTime, std::regex("-|:"), ""); auto index = strCurTime.find(" "); std::string data = strCurTime.substr(0, index); std::string time = strCurTime.substr(index+1); std::string strTempCallID = regex_replace(m_Id, std::regex("-"), ""); strTempCallID = time + "_" + strTempCallID; m_RecordFile = CConfig::GetInstance()->recordPath() + "/" + data + "/"; // 判断文件夹是否存在 boost::filesystem::path p(m_RecordFile); if (!boost::filesystem::exists(p)) { boost::filesystem::create_directories(p); } std::stringstream ss; ss << m_RecordFile << pChan->no() << "/" << strTempCallID << ".wav"; m_RecordFile = ss.str(); } } void Session::__onLineTalking(VirtualChan * pChan) { // 如果是监听 不再产生录音文件文件路径 //if (pChan->currOp() == PDU_CMD_AGENT_MONI_LISTEN) return; // 对该线路进行录音处理 if (pChan == NULL) return; if (pChan->type() != DEV_RES_TYPE_EXT) return; if (pChan->recordFile() != "") return ; if (m_RecordFile.empty() || m_RecordFile == "") { __createFileName(pChan); LineOpParam LineOpInfo; memset(&LineOpInfo, 0, sizeof(LineOpInfo)); LineOpInfo.szParam3 = m_RecordFile; //COperationReactor::GetInstance()->procOperation(-1, LINE_OP_RECORD, pChan->no(), &LineOpInfo); // 执行录音 pChan->setRecordFile(m_RecordFile); for (auto pTempChan : m_ListChan) { if (pTempChan != NULL && pTempChan->type() != pChan->type() && pTempChan->recordFile() == "") pTempChan->setRecordFile(m_RecordFile); } } else { pChan->setRecordFile(m_RecordFile); } }