JAVA中调用阿里云语音通知Api并接收消息回执

JAVA中调用阿里云语音通知Api并接收消息回执

配置文件

JAVA中调用阿里云语音通知Api并接收消息回执_第1张图片
需要的包

		<dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dyvmsapi</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.5.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.aliyun.mns</groupId>
            <artifactId>aliyun-sdk-mns</artifactId>
            <version>1.1.8</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>dyvmsapi20170525</artifactId>
            <version>0.0.2</version>
        </dependency>
        <dependency>
            <groupId>com.meiyuan</groupId>
            <artifactId>alicom-mns-receive-sdk-0.0.1-SNAPSHOT</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.meiyuan</groupId>
            <artifactId>aliyun-java-sdk-dybaseapi-1.0.0-SNAPSHOT</artifactId>
            <version>1.0.0</version>
        </dependency>

具体代码及步骤

1.获取配置文件中的参数

package com.meiyuan.reservation.dto.call;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.io.Serializable;

/**
 * 功能描述:ali云 语音通知辅助类实体
 *
 * @author tc
 * @version 1.0
 * @since 2020/12/15 17:24
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "aliyun")
public class ClientConfigDto implements Serializable {

    private static final long serialVersionUID = 7736942314547915021L;

    /**
     * 您的AccessKey ID
     */
    private String accessKeyId;

    /**
     * 您的AccessKey Secret
     */
    private String accessKeySecret;

    /**
     * 语音文件的语音ID
     */
    private String ttsCode;

    private String product;

    private String domain;

    /**
     * 主叫号码
     */
    private String calledShowNumber;

    /**
     * 相应产品的消息类型
     */
    private String messageType;

    /**
     * 在云通信页面开通相应业务消息后,就能在页面上获得对应的queueName
     */
    private String queueName;
}

2.调用阿里云语音Api,三种不同的呼叫方式,根据需要选择调用

package com.meiyuan.reservation.call;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dyvmsapi.model.v20170525.IvrCallRequest;
import com.aliyuncs.dyvmsapi.model.v20170525.IvrCallResponse;
import com.aliyuncs.dyvmsapi.model.v20170525.SingleCallByTtsRequest;
import com.aliyuncs.dyvmsapi.model.v20170525.SingleCallByTtsResponse;
import com.aliyuncs.dyvmsapi.model.v20170525.SingleCallByVoiceRequest;
import com.aliyuncs.dyvmsapi.model.v20170525.SingleCallByVoiceResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.meiyuan.reservation.dto.call.CallMsgDto;
import com.meiyuan.reservation.dto.call.ClientConfigDto;
import com.meiyuan.reservation.mq.sender.ReservationMqSender;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 功能描述:ali云 语音通知辅助类
 *
 * @author tc
 * @version 1.0
 * @since 2020/12/15 17:17
 */
@Slf4j
@Component
public class VmsDemo {

    /**
     * 文本转语音外呼
     *
     * @return
     * @throws ClientException
     */
    @Autowired
    private ReservationMqSender reservationMqSender;

    public SingleCallByTtsResponse singleCallByTts(ClientConfigDto dto, String managerPhone, String reservationId, String tailNumber, String time) throws ClientException {
        //设置访问超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");
        //初始化acsClient 暂时不支持多region
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", dto.getAccessKeyId(), dto.getAccessKeySecret());
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", dto.getProduct(), dto.getDomain());
        IAcsClient acsClient = new DefaultAcsClient(profile);
        SingleCallByTtsRequest request = new SingleCallByTtsRequest();
        // 被叫显号,若您使用的模板为公共号池号码外呼模板,则该字段值必须为空;
        // 若您使用的模板为专属号码外呼模板,则必须传入已购买的号码,仅支持一个号码,您可以在语音服务控制台上查看已购买的号码。
        if ("numberPool".equals(dto.getCalledShowNumber())) {
            request.setCalledShowNumber("");
        } else {
            request.setCalledShowNumber(dto.getCalledShowNumber());
        }
        //必填-被叫号码(店长)
        request.setCalledNumber(managerPhone);
        //必填-Tts模板ID
        request.setTtsCode(dto.getTtsCode());
        // 这里主要是填写我们语音文字模板里面的参数 ${param}
        System.out.println(time);
        request.setTtsParam("{\"time\":\"" + time + "\",\"tailNumber\":\"" + tailNumber + "\"}");
        //可选-音量 取值范围 0--200
        request.setVolume(200);
        //可选-播放次数
        request.setPlayTimes(2);
        //可选-外部扩展字段,此ID将在回执消息中带回给调用方
        request.setOutId(reservationId + ":" + managerPhone);
        SingleCallByTtsResponse singleCallByTtsResponse = acsClient.getAcsResponse(request);
        if (singleCallByTtsResponse.getCode() != null && singleCallByTtsResponse.getCode().equals("OK")) {
            //请求成功
            log.info("请求成功");
        } else {
            log.info("语音呼叫失败,code{}", singleCallByTtsResponse.getCode());
            if (singleCallByTtsResponse.getCode().equals("isv.BUSINESS_LIMIT_CONTROL")) {
                log.info("流控限制,3分钟后重试");
                CallMsgDto callMsgDto = new CallMsgDto();
                callMsgDto.setManagerPhone(Arrays.asList(managerPhone));
                callMsgDto.setReservationId(reservationId);
                callMsgDto.setContactPhone(tailNumber);
                callMsgDto.setReservationTime(time);
                reservationMqSender.sendLaunchMsg(callMsgDto, 3 * 60 * 1000L);
            }
        }
        return singleCallByTtsResponse;
    }

    /**
     * 语音文件外呼
     *
     * @return
     * @throws ClientException
     */
    public SingleCallByVoiceResponse singleCallByVoice(ClientConfigDto dto) throws ClientException {

        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", dto.getAccessKeyId(), dto.getAccessKeySecret());
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", dto.getProduct(), dto.getDomain());
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象-具体描述见控制台-文档部分内容
        SingleCallByVoiceRequest request = new SingleCallByVoiceRequest();
        //必填-被叫显号,可在语音控制台中找到所购买的显号
        request.setCalledShowNumber("025000000");
        //必填-被叫号码
        request.setCalledNumber("15000000000");
        //必填-语音文件ID
        request.setVoiceCode("3a7c382b-ee87-493f-bfa0-b9fd6f31f8bb.wav");
        //可选-外部扩展字段
        request.setOutId("yourOutId");

        //hint 此处可能会抛出异常,注意catch
        SingleCallByVoiceResponse singleCallByVoiceResponse = acsClient.getAcsResponse(request);

        return singleCallByVoiceResponse;
    }

    /**
     * 交互式语音应答
     *
     * @return
     * @throws ClientException
     */
    public IvrCallResponse ivrCall(ClientConfigDto dto) throws ClientException {
        //可自助调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
        System.setProperty("sun.net.client.defaultReadTimeout", "10000");

        //初始化acsClient,暂不支持region化
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", dto.getAccessKeyId(), dto.getAccessKeySecret());
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", dto.getProduct(), dto.getDomain());
        IAcsClient acsClient = new DefaultAcsClient(profile);

        //组装请求对象-具体描述见控制台-文档部分内容
        IvrCallRequest request = new IvrCallRequest();
        //必填-被叫显号,可在语音控制台中找到所购买的显号
        request.setCalledShowNumber("057156210000");
        //必填-被叫号码
        request.setCalledNumber("15000000000");
        request.setPlayTimes(3L);

        //必填-语音文件ID或者tts模板的模板号,有参数的模板需要设置模板变量的值
        //request.setStartCode("ebe3a2b5-c287-42a4-8299-fc40ae79a89f.wav");
        request.setStartCode("TTS_713900000");
        request.setStartTtsParams("{\"product\":\"aliyun\",\"code\":\"123\"}");
        List<IvrCallRequest.MenuKeyMap> menuKeyMaps = new ArrayList<IvrCallRequest.MenuKeyMap>();
        IvrCallRequest.MenuKeyMap menuKeyMap1 = new IvrCallRequest.MenuKeyMap();
        menuKeyMap1.setKey("1");
        menuKeyMap1.setCode("9a9d7222-670f-40b0-a3af.wav");
        menuKeyMaps.add(menuKeyMap1);
        IvrCallRequest.MenuKeyMap menuKeyMap2 = new IvrCallRequest.MenuKeyMap();
        menuKeyMap2.setKey("2");
        menuKeyMap2.setCode("44e3e577-3d3a-418f-932c.wav");
        menuKeyMaps.add(menuKeyMap2);
        IvrCallRequest.MenuKeyMap menuKeyMap3 = new IvrCallRequest.MenuKeyMap();
        menuKeyMap3.setKey("3");
        menuKeyMap3.setCode("TTS_71390000");
        menuKeyMap3.setTtsParams("{\"product\":\"aliyun\",\"code\":\"123\"}");
        menuKeyMaps.add(menuKeyMap3);
        request.setMenuKeyMaps(menuKeyMaps);
        //结束语可以使一个无参模板或者一个语音文件ID
        request.setByeCode("TTS_71400007");

        //可选-外部扩展字段
        request.setOutId("yourOutId");

        //hint 此处可能会抛出异常,注意catch
        IvrCallResponse ivrCallResponse = acsClient.getAcsResponse(request);

        return ivrCallResponse;
    }
}

3.把监听器交给spring管理,服务启动的时候就去监听阿里的回执队列,获取回执消息


package com.meiyuan.reservation;

import com.alicom.mns.tools.DefaultAlicomMessagePuller;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import com.meiyuan.marsh.jetcache.anno.config.EnableAdvancedCreateCacheAnnotation;
import com.meiyuan.reservation.dto.call.CallMsgDto;
import com.meiyuan.reservation.dto.call.ClientConfigDto;
import com.meiyuan.reservation.dto.call.ReservationProperties;
import com.meiyuan.reservation.mq.listener.MyMessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;

/**
 * @author admin
 */
@SpringBootApplication(scanBasePackages = "com.meiyuan")
@EnableDiscoveryClient
@EnableMethodCache(basePackages = "com.meiyuan")
@EnableAdvancedCreateCacheAnnotation
@EnableFeignClients(basePackages = {"com.meiyuan"})
@EnableConfigurationProperties
@EnableAsync
public class SaasReservationServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SaasReservationServerApplication.class, args);
    }

    @Autowired
    private MyMessageListener messageListener;

    @Autowired
    private ReservationProperties reservationProperties;

    @Bean
    public CallMsgDto listenerBean (ClientConfigDto dto) throws Exception {
        DefaultAlicomMessagePuller puller = new DefaultAlicomMessagePuller();
        if (reservationProperties.getReceive().equals("true")) {
            puller.startReceiveMsg(dto.getAccessKeyId(), dto.getAccessKeySecret(), dto.getMessageType(), dto.getQueueName(), messageListener);
        }
        return new CallMsgDto();
    }
}

4.获取到回执消息后的业务逻辑处理


package com.meiyuan.reservation.mq.listener;

import com.alicom.mns.tools.MessageListener;
import com.alicp.jetcache.anno.CreateCache;
import com.aliyun.mns.model.Message;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.google.gson.Gson;
import com.meiyuan.commons.tools.redis.JetcacheNames;
import com.meiyuan.marsh.jetcache.AdvancedCache;
import com.meiyuan.marsh.jetcache.anno.AdvancedCreateCache;
import com.meiyuan.reservation.call.VmsDemo;
import com.meiyuan.reservation.dao.ReservationDao;
import com.meiyuan.reservation.dao.VoiceReportDao;
import com.meiyuan.reservation.dto.call.CallMsgDto;
import com.meiyuan.reservation.dto.call.ClientConfigDto;
import com.meiyuan.reservation.dto.call.ReservationProperties;
import com.meiyuan.reservation.entity.ReservationEntity;
import com.meiyuan.reservation.entity.VoiceReportEntity;
import com.meiyuan.reservation.enums.ResCallStatusEnum;
import com.meiyuan.reservation.enums.TimeEnum;
import com.meiyuan.reservation.mq.sender.ReservationMqSender;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 功能描述:回执消息监听器
 *
 * @author tiancheng
 * @version V1.0.0
 * @since 2020/12/24 9:30
 */
@Slf4j
@Component
public class MyMessageListener implements MessageListener {

    @AdvancedCreateCache(@CreateCache(name = JetcacheNames.PREFIX_SEQ_RESERVATION))
    private AdvancedCache cache;

    @Resource
    private VoiceReportDao voiceReportDao;

    @Autowired
    private ReservationDao reservationDao;

    @Autowired
    private ReservationProperties reservationProperties;

    @Autowired
    private ReservationMqSender reservationMqSender;

    private Gson gson = new Gson();

    @Override
    public boolean dealMessage(Message message) {

        //消息的几个关键值
        log.info("消息接收时间[{}],message handle[{}],body[{}],id[{}],dequeue count[{}]", null, message.getReceiptHandle(),
                message.getMessageBodyAsString(), message.getMessageId(), message.getDequeueCount());
        log.info("监听到语音回执消息------------------------------{}", message);
        try {
            Map<String, Object> contentMap = gson.fromJson(message.getMessageBodyAsString(), HashMap.class);
            //依据自己的消息类型,获取对应的字段
            String callId = (String) contentMap.get("call_id");
            String startTime = (String) contentMap.get("start_time");
            String endTime = (String) contentMap.get("end_time");
            String caller = (String) contentMap.get("caller");
            String callee = (String) contentMap.get("callee");
            String duration = (String) contentMap.get("duration");
            String statusCode = (String) contentMap.get("status_code");
            String earlyMediaCode = (String) contentMap.get("early_media_code");
            String hangupDirection = (String) contentMap.get("hangup_direction");
            String statusMsg = (String) contentMap.get("status_msg");
            String outId = (String) contentMap.get("out_id");
            String dtmf = (String) contentMap.get("dtmf");
            String voiceType = (String) contentMap.get("voice_type");
            String dialogId = (String) contentMap.get("dialog_id");
            String tollType = (String) contentMap.get("toll_type");
            VoiceReportEntity voiceReportEntity = new VoiceReportEntity(IdWorker.getIdStr(), callId, startTime, endTime, caller, callee, duration, statusCode,
                    earlyMediaCode, hangupDirection, statusMsg,
                    outId, dtmf, voiceType, dialogId, tollType);
            log.info("消息回执的code++++++++{}", statusCode);
            //业务逻辑
            //将回执信息存入数据库
            voiceReportDao.insert(voiceReportEntity);
            String phoneNumber = voiceReportEntity.getCallee();
            String[] split = outId.split(":");
            String reservationId = split[0];
            String phone = split[1];
            ReservationEntity reservationEntity = reservationDao.selectById(reservationId);
            //根据状态码判断下一步处理逻辑
            //1.未接听,占线,关机(10分钟后重试,重试一次)
            if (statusCode.equals(ResCallStatusEnum.USER_NOT_ANSWER.getValue()) || statusCode.equals(ResCallStatusEnum.USER_BUSY.getValue())
                    || statusCode.equals(ResCallStatusEnum.USER_SWITCH_OFF.getValue()) || statusCode.equals(ResCallStatusEnum.USER_NOT_CONNECT.getValue())) {
                Integer num = (Integer) cache.get(outId);
                if (num == null || reservationEntity == null) {
                    log.error("消息错误{}", outId);
                } else if (num <= 1) {
                    cache.set(outId, 2);
                    long millis;
                    if (reservationProperties.getRetry().equals(true)) {
                        millis = TimeEnum.INTERVAL_TIME.getValue();
                    } else {
                        millis = 90 * 1000L;
                    }
                    Date date = reservationEntity.getReservationTime();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String reservationTime = sdf.format(date);
                    CallMsgDto callMsgDto = new CallMsgDto();
                    ArrayList<String> managerPhone = new ArrayList<>();
                    managerPhone.add(phoneNumber);
                    callMsgDto.setManagerPhone(managerPhone);
                    callMsgDto.setReservationId(reservationId);
                    callMsgDto.setContactPhone(reservationEntity.getContactPhone());
                    callMsgDto.setReservationTime(reservationTime);
                    reservationMqSender.sendLaunchMsg(callMsgDto, millis);
                    log.info("用户忙{}", phone);
                }
            } else if (statusCode.equals(ResCallStatusEnum.USER_SUCCESS.getValue()) || statusCode.equals(ResCallStatusEnum.USER_HANG_UP.getValue())) {
                log.info("用户应答{}", phone);
            } else {
                log.error("用户无法接通{}", phone);
            }
        } catch (com.google.gson.JsonSyntaxException e) {
            log.error("error_json_format:" + message.getMessageBodyAsString(), e);
            //理论上不会出现格式错误的情况,所以遇见格式错误的消息,只能先delete,否则重新推送也会一直报错
            return true;
        } catch (Throwable e) {
            //您自己的代码部分导致的异常,应该return false,这样消息不会被delete掉,而会根据策略进行重推
            log.error("系统内部异常,消息重试");
            return false;
        }
        //消息处理成功,返回true, SDK将调用MNS的delete方法将消息从队列中删除掉
        return true;
    }

}

你可能感兴趣的:(java)