109 lines
3.5 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
namespace Modules\EcommerceFinance\Services;
use PDO;
use PDOException;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
/**
* JianSun SQL Server 数据库连接管理
* 使用 PDO_ODBC 直连 SQL Server(无需 sqlsrv 扩展)
*/
class JianSunConnection
{
private static ?PDO $instance = null;
/**
* 获取 PDO 连接实例(单例模式)
*/
public static function get(): PDO
{
if (self::$instance === null) {
self::$instance = self::create();
}
return self::$instance;
}
/**
* 创建新的 PDO 连接
*/
public static function create(): PDO
{
if (! config('jiansun.enabled')) {
throw new ServiceUnavailableHttpException(
60,
'上游数据同步尚未启用,请联系管理员。'
);
}
$connection = config('jiansun.connection', []);
$host = trim((string) ($connection['host'] ?? ''));
$port = (int) ($connection['port'] ?? 1433);
$db = trim((string) ($connection['database'] ?? ''));
$user = trim((string) ($connection['username'] ?? ''));
$pass = (string) ($connection['password'] ?? '');
$driver = trim((string) ($connection['odbc_driver'] ?? 'FreeTDS'), '{} ');
$tdsVersion = trim((string) ($connection['tds_version'] ?? '7.4'));
$connectTimeout = max((int) ($connection['connect_timeout'] ?? 5), 1);
$queryTimeout = max((int) ($connection['query_timeout'] ?? 15), 1);
if ($host === '' || $db === '' || $user === '' || $pass === '' || $driver === '') {
throw new ServiceUnavailableHttpException(
60,
'上游数据库配置不完整,请联系管理员。'
);
}
$dsn = sprintf(
'odbc:Driver={%s};Server=%s;Port=%d;Database=%s;UID=%s;PWD=%s;TDS_Version=%s;ClientCharset=UTF-8;Connect Timeout=%d',
str_replace('}', '}}', $driver),
self::quoteOdbcValue($host),
$port,
self::quoteOdbcValue($db),
self::quoteOdbcValue($user),
self::quoteOdbcValue($pass),
$tdsVersion,
$connectTimeout
);
try {
// FreeTDS 的 PDO_ODBC 需要 UID/PWD 位于连接字符串中;异常链不得
// 携带 DSN,否则日志或调试堆栈可能泄露凭据。
$pdo = new PDO($dsn, null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_TIMEOUT => $queryTimeout,
PDO::ATTR_PERSISTENT => false,
]);
return $pdo;
} catch (PDOException $e) {
Log::warning('JianSun 数据库连接失败。', [
'sql_state' => (string) $e->getCode(),
'exception' => $e::class,
]);
throw new ServiceUnavailableHttpException(
60,
'上游数据库暂时无法连接,请稍后再试。'
);
}
}
/**
* 重置连接(用于长时间运行或测试场景)
*/
public static function reset(): void
{
self::$instance = null;
}
private static function quoteOdbcValue(string $value): string
{
return '{' . str_replace('}', '}}', $value) . '}';
}
}