- 坐席和坐席组长自己登陆也可以打
(1) 坐席组长登陆 -----> 进入坐席---->我的客户 (如果有客户数据可以直接点击呼叫进行拨打,没有可以进行新增)
(2) (1) ----> 点击查看 ------> 页面上有两处电话图标可以进行点击拨打。
-
前端请求接口: http://192.168.134.133:3000/api/agentindesk/agentCTI
请求方式:post
后端controllers
路径:controllers/agentindesk.js方法: Agentindesk.prototype.postAgentCTI = function(req, res, next) {
_this.common.redisClient.hget(guest.realm + ':C:AGENT:' + guest.agentNo + ':H', "destNum", function(err, results) {
var cti = require('../lib/dialer')(guest);
cti.callCustomer(req, res, next);
cti.callBack(req, res, next);
var cti = require('../sapi/cti')(guest);
cti.agent(req, res, next);
}
}lib/dialer.js 相关的模块 Dialer.prototype.callCustomer = function(req, res, next) { 涉及到的表:customers 以及对应的models的findById(). } 发起呼叫接口 - 双呼 Dialer.prototype.callBack = function(req, res, next) { common.redisClient.hgetall(req.params.realm + ':C:H', function(rde, rdres) { 涉及的表:client 以及对应的models的list common.redisClient.hget(req.params.realm + ':APILIMIT:' + common.moment().format('YYYYMMDD') + ':H', "call", function(totalcall_err, totalcall_res) { } //检查主叫是否为坐席 common.redisClient.exists(req.params.realm + ':C:AGENT:' + req.body.caller + ':H', function(isagent_err, isagent_res) { } //对各种通话权限的判断 common.redisClient.rpush(req.params.realm + ':DIALER:L', JSON.stringify(json)); common.redisClient.hincrby(req.params.realm + ':APILIMIT:' + common.moment().format('YYYYMMDD') + ':H', 'call', 1); common.redisClient.expire(req.params.realm + ':APILIMIT:' + common.moment().format('YYYYMMDD') + ':H', xes); } }
//*******************************************************************************************************************
相关的 /sapi/cti.js
CTI.prototype.agent = function (req, res, next) {
common.redisClient.hget(req.params.realm+":C:H","timezone",function(err,tz){
_this.agentAction(req,tz,function(err,ares){
});
});
}
CTI.prototype.agentAction = function(req,tz,callback){
}
拨号盘相关的表:project_customers_contacts.json, project_customers.json
入参:
{ action: 'dial',
customer_id: '5a7929c61611889d053fcc0b',
hideContact: true,
phoneField: '电话号码',
calleeShowNum: '02100000000',
agentGroupNo: '83901',
userField: '{"project_id":"58afa2554e6b565b30997032", "campaign_id":"58b51f27ffe0be7e1db2a31f", "customer_id":"5a7929c61611889d053fcc0b"}'
}
相关的redis:
- key: :C:AGENT:
数据结构H: state
deviceType
destNum
sipPass
webPass
loginPass
customCallerId
agentId
requireSignin
displayName
clickDialOnly
depId - key C:H (订单相关的)
数据结构:
1) timezone
3) language
5) interTimeout
7) exterTimeout
9) name
11) token
13) customCDR
15) cdrEvents
17) callEvents
19) agentEvents
21) monitorInbound
23) monitorOutbound
25) monitorInternal
27) monitorFormat
29) monitorParttern
31) asr
33) asrCallback
35) callerIdPolicy
37) client_id
39) email_server_id
41) sms_profile_id
43) restrictCallerId
45) parentRealm
3. keys ':APILIMIT:'
数据结构:
4. keys ':DIALER:L'
数据结构:
{
"type": "call",
"caller": "10001",
"callee": "15321306404",
"callerShowNum": "15321306404",
"calleeShowNum": "02100000000",
"account": "10001",
"userField": "{\"project_id\":\"5a3cdfedbe680a7c75ae0849\", \"campaign_id\":\"5a5dc4250be91c8c67f1d493\", \"customer_id\":\"5a6ac86a7474053b4f329dc8\"}",
"callback": "",
"hideContact": true,
"agentGroupNo": "1001",
"sessionId": "_180153bd7fb1e0b3a271cb97bba4054c140e"
}
-
请求地址: 192.168.134.133:3000/api/agents/roles?filter=%7B%7D&page=0&pageSize=0
请求方式:get
描述:坐席-----> 管理界面---(坐席组长下的坐席)后端controllers
pass: controllers/agents.js接口功能描述:如果是client,可以看到其下所有座席;如果是座席组长,可以看到座席组长下所有座席。
方法: Agents.prototype.list = function(req, res, next, obj) {}
调用了models.js 里面的list方法。
入参:{ controller: 'agents' }
-
请求地址:http://192.168.134.133:3000/api/profiles/ranking
请求方式:get
描述:坐席---> 联系客户-----(下面第一个下拉列表的选项)后端controllers
pass: controllers/profiles/profiles.js
方法: Profiles.prototype.getCampaigns = function(callback) {}
涉及的表: agents, agent_groups, campaigns
参数:无
- 请求地址:http://192.168.134.133:3000/api/profiles/agents/507f1f77bcf86cd799439108
请求方式:get
描述: 坐席----> 管理界面-----(坐席以及坐席组长的profiles)
方法:Profiles.prototype.searchInformation = function(req, res, next) {}
涉及的表:agents,accounts
- 请求地址: http://192.168.134.133:3000/api/profiles/agents/507f1f77bcf86cd799439108
请求方式: put
描述: 坐席------> 管理界面--------> 坐席简介-------> 确认更改
方法:Profiles.prototype.updateInformation = function(req, res, next) {}
涉及的表: agents , accounts
入参:
-
请求地址: http://192.168.134.133:3000/api/agentindesk/58b51f27ffe0be7e1db2a31f/private?filter=%7B%22campaigns.campaign_id%22:%7B%22logic%22:%22%3D%22,%22value%22:%2258b51f27ffe0be7e1db2a31f%22%7D,%22currentStat.callresult_id%22:%7B%22logic%22:%22%3D%22,%22value%22:%22%22%7D%7D&page=0&pageSize=15&sort=%7B%22campaigns.updatedAt%22:1%7D
请求方式:get
描述: 坐席------> 联系客户------>表格内客户的数据
方法: Agentindesk.prototype.listPrivate = function(req, res, next){}
涉及到的表: campaigns, project_customers_***************,customers
入参: filter: '{"campaigns.campaign_id":
{"logic":"=","value":"58b51f27ffe0be7e1db2a31f"},
"currentStat.callresult_id":{"logic":"=","value":""}}',
page: '0',
pageSize: '15',
sort: '{"campaigns.updatedAt":1}' -
请求地址: http://192.168.134.133:3000/api/agentindesk/customerSchema?campaign_id=58b51f27ffe0be7e1db2a31f
请求方式:get
描述:坐席------> 联系客户------>表格的 theader (获取客户表字段属性)
方法: Agentindesk.prototype.listCustomerSchema = function (req, res, next) {}
涉及到的表:campaigns,project_customers_***************,customers.
入参:campaign_id
请求地址: http://192.168.134.133:3000/api/agentindesk/callResults?filter=%7B%7D&page=0&pageSize=0&sort=%7B%22value%22:1%7D
请求方式:get
描述:坐席-----> 联系客户-------> 呼叫结果---(呼叫结果选项的下拉列表)
请求方式:get
方法:Agentindesk.prototype.listCallResults = function(req, res, next){
操作的表: callresults
入参: {
controller: 'agentindesk',
id: 'callResults'
}
-
请求地址:http://192.168.134.133:3000/api/agentindesk/customers
请求方式:post
描述:坐席---------> 联系客户------(新增客户)
方法:Agentindesk.prototype.createCustomer = function (req, res, next) {}
**注释:检查该计划是否允许添加客户
操作的表:campaigns, project_customers_***********,customers
入参: { '姓名': '',
'电话号码': '111111111',
'电子邮件': '',
'性别': '',
'年龄': '',
'城市': '',
'区域': '',
'备注': '',
campaign_id: '58b51f27ffe0be7e1db2a31f'
} -
请求地址:http://192.168.134.133:3000/api/agentindesk/58b51f27ffe0be7e1db2a31f/random?campaign_id=58b51f27ffe0be7e1db2a31f&pageSize=15&project_id=58afa2554e6b565b30997032
请求方式:get
描述: 坐席---------> 联系客户------抢 (接口)
方法: Agentindesk.prototype.listRandom = function(req, res, next){}
后端方法的描述:随机列出指定任务的公海数据
操作的表: campaigns, project_customers_****************,customers
入参:{ controller: 'agentindesk',
id: '58b51f27ffe0be7e1db2a31f',
resource: 'random'
}
12 请求地址: http://192.168.134.133:3000/api/agentindesk/58b51f27ffe0be7e1db2a31f/random?campaign_id=58b51f27ffe0be7e1db2a31f&pageSize=15&project_id=58afa2554e6b565b30997032
请求方式:get
描述:坐席-------> 联系客户-------换一批 (接口)
方法: Agentindesk.prototype.listRandom = function(req, res, next){}
后端方法的描述:随机列出指定任务的公海数据
操作的表: campaigns, project_customers_****************,customers
入参:{ controller: 'agentindesk',
id: '58b51f27ffe0be7e1db2a31f',
resource: 'random'
}
13. 请求地址: http://192.168.134.133:3000/api/agentindesk/58b51f27ffe0be7e1db2a31f/private?filter=%7B%22campaigns.campaign_id%22:%7B%22logic%22:%22%3D%22,%22value%22:%2258b51f27ffe0be7e1db2a31f%22%7D,%22campaigns.appointment%22:%7B%22from%22:%222018-02-08+00:00:00%22,%22to%22:%222018-02-08+23:59:59%22%7D,%22currentStat.callresult_id%22:%7B%22logic%22:%22%3D%22,%22value%22:%22%22%7D%7D&page=0&pageSize=15&sort=%7B%22campaigns.appointment%22:1%7D
请求方式:get
描述:坐席-----> 联系客户-------> (左边列表最后一个选项)我的预约
方法: Agentindesk.prototype.listPrivate = function(req, res, next){}
操作的表: campaigns, project_customers_***************,
入参: {
page: 0,
pageSize: 15,
data: [],
total: 0,
totalPages: 0
}
14. 拨打电话相关最重要的三个接口:
(1) http://192.168.134.133:3000/api/agentindesk/renewPolling
(2) http://192.168.134.133:3000/api/agentindesk/agentCTI
(3) http://192.168.134.133:3000/api/agentindesk/checkQuota?campaign_id=58b51f27ffe0be7e1db2a31f
1. 前端功能描述:坐席-------->联系客户---->呼叫
(1)前端
路径: app/components/agentdesk/.
前端页面: agentdesk.html
主要HTML代码:
<a ng-click="popCustomer('dial', drow);" href="#" class="btn default btn-xs green" ng-show="curNavLi != 'public' && curNavLi != 'campaignPublic'"><i class="fa fa-phone"></i> 呼叫 </a>
主要调用的js: agentdesk.js
调用js的方法:
$scope.popCustomer = function (type, info) {}
参数结构:type = "dial";
info = {
_id
accounts
callStat
callresults
campaigns
comments
created
created_by_id
created_by_username
dialListStat
dialerStat
isInPDList
sale_funnels
tag
updated
区域
城市
备注
姓名
年龄
性别
电子邮件
电话号码
}
内部判断逻辑:1.1 对拨打条件进行判定.
客户已质检,不能拨打电话,判断条件qced = "yes"
客户已成功提交,不能拨打电话,判断条件:sales_funnel_value > 0
客户已失败提交,不能拨打电话,判定条件:sales_funnel_value < 0
1.2 检查配额
前端的请求接口:
clusterccHttp.post('agentindesk/agentCTI', params).success(function (results, status, headers, config) {}
返回参数对各种失败信息做判定:
1.1 正在拨号,请稍后...
1.2 拨号失败
1.3 拨号申请中,请稍后...
1.4 拨号失败
(2) 后端
请求方式:post
后端controllers
路径:controllers/agentindesk.js
方法: Agentindesk.prototype.postAgentCTI = function(req, res, next) {
_this.common.redisClient.hget(guest.realm + ':C:AGENT:' + guest.agentNo + ':H', "destNum", function(err, results) {
var cti = require('../lib/dialer')(guest);
cti.callCustomer(req, res, next);
cti.callBack(req, res, next);
var cti = require('../sapi/cti')(guest);
cti.agent(req, res, next);
}
}
lib/dialer.js 相关的模块
Dialer.prototype.callCustomer = function(req, res, next) {
涉及到的表:customers
以及对应的models的findById().
}
发起呼叫接口 - 双呼
Dialer.prototype.callBack = function(req, res, next) {
common.redisClient.hgetall(req.params.realm + ':C:H', function(rde, rdres) {
涉及的表:client
以及对应的models的list
common.redisClient.hget(req.params.realm + ':APILIMIT:' + common.moment().format('YYYYMMDD') + ':H', "call", function(totalcall_err, totalcall_res) {
}
//检查主叫是否为坐席
common.redisClient.exists(req.params.realm + ':C:AGENT:' + req.body.caller + ':H', function(isagent_err, isagent_res) {
}
//对各种通话权限的判断
common.redisClient.rpush(req.params.realm + ':DIALER:L', JSON.stringify(json));
common.redisClient.hincrby(req.params.realm + ':APILIMIT:' + common.moment().format('YYYYMMDD') + ':H', 'call', 1);
common.redisClient.expire(req.params.realm + ':APILIMIT:' + common.moment().format('YYYYMMDD') + ':H', xes);
}
}
//*******************************************************************************************************************
相关的 /sapi/cti.js
CTI.prototype.agent = function (req, res, next) {
common.redisClient.hget(req.params.realm+":C:H","timezone",function(err,tz){
_this.agentAction(req,tz,function(err,ares){
});
});
}
CTI.prototype.agentAction = function(req,tz,callback){
}
拨号盘相关的表:project_customers_contacts.json, project_customers.json
入参:
{ action: 'dial',
customer_id: '5a7929c61611889d053fcc0b',
hideContact: true,
phoneField: '电话号码',
calleeShowNum: '02100000000',
agentGroupNo: '83901',
userField: '{"project_id":"58afa2554e6b565b30997032", "campaign_id":"58b51f27ffe0be7e1db2a31f", "customer_id":"5a7929c61611889d053fcc0b"}'
}
相关的redis:
1. key: *:C:AGENT:*
数据结构H: state
deviceType
destNum
sipPass
webPass
loginPass
customCallerId
agentId
requireSignin
displayName
clickDialOnly
depId
2. key *C:H* (订单相关的)
数据结构:
1) timezone
3) language
5) interTimeout
7) exterTimeout
9) name
11) token
13) customCDR
15) cdrEvents
17) callEvents
19) agentEvents
21) monitorInbound
23) monitorOutbound
25) monitorInternal
27) monitorFormat
29) monitorParttern
31) asr
33) asrCallback
35) callerIdPolicy
37) client_id
39) email_server_id
41) sms_profile_id
43) restrictCallerId
45) parentRealm
3. keys ':APILIMIT:'
数据结构:
4. keys ':DIALER:L'
数据结构:
{
"type": "call",
"caller": "10001",
"callee": "15321306404",
"callerShowNum": "15321306404",
"calleeShowNum": "02100000000",
"account": "10001",
"userField": "{\"project_id\":\"5a3cdfedbe680a7c75ae0849\", \"campaign_id\":\"5a5dc4250be91c8c67f1d493\", \"customer_id\":\"5a6ac86a7474053b4f329dc8\"}",
"callback": "",
"hideContact": true,
"agentGroupNo": "1001",
"sessionId": "_180153bd7fb1e0b3a271cb97bba4054c140e"
}
2. 功能描述:坐席-------->管理界面--->个人信息
(1) 前端
路径: app/components/profiles/profiles.js
前端页面: profiles.html
前端js方法:
clusterccHttp.get("profiles/" + type + "/" + $window.localStorage._id){}
传参: (1) type = agents
(2) $window.localStorage._id 507f1f77bcf86cd799439108
(2) 后端
请求方式:get
描述: 坐席----> 管理界面-----(坐席以及坐席组长的profiles)
方法:Profiles.prototype.searchInformation = function(req, res, next) {}
涉及的表:agents,accounts
3. 功能描述:坐席-------->管理界面--->坐席数据
(1) 前端
路径: app/components/profiles/profiles.js
前端页面: profiles.html
2,3 HTML代码片段
<div class="profile-usermenu">
<ul class="nav">
<li ng-class="{active: PageLocation=='Dashboard'}" ng-click="PageLocation='Dashboard'" ng-show="isAgent == 'yes'">
<a href="#">
<i class="icon-home"></i>
{{'Agent Data' | translate}}
<span ng-if="PageLocation=='Dashboard'" class="btn-default" style="background:transparent" ng-click="fetchRanking()">
<i class="fa fa-refresh"></i>
</span>
</a>
</li>
<li ng-class="{active: PageLocation=='Agent'}" ng-click="PageLocation='Agent'" ng-show="isAgent == 'yes'">
<a href="#" >
<i class="icon-settings"></i>
{{'Personal Information' | translate}}</a>
</li>
<li ng-class="{active: PageLocation=='Account'}" ng-click="PageLocation='Account'" ng-hide="true" ng-if="(!isAgent || isAgent == 'no')">
<a href="#">
<i class="icon-settings"></i>
{{'Personal Information' | translate}}</a>
</li>
</ul>
</div>
调用方法:fetchRanking = function(){}
调用后端接口的代码: clusterccHttp.get('profiles/ranking').success(function(data, status, headers, config) {}
请求方式:get
(2)后端
路径: controllers/profiles/profiles.js
方法: Profiles.prototype.getCampaigns = function(callback) {}
涉及的表: agents, agent_groups, campaigns
参数:无
3.1 排行榜内的数据,和3 使用的接口相同
campaigns:
58b51f27ffe0be7e1db2a31f:"Demo Campaign 1"
58b51f27ffe0be7e1db2a320:"Demo Campaign 2"
客户弹屏:
function popCustomerFun($rootScope, $scope, $translate, clusterccHttp, $window, $injector, $compile, info) {
}
data 数据结构:
{
"姓名":"sym",
"电话号码":"13856421235",
"电子邮件":"1171@qq.com",
"性别":"男",
"年龄":"25",
"城市":"北京",
"区域":"",
"备注":"",
"campaigns":[
{
"agentNo":"1007",
"agentName":"HoegerMarlon",
"agent_id":"507f1f77bcf86cd799439108",
"campaign_id":"58b51f27ffe0be7e1db2a31f",
"sessionId":"",
"sales_funnel_value":0,
"qced":"no"
}
],
"dialerStat":{
"totalDialedNum":0
},
"callStat":{
"called":0,
"answered":0
},
"dialListStat":{
"isInPDList":false,
"priority":0,
"scheduleAt":""
},
"isInPDList":false,
"comments":[
],
"tag":[
],
"created_by_username":"1007",
"created_by_id":"507f1f77bcf86cd799439108",
"created":"2018-02-11T07:01:03.000Z",
"updated":"2018-02-11T07:01:03.000Z",
"_id":"5a7fea2f17ca740000e08c1e"
}
{
"_id" : ObjectId("5a8166f7253706f3c3c95d29"),
"project_customer_id" : ObjectId("5a8166f7253706f3c3c95d28"),
"project_id" : ObjectId("58afa2554e6b565b30997032"),
"campaign_id" : ObjectId("58b51f27ffe0be7e1db2a31f"),
"schedule_time" : "",
"agentgroupno" : "",
"agentno" : "1007",
"mobile" : "566554645566",
"city_id" : "",
"area_id" : "",
"level_id" : "",
"survey_answer_id" : "",
"successed_type" : 0,
"data_from" : 0,
"bigdata_batch_no" : "",
"tag" : [],
"bale_time" : "",
"is_diallist" : 0,
"project_customer_contact_id" : "",
"last_sales_funnel_value" : -99,
"last_callresult_id" : "",
"last_agent_id" : "",
"last_agentno" : "",
"last_memo" : "",
"last_contact_create_time" : "",
"last_contact_update_time" : "",
"last_is_qc" : 0,
"last_qc_status" : "",
"last_qc_update_time" : "",
"last_qc_operator_id" : "",
"last_qc_memo" : "",
"operator_id" : ObjectId("507f1f77bcf86cd799439108"),
"operate_type" : 1,
"is_uploadAudio" : 0,
"uploadAudioTimes" : 0,
"create_time" : ISODate("2018-02-12T10:05:43.000Z"),
"update_time" : ISODate("2018-02-12T10:05:43.000Z")
}
分页
需要改的地方:
(1)
data.campaigns.forEach(function (c) {
if (c.campaign_id == campaign_id && c.survey_answer_id) {
survey_answer_id = c.survey_answer_id;
}
});
(2)
if (data.comments.length == 0) {
$(myobj + '#newCommentTabBtn').trigger('click');
} else if (info.callInfo) {
var lastCommentData = data.comments[data.comments.length - 1];
var cursessionId = (typeof(info.callInfo.parentCdrId) != 'undefined' && info.callInfo.parentCdrId != '') ? info.callInfo.parentCdrId : info.callInfo.sessionId;
if (lastCommentData.sessionId == cursessionId) {
autoAdd = 'yes';
$(myobj + '#lastCommentTabBtn').trigger('click');
} else {
$(myobj + '#newCommentTabBtn').trigger('click');
}
} else {
$(myobj + '#lastCommentTabBtn').trigger('click');
}
(3) //项目是否需要质检
if ($scope.campaignObjs[campaign_id] && $scope.campaignObjs[campaign_id].hasOwnProperty("needQC") && $scope.campaignObjs[campaign_id].needQC == true) {
//坐席提交成功客户后,质检前不允许新增联络记录
if (action == 'add' && data.comments.length > 0 && data.comments[data.comments.length - 1].sales_funnel_value > 0 && data.comments[data.comments.length - 1].qced == 'no') {
clustercc.bootBoxAlert('此客户为成功客户,因未质检,目前不允许新增联络记录.');
return;
}
}
(4) var valideResult = $scope.validOperate('view', data); //暂时认为不需要更改
(5) //客户资料字段获取
var fieldsAry = $scope.customerFieldAttrJson[campaign_id].fieldsDeafult;
if (campaign_id != 'pubcustomers') {
fieldsAry = $scope.campaigns[$scope.campaignsAry.indexOf(campaign_id)].fields;
}
(6)
filter:"{"_id":{"logic":"=","value":"-1"},"dialListStat.isInPDList":{"logic":"between","value":[null,null,false]}}"
page:"0"
pageSize:"20"
sort:"{"dialerStat.lastDialAt":-1}"
呼叫结果改为二级结构:
- 坐席组长--联系客户:二级搜索以及界面: 1.5 天
联系客户--详情页: 呼叫结果二级联动搜索以及界面: 1.5 天
-
呼叫结果列表:
呼叫结果管理界面: 1天
大搜索框功能实现: 0.5 天
小搜索框功能实现: 1天
编辑呼叫结果:
编辑呼叫结果界面:1 天
实现后端功能: 1天 -
客户数据页面:
(1) 客户数据:实现列表展示界面。 1
(2) 实现客户数据列表的展示。 1
(3) 实现客户数据大搜索(联动的查询) 1
(4) 实现客户数据小搜索查询. 1
(5) 实现特殊备注联络备注下载。 2 (需要熟悉代码) -
成功提交客户:
(1) 成功提交:实现列表展示界面。 1
(2) 实现成功提交客户数据列表的展示。 1
(3) 实现成功提交客户数据大搜索(联动的查询) 1
(4) 实现成功提交客户数据小搜索查询. 1
(5) 实现成功提交特殊备注联络备注下载。 1
网友评论