* переписал функции прототипов в сервисы
* оптимизация доставки контента до клиента * переписал запросы выборок * убрал из подсчета переведенных * добавил сохранение метрикам для вывода в дашборд
This commit is contained in:
@@ -18,6 +18,7 @@ use App\Models\UnwantedEvent;
|
||||
use App\Models\User;
|
||||
use App\Services\DateRangeService;
|
||||
use App\Services\MisPatientService;
|
||||
use App\Services\ReportService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
@@ -28,14 +29,11 @@ use Inertia\Inertia;
|
||||
|
||||
class ReportController extends Controller
|
||||
{
|
||||
protected MisPatientService $misPatientService;
|
||||
protected DateRangeService $dateService;
|
||||
|
||||
public function __construct(MisPatientService $misPatientService, DateRangeService $dateRangeService)
|
||||
{
|
||||
$this->misPatientService = $misPatientService;
|
||||
$this->dateService = $dateRangeService;
|
||||
}
|
||||
public function __construct(
|
||||
protected MisPatientService $misPatientService,
|
||||
protected ReportService $reportService,
|
||||
protected DateRangeService $dateRangeService)
|
||||
{ }
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
@@ -614,78 +612,20 @@ class ReportController extends Controller
|
||||
public function getPatients(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$data = $request->validate([
|
||||
'status' => 'required|string', // plan emergency observation deceased
|
||||
|
||||
$validated = $request->validate([
|
||||
'status' => 'required|string',
|
||||
'startAt' => 'nullable',
|
||||
'endAt' => 'nullable',
|
||||
]);
|
||||
|
||||
// Получаем базовые данные
|
||||
$status = $data['status'];
|
||||
$model = new MisMedicalHistory();
|
||||
$misDepartmentId = $request->user()->department->rf_mis_department_id;
|
||||
$userDepartmentId = $request->user()->department->department_id;
|
||||
$branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)->value('StationarBranchID');
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
if (!$branchId) {
|
||||
return response()->json([]);
|
||||
}
|
||||
|
||||
// Определяем даты в зависимости от роли
|
||||
[$startDate, $endDate] = $this->getDateRangeForRole($user, $data['startAt'] ?? null, $data['endAt'] ?? null);
|
||||
|
||||
// Для заведующего/админа ищем отчет по endDate (дате просмотра)
|
||||
$reportIds = [];
|
||||
$reports = $this->getReportsForDateRange($user->rf_department_id, $startDate, $endDate);
|
||||
$reportIds = $reports->pluck('report_id')->toArray();
|
||||
|
||||
// Определяем, используем ли мы снапшоты
|
||||
$useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || Carbon::parse($endDate)->isToday() === false;
|
||||
|
||||
// Обработка каждого статуса
|
||||
if ($useSnapshots) {
|
||||
// Используем снапшоты: получаем ID пациентов, затем данные из реплики
|
||||
$patients = match($status) {
|
||||
'plan', 'emergency' => $this->getPatientsFromSnapshotsUsingReplica($status, $reportIds, $branchId, $startDate, $endDate),
|
||||
'observation' => $this->getObservationPatientsFromSnapshotsUsingReplica($userDepartmentId, $reportIds),
|
||||
'outcome-discharged' => $this->getOutcomePatientsFromSnapshotsUsingReplica('discharged', $reportIds, $branchId, $startDate, $endDate),
|
||||
'outcome-transferred' => $this->getOutcomePatientsFromSnapshotsUsingReplica('transferred', $reportIds, $branchId, $startDate, $endDate),
|
||||
'outcome-deceased' => $this->getOutcomePatientsFromSnapshotsUsingReplica('deceased', $reportIds, $branchId, $startDate, $endDate),
|
||||
default => collect()
|
||||
};
|
||||
} else {
|
||||
// // Используем реплику для врачей или когда нет отчетов
|
||||
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
|
||||
|
||||
$patients = match($status) {
|
||||
'plan', 'emergency' => $this->getPlanOrEmergencyPatients($status, $isHeadOrAdmin, $branchId, $startDate, $endDate),
|
||||
'observation' => $this->getObservationPatients($userDepartmentId),
|
||||
'outcome-discharged' => $this->getDischargedPatients($branchId, $startDate, $endDate),
|
||||
'outcome-transferred' => $this->getTransferredPatients($branchId, $startDate, $endDate),
|
||||
'outcome-deceased' => $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate),
|
||||
default => 0
|
||||
};
|
||||
}
|
||||
|
||||
// Если есть пациенты, добавляем дополнительные данные
|
||||
if ($patients->isNotEmpty()) {
|
||||
$patients = $patients->map(function ($item, $index) use ($branchId, $startDate, $endDate) {
|
||||
$item->num = $index + 1;
|
||||
$item->misStationarBranchId = $branchId;
|
||||
$item->startDate = $startDate;
|
||||
$item->endDate = $endDate;
|
||||
return $item;
|
||||
});
|
||||
|
||||
// Загружаем связи
|
||||
$patients->load(['migrations' => function ($query) use ($startDate, $endDate, $branchId) {
|
||||
$query->whereHas('diagnosis', function ($q) {
|
||||
$q->where('rf_DiagnosTypeID', 3);
|
||||
})
|
||||
->with('diagnosis.mkb')
|
||||
->where('rf_StationarBranchID', $branchId);
|
||||
}]);
|
||||
}
|
||||
$patients = $this->reportService->getPatientsByStatus(
|
||||
Auth::user(),
|
||||
$validated['status'],
|
||||
$dateRange
|
||||
);
|
||||
|
||||
return response()->json(FormattedPatientResource::collection($patients));
|
||||
}
|
||||
@@ -693,81 +633,20 @@ class ReportController extends Controller
|
||||
public function getPatientsCount(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$data = $request->validate([
|
||||
'status' => 'required|string', // plan emergency observation deceased
|
||||
|
||||
$validated = $request->validate([
|
||||
'status' => 'required|string',
|
||||
'startAt' => 'nullable',
|
||||
'endAt' => 'nullable',
|
||||
]);
|
||||
|
||||
// Получаем базовые данные
|
||||
$status = $data['status'];
|
||||
$model = new MisMedicalHistory();
|
||||
$misDepartmentId = $request->user()->department->rf_mis_department_id;
|
||||
$userDepartmentId = $request->user()->department->department_id;
|
||||
$branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)->value('StationarBranchID');
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
if (!$branchId) {
|
||||
return response()->json([]);
|
||||
}
|
||||
|
||||
// Определяем даты в зависимости от роли
|
||||
[$startDate, $endDate] = $this->getDateRangeForRole($user, $data['startAt'] ?? null, $data['endAt'] ?? null);
|
||||
|
||||
// Для заведующего/админа ищем отчеты по промежутку дат
|
||||
$reportIds = [];
|
||||
$reports = $this->getReportsForDateRange($user->rf_department_id, $startDate, $endDate);
|
||||
$reportIds = $reports->pluck('report_id')->toArray();
|
||||
|
||||
// Определяем, используем ли мы снапшоты
|
||||
$useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || Carbon::parse($endDate)->isToday() === false;
|
||||
|
||||
if ($useSnapshots) {
|
||||
// Считаем из снапшотов
|
||||
$patientTypeMap = [
|
||||
'plan' => 'plan',
|
||||
'emergency' => 'emergency',
|
||||
'observation' => 'observation',
|
||||
'outcome' => null,
|
||||
'outcome-discharged' => 'discharged',
|
||||
'outcome-transferred' => 'transferred',
|
||||
'outcome-deceased' => 'deceased'
|
||||
];
|
||||
|
||||
$patientType = $patientTypeMap[$status] ?? null;
|
||||
|
||||
if ($status === 'outcome') {
|
||||
// Считаем уникальных пациентов по всем типам исходов
|
||||
$count = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
|
||||
->whereIn('patient_type', ['discharged', 'transferred', 'deceased'])
|
||||
->distinct('rf_medicalhistory_id')
|
||||
->count('rf_medicalhistory_id');
|
||||
} elseif ($patientType) {
|
||||
$count = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
|
||||
->where('patient_type', $patientType)
|
||||
->distinct('rf_medicalhistory_id')
|
||||
->count('rf_medicalhistory_id');
|
||||
} else {
|
||||
$count = 0;
|
||||
}
|
||||
} else {
|
||||
// Определяем, является ли пользователь заведующим/администратором
|
||||
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
|
||||
|
||||
$isOutcomeOrObservation = in_array($status, ['outcome', 'observation']);
|
||||
if ($isOutcomeOrObservation)
|
||||
{
|
||||
switch ($status) {
|
||||
case 'observation':
|
||||
$count = ObservationPatient::where('rf_department_id', $userDepartmentId)->count();
|
||||
break;
|
||||
case 'outcome':
|
||||
$count = $this->getAllOutcomePatients($branchId, $startDate, $endDate, true);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$count = $this->getPlanOrEmergencyPatients($status, $isHeadOrAdmin, $branchId, $startDate, $endDate, true);
|
||||
}
|
||||
}
|
||||
$count = $this->reportService->getPatientsCountByStatus(
|
||||
Auth::user(),
|
||||
$validated['status'],
|
||||
$dateRange,
|
||||
);
|
||||
|
||||
return response()->json($count);
|
||||
}
|
||||
|
||||
@@ -3,118 +3,158 @@
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\MisStationarBranch;
|
||||
use App\Http\Resources\Mis\FormattedPatientResource;
|
||||
use App\Models\MetrikaGroup;
|
||||
use App\Models\MisLpuDoctor;
|
||||
use App\Models\Report;
|
||||
use App\Models\UnwantedEvent;
|
||||
use App\Services\DateRangeService;
|
||||
use App\Services\MisPatientService;
|
||||
use App\Services\ReportService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class ReportController extends Controller
|
||||
{
|
||||
protected DateRangeService $dateService;
|
||||
protected MisPatientService $misPatientService;
|
||||
|
||||
public function __construct(MisPatientService $misPatientService, DateRangeService $dateRangeService)
|
||||
{
|
||||
$this->misPatientService = $misPatientService;
|
||||
$this->dateService = $dateRangeService;
|
||||
}
|
||||
public function __construct(
|
||||
protected ReportService $reportService,
|
||||
protected DateRangeService $dateRangeService
|
||||
) {}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = \Auth::user();
|
||||
$user = Auth::user();
|
||||
$department = $user->department;
|
||||
|
||||
$queryStartDate = $request->query('startAt');
|
||||
$queryEndDate = $request->query('endAt');
|
||||
[$startDate, $endDate] = $this->dateService->getDateRangeForUser($user, $queryStartDate, $queryEndDate);
|
||||
$isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate);
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
// Если диапазон содержит сутки
|
||||
if ($isRangeOneDay) {
|
||||
// Устанавливаем дату отчета, как последний день из выборки
|
||||
$dateReport = $endDate;
|
||||
} else {
|
||||
// Устанавливаем дату отчета, как выборку
|
||||
$dateReport = [$startDate, $endDate];
|
||||
}
|
||||
// Получаем статистику
|
||||
$statistics = $this->reportService->getReportStatistics($user, $dateRange);
|
||||
|
||||
if ($isRangeOneDay) {
|
||||
// Статистика выводится с нарастающим числом
|
||||
$reports = $department->reports()
|
||||
->whereDate('created_at', $dateReport)
|
||||
->get();
|
||||
} else {
|
||||
$reports = $department->reports()
|
||||
->whereBetween('created_at', $dateReport)
|
||||
->get();
|
||||
}
|
||||
// Получаем метрики
|
||||
$metrikaGroup = MetrikaGroup::whereMetrikaGroupId(2)->first();
|
||||
$metrikaItems = $metrikaGroup->metrikaItems;
|
||||
|
||||
$isReports = $reports->count() > 0;
|
||||
|
||||
$allCount = 0; $outcomeCount = 0; $currentCount = 0; $occupiedBeds = 0; $planCount = 0;
|
||||
$emergencyCount = 0; $planSurgical = 0; $emergencySurgical = 0; $transferredCount = 0;
|
||||
$deceasedCount = 0;
|
||||
if ($isReports) {
|
||||
foreach ($reports as $report) {
|
||||
$allCount += $this->getMetrikaResultFromReport($report, 3, $isRangeOneDay);
|
||||
$currentCount += $this->getMetrikaResultFromReport($report, 8, false);
|
||||
$occupiedBeds += $this->getMetrikaResultFromReport($report, 8, $isRangeOneDay);
|
||||
$planCount += $this->getMetrikaResultFromReport($report, 4, $isRangeOneDay);
|
||||
$emergencyCount += $this->getMetrikaResultFromReport($report, 12, $isRangeOneDay);
|
||||
$planSurgical += $this->getMetrikaResultFromReport($report, 11, $isRangeOneDay);
|
||||
$emergencySurgical += $this->getMetrikaResultFromReport($report, 10, $isRangeOneDay);
|
||||
$transferredCount += $this->getMetrikaResultFromReport($report, 13, $isRangeOneDay);
|
||||
$outcomeCount += $this->getMetrikaResultFromReport($report, 7, $isRangeOneDay);
|
||||
$deceasedCount += $this->getMetrikaResultFromReport($report, 9, $isRangeOneDay);
|
||||
}
|
||||
} else {
|
||||
$misDepartmentId = $request->user()->department->rf_mis_department_id;
|
||||
|
||||
$branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
|
||||
->value('StationarBranchID');
|
||||
|
||||
$planCount = $this->misPatientService->getInStationarPatients('plan', $branchId, $dateReport)->count();
|
||||
}
|
||||
|
||||
$bedsCount = $department->metrikaDefault()
|
||||
->where('rf_metrika_item_id', 1)->value('value');
|
||||
|
||||
$percentLoadedBeds = $bedsCount > 0 ? round($occupiedBeds * 100 / $bedsCount) : 0;
|
||||
// Получаем информацию о текущем отчете
|
||||
$reportInfo = $this->reportService->getCurrentReportInfo($user, $dateRange);
|
||||
|
||||
return Inertia::render('Report/Index', [
|
||||
'department' => [
|
||||
'beds' => $bedsCount,
|
||||
'recipients' => [
|
||||
'all' => $allCount,
|
||||
'plan' => $planCount,
|
||||
'emergency' => $emergencyCount,
|
||||
'transferred' => $transferredCount,
|
||||
],
|
||||
'outcome' => $outcomeCount,
|
||||
'consist' => $currentCount,
|
||||
'percentLoadedBeds' => $percentLoadedBeds,
|
||||
'surgical' => [
|
||||
'plan' => $planSurgical,
|
||||
'emergency' => $emergencySurgical
|
||||
],
|
||||
'deceased' => $deceasedCount,
|
||||
'beds' => $department->beds,
|
||||
'percentLoadedBeds' => $this->calculateBedOccupancy($department, $user),
|
||||
...$statistics,
|
||||
],
|
||||
'dates' => [
|
||||
'startAt' => $dateRange->startTimestamp(),
|
||||
'endAt' => $dateRange->endTimestamp()
|
||||
],
|
||||
'report' => $reportInfo,
|
||||
'metrikaItems' => $metrikaItems,
|
||||
'userId' => $reportInfo['userId'],
|
||||
'userName' => $reportInfo['userName']
|
||||
]);
|
||||
}
|
||||
|
||||
private function getMetrikaResultFromReport(Report $report, int $metrikaItem, bool $sum = true)
|
||||
public function store(Request $request)
|
||||
{
|
||||
if ($sum) {
|
||||
return (int) ($report->metrikaResults()
|
||||
->where('rf_metrika_item_id', $metrikaItem)
|
||||
->sum(DB::raw('CAST(value AS INTEGER)')) ?: 0);
|
||||
}
|
||||
$validated = $request->validate([
|
||||
'metrics' => 'required|array',
|
||||
'observationPatients' => 'nullable|array',
|
||||
'departmentId' => 'required|integer',
|
||||
'unwantedEvents' => 'nullable|array',
|
||||
'dates' => 'required|array',
|
||||
'userId' => 'required|integer',
|
||||
'reportId' => 'nullable|integer'
|
||||
]);
|
||||
|
||||
return (int) ($report->metrikaResults()
|
||||
->where('rf_metrika_item_id', $metrikaItem)
|
||||
->value('value') ?: 0);
|
||||
$report = $this->reportService->storeReport($validated, Auth::user());
|
||||
|
||||
return response()->json([
|
||||
'message' => 'success',
|
||||
'report_id' => $report->report_id
|
||||
]);
|
||||
}
|
||||
|
||||
public function getPatients(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$validated = $request->validate([
|
||||
'status' => 'required|string',
|
||||
'startAt' => 'nullable',
|
||||
'endAt' => 'nullable',
|
||||
]);
|
||||
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
$patients = $this->reportService->getPatientsByStatus(
|
||||
Auth::user(),
|
||||
$validated['status'],
|
||||
$dateRange
|
||||
);
|
||||
|
||||
return response()->json(FormattedPatientResource::collection($patients));
|
||||
}
|
||||
|
||||
public function getPatientsCount(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
$validated = $request->validate([
|
||||
'status' => 'required|string',
|
||||
'startAt' => 'nullable',
|
||||
'endAt' => 'nullable',
|
||||
]);
|
||||
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
$count = $this->reportService->getPatientsCountByStatus(
|
||||
Auth::user(),
|
||||
$validated['status'],
|
||||
$dateRange,
|
||||
);
|
||||
|
||||
return response()->json($count);
|
||||
}
|
||||
|
||||
public function removeObservation(Request $request)
|
||||
{
|
||||
$validated = $request->validate(['id' => 'required|integer']);
|
||||
|
||||
$this->reportService->removeObservationPatient($validated['id']);
|
||||
|
||||
return response()->json(['message' => 'Удалено'], 200);
|
||||
}
|
||||
|
||||
public function removeUnwantedEvent(UnwantedEvent $unwantedEvent)
|
||||
{
|
||||
$unwantedEvent->delete();
|
||||
return response()->json(['message' => 'Удалено'], 200);
|
||||
}
|
||||
|
||||
public function getDepartmentUsers()
|
||||
{
|
||||
$users = MisLpuDoctor::select(['LPUDoctorID', 'FAM_V', 'IM_V', 'OT_V'])
|
||||
->active()
|
||||
->inMyDepartment()
|
||||
->get();
|
||||
|
||||
return response()->json($users, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Рассчитать загруженность коек
|
||||
*/
|
||||
private function calculateBedOccupancy($department, $user): int
|
||||
{
|
||||
$beds = (int)$department->metrikaDefault()->where('rf_metrika_item_id', 1)->first()->value;
|
||||
$occupiedBeds = optional(Report::where('rf_department_id', $user->rf_department_id)
|
||||
->join('metrika_results', 'reports.report_id', '=', 'metrika_results.rf_report_id')
|
||||
->where('metrika_results.rf_metrika_item_id', 8)
|
||||
->orderBy('sent_at', 'desc')
|
||||
->first())->value ?? 0;
|
||||
|
||||
return $beds > 0 ? round(intval($occupiedBeds) * 100 / $beds) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,21 +20,12 @@ class FormattedPatientResource extends JsonResource
|
||||
return [
|
||||
'id' => $this->MedicalHistoryID,
|
||||
'num' => $this->num,
|
||||
'mkb' => $this->whenLoaded('migrations', function () {
|
||||
'mkb.ds' => $this->migrations->first()->diagnosis->first()?->mkb?->DS,
|
||||
'operations' => $this->surgicalOperations->map(function ($operation) {
|
||||
return [
|
||||
'ds' => $this->migrations()->first()->diagnosis()->first()?->mkb()->first()->DS ?? null,
|
||||
'name' => $this->migrations()->first()->diagnosis()->first()?->mkb()->first()->NAME ?? null,
|
||||
'code' => $operation->serviceMedical->ServiceMedicalCode
|
||||
];
|
||||
}),
|
||||
'operations' => $this->whenLoaded('surgicalOperations', function () {
|
||||
return $this->operationOnBranch($this->misStationarBranchId, $this->startDate, $this->endDate)
|
||||
->get()
|
||||
->map(function (MisSurgicalOperation $operation) {
|
||||
return [
|
||||
'code' => $operation->serviceMedical->ServiceMedicalCode ?? null,
|
||||
];
|
||||
});
|
||||
}),
|
||||
'fullname' => Str::ucwords(Str::lower("$this->FAMILY $this->Name $this->OT")),
|
||||
'age' => Carbon::parse($this->BD)->diff(Carbon::now())->format('%y'),
|
||||
'birth_date' => Carbon::parse($this->BD)->format('d.m.Y'),
|
||||
|
||||
Reference in New Issue
Block a user