#include "Session.h" #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_IsSaveDb(true), m_AtionId(0) { } Session::~Session() { } 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); 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()); } __onPlayAgentNo(pChan, pNotify); // 判断是否报工号 } 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) { auto it = m_ListChan.begin(); while (it != m_ListChan.end()) { if (*it != pChan) return *it; ++it; } return nullptr; } VirtualChan * Session::__getFirstChan() { if(m_ListChan.empty()) return nullptr; return m_ListChan.front(); } VirtualChan * Session::__getChan(string ChanId) { 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) { LOG_DEBUG("通道数量:%d %s,%d",m_ListChan.size(),__FUNCTION__,__LINE__); m_ListChan.emplace_back(pChan); LOG_DEBUG("通道数量:%d %s,%d", m_ListChan.size(), __FUNCTION__, __LINE__); } /***************************************************************** **【函数名称】 __delChan **【函数功能】 删除指定通道 **【参数】 **【返回值】 *****************************************************************/ void Session::__delChan(VirtualChan * pChan) { m_ListChan.remove(pChan); } /***************************************************************** **【函数名称】 __clearChan **【函数功能】 清空通道 **【参数】 **【返回值】 *****************************************************************/ void Session::__clearChan(void) { m_ListChan.clear(); } void Session::__notifySessionChanEvent(VirtualChan * pChanHost, PCHAN_EVENT_NOTIFY pNotify) { 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 (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; // 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); // 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; // 分机外呼 } 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(); } if (pNotify->EventId == CHANNEL_EVENT_HANGUP_COMPLETE && m_IsSaveDb ) // 收到挂机事件,且第一次更新数据库(update) { std::string curTime = Util::CurTime(); std::string agent; std::string callee; // 被叫 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); //std::string callid = pNotify->CcId.empty() == true ? pNotify->CallId : pNotify->CcId; // 获取callid 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 callee_agent = '%s',callee = '%s', ring_time = %s,answer_time = %s,end_time = '%s',record_path = '%s', hangup_cause = '%s' where uuid = '%s' and action_id = %d"); fmt %agent %callee % m_RingTime %m_AnswerTime %curTime %m_RecordFile %pNotify->HangupCause %pChanHost->sessionId() % m_AtionId; SqlWrite::GetInstance()->addSql(fmt.str()); // 清空 m_RingTime.clear(); m_AnswerTime.clear(); m_IsSaveDb = false; } } 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 { Format fmt("insert into auto_cdr(uuid,caller,create_time,task_id) values('%s','%s','%s',%ld)"); // 分机呼叫 call_type=1 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); if (pChan->isVoid()) // 若通道已被丢弃,则删除 m_pParent->delChan(pChan); } void Session::__onPlayAgentNo(VirtualChan * pChan, PCHAN_EVENT_NOTIFY pNotify) { LOG_DEBUG("调用播报工号 %d %d %d %d", pNotify->EventId, pChan->isInMeeting(), m_ListChan.size(), pChan->type()); // 事件为接听,通道为分机,线路数为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); } }