1、为什么使用点击拨号电话,在哪些场景使用?
很久以前由于
互联网监控还没完善
,有歪念的不法分子为了掩盖自己的身份(电话号码)
,从而在某些运营商或者其他渠道进行合作,然后通过这种渠道虚拟电话号码拨打
或者发短信
进行欺骗些没有意识的良民的老百姓财产
。当
网络监控工作逐步完善
之后,国家为了保护老百姓的人身财产权益
,然后慢慢会改善了,然而会慢慢减少短信的欺骗,只要是经过范围内控制,天涯海角都能通过大数据
进行分析找到痕迹
。经过以上两点进行阐述,大概有点明白了吧?回归正传, 点击点击电话回拨场景一般用在是软件开发里面呢?俺就讲述一个亲身经历参与的开发项目案例上面吧。曾经参与一个关于律师的互联网的产品研发,然后这个产品
最终的宗旨为人民解忧
,通过线上律师进行来,解答,咨询,律师函定制,合同服务等(记住律师回答您的问题不是免费,老铁们要收费的呢,人家的知识点可贵了
?而且别人说不定接一单可以三年不干活的呢)制定来为线上用户在生活中有法律纠纷的疑难疑点,比如:房产纠纷,财产纠纷,离婚纠纷,个人利益,劳动合同纠纷等操作问题,然而项目分用户端
和律师端
,还有一个后台管理后台(管理用户或律师)端的发布信息
进行开发。刚刚提到律师
不会很清闲帮你解答
的呢?一个为解决线上用户有法律法规纠纷,二来为了吸引律师来注册到平台里面处理问题,故我们就想办法帮律师创收这方面进行考虑,然后开发了一个呼叫电话功能模块.
这里就涉及律师的隐私问题(手机号码或者微信)
,故就用上了点击拨号电话呼叫API,从而在律师身上抽点水。
2、点击回拨有哪些提供商选择呢?
- 虽然很多国内或国外很企业做这样的中间商,个人建议推荐
华为和阿里云
毕竟大公司靠谱,选择国外的不好监控,而且API国外点也调用也慢
而且华为在通信知名度就不多说了,阿里没用过故不做过多的阐述,相信也做得杠杠的。
华为点击回拨API
阿里云点击回拨API
- 文章这里以华为为例进行展开,详解API请看在线文档
https://support.huaweicloud.com/api-VoiceCall/rtc_05_0002.html
3 准备工作
- 公司要运营商(华为或阿里)公司进行签到合约,找到他们平台负责人进行获取
- 注意:
老证书AK,录音的时候会用到
,在双方签合同的时候必须强调进行签约,否则不签约第二次审批走流程就时间长了
1.所有使用的号码均需要使用+86开头
2.ContentType均为application/json;charset=UTF-8
3.使用https请求
4.正常分配的平台号码均为028的四川地区固话,如需显示特殊号码,请联系移动申请
5.语音通知业务因管控要求,只能使用固定语音文件+TTS形式或纯固定语音文件方式,如有疑问请向合作经理咨询
6.测试账号只分配2个端口,即支持一路通话并发,如需更多端口用于调测,请向移动申请
7.使用的固定放音文件为.wav,CCITT A-Law 8.00 kHz,8位单声道
8.1010001(检查参数是否没使用双引号包裹,检查参数类型是否设置错误Integer直接填数字,不需要引号包裹)
9.1010002(检查参数是否缺失,检查号码是否都使用+86开头)
参数 | 参数说明 |
---|---|
主机Ip | 华为分配的Ip |
APP_KEY | 申请应用标识,在应用创建时由能力开放管理平台分配 |
APP_SECRET | 应用密钥,创建应用时CaaS平台分配给APP_KEY的密钥 |
USER_NAME | 用户名称 |
PASS_WORD | 明文密码, SP在CaaS平台上注册时设置的密码或会议业务码/个人号码的密码 |
PLATFORM_NUMBER | 平台号码 |
TYPE | 账号类型: 1-用户号码 2-SP账号 |
REFRESH_TOKEN | 授权类型 |
AK | 老证书AK,录音的时候会用到,在双方签合同的时候必须强调进行签约,否则不签约第二次审批走流程就时间长了 |
4、大客户SP简单认证API
-
大客户SP简单认证API是提供给合作伙伴的一种简单的认证接口,仅允许批发模式的应用调用。
大客户SP简单认证流程说明:
1、第三方应用接收到用户的登录请求,首先检测平台是否具有合法的用户授权,如果没有得到用户的授权,则要求用户进行认证;
2、第三方应用要求用户输入账号、口令;
3、用户输入认证信息,并且提交;
4、第三方应用携带用户的账号、口令向CaaS平台发起授权请求(授权接口采用HTTPS
通讯,保证帐号口令的安全传输);
5、CaaS平台生成访问Token并返回给第三方应用,第三方应用使用该Token向CaaS平台
发送业务请求。
- 代码实现 ,HttpClient通信工具
- 请参考我之前写的kotlin与java的HttpClient封装http通信工具类文章
/**
* @Description 大客户 SP 简单认证 API,详细请参考华为提供的【点击呼叫API接口文档.pdf】文档指引
* @Author liangjilong
* @Date 2017年6月6日 下午1:42:41
* @return 参数
* @return SimpleVerifyVo 返回类型
* @throws
*/
public static SimpleVerifyVo verifyFastLogin(){
try {
String reqUrl = ClickCallConfigUtil.HOST_URL+"rest/fastlogin/v1.0";
//======请求参数================
Map reqMap = new HashMap();
reqMap.put("app_key", ClickCallConfigUtil.APPKEY);
reqMap.put("username", ClickCallConfigUtil.USERNAME);
reqMap.put("type", ClickCallConfigUtil.TYPE);
String reqParams = HttpClientUtil.getConcatParams(reqMap);
String newReqUrl= reqUrl+"?"+reqParams;//请求链接
Map headerMap = new HashMap();
headerMap.put("Authorization", ClickCallConfigUtil.PASSWORD);//设置密码授权头header属性文件
headerMap.put("Content-Type", GlobalConstants.CONTENT_TYPE);//设置密码授权头header属性文件
//方式请求方法一:
//String retMsg = HttpClientUtil.createHttps(newReqUrl,"POST",headerMap,true,GlobalConstants.SSL_VERSION,null);
//方式请求方法二:
String retMsg = HttpClientUtil.createHttpsPost(newReqUrl, null, GlobalConstants.CONTENT_TYPE, GlobalConstants.UTF8, headerMap);
if(ObjectUtil.isNotEmpty(retMsg) && !"FAIL".equals(retMsg)){
JSONObject jsonObject = JSONObject.fromObject(retMsg);
if(!ObjectUtil.isEmpty(jsonObject)){
return (SimpleVerifyVo)JSONObject.toBean(jsonObject, SimpleVerifyVo.class);
}else{
return null;
}
}else{
return null;
}
} catch (UnsupportedEncodingException e) {
logger.error("@see "+getCurrentClassName()+"#verifyFastLogin,出现异常,异常信息为:"+e.getMessage());
e.printStackTrace();
}
return null;
}
5、 刷新授权API
Access token的有效期不是无限的。当用户授权给第三方应用使用的Access token超过了其生命周期时,可以通过刷新机制来获取新的Access Token。刷新授权API用于根据授权时返回的refresh_token参数获取新的Access Token。
代码实现
/**
* @Description 刷新授权API
* @Author liangjilong
* @Date 2017年6月6日 下午1:46:42 参数
* @return void 返回类型
* @throws
*/
public static SimpleVerifyVo refreshOAuth(){
try {
SimpleVerifyVo simpleVerifyVo = verifyFastLogin();
String reqUrl = ClickCallConfigUtil.HOST_URL+"omp/oauth/refresh";
Map reqMap = new HashMap();
reqMap.put("app_key", ClickCallConfigUtil.APPKEY);
reqMap.put("grant_type", ClickCallConfigUtil.REFRESH_TOKEN);
reqMap.put("app_secret", ClickCallConfigUtil.APP_SECRET);
if(simpleVerifyVo!=null){
//刷新令牌。授权时CaaS平台返回的refresh_token字段的值
String refresh_token = simpleVerifyVo.getRefresh_token();
reqMap.put("refresh_token", refresh_token);
}
String reqParams = HttpClientUtil.getConcatParams(reqMap);
Map headerMap = new HashMap();
headerMap.put("Content-Type", GlobalConstants.CONTENT_TYPE);//设置密码授权头header属性文件
String newReqUrl= reqUrl+"?"+reqParams;//请求链接
String retMsg = HttpClientUtil.createHttps(newReqUrl,"POST",headerMap,true,GlobalConstants.SSL_VERSION,null);
if(ObjectUtil.isNotEmpty(retMsg) && !"FAIL".equals(retMsg)){
JSONObject jsonObject = JSONObject.fromObject(retMsg);
if(!ObjectUtil.isEmpty(jsonObject)){
return (SimpleVerifyVo)JSONObject.toBean(jsonObject, SimpleVerifyVo.class);
}
return null;
}else{
return null;
}
} catch (UnsupportedEncodingException e) {
logger.error("@see "+getCurrentClassName()+"#refreshOAuth,出现异常,异常信息为:"+e.getMessage());
e.printStackTrace();
}
return null;
}
6、取消授权 API
- 开发者调用取消授权API,CaaS平台将回收应用使用者对应用的授权。取消授权后如需再次访问API资源,第三方APP需要重新获得应用使用者的授权。
- 代码实现
/**
* @Description 取消授权 API,返回true表示取消成功,true表示取消失败
*
* @Author liangjilong
* @Date 2017年6月6日 下午1:46:42 参数
* @return void 返回类型
* @throws
*/
public static boolean cancelOAuth(){
try {
SimpleVerifyVo simpleVerifyVo = verifyFastLogin();
String reqUrl = ClickCallConfigUtil.HOST_URL+"rest/logout/v1.0";
Map reqMap = new HashMap();
reqMap.put("app_key", ClickCallConfigUtil.APPKEY);
if(simpleVerifyVo!=null){
//刷新令牌。授权时CaaS平台返回的access_token字段的值
String access_token = simpleVerifyVo.getAccess_token();
reqMap.put("access_token", access_token);
}
String reqParams = HttpClientUtil.getConcatParams(reqMap);
Map headerMap = new HashMap();
headerMap.put("Content-Type", GlobalConstants.CONTENT_TYPE);//设置密码授权头header属性文件
String newReqUrl= reqUrl+"?"+reqParams;//请求链接
String retMsg = HttpClientUtil.createHttps(newReqUrl,"POST",headerMap,true,GlobalConstants.SSL_VERSION,null);
if(ObjectUtil.isNotEmpty(retMsg) && !"FAIL".equals(retMsg)){
JSONObject jsonObject = JSONObject.fromObject(retMsg);
if(jsonObject!=null){
boolean hasResultcode = jsonObject.has("resultcode");
if(hasResultcode){
String resultcode = jsonObject.getString("resultcode");
if(ObjectUtil.isNotEmpty(resultcode) && "0".equals(resultcode)){
return true; //取消成功
}
}else{
return false;//取消失败
}
}
}else{
return false; //这里表示不成功,因为华为那边只能一分钟请求一次获取授权登录
}
} catch (UnsupportedEncodingException e) {
logger.error("@see "+getCurrentClassName()+"#cancelOAuth,出现异常,异常信息为:"+e.getMessage());
e.printStackTrace();
}
return false;
}
7、刷新访问 OceanStor
- 本API用于SP向系统请求重新生成录音文件的下载证书。
刷新后,旧证书3天(OMU上配置)后失效。旧证书没有失效,再次刷新,返回刷新频繁错误码。 - 代码实现
/**
* @Description 刷新访问 OceanStor 的证书 ,此接口语音会用到的Key
* @Author liangjilong
* @Date 2017年6月6日 下午1:46:42 参数
* @return void 返回类型
* @throws
*/
public static void refreshKey(String access_token){
try {
String reqUrl = ClickCallConfigUtil.HOST_URL+"rest/refreshkey/v2.0";
Map reqMap = new HashMap();
reqMap.put("app_key", ClickCallConfigUtil.APPKEY);
if(access_token!=null){
//刷新令牌。授权时CaaS平台返回的access_token字段的值
reqMap.put("access_token", access_token);
}else{
SimpleVerifyVo simpleVerifyVo = verifyFastLogin();
if(ObjectUtil.isNotEmpty(simpleVerifyVo)){
access_token = simpleVerifyVo.getAccess_token();
reqMap.put("access_token", access_token);
}
}
reqMap.put("AK", ClickCallConfigUtil.AK);
String reqParams = HttpClientUtil.getConcatParams(reqMap);
Map headerMap = new HashMap();
headerMap.put("Content-Type", GlobalConstants.CONTENT_TYPE);//设置密码授权头header属性文件
String newReqUrl= reqUrl+"?"+reqParams;//请求链接
String retMsg = HttpClientUtil.createHttps(newReqUrl,"POST",headerMap,true,GlobalConstants.SSL_VERSION,null);
if(ObjectUtil.isNotEmpty(retMsg) && !"FAIL".equals(retMsg)){
}else{
}
} catch (UnsupportedEncodingException e) {
logger.error("@see "+getCurrentClassName()+"#refreshKey,出现异常,异常信息为:"+e.getMessage());
e.printStackTrace();
}
}
8、点击呼叫场景 API
-
点击呼叫是同时将主叫号码和被叫号码通过IP网络发送给服务器,由服务器呼叫主叫和被叫,使主叫和被叫能够互相通话。
- 点击呼叫业务体验描述:
用户A想要让用户B和用户C通话。一般情况下,用户A会是用户B和用户C的某一个人。
- 用户A向CaaS平台发送点击拨号业务请求。
- CaaS平台呼叫用户B的号码。
- 用户B接听,并听到提示音。
- CaaS平台呼叫用户C的号码。
- 用户C接听。
- 用户B和用户C通话。
- 应用场景,电话咨询
某用户在浏览某培训机构的网页或APP,对培训内容很感兴趣。此用户则在此网页或APP输入自己的电话号码,点联系客服。培训机构的系统就会调用CaaS平台的点击拨号接口,CaaS平台先呼叫某客服,接通后再呼叫此用户,就此用户和客服建立了通话。
场景价值:
1、双方均作为被叫,可为其省去电话费用,费用由企业支付。
2、可使用企业号码作为主显号码,屏蔽双方个人的真实号码,保护个人号码不泄露。
3、企业认为重要的电话,可在接口中指示对通话进行录音,作为业务存档资料
- 代码实现
/**
* @Description 点击呼叫场景API
* @Author liangjilong
* @Date 2017年6月7日 上午10:16:52
* @param reqBodyMap 请求body参数
* @return 参数
* @return ClickCall 返回类型
* @throws
*/
public synchronized static ClickCallVo clickCall(Map reqBodyMap,String access_token){
try {
//商用环境
String reqUrl = ClickCallConfigUtil.HOST_URL+"rest/httpsessions/click2Call/v2.0";
//测试环境
//String reqUrl = ClickCallConfigUtil.HOST_URL+"sandbox/rest/httpsessions/click2Call/v2.0";
Map reqMap = new HashMap();
reqMap.put("app_key", ClickCallConfigUtil.APPKEY);
reqMap.put("access_token", access_token);
String reqParams = HttpClientUtil.getConcatParams(reqMap);
Map headerMap = setJsonContentType();
String bodyParam = JSONUtil.mapToJson(reqBodyMap);//请求body参数
String newReqUrl= java.net.URLDecoder.decode(reqUrl+"?"+reqParams,"utf-8"); ;//请求链接
String retMsg = HttpClientUtil.createHttps(newReqUrl,"POST",headerMap,true,GlobalConstants.SSL_VERSION,bodyParam);
if(ObjectUtil.isNotEmpty(retMsg) && !"FAIL".equals(retMsg)){
JSONObject jsonObject = JSONObject.fromObject(retMsg);
ClickCallVo clickCall = null ;
if(!ObjectUtil.isEmpty(jsonObject)){
clickCall = (ClickCallVo)JSONObject.toBean(jsonObject, ClickCallVo.class);
}
return clickCall;
}else{
return null;
}
} catch (Exception e) {
logger.error("@see "+getCurrentClassName()+"#clickCall,出现异常,异常信息为:"+e.getMessage());
e.printStackTrace();
}
return null;
}
9、 呼叫终止场景 API
- 呼叫终止API就是在点击类业务执行的过程中终止业务。比如终止正在进行的点击拨号。如果业务已经执行完毕,呼叫终止API会返回失败。
- 呼叫终止体验描述:
用户A想要终止正在进行的点击拨号业务。
1、用户A向Caas平台发送终止呼叫请求;
2、Caas平台会分别将B、C用户都挂机。
应用场景
终止整个会话呼叫,可以和点击呼叫和语音验证码联合使用
- 代码实现
/**
* @Description 呼叫终止场景API,此接口必须是要已经在线才可以停止
* @Author liangjilong
* @Date 2017年6月7日 上午10:21:10
* @param reqBodyMap 请求body参数
* @return void 返回类型
* @throws
*/
public static boolean callStop(String sessionId,String access_token){
try {
String reqUrl = ClickCallConfigUtil.HOST_URL+"rest/httpsessions/callStop/v2.0";//商用环境
Map reqMap = new HashMap();
reqMap.put("app_key", ClickCallConfigUtil.APPKEY);
reqMap.put("access_token", access_token);
Map callStopReqBodyMap = new HashMap();
callStopReqBodyMap.put("sessionid", sessionId);//使用点击呼叫场景API或语音验 证码场景API时返回的sessionid值。
callStopReqBodyMap.put("signal", "call_stop");//signal的值固定为"call_stop", 表示主被叫都拆线
Map headerMap = setJsonContentType();
String reqParams = HttpClientUtil.getConcatParams(reqMap);
String newReqUrl= java.net.URLDecoder.decode(reqUrl+"?"+reqParams,"utf-8"); ;//请求链接
//请求body参数
String bodyParam = JSONUtil.mapToJson(callStopReqBodyMap);
String retMsg = HttpClientUtil.createHttps(newReqUrl,"POST",headerMap,true,GlobalConstants.SSL_VERSION,bodyParam);
if(ObjectUtil.isNotEmpty(retMsg) && !"FAIL".equals(retMsg)){
JSONObject jsonObject =JSONObject.fromObject(retMsg);
if(jsonObject!=null){
boolean hasReturnCode = jsonObject.has("returnCode");
if(hasReturnCode){
String returnCode = jsonObject.getString("returnCode");
if(ObjectUtil.isNotEmpty(returnCode) && "0".equals(returnCode)){
return true;
}
}
}else{
return false;
}
}else{
return false;
}
} catch (UnsupportedEncodingException e) {
logger.error("@see "+getCurrentClassName()+"#callStop,出现异常,异常信息为:"+e.getMessage());
e.printStackTrace();
}
return false;
}
10、查询呼叫端口使用情况
- 用于查询APPKEY对应的呼叫最大端口数和空闲端口数
- 代码实现
/**
* @TODO 华为接口返回 Inner error
* @Description 查询呼叫端口使用情况
* @Author liangjilong
* @Date 2017年6月6日 下午1:55:39 参数
* @return void 返回类型
* @throws
*/
public static void queryCallUseVoicePort(String access_token){
try {
String reqUrl = ClickCallConfigUtil.HOST_URL+"rest/provision/call/voice/port/v1.0";//商用环境
Map reqMap = new HashMap();
reqMap.put("app_key", ClickCallConfigUtil.APPKEY);
//刷新令牌。授权时CaaS平台返回的access_token字段的值
reqMap.put("access_token", access_token);
Map headerMap = setJsonContentType();
String reqParams = HttpClientUtil.getConcatParams(reqMap);
String newReqUrl= java.net.URLDecoder.decode(reqUrl+"?"+reqParams,"utf-8"); ;//请求链接
String retMsg = HttpClientUtil.createHttps(newReqUrl,"POST",headerMap,true,GlobalConstants.SSL_VERSION,null);
if(ObjectUtil.isNotEmpty(retMsg) && !"FAIL".equals(retMsg)){
JSONObject jsonObject =JSONObject.fromObject(retMsg);
}else{
}
} catch (UnsupportedEncodingException e) {
logger.error("queryCallUseVoicePort");
e.printStackTrace();
}
}
11、点击回拨的录入下载
- 在某种场景下,录音是非常重要的一个文件,比如:律师与用户的对话,当用户在交流过程中,用户听不太清楚律师的通话的时候,语音就显得尤其为重要,这时,用户就可以在软件App里面在线反复听音频。
- 代码实现
/**
*
* @Description 华为点击回拨的录入下载是调用amazon(亚马逊)https://aws.amazon.com/releasenotes/4473256057156237
* 下载sdk请参考官方文档,地址为:Download the latest AWS SDK for Java
*
* @Author liangjilong
* @Email [email protected]
* @Date 2017年8月1日 上午11:00:47
* @param fileName 录音文件名(S3存储对象)
* @param buckName 录音文件名所在的目录名(S3存储桶名)
* @return 参数
* @return String 返回类型
*/
public static String [] downRecordFile(String fileName,String buckName ){
try {
String filePath = PropertiesLoader.getProperty("HuaWei.recordPath");
//不存在就创建目录.
IoUtils.createDirectory(filePath);
String appKey = PropertiesLoader.getProperty("HuaWei.AK");//获取AppKey
String appSecret = PropertiesLoader.getProperty("HuaWei.SK");//获取AppSecret
String urlPort = PropertiesLoader.getProperty("HuaWei.RecordUrl");//获取录入URl
//String buckName = PropertiesLoader.getProperty("HuaWei.buckName");//存储空间名称
//创建亚马逊AWSCredentials对象
AWSCredentials credentials = new BasicAWSCredentials(appKey, appSecret);
//创建亚马逊AmazonS3对象
AmazonS3 amazonS3 = new AmazonS3Client(credentials);
amazonS3.setEndpoint(urlPort);//调用录入接口地址
//建立亚马孙http请求
GeneratePresignedUrlRequest httpRequest = new GeneratePresignedUrlRequest(buckName, fileName);
String tmpUrl = amazonS3.generatePresignedUrl(httpRequest).toString();
CloseableHttpClient client = HttpClientUtil.createHttpClient();
HttpGet httpget = new HttpGet(tmpUrl);
CloseableHttpResponse resp = client.execute(httpget);
int status = resp.getStatusLine().getStatusCode();
if (status==200) {
HttpEntity entity = resp.getEntity();
if(ObjectUtil.isNotEmpty(entity)){
IoUtils.writer(entity.getContent(), filePath+"/"+fileName);
logger.info("下载录入文件成功,下载文件为:"+fileName);
}
return new String []{tmpUrl,"true"};
//return true;
} else {
HttpEntity entity = resp.getEntity();
if(ObjectUtil.isNotEmpty(entity)){
String retMsg = IoUtils.readerByIs(entity.getContent());
logger.error("下载录入文件失败,返回信息为:"+retMsg);
}
return new String []{tmpUrl,"false"};
}
} catch (Exception e) {
logger.error("下载录入文件失败,返回信息为:"+e.getMessage());
e.printStackTrace();
}
return null;
}
12、呼叫状态和话单通知
- 开发方提供接口连接接收第三方API的
入参数Body
- 第三方SP如果需要获取呼叫状态信息和话单记录,可根据如下两种情况进行操作:l 若第三方SP使用的开放功能为点击呼叫、语音验证码、语音通知时,
可在申请APP时指定statusUrl和feeUrl
,或在发起请求时填写statusUrl和feeUrl即可。l 若第三方SP使用的开放功能为其他功能,需要在申请APP时指定statusUrl和feeUrl。CaaS平台就会把此次呼叫对应的呼叫状态和话单推送给第三方服务器的URL。statusUrl和feeUrl只支持POST方法
。注:若CaaS平台推送话单给SP失败,会重传话单,直至SP接收成功。最多重传6次,每次时间间隔1小时
。
- 应用场景
SP实时获取每个呼叫的状态
SP实时获取每个呼叫的话单
- SP用于接收状态上报的URL;点 击类业务触发过程中通话的状态,信息CaaS平台会推送至此服务器地址(支持域名和IP);此字段要求进行BASE64编码。最大128字节
关键源代码和文档下载