序言:
首先是需要在华为云上申请隐私通话服务的具体的大家可以去看官方文档:隐私通话接入文档 在这儿呢我主要介绍AXB模式,其他模式(AX、AXYB等)都是大同小异的。
AXB:是指A用户和B用户同时绑定隐私号码X,并且通过X进行通话。注意:1个X号码允许绑定1000对用户号码,但用户号码不可重复。例如,允许同时绑定AXB和CXD,但不允许同时绑定AXB和BXC
话不多说直接开整:
1.首先是绑定接口
绑定接口主要注意他的 msgdgt(签名摘要),主要构成是:
1)把消息头(appkey和ts)、消息体按key的字母原样顺序排序
2)排序后将密钥、消息头(appkey和ts)和消息体的所有key、value串起来以UTF-8编码进行MD5加密,如:MD5(secretkey1value1key2value2..)
3)生成32位大写的摘要字符串,如:BA9854BED1A2986B061E2713F403C752
4)参数释意
参数 |
类型 |
意义 |
是否必传 |
备注 |
appkey |
string |
应用id |
M |
|
ts |
string |
业务时间戳 |
M |
格式yyyyMMddHHmmssSSS,时间采用北京时间,24小时制,精确至毫秒 1)时间格式检查; 2)请求带过来的时间与当前时间比较,前后差值不能超过5分钟; |
msgdgt |
string |
签名摘要 |
M |
MD5摘要 |
requestId |
string |
业务id |
M |
消息请求标识 |
telA |
string |
真实号码 |
M |
|
telX |
string |
小号号码 |
O |
X号码,国内号码格式; mode101模式下,该参数必须携带; mode102模式下,该参数不携带 |
telB |
string |
对端号码 |
M |
|
subts |
string |
绑定时间 |
M |
格式为yyyyMMddHHmmss。时间采用北京时间,24小时制。 |
anucode |
string |
主叫侧放音编码 |
M |
AXB业务时必须设置。固定填写"0,0,0"
放音编码必须包含3个场景的编码。按照“B->X,A->X,其他号码->X”的顺序填写编码,编码之间以逗号分隔。
比如:“1,2,3”表示B->X放音编号为1,A->X放音编号为2, 其他号码->X放音编号为3。 |
anucodecalled |
string |
被叫侧放音编码 |
O |
被叫侧放音编码
被叫放音编码必须包含2个场景的编码。按照“A被叫,B被叫”的顺序填写编码,编码之间以逗号分隔。
比如:“1,2”表示A号码为被叫侧接听时的放音编号为1,B号码为被叫侧接听时的放音编号为2。 |
areacode |
string |
区号 |
O |
去掉“0” 例如:北京(10); mode101模式下,该参数可不携带; mode102模式下,该参数需携带,小号业务系统按区号从资源池选择X号码,接入商根据自己申请的X号码填写对应的区号 |
expiration |
string |
过期时间 |
M |
单位:秒,必须为数字 0:不会自动解绑 非0:自动解绑周期 |
remark |
string |
接入商自有字段 |
O |
接入商自有字段,不能超过30个字节 |
transid |
string |
事务ID |
O |
相同事务ID的幂等操作 |
extra |
json |
扩展参数 |
M |
|
-callrecording |
string |
录音控制 |
M |
仅下列值有效。默认1(开通录音功能)。 1:接通后录音 2:呼叫确认后录音 |
-calldisplay |
string |
来显控制 |
O |
可选。 两个取值组成,A->X,B->X;以“,”隔开,比如“0,1” 取值默认为0(不显示真实号码)。 0:不显示真实号码 1:显示真实号码 |
-callrestrict |
string |
呼叫控制 |
O |
可选。仅下列值有效。默认是1。 1 AXB做呼叫控制,A和B有权限,其他号码无权限,即为现有的AXB 2 AXB的单通控制,A无权限,B有权限,其他号码无权限 3 AXB的单通控制,A有权限,B以及其他号码无权限 6 均无权限 |
-calldisplayshow |
string |
推送被叫来显号码控制 |
O |
可选。仅下列值有效。默认是0。 0 推送中不携带被叫来显号码 1 推送中携带被叫来显号码 |
-callunsub |
string |
解绑推送消息控制 |
O |
可选。仅下列值有效。默认是0。 0 解绑不推送消息 1 解绑推送消息 |
-callpickup |
string |
被叫接通推送事件控制 |
M |
必选。 当前场景必须填1 0 不推送被叫接通事件 1 推送被叫接通事件 |
5)响应参数
参数 |
类型 |
意义 |
是否必传 |
备注 |
subid |
string |
绑定id |
M |
|
telX |
string |
小号 |
M |
|
6)代码实现
private String appKey="SXHWD_LJ1"; // APP_Key private String appSecret="sxhwd"; // APP_Secret private String ompDomainName="http://122.112.233.87:28080"; // APP接入地址 /** * Build the real url of https request | 构建隐私保护通话平台请求路径 * * @param path 接口访问URI * @return */ private String buildOmpUrl(String path) { return ompDomainName + path; } @Override public AjaxResult axbBindNumber(String relationNum, String callerNum, String calleeNum) { // //mode101:APP自带x号码 //mode102:平台分配x号码 // String url = "/v2/axb/mode101"; String url = "/v2/axb/mode102"; String realUrl = buildOmpUrl(url); // // 封装JOSN请求 JSONObject json = new JSONObject(); //请求标识 String requestId=UUID.randomUUID().toString(); String nowData=new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()); //绑定时长 Long expiration=24*60*60*30L; json.put("requestId", requestId); json.put("telA", callerNum); // A方真实号码(手机或固话) json.put("telB", calleeNum); // B方真实号码(手机或固话) // json.put("telX", relationNum); // (虚拟号码)mode101 json.put("anucode", "0,0,0"); // 主叫侧放音编码 json.put("subts", nowData); // 绑定时间 json.put("expiration", expiration); // 绑定时间 JSONObject extra = new JSONObject(); extra.put("callrecording", "1"); extra.put("callpickup", "1"); json.put("extra", extra); log.info("请求地址", realUrl); PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL url1 = new URL(realUrl); // 打开和URL之间的连接 URLConnection conn = url1.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("appkey", "SXHWD_LJ1"); conn.setRequestProperty("Accept", "application/json;charset=utf-8"); conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); conn.setRequestProperty("ts", nowData); String msgdgt = pingjie(appKey, nowData, json); conn.setRequestProperty("msgdgt", msgdgt); log.info("msgdgt{}"+msgdgt); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(json.toJSONString()); log.info("请求参数{}" + json); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("发送 POST 请求出现异常!" + e); log.error("发送 POST 请求出现异常!{}", e); e.printStackTrace(); } //使用finally块来关闭输出流、输入流 finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } log.info("响应数据{}" + result); JSONObject jsonObject = JSON.parseObject(result); String code=jsonObject.getString("code"); //绑定成功 if ("0".equals(code)) { JSONObject data = JSON.parseObject(jsonObject.getString("subid")); //唯一标识 String subid = data.getString("subid"); String telX = data.getString("telX"); //写入数据库 LinjiaBindTel tel= bindTelService.selectByPhone(callerNum,calleeNum); LinjiaBindTel bindTel= new LinjiaBindTel(); bindTel.setTelA(callerNum); bindTel.setTelB(calleeNum); bindTel.setTelX(telX); bindTel.setSubid(subid); if (tel !=null){ //更新 bindTelService.updateBindTel(bindTel); log.info("绑定成功,telX{},telA{},telB{}",telX,callerNum,calleeNum); return AjaxResult.success("绑定成功",telX); } //新增 bindTelService.insertBindTel(bindTel); //开启定时任务,时间到了删除记录 new Timer().schedule(new TimerTask() { @Override public void run() { bindTelService.deleteTelBySubid(subid); log.info("解绑成功,subid{}",subid); } },expiration); return AjaxResult.success("绑定成功",telX); }else { return AjaxResult.error(jsonObject.getString("message")); } } /** * 生成 msgdgt 验证 * @param appKey * @param ts * @param json * @return */ public String pingjie(String appKey, String ts, JSONObject json) { Map map = new HashMap(); map.put("appkey", appKey); map.put("ts", ts); //json等于null(解绑),反之绑定 if (json != null){ JSONObject extra = (JSONObject) json.get("extra"); json.remove("extra"); map.putAll(JSON.parseObject(json.toJSONString(), Map.class)); map.putAll(JSON.parseObject(extra.toJSONString(), Map.class)); //将extra重新加入 JSONObject extra2 = new JSONObject(); extra2.put("callrecording", "1"); extra2.put("callpickup", "1"); json.put("extra", extra2); } String str = appSecret.concat(getMapToString(sortByKey(map))); log.info("未加密{}" + str); return MD5.MD5Encode(str); } /** * 按map的key排序 * @param map * @return */ private Map<String, Object> sortByKey(Map<String, Object> map) { Map<String, Object> result = new LinkedHashMap<>(map.size()); map.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .forEachOrdered(e -> result.put(e.getKey(), e.getValue())); return result; } /** * map转string * @param map * @return */ public static String getMapToString(Map<String, Object> map) { Set<String> keySet = map.keySet(); //将set集合转换为数组 String[] keyArray = keySet.toArray(new String[keySet.size()]); //因为String拼接效率会很低的,所以转用StringBuilder StringBuilder sb = new StringBuilder(); for (int i = 0; i < keyArray.length; i++) { // 参数值为空,则不参与签名 这个方法trim()是去空格 if ((String.valueOf(map.get(keyArray[i]))).trim().length() > 0) { sb.append(keyArray[i]).append(String.valueOf(map.get(keyArray[i])).trim()); } } return sb.toString(); }
2.解绑
1)参数
参数 |
类型 |
意义 |
是否必传 |
备注 |
appkey |
string |
应用id |
M |
|
ts |
string |
业务时间戳 |
M |
格式yyyyMMddHHmmssSSS,时间采用北京时间,24小时制,精确至毫秒 |
msgdgt |
string |
签名摘要 |
M |
MD5摘要 |
2)响应参数
{ "code": "0", "message": "success" }
3)解绑代码
String url = "/v2/axb/"; String realUrl = buildOmpUrl(url)+subscriptionId; String nowData=new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()); HttpURLConnection httpURLConnection = null; try { URL url1 = new URL(realUrl); httpURLConnection = (HttpURLConnection) url1.openConnection(); httpURLConnection.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); httpURLConnection.setRequestProperty("appkey", "SXHWD_LJ1"); httpURLConnection.setRequestProperty("Accept", "application/json;charset=utf-8"); httpURLConnection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); httpURLConnection.setRequestProperty("ts", nowData); String msgdgt = pingjie(appKey, nowData, null); httpURLConnection.setRequestProperty("msgdgt", msgdgt); httpURLConnection.setRequestMethod("DELETE"); //请求不能使用缓存 httpURLConnection.setUseCaches(false); //允许向urlConnection写出 httpURLConnection.setDoOutput(true); //允许从urlConnection读入 httpURLConnection.setDoInput(true); //获取连接 httpURLConnection.connect(); //得到输出流对象 OutputStream outputStream = httpURLConnection.getOutputStream(); DataOutputStream dataOutPutStream = new DataOutputStream(outputStream); //向输出流写数据,这些数据将存到内存缓冲区 //刷新对象输出流,将字节都写入到流中 dataOutPutStream.flush(); //调用getInputStream()函数时才把准备好的http请求正式发送到服务器,getInputStream的返回值是InputStream InputStream inputStream = httpURLConnection.getInputStream(); //返回的输入流用于读取服务器对此次http请求返回的信息 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"utf-8")); String s = ""; StringBuilder stringBuilder = new StringBuilder(); while((s = bufferedReader.readLine()) != null){ stringBuilder.append(s); } bufferedReader.close(); dataOutPutStream.close(); System.out.println(stringBuilder.toString()); } catch (IOException exception) { exception.printStackTrace(); } finally { if (httpURLConnection != null) { httpURLConnection.disconnect(); } }
欢迎大家补充哦!!!