Files
zyt/server/app/common/service/pharmacy/EjPharmacyClient.php
T
2026-07-22 14:50:34 +08:00

135 lines
5.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace app\common\service\pharmacy;
use Closure;
use RuntimeException;
use think\facade\Config;
final class EjPharmacyClient
{
private string $baseUrl;
private string $appKey;
private string $appSecret;
/** @var null|Closure(string,string,string,array<int,string>):array{http_status:int,body:array<string,mixed>,request_id:string} */
private ?Closure $transport;
public function __construct(
?string $baseUrl = null,
?string $appKey = null,
?string $appSecret = null,
?callable $transport = null
)
{
$this->baseUrl = rtrim($baseUrl ?? (string) Config::get('ej_pharmacy.base_url', ''), '/');
$this->appKey = $appKey ?? (string) Config::get('ej_pharmacy.app_key', '');
$this->appSecret = $appSecret ?? (string) Config::get('ej_pharmacy.app_secret', '');
if ($this->baseUrl === '' || $this->appKey === '' || $this->appSecret === '') {
throw new RuntimeException('恩济药房接口未配置完整');
}
$this->transport = $transport === null ? null : Closure::fromCallable($transport);
}
public static function isConfigured(): bool
{
return (bool) Config::get('ej_pharmacy.enabled', false)
&& trim((string) Config::get('ej_pharmacy.base_url', '')) !== ''
&& trim((string) Config::get('ej_pharmacy.app_key', '')) !== ''
&& trim((string) Config::get('ej_pharmacy.app_secret', '')) !== '';
}
/** @return array{http_status:int,body:array<string,mixed>,request_id:string} */
public function medicines(int $after = 0, int $limit = 100): array
{
return $this->request('GET', '/api/openapi/v1/medicines', null, [
'after' => max($after, 0),
'limit' => min(max($limit, 1), 500),
]);
}
/** @param array<string,mixed> $payload @return array{http_status:int,body:array<string,mixed>,request_id:string} */
public function importMedicines(array $payload): array
{
return $this->request('POST', '/api/openapi/v1/medicine-imports', $payload);
}
/** @param array<string,mixed> $payload @return array{http_status:int,body:array<string,mixed>,request_id:string} */
public function createPrescriptionOrder(array $payload): array
{
return $this->request('POST', '/api/openapi/v1/prescription-orders', $payload);
}
/** @return array{http_status:int,body:array<string,mixed>,request_id:string} */
public function prescriptionOrder(string $sourceOrderNo, int $sourceRevision = 0): array
{
$query = ['source_system' => 'zyt'];
if ($sourceRevision > 0) {
$query['source_revision'] = $sourceRevision;
}
return $this->request(
'GET',
'/api/openapi/v1/prescription-orders/' . rawurlencode($sourceOrderNo),
null,
$query
);
}
/** @param array<string,mixed>|null $payload @param array<string,int|string> $query @return array{http_status:int,body:array<string,mixed>,request_id:string} */
private function request(string $method, string $path, ?array $payload = null, array $query = []): array
{
$queryString = $query === [] ? '' : http_build_query($query, '', '&', PHP_QUERY_RFC3986);
$pathWithQuery = $path . ($queryString !== '' ? '?' . $queryString : '');
$body = $payload === null
? ''
: (string) json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
$requestId = bin2hex(random_bytes(16));
$canonical = EjPharmacySignature::canonical($method, $pathWithQuery, $timestamp, $nonce, $body);
$headers = [
'Accept: application/json',
'Content-Type: application/json; charset=utf-8',
'X-App-Key: ' . $this->appKey,
'X-Timestamp: ' . $timestamp,
'X-Nonce: ' . $nonce,
'X-Signature: ' . EjPharmacySignature::sign($this->appSecret, $canonical),
'X-Request-Id: ' . $requestId,
'Expect:',
];
if ($this->transport !== null) {
return ($this->transport)(strtoupper($method), $pathWithQuery, $body, $headers);
}
$ch = curl_init($this->baseUrl . $pathWithQuery);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => strtoupper($method),
CURLOPT_HTTPHEADER => $headers,
CURLOPT_CONNECTTIMEOUT => (int) Config::get('ej_pharmacy.connect_timeout', 5),
CURLOPT_TIMEOUT => (int) Config::get('ej_pharmacy.request_timeout', 30),
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
]);
if ($payload !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
$raw = curl_exec($ch);
$httpStatus = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($raw === false) {
throw new RuntimeException('恩济药房通信失败:' . $error);
}
$decoded = json_decode((string) $raw, true);
if (!is_array($decoded)) {
throw new RuntimeException('恩济药房返回了无效 JSONHTTP ' . $httpStatus);
}
return ['http_status' => $httpStatus, 'body' => $decoded, 'request_id' => $requestId];
}
}