From 9ba53b614561c947495aacef79803827331032e8 Mon Sep 17 00:00:00 2001 From: long <452591453@qq.com> Date: Tue, 28 Jul 2026 15:15:14 +0800 Subject: [PATCH] debug --- server/.example.env | 4 ++ .../service/pharmacy/EjPharmacyClient.php | 60 +++++++++++++++++++ server/config/ej_pharmacy.php | 5 ++ 3 files changed, 69 insertions(+) diff --git a/server/.example.env b/server/.example.env index b5636f98..e3fcce4f 100755 --- a/server/.example.env +++ b/server/.example.env @@ -8,6 +8,10 @@ EJ_PHARMACY_BASE_URL = "https://ej.example.com" EJ_PHARMACY_APP_KEY = "" EJ_PHARMACY_APP_SECRET = "" EJ_PHARMACY_CALLBACK_SECRET = "" +# auto:NSS cURL 自动使用 PHP OpenSSL;也可显式填写 openssl 或 curl。 +EJ_PHARMACY_HTTP_TRANSPORT = "auto" +# CentOS/RHEL 7 可填写 /etc/pki/tls/certs/ca-bundle.crt;留空使用 PHP 默认 CA。 +EJ_PHARMACY_CA_FILE = "" EJ_PHARMACY_SUBMISSION_LEASE_SECONDS = 300 EJ_PHARMACY_CONNECT_TIMEOUT = 5 EJ_PHARMACY_REQUEST_TIMEOUT = 30 diff --git a/server/app/common/service/pharmacy/EjPharmacyClient.php b/server/app/common/service/pharmacy/EjPharmacyClient.php index 44723be6..23c64020 100644 --- a/server/app/common/service/pharmacy/EjPharmacyClient.php +++ b/server/app/common/service/pharmacy/EjPharmacyClient.php @@ -104,6 +104,14 @@ final class EjPharmacyClient return ($this->transport)(strtoupper($method), $pathWithQuery, $body, $headers); } + $configuredTransport = strtolower((string) Config::get('ej_pharmacy.http_transport', 'auto')); + $curlSsl = strtoupper((string) ((function_exists('curl_version') ? curl_version() : [])['ssl_version'] ?? '')); + if ($configuredTransport === 'openssl' + || ($configuredTransport === 'auto' && str_starts_with($curlSsl, 'NSS/')) + ) { + return $this->requestWithOpenSsl($method, $pathWithQuery, $body, $headers, $requestId); + } + $ch = curl_init($this->baseUrl . $pathWithQuery); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, @@ -131,4 +139,56 @@ final class EjPharmacyClient return ['http_status' => $httpStatus, 'body' => $decoded, 'request_id' => $requestId]; } + + /** @param array $headers @return array{http_status:int,body:array,request_id:string} */ + private function requestWithOpenSsl( + string $method, + string $path, + string $body, + array $headers, + string $requestId + ): array { + $ssl = [ + 'verify_peer' => true, + 'verify_peer_name' => true, + 'allow_self_signed' => false, + ]; + $caFile = trim((string) Config::get('ej_pharmacy.ca_file', '')); + if ($caFile !== '') { + $ssl['cafile'] = $caFile; + } + + $context = stream_context_create([ + 'http' => [ + 'method' => strtoupper($method), + 'header' => implode("\r\n", $headers), + 'content' => $body, + 'ignore_errors' => true, + 'timeout' => (int) Config::get('ej_pharmacy.request_timeout', 30), + 'protocol_version' => 1.1, + ], + 'ssl' => $ssl, + ]); + $raw = @file_get_contents($this->baseUrl . $path, false, $context); + if ($raw === false) { + $lastError = error_get_last(); + $message = is_array($lastError) ? (string) ($lastError['message'] ?? '') : ''; + throw new RuntimeException('恩济药房通信失败:' . ($message !== '' ? $message : 'OpenSSL 请求失败')); + } + + $httpStatus = 0; + $responseHeaders = $http_response_header ?? []; + foreach (array_reverse($responseHeaders) as $responseHeader) { + if (preg_match('/^HTTP\/\S+\s+(\d{3})\b/i', $responseHeader, $matches)) { + $httpStatus = (int) $matches[1]; + break; + } + } + $decoded = json_decode((string) $raw, true); + if (!is_array($decoded)) { + throw new RuntimeException('恩济药房返回了无效 JSON,HTTP ' . $httpStatus); + } + + return ['http_status' => $httpStatus, 'body' => $decoded, 'request_id' => $requestId]; + } } diff --git a/server/config/ej_pharmacy.php b/server/config/ej_pharmacy.php index 5ea46d2f..44c2aa89 100644 --- a/server/config/ej_pharmacy.php +++ b/server/config/ej_pharmacy.php @@ -13,6 +13,11 @@ return [ 'app_key' => trim((string) env('EJ_PHARMACY_APP_KEY', '')), 'app_secret' => trim((string) env('EJ_PHARMACY_APP_SECRET', '')), 'callback_secret' => trim((string) env('EJ_PHARMACY_CALLBACK_SECRET', '')), + // RHEL/CentOS 7 ships cURL with NSS, which rejects some otherwise valid + // server certificate chains. `auto` selects the OpenSSL stream transport + // for NSS builds while keeping cURL for OpenSSL builds. + 'http_transport' => strtolower(trim((string) env('EJ_PHARMACY_HTTP_TRANSPORT', 'auto'))), + 'ca_file' => trim((string) env('EJ_PHARMACY_CA_FILE', '')), 'connect_timeout' => max((int) env('EJ_PHARMACY_CONNECT_TIMEOUT', 5), 1), 'request_timeout' => max((int) env('EJ_PHARMACY_REQUEST_TIMEOUT', 30), 5), ];