提交其他文件
This commit is contained in:
291
app/common/service/VoiceNotifyService.php
Normal file
291
app/common/service/VoiceNotifyService.php
Normal file
@ -0,0 +1,291 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user