7 changed files with 491 additions and 236 deletions
@ -0,0 +1,42 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace app\sms; |
||||
|
|
||||
|
use app\sms\contracts\SmsDriverInterface; |
||||
|
|
||||
|
class Sms |
||||
|
{ |
||||
|
protected $driver; |
||||
|
/** |
||||
|
* Sms constructor. |
||||
|
* @param string|null $channel |
||||
|
* @throws \Exception |
||||
|
*/ |
||||
|
public function __construct(?string $channel = null) |
||||
|
{ |
||||
|
$channel = $channel ?: config('sms.default'); |
||||
|
$drivers = config('sms.channels'); |
||||
|
|
||||
|
if (!isset($drivers[$channel])) { |
||||
|
throw new \Exception("短信通道 [$channel] 未配置"); |
||||
|
} |
||||
|
|
||||
|
$class = $drivers[$channel]['driver']; |
||||
|
$config = $drivers[$channel]['config']; |
||||
|
|
||||
|
if (!class_exists($class)) { |
||||
|
throw new \Exception("短信驱动类 [$class] 不存在"); |
||||
|
} |
||||
|
|
||||
|
$this->driver = new $class($config); |
||||
|
|
||||
|
if (!($this->driver instanceof SmsDriverInterface)) { |
||||
|
throw new \Exception("[$class] 必须实现 SmsDriverInterface 接口"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function send(string $mobile, string $templateId, array $data = []): array |
||||
|
{ |
||||
|
return $this->driver->send($mobile, $templateId, $data); |
||||
|
} |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace app\sms\contracts; |
||||
|
|
||||
|
interface SmsDriverInterface |
||||
|
{ |
||||
|
public function send(string $mobile, string $templateId, array $data = []): array; |
||||
|
} |
@ -0,0 +1,165 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace app\sms\drivers; |
||||
|
|
||||
|
use app\sms\contracts\SmsDriverInterface; |
||||
|
use GuzzleHttp\Client; |
||||
|
|
||||
|
class WorldSmsDriver implements SmsDriverInterface |
||||
|
{ |
||||
|
protected $config; |
||||
|
protected $client; |
||||
|
|
||||
|
public function __construct(array $config) |
||||
|
{ |
||||
|
$this->config = $config; |
||||
|
$this->client = new Client([ |
||||
|
'base_uri' => rtrim($this->config['host'], '/') . '/', |
||||
|
'timeout' => 5.0, |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发送短信 |
||||
|
* @param string $mobile 手机号(多个用逗号分隔) |
||||
|
* @param string $templateId 未使用模板可留空 |
||||
|
* @param array $data ['content' => '短信内容'] |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function send(string $mobile, string $templateId = '', array $data = []): array |
||||
|
{ |
||||
|
$url = 'sendsms'; |
||||
|
$body = [ |
||||
|
'account' => $this->config['account'], |
||||
|
'password' => $this->config['password'], |
||||
|
'numbers' => $mobile, |
||||
|
'smstype' => $data['smstype'] ?? 0, |
||||
|
'sender' => $data['title'] ?? '', |
||||
|
'content' => $data['content'] ?? '', |
||||
|
]; |
||||
|
try { |
||||
|
$response = $this->client->request('POST', $url, [ |
||||
|
'headers' => [ |
||||
|
'Content-Type' => 'application/json;charset=utf-8' |
||||
|
], |
||||
|
'body' => json_encode($body, JSON_UNESCAPED_UNICODE), |
||||
|
]); |
||||
|
$result = json_decode($response->getBody()->getContents(), true); |
||||
|
|
||||
|
return [ |
||||
|
'success' => $result['status'] === 0, |
||||
|
'data' => $result, |
||||
|
'msg' => $result['status'] === 0 ? '发送成功' : '发送失败', |
||||
|
]; |
||||
|
} catch (\Throwable $e) { |
||||
|
|
||||
|
return [ |
||||
|
'success' => false, |
||||
|
'msg' => '请求失败:' . $e->getMessage(), |
||||
|
'data' => [], |
||||
|
]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 查询余额 |
||||
|
*/ |
||||
|
public function getBalance(): array |
||||
|
{ |
||||
|
$url = 'getbalance'; |
||||
|
$body = [ |
||||
|
'version' => '3.4', |
||||
|
'account' => $this->config['account'], |
||||
|
'password' => $this->config['password'], |
||||
|
]; |
||||
|
|
||||
|
return $this->getJson($url, $body); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 查询短信状态(单条) |
||||
|
*/ |
||||
|
public function getStatus(string $msgid): array |
||||
|
{ |
||||
|
$url = 'getstatus'; |
||||
|
$body = [ |
||||
|
'account' => $this->config['account'], |
||||
|
'password' => $this->config['password'], |
||||
|
'msgid' => $msgid, |
||||
|
]; |
||||
|
|
||||
|
return $this->getJson($url, $body); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取接收短信 |
||||
|
*/ |
||||
|
public function getInbox(): array |
||||
|
{ |
||||
|
$url = 'getinbox'; |
||||
|
$body = [ |
||||
|
'account' => $this->config['account'], |
||||
|
'password' => $this->config['password'], |
||||
|
]; |
||||
|
|
||||
|
return $this->getJson($url, $body); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通用 POST 请求 |
||||
|
*/ |
||||
|
protected function postJson(string $url, array $body): array |
||||
|
{ |
||||
|
try { |
||||
|
$response = $this->client->post($url, [ |
||||
|
'headers' => [ |
||||
|
'Content-Type' => 'application/json;charset=utf-8', |
||||
|
], |
||||
|
'json' => $body, |
||||
|
]); |
||||
|
|
||||
|
$result = json_decode($response->getBody()->getContents(), true); |
||||
|
|
||||
|
return [ |
||||
|
'success' => $result['status'] === 0, |
||||
|
'msg' => $result['status'] === 0 ? '操作成功' : ($result['message'] ?? '失败'), |
||||
|
'data' => $result, |
||||
|
]; |
||||
|
} catch (\Throwable $e) { |
||||
|
return [ |
||||
|
'success' => false, |
||||
|
'msg' => '请求异常: ' . $e->getMessage(), |
||||
|
'data' => [], |
||||
|
]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通用 GET 请求 |
||||
|
*/ |
||||
|
protected function getJson(string $url, array $query = []): array |
||||
|
{ |
||||
|
try { |
||||
|
$response = $this->client->get($url, [ |
||||
|
'headers' => [ |
||||
|
'Accept' => 'application/json', |
||||
|
], |
||||
|
'query' => $query, |
||||
|
]); |
||||
|
|
||||
|
$result = json_decode($response->getBody()->getContents(), true); |
||||
|
|
||||
|
return [ |
||||
|
'success' => $result['status'] === 0, |
||||
|
'msg' => $result['status'] === 0 ? '操作成功' : ($result['message'] ?? '失败'), |
||||
|
'data' => $result, |
||||
|
]; |
||||
|
} catch (\Throwable $e) { |
||||
|
return [ |
||||
|
'success' => false, |
||||
|
'msg' => '请求异常: ' . $e->getMessage(), |
||||
|
'data' => [], |
||||
|
]; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
<?php |
||||
|
return [ |
||||
|
'default' => 'world', |
||||
|
|
||||
|
'channels' => [ |
||||
|
'world' => [ |
||||
|
'driver' => \app\sms\drivers\WorldSmsDriver::class, |
||||
|
'config' => [ |
||||
|
'host' => 'http://8.218.111.176:20003/', |
||||
|
'account' => '068109OTP', |
||||
|
'password' => 'y5CjJzUOL', |
||||
|
], |
||||
|
], |
||||
|
// 其他通道... |
||||
|
], |
||||
|
]; |
Loading…
Reference in new issue