使用動態(tài)IVR實現(xiàn)呼轉(zhuǎn)
本文介紹動態(tài)IVR實現(xiàn)呼轉(zhuǎn)的應用場景,在該應用場景下的實現(xiàn)思路以及具體的Java實現(xiàn)方式。
應用場景
售后服務是解決用戶在使用產(chǎn)品時遇到問題的有效途徑之一。傳統(tǒng)的售后服務方式是用戶撥打售后熱線,客服直接接聽,這種方式需要每個客服對產(chǎn)品問題全面了解才能為用戶提供精準的解決方案,這無疑會增加客服的工作壓力以及企業(yè)的培訓壓力。為了解決上面的問題,企業(yè)可以對產(chǎn)品可能存在的問題進行分類,對客服進行有針對性的培訓,用戶撥打售后熱線,根據(jù)提示按鍵轉(zhuǎn)接到指定的售后服務人員。
實現(xiàn)功能
阿里云語音服務動態(tài)IVR呼轉(zhuǎn)已實現(xiàn)了接聽電話、自動放音、記錄通話過程中按鍵信息以及呼叫轉(zhuǎn)接等功能。由于呼轉(zhuǎn)號碼需要您提供,您需要實現(xiàn)一個接口(下文統(tǒng)稱為回調(diào)接口)供動態(tài)IVR調(diào)用,調(diào)用方式為在動態(tài)IVR配置填寫接收HTTP請求的URL。以上述應用場景為例,您在語音服務平臺申請可以呼入呼出的號碼,并使用動態(tài)IVR功能,用戶使用產(chǎn)品時發(fā)生故障主動撥打使用動態(tài)IVR功能的號碼,整個業(yè)務流程為:
本文通過Spring Boot實現(xiàn)供動態(tài)IVR流程中語音平臺調(diào)用的回調(diào)接口。
實現(xiàn)思路
本文以上述應用場景為例,回調(diào)接口完成對動態(tài)IVR請求的接收,并按照下圖的實現(xiàn)邏輯完成對呼轉(zhuǎn)號碼的返回,本文中完整代碼可點擊下載,您可以根據(jù)自己的業(yè)務需求書寫符合您需要的處理請求代碼。
實現(xiàn)步驟
步驟一:接收動態(tài)IVR請求以及返回信息
動態(tài)IVR向回調(diào)接口發(fā)送的請求參數(shù)信息包含呼入主叫號碼、呼入被叫號碼、當前時間戳、呼叫唯一標識以及用戶按鍵信息,其中用戶按鍵信息為可選參數(shù)。請求參數(shù)和返回信息格式詳情,請參見動態(tài)IVR呼轉(zhuǎn)回調(diào)接口。
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;
}
/** 成功的時候調(diào)用 */
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;
}
/** 失敗的時候調(diào)用 */
public static <T> Result<T> err(String result, String msg) {
return new Result<>(result,msg);
}
}
步驟二:處理請求信息
在application配置文件中設置當前有效的按鍵信息,接收動態(tài)IVR請求后,判斷請求信息中是否有按鍵信息,如果沒有按鍵信息時直接返回默認的被叫轉(zhuǎn)接號碼,有按鍵信息判斷按鍵信息是否與配置文件中有效按鍵匹配,符合后查詢數(shù)據(jù)庫返回轉(zhuǎn)接號碼,不符合時返回失敗信息;該步驟代碼您可以按需修改。
下表為本文示例中的數(shù)據(jù)庫表結(jié)構(gòu),數(shù)據(jù)庫中存儲的信息包含按鍵信息,轉(zhuǎn)接號碼,號碼使用狀態(tài),呼轉(zhuǎn)顯示號碼以及號碼用途描述。
Field
Type
Null
Default
Description
keyCode
int
NO
NULL
有效的按鍵信息。
phoneNumber
varchar(100)
NO
NULL
對應的轉(zhuǎn)接號碼。
status
int
NO
NULL
號碼使用狀態(tài)。
0表示號碼目前不可用
1表示號碼可用
showNumber
varchar(100)
YES
NULL
呼轉(zhuǎn)顯示號碼。
description
varchar(200)
YES
NULL
號碼用途描述。
根據(jù)按鍵信息以及狀態(tài)進行數(shù)據(jù)庫查詢。
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));
}
}
根據(jù)業(yè)務邏輯以及查詢信息處理動態(tài)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","沒有可用的呼轉(zhuǎn)號碼");
}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","沒有可用的呼轉(zhuǎn)號碼");
}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; // 呼轉(zhuǎn)號碼
private String showNumber; // 呼轉(zhuǎn)顯示號碼
}
步驟三:創(chuàng)建ECS實例部署jar包
動態(tài)IVR調(diào)用回調(diào)接口的格式通過URL進行,URL格式為:IP地址+端口號。本文使用ECS實例進行部署,并為ECS實例綁定EIP,詳細過程為:
為ECS實例綁定EIP有兩種方式。
創(chuàng)建ECS實例時,在帶寬和安全組中勾選分配IPV4地址,根據(jù)需要選擇帶寬計費模式,設置帶寬值,并選擇安全組。
已創(chuàng)建ECS實例時,綁定EIP詳情,請參見綁定和解綁彈性公網(wǎng)IP。
在ECS實例中部署jar包所需要的Java環(huán)境,并通過
java -jar jar包名
運行。
步驟四:動態(tài)IVR呼轉(zhuǎn)回調(diào)接口配置
動態(tài)IVR呼轉(zhuǎn)回調(diào)設置分為兩種情況,第一種是針對所有允許呼入呼出的號碼設置,即全局默認設置,第二種是針對某一個允許呼入呼出號碼設置,即指定號碼設置。
查看ECS實例EIP詳情,請參見查看IP地址,本文使用的IP地址如下所示。
根據(jù)查看的IP地址以及使用端口號進行動態(tài)IVR呼轉(zhuǎn)配置,詳情請參見動態(tài)IVR呼轉(zhuǎn)回調(diào)接口。配置效果如下圖所示。
實現(xiàn)效果
以上述設置為例,號碼1553443****為用戶主叫號碼,1705675****為被叫號碼,1553443****向1705675****發(fā)起呼叫,實現(xiàn)效果如下。
被叫1705675****自動接聽,播放首次呼入放音音頻;
用戶按鍵并以#號鍵結(jié)束;
語音平臺向回調(diào)接口發(fā)起請求,若URL可用且轉(zhuǎn)接號碼可用此時用戶會聽到呼轉(zhuǎn)放音音頻;
呼轉(zhuǎn)完成后,用戶與呼轉(zhuǎn)號碼進行對話;
當回調(diào)接口URL不可用,用戶聽到訪問URL失敗放音音頻;
當回調(diào)接口返回的查詢呼轉(zhuǎn)號碼不可用時,用戶聽到查分機不存在放音。