324 lines
10 KiB
PHP
324 lines
10 KiB
PHP
<?php
|
||
|
||
// app/Repositories/SttMedicalHistoryRepository.php
|
||
namespace App\Repositories;
|
||
|
||
use App\Models\SI\SttMedicalHistory as SiMedicalHistory;
|
||
use App\Models\Mis\SttMedicalHistory as MisMedicalHistory;
|
||
use Illuminate\Database\Eloquent\Builder;
|
||
use Illuminate\Pagination\LengthAwarePaginator;
|
||
use Illuminate\Support\Collection;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Str;
|
||
|
||
class MedicalHistoryRepository
|
||
{
|
||
protected SiMedicalHistory $siModel;
|
||
protected MisMedicalHistory $misModel;
|
||
|
||
// Поля по умолчанию
|
||
protected array $defaultFieldsSI = [
|
||
'id',
|
||
'family',
|
||
'name',
|
||
'ot',
|
||
'daterecipient',
|
||
'dateextract',
|
||
'medcardnum'
|
||
];
|
||
|
||
protected array $defaultFieldsMis = [
|
||
'MedicalHistoryID',
|
||
'FAMILY',
|
||
'Name',
|
||
'OT',
|
||
'DateRecipient',
|
||
'DateExtract',
|
||
'MedCardNum'
|
||
];
|
||
|
||
public function __construct(
|
||
SiMedicalHistory $siModel,
|
||
MisMedicalHistory $misModel
|
||
) {
|
||
$this->siModel = $siModel;
|
||
$this->misModel = $misModel;
|
||
}
|
||
|
||
/**
|
||
* ПОИСК ТОЛЬКО В POSTGRESQL (основной)
|
||
*/
|
||
public function searchInPostgres(
|
||
?string $searchText,
|
||
?string $dateFrom,
|
||
?string $dateTo,
|
||
?int $status,
|
||
int $pageSize = 15,
|
||
string $sortBy = 'dateextract',
|
||
string $sortDir = 'desc',
|
||
): LengthAwarePaginator {
|
||
|
||
$query = $this->siModel->newQuery();
|
||
|
||
$this->applyStatusFilter($query, $status);
|
||
$this->applyDateFilter($query, 'dateextract', $dateFrom, $dateTo);
|
||
$this->applySearchConditions($query, $searchText);
|
||
|
||
return $query->select($this->defaultFieldsSI)
|
||
->orderBy($sortBy, $sortDir)
|
||
->paginate($pageSize)
|
||
->through(function ($item) {
|
||
$item->database_source = 'postgresql';
|
||
return $item;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* ПОИСК ТОЛЬКО В MSSQL (исторический)
|
||
*/
|
||
public function searchInMssql(
|
||
?string $searchText,
|
||
?string $dateFrom,
|
||
?string $dateTo,
|
||
?int $status,
|
||
int $pageSize = 15,
|
||
string $sortBy = 'DateExtract',
|
||
string $sortDir = 'desc'
|
||
): LengthAwarePaginator {
|
||
|
||
$query = $this->misModel->newQuery();
|
||
|
||
$this->applyStatusFilter($query, $status);
|
||
$this->applyDateFilter($query, 'DateExtract', $dateFrom, $dateTo);
|
||
$this->applySearchConditions($query, $searchText, 'mssql');
|
||
|
||
return $query->select($this->defaultFieldsMis)
|
||
->orderBy($sortBy, $sortDir)
|
||
->paginate($pageSize)
|
||
->through(function ($item) {
|
||
$item->database_source = 'mssql';
|
||
return $item;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* УМНЫЙ ПОИСК (сначала PostgreSQL, потом MSSQL если мало)
|
||
*/
|
||
public function smartSearch(
|
||
?string $searchText,
|
||
?string $dateFrom,
|
||
?string $dateTo,
|
||
int $pageSize = 15
|
||
): LengthAwarePaginator {
|
||
|
||
// 1. Ищем в PostgreSQL
|
||
$pgPaginator = $this->searchInPostgres($searchText, $dateFrom, $dateTo, $pageSize);
|
||
|
||
// 2. Если мало результатов, добавляем из MSSQL
|
||
if ($pgPaginator->total() < $pageSize && $pgPaginator->total() < 10) {
|
||
$needed = $pageSize - $pgPaginator->count();
|
||
|
||
$mssqlResults = $this->searchInMssql($searchText, $dateFrom, $dateTo, $needed)
|
||
->getCollection();
|
||
|
||
$allResults = $pgPaginator->getCollection()
|
||
->merge($mssqlResults)
|
||
->sortByDesc('dateextract')
|
||
->values();
|
||
|
||
return new LengthAwarePaginator(
|
||
$allResults,
|
||
$pgPaginator->total() + $mssqlResults->count(),
|
||
$pageSize,
|
||
request()->get('page', 1)
|
||
);
|
||
}
|
||
|
||
return $pgPaginator;
|
||
}
|
||
|
||
/**
|
||
* РАЗДЕЛЬНЫЙ ПОИСК (отдельные результаты по БД)
|
||
*/
|
||
public function separateSearch(
|
||
?string $searchText,
|
||
?string $dateFrom,
|
||
?string $dateTo,
|
||
?int $status,
|
||
int $perPage = 15,
|
||
): array {
|
||
$pgPaginator = $this->searchInPostgres($searchText, $dateFrom, $dateTo, $status, $perPage);
|
||
$mssqlPaginator = $this->searchInMssql($searchText, $dateFrom, $dateTo, $status, $perPage);
|
||
|
||
return [
|
||
'si' => $pgPaginator,
|
||
'mis' => $mssqlPaginator,
|
||
'stats' => [
|
||
'si_total' => $pgPaginator->total(),
|
||
'mis_total' => $mssqlPaginator->total(),
|
||
'combined_total' => $pgPaginator->total() + $mssqlPaginator->total(),
|
||
]
|
||
];
|
||
}
|
||
|
||
/**
|
||
* ПРИМЕНЕНИЕ УСЛОВИЙ ПОИСКА
|
||
*/
|
||
private function applySearchConditions($query, ?string $searchText, string $dbType = 'postgresql'): void
|
||
{
|
||
// Разбиваем поисковую строку на слова
|
||
$words = preg_split('/\s+/', trim($searchText));
|
||
$words = array_filter($words);
|
||
|
||
if (empty($words)) {
|
||
return;
|
||
}
|
||
|
||
$query->where(function($q) use ($words, $dbType) {
|
||
// Если одно слово - ищем в любом поле
|
||
if (count($words) === 1) {
|
||
$word = Str::ucfirst($words[0]);
|
||
$pattern = $word . '%'; // Префиксный поиск для использования индекса
|
||
|
||
if ($dbType === 'postgresql') {
|
||
$q->where('family', 'LIKE', $pattern)
|
||
->orWhere('name', 'LIKE', $pattern)
|
||
->orWhere('ot', 'LIKE', $pattern);
|
||
} else {
|
||
$q->where('FAMILY', 'LIKE', $pattern)
|
||
->orWhere('Name', 'LIKE', $pattern)
|
||
->orWhere('OT', 'LIKE', $pattern);
|
||
}
|
||
}
|
||
// Если несколько слов - предполагаем Ф+И+О
|
||
else {
|
||
// Берем первые 3 слова
|
||
$family = !empty($words[0]) ? Str::ucfirst($words[0]) : null;
|
||
$name = !empty($words[1]) ? Str::ucfirst($words[1]) : null;
|
||
$ot = !empty($words[2]) ? Str::ucfirst($words[2]) : null;
|
||
|
||
if ($dbType === 'postgresql') {
|
||
$q->where('family', 'LIKE', $family . '%');
|
||
} else {
|
||
$q->where('FAMILY', 'LIKE', $family . '%');
|
||
}
|
||
|
||
if ($name) {
|
||
if ($dbType === 'postgresql') {
|
||
$q->where('name', 'LIKE', $name . '%');
|
||
} else {
|
||
$q->where('Name', 'LIKE', $name . '%');
|
||
}
|
||
}
|
||
|
||
if ($ot) {
|
||
if ($dbType === 'postgresql') {
|
||
$q->where('ot', 'LIKE', $ot . '%');
|
||
} else {
|
||
$q->where('OT', 'LIKE', $ot . '%');
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
private function applyStatusFilter(Builder $query, ?int $value)
|
||
{
|
||
if ($value === 0) {
|
||
$query->doesntHave('archiveInfo');
|
||
} else {
|
||
if ($query->withExists('archiveInfo') && !empty($value)) {
|
||
$query->withWhereHas('archiveInfo', function ($q) use ($value) {
|
||
$q->where('status_id', '=', $value);
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ФИЛЬТР ПО ДАТЕ
|
||
*/
|
||
private function applyDateFilter($query, string $dateField, ?string $dateFrom, ?string $dateTo): void
|
||
{
|
||
if (!empty($dateFrom)) {
|
||
$query->whereDate($dateField, '>=', $dateFrom);
|
||
}
|
||
|
||
if (!empty($dateTo)) {
|
||
$query->whereDate($dateField, '<=', $dateTo);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ПОЛУЧИТЬ СТАТИСТИКУ ПО БАЗАМ
|
||
*/
|
||
public function getDatabaseStats(): array
|
||
{
|
||
return [
|
||
'postgresql' => [
|
||
'total' => $this->siModel->count(),
|
||
'connection' => config('database.connections.pgsql.database'),
|
||
'status' => 'primary',
|
||
],
|
||
'mssql' => [
|
||
'total' => $this->misModel->count(),
|
||
'connection' => config('database.connections.sqlsrv.database'),
|
||
'status' => 'secondary',
|
||
]
|
||
];
|
||
}
|
||
|
||
/**
|
||
* БЫСТРЫЙ ПОИСК ПО ТИПУ
|
||
*/
|
||
public function quickSearch(string $type, string $value): Collection
|
||
{
|
||
return match($type) {
|
||
'medcard' => $this->searchByMedCard($value),
|
||
'enp' => $this->searchByEnp($value),
|
||
'fullname' => $this->searchByFullName($value),
|
||
default => collect(),
|
||
};
|
||
}
|
||
|
||
private function searchByMedCard(string $medCard): Collection
|
||
{
|
||
$pgResults = $this->siModel->where('medcardnum', 'LIKE', "%{$medCard}%")
|
||
->limit(10)
|
||
->get()
|
||
->each(fn($item) => $item->database_source = 'postgresql');
|
||
|
||
$mssqlResults = $this->misModel->where('medcardnum', 'LIKE', "%{$medCard}%")
|
||
->limit(10)
|
||
->get()
|
||
->each(fn($item) => $item->database_source = 'mssql');
|
||
|
||
return $pgResults->concat($mssqlResults)
|
||
->sortBy('medcardnum')
|
||
->values();
|
||
}
|
||
|
||
private function searchByFullName(string $name): Collection
|
||
{
|
||
$pattern = "%{$name}%";
|
||
|
||
$pgResults = $this->siModel->where('family', 'ILIKE', $pattern)
|
||
->orWhere('name', 'ILIKE', $pattern)
|
||
->orWhere('ot', 'ILIKE', $pattern)
|
||
->limit(10)
|
||
->get()
|
||
->each(fn($item) => $item->database_source = 'postgresql');
|
||
|
||
$mssqlResults = $this->misModel->where('family', 'LIKE', $pattern)
|
||
->orWhere('name', 'LIKE', $pattern)
|
||
->orWhere('ot', 'LIKE', $pattern)
|
||
->limit(10)
|
||
->get()
|
||
->each(fn($item) => $item->database_source = 'mssql');
|
||
|
||
return $pgResults->concat($mssqlResults)
|
||
->sortBy('family')
|
||
->values();
|
||
}
|
||
}
|