bootstrapCaBundle(); $config = $this->config(); if (!$this->isConfigured($config)) { if ($this->isDebugMode()) { return [ 'provider' => 'debug', 'request_id' => 'debug', 'biz_id' => 'debug', 'raw_body' => [ 'Code' => 'OK', 'Message' => 'DEBUG_SMS_BYPASS', ], 'debug_code' => $code, ]; } throw new \RuntimeException('短信配置未完成,请先在后台系统配置中填写阿里云短信参数'); } $client = new Dysmsapi(new Config([ 'accessKeyId' => $config['access_key_id'], 'accessKeySecret' => $config['access_key_secret'], 'regionId' => $config['region_id'] ?: 'cn-hangzhou', 'endpoint' => $config['endpoint'] ?: null, ])); $response = $client->sendSms(new SendSmsRequest([ 'phoneNumbers' => $mobile, 'signName' => $config['sign_name'], 'templateCode' => $config['login_template_code'], 'templateParam' => json_encode(['code' => $code], JSON_UNESCAPED_UNICODE), ])); $body = $response->body ? $response->body->toMap() : []; $responseCode = (string)($body['Code'] ?? ''); if ($responseCode !== 'OK') { throw new \RuntimeException((string)($body['Message'] ?? '短信发送失败')); } return [ 'provider' => 'aliyun_sms', 'request_id' => (string)($body['RequestId'] ?? ''), 'biz_id' => (string)($body['BizId'] ?? ''), 'raw_body' => $body, 'debug_code' => null, ]; } private function config(): array { $rows = Db::name('system_configs') ->where('config_group', 'sms') ->select() ->toArray(); $map = []; foreach ($rows as $row) { $map[$row['config_key']] = trim((string)($row['config_value'] ?? '')); } return [ 'access_key_id' => $map['access_key_id'] ?? '', 'access_key_secret' => $map['access_key_secret'] ?? '', 'sign_name' => $map['sign_name'] ?? '', 'login_template_code' => $map['login_template_code'] ?? '', 'region_id' => $map['region_id'] ?? 'cn-hangzhou', 'endpoint' => $map['endpoint'] ?? '', ]; } private function isConfigured(array $config): bool { return $config['access_key_id'] !== '' && $config['access_key_secret'] !== '' && $config['sign_name'] !== '' && $config['login_template_code'] !== ''; } private function isDebugMode(): bool { return in_array(strtolower((string)($_ENV['APP_DEBUG'] ?? 'false')), ['1', 'true'], true); } private function bootstrapCaBundle(): void { if (ini_get('curl.cainfo') || ini_get('openssl.cafile')) { return; } foreach ($this->candidateCaFiles() as $path) { if (!is_file($path)) { continue; } ini_set('curl.cainfo', $path); ini_set('openssl.cafile', $path); putenv('CURL_CA_BUNDLE=' . $path); putenv('SSL_CERT_FILE=' . $path); return; } } private function candidateCaFiles(): array { return [ '/etc/ssl/cert.pem', '/private/etc/ssl/cert.pem', '/opt/homebrew/etc/openssl@3/cert.pem', '/usr/local/etc/openssl@3/cert.pem', ]; } }