本文目录导读:

我来为您提供一个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());
}
}
}
注意事项
- 号码格式: 确保电话号码格式正确,通常为E.164格式(如
+8613800138000) - 并发控制: 批量外呼时注意平台的并发限制
- 错误处理: 实现完善的错误处理和重试机制
- 日志记录: 记录每次呼叫的详细日志便于排查
- 安全认证: 保护好API密钥和认证信息
- 合规性: 遵守相关的通信法规和平台使用条款
这些代码示例展示了Java中实现外呼功能的不同方式,您可以根据实际需求选择合适的实现方案。