Java外呼案例

wen java案例 2

本文目录导读:

Java外呼案例

  1. 基于HTTP API的外呼(云通信平台)
  2. 基于SIP协议的外呼
  3. 完整的外呼系统集成示例
  4. 配置与测试
  5. 注意事项

我来为您提供一个Java外呼(电话呼叫)的完整案例,这里我展示两种常见的外呼方式:基于HTTP API的外呼(适用于大多数云通信平台)和基于SIP协议的外呼(传统方式)。

基于HTTP API的外呼(云通信平台)

1 使用RestTemplate调用API

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class CloudCallService {
    private static final String API_URL = "https://api.yuntongxin.com/v1/call";
    private static final String ACCOUNT_SID = "your_account_sid";
    private static final String AUTH_TOKEN = "your_auth_token";
    private RestTemplate restTemplate;
    private ObjectMapper objectMapper;
    public CloudCallService() {
        this.restTemplate = new RestTemplate();
        this.objectMapper = new ObjectMapper();
    }
    /**
     * 发起外呼请求
     * @param callerNumber 主叫号码
     * @param calleeNumber 被叫号码
     * @return 呼叫结果
     */
    public CallResult makeCall(String callerNumber, String calleeNumber) {
        // 1. 构建请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Basic " + 
                    java.util.Base64.getEncoder().encodeToString(
                        (ACCOUNT_SID + ":" + AUTH_TOKEN).getBytes()));
        // 2. 构建请求体
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("from", callerNumber);
        requestBody.put("to", calleeNumber);
        requestBody.put("callerNumber", callerNumber);
        requestBody.put("timeout", 30); // 超时时间30秒
        // 3. 发送请求
        HttpEntity<Map<String, Object>> requestEntity = 
            new HttpEntity<>(requestBody, headers);
        try {
            String response = restTemplate.postForObject(
                API_URL, requestEntity, String.class);
            // 4. 解析响应
            return parseResponse(response);
        } catch (Exception e) {
            e.printStackTrace();
            return new CallResult(false, "呼叫失败: " + e.getMessage());
        }
    }
    private CallResult parseResponse(String response) {
        try {
            Map<String, Object> responseMap = 
                objectMapper.readValue(response, Map.class);
            String code = (String) responseMap.get("code");
            String message = (String) responseMap.get("message");
            if ("000000".equals(code)) {
                String callId = (String) responseMap.get("callId");
                return new CallResult(true, "呼叫成功", callId);
            } else {
                return new CallResult(false, "呼叫失败: " + message);
            }
        } catch (Exception e) {
            return new CallResult(false, "解析响应失败");
        }
    }
}
// 呼叫结果类
class CallResult {
    private boolean success;
    private String message;
    private String callId;
    public CallResult(boolean success, String message) {
        this.success = success;
        this.message = message;
    }
    public CallResult(boolean success, String message, String callId) {
        this.success = success;
        this.message = message;
        this.callId = callId;
    }
    // Getters and Setters
    public boolean isSuccess() { return success; }
    public void setSuccess(boolean success) { this.success = success; }
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }
    public String getCallId() { return callId; }
    public void setCallId(String callId) { this.callId = callId; }
    @Override
    public String toString() {
        return "CallResult{success=" + success + 
               ", message='" + message + '\'' + 
               ", callId='" + callId + '\'' + '}';
    }
}

2 使用OkHttp实现异步调用

import okhttp3.*;
import com.google.gson.Gson;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class AsyncCallService {
    private OkHttpClient client;
    private Gson gson;
    private static final String API_KEY = "your_api_key";
    private static final String API_SECRET = "your_api_secret";
    public AsyncCallService() {
        this.client = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .build();
        this.gson = new Gson();
    }
    /**
     * 异步发起外呼
     * @param from 主叫号码
     * @param to 被叫号码
     * @param callback 回调接口
     */
    public void makeCallAsync(String from, String to, CallCallback callback) {
        // 构建请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("from", from);
        params.put("to", to);
        String jsonBody = gson.toJson(params);
        // 构建请求
        RequestBody requestBody = RequestBody.create(
            MediaType.parse("application/json"), jsonBody);
        Request request = new Request.Builder()
            .url("https://api.example.com/v1/call")
            .header("Authorization", "Bearer " + getToken())
            .post(requestBody)
            .build();
        // 异步执行
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callback.onFailure("网络错误: " + e.getMessage());
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    CallResult result = gson.fromJson(responseBody, CallResult.class);
                    callback.onSuccess(result);
                } else {
                    callback.onFailure("请求失败: " + response.code());
                }
            }
        });
    }
    // 获取认证Token(简化版)
    private String getToken() {
        return java.util.Base64.getEncoder().encodeToString(
            (API_KEY + ":" + API_SECRET).getBytes());
    }
    // 回调接口
    public interface CallCallback {
        void onSuccess(CallResult result);
        void onFailure(String errorMessage);
    }
}
// 使用示例
public class Main {
    public static void main(String[] args) {
        AsyncCallService service = new AsyncCallService();
        // 异步调用
        service.makeCallAsync("13800138000", "13900139000", 
            new AsyncCallService.CallCallback() {
                @Override
                public void onSuccess(CallResult result) {
                    System.out.println("呼叫成功: " + result);
                }
                @Override
                public void onFailure(String errorMessage) {
                    System.out.println("呼叫失败: " + errorMessage);
                }
            });
        // 主线程继续执行其他任务
        System.out.println("呼叫已发起,等待结果...");
        // 等待异步结果(实际应用中不需要)
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

基于SIP协议的外呼

1 使用JsSIP库(Java包装版本)

import javax.sip.*;
import javax.sip.address.*;
import javax.sip.header.*;
import javax.sip.message.*;
import java.util.*;
public class SipCallService {
    private SipFactory sipFactory;
    private SipStack sipStack;
    private SipProvider sipProvider;
    private static final String SIP_DOMAIN = "sip.example.com";
    private static final String USERNAME = "your_username";
    private static final String PASSWORD = "your_password";
    public SipCallService() throws PeerUnavailableException, TransportNotSupportedException,
            InvalidArgumentException, ObjectInUseException, TooManyListenersException {
        // 初始化SIP栈
        initSipStack();
    }
    private void initSipStack() throws PeerUnavailableException, TransportNotSupportedException,
            InvalidArgumentException, ObjectInUseException, TooManyListenersException {
        sipFactory = SipFactory.getInstance();
        sipFactory.setPathName("gov.nist");
        Properties properties = new Properties();
        properties.setProperty("javax.sip.STACK_NAME", "SipClient");
        properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug.log");
        properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server.log");
        properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
        sipStack = sipFactory.createSipStack(properties);
        // 创建SIP Provider
        ListeningPoint listeningPoint = sipStack.createListeningPoint(
            "127.0.0.1", 5060, "udp");
        sipProvider = sipStack.createSipProvider(listeningPoint);
        // 添加监听器
        sipProvider.addSipListener(new SipListener() {
            @Override
            public void processRequest(RequestEvent requestEvent) {
                // 处理请求
                System.out.println("收到请求: " + requestEvent.getRequest().getMethod());
            }
            @Override
            public void processResponse(ResponseEvent responseEvent) {
                // 处理响应
                System.out.println("收到响应: " + responseEvent.getResponse().getStatusCode());
            }
            @Override
            public void processTimeout(TimeoutEvent timeoutEvent) {
                System.out.println("超时事件");
            }
            @Override
            public void processIOException(IOExceptionEvent exceptionEvent) {
                System.out.println("IO异常");
            }
            @Override
            public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
                System.out.println("事务终止");
            }
            @Override
            public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
                System.out.println("对话终止");
            }
        });
        System.out.println("SIP栈初始化完成");
    }
    /**
     * 发起SIP呼叫
     * @param callerSipUri 主叫SIP地址
     * @param calleeSipUri 被叫SIP地址
     */
    public void makeSipCall(String callerSipUri, String calleeSipUri) {
        try {
            // 1. 创建地址
            AddressFactory addressFactory = sipFactory.createAddressFactory();
            HeaderFactory headerFactory = sipFactory.createHeaderFactory();
            MessageFactory messageFactory = sipFactory.createMessageFactory();
            // 2. 构建From和To地址
            Address fromAddress = addressFactory.createAddress(
                "sip:" + callerSipUri + "@" + SIP_DOMAIN);
            Address toAddress = addressFactory.createAddress(
                "sip:" + calleeSipUri + "@" + SIP_DOMAIN);
            // 3. 创建Call-ID
            String callId = UUID.randomUUID().toString();
            CallIdHeader callIdHeader = headerFactory.createCallIdHeader(callId);
            // 4. 创建CSeq
            CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.INVITE);
            // 5. 创建Via
            ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
            ViaHeader viaHeader = headerFactory.createViaHeader(
                "127.0.0.1", 5060, "udp", "branch" + UUID.randomUUID().toString());
            viaHeaders.add(viaHeader);
            // 6. 创建Max-Forwards
            MaxForwardsHeader maxForwardsHeader = headerFactory.createMaxForwardsHeader(70);
            // 7. 创建INVITE请求
            Request inviteRequest = messageFactory.createRequest(
                "INVITE sip:" + calleeSipUri + "@" + SIP_DOMAIN + " SIP/2.0\r\n\r\n");
            // 8. 添加头部
            inviteRequest.setHeader(fromAddress.getHeader("From"));
            inviteRequest.setHeader(toAddress.getHeader("To"));
            inviteRequest.setHeader(callIdHeader);
            inviteRequest.setHeader(cSeqHeader);
            inviteRequest.setHeader(maxForwardsHeader);
            inviteRequest.setHeader(viaHeaders.get(0));
            // 9. 发送请求
            ClientTransaction clientTransaction = 
                sipProvider.getNewClientTransaction(inviteRequest);
            clientTransaction.sendRequest();
            System.out.println("SIP呼叫已发起: " + callerSipUri + " -> " + calleeSipUri);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("发起SIP呼叫失败: " + e.getMessage());
        }
    }
}

完整的外呼系统集成示例

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Service
public class IntegratedCallService {
    private static final Logger logger = LoggerFactory.getLogger(IntegratedCallService.class);
    private final CloudCallService cloudCallService;
    private final ExecutorService executorService;
    // 呼叫状态枚举
    public enum CallStatus {
        INITIATED, RINGING, CONNECTED, COMPLETED, FAILED
    }
    @Autowired
    public IntegratedCallService(CloudCallService cloudCallService) {
        this.cloudCallService = cloudCallService;
        this.executorService = Executors.newFixedThreadPool(10);
    }
    /**
     * 发起批量外呼
     * @param callTasks 呼叫任务列表
     */
    public void batchMakeCall(List<CallTask> callTasks) {
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (CallTask task : callTasks) {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    makeSingleCall(task);
                } catch (Exception e) {
                    logger.error("外呼失败: {} -> {}", 
                        task.getCallerNumber(), task.getCalleeNumber(), e);
                    task.setStatus(CallStatus.FAILED);
                    task.setErrorMessage(e.getMessage());
                }
            }, executorService);
            futures.add(future);
        }
        // 等待所有呼叫完成或超时
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .join();
        logger.info("批量外呼完成,共 {} 个任务", callTasks.size());
    }
    /**
     * 发起单个呼叫
     * @param task 呼叫任务
     */
    private void makeSingleCall(CallTask task) {
        // 记录开始呼叫
        logger.info("开始呼叫: {} -> {}", task.getCallerNumber(), task.getCalleeNumber());
        task.setStatus(CallStatus.INITIATED);
        // 调用云通信平台
        CallResult result = cloudCallService.makeCall(
            task.getCallerNumber(), task.getCalleeNumber());
        if (result.isSuccess()) {
            task.setStatus(CallStatus.CONNECTED);
            task.setCallId(result.getCallId());
            logger.info("呼叫成功: {}", result.getCallId());
        } else {
            task.setStatus(CallStatus.FAILED);
            task.setErrorMessage(result.getMessage());
            logger.error("呼叫失败: {}", result.getMessage());
        }
    }
}
// 呼叫任务类
class CallTask {
    private String callerNumber;
    private String calleeNumber;
    private String callId;
    private IntegratedCallService.CallStatus status;
    private String errorMessage;
    public CallTask(String callerNumber, String calleeNumber) {
        this.callerNumber = callerNumber;
        this.calleeNumber = calleeNumber;
    }
    // Getters and Setters
    public String getCallerNumber() { return callerNumber; }
    public void setCallerNumber(String callerNumber) { this.callerNumber = callerNumber; }
    public String getCalleeNumber() { return calleeNumber; }
    public void setCalleeNumber(String calleeNumber) { this.calleeNumber = calleeNumber; }
    public String getCallId() { return callId; }
    public void setCallId(String callId) { this.callId = callId; }
    public IntegratedCallService.CallStatus getStatus() { return status; }
    public void setStatus(IntegratedCallService.CallStatus status) { this.status = status; }
    public String getErrorMessage() { return errorMessage; }
    public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
}

配置与测试

1 Maven依赖(pom.xml)

<dependencies>
    <!-- HTTP客户端 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- OkHttp -->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version>
    </dependency>
    <!-- Gson -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>
    <!-- SIP协议支持(可选) -->
    <dependency>
        <groupId>javax.sip</groupId>
        <artifactId>jsip</artifactId>
        <version>2.0.0</version>
    </dependency>
    <!-- 日志 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.9</version>
    </dependency>
</dependencies>

2 配置文件(application.yml)

call:
  api:
    url: https://api.example.com/v1/call
    account-sid: your_account_sid
    auth-token: your_auth_token
  sip:
    domain: sip.example.com
    username: your_username
    password: your_password
  # 外呼配置
  settings:
    timeout: 30
    max-retries: 3
    batch-size: 10
logging:
  level:
    com.example.call: DEBUG

3 测试代码

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class CallServiceTest {
    @Autowired
    private CloudCallService callService;
    @Autowired
    private IntegratedCallService integratedCallService;
    @Test
    void testSingleCall() {
        // 测试单次呼叫
        CallResult result = callService.makeCall("13800138000", "13900139000");
        System.out.println("呼叫结果: " + result);
    }
    @Test
    void testBatchCall() {
        // 测试批量呼叫
        List<CallTask> tasks = new ArrayList<>();
        tasks.add(new CallTask("13800138000", "13900139000"));
        tasks.add(new CallTask("13800138001", "13900139001"));
        tasks.add(new CallTask("13800138002", "13900139002"));
        integratedCallService.batchMakeCall(tasks);
        for (CallTask task : tasks) {
            System.out.println("任务: " + task.getCalleeNumber() + 
                             " 状态: " + task.getStatus());
        }
    }
}

注意事项

  1. 号码格式: 确保电话号码格式正确,通常为E.164格式(如+8613800138000
  2. 并发控制: 批量外呼时注意平台的并发限制
  3. 错误处理: 实现完善的错误处理和重试机制
  4. 日志记录: 记录每次呼叫的详细日志便于排查
  5. 安全认证: 保护好API密钥和认证信息
  6. 合规性: 遵守相关的通信法规和平台使用条款

这些代码示例展示了Java中实现外呼功能的不同方式,您可以根据实际需求选择合适的实现方案。

抱歉,评论功能暂时关闭!