Профиль хирургии
This commit is contained in:
@@ -50,35 +50,36 @@ class ReportService
|
||||
*/
|
||||
public function storeReport(array $data, User $user, $fillableAuto = false): Report
|
||||
{
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$report = DB::transaction(function () use ($data, $user, $fillableAuto) {
|
||||
$report = $this->createOrUpdateReport($data, $user);
|
||||
|
||||
// Сохраняем все, что НЕ зависит от других отчетов
|
||||
$this->saveMetrics($report, $data['metrics'] ?? []);
|
||||
$this->saveUnwantedEvents($report, $data['unwantedEvents'] ?? []);
|
||||
$this->saveObservationPatients($report, $data['observationPatients'] ?? [], $user->rf_department_id);
|
||||
|
||||
// Сохраняем снапшоты пациентов
|
||||
$this->snapshotService->createPatientSnapshots($report, $user, $data['dates'], $fillableAuto);
|
||||
|
||||
return $report;
|
||||
});
|
||||
|
||||
DB::transaction(function () use ($report) {
|
||||
// Сохраняем метрику среднего койко-дня из снапшотов
|
||||
$this->saveAverageBedDaysMetricFromSnapshots($report);
|
||||
|
||||
DB::commit();
|
||||
$this->saveLethalMetricFromSnapshots($report);
|
||||
|
||||
// ОЧИСТКА КЭША ПОСЛЕ УСПЕШНОГО СОЗДАНИЯ ОТЧЕТА
|
||||
$this->clearCacheAfterReportCreation($user, $report);
|
||||
$this->savePreoperativeMetric($report);
|
||||
|
||||
return $report;
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
$this->saveDepartmentLoadedMetric($report);
|
||||
});
|
||||
|
||||
$this->clearCacheAfterReportCreation($user, $report);
|
||||
|
||||
return $report;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить метрику среднего койко-дня из снапшотов отчета
|
||||
* Сохранить метрику койко-дня из снапшотов отчета
|
||||
*/
|
||||
protected function saveAverageBedDaysMetricFromSnapshots(Report $report): void
|
||||
{
|
||||
@@ -128,7 +129,7 @@ class ReportService
|
||||
}
|
||||
}
|
||||
|
||||
$avgBedDays = $validCount > 0 ? round($totalDays / $validCount, 1) : 0;
|
||||
$bedDays = $validCount > 0 ? $totalDays: 0;
|
||||
|
||||
// Сохраняем метрику
|
||||
MetrikaResult::updateOrCreate(
|
||||
@@ -136,10 +137,10 @@ class ReportService
|
||||
'rf_report_id' => $report->report_id,
|
||||
'rf_metrika_item_id' => 18,
|
||||
],
|
||||
['value' => $avgBedDays]
|
||||
['value' => $bedDays]
|
||||
);
|
||||
|
||||
\Log::info("Saved average bed days metric for report {$report->report_id}: {$avgBedDays} (from {$validCount} patients)");
|
||||
//\Log::info("Saved average bed days metric for report {$report->report_id}: {$avgBedDays} (from {$validCount} patients)");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
\Log::error("Failed to save average bed days metric: " . $e->getMessage());
|
||||
@@ -147,16 +148,120 @@ class ReportService
|
||||
}
|
||||
}
|
||||
|
||||
protected function saveLethalMetricFromSnapshots(Report $report): void
|
||||
{
|
||||
// Получаем все снапшоты выписанных пациентов из этого отчета
|
||||
$snapshots = MedicalHistorySnapshot::where('rf_report_id', $report->report_id)
|
||||
->whereIn('patient_type', ['discharged', 'deceased']) // выписанные и умершие
|
||||
->with('medicalHistory')
|
||||
->get();
|
||||
|
||||
if ($snapshots->isEmpty()) {
|
||||
// Если нет выписанных, сохраняем 0
|
||||
MetrikaResult::updateOrCreate(
|
||||
[
|
||||
'rf_report_id' => $report->report_id,
|
||||
'rf_metrika_item_id' => 18,
|
||||
],
|
||||
['value' => 0]
|
||||
);
|
||||
|
||||
\Log::info("No discharged patients in report {$report->report_id}, saved 0");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить предоперационный койко-день из снапшотов
|
||||
*/
|
||||
protected function savePreoperativeMetric(Report $report): void
|
||||
{
|
||||
// 1. Получаем ВСЕ предыдущие отчеты этого отделения
|
||||
$allPreviousReports = Report::where('rf_department_id', $report->rf_department_id)
|
||||
->where('sent_at', '<=', $report->sent_at)
|
||||
->orderBy('sent_at')
|
||||
->pluck('report_id');
|
||||
|
||||
if ($allPreviousReports->isEmpty()) {
|
||||
$this->saveMetric($report, 21, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Получаем ВСЕХ пациентов из всех отчетов (discharged + deceased)
|
||||
$allPatients = MedicalHistorySnapshot::whereIn('rf_report_id', $allPreviousReports)
|
||||
->whereIn('patient_type', ['discharged', 'deceased'])
|
||||
->pluck('rf_medicalhistory_id')
|
||||
->unique();
|
||||
|
||||
if ($allPatients->isEmpty()) {
|
||||
$this->saveMetric($report, 21, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Получаем операции для ВСЕХ пациентов
|
||||
$operations = DB::table('stt_surgicaloperation as so')
|
||||
->join('stt_migrationpatient as mp', 'so.rf_MedicalHistoryID', '=', 'mp.rf_MedicalHistoryID')
|
||||
->whereIn('so.rf_MedicalHistoryID', $allPatients)
|
||||
->whereNotNull('so.Date')
|
||||
->whereNotNull('mp.DateIngoing')
|
||||
->select(
|
||||
'so.rf_MedicalHistoryID',
|
||||
DB::raw('MIN(so."Date") as first_operation'),
|
||||
DB::raw('MIN(mp."DateIngoing") as first_admission')
|
||||
)
|
||||
->groupBy('so.rf_MedicalHistoryID')
|
||||
->get();
|
||||
|
||||
if ($operations->isEmpty()) {
|
||||
$this->saveMetric($report, 21, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Считаем общее количество дней и пациентов
|
||||
$totalDays = 0;
|
||||
$patientCount = 0;
|
||||
|
||||
foreach ($operations as $op) {
|
||||
$days = Carbon::parse($op->first_admission)
|
||||
->diffInDays(Carbon::parse($op->first_operation));
|
||||
|
||||
if ($days >= 0) {
|
||||
$totalDays += $days;
|
||||
$patientCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Нарастающий итог = общее количество дней / общее количество пациентов
|
||||
$avgDays = $patientCount > 0 ? round($totalDays / $patientCount, 1) : 0;
|
||||
|
||||
// 6. Сохраняем метрику
|
||||
$this->saveMetric($report, 21, $avgDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить предоперационный койко-день из снапшотов
|
||||
*/
|
||||
protected function saveDepartmentLoadedMetric(Report $report): void
|
||||
{
|
||||
// Получаем все снапшоты выписанных пациентов из этого отчета
|
||||
$currentCount = $report->metrikaResults()->where('rf_metrika_item_id', 8)->value('value');
|
||||
$bedsCount = $report->metrikaResults()->where('rf_metrika_item_id', 1)->value('value');
|
||||
|
||||
$percentLoaded = $bedsCount > 0 ? round($currentCount * 100 / $bedsCount) : 0;
|
||||
|
||||
$this->saveMetric($report, 22, $percentLoaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Очистить кэш после создания отчета
|
||||
*/
|
||||
private function clearCacheAfterReportCreation(User $user, Report $report): void
|
||||
{
|
||||
// Очищаем кэш статистики для пользователя
|
||||
$this->statisticsService->clearStatisticsCache($user);
|
||||
// $this->statisticsService->clearStatisticsCache($user);
|
||||
|
||||
// Также можно очистить кэш для всех пользователей отдела
|
||||
$this->statisticsService->clearDepartmentStatisticsCache($user->rf_department_id);
|
||||
// $this->statisticsService->clearDepartmentStatisticsCache($user->rf_department_id);
|
||||
|
||||
// Очищаем кэш за сегодня и вчера (так как отчеты влияют на эти даты)
|
||||
$this->clearDailyCache($user, $report->created_at);
|
||||
@@ -305,6 +410,22 @@ class ReportService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить метрику отчета
|
||||
*/
|
||||
private function saveMetric(Report $report, int $metrikaId, float $value): void
|
||||
{
|
||||
MetrikaResult::updateOrCreate(
|
||||
[
|
||||
'rf_report_id' => $report->report_id,
|
||||
'rf_metrika_item_id' => $metrikaId,
|
||||
],
|
||||
[
|
||||
'value' => $value,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранить нежелательные события
|
||||
*/
|
||||
@@ -460,6 +581,7 @@ class ReportService
|
||||
);
|
||||
|
||||
$reportIds = $reports->pluck('report_id')->toArray();
|
||||
$lastReport = array_first($reportIds);
|
||||
|
||||
// Получаем статистику из снапшотов
|
||||
$snapshotStats = [
|
||||
@@ -471,7 +593,8 @@ class ReportService
|
||||
// 'discharged' => $this->getMetrikaResultCount('discharged', $reportIds),
|
||||
'transferred' => $this->getMetrikaResultCount(13, $reportIds),
|
||||
'recipient' => $this->getMetrikaResultCount(3, $reportIds),
|
||||
'beds' => $this->getMetrikaResultCount(1, $reportIds, false)
|
||||
'beds' => $this->getMetrikaResultCount(1, $reportIds, false),
|
||||
'countStaff' => $this->getMetrikaResultCount(17, [$lastReport], false)
|
||||
];
|
||||
|
||||
// Получаем ID поступивших пациентов
|
||||
@@ -499,6 +622,7 @@ class ReportService
|
||||
'extractCount' => $snapshotStats['outcome'] ?? 0,
|
||||
'currentCount' => $snapshotStats['current'] ?? 0,//$this->calculateCurrentPatientsFromSnapshots($reportIds, $branchId),
|
||||
'deadCount' => $snapshotStats['deceased'] ?? 0,
|
||||
'countStaff' => $snapshotStats['countStaff'] ?? 0,
|
||||
'surgicalCount' => $surgicalCount,
|
||||
'recipientIds' => $recipientIds,
|
||||
'beds' => $snapshotStats['beds'] ?? 0,
|
||||
@@ -819,7 +943,8 @@ class ReportService
|
||||
{
|
||||
return UnwantedEvent::whereHas('report', function ($query) use ($department, $dateRange) {
|
||||
$query->where('rf_department_id', $department->department_id)
|
||||
->whereBetween('sent_at', [$dateRange->startSql(), $dateRange->endSql()]);
|
||||
->whereDate('sent_at', '>=', $dateRange->startSql())
|
||||
->whereDate('sent_at', '<=', $dateRange->endSql());
|
||||
})
|
||||
->get()
|
||||
->map(function ($item) {
|
||||
@@ -876,14 +1001,16 @@ class ReportService
|
||||
{
|
||||
if ($dateRange->isOneDay) {
|
||||
return Report::where('rf_department_id', $departmentId)
|
||||
->whereDate('created_at', $dateRange->endSql())
|
||||
->orderBy('created_at', 'ASC')
|
||||
->whereDate('sent_at', $dateRange->endSql())
|
||||
->orderBy('sent_at', 'DESC')
|
||||
->get();
|
||||
}
|
||||
|
||||
return Report::where('rf_department_id', $departmentId)
|
||||
->whereBetween('created_at', [$dateRange->startSql(), $dateRange->endSql()])
|
||||
->orderBy('created_at', 'ASC')
|
||||
// ->whereBetween('created_at', [$dateRange->startSql(), $dateRange->endSql()])
|
||||
->where('sent_at', '>', $dateRange->startSql())
|
||||
->where('sent_at', '<=', $dateRange->endSql())
|
||||
->orderBy('sent_at', 'DESC')
|
||||
->get();
|
||||
}
|
||||
|
||||
@@ -972,4 +1099,44 @@ class ReportService
|
||||
return $patient;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить статистику выполнения плана по госпитализации
|
||||
*/
|
||||
public function getRecipientPlanOfYear(Department $department, DateRange $dateRange): array
|
||||
{
|
||||
$periodPlanModel = $department->recipientPlanOfYear();
|
||||
// Рассчитываем коэффициент периода (округляем в большую сторону)
|
||||
$monthsInPeriod = ceil($dateRange->startDate->diffInMonths($dateRange->endDate));
|
||||
$annualPlan = $periodPlanModel ? (int)$periodPlanModel->value : 0;
|
||||
$oneMonthPlan = ceil($annualPlan / 12);
|
||||
$periodPlan = round($oneMonthPlan * $monthsInPeriod);
|
||||
|
||||
$progress = 0;
|
||||
$query = $department->reports()
|
||||
->with('metrikaResults')
|
||||
->where('sent_at', '>=', $dateRange->startSql())
|
||||
->where('sent_at', '<=', $dateRange->endSql());
|
||||
|
||||
if ($dateRange->isOneDay) {
|
||||
$query->where('sent_at', '>=', $dateRange->startFirstOfMonth())
|
||||
->where('sent_at', '<=', $dateRange->endSql());
|
||||
} else {
|
||||
$query->where('sent_at', '>=', $dateRange->startSql())
|
||||
->where('sent_at', '<=', $dateRange->endSql());
|
||||
}
|
||||
|
||||
$reports = $query->get();
|
||||
|
||||
foreach ($reports as $report) {
|
||||
$outcome = $report->metrikaResults()->where('rf_metrika_item_id', 7)->first();
|
||||
if ($outcome) $progress += (int)$outcome->value;
|
||||
}
|
||||
|
||||
return [
|
||||
'plan' => $periodPlan,
|
||||
'progress' => $progress
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user