本文介绍动态IVR实现呼转的应用场景,在该应用场景下的实现思路以及具体的Java实现方式。
应用场景
售后服务是解决用户在使用产品时遇到问题的有效途径之一。传统的售后服务方式是用户拨打售后热线,客服直接接听,这种方式需要每个客服对产品问题全面了解才能为用户提供精准的解决方案,这无疑会增加客服的工作压力以及企业的培训压力。为了解决上面的问题,企业可以对产品可能存在的问题进行分类,对客服进行有针对性的培训,用户拨打售后热线,根据提示按键转接到指定的售后服务人员。
实现功能
阿里云语音服务动态IVR呼转已实现了接听电话、自动放音、记录通话过程中按键信息以及呼叫转接等功能。由于呼转号码需要您提供,您需要实现一个接口(下文统称为回调接口)供动态IVR调用,调用方式为在动态IVR配置填写接收HTTP请求的URL。以上述应用场景为例,您在语音服务平台申请可以呼入呼出的号码,并使用动态IVR功能,用户使用产品时发生故障主动拨打使用动态IVR功能的号码,整个业务流程为:

本文通过Spring Boot实现供动态IVR流程中语音平台调用的回调接口。
实现思路
本文以上述应用场景为例,回调接口完成对动态IVR请求的接收,并按照下图的实现逻辑完成对呼转号码的返回,本文中完整代码可点击下载,您可以根据自己的业务需求书写符合您需要的处理请求代码。

实现步骤
步骤一:接收动态IVR请求以及返回信息
动态IVR向回调接口发送的请求参数信息包含呼入主叫号码、呼入被叫号码、当前时间戳、呼叫唯一标识以及用户按键信息,其中用户按键信息为可选参数。请求参数和返回信息格式详情,请参见动态IVR呼转回调接口。
public class VmsController {
@Autowired
private MyConfigration myConfig;
@Autowired
private IvrDaoImpl ivrDaoImpl;
@RequestMapping("/http")
public Result<Object> http(@RequestParam(value = "action")String action,
@RequestParam(value = "caller") String caller,
@RequestParam(value = "serviceNumber") String serviceNumber,
@RequestParam(value = "timestamp") String timestamp,
@RequestParam(value = "uuid") String uuid,
@RequestParam(value = "dtmf",required = false) Integer dtmf) {
// write code here
IvrReceive ivrObject = new IvrReceive(action,caller,serviceNumber,timestamp,uuid,dtmf);
SearchResponse searchResponse = new SearchResponse(myConfig,ivrObject,ivrDaoImpl);;
return searchResponse.noKeyResponse();
}
}
返回信息的Result类设计:
@Data
public class Result<T> {
private String result;
private String msg;
private T data;
private Result(String result, String msg){
this.result = result;
this.msg = msg;
}
/** 成功的时候调用 */
public static<T> Result<T> ok(T data) {
return new Result<T>(data);
}
public static<T> Result<T> ok(String result, String msg, T data){
Result<T> success = ok(data);
success.setResult(result);
success.setMsg(msg);
return success;
}
/** 失败的时候调用 */
public static <T> Result<T> err(String result, String msg) {
return new Result<>(result,msg);
}
}
步骤二:处理请求信息
在application配置文件中设置当前有效的按键信息,接收动态IVR请求后,判断请求信息中是否有按键信息,如果没有按键信息时直接返回默认的被叫转接号码,有按键信息判断按键信息是否与配置文件中有效按键匹配,符合后查询数据库返回转接号码,不符合时返回失败信息;该步骤代码您可以按需修改。
-
下表为本文示例中的数据库表结构,数据库中存储的信息包含按键信息,转接号码,号码使用状态,呼转显示号码以及号码用途描述。
Field
Type
Null
Default
Description
keyCode
int
NO
NULL
有效的按键信息。
phoneNumber
varchar(100)
NO
NULL
对应的转接号码。
status
int
NO
NULL
号码使用状态。
-
0表示号码目前不可用
-
1表示号码可用
showNumber
varchar(100)
YES
NULL
呼转显示号码。
description
varchar(200)
YES
NULL
号码用途描述。
-
-
根据按键信息以及状态进行数据库查询。
public class IvrDaoImpl implements IvrDao{
@Autowired
private JdbcTemplate jdbcTemplate;
/** 请求中有按键信息 */
@Override
public List<IvrBean> findInfo(Integer keyCode) {
String sql = "select * from ivr where keyCode=? and status=1";
Object[] params = new Object[]{keyCode};
return jdbcTemplate.query(sql, params, new BeanPropertyRowMapper<IvrBean>(IvrBean.class));
}
/** 请求中无按键信息 */
@Override
public List<IvrBean> findInfo(){
String sql = "select * from ivr where status=1";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<IvrBean>(IvrBean.class));
}
}
-
根据业务逻辑以及查询信息处理动态IVR请求。
public class SearchResponse {
@Autowired
private final KeyConfigs keyConfigs;
private final IvrReceive ivrReceive;
@Autowired
private final IvrDao ivrDao;
public Result<Object> result(){
if(ivrReceive.getDtmf().equals(-1)){
return noKeyResponse();
}else if(compare()){
return response();
}else{
return Result.err("fail","按键功能不支持");
}
}
/** 比较按键信息是否有效 */
public boolean compare() {
Integer temp = ivrReceive.getDtmf();
return keyConfigs.infoReturn().contains(temp);
}
/** 请求中有按键信息 */
public Result<Object> response(){
Integer temp = ivrReceive.getDtmf();
List<IvrBean> list = ivrDaoImpl.findInfo(temp);
if(list.size() == 0){
return Result.err("fail","没有可用的呼转号码");
}else{
String phoneNumber = list.get(0).getPhoneNumber();
String showNumber = list.get(0).getShowNumber() == null ? ivrReceive.getServiceNumber() : list.get(0).getShowNumber();;
return Result.ok("success", "成功", new Info(phoneNumber, showNumber));
}
}
/** 请求中没有按键信息 */
public Result<Object> noKeyResponse(){
Integer temp = ivrReceive.getDtmf();
List<IvrBean> list = ivrDaoImpl.findInfo();
if(list.size() == 0){
return Result.err("fail","没有可用的呼转号码");
}else{
String phoneNumber = list.get(0).getPhoneNumber();
String showNumber = list.get(0).getShowNumber() == null ? ivrReceive.getServiceNumber() : list.get(0).getShowNumber();;
return Result.ok("success", "成功", new Info(phoneNumber, showNumber);
}
}
}
@AllArgsConstructor
@Data
class Info{
private String called; // 呼转号码
private String showNumber; // 呼转显示号码
}
步骤三:创建ECS实例部署jar包
动态IVR调用回调接口的格式通过URL进行,URL格式为:IP地址+端口号。本文使用ECS实例进行部署,并为ECS实例绑定EIP,详细过程为:
-
为ECS实例绑定EIP有两种方式。
-
创建ECS实例时,在带宽和安全组中勾选分配IPV4地址,根据需要选择带宽计费模式,设置带宽值,并选择安全组。
-
已创建ECS实例时,绑定EIP详情,请参见弹性公网IP。
-
-
在ECS实例中部署jar包所需要的Java环境,并通过
java -jar jar包名运行。
步骤四:动态IVR呼转回调接口配置
动态IVR呼转回调设置分为两种情况,第一种是针对所有允许呼入呼出的号码设置,即全局默认设置,第二种是针对某一个允许呼入呼出号码设置,即指定号码设置。
-
查看ECS实例EIP详情,请参见查看IP地址。
在ECS实例详情页面,查看实例的公网IP地址(即弹性公网IP),该地址将用于配置动态IVR呼转回调接口的URL。
-
根据查看的IP地址以及使用端口号进行动态IVR呼转配置,详情请参见动态IVR呼转回调接口。
在配置页面中,全局默认URL设置为
http://。下方的指定号码URL配置区域可为特定号码配置独立的回调URL,包括呼叫开始URL、振铃URL、呼叫中间URL、呼叫结束URL等字段。产生IVR交互时,系统优先使用指定号码URL,未配置时使用全局默认URL。:<端口号>/ivr/call?serviceCode=TransferNumber
实现效果
以上述设置为例,号码1553443**为用户主叫号码,1705675为被叫号码,1553443向1705675**发起呼叫,实现效果如下。
-
被叫1705675****自动接听,播放首次呼入放音音频;
-
用户按键并以#号键结束;
-
语音平台向回调接口发起请求,若URL可用且转接号码可用此时用户会听到呼转放音音频;
-
呼转完成后,用户与呼转号码进行对话;
-
当回调接口URL不可用,用户听到访问URL失败放音音频;
-
当回调接口返回的查询呼转号码不可用时,用户听到查分机不存在放音。