Files
chazhi_admin_broker/app/common/service/VoiceNotifyService.php
2026-03-11 18:24:59 +08:00

291 lines
7.6 KiB
PHP

<?php
namespace app\common\service;
use think\facade\Log;
/**
* 语音通知服务
* Class VoiceNotifyService
* @package app\common\service
*/
class VoiceNotifyService
{
// 云呼平台API地址
protected $baseUrl = 'https://101.37.133.245:11008';
// 平台和版本
protected $platform = 'voice';
protected $version = '1.0.0';
// 超时时间
protected $timeout = 30;
// 用户凭证(从文档示例中获取)
protected $appId = '100860';
protected $token = '9ce17087d80140ce85e2b2e8cedc493e';
// 功能代码:语音通知
protected $functionCode = 'notify';
/**
* 构造函数(可传入自定义凭证)
* @param string $appId 应用ID
* @param string $token 令牌
*/
public function __construct($appId = null, $token = null)
{
if ($appId) {
$this->appId = $appId;
}
if ($token) {
$this->token = $token;
}
}
/**
* 发送语音通知
* @param string $phone 被叫手机号
* @param string $templateId 模板ID
* @param array $templateArgs 模板参数
* @param array $options 其他选项
* @return array
*/
public function send($phone, $templateId, $templateArgs = [], $options = [])
{
try {
// 验证手机号
if (!$this->checkPhone($phone)) {
return ['success' => false, 'message' => '手机号格式错误'];
}
// 验证模板ID
if (empty($templateId)) {
return ['success' => false, 'message' => '模板ID不能为空'];
}
// 准备请求数据
$data = $this->prepareData($phone, $templateId, $templateArgs, $options);
// 生成签名和时间戳
$timestamp = $this->getTimestamp();
$sig = $this->generateSig($timestamp);
// 发送请求
$result = $this->makeRequest($data, $sig, $timestamp);
// 返回结果
return [
'success' => true,
'call_id' => $result['callId'] ?? '',
'result' => $result['result'] ?? '',
'message' => $result['message'] ?? '发送成功',
'raw_data' => $result
];
} catch (\Exception $e) {
Log::error('语音通知发送失败: ' . $e->getMessage());
return [
'success' => false,
'message' => '发送失败: ' . $e->getMessage()
];
}
}
/**
* 准备请求数据
* @param string $phone
* @param string $templateId
* @param array $templateArgs
* @param array $options
* @return array
*/
protected function prepareData($phone, $templateId, $templateArgs, $options)
{
$data = [
'calleeNumber' => $phone,
'templateId' => $templateId,
];
// 模板参数
if (!empty($templateArgs)) {
$data['templateArgs'] = $templateArgs;
} else {
// 如果模板无参数,传空对象
$data['templateArgs'] = new \stdClass();
}
// 可选参数处理
if (!empty($options['displayNumber'])) {
$data['displayNumber'] = $options['displayNumber'];
}
if (isset($options['replayTimes']) && in_array($options['replayTimes'], [1, 2, 3])) {
$data['replayTimes'] = $options['replayTimes'];
}
if (isset($options['ttsFirst']) && in_array($options['ttsFirst'], [0, 1])) {
$data['ttsFirst'] = $options['ttsFirst'];
}
if (isset($options['recordButton']) && in_array($options['recordButton'], [0, 1])) {
$data['recordButton'] = $options['recordButton'];
}
if (isset($options['callRec']) && in_array($options['callRec'], [0, 1])) {
$data['callRec'] = $options['callRec'];
}
if (!empty($options['callbackUrl'])) {
$data['callbackUrl'] = $options['callbackUrl'];
}
return $data;
}
/**
* 生成签名
* @param string $timestamp
* @return string
*/
protected function generateSig($timestamp)
{
$str = $this->appId . $this->token . $timestamp;
return md5($str);
}
/**
* 获取时间戳(毫秒)
* @return string
*/
protected function getTimestamp()
{
return round(microtime(true) * 1000);
}
/**
* 发送HTTP请求
* @param array $data
* @param string $sig
* @param string $timestamp
* @return array
* @throws \Exception
*/
protected function makeRequest($data, $sig, $timestamp)
{
// 构建URL
$url = sprintf('%s/%s/%s/%s/%s/%s',
$this->baseUrl,
$this->platform,
$this->version,
$this->functionCode,
$this->appId,
$sig
);
// 构建Authorization头部
$auth = base64_encode($this->appId . ':' . $timestamp);
// 准备请求头
$headers = [
'Host: ' . parse_url($this->baseUrl, PHP_URL_HOST),
'Authorization: ' . $auth,
'Accept: application/json',
'Content-Type: application/json',
];
// 初始化cURL
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data, JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
]);
// 执行请求
$response = curl_exec($ch);
// 检查错误
if ($error = curl_error($ch)) {
throw new \Exception('网络请求失败: ' . $error);
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// 检查HTTP状态码
if ($httpCode !== 200) {
throw new \Exception('HTTP错误: ' . $httpCode);
}
// 解析JSON响应
$result = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \Exception('响应数据解析失败');
}
// 检查响应结果
if (!isset($result['result'])) {
throw new \Exception('响应缺少result字段');
}
// 000000 表示成功
if ($result['result'] !== '000000') {
$message = $result['message'] ?? '未知错误';
throw new \Exception($message . ' (code: ' . $result['result'] . ')');
}
return $result;
}
/**
* 验证手机号格式
* @param string $phone
* @return bool
*/
protected function checkPhone($phone)
{
return preg_match('/^1[3-9]\d{9}$/', $phone) === 1;
}
/**
* 设置自定义凭证
* @param string $appId
* @param string $token
* @return $this
*/
public function setCredentials($appId, $token)
{
$this->appId = $appId;
$this->token = $token;
return $this;
}
/**
* 设置API地址
* @param string $url
* @return $this
*/
public function setBaseUrl($url)
{
$this->baseUrl = $url;
return $this;
}
/**
* 设置超时时间
* @param int $seconds
* @return $this
*/
public function setTimeout($seconds)
{
$this->timeout = $seconds;
return $this;
}
}