本文目录导读:

我来为您提供一个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中实现外呼功能的不同方式,您可以根据实际需求选择合适的实现方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播