Files
kartoteka/app/Repositories/MedicalHistoryRepository.php
2025-12-12 17:10:05 +09:00

319 lines
10 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
// app/Repositories/SttMedicalHistoryRepository.php
namespace App\Repositories;
use App\Models\SI\SttMedicalHistory as SiMedicalHistory;
use App\Models\Mis\SttMedicalHistory as MisMedicalHistory;
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($query, ?int $value)
{
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();
}
}