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