170 lines
5.2 KiB
PHP
170 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\SystemConfig;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
class SmsService
|
|
{
|
|
/**
|
|
* 发送验证码
|
|
*/
|
|
public function sendCode(string $phone, string $type = 'login', int $expireMinutes = 5): array
|
|
{
|
|
$config = $this->getConfig();
|
|
|
|
if (!$config['enabled']) {
|
|
return ['success' => false, 'message' => '短信服务未启用'];
|
|
}
|
|
|
|
// 生成6位验证码
|
|
$code = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
|
|
|
|
// 存储验证码
|
|
$cacheKey = "sms_code:{$phone}:{$type}";
|
|
Cache::put($cacheKey, [
|
|
'code' => $code,
|
|
'expires_at' => now()->addMinutes($expireMinutes)->timestamp,
|
|
], now()->addMinutes($expireMinutes + 5));
|
|
|
|
// 调用阿里云短信
|
|
if ($config['driver'] === 'aliyun') {
|
|
return $this->sendAliyunSms($phone, $code, $type, $config);
|
|
}
|
|
|
|
// 模拟模式(开发环境)
|
|
\Log::info("SMS Code [{$type}] for {$phone}: {$code}");
|
|
|
|
return [
|
|
'success' => true,
|
|
'message' => '验证码已发送(模拟模式)',
|
|
'data' => ['code' => $code], // 开发环境返回验证码
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 验证验证码
|
|
*/
|
|
public function verifyCode(string $phone, string $code, string $type = 'login'): bool
|
|
{
|
|
$cacheKey = "sms_code:{$phone}:{$type}";
|
|
$data = Cache::get($cacheKey);
|
|
|
|
if (!$data) {
|
|
return false;
|
|
}
|
|
|
|
if ($data['expires_at'] < now()->timestamp) {
|
|
Cache::forget($cacheKey);
|
|
return false;
|
|
}
|
|
|
|
if ($data['code'] !== $code) {
|
|
return false;
|
|
}
|
|
|
|
// 验证成功后删除
|
|
Cache::forget($cacheKey);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 获取短信配置
|
|
*/
|
|
public function getConfig(): array
|
|
{
|
|
$cacheKey = 'sms_service_config';
|
|
|
|
return Cache::remember($cacheKey, 3600, function () {
|
|
return [
|
|
'enabled' => SystemConfig::getValue('sms', 'enabled', false),
|
|
'driver' => SystemConfig::getValue('sms', 'driver', 'aliyun'),
|
|
'access_key_id' => SystemConfig::getValue('sms', 'access_key_id', ''),
|
|
'access_key_secret' => SystemConfig::getValue('sms', 'access_key_secret', ''),
|
|
'sign_name' => SystemConfig::getValue('sms', 'sign_name', ''),
|
|
'template_codes' => SystemConfig::getValue('sms', 'template_codes', []),
|
|
];
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 阿里云短信发送
|
|
*/
|
|
private function sendAliyunSms(string $phone, string $code, string $type, array $config): array
|
|
{
|
|
$templateCode = $this->getTemplateCode($type, $config['template_codes']);
|
|
|
|
if (!$templateCode) {
|
|
return ['success' => false, 'message' => '未配置短信模板'];
|
|
}
|
|
|
|
try {
|
|
$params = [
|
|
'PhoneNumbers' => $phone,
|
|
'SignName' => $config['sign_name'],
|
|
'TemplateCode' => $templateCode,
|
|
'TemplateParam' => json_encode(['code' => $code]),
|
|
];
|
|
|
|
// 阿里云 OpenAPI 签名
|
|
$signResult = $this->aliyunSign($config['access_key_id'], $config['access_key_secret'], $params);
|
|
|
|
$response = \Illuminate\Support\Facades\Http::withHeaders([
|
|
'Content-Type' => 'application/x-www-form-urlencoded',
|
|
])->asForm()->post('https://dysmsapi.aliyuncs.com/', array_merge($params, $signResult));
|
|
|
|
$result = $response->json();
|
|
|
|
if (($result['Code'] ?? '') === 'OK') {
|
|
return ['success' => true, 'message' => '发送成功'];
|
|
}
|
|
|
|
return ['success' => false, 'message' => $result['Message'] ?? '发送失败'];
|
|
} catch (\Exception $e) {
|
|
\Log::error('阿里云短信发送失败: ' . $e->getMessage());
|
|
return ['success' => false, 'message' => '短信发送失败'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取模板代码
|
|
*/
|
|
private function getTemplateCode(string $type, array $templates): ?string
|
|
{
|
|
$map = [
|
|
'login' => $templates['login'] ?? null,
|
|
'reset_password' => $templates['reset_password'] ?? $templates['reset'] ?? null,
|
|
'pair' => $templates['pair'] ?? $templates['login'] ?? null,
|
|
];
|
|
|
|
return $map[$type] ?? null;
|
|
}
|
|
|
|
/**
|
|
* 阿里云 API 签名
|
|
*/
|
|
private function aliyunSign(string $accessKeyId, string $accessKeySecret, array $params): array
|
|
{
|
|
$params['AccessKeyId'] = $accessKeyId;
|
|
$params['Format'] = 'JSON';
|
|
$params['SignatureMethod'] = 'HMAC-SHA1';
|
|
$params['SignatureVersion'] = '1.0';
|
|
$params['SignatureNonce'] = uniqid();
|
|
$params['Timestamp'] = gmdate('Y-m-d\TH:i:s\Z');
|
|
$params['Version'] = '2017-05-25';
|
|
|
|
ksort($params);
|
|
$stringToSign = 'POST&%2F&' . urlencode(http_build_query($params, '', '&', PHP_QUERY_RFC3986));
|
|
|
|
$signature = base64_encode(
|
|
hash_hmac('sha1', $stringToSign, $accessKeySecret . '&', true)
|
|
);
|
|
|
|
$params['Signature'] = $signature;
|
|
|
|
return $params;
|
|
}
|
|
}
|